// // Rocket Science 101 Version 1.1 - Copyright (C) 1996, 2001 by Lorenz Wiest // // // // // "Contrary to popular opinion, filename is not from the English terms // name and file. It is pronounced 'feh-LEE-nah-mee' and comes from the // Tibetan, meaning 'What it is ?'" // -- David Swift on CompuServe // // // "Three things are certain: // Death, taxes, and lost data. // Guess which has occurred." // // "Chaos reigns within. // Reflect, repent, and reboot. // Order shall return." // // "Yesterday it worked, // Today it is not working. // The web is like that." // // "Serious error. // All shortcuts have disappeared. // Screen. Mind. Both are blank." // // "To have no errors // Would be life without meaning. // No struggle, no joy." // // -- Error Messages of the BeOS's web-browser (actually Japanese haiku!) // // // GNU License // ============================================================================= // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 2 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. // // You should have received a copy of the GNU General Public License along with // this program; if not, write to the Free Software Foundation, Inc., 675 Mass // Ave, Cambridge, MA 02139, USA. // // // Notes // ============================================================================= // - fix in Gadget class // - Fixed superfluous image reload in run() // - Code is Java 1.0.2 compliant and should run on any Java-enabled web browser. // import java.applet.*; import java.awt.*; import java.awt.image.*; //////////////////////////////////////////////////////////////////////////////// // // // RocketScience // // // //////////////////////////////////////////////////////////////////////////////// public final class RocketScience extends Applet implements Runnable, Sleeper { final static int WIDTH = 641; final static int HEIGHT = 481; final static int MX = WIDTH / 2; final static int MY = HEIGHT / 2; final static int DELTAT = 20; // timespan [s] per timestep final static int TSTEPS = 40; // timesteps between frames final static float ZOOM = (float)(150.0 / Spacecraft.GEOSTATIONARY_RADIUS); final static int NOTE_TIMERID = 1; final static int DEMO_TIMERID = 2; final static int SOYUZ_TIMERID = 3; final static int DEMO_TIME = 120; // in [s] final static int SOYUZ_LAUNCH_TIME = 5; final static int NOTE_TIME = 2; Image offScreen; Graphics offScreenGC; Rectangle offScreenClipRect; Spacecraft Atlantis; Spacecraft Soyuz; MirSpacecraft Mir; Bob AtlantisBob; MirBob MirBob; Bob SoyuzBob; Bob AtlantisJetBob; Bob SoyuzJetBob; Bob DotsBob; Bob EarthBob; Bob NoteBob; Bob AtlantisLabelBob; Bob MirLabelBob; Bob SoyuzLabelBob; Bob TachoBob; Image stripMirImage; Image stripAtlantisImage; Image stripSoyuzImage; Image stripJetImage; Image stripDotsImage; Image stripEarthImage; Image stripNoteImage; Image stripLabelsImage; Image stripTachoImage; Gadget StartGadget; Gadget BrakeGadget; Gadget SpeedGadget; Gadget ClearGadget; Image stripStartGadget; Image stripBrakeGadget; Image stripSpeedGadget; Image stripClearGadget; Timer NoteTimer; Timer DemoTimer; Timer SoyuzTimer; boolean InDemoMode = true; boolean DoResetGame = true; boolean DoRejectMouseEvents = true; Autopilot Autopilot = new Autopilot(); DirtyRect dirtyRect; Thread gameThread = null; boolean AllImagesLoaded = false; //*** Applet ****************************************************************** public String getAppletInfo() { return("RocketScience 101 Version 1.1 - Build 030201 - Copyright 1996, 2001 by Lorenz Wiest"); } //*** Applet ****************************************************************** public void start() { // Initialize backbuffer if(offScreenGC==null) { offScreen = this.createImage(WIDTH, HEIGHT); offScreenGC = offScreen.getGraphics(); offScreenClipRect = new Rectangle(0, 0, WIDTH, HEIGHT); } // Initialize spacecrafts Atlantis = new Spacecraft(0, 0, 0, 0, true); Soyuz = new Spacecraft(0, 0, 0, 0, false); Mir = new MirSpacecraft(0, 0, 0, 0, true); // Init Timer NoteTimer = new Timer(this, NOTE_TIMERID ); DemoTimer = new Timer(this, DEMO_TIMERID ); SoyuzTimer = new Timer(this, SOYUZ_TIMERID); // Init dirtyRect dirtyRect = new DirtyRect(); InDemoMode = true; DoResetGame = true; DoRejectMouseEvents = true; if(gameThread==null) { gameThread = new Thread(this, "RocketScience 101 Game Thread"); gameThread.start(); } } //*** Applet ****************************************************************** public void stop() { if(gameThread!=null && gameThread.isAlive()) gameThread.stop(); gameThread = null; SoyuzTimer.stop(); DemoTimer.stop(); NoteTimer.stop(); } //*** Applet ****************************************************************** public void run() { final int FRAME_SLEEP = 50; // Initialize backbuffer if(offScreenGC==null) { offScreen = this.createImage(WIDTH, HEIGHT); offScreenGC = offScreen.getGraphics(); offScreenClipRect = new Rectangle(0, 0, WIDTH, HEIGHT); } // Load image strips via MediaTracker if(AllImagesLoaded==false) { offScreenGC.setColor(Color.white); offScreenGC.fillRect(0, 0, WIDTH, HEIGHT); stripMirImage = this.getImage(this.getCodeBase(), "mir.gif"); stripAtlantisImage = this.getImage(this.getCodeBase(), "atlantis.gif"); stripSoyuzImage = this.getImage(this.getCodeBase(), "soyuz.gif"); stripJetImage = this.getImage(this.getCodeBase(), "jet.gif"); stripDotsImage = this.getImage(this.getCodeBase(), "dots.gif"); stripEarthImage = this.getImage(this.getCodeBase(), "earth.gif"); stripNoteImage = this.getImage(this.getCodeBase(), "note.gif"); stripLabelsImage = this.getImage(this.getCodeBase(), "labels.gif"); stripTachoImage = this.getImage(this.getCodeBase(), "tacho.gif"); stripStartGadget = this.getImage(this.getCodeBase(), "reset.gif"); stripBrakeGadget = this.getImage(this.getCodeBase(), "brake.gif"); stripSpeedGadget = this.getImage(this.getCodeBase(), "speed.gif"); stripClearGadget = this.getImage(this.getCodeBase(), "clear.gif"); MediaTracker tracker = new MediaTracker(this); tracker.addImage(stripMirImage, 0); tracker.addImage(stripAtlantisImage, 1); tracker.addImage(stripSoyuzImage, 2); tracker.addImage(stripJetImage, 3); tracker.addImage(stripDotsImage, 4); tracker.addImage(stripEarthImage, 5); tracker.addImage(stripNoteImage, 6); tracker.addImage(stripLabelsImage, 7); tracker.addImage(stripStartGadget, 8); tracker.addImage(stripBrakeGadget, 9); tracker.addImage(stripSpeedGadget, 10); tracker.addImage(stripClearGadget, 11); tracker.addImage(stripTachoImage, 12); for(int i=0; i<13; i++) { try { tracker.waitForID(i); } catch (InterruptedException e) {} paintTracker( 100 * i / (13-1)); } AllImagesLoaded = true; } // Initialize bobs initBobs(); MirBob.setFrameRow(MIRBOB_ROW_1SOYUZ); Autopilot.setSoyuzLaunchCount(2); // Initialize gadgets StartGadget = new Gadget(155, 450, stripStartGadget, offScreen); BrakeGadget = new Gadget(255, 450, stripBrakeGadget, offScreen); SpeedGadget = new Gadget(328, 450, stripSpeedGadget, offScreen); ClearGadget = new Gadget(428, 450, stripClearGadget, offScreen); // Game loop starts here InDemoMode = true; DoRejectMouseEvents = false; DoResetGame = true; boolean saveDemoMode; while(Thread.currentThread()==gameThread) { if(DoResetGame) resetGame(); DoResetGame = false; if(InDemoMode) enterDemoMode(); else exitDemoMode(); saveDemoMode = InDemoMode; while(!DoResetGame && saveDemoMode==InDemoMode) { Autopilot.nextManeuver(MirBob, SoyuzBob, Mir, Soyuz, DELTAT); if((!Mir.blockAtlantisDocking()) && (!InDemoMode) && (!Atlantis.hasDocked())) handleAtlantisRendezvous(); for(int j=0; j SENSERADIUS_SQR) { if(NoteBob.isVisible() && NoteBob.getFrameRow()!=NOTEBOB_ROW_OK4DOCKING) killNote(); } else { float dvx = Mir.vx - Atlantis.vx; // dvx, dvy are in Atlantis-centered CS float dvy = Mir.vy - Atlantis.vy; float dot = dvx * dx + dvy * dy; if(dot < 0) { // ATLANTIS moves *TOWARD* MIR float approachVelocity = (float)Math.sqrt(dvx * dvx + dvy * dvy); int percent = (int)(approachVelocity / CONVERT_TO_PERCENT); if(percent>100) percent = 100; if(percent<20) { setNote(NOTEBOB_ROW_LOOKSGOOD); paintTachoBob(TACHOBOB_ROW_GREEN, percent); TachoBob.setVisible(true); } else if(percent<40) { setNote(NOTEBOB_ROW_LOOKSGOOD); paintTachoBob(TACHOBOB_ROW_ORANGE, percent); TachoBob.setVisible(true); } else { setNote(NOTEBOB_ROW_TOOFAST); paintTachoBob(TACHOBOB_ROW_RED, percent); TachoBob.setVisible(true); } if(percent<20 && distMA_sqr < DOCKINGRADIUS_SQR) { setNote(NOTEBOB_ROW_DOCKED); Atlantis.setDocked(true); disableGadgets(); } } } } //****************************************************************************** private synchronized void paintTachoBob(int rowIndex, int percent) { blit(TachoBob.stripImage, 0, 0, stripTachoImage, 0, -TACHOBOB_ROW_MASTER * TachoBob.bitmapHeight, TachoBob.width, TachoBob.height); int pixelPercentage = (int)(percent * 0.01 * TachoBob.width); blit(TachoBob.stripImage, 0, 0, stripTachoImage, 0, -rowIndex * TachoBob.bitmapHeight, pixelPercentage, TachoBob.height); int markerPos = pixelPercentage - TACHOBOB_MARKERWIDTH / 2 ; blit(TachoBob.stripImage, markerPos, 0, stripTachoImage, markerPos, -TACHOBOB_ROW_MARKER * TachoBob.bitmapHeight, TACHOBOB_MARKERWIDTH, TachoBob.height); } //****************************************************************************** public void updateBobPositions(Autopilot Autopilot) { final int PIXEL_SENSERADIUS_SQR = 100 * 100; int dx; int dy; int distAM_sqr; MirBob.x = (int)(MX + Mir.x * ZOOM + MirBob.offsetX); MirBob.y = (int)(MY - Mir.y * ZOOM + MirBob.offsetY); // Calculate Atlantis Bob position if(Atlantis.hasDocked()) { AtlantisBob.x = MirBob.x + MirBob.transX[MirBob.getFrameNr()]; AtlantisBob.y = MirBob.y + MirBob.transY[MirBob.getFrameNr()]; AtlantisJetBob.setVisible(false); if(MirBob.getFrameNr()!=AtlantisBob.getFrameNr()) AtlantisBob.setFrameNr(MirBob.getFrameNr()); } else { AtlantisBob.x = (int)(MX + Atlantis.x * ZOOM + AtlantisBob.offsetX); AtlantisBob.y = (int)(MY - Atlantis.y * ZOOM + AtlantisBob.offsetY); if(!Mir.blockAtlantisDocking()) { dx = MirBob.x - MirBob.offsetX - (AtlantisBob.x - AtlantisBob.offsetX); dy = -(MirBob.y - MirBob.offsetY - (AtlantisBob.y - AtlantisBob.offsetY)); if((dx * dx + dy * dy) < PIXEL_SENSERADIUS_SQR) AtlantisBob.setFrameNr(-dx, -dy); } // Calc Atlantis Jet position if(Atlantis.thrust!= Spacecraft.NO_THRUST) { AtlantisJetBob.setVisible(true); AtlantisJetBob.x = AtlantisBob.x - AtlantisBob.offsetX + AtlantisJetBob.offsetX; AtlantisJetBob.y = AtlantisBob.y - AtlantisBob.offsetY + AtlantisJetBob.offsetY; AtlantisJetBob.setFrameNr(-Atlantis.thrustX, -Atlantis.thrustY); } else AtlantisJetBob.setVisible(false); } // Calc Soyuz position if(SoyuzBob.isVisible()) { SoyuzBob.x = (int)(MX + Soyuz.x * ZOOM + SoyuzBob.offsetX); SoyuzBob.y = (int)(MY - Soyuz.y * ZOOM + SoyuzBob.offsetY); dx = MirBob.x - MirBob.offsetX - (SoyuzBob.x - SoyuzBob.offsetX); dy = -(MirBob.y - MirBob.offsetY - (SoyuzBob.y - SoyuzBob.offsetY)); if((dx * dx + dy * dy) < PIXEL_SENSERADIUS_SQR) SoyuzBob.setFrameNr(dx, dy); if(Soyuz.thrust>Spacecraft.NO_THRUST) SoyuzBob.setFrameNr(-Autopilot.soyuzVelocityDirX, -Autopilot.soyuzVelocityDirY); } // Calc Soyuz Jets position if(Soyuz.thrust!=Spacecraft.NO_THRUST) { Soyuz.thrust--; SoyuzJetBob.setVisible(true); SoyuzJetBob.x = (int)(MX + Soyuz.x * ZOOM + SoyuzJetBob.offsetX); SoyuzJetBob.y = (int)(MY - Soyuz.y * ZOOM + SoyuzJetBob.offsetY); SoyuzJetBob.setFrameNr(Autopilot.soyuzVelocityDirX, Autopilot.soyuzVelocityDirY); } else SoyuzJetBob.setVisible(false); // Calc Mir position dx = AtlantisBob.x - AtlantisBob.offsetX - (MirBob.x - MirBob.offsetX); dy = -(AtlantisBob.y - AtlantisBob.offsetY - (MirBob.y - MirBob.offsetX)); distAM_sqr = dx * dx + dy * dy; if((!Mir.blockAtlantisDocking()) && (distAM_sqr < PIXEL_SENSERADIUS_SQR) && (!Atlantis.hasDocked())) MirBob.setFrameNr(dx, dy); else { if(SoyuzBob.isVisible()) { dx = SoyuzBob.x - SoyuzBob.offsetX - (MirBob.x - MirBob.offsetX); dy = -(SoyuzBob.y - SoyuzBob.offsetY - (MirBob.y - MirBob.offsetY)); if((dx * dx + dy * dy) < PIXEL_SENSERADIUS_SQR) { if(Autopilot.getManeuverType()==Autopilot.OM_HIDESOYUZ) { if(Autopilot.getManeuverClass()==Autopilot.OMC_LODOCK) { MirBob.setFrameNr(dy, -dx); if((distAM_sqr < PIXEL_SENSERADIUS_SQR) && (!InDemoMode)) setNote(NOTEBOB_ROW_SOYUZ28); } if(Autopilot.getManeuverClass()==Autopilot.OMC_HIDOCK) { MirBob.setFrameNr(-dy, dx); if((distAM_sqr < PIXEL_SENSERADIUS_SQR) && (!InDemoMode)) setNote(NOTEBOB_ROW_SOYUZ10); } } if(Autopilot.getManeuverClass()==Autopilot.OMC_LOSEPR) MirBob.setFrameNr( dy, -dx); if(Autopilot.getManeuverClass()==Autopilot.OMC_HISEPR) MirBob.setFrameNr(-dy, dx); } } } // Calc Note position if(NoteBob.isVisible()) { if(AtlantisBob.x < MirBob.x) { NoteBob.x = MirBob.x + MirBob.width; NoteBob.y = MirBob.y - NOTEBOB_HALFHEIGHT; NoteBob.setFrameNr(NOTEBOB_FRM_RIGHT); } else { NoteBob.x = MirBob.x - NoteBob.width; NoteBob.y = MirBob.y - NOTEBOB_HALFHEIGHT; NoteBob.setFrameNr(NOTEBOB_FRM_LEFT); } } // Calc Tacho position if(TachoBob.isVisible()) { if(AtlantisBob.x < MirBob.x) { TachoBob.x = NoteBob.x + TACHOBOB_NOTEOFFSET_RIGHT; TachoBob.y = NoteBob.y + TACHOBOB_NOTEOFFSET_DOWN; } else { TachoBob.x = NoteBob.x + TACHOBOB_NOTEOFFSET_LEFT; TachoBob.y = NoteBob.y + TACHOBOB_NOTEOFFSET_DOWN; } } // Calc DemoMode labels position if(InDemoMode) { AtlantisLabelBob.x = AtlantisBob.x + AtlantisBob.width; AtlantisLabelBob.y = AtlantisBob.y - LABELBOB_HALFHEIGHT; MirLabelBob.x = MirBob.x + MirBob.width; MirLabelBob.y = MirBob.y - LABELBOB_HALFHEIGHT; if(SoyuzBob.isVisible()) { SoyuzLabelBob.x = SoyuzBob.x + SoyuzBob.width; SoyuzLabelBob.y = SoyuzBob.y - LABELBOB_HALFHEIGHT; if((Autopilot.getManeuverClass()==Autopilot.OMC_HIDOCK) || (Autopilot.getManeuverClass()==Autopilot.OMC_HISEPR) ) SoyuzLabelBob.setFrameRow(LABELBOB_ROW_SOYUZ28); else SoyuzLabelBob.setFrameRow(LABELBOB_ROW_SOYUZ10); } } EarthBob.cycleFrameNr(); } /******************************************************************************* * * * Core Render Engine * * * *******************************************************************************/ public final void blit(Image destImage, int destX, int destY, Image srcImage, int srcX, int srcY, int w, int h) { blit(destImage.getGraphics(), destX, destY, srcImage, srcX, srcY, w, h); } public final void blit(Graphics destGC, int destX, int destY, Image srcImage, int srcX, int srcY, int w, int h) { Graphics g = destGC.create(); g.clipRect(destX, destY, w, h); g.drawImage(srcImage, srcX, srcY, null); g.dispose(); } /******************************************************************************* * * * Bob Render Engine * * * *******************************************************************************/ final static int ATLANTISBOB_WIDTH = 39; final static int ATLANTISBOB_HEIGHT = 39; final static int ATLANTISBOB_OFFSETX = -19; final static int ATLANTISBOB_OFFSETY = -19; final static int ATLANTISBOB_BITMAPWIDTH = 40; final static int ATLANTISBOB_BITMAPHEIGHT = 0; final static int ATLANTISBOB_COLSOFFRAMES = 16; final static int SOYUZBOB_WIDTH = 21; final static int SOYUZBOB_HEIGHT = 21; final static int SOYUZBOB_OFFSETX = -10; final static int SOYUZBOB_OFFSETY = -10; final static int SOYUZBOB_BITMAPWIDTH = 22; final static int SOYUZBOB_BITMAPHEIGHT = 0; final static int SOYUZBOB_COLSOFFRAMES = 16; final static int MIRBOB_WIDTH = 45; final static int MIRBOB_HEIGHT = 45; final static int MIRBOB_OFFSETX = -22; final static int MIRBOB_OFFSETY = -22; final static int MIRBOB_BITMAPWIDTH = 46; final static int MIRBOB_BITMAPHEIGHT = 46; final static int MIRBOB_COLSOFFRAMES = 16; public final static int MIRBOB_ROW_0SOYUZ = 0; // stored in MirBob.frameRow public final static int MIRBOB_ROW_1SOYUZ = 1; public final static int MIRBOB_ROW_2SOYUZ = 2; final static int ATLANTISJETBOB_WIDTH = 43; final static int ATLANTISJETBOB_HEIGHT = 43; final static int ATLANTISJETBOB_OFFSETX = -21; final static int ATLANTISJETBOB_OFFSETY = -21; final static int ATLANTISJETBOB_BITMAPWIDTH = 44; final static int ATLANTISJETBOB_BITMAPHEIGHT = 0; final static int ATLANTISJETBOB_COLSOFFRAMES = 16; final static int SOYUZJETBOB_WIDTH = 43; final static int SOYUZJETBOB_HEIGHT = 43; final static int SOYUZJETBOB_OFFSETX = -21; final static int SOYUZJETBOB_OFFSETY = -21; final static int SOYUZJETBOB_BITMAPWIDTH = 44; final static int SOYUZJETBOB_BITMAPHEIGHT = 0; final static int SOYUZJETBOB_COLSOFFRAMES = 16; final static int DOTSBOB_WIDTH = 6; final static int DOTSBOB_HEIGHT = 6; final static int DOTSBOB_OFFSETX = -2; // Might be a little off final static int DOTSBOB_OFFSETY = -2; // Might be a little off final static int DOTSBOB_BITMAPWIDTH = 7; final static int DOTSBOB_BITMAPHEIGHT = 7; final static int DOTSBOB_COLSOFFRAMES = 3; public final static int DOT_FRM_ATLANTIS = 0; // stored in DotBob.frameNr public final static int DOT_FRM_ATLANTIS_THRUSTS = 1; public final static int DOT_FRM_MIR = 2; final static int EARTHBOB_WIDTH = 47; final static int EARTHBOB_HEIGHT = 47; final static int EARTHBOB_OFFSETX = -24; final static int EARTHBOB_OFFSETY = -24; final static int EARTHBOB_BITMAPWIDTH = 47; final static int EARTHBOB_BITMAPHEIGHT = 0; final static int EARTHBOB_COLSOFFRAMES = 16; final static float EARTHBOB_RADIUS = EARTHBOB_WIDTH / ( 2 * ZOOM ); // ??? final static int NOTEBOB_WIDTH = 134; final static int NOTEBOB_HEIGHT = 17; final static int NOTEBOB_OFFSETX = 0; final static int NOTEBOB_OFFSETY = 0; final static int NOTEBOB_BITMAPWIDTH = 134; final static int NOTEBOB_BITMAPHEIGHT = 17; final static int NOTEBOB_COLSOFFRAMES = 2; final static int NOTEBOB_HALFHEIGHT = 9; final static int NOTEBOB_FRM_RIGHT = 0; // stored in NoteBob.frameNr final static int NOTEBOB_FRM_LEFT = 1; final static int NOTEBOB_ROW_OK4DOCKING = 0; // stored in NoteBob.frameRow final static int NOTEBOB_ROW_SOYUZ10 = 1; final static int NOTEBOB_ROW_SOYUZ28 = 2; final static int NOTEBOB_ROW_TOOCLOSE = 3; final static int NOTEBOB_ROW_TOOFAST = 4; final static int NOTEBOB_ROW_DRIFTING = 5; final static int NOTEBOB_ROW_LOOKSGOOD = 6; final static int NOTEBOB_ROW_DOCKED = 7; final static int TACHOBOB_WIDTH = 124; final static int TACHOBOB_HEIGHT = 10; final static int TACHOBOB_OFFSETX = 0; final static int TACHOBOB_OFFSETY = 0; final static int TACHOBOB_BITMAPWIDTH = 0; final static int TACHOBOB_BITMAPHEIGHT = 10; final static int TACHOBOB_COLSOFFRAMES = 1; final static int TACHOBOB_ROW_MASTER = 1; // stored in TachoBob.frameRow final static int TACHOBOB_ROW_RED = 2; final static int TACHOBOB_ROW_ORANGE = 3; final static int TACHOBOB_ROW_GREEN = 4; final static int TACHOBOB_ROW_MARKER = 5; final static int TACHOBOB_MARKERWIDTH = 7; final static int TACHOBOB_NOTEOFFSET_LEFT = 0; // offsets from NoteBob.x final static int TACHOBOB_NOTEOFFSET_RIGHT = 10; // offsets from NoteBob.x final static int TACHOBOB_NOTEOFFSET_DOWN = 21; // offsets from NoteBob.y final static int LABELBOB_WIDTH = 121; final static int LABELBOB_HEIGHT = 17; final static int LABELBOB_OFFSETX = 0; final static int LABELBOB_OFFSETY = 0; final static int LABELBOB_BITMAPWIDTH = 121; final static int LABELBOB_BITMAPHEIGHT = 17; final static int LABELBOB_COLSOFFRAMES = 2; final static int LABELBOB_HALFHEIGHT = 9; final static int LABELBOB_ROW_ATLANTIS = 0; // stored in AtlantisLabelBob.frameRow final static int LABELBOB_ROW_MIR = 1; // stored in MirLabelBob.frameRow final static int LABELBOB_ROW_SOYUZ10 = 2; // stored in SoyuzLabelBob.frameRow final static int LABELBOB_ROW_SOYUZ28 = 3; // stored in SoyuzLabelBob.frameRow //****************************************************************************** public synchronized void drawBobs() { dirtyRect.beginClipRect(); AtlantisLabelBob.lift(dirtyRect); MirLabelBob.lift(dirtyRect); SoyuzLabelBob.lift(dirtyRect); if(!SoyuzBob.isVisible()) SoyuzLabelBob.setInUse(false); NoteBob.lift(dirtyRect); TachoBob.lift(dirtyRect); MirBob.lift(dirtyRect); MirBob.setInUse(true); AtlantisBob.lift(dirtyRect); AtlantisBob.setInUse(true); AtlantisJetBob.lift(dirtyRect); SoyuzBob.lift(dirtyRect); SoyuzJetBob.lift(dirtyRect); EarthBob.lift(dirtyRect); if(!InDemoMode) dropDots(); EarthBob.save(); SoyuzJetBob.save(); SoyuzBob.save(); AtlantisJetBob.save(); AtlantisBob.save(); MirBob.save(); NoteBob.save(); TachoBob.save(); if(SoyuzBob.isVisible()) SoyuzLabelBob.save(); MirLabelBob.save(); AtlantisLabelBob.save(); EarthBob.drop(dirtyRect); SoyuzJetBob.drop(dirtyRect); SoyuzBob.drop(dirtyRect); AtlantisJetBob.drop(dirtyRect); AtlantisBob.drop(dirtyRect); MirBob.drop(dirtyRect); TachoBob.drop(dirtyRect); NoteBob.drop(dirtyRect); if(SoyuzBob.isVisible()) SoyuzLabelBob.drop(dirtyRect); MirLabelBob.drop(dirtyRect); AtlantisLabelBob.drop(dirtyRect); StartGadget.draw(); BrakeGadget.draw(); SpeedGadget.draw(); ClearGadget.draw(); dirtyRect.addClipRect(StartGadget.getRectangle()); dirtyRect.addClipRect(BrakeGadget.getRectangle()); dirtyRect.addClipRect(SpeedGadget.getRectangle()); dirtyRect.addClipRect(ClearGadget.getRectangle()); offScreenClipRect = dirtyRect.endClipRect(); } //****************************************************************************** public void dropDots() { if(!Atlantis.hasDocked()) { DotsBob.x = AtlantisBob.x - AtlantisBob.offsetX + DotsBob.offsetX; DotsBob.y = AtlantisBob.y - AtlantisBob.offsetY + DotsBob.offsetY; if(AtlantisJetBob.isVisible()) DotsBob.drop(dirtyRect, DOT_FRM_ATLANTIS_THRUSTS); else DotsBob.drop(dirtyRect, DOT_FRM_ATLANTIS); } if(Mir.doTrail()) { DotsBob.x = MirBob.x - MirBob.offsetX + DotsBob.offsetX; DotsBob.y = MirBob.y - MirBob.offsetY + DotsBob.offsetY; DotsBob.drop(dirtyRect, DOT_FRM_MIR); } } //****************************************************************************** public synchronized void initBobs() { AtlantisBob = new Bob( ATLANTISBOB_WIDTH, // width ATLANTISBOB_HEIGHT, // height ATLANTISBOB_COLSOFFRAMES, // colsOfFrames stripAtlantisImage, // stripImage ATLANTISBOB_OFFSETX, // offsetX ATLANTISBOB_OFFSETY, // offsetY ATLANTISBOB_BITMAPWIDTH, // bitmapWidth ATLANTISBOB_BITMAPHEIGHT, // bitmapheight true, offScreen, this); SoyuzBob = new Bob( SOYUZBOB_WIDTH, // width SOYUZBOB_HEIGHT, // height SOYUZBOB_COLSOFFRAMES, // colsOfFrames stripSoyuzImage, // stripImage SOYUZBOB_OFFSETX, // offsetX SOYUZBOB_OFFSETY, // offsetY SOYUZBOB_BITMAPWIDTH, // bitmapWidth SOYUZBOB_BITMAPHEIGHT, // bitmapheight false, offScreen, this); MirBob = new MirBob( MIRBOB_WIDTH, // width MIRBOB_HEIGHT, // height MIRBOB_COLSOFFRAMES, // colsOfFrames stripMirImage, // stripImage MIRBOB_OFFSETX, // offsetX MIRBOB_OFFSETY, // offsetY MIRBOB_BITMAPWIDTH, // bitmapWidth MIRBOB_BITMAPHEIGHT, // bitmapheight true, offScreen, this); AtlantisJetBob = new Bob( ATLANTISJETBOB_WIDTH, // width ATLANTISJETBOB_HEIGHT, // height ATLANTISJETBOB_COLSOFFRAMES, // colsOfFrames stripJetImage, // stripImage ATLANTISJETBOB_OFFSETX, // offsetX ATLANTISJETBOB_OFFSETY, // offsetY ATLANTISJETBOB_BITMAPWIDTH, // bitmapWidth ATLANTISJETBOB_BITMAPHEIGHT, // bitmapheight false, offScreen, this ); SoyuzJetBob = new Bob( SOYUZJETBOB_WIDTH, // width SOYUZJETBOB_HEIGHT, // height SOYUZJETBOB_COLSOFFRAMES, // colsOfFrames stripJetImage, // stripImage SOYUZJETBOB_OFFSETX, // offsetX SOYUZJETBOB_OFFSETY, // offsetY SOYUZJETBOB_BITMAPWIDTH, // bitmapWidth SOYUZJETBOB_BITMAPHEIGHT, // bitmapheight false, offScreen, this ); DotsBob = new Bob( DOTSBOB_WIDTH, // width DOTSBOB_HEIGHT, // height DOTSBOB_COLSOFFRAMES, // colsOfFrames stripDotsImage, // stripImage DOTSBOB_OFFSETX, // offsetX DOTSBOB_OFFSETY, // offsetY DOTSBOB_BITMAPWIDTH, // bitmapWidth DOTSBOB_BITMAPHEIGHT, // bitmapheight true, offScreen, this ); EarthBob = new Bob( EARTHBOB_WIDTH, // width EARTHBOB_HEIGHT, // height EARTHBOB_COLSOFFRAMES, // colsOfFrames stripEarthImage, // stripImage EARTHBOB_OFFSETX, // offsetX EARTHBOB_OFFSETY, // offsetY EARTHBOB_BITMAPWIDTH, // bitmapWidth EARTHBOB_BITMAPHEIGHT, // bitmapheight true, offScreen, this ); EarthBob.x = MX + EARTHBOB_OFFSETX; EarthBob.y = MY + EARTHBOB_OFFSETY; EarthBob.setFrameNr(0); NoteBob = new Bob( NOTEBOB_WIDTH, // width NOTEBOB_HEIGHT, // height NOTEBOB_COLSOFFRAMES, // colsOfFrames stripNoteImage, // stripImage NOTEBOB_OFFSETX, // offsetX NOTEBOB_OFFSETY, // offsetY NOTEBOB_BITMAPWIDTH, // bitmapWidth NOTEBOB_BITMAPHEIGHT, // bitmapheight false, offScreen, this ); TachoBob = new Bob( TACHOBOB_WIDTH, // width TACHOBOB_HEIGHT, // height TACHOBOB_COLSOFFRAMES, // colsOfFrames this.createImage(TACHOBOB_WIDTH, TACHOBOB_HEIGHT), TACHOBOB_OFFSETX, // offsetX TACHOBOB_OFFSETY, // offsetY TACHOBOB_BITMAPWIDTH, // bitmapWidth TACHOBOB_BITMAPHEIGHT, // bitmapheight false, offScreen, this ); AtlantisLabelBob = new Bob( LABELBOB_WIDTH, // width LABELBOB_HEIGHT, // height LABELBOB_COLSOFFRAMES, // colsOfFrames stripLabelsImage, // stripImage LABELBOB_OFFSETX, // offsetX LABELBOB_OFFSETY, // offsetY LABELBOB_WIDTH, // bitmapWidth LABELBOB_HEIGHT, // bitmapheight false, offScreen, this ); SoyuzLabelBob = new Bob( LABELBOB_WIDTH, // width LABELBOB_HEIGHT, // height LABELBOB_COLSOFFRAMES, // colsOfFrames stripLabelsImage, // stripImage LABELBOB_OFFSETX, // offsetX LABELBOB_OFFSETY, // offsetY LABELBOB_WIDTH, // bitmapWidth LABELBOB_HEIGHT, // bitmapheight false, offScreen, this ); MirLabelBob = new Bob( LABELBOB_WIDTH, // width LABELBOB_HEIGHT, // height LABELBOB_COLSOFFRAMES, // colsOfFrames stripLabelsImage, // stripImage LABELBOB_OFFSETX, // offsetX LABELBOB_OFFSETY, // offsetY LABELBOB_WIDTH, // bitmapWidth LABELBOB_HEIGHT, // bitmapheight false, offScreen, this ); } } //////////////////////////////////////////////////////////////////////////////// // // // AutopilotTable // // // //////////////////////////////////////////////////////////////////////////////// // A helper class for Autopilot final class AutopilotTable { float alfa; float semiAxis; int maneuverType; public AutopilotTable() { } public AutopilotTable(float alfa, float semiAxis, int maneuverType) { this.alfa = alfa; this.semiAxis = semiAxis; this.maneuverType = maneuverType; } } //////////////////////////////////////////////////////////////////////////////// // // // Autopilot // // // //////////////////////////////////////////////////////////////////////////////// // Autopilot as a table-driven Finite State Machine -- still pretty messy! final class Autopilot { final static float DEG2RAD = (float)(Math.PI / 180.0); final static float RAD2DEG = (float)(180.0 / Math.PI); final static int OM_CLASSES = 4; // number of orbital maneuver classes final static int OMC_LODOCK = 0; // maneuver class IDs final static int OMC_LOSEPR = 1; final static int OMC_HIDOCK = 2; final static int OMC_HISEPR = 3; final static int OM_STEPS = 8; // number of steps of each orbital meneuver final static int OM_INITLAUNCH = 0; // maneuver step IDs final static int OM_MANEUVER = 1; final static int OM_RENDEZVOUS = 2; final static int OM_HIDESOYUZ = 3; final static int OM_DOCKING = 4; final static int OM_SEPARATION = 5; final static int OM_SHOWSOYUZ = 6; final static int OM_CLEANUPDESCENT = 7; final static int OM_NOP = 8; final static int NIL = -1; final static int SOYUZTHRUST_NUM_FRAMES = 10; // soyuz jet is shown for this # of screen frames private int OMIndex; private boolean OMPending; private int OMClass; private float TD; private float deltaTD; private boolean readyJets; private int soyuzLaunchCount; public float soyuzVelocityDirX; public float soyuzVelocityDirY; private AutopilotTable OM; // the current orbital maneuver private AutopilotTable OMTable[][]; //////////////////////////////////////////////////////////////////////////////// public Autopilot() { OM = new AutopilotTable(); OMTable = new AutopilotTable[OM_CLASSES][OM_STEPS]; // Fill tables for automatic high/low orbit docking/separation procedures float R = Spacecraft.GEOSTATIONARY_RADIUS; float GROUND_ORBIT = (float)(1.0 / 2.0 * 2.0 / 7.0 * R); float TRANSFER_PARK_TO_GROUND = (float)(1.0 / 2.0 * (1.0 / 2.0 + 1.0 / 7.0) * R); float PARK_ORBIT = (float)(1.0 / 2.0 * R); float TRANSFER_HIGH_TO_PARK = (float)(1.0 / 2.0 * (6.0 / 5.0 + 1.0 / 2.0) * R); float HIGH_ORBIT = (float)(6.0 / 5.0 * R); float TRANSFER_MIR_TO_HIGH = (float)(1.0 / 2.0 * (6.0 / 5.0 + 1.0) * R); float TRANSFER_LOW_TO_PARK = (float)(1.0 / 2.0 * (4.0 / 5.0 + 1.0 / 2.0) * R); float TRANSFER_MIR_TO_LOW = (float)(1.0 / 2.0 * (4.0 / 5.0 + 1.0) * R); float LOW_ORBIT = (float)(4.0 / 5.0 * R); float MIR_ORBIT = (float)(1.0 * R); float SPECIAL = (float)(1.0 / 7.0 * R); OMTable[OMC_HISEPR][0] = new AutopilotTable(NIL, NIL, OM_CLEANUPDESCENT); OMTable[OMC_HISEPR][1] = new AutopilotTable(NIL, GROUND_ORBIT, OM_MANEUVER ); OMTable[OMC_HISEPR][2] = new AutopilotTable(NIL, TRANSFER_PARK_TO_GROUND, OM_MANEUVER ); OMTable[OMC_HISEPR][3] = new AutopilotTable(NIL, PARK_ORBIT, OM_MANEUVER ); OMTable[OMC_HISEPR][4] = new AutopilotTable(NIL, TRANSFER_HIGH_TO_PARK, OM_MANEUVER ); OMTable[OMC_HISEPR][5] = new AutopilotTable(NIL, HIGH_ORBIT, OM_MANEUVER ); OMTable[OMC_HISEPR][6] = new AutopilotTable(NIL, NIL, OM_SHOWSOYUZ ); OMTable[OMC_HISEPR][7] = new AutopilotTable(NIL, TRANSFER_MIR_TO_HIGH, OM_SEPARATION ); OMTable[OMC_LOSEPR][0] = new AutopilotTable(NIL, NIL, OM_CLEANUPDESCENT); OMTable[OMC_LOSEPR][1] = new AutopilotTable(NIL, GROUND_ORBIT, OM_MANEUVER ); OMTable[OMC_LOSEPR][2] = new AutopilotTable(NIL, TRANSFER_PARK_TO_GROUND, OM_MANEUVER ); OMTable[OMC_LOSEPR][3] = new AutopilotTable(NIL, PARK_ORBIT, OM_MANEUVER ); OMTable[OMC_LOSEPR][4] = new AutopilotTable(NIL, TRANSFER_LOW_TO_PARK, OM_MANEUVER ); OMTable[OMC_LOSEPR][5] = new AutopilotTable(NIL, LOW_ORBIT, OM_MANEUVER ); OMTable[OMC_LOSEPR][6] = new AutopilotTable(NIL, NIL, OM_SHOWSOYUZ ); OMTable[OMC_LOSEPR][7] = new AutopilotTable(NIL, TRANSFER_MIR_TO_LOW, OM_SEPARATION ); OMTable[OMC_HIDOCK][0] = new AutopilotTable(NIL, NIL, OM_DOCKING ); OMTable[OMC_HIDOCK][1] = new AutopilotTable(NIL, MIR_ORBIT, OM_MANEUVER ); OMTable[OMC_HIDOCK][2] = new AutopilotTable(NIL, NIL, OM_HIDESOYUZ ); OMTable[OMC_HIDOCK][3] = new AutopilotTable(NIL, TRANSFER_MIR_TO_HIGH, OM_RENDEZVOUS); OMTable[OMC_HIDOCK][4] = new AutopilotTable(NIL, HIGH_ORBIT, OM_MANEUVER ); OMTable[OMC_HIDOCK][5] = new AutopilotTable(NIL, TRANSFER_HIGH_TO_PARK, OM_MANEUVER ); OMTable[OMC_HIDOCK][6] = new AutopilotTable(NIL, PARK_ORBIT, OM_MANEUVER ); OMTable[OMC_HIDOCK][7] = new AutopilotTable(SPECIAL, TRANSFER_PARK_TO_GROUND, OM_INITLAUNCH); OMTable[OMC_LODOCK][0] = new AutopilotTable(NIL, NIL, OM_DOCKING ); OMTable[OMC_LODOCK][1] = new AutopilotTable(NIL, MIR_ORBIT, OM_MANEUVER ); OMTable[OMC_LODOCK][2] = new AutopilotTable(NIL, NIL, OM_HIDESOYUZ ); OMTable[OMC_LODOCK][3] = new AutopilotTable(NIL, TRANSFER_MIR_TO_LOW, OM_RENDEZVOUS); OMTable[OMC_LODOCK][4] = new AutopilotTable(NIL, LOW_ORBIT, OM_MANEUVER ); OMTable[OMC_LODOCK][5] = new AutopilotTable(NIL, TRANSFER_LOW_TO_PARK, OM_MANEUVER ); OMTable[OMC_LODOCK][6] = new AutopilotTable(NIL, PARK_ORBIT, OM_MANEUVER ); OMTable[OMC_LODOCK][7] = new AutopilotTable(SPECIAL, TRANSFER_PARK_TO_GROUND, OM_INITLAUNCH); } //////////////////////////////////////////////////////////////////////////////// public void reset(MirSpacecraft Mir, Spacecraft Soyuz) { Soyuz.thrust = Spacecraft.NO_THRUST; Mir.setBlockAtlantisDocking(false); OMIndex = NIL; OMPending = false; for(int i=0; i 0) ? true : false; // readyJets if soyuz 180 deg from OM Point break; case OM_NOP : break; } } //////////////////////////////////////////////////////////////////////////////// public void navigate(MirBob MirBob, Bob SoyuzBob, MirSpacecraft Mir, Spacecraft Soyuz) { final float CATCHRADIUS_SQR = (8 * 8) / (RocketScience.ZOOM * RocketScience.ZOOM); float vh; float normdeltav; float alfa; float dx; float dy; if(OMPending==true) { switch(OM.maneuverType) { case OM_SHOWSOYUZ: dx = Mir.x - Soyuz.x; dy = Mir.y - Soyuz.y; if((dx * dx + dy * dy) > CATCHRADIUS_SQR) { SoyuzBob.setVisible(true); if(soyuzLaunchCount==2) MirBob.setFrameRow(RocketScience.MIRBOB_ROW_1SOYUZ); if(soyuzLaunchCount==3) MirBob.setFrameRow(RocketScience.MIRBOB_ROW_0SOYUZ); OMPending = false; } break; case OM_HIDESOYUZ: dx = Mir.x - Soyuz.x; dy = Mir.y - Soyuz.y; if((dx * dx + dy * dy) < CATCHRADIUS_SQR) { SoyuzBob.setVisible(false); if(soyuzLaunchCount==0) MirBob.setFrameRow(RocketScience.MIRBOB_ROW_1SOYUZ); if(soyuzLaunchCount==1) MirBob.setFrameRow(RocketScience.MIRBOB_ROW_2SOYUZ); OMPending = false; } break; case OM_RENDEZVOUS: TD = TD - deltaTD; if(TD<=0) { Mir.setBlockAtlantisDocking(true); if(OMClass==OMC_LODOCK) OMTable[OMC_LODOCK][1].alfa = trim360(atan360(Soyuz.x, Soyuz.y) + 180); if(OMClass==OMC_HIDOCK) OMTable[OMC_HIDOCK][1].alfa = trim360(atan360(Mir.x, Mir.y) + 180); vh = DeltaVHohmann(Soyuz, OM.semiAxis); Soyuz.v = (float)Math.sqrt(Soyuz.vx * Soyuz.vx + Soyuz.vy * Soyuz.vy); normdeltav = (vh / Soyuz.v) - 1; Soyuz.thrust = SOYUZTHRUST_NUM_FRAMES; soyuzVelocityDirX = (normdeltav < 0) ? Soyuz.vx : -Soyuz.vx; soyuzVelocityDirY = (normdeltav < 0) ? Soyuz.vy : -Soyuz.vy; Soyuz.vx += normdeltav * Soyuz.vx; Soyuz.vy += normdeltav * Soyuz.vy; OMPending = false; } break; case OM_MANEUVER: alfa = OM.alfa - atan360(Soyuz.x, Soyuz.y); if(alfa < -180) alfa += 360; if( (alfa > 0) && (readyJets==false) ) readyJets = true; if( (alfa < 0) && (readyJets==true) ) { vh = DeltaVHohmann(Soyuz, OM.semiAxis); Soyuz.v = (float)Math.sqrt(Soyuz.vx * Soyuz.vx + Soyuz.vy * Soyuz.vy); normdeltav = (vh / Soyuz.v) - 1; Soyuz.thrust = SOYUZTHRUST_NUM_FRAMES; soyuzVelocityDirX = (normdeltav < 0) ? Soyuz.vx : -Soyuz.vx; soyuzVelocityDirY = (normdeltav < 0) ? Soyuz.vy : -Soyuz.vy; Soyuz.vx += normdeltav * Soyuz.vx; Soyuz.vy += normdeltav * Soyuz.vy; OMPending = false; readyJets = false; } break; case OM_NOP: break; } } } /*** Propellerhead Info ******************************************************** * * * How to calculate the Hohmann transfer velocity vh * * * * In order to transfer a spacecraft from a circular orbit with radius r1 to * * reach an orbit of radius r2 you have to accelerate the spacecraft to the * * Hohmann velocity vh. With v2 as the velocity at the target orbit, use the * * conservation of moments * * * * vh * r1 = v2 * r2 -> v2 = vh * ( r1 / r2 ) (1) * * * * and the conservation of energy * * * * -g * m1 * m2 / r1 + 1/2 * m2 * vh^2 = -g * m1 * m2 / r2 + 1/2 * m2 * v2^2 * * * * where g is the gravitational constant, and m1 the mass of the Earth. * * Plug in Equ.(1), set r2 = 2 * a - r1, where a is the major semi-axis of the * * (Hohmann) transfer orbit ellipse. After a little algebra we receive * * * * vh^2 = 2 * g * m1 * ( 1 / r1 - 1 / a ) * * * * With the spacecraft already being on a circular orbit with velocity v1 the * * necessary change deltaV to vh is * * * * deltaV = vh - v1; * * * *******************************************************************************/ //////////////////////////////////////////////////////////////////////////////// private float DeltaVHohmann(Spacecraft s, float a) { float r1 = (float)Math.sqrt(s.x * s.x + s.y * s.y); float vh = (float)Math.sqrt(2 * Spacecraft.GM / r1 - Spacecraft.GM / a); return(vh); } //////////////////////////////////////////////////////////////////////////////// public void triggerSoyuz() { if(OMIndex==NIL) { soyuzLaunchCount++; if(soyuzLaunchCount==4) soyuzLaunchCount = 0; switch(soyuzLaunchCount) { case 0 : OMClass = OMC_LODOCK; break; case 1 : OMClass = OMC_HIDOCK; break; case 2 : OMClass = OMC_HISEPR; break; case 3 : OMClass = OMC_LOSEPR; break; } OMIndex = OM_STEPS - 1; } } //////////////////////////////////////////////////////////////////////////////// private float atan360(float x, float y) { double phi; phi = Math.atan2((double)y, (double)x) * RAD2DEG; if(phi<0) phi += 360; return((float)phi); } //////////////////////////////////////////////////////////////////////////////// private float trim360(float phi) { if(phi>360) phi -= 360; return(phi); } } //////////////////////////////////////////////////////////////////////////////// // // // Sleeper // // // //////////////////////////////////////////////////////////////////////////////// interface Sleeper { public void wakeUp(int id); } //////////////////////////////////////////////////////////////////////////////// // // // Timer // // // //////////////////////////////////////////////////////////////////////////////// final class Timer implements Runnable { final static int OFF = -1; private int id; private long snooze; private Thread timerThread; // This is an ugly clutch. Something like a callback function/method should be // more elegant. To be implemented in release 2.0 :-) private RocketScience client; //////////////////////////////////////////////////////////////////////////////// public Timer(RocketScience client, int id) { this.client = client; this.id = id; this.snooze = OFF; if(this.timerThread==null) { this.timerThread = new Thread(this, "RocketScience 101 Timer Thread " + id); this.timerThread.start(); } } //////////////////////////////////////////////////////////////////////////////// public void clearSnooze() { if(this.timerThread!=null) this.snooze = OFF; } //////////////////////////////////////////////////////////////////////////////// public void setSnooze(long snooze) { if(this.timerThread!=null) this.snooze = snooze; } //////////////////////////////////////////////////////////////////////////////// public void run() { while(Thread.currentThread()==this.timerThread) { try { this.timerThread.sleep(1000); // sleep 1 sec } catch(InterruptedException e) {} if(this.snooze!=OFF) { this.snooze--; if(this.snooze<1) { this.snooze = OFF; this.client.wakeUp(this.id); } } } } //////////////////////////////////////////////////////////////////////////////// public synchronized void stop() { if(this.timerThread!=null && this.timerThread.isAlive()) this.timerThread.stop(); this.timerThread = null; } } //////////////////////////////////////////////////////////////////////////////// // // // DirtyRect // // // //////////////////////////////////////////////////////////////////////////////// // Implements the classic dirty rectangle algorithm. In DirtyRect the elegant // Java allocation method ("new") for adding rectangles is traded with one that // uses a static Rectangle. Since these methods here are called quite often, // this avoids the necessity for frequent garbage collection. final class DirtyRect { private Rectangle clipRect; // global clipRectangle for all bobs private boolean firstClip; // flags start of clipping private Rectangle tmpRect; // static rect to avoid dynamic allocation //////////////////////////////////////////////////////////////////////////////// public DirtyRect() { this.firstClip = true; this.clipRect = new Rectangle(); this.tmpRect = new Rectangle(); } //////////////////////////////////////////////////////////////////////////////// public void beginClipRect() { this.firstClip = true; } //////////////////////////////////////////////////////////////////////////////// public Rectangle endClipRect() { return this.clipRect; } //////////////////////////////////////////////////////////////////////////////// public void addClipRect(Rectangle rect) { addClipRect(rect.x, rect.y, rect.width, rect.height); } //////////////////////////////////////////////////////////////////////////////// public void addClipRect(int x, int y, int width, int height) { if(this.firstClip) { this.clipRect.x = x; this.clipRect.y = y; this.clipRect.width = width; this.clipRect.height = height; this.firstClip = false; } else { this.tmpRect.x = x; this.tmpRect.y = y; this.tmpRect.width = width; this.tmpRect.height = height; this.clipRect = this.clipRect.union(tmpRect); } } } //////////////////////////////////////////////////////////////////////////////// // // // Bob // // // //////////////////////////////////////////////////////////////////////////////// // A "Bob" is AMIGA-lingo for a blitter object (=sprite). class Bob { int x; // left coordinate int y; // top coordinate int width; // width int height; // height private int saveX; // top coordinate of bob area to save private int saveY; // left coordinate of bob area to save Image saveImage; // place for the bob area to save private int frameNr; // frame number (column) private int frameRow; // frame row int colsOfFrames; // # of frame columns Image stripImage; // actual image strip int offsetX; // x offset of the image to the bob's x coordinate int offsetY; // y offset of the image to the bob's x coordinate int bitmapWidth; // x offset to the next frame on the image strip int bitmapHeight; // y offset to the next frame on the image strip private boolean visible; // is bob currently visible private boolean inUse; // is save image used -> backbuffer must be restored private Image destImage; // destination image -> where the bob is rendered to //////////////////////////////////////////////////////////////////////////////// public Bob( int width, int height, int colsOfFrames, Image stripImage, int offsetX, int offsetY, int bitmapWidth, int bitmapHeight, boolean visible, Image destImage, Component component) { this.x = 0; this.y = 0; this.width = width; this.height = height; this.saveX = 0; this.saveY = 0; this.saveImage = component.createImage(this.width, this.height); this.frameNr = 0; this.frameRow = 0; this.colsOfFrames = colsOfFrames; this.stripImage = stripImage; this.offsetX = offsetX; this.offsetY = offsetY; this.bitmapWidth = bitmapWidth; this.bitmapHeight = bitmapHeight; this.visible = visible; this.inUse = false; this.destImage = destImage; } //////////////////////////////////////////////////////////////////////////////// public void blit(Image destImage, int destX, int destY, Image srcImage, int srcX, int srcY, int w, int h) { Graphics g = destImage.getGraphics().create(); g.clipRect(destX, destY, w, h); g.drawImage(srcImage, srcX, srcY, null); g.dispose(); } //////////////////////////////////////////////////////////////////////////////// public void lift(DirtyRect dirtyRect) { // Copies saveImage to screen if(this.isInUse()) { blit(this.destImage, this.saveX, this.saveY, this.saveImage, this.saveX, this.saveY, this.width, this.height); if(!this.visible) this.inUse = false; dirtyRect.addClipRect(this.saveX, this.saveY, this.width, this.height); } } //////////////////////////////////////////////////////////////////////////////// public void save() { // Copies screen to saveImage if(this.visible) { blit(this.saveImage, 0, 0, this.destImage, -this.x, -this.y, this.width, this.height); this.saveX = this.x; this.saveY = this.y; } } //////////////////////////////////////////////////////////////////////////////// public void drop(DirtyRect dirtyRect) { // Copies bob to screen if(this.visible) { blit(this.destImage, this.x, this.y, this.stripImage, -this.frameNr * this.bitmapWidth + this.x, -this.frameRow * this.bitmapHeight + this.y, this.width, this.height); this.inUse = true; dirtyRect.addClipRect(this.x, this.y, this.width, this.height); } } public void drop(DirtyRect dirtyRect, int frameNr) { this.frameNr = frameNr; this.drop(dirtyRect); } ////////////////////////////////////////////////////////////////////////////////////////////// public void setFrameNr(int frameNr) { this.frameNr = frameNr; } public void setFrameNr(float x, float y) { final float RAD2DEG = (float)(180.0 / Math.PI); double phi = Math.atan2((double)y, (double)x) * RAD2DEG; int imageAngle = (int)((360.0 / this.colsOfFrames ) + 1); int frameNr; phi += 0.5 * imageAngle; if(phi<0) phi += 360; if(phi>=360) phi -= 360; frameNr = (int)phi / imageAngle; setFrameNr(frameNr); } ////////////////////////////////////////////////////////////////////////////////////////////// public int getFrameNr() { return this.frameNr; } ////////////////////////////////////////////////////////////////////////////////////////////// public void setFrameRow(int frameRow) { this.frameRow = frameRow; } ////////////////////////////////////////////////////////////////////////////////////////////// public void cycleFrameNr() { this.frameNr++; if(this.frameNr>=this.colsOfFrames) this.frameNr = 0; } ////////////////////////////////////////////////////////////////////////////////////////////// public int getFrameRow() { return this.frameRow; } ////////////////////////////////////////////////////////////////////////////////////////////// public boolean isVisible() { return(this.visible); } ////////////////////////////////////////////////////////////////////////////////////////////// public void setVisible(boolean visible) { this.visible = visible; } ////////////////////////////////////////////////////////////////////////////////////////////// public boolean isInUse() { return(this.inUse); } ////////////////////////////////////////////////////////////////////////////////////////////// public void setInUse(boolean inUse) { this.inUse = inUse; } } //////////////////////////////////////////////////////////////////////////////// // // // MirBob // // // //////////////////////////////////////////////////////////////////////////////// final class MirBob extends Bob { // Translation offsets AtlantisBob to MirBob for all frames int transX[] = {27, 26, 21, 13, 3, -8, -15, -19, -21, -19, -14, -7, 3, 13, 21, 26}; int transY[] = { 3, -7, -15, -19, -21, -19, -14, -6, 3, 12, 20, 26, 27, 24, 20, 11}; MirBob( int width, int height, int colsOfFrames, Image stripImage, int offsetX, int offsetY, int bitmapWidth, int bitmapHeight, boolean isVisible, Image destImage, Component component) { super( width, height, colsOfFrames, stripImage, offsetX, offsetY, bitmapWidth, bitmapHeight, isVisible, destImage, component); } } //////////////////////////////////////////////////////////////////////////////// // // // Spacecraft // // // //////////////////////////////////////////////////////////////////////////////// class Spacecraft { public final static float GRAVITY = (float)6.67E-11; // [N kg2/m2] public final static float EARTH_MASS = (float)6.00E+24; // [kg] public final static float EARTH_RADIUS = (float)6.40E+06; // [m] public final static float ONE_DAY = (float)(24.0 * 60.0 * 60.0); // [s] public final static float GM = GRAVITY * EARTH_MASS; public final static float GEOSTATIONARY_RADIUS = (float)Math.pow(GRAVITY * EARTH_MASS * Math.pow(ONE_DAY / (2 * Math.PI), 2), 1.0 / 3.0); // [m] public final static float NO_THRUST = 0; public final static float ATLANTIS_SPEED_THRUST = (float)(70 * 0.001); public final static float ATLANTIS_BRAKE_THRUST = -(float)(70 * 0.001); float x; float y; float vx; float vy; float r; float v; float thrust; float thrustX; float thrustY; boolean onScreen; private boolean hasDocked; //////////////////////////////////////////////////////////////////////////////// public Spacecraft(float x, float y, float vx, float vy, boolean onScreen) { this.x = x; this.y = y; this.vx = vx; this.vy = vy; this.r = (float)Math.sqrt( x * x + y * y ); this.v = (float)Math.sqrt( vx * vx + vy * vy ); this.onScreen = onScreen; this.thrust = NO_THRUST; this.thrustX = 0; this.thrustY = 0; this.hasDocked = false; } //////////////////////////////////////////////////////////////////////////////// public boolean hasDocked() { return this.hasDocked; } //////////////////////////////////////////////////////////////////////////////// public void setDocked(boolean docked) { this.hasDocked = docked; } //////////////////////////////////////////////////////////////////////////////// public void newPosition(float deltaT) { // Uses a 2nd order Runge-Kutte algorithm to solve the equation of motion float d; float ax; float ay; float tmpX; float tmpY; d = (float)Math.sqrt(x * x + y * y); d = -GM / (d * d * d); ax = d * x + thrustX; ay = d * y + thrustY; tmpX = x + ( vx + (float)0.5 * ax * deltaT ) * deltaT; tmpY = y + ( vy + (float)0.5 * ay * deltaT ) * deltaT; d = (float)Math.sqrt(tmpX * tmpX + tmpY * tmpY); d = -GM / (d * d * d); ax = (float)0.5 * (ax + d * tmpX); ay = (float)0.5 * (ay + d * tmpY); x += ( vx + (float)0.5 * ax * deltaT ) * deltaT; y += ( vy + (float)0.5 * ay * deltaT ) * deltaT; vx += ax * deltaT; vy += ay * deltaT; r = (float)Math.sqrt(x * x + y * y); v = (float)Math.sqrt(vx * vx + vy * vy); } } //////////////////////////////////////////////////////////////////////////////// // // // MirSpacecraft // // // //////////////////////////////////////////////////////////////////////////////// final class MirSpacecraft extends Spacecraft { private int trailCnt; private int trailMax; private boolean trailDoTrail; private boolean blockAtlantisDocking = false; MirSpacecraft(float x, float y, float vx, float vy, boolean onScreen) { super(x, y, vx, vy, onScreen); } //////////////////////////////////////////////////////////////////////////////// public boolean blockAtlantisDocking() { return this.blockAtlantisDocking; } //////////////////////////////////////////////////////////////////////////////// public void setBlockAtlantisDocking(boolean isBlocked) { this.blockAtlantisDocking = isBlocked; } //////////////////////////////////////////////////////////////////////////////// public void resetTrail(int numTrailDots) { this.trailCnt = 0; this.trailMax = numTrailDots; this.trailDoTrail = true; } //////////////////////////////////////////////////////////////////////////////// public boolean doTrail() { this.trailCnt++; if(this.trailCnt>this.trailMax) this.trailDoTrail = false; return(this.trailDoTrail); } } //////////////////////////////////////////////////////////////////////////////// // // // Gadget // // // //////////////////////////////////////////////////////////////////////////////// // A "Gadget" is AMIGA-lingo for a push button final class Gadget { final static int GADGET_ROW_UP = 0; final static int GADGET_ROW_DOWN = 1; final static int GADGET_ROW_DISABLED = 2; private Rectangle rect; private boolean pressed; private boolean enabled; private Image image; private Image destImage; //////////////////////////////////////////////////////////////////////////////// public Gadget(int x, int y, Image image, Image destImage) { this.rect = new Rectangle(x, y, image.getWidth(null), image.getHeight(null) / 3); this.image = image; this.pressed = false; this.enabled = true; this.destImage = destImage; } //////////////////////////////////////////////////////////////////////////////// public boolean inside(int x, int y) { if(this.enabled) return this.rect.inside(x, y); else return false; } //////////////////////////////////////////////////////////////////////////////// public Rectangle getRectangle() { return this.rect; } //////////////////////////////////////////////////////////////////////////////// public boolean isPressed() { return this.pressed; } //////////////////////////////////////////////////////////////////////////////// public boolean isEnabled() { return this.enabled; } //////////////////////////////////////////////////////////////////////////////// public void enable() { this.pressed = false; this.enabled = true; this.draw(); } //////////////////////////////////////////////////////////////////////////////// public void disable() { this.pressed = false; this.enabled = false; this.draw(); } //////////////////////////////////////////////////////////////////////////////// public void down() { if(this.enabled) { this.drawDown(); this.pressed = true; } } //////////////////////////////////////////////////////////////////////////////// public void up() { if(this.enabled) { this.drawUp(); this.pressed = false; } } //////////////////////////////////////////////////////////////////////////////// public void draw() { if(this.enabled) { if(this.pressed) this.drawDown(); else this.drawUp(); } else this.drawDisabled(); } private synchronized void drawUp() { Graphics g = destImage.getGraphics().create(); g.clipRect(this.rect.x, this.rect.y, this.rect.width, this.rect.height); g.drawImage(this.image, this.rect.x, -this.rect.height * GADGET_ROW_UP + this.rect.y, null); g.dispose(); } private synchronized void drawDown() { Graphics g = this.destImage.getGraphics().create(); g.clipRect(this.rect.x, this.rect.y, this.rect.width, this.rect.height); g.drawImage(this.image, this.rect.x, -this.rect.height * GADGET_ROW_DOWN + this.rect.y, null); g.dispose(); } private synchronized void drawDisabled() { Graphics g = this.destImage.getGraphics().create(); g.clipRect(this.rect.x, this.rect.y, this.rect.width, this.rect.height); g.drawImage(this.image, this.rect.x, -this.rect.height * GADGET_ROW_DISABLED + this.rect.y, null); g.dispose(); } }