package rabbitescape.render.gameloop; import java.util.ArrayList; import java.util.List; import rabbitescape.engine.solution.PlaceTokenAction; import rabbitescape.engine.solution.SelectAction; import rabbitescape.engine.solution.SolutionIgnorer; import rabbitescape.engine.solution.SolutionInterpreter; import rabbitescape.engine.solution.SolutionRecorderTemplate; import rabbitescape.engine.solution.SolutionTimeStep; import rabbitescape.engine.solution.TimeStepAction; import rabbitescape.engine.solution.UiPlayback; import rabbitescape.engine.LevelWinListener; import rabbitescape.engine.Token; import rabbitescape.engine.World; import rabbitescape.engine.World.CompletionState; public class GeneralPhysics implements Physics { /** * Everything that modifies the world goes through here, with * synchronization. * * Public for test */ public static class WorldModifier { private final World world; public final SolutionRecorderTemplate solutionRecorder; public WorldModifier( World world, SolutionRecorderTemplate solutionRecorder ) { this.world = world; this.solutionRecorder = solutionRecorder; } public synchronized void step() { world.step(); solutionRecorder.appendStepEnd( ); } public synchronized void addToken( int tileX, int tileY, Token.Type type ) { world.changes.addToken( tileX, tileY, type ); } } private final long max_allowed_skips; public static final long simulation_time_step_ms = 70; public int frame; public boolean fast; public final World world; private final WaterAnimation water; private final WorldModifier worldModifier; private final LevelWinListener winListener; private final List<StatsChangedListener> statsListeners; public final SolutionInterpreter solutionInterpreter; private final UiPlayback uiPlayback; private static final int FAST_FRAME_SKIP = 3; public GeneralPhysics( World world, LevelWinListener winListener, boolean fast ) { this( world, WaterAnimation.getDummyWaterAnimation(), winListener, fast, new SolutionIgnorer(), SolutionInterpreter.getNothingPlaying(), null, false ); } public GeneralPhysics( World world, WaterAnimation waterAnimation, LevelWinListener winListener, boolean fast ) { this( world, waterAnimation, winListener, fast, new SolutionIgnorer(), SolutionInterpreter.getNothingPlaying(), null, false ); } public GeneralPhysics( World world, WaterAnimation water, LevelWinListener winListener, boolean fast, SolutionRecorderTemplate solutionRecorder, SolutionInterpreter solutionInterpreter, UiPlayback uiPlayback, boolean noFrameSkipping ) { this.frame = 0; this.world = world; this.water = water; this.worldModifier = new WorldModifier( world, solutionRecorder ); this.winListener = winListener; this.fast = fast; this.statsListeners = new ArrayList<>(); this.solutionInterpreter = solutionInterpreter; this.uiPlayback = uiPlayback; this.max_allowed_skips = noFrameSkipping ? 1 : 10 ; } @Override public long step( long simulation_time, long frame_start_time ) { for ( int skipped = 0; skipped < max_allowed_skips; ++skipped ) { if ( simulation_time >= frame_start_time ) { break; } frame += fast ? FAST_FRAME_SKIP : 1; if ( frame >= 10 ) { frame -= 10; doInterpreterActions(); worldModifier.step(); water.step( world ); checkWon(); notifyStatsListeners(); } simulation_time += simulation_time_step_ms; } return simulation_time; } @Override public int frameNumber() { return frame; } @Override public boolean gameRunning() { return ( world.completionState() == World.CompletionState.RUNNING ); } /** * Take actions for demo mode, eg drop tokens. */ private void doInterpreterActions() { SolutionTimeStep stp = solutionInterpreter.next( CompletionState.RUNNING ); for ( TimeStepAction action: stp.actions ) { if ( action instanceof SelectAction ) { uiPlayback.selectToken( (SelectAction)action ); } else if ( action instanceof PlaceTokenAction ) { uiPlayback.placeToken( (PlaceTokenAction)action ); } } } private void notifyStatsListeners() { for ( StatsChangedListener listener : statsListeners ) { listener.changed( world.num_waiting, world.numRabbitsOut(), world.num_saved ); } } public int addToken( int tileX, int tileY, Token.Type ability ) { if ( gameRunning() && tileX >= 0 && tileX < world.size.width && tileY >= 0 && tileY < world.size.height && world.abilities.get( ability ) > 0 ) { worldModifier.addToken( tileX, tileY, ability ); } return world.abilities.get( ability ); } public void addStatsChangedListener( StatsChangedListener listener ) { statsListeners.add( listener ); } private void checkWon() { switch ( world.completionState() ) { case WON: { winListener.won(); break; } case LOST: { winListener.lost(); break; } default: { break; } } } @Override public void dispose() { } @Override public World world() { return world; } }