package jgame.platform; import jgame.*; import jgame.impl.*; import java.io.*; import javax.microedition.midlet.MIDlet; import javax.microedition.lcdui.Display; import javax.microedition.lcdui.*; import javax.microedition.media.*; import javax.microedition.media.control.*; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; import java.util.Timer; import java.util.Random; import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Image; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.game.Sprite; import javax.microedition.lcdui.game.GameCanvas; import javax.microedition.lcdui.game.TiledLayer; import javax.microedition.lcdui.game.LayerManager; import javax.microedition.rms.*; import java.io.IOException; import javax.microedition.io.ConnectionNotFoundException; public abstract class JGEngine extends MIDlet implements JGEngineInterface { /*=== main objects ===*/ MIDPImage imageutil = new MIDPImage(); EngineLogic el = new EngineLogic(imageutil,true,true); Thread gamethread=null; /*=== canvas ===*/ JGCanvas canvas = new JGCanvas(); public void setProgressBar(double pos) { // XXX check out if the load screen gets updated properly if we don't // use defineMedia canvas.progress_bar=pos; canvas.drawAll(); } public void setProgressMessage(String msg) { canvas.progress_message=msg; canvas.drawAll(); } public void setAuthorMessage(String msg) { canvas.author_message=msg; canvas.drawAll(); } Image background=null; Graphics bgg=null; class JGCanvas extends GameCanvas { /** for displaying progress info */ String progress_message="Loading files ..."; /** for displaying progress bar, value between 0.0 - 1.0 */ public double progress_bar=0.0; String author_message="JGame "+JGameVersionString; public JGCanvas () { super(false); setFullScreenMode(true); } boolean is_initialised=false; void setInitialised() { is_initialised=true; //initpainter=null; } public void keyPressed(int keyCode) { switch (keyCode) { case KEY_NUM0 : keymap['0'] = true; break; case KEY_NUM1 : keymap['1'] = true; break; case KEY_NUM2 : keymap['2'] = true; break; case KEY_NUM3 : keymap['3'] = true; break; case KEY_NUM4 : keymap['4'] = true; break; case KEY_NUM5 : keymap['5'] = true; break; case KEY_NUM6 : keymap['6'] = true; break; case KEY_NUM7 : keymap['7'] = true; break; case KEY_NUM8 : keymap['8'] = true; break; case KEY_NUM9 : keymap['9'] = true; break; case KEY_POUND: keymap[' '] = true; keymap['#'] = true; break; case KEY_STAR : keymap[KeyEnter] = true; keymap['*'] = true; break; default: // only parse game key when no other key was identified. // because sagem my401c maps numeric keys to game keys as well int gameAct = getGameAction(keyCode); switch (gameAct) { case UP : keymap[KeyUp] = true; break; case LEFT : keymap[KeyLeft] = true; break; case RIGHT: keymap[KeyRight] = true; break; case DOWN : keymap[KeyDown] = true; break; case FIRE : keymap[KeyShift] = true; // support GAME_A ... GAME-D } } if (wakeup_key==-1 || keymap[wakeup_key]) { if (!running) { start(); // key is cleared when it is used as wakeup key if (wakeup_key!=-1) keymap[wakeup_key]=false; } } } public void keyReleased(int keyCode) { switch (keyCode) { case KEY_NUM0 : keymap['0'] = false; break; case KEY_NUM1 : keymap['1'] = false; break; case KEY_NUM2 : keymap['2'] = false; break; case KEY_NUM3 : keymap['3'] = false; break; case KEY_NUM4 : keymap['4'] = false; break; case KEY_NUM5 : keymap['5'] = false; break; case KEY_NUM6 : keymap['6'] = false; break; case KEY_NUM7 : keymap['7'] = false; break; case KEY_NUM8 : keymap['8'] = false; break; case KEY_NUM9 : keymap['9'] = false; break; case KEY_POUND: keymap[' '] = false; keymap['#'] = false; break; case KEY_STAR : keymap[KeyEnter] = false; keymap['*'] = false; } int gameAct = getGameAction(keyCode); switch (gameAct) { case UP : keymap[KeyUp] = false; break; case LEFT : keymap[KeyLeft] = false; break; case RIGHT: keymap[KeyRight] = false; break; case DOWN : keymap[KeyDown] = false; break; case FIRE : keymap[KeyFire] = false; // support GAME_A ... GAME-D } } public void pointerPressed(int x, int y) { mousepos.x = (int)((x-el.canvas_xofs)/el.x_scale_fac); mousepos.y = (int)((y-el.canvas_yofs)/el.y_scale_fac); mousebutton[1] = true; keymap[256]=true; mouseinside=true; } public void pointerReleased(int x, int y) { mousepos.x = (int)((x-el.canvas_xofs)/el.x_scale_fac); mousepos.y = (int)((y-el.canvas_yofs)/el.y_scale_fac); mousebutton[1] = false; keymap[256]=false; mouseinside=false; } public void pointerDragged(int x, int y) { mousepos.x = (int)((x-el.canvas_xofs)/el.x_scale_fac); mousepos.y = (int)((y-el.canvas_yofs)/el.y_scale_fac); mouseinside=true; } public void clearCanvas() { // fill canvas with black if (bufg==null) bufg = getGraphics(); bufg.setClip(0,0,getWidth(),getHeight()); setColor(bufg,JGColor.black); bufg.fillRect(0,0,getWidth(),getHeight()); } long last_update_time = 0; Graphics bufg=null; public void drawAll() { try { if (el.is_exited) { //paintExitMessage(g); return; } // get graphics of built-in off-screen buffer. // nokia developer's guide says that the same graphics can be used // throughout the lifecycle of the canvas. if (bufg==null) bufg = getGraphics(); if (!is_initialised) { long time = System.currentTimeMillis(); if (time < last_update_time + 200) return; last_update_time = time; setFont(bufg,el.msg_font); setColor(bufg,el.fg_color); JGImage splash = el.existsImage("splash_image") ? el.getImage("splash_image") : null; if (splash!=null) { JGPoint splash_size=getImageSize("splash_image"); drawImage(bufg,viewWidth()/2-splash_size.x/2, Math.max(0,viewHeight()/4-splash_size.y/2), "splash_image", false); } drawString(bufg,progress_message, viewWidth()/2,3*viewHeight()/5,0,false); //if (canvas.progress_message!=null) { //drawString(bufg,canvas.progress_message, // viewWidth()/2,2*viewHeight()/3,0); //} // paint the right hand side black in case the bar decreases setColor(bufg,el.bg_color); drawRect(bufg,(int)(viewWidth()*(0.1+0.8*progress_bar)), (int)(viewHeight()*0.75), (int)(viewWidth()*0.8*(1.0-progress_bar)), (int)(viewHeight()*0.05), true,false, false); // left hand side of bar setColor(bufg,el.fg_color); drawRect(bufg,(int)(viewWidth()*0.1), (int)(viewHeight()*0.75), (int)(viewWidth()*0.8*progress_bar), (int)(viewHeight()*0.05), true,false, false); // length stripes /*drawRect(bufg,(int)(viewWidth()*0.1), (int)(viewHeight()*0.6), (int)(viewWidth()*0.8), (int)(viewHeight()*0.008), true,false, false); drawRect(bufg,(int)(viewWidth()*0.1), (int)(viewHeight()*(0.6+0.046)), (int)(viewWidth()*0.8), (int)(viewHeight()*0.008), true,false, false);*/ drawString(bufg,author_message, viewWidth()-16,viewHeight()-getFontHeight(el.msg_font)-10, 1,false); flushGraphics(); return; } bufg.setClip(Math.max(0,el.canvas_xofs),Math.max(0,el.canvas_yofs), Math.min(el.width,el.winwidth), Math.min(el.height,el.winheight) ); //bufg.setClip(0,0,120,120); // block update thread synchronized (el.objects) { // paint any part of bg which is not yet defined el.repaintBG(JGEngine.this); /* clear buffer */ buf_gfx = bufg; // enable objects to draw on buffer gfx. // Draw background to buffer. // this part is the same across jre and midp. Move it to // EngineLogic? //bufg.drawImage(background,-scaledtilex,-scaledtiley,this); int tilexshift=el.moduloFloor(el.tilexofs+1,el.viewnrtilesx+3); int tileyshift=el.moduloFloor(el.tileyofs+1,el.viewnrtilesy+3); int sx1 = tilexshift+1; int sy1 = tileyshift+1; int sx2 = el.viewnrtilesx+3; int sy2 = el.viewnrtilesy+3; if (sx2-sx1 > el.viewnrtilesx) sx2 = sx1 + el.viewnrtilesx; if (sy2-sy1 > el.viewnrtilesy) sy2 = sy1 + el.viewnrtilesy; int bufmidx = sx2-sx1; int bufmidy = sy2-sy1; copyBGToBuf(bufg,sx1,sy1, sx2,sy2, 0,0); sx1 = 0; sy1 = 0; sx2 = tilexshift-1; sy2 = tileyshift-1; copyBGToBuf(bufg,sx1,sy1, sx2,sy2, bufmidx,bufmidy); sx1 = 0; sy1 = tileyshift+1; sx2 = tilexshift-1; sy2 = el.viewnrtilesy+3; if (sy2-sy1 > el.viewnrtilesy) sy2 = sy1 + el.viewnrtilesy; copyBGToBuf(bufg,sx1,sy1, sx2,sy2, bufmidx,0); sx1 = tilexshift+1; sy1 = 0; sx2 = el.viewnrtilesx+3; sy2 = tileyshift-1; if (sx2-sx1 > el.viewnrtilesx) sx2 = sx1 + el.viewnrtilesx; copyBGToBuf(bufg,sx1,sy1, sx2,sy2, 0,bufmidy); for (int i=0; i do full frame. synchronized (el.objects) { doFrameAll(); el.updateViewOffset(); } canvas.drawAll(); frames_skipped=0; if (cur_time+3 < target_time) { //we even have some time left -> sleep it away Thread.sleep(target_time-cur_time); } else { // we don't, just yield to give input handler and // painter some time Thread.yield(); } target_time += (1000.0/el.fps); //} else if (cur_time > //target_time + (long)(1000.0*el.maxframeskip/el.fps)) { // // we lag behind more than the max # frames -> // // draw full frame and reset target time // synchronized (el.objects) { // doFrameAll(); // el.updateViewOffset(); // } // canvas.drawAll(); // frames_skipped=0; // // yield to give input handler + painter some time // Thread.yield(); // target_time=cur_time + (long)(1000.0/el.fps); } else { // we lag behind a little -> frame skip synchronized (el.objects) { doFrameAll(); el.updateViewOffset(); } // if we skip too many frames in succession, draw a frame if ((++frames_skipped) > el.maxframeskip) { //canvas.repaint(); canvas.drawAll(); frames_skipped=0; target_time=cur_time + (long)(1000.0/el.fps); } else { target_time += (long)(1000.0/el.fps); } // yield to give input handler some time Thread.yield(); } } } catch (InterruptedException e) { /* exit thread when interrupted */ System.out.println("JGame thread exited."); } catch (Exception e) { dbgShowException("MAIN",e); } catch (JGameError e) { e.printStackTrace(); exitEngine("Error in main:\n"+dbgExceptionToString(e)); } } } /*====== variables from engine ======*/ /** Should engine thread run or halt? Set by start() / stop()*/ boolean running=true; Graphics buf_gfx=null; /*====== platform-dependent variables ======*/ /* keyboard */ /** The codes 256-258 are the mouse buttons */ boolean [] keymap = new boolean [256+3]; int lastkey=0; char lastkeychar=0; int wakeup_key=0; /*====== images ======*/ public JGImage getImage(String imgname) { return el.getImage(imgname); } public JGPoint getImageSize(String imgname) { return el.getImageSize(imgname); } public void defineMedia(String filename) { el.defineMedia(this,filename); } public void defineImage(String name, String tilename, int collisionid, String imgfile, String img_op, int top,int left, int width,int height) { el.defineImage(this,name,tilename,collisionid,imgfile,img_op, top,left, width,height); } public void defineImage(String imgname, String tilename, int collisionid, String imgfile, String img_op) { el.defineImage(this,imgname,tilename,collisionid,imgfile, img_op); } public void defineImage(String imgname, String tilename, int collisionid, String imgmap, int mapidx, String img_op, int top,int left, int width,int height) { el.defineImage(imgname,tilename,collisionid, imgmap, mapidx, img_op, top,left,width,height ); } public void defineImage(String imgname, String tilename, int collisionid, String imgmap, int mapidx, String img_op) { el.defineImage(imgname,tilename,collisionid, imgmap, mapidx, img_op); } public void defineImageRotated(String name, String tilename, int collisionid, String srcname, double angle) { el.defineImageRotated(this,name,tilename,collisionid, srcname, angle); } public void defineImageMap(String mapname, String imgfile, int xofs,int yofs, int tilex,int tiley, int skipx,int skipy) { el.defineImageMap(this,mapname,imgfile, xofs,yofs, tilex,tiley, skipx,skipy); } public JGRectangle getImageBBox(String imgname) { return el.getImageBBox(imgname); } /*====== PF/view ======*/ /*====== objects from canvas ======*/ public void markAddObject(JGObject obj) { el.markAddObject(obj); } public boolean existsObject(String index) { return el.existsObject(index); } public JGObject getObject(String index) { return el.getObject(index); } public void moveObjects(String prefix, int cidmask) { el.moveObjects(this,prefix, cidmask); } public void moveObjects() { el.moveObjects(this); } public void checkCollision(int srccid,int dstcid) { el.checkCollision(this,srccid,dstcid); } public int checkCollision(int cidmask, JGObject obj) { return el.checkCollision(cidmask,obj); } public int checkBGCollision(JGRectangle r) { return el.checkBGCollision(r); } public void checkBGCollision(int tilecid,int objcid) { el.checkBGCollision(this,tilecid,objcid); } /* objects from engine */ public Vector getObjects(String prefix,int cidmask,boolean suspended_obj, JGRectangle bbox) { return el.getObjects(prefix,cidmask,suspended_obj, bbox); } public void removeObject(JGObject obj) { el.removeObject(obj); } public void removeObjects(String prefix,int cidmask) { el.removeObjects(prefix,cidmask); } public void removeObjects(String prefix,int cidmask,boolean suspended_obj) { el.removeObjects(prefix,cidmask,suspended_obj); } public int countObjects(String prefix,int cidmask) { return el.countObjects(prefix,cidmask); } public int countObjects(String prefix,int cidmask,boolean suspended_obj) { return el.countObjects(prefix,cidmask,suspended_obj); } void drawObject(Graphics g, JGObject o) { if (!o.is_suspended) { drawImage(g,(int)o.x,(int)o.y,o.getImageName(),true); try { o.paint(); } catch (JGameError ex) { ex.printStackTrace(); exitEngine(dbgExceptionToString(ex)); } catch (Exception e) { dbgShowException(o.getName(),e); } } // debug functionality not implemented in midp } /*====== BG/tiles ======*/ public void setBGImage(String bgimg) { el.setBGImage(bgimg,0,true,true); } public void setBGImage(int depth, String bgimg,boolean wrapx,boolean wrapy){ el.setBGImage(bgimg,depth,wrapx,wrapy); } public void setTileSettings(String out_of_bounds_tile, int out_of_bounds_cid,int preserve_cids) { el.setTileSettings(out_of_bounds_tile,out_of_bounds_cid,preserve_cids); } public void fillBG(String filltile) { el.fillBG(filltile); } public void setTileCid(int x,int y,int and_mask,int or_mask) { el.setTileCid(x,y,and_mask,or_mask); } public void setTile(int x,int y,String tilestr) { el.setTile(x,y,tilestr); } void setColor(Graphics g,JGColor col) { g.setColor(col.r,col.g,col.b); } /** xi,yi are tile indexes relative to the tileofs, that is, the top left * of the bg, + 1. They must be within both the tilemap and the view. */ public void drawTile(int xi,int yi,int tileid) { if (background == null) return; // determine position within bg int x = el.moduloFloor(xi+1,el.viewnrtilesx+3) * el.scaledtilex; int y = el.moduloFloor(yi+1,el.viewnrtilesy+3) * el.scaledtiley; // draw if (bgg==null) bgg = background.getGraphics(); Integer tileid_obj = new Integer(tileid); MIDPImage img = (MIDPImage)el.getTileImage(tileid_obj); // define background behind tile in case the tile is null or // transparent. if (img==null||el.images_transp.containsKey(tileid_obj)) { EngineLogic.BGImage bg_image = (EngineLogic.BGImage) el.bg_images.elementAt(0); if (bg_image==null) { setColor(bgg,el.bg_color); bgg.fillRect(x,y,el.scaledtilex,el.scaledtiley); } else { int xtile = el.moduloFloor(xi,bg_image.tiles.x); int ytile = el.moduloFloor(yi,bg_image.tiles.y); // we use quick getimage here, unlike the jre version bgg.drawRegion(((MIDPImage)el.getImage(bg_image.imgname)).img, xtile*el.scaledtilex, ytile*el.scaledtiley, el.scaledtilex, el.scaledtiley, Sprite.TRANS_NONE, x,y, Graphics.TOP|Graphics.LEFT); } } if (img!=null) { bgg.drawImage(img.img,x,y,Graphics.TOP|Graphics.LEFT); } //System.out.println("Drawn tile"+tileid); } public int countTiles(int tilecidmask) { return el.countTiles(tilecidmask); } public int getTileCid(int xidx,int yidx) { return el.getTileCid(xidx,yidx); } public String getTileStr(int xidx,int yidx) { return el.getTileStr(xidx,yidx); } public int getTileCid(JGRectangle tiler) { return el.getTileCid(tiler); } public JGRectangle getTiles(JGRectangle r) { return el.getTiles(r); } public boolean getTiles(JGRectangle dest,JGRectangle r) { return el.getTiles(dest,r); } public void setTileCid(int x,int y,int value) { el.setTileCid(x,y,value); } public void orTileCid(int x,int y,int or_mask) { el.orTileCid(x,y,or_mask); } public void andTileCid(int x,int y,int and_mask) { el.andTileCid(x,y,and_mask); } public void setTile(JGPoint tileidx,String tilename) { el.setTile(tileidx,tilename); } public void setTiles(int xofs,int yofs,String [] tilemap) { el.setTiles(xofs,yofs,tilemap); } public void setTilesMulti(int xofs,int yofs,String [] tilemap) { el.setTilesMulti(xofs,yofs,tilemap); } public int getTileCidAtCoord(double x,double y) { return el.getTileCidAtCoord(x,y); } public int getTileCid(JGPoint center, int xofs, int yofs) { return el.getTileCid(center, xofs, yofs); } public String getTileStrAtCoord(double x,double y) { return el.getTileStrAtCoord(x,y); } public String getTileStr(JGPoint center, int xofs, int yofs) { return el.getTileStr(center, xofs,yofs); } public int tileStrToID(String tilestr) { return el.tileStrToID(tilestr); } public String tileIDToStr(int tileid) { return el.tileIDToStr(tileid); } /** dx1 and dy1 are coordinates on canvas buffer, without canvas_ofs. */ void copyBGToBuf(Graphics bufg, int sx1,int sy1,int sx2,int sy2, int dx1,int dy1) { //dx1 += el.canvas_xofs; //dy1 += el.canvas_yofs; //System.out.println("("+sx1+","+sy1+")-("+sx2+","+sy2+")"); if (sx2<=sx1 || sy2<=sy1) return; int barrelx = el.scaleXPos(el.moduloFloor(el.xofs,el.tilex),false); int barrely = el.scaleYPos(el.moduloFloor(el.yofs,el.tiley),false); int barreldx = (sx1==0) ? barrelx : 0; int barreldy = (sy1==0) ? barrely : 0; barrelx = (sx1==0) ? 0 : barrelx; barrely = (sy1==0) ? 0 : barrely; int dx2 = dx1 + sx2-sx1; int dy2 = dy1 + sy2-sy1; // ensure source coordinates are not out of the bounds of the source // image int sx1e = barrelx+sx1*el.scaledtilex; int sy1e = barrely+sy1*el.scaledtiley; int sx2e = barrelx+sx2*el.scaledtilex; int sx2max = (el.viewnrtilesx+3)*el.scaledtilex; if (sx2e > sx2max) sx2e = sx2max; int sy2e = barrely+sy2*el.scaledtiley; int sy2max = (el.viewnrtilesy+3)*el.scaledtiley; if (sy2e > sy2max) sy2e = sy2max; //void drawRegion(Image src, int x_src, int y_src, int width, int height, int transform, int x_dest, int y_dest, int anchor) bufg.drawRegion(background, sx1e,sy1e, sx2e-sx1e, sy2e-sy1e, //barrelx+sx1*el.scaledtilex, barrely+sy1*el.scaledtiley, //(sx2-sx1)*el.scaledtilex, (sy2-sy1)*el.scaledtiley, Sprite.TRANS_NONE, dx1*el.scaledtilex-barreldx + el.canvas_xofs, dy1*el.scaledtiley-barreldy + el.canvas_yofs, Graphics.TOP|Graphics.LEFT); //bufg.drawImage(background, // dx1*el.scaledtilex-barreldx, dy1*el.scaledtiley-barreldy, // dx2*el.scaledtilex-barreldx, dy2*el.scaledtiley-barreldy, // barrelx+sx1*el.scaledtilex, barrely+sy1*el.scaledtiley, // barrelx+sx2*el.scaledtilex, barrely+sy2*el.scaledtiley, // this); } /*====== math ======*/ public double moduloXPos(double x) { return el.moduloXPos(x); } public double moduloYPos(double y) { return el.moduloYPos(y); } /*====== debug ======*/ public void dbgShowBoundingBox(boolean enabled) {} public void dbgShowGameState(boolean enabled) {} public void dbgShowFullStackTrace(boolean enabled) {} public void dbgShowMessagesInPf(boolean enabled) {} public void dbgSetMessageExpiry(int ticks) {} public void dbgSetMessageFont(JGFont font) { } public void dbgSetDebugColor1(JGColor col) { } public void dbgSetDebugColor2(JGColor col) { } public void dbgPrint(String msg) { dbgPrint("MAIN",msg); } public void dbgPrint(String source,String msg) { System.out.println(source+": "+msg); } public void dbgShowException(String source, Throwable e) { e.printStackTrace(); //dbgPrint(source,st.toString()); } public String dbgExceptionToString(Throwable e) { return e.toString(); } //public void setCanvasSettings(int nrtilesx,int nrtilesy,int tilex,int tiley, //Color fgcolor, Color bgcolor, Font msgfont) { // el.setCanvasSettings(nrtilesx,nrtilesy,tilex,tiley, // fgcolor, bgcolor, msgfont); //} public void requestGameFocus() { } // note: these get and set methods do not delegate calls public boolean isApplet() { return false; } public boolean isMidlet() { return true; } public boolean isOpenGL() { return false; } public boolean isAndroid() { return false; } public int viewWidth() { return el.viewnrtilesx*el.tilex; } public int viewHeight() { return el.viewnrtilesy*el.tiley; } public int viewTilesX() { return el.viewnrtilesx; } public int viewTilesY() { return el.viewnrtilesy; } public int viewXOfs() { return el.pendingxofs; } public int viewYOfs() { return el.pendingyofs; } //public int viewTileXOfs() { return canvas.tilexofs; } //public int viewTileYOfs() { return canvas.tileyofs; } public int pfWidth() { return el.nrtilesx*el.tilex; } public int pfHeight() { return el.nrtilesy*el.tiley; } public int pfTilesX() { return el.nrtilesx; } public int pfTilesY() { return el.nrtilesy; } public boolean pfWrapX() { return el.pf_wrapx; } public boolean pfWrapY() { return el.pf_wrapy; } public int tileWidth() { return el.tilex; } public int tileHeight() { return el.tiley; } public int displayWidth() { return el.winwidth; } public int displayHeight() { return el.winheight; } public double getFrameRate() { return el.fps; } public double getGameSpeed() { return el.gamespeed; } public double getFrameSkip() { return el.maxframeskip; } public boolean getVideoSyncedUpdate() { return false; } public int getOffscreenMarginX() { return el.offscreen_margin_x; } public int getOffscreenMarginY() { return el.offscreen_margin_y; } public double getXScaleFactor() { return el.x_scale_fac; } public double getYScaleFactor() { return el.y_scale_fac; } public double getMinScaleFactor() { return el.min_scale_fac; } public void setViewOffset(int xofs,int yofs,boolean centered) { el.setViewOffset(xofs,yofs,centered); } public void setBGImgOffset(int depth, double xofs, double yofs, boolean centered) { } public void setViewZoomRotate(double zoom, double rotate) { } public void setPFSize(int nrtilesx,int nrtilesy) { el.setPFSize(nrtilesx,nrtilesy); } public void setPFWrap(boolean wrapx,boolean wrapy,int shiftx,int shifty) { el.setPFWrap(wrapx,wrapy,shiftx,shifty); } public void setFrameRate(double fps, double maxframeskip) { el.setFrameRate(fps, maxframeskip); } public void setVideoSyncedUpdate(boolean value) {} public void setGameSpeed(double gamespeed) { el.setGameSpeed(gamespeed); } public void setRenderSettings(int alpha_thresh,JGColor render_bg_col) { el.setRenderSettings(alpha_thresh,render_bg_col); } public void setOffscreenMargin(int xmargin,int ymargin) { el.setOffscreenMargin(xmargin,ymargin); } /** Set global background colour, which is displayed in borders, and behind * transparent tiles if no BGImage is defined. */ public void setBGColor(JGColor bgcolor) { el.bg_color=bgcolor; } /** Set global foreground colour, used for printing text and status * messages. It is also the default colour for painting */ public void setFGColor(JGColor fgcolor) { el.fg_color=fgcolor; } /** Set the (unscaled) message font, used for displaying status messages. * It is also the default font for painting. */ public void setMsgFont(JGFont msgfont) { el.msg_font = msgfont; } /** Set foreground and background colour, and message font in one go; * passing a null means ignore that argument. */ public void setColorsFont(JGColor fgcolor,JGColor bgcolor,JGFont msgfont) { if (msgfont!=null) el.msg_font = msgfont; if (fgcolor!=null) el.fg_color = fgcolor; if (bgcolor!=null) setBGColor(bgcolor); } /** Set parameters of outline surrounding text (for example, used to * increase contrast). * @param thickness 0 = turn off outline */ public void setTextOutline(int thickness,JGColor colour) { // curiously, I've seen the init screen draw in-between these two // statements. Check of if that's what really happened el.outline_colour=colour; el.outline_thickness=thickness; } /** Unimplemented, does nothing. */ public void setMouseCursor(int cursor) {} /** Unimplemented, does nothing. */ public void setMouseCursor(Object cursor) {} /* timers */ public void removeAllTimers() { el.removeAllTimers(); } public void registerTimer(JGTimer timer) { el.registerTimer(timer); } /* game state */ public void setGameState(String state) { el.setGameState(state); } public void addGameState(String state) { el.addGameState(state); } public void removeGameState(String state) { el.removeGameState(state); } public void clearGameState() { el.clearGameState(); } public boolean inGameState(String state) { return el.inGameState(state); } public boolean inGameStateNextFrame(String state) { return el.inGameStateNextFrame(state); } /** Do some administration, call doFrame. */ private void doFrameAll() { //audioNewFrame(); // the first flush is needed to remove any objects that were created // in the main routine after the last moveObjects or checkCollision el.flushRemoveList(); el.flushAddList(); // tick timers before doing state transitions, because timers may // initiate new transitions. el.tickTimers(); el.flushRemoveList(); el.flushAddList(); // the game state transition starts here el.gamestate.removeAllElements(); int maxi = el.gamestate_nextframe.size(); for (int i=0; i=statesuffixes.length) exitEngine("Game state "+suffix+" not supported!"); int idx = statesuffixes.length*prefidx + sufidx; //if (methodidx==null) { // methodidx = new Hashtable(); // for (int i=0; i0) { int origcol = g.getColor(); setColor(el.outline_colour); int real_thickness=Math.max( el.scaleXPos(el.outline_thickness,false),1 ); for (int i=-real_thickness; i<=real_thickness; i++) { if (i==0) continue; g.drawString(str,(int)x+i,(int)y,anchor); } for (int i=-real_thickness; i<=real_thickness; i++) { if (i==0) continue; g.drawString(str,(int)x,(int)y+i,anchor); } g.setColor(origcol); } g.drawString(str,(int)x,(int)y,anchor); } public void drawImageString(String string, double x, double y, int align, String imgmap, int char_offset, int spacing) { el.drawImageString(this,string,x,y,align,imgmap,char_offset,spacing, false); } public void drawImageString(String string, double x, double y, int align, String imgmap, int char_offset, int spacing,boolean pf_relative) { el.drawImageString(this,string,x,y,align,imgmap,char_offset,spacing, pf_relative); } /* input */ /** MIDP has different mouse semantics, based on touch screen. Touch * screens have only press, drag, and release (no mouse move without * button press). There is only one "button", button 1. Mouseinside is * set to false when finger/stylus is not on the touch screen. */ JGPoint mousepos = new JGPoint(0,0); boolean [] mousebutton = new boolean[] {false,false,false,false}; boolean mouseinside=false; /** XXX: does not produce a clone of mousepos, for efficiency reasons! */ public JGPoint getMousePos() { return mousepos; } public int getMouseX() { return mousepos.x; } public int getMouseY() { return mousepos.y; } public boolean getMouseButton(int nr) { return mousebutton[nr]; } public void clearMouseButton(int nr) { mousebutton[nr]=false; } public void setMouseButton(int nr) { mousebutton[nr]=true; } public boolean getMouseInside() { return mouseinside; } public boolean getKey(int key) { return keymap[key]; } public void clearKey(int key) { keymap[key]=false; } public void setKey(int key) { keymap[key]=true; } public int getLastKey() { return lastkey; } public char getLastKeyChar() { return lastkeychar; } public void clearLastKey() { lastkey=0; lastkeychar=0; } /* maybe move the translations? Should they be system dependent? */ /** Get a printable string describing the key. Non-static version for the * sake of the interface. */ public String getKeyDesc(int key) { return getKeyDescStatic(key); } /** Get a printable string describing the key. */ public static String getKeyDescStatic(int key) { if (key==32) return "#"; if (key==0) return "(none)"; if (key==KeyEnter) return "*"; if (key==KeyStar) return "*"; if (key==' ') return "#"; if (key==KeyPound) return "#"; //if (key==KeyEsc) return "escape"; if (key==KeyUp) return "cursor up"; if (key==KeyDown) return "cursor down"; if (key==KeyLeft) return "cursor left"; if (key==KeyRight) return "cursor right"; if (key==KeyShift) return "fire"; //if (key==KeyAlt) return "alt"; //if (key==KeyCtrl) return "control"; //if (key==KeyMouse1) return "left mouse button"; //if (key==KeyMouse2) return "middle mouse button"; //if (key==KeyMouse3) return "right mouse button"; //if (key==27) return "escape"; if (key >= 33 && key <= 95) return new String(new char[] {(char)key}); return "keycode "+key; } /** Obtain key code from printable string describing the key, the inverse * of getKeyDesc. The string is trimmed and lowercased. Non-static version * for the sake of the interface. */ public int getKeyCode(String keydesc) { return getKeyCodeStatic(keydesc); } /** Obtain key code from printable string describing the key, the inverse * of getKeyDesc. The string is trimmed and lowercased. */ public static int getKeyCodeStatic(String keydesc) { // tab, enter, backspace, insert, delete, home, end, pageup, pagedown // escape keydesc = keydesc.toLowerCase().trim(); //if (keydesc.equals("space")) { // return 32; //} else if (keydesc.equals("escape")) { // return KeyEsc; //} else if (keydesc.equals("(none)")) { return 0; //} else if (keydesc.equals("enter")) { // return KeyEnter; } else if (keydesc.equals("cursor up")) { return KeyUp; } else if (keydesc.equals("cursor down")) { return KeyDown; } else if (keydesc.equals("cursor left")) { return KeyLeft; } else if (keydesc.equals("cursor right")) { return KeyRight; //} else if (keydesc.equals("shift")) { // return KeyShift; } else if (keydesc.equals("fire")) { return KeyFire; } else if (keydesc.equals("star")) { return '*'; } else if (keydesc.equals("pound")) { return '#'; //} else if (keydesc.equals("alt")) { // return KeyAlt; //} else if (keydesc.equals("control")) { // return KeyCtrl; //} else if (keydesc.equals("left mouse button")) { // return KeyMouse1; //} else if (keydesc.equals("middle mouse button")) { // return KeyMouse2; //} else if (keydesc.equals("right mouse button")) { // return KeyMouse3; } else if (keydesc.startsWith("keycode")) { return Integer.parseInt(keydesc.substring(7)); } else if (keydesc.length() == 1) { return keydesc.charAt(0); } return 0; } public boolean hasAccelerometer() { return false; } public double getAccelX() { return 0; } public double getAccelY() { return 0; } public double getAccelZ() { return 1; } public double [] getAccelVec() { return new double[] { 0,0,1 }; } /*====== animation ======*/ public void defineAnimation (String id, String [] frames, double speed) { el.defineAnimation(id,frames,speed); } public void defineAnimation (String id, String [] frames, double speed, boolean pingpong) { el.defineAnimation(id,frames, speed, pingpong); } public Animation getAnimation(String id) { return el.getAnimation(id); } public String getConfigPath(String filename) { return null; // not implemented yet. midp should use the special configuration // features for this. } public int invokeUrl(String url,String target) { try { if (platformRequest(url)) { // XXX signal to user that applet must be quit first } return 1; } catch (ConnectionNotFoundException e) { return 0; } } // void paintExitMessage(Graphics g) { try { // setFont(g,debugmessage_font); // int height = (int) (getFontHeight(g,null) / el.y_scale_fac); // // clear background // setColor(g,el.bg_color); // drawRect(g, el.viewWidth()/2, el.viewHeight()/2, // 9*el.viewWidth()/10, height*5, true,true, false); // setColor(g,debug_auxcolor2); // // draw colour bars // drawRect(g, el.viewWidth()/2, el.viewHeight()/2 - 5*height/2, // 9*viewWidth()/10, 5, true,true, false); // drawRect(g, el.viewWidth()/2, el.viewHeight()/2 + 5*height/2, // 9*viewWidth()/10, 5, true,true, false); // setColor(g,el.fg_color); // int ypos = el.viewHeight()/2 - 3*height/2; // StringTokenizer toker = new StringTokenizer(el.exit_message,"\n"); // while (toker.hasMoreTokens()) { // drawString(g,toker.nextToken(),el.viewWidth()/2,ypos,0, false); // ypos += height+1; // } // } catch(java.lang.NullPointerException e) { // // this sometimes happens during drawString when the applet is exiting // // but calls repaint while the graphics surface is already disposed. // // See also bug 4791314: // // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4791314 // } } /* computation */ public boolean and(int value, int mask) { return el.and(value, mask); } public double random(double min, double max) { return el.random(min, max); } public double random(double min, double max, double interval) { return el.random(min, max, interval); } public int random(int min, int max, int interval) { return el.random(min, max, interval); } /** Short and fast Math.atan2 replacement. Average numerical error is * less than 0.001 radians, maximum error is 0.005 radians. */ public double atan2(double y,double x) { boolean xneg = x<0; boolean yneg = y<0; if (xneg) x = -x; if (yneg) y = -y; double res,r; if (x>y) { // 0 to 45 deg r = y/x; } else { //45 to 90 deg r = x/y; } // taken from http://www.restena.lu/convict/Jeunes/Math/arctan.htm // y = -0.30097 + 0.61955*x - 0.001659*x*x // x is between 0 and 100%, y between 0 and 45 degrees res = -0.30097 + 61.955*r - 16.59*r*r; // convert x/y angle back to y/x if (y>x) res = 90 - res; // convert degrees to radians (we should incorporate this into the // equation above one day) //res = (res/45) * Math.PI*0.25; res *= 0.017453293; if (xneg && yneg) return -Math.PI+res; if (xneg) return Math.PI-res; if (yneg) return -res; return res; } public JGPoint getTileIndex(double x, double y) { return el.getTileIndex(x, y); } public JGPoint getTileCoord(int tilex, int tiley) { return el.getTileCoord(tilex, tiley); } public JGPoint getTileCoord(JGPoint tileidx) { return el.getTileCoord(tileidx); } public double snapToGridX(double x, double gridsnapx) { return el.snapToGridX(x, gridsnapx); } public double snapToGridY(double y, double gridsnapy) { return el.snapToGridY(y, gridsnapy); } public void snapToGrid(JGPoint p,int gridsnapx,int gridsnapy) { el.snapToGrid(p,gridsnapx,gridsnapy); } public boolean isXAligned(double x,double margin) { return el.isXAligned(x,margin); } public boolean isYAligned(double y,double margin) { return el.isYAligned(y,margin); } public double getXAlignOfs(double x) { return el.getXAlignOfs(x); } public double getYAlignOfs(double y) { return el.getYAlignOfs(y); } // XXX please test these two methods public double getXDist(double x1, double x2) { return el.getXDist(x1, x2); } public double getYDist(double y1, double y2) { return el.getYDist(y1, y2); } /* We assume here that Midp phones do not typically support playing more * than 1 sample or 1 midi file at once. So there is only 1 channel. */ // see also: //http://wiki.forum.nokia.com/index.php/How_to_handle_multiple_sounds_in_Java_ME boolean audioenabled=true; Hashtable players = new Hashtable(); Player cur_player = null; String cur_sample=null; boolean cur_sample_looping=false; String looping_clip_while_disabled=null; // XXX volume control is fixed int soundvolume=50; PlayerListener playerlist = new PlayerListener() { public void playerUpdate(Player player, String event, Object eventData){ if (event.equals(PlayerListener.STARTED)) { ((VolumeControl)player.getControl("VolumeControl")) .setLevel(soundvolume); } } }; public void enableAudio() { if (audioenabled) return; audioenabled=true; if (looping_clip_while_disabled!=null) { playAudio(looping_clip_while_disabled); } //if (cur_sample_looping && cur_player!=null) { // if (cur_player.getState()==Player.STARTED) return; // try { // cur_player.start(); // } catch (javax.microedition.media.MediaException e) { // // XXX we should ignore this exception for platforms that cannot // // handle sound or samples // throw new JGameError(e.toString()); // } //} looping_clip_while_disabled=null; } public void disableAudio() { if (!audioenabled) return; audioenabled=false; stopAudio(); } public void defineAudioClip(String clipid,String filename) { el.defineAudioClip(this,clipid,filename); } public String lastPlayedAudio(String channel) { return cur_sample; } public void playAudio(String clipid) { playAudio(null,clipid,false); } public void playAudio(String channel,String clipid,boolean loop) { if (loop) looping_clip_while_disabled=clipid; if (!audioenabled) return; try { if (cur_player!=null) { // player still running -> do not retrigger, this may yield // errors or sound delays if (cur_player.getState()==Player.STARTED) return; // player stopped and new player not equal to old player // -> free audio device // However, I did not see a latency improvement when not // deallocating a player but reusing the just stopped one. if (cur_player != players.get(clipid)) cur_player.deallocate(); cur_player=null; } cur_player = (Player)players.get(clipid); if (cur_player==null) { String resname = (String)el.audioclips.get(clipid); boolean is_wav = resname.toLowerCase().endsWith("wav"); boolean is_mid = resname.toLowerCase().endsWith("mid") || resname.toLowerCase().endsWith("midi"); if (is_wav || is_mid) { InputStream in = getClass().getResourceAsStream(resname); cur_player = Manager.createPlayer(in, is_wav? "audio/x-wav" : "audio/midi"); cur_player.addPlayerListener(playerlist); players.put(clipid,cur_player); } } if (cur_player!=null) { // looping audio can only be stopped with stopAudio! if (loop) cur_player.setLoopCount(-1); try { cur_player.start(); } catch (MediaException e) { /* ignore, I found this is thrown for no good reason on * Vairy Touch II when starting audio just after it * was stopped */ } cur_sample_looping=loop; } } catch (Exception e) { e.printStackTrace(); // XXX we should ignore this exception for platforms that cannot // handle sound or samples throw new JGameError(e.toString()); } } public void stopAudio(String channel) { stopAudio(); } public void stopAudio() { if (cur_player!=null) cur_player.deallocate(); looping_clip_while_disabled=null; } private static final int STORE_INT=0; private static final int STORE_DOUBLE=1; private static final int STORE_STRING=2; /* id -> Object */ Hashtable store_data = new Hashtable(); /* id -> number */ Hashtable store_ids = new Hashtable(); /** is initialised by storeInit */ RecordStore store_impl = null; private void storeInit() { try { store_impl = RecordStore.openRecordStore("JGameStore",true); RecordEnumeration e = store_impl.enumerateRecords(null,null,false); while (e.hasNextElement()) { int recid = e.nextRecordId(); storeGetRecord(recid); } } catch (Exception e) { // XXX remove this. In production code, ignore store errors throw new Error("Error initing store!"); } } private void storeGetRecord(int recid) { try { byte [] data = store_impl.getRecord(recid); ByteArrayInputStream datai = new ByteArrayInputStream(data); DataInputStream dataib = new DataInputStream(datai); String id = dataib.readUTF(); store_ids.put(id,new Integer(recid)); int type = dataib.readInt(); if (type==STORE_INT) { store_data.put(id,new Integer(dataib.readInt())); } else if (type==STORE_DOUBLE) { store_data.put(id,new Double(dataib.readDouble())); } else if (type==STORE_STRING) { store_data.put(id,dataib.readUTF()); } else { throw new Error("Unexpected store record type "+type); } dataib.close(); } catch (Exception e) { // XXX remove this. In production code, ignore store errors throw new Error("Error reading store!"); } } private void storePutRecord(String id,Object value) { try { Integer recido = (Integer)store_ids.get(id); int recid=-1; if (recido!=null) recid = recido.intValue(); // recid = store_impl.getNextRecordId(); ByteArrayOutputStream bout= new ByteArrayOutputStream(); DataOutputStream dout = new DataOutputStream(bout); dout.writeUTF(id); if (value instanceof Integer) { dout.writeInt(STORE_INT); dout.writeInt(((Integer)value).intValue()); } else if (value instanceof Double) { dout.writeInt(STORE_DOUBLE); dout.writeDouble(((Double)value).doubleValue()); } else if (value instanceof String) { dout.writeInt(STORE_STRING); dout.writeUTF((String)value); } else { throw new Error("Unsupported store class "+value.getClass()); } dout.flush(); if (recid==-1) { recid = store_impl.addRecord(bout.toByteArray(),0,bout.size()); store_ids.put(id,new Integer(recid)); } else { store_impl.setRecord(recid,bout.toByteArray(),0,bout.size()); } store_data.put(id,value); dout.close(); } catch (Exception e) { // XXX remove this. In production code, ignore store errors throw new Error("Error writing store!"); } } public void storeWriteInt(String id,int value) { storePutRecord(id,new Integer(value)); } public void storeWriteDouble(String id,double value) { storePutRecord(id,new Double(value)); } public void storeWriteString(String id,String value) { storePutRecord(id,value); } public void storeRemove(String id) { try { if (store_ids.containsKey(id)) { int recid = ((Integer)store_ids.get(id)).intValue(); store_ids.remove(id); store_data.remove(id); store_impl.deleteRecord(recid); } } catch (Exception e) { // XXX remove this. In production code, ignore store errors throw new Error("Error removing record from store!"); } } public boolean storeExists(String id) { return store_ids.containsKey(id); } public int storeReadInt(String id,int undef) { Integer ret = (Integer)store_data.get(id); if (ret==null) return undef; return ret.intValue(); } public double storeReadDouble(String id,double undef) { Double ret = (Double)store_data.get(id); if (ret==null) return undef; return ret.doubleValue(); } public String storeReadString(String id,String undef) { String ret = (String)store_data.get(id); if (ret==null) return undef; return ret; } /*====== options ======*/ public void optsAddTitle(String title) {} public void optsAddNumber(String varname,String title,String desc, int decimals, double lower,double upper,double step, double initial) {} public void optsAddBoolean(String varname,String title,String desc, boolean initial) {} public void optsAddEnum(String varname,String title,String desc, String [] values, int initial) {} public void optsAddKey(String varname,String title,String desc,int initial){ } public void optsAddString(String varname,String title,String desc, int maxlen, boolean isPassword, String initial) {} public void optsClear() {} }