@Override public void render() { // standard clear the background routine for libGDX Gdx.gl.glClearColor(bgColor.r / 255.0f, bgColor.g / 255.0f, bgColor.b / 255.0f, 1.0f); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); // not sure if this is always needed... // Gdx.gl.glEnable(GL20.GL_BLEND); // used as the z-axis when generating Simplex noise to make water seem to "move" counter += Gdx.graphics.getDeltaTime() * 15; // this does the standard lighting for walls, floors, etc. but also uses counter to do the // Simplex noise thing. lights = DungeonUtility.generateLightnessModifiers(decoDungeon, counter); // textFactory.configureShader(batch); // you done bad. you done real bad. if (health <= 0) { // still need to display the map, then write over it with a message. toCursor.clear(); monsters.clear(); display.setLightnesses(GwtCompatibility.fill2D(-200, width * 2, height)); putMap(); display.putBoxedString(width - 18, height / 2 - 6, " THE PHANTOMS HAVE EATEN YOUR SOUL! "); display.putBoxedString(width - 18, height / 2 - 4, " AT LAST, YOU HEAR THEM MUTTER, "); display.putBoxedString(width - lang.length() / 2, height / 2, lang); display.putBoxedString(width - 18, height / 2 + 4, " ...WHATEVER THAT MEANS. "); display.putBoxedString(width - 18, height / 2 + 9, " q to quit. "); // because we return early, we still need to draw. stage.draw(); stage.act(); // q still needs to quit. if (input.hasNext()) input.next(); return; } // need to display the map every frame, since we clear the screen to avoid artifacts. putMap(); // if the user clicked, we have a list of moves to perform. if (!awaitedMoves.isEmpty()) { // extremely similar to the block below that also checks if animations are done // this doesn't check for input, but instead processes and removes Points from awaitedMoves. if (!display.hasActiveAnimations()) { ++framesWithoutAnimation; if (framesWithoutAnimation >= 3) { framesWithoutAnimation = 0; switch (phase) { case WAIT: case MONSTER_ANIM: Coord m = awaitedMoves.remove(0); toCursor.remove(0); move(m.x - player.gridX, m.y - player.gridY); break; case PLAYER_ANIM: postMove(); break; } } } } // if we are waiting for the player's input and get input, process it. else if (input.hasNext() && !display.hasActiveAnimations() && phase == Phase.WAIT) { input.next(); } // if the previous blocks didn't happen, and there are no active animations, then either change // the phase // (because with no animations running the last phase must have ended), or start a new animation // soon. else if (!display.hasActiveAnimations()) { ++framesWithoutAnimation; if (framesWithoutAnimation >= 3) { framesWithoutAnimation = 0; switch (phase) { case WAIT: break; case MONSTER_ANIM: { phase = Phase.WAIT; } break; case PLAYER_ANIM: { postMove(); } } } } // if we do have an animation running, then how many frames have passed with no animation needs // resetting else { framesWithoutAnimation = 0; } input.show(); // stage has its own batch and must be explicitly told to draw(). this also causes it to act(). stage.getViewport().apply(true); stage.draw(); stage.act(); subCell.erase(); if (help == null) { // display does not draw all AnimatedEntities by default, since FOV often changes how they // need to be drawn. batch.begin(); // the player needs to get drawn every frame, of course. display.drawActor(batch, 1.0f, player); subCell.put( player.gridX * 2 + 1, player.gridY, Character.forDigit(health, 10), SColor.DARK_PINK); for (Monster mon : monsters) { // monsters are only drawn if within FOV. if (fovmap[mon.entity.gridX][mon.entity.gridY] > 0.0) { display.drawActor(batch, 1.0f, mon.entity); if (mon.state > 0) subCell.put(mon.entity.gridX * 2, mon.entity.gridY, '!', SColor.YELLOW); } } subCell.draw(batch, 1.0F); // batch must end if it began. batch.end(); } // if using a filter that changes each frame, clear the known relationship between requested and // actual colors if (changingColors) { fgCenter.clearCache(); bgCenter.clearCache(); } }