/* * Copyright (c) 2003-onwards Shaven Puppy Ltd * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'Shaven Puppy' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package worm; import java.io.Serializable; import java.util.Calendar; import net.puppygames.applet.Game; import net.puppygames.applet.GameState; import net.puppygames.applet.MiniGame; import net.puppygames.applet.PlayerSlot; import net.puppygames.applet.Screen; import net.puppygames.applet.effects.LabelEffect; import net.puppygames.applet.effects.Particle; import net.puppygames.applet.screens.DialogScreen; import net.puppygames.applet.screens.NagScreen; import net.puppygames.gamecommerce.shared.RegistrationDetails; import org.lwjgl.Sys; import org.lwjgl.input.Mouse; import org.lwjgl.util.Point; import worm.animation.SimpleThingWithLayers; import worm.features.LayersFeature; import worm.screens.ChooseGameModeScreen; import worm.screens.GameScreen; import com.shavenpuppy.jglib.Resources; import com.shavenpuppy.jglib.interpolators.LinearInterpolator; import com.shavenpuppy.jglib.openal.ALBuffer; import com.shavenpuppy.jglib.resources.Attenuator; import com.shavenpuppy.jglib.resources.AttenuatorFeature; import com.shavenpuppy.jglib.resources.MappedColor; import com.shavenpuppy.jglib.sprites.SoundCommand; import com.shavenpuppy.jglib.sprites.Sprite; import com.shavenpuppy.jglib.sprites.SpriteAllocator; import com.shavenpuppy.jglib.sprites.SpriteEngine; import com.shavenpuppy.jglib.sprites.SpriteImage; import com.shavenpuppy.jglib.sprites.StaticSpriteEngine; /** * Revenge of the Titans game */ public class Worm extends MiniGame { private static final long serialVersionUID = 1L; private static final boolean TEST_XMAS = true; // Only honoured if DEBUG is also true private static final boolean FORCE_XMAS = true; // Force Xmas to this value when TEST_XMAS is true public static final float MAX_LOUD_ATTENUATION_DISTANCE = 1280.0f; public static final float MAX_ATTENUATION_DISTANCE = 720.f; public static final float MIN_ATTENUATION_GAIN = 0.01f; private static final int FAST_FORWARD_SPEED = 9; /** Singleton */ private static Worm instance; /** Game state mode for when we create a new {@link WormGameState} */ private static int newMode; /** Game state (shadows Game.gameState) */ private static WormGameState gameState; /** Sound attenuators */ public static final Attenuator ATTENUATOR = new Attenuator() { @Override public float getVolume(float x, float y) { Point spriteOffset = GameScreen.getSpriteOffset(); double ddx = -spriteOffset.getX() + Game.getWidth() / 2.0f - x; double ddy = -spriteOffset.getY() + Game.getHeight() / 2.0f - y; double dist = Math.sqrt(ddx * ddx + ddy * ddy); return LinearInterpolator.instance.interpolate(1.0f, MIN_ATTENUATION_GAIN, (float) dist / MAX_ATTENUATION_DISTANCE); } }; public static final Attenuator LOUD_ATTENUATOR = new Attenuator() { @Override public float getVolume(float x, float y) { Point spriteOffset = GameScreen.getSpriteOffset(); double ddx = -spriteOffset.getX() + Game.getWidth() / 2.0f - x; double ddy = -spriteOffset.getY() + Game.getHeight() / 2.0f - y; double dist = Math.sqrt(ddx * ddx + ddy * ddy); return LinearInterpolator.instance.interpolate(1.0f, MIN_ATTENUATION_GAIN, (float) dist / MAX_LOUD_ATTENUATION_DISTANCE); } }; private static final AttenuatorFeature ATTENUATOR_FEATURE = new AttenuatorFeature("default.attenuator") { @Override public float getVolume(float x, float y) { return Game.getSFXVolume() * ATTENUATOR.getVolume(x, y); } }; private static final AttenuatorFeature LOUD_ATTENUATOR_FEATURE = new AttenuatorFeature("loud.attenuator") { @Override public float getVolume(float x, float y) { return Game.getSFXVolume() * LOUD_ATTENUATOR.getVolume(x, y); } }; private static final AttenuatorFeature DEFAULT_ATTENUATOR_FEATURE = new AttenuatorFeature("none.attenuator") { @Override public float getVolume(float x, float y) { return Game.getSFXVolume(); } }; private static boolean xmas; static { Resources.put(ATTENUATOR_FEATURE); Resources.put(LOUD_ATTENUATOR_FEATURE); Resources.put(DEFAULT_ATTENUATOR_FEATURE); Calendar c = Calendar.getInstance(); int month = c.get(Calendar.MONTH); int day = c.get(Calendar.DAY_OF_MONTH); if ((month == Calendar.DECEMBER && day >= 19) || (month == Calendar.JANUARY && day < 9) || (TEST_XMAS && DEBUG)) { xmas = TEST_XMAS && DEBUG ? FORCE_XMAS : true; } } private static final SpriteAllocator MOUSE_SPRITE_ALLOCATOR = new SpriteAllocator() { private static final long serialVersionUID = 1L; @Override public Sprite allocateSprite(Serializable owner) { return instance.mouseSpriteEngine.allocateSprite(owner); } }; /** Global mouse pointers rendered using this very small sprite engine */ private transient SpriteEngine mouseSpriteEngine; /** Mouse pointer */ private transient SimpleThingWithLayers mouseLayers; /* * Options */ private static boolean showTooltips, showInfo, showHints, autoDifficulty; /** * C'tor */ public Worm(String name) { super(name); } public static void setMouseAppearance(LayersFeature newMouseAppearance) { instance.doSetMouseAppearance(newMouseAppearance); } private void doSetMouseAppearance(LayersFeature newMouseAppearance) { mouseLayers.requestSetAppearance(newMouseAppearance); updateMouse(); } private void updateMouse() { boolean insideWindow = Mouse.isInsideWindow() && !Game.isPaused(); if (mouseLayers.getSprites() != null) { float lx = physicalXtoLogicalX(getMouseX()); float ly = physicalYtoLogicalY(getMouseY()); for (int i = 0; i < mouseLayers.getSprites().length; i++) { mouseLayers.getSprite(i).setLocation(lx, ly); mouseLayers.getSprite(i).setVisible(insideWindow); } } mouseSpriteEngine.tick(); } @Override protected void setGameState(GameState newGameState) { super.setGameState(newGameState); Worm.gameState = (WormGameState) newGameState; } /** * @return the game state */ public static WormGameState getGameState() { return gameState; } @Override protected void doGameOver() { gameState.onGameOver(); GameScreen.gameOver(); } @Override protected void doShowOptions() { net.puppygames.applet.screens.OptionsScreen.show(); } @Override protected void doShowHelp() { Sys.openURL("http://www.puppygames.net/revenge-of-the-titans/help/" + getLanguage()); } @Override protected void doCreate() { setInstance(this); super.doCreate(); Sprite.setMissingImage((SpriteImage) Resources.get("spriteimage.missing")); mouseSpriteEngine = new StaticSpriteEngine(false, 0, false, 1); mouseSpriteEngine.create(); mouseLayers = new SimpleThingWithLayers(MOUSE_SPRITE_ALLOCATOR); setMouseAppearance(Res.getMousePointer()); Particle.setMaxParticles(4096); SoundCommand.setDefaultAttenuator(DEFAULT_ATTENUATOR_FEATURE); } public static void setInstance(Worm instance) { Worm.instance = instance; } @Override protected void postRender() { mouseSpriteEngine.render(); } @Override protected void doEndGame() { if (MiniGame.isDemoExpired()) { NagScreen.show("You know you want to!", true); } else { net.puppygames.applet.screens.TitleScreen.show(); } } @Override protected void doTick() { updateMouse(); GameScreen gameScreen = GameScreen.getInstance(); if (gameScreen.isFastForward()) { for (int i = 0; i < FAST_FORWARD_SPEED; i ++) { gameScreen.tick(); } gameScreen.clearFastForward(); } } @Override protected void onPaused() { updateMouse(); Screen.tickAllScreens(); } /** * @return the number of worlds unlocked the current slot has, past Earth */ public static int getMaxWorld() { return getPlayerSlot().getPreferences().getInt("maxlevel_"+WormGameState.GAME_MODE_CAMPAIGN, 0) / WormGameState.LEVELS_IN_WORLD; } public static int getMaxLevelUnlockedInWorld(int world) { if (world < getMaxWorld()) { return WormGameState.LEVELS_IN_WORLD - 1; } else { return getMaxLevel(WormGameState.GAME_MODE_CAMPAIGN) % WormGameState.LEVELS_IN_WORLD; } } public static void setMaxLevel(int level, int gameMode) { getPlayerSlot().getPreferences().putInt("maxlevel_"+gameMode, level); flushPrefs(); } public static int getMaxLevel(int gameMode) { return getMaxLevel(getPlayerSlot(), gameMode); } public static int getMaxLevel(PlayerSlot slot, int gameMode) { return slot.getPreferences().getInt("maxlevel_"+gameMode, 0); } /** * Sets some extra level data * @param level * @param gameMode TODO * @param key * @param value */ public static void setExtraLevelData(int level, int gameMode, String key, String value) { getPlayerSlot().getPreferences().put(key+"."+gameMode+"."+level, value); flushPrefs(); } /** * Sets some extra level data * @param level * @param gameMode TODO * @param key * @param value */ public static void setExtraLevelData(int level, int gameMode, String key, int value) { getPlayerSlot().getPreferences().putInt(key+"."+gameMode+"."+level, value); flushPrefs(); } /** * Sets some extra level data * @param level * @param gameMode TODO * @param key * @param value */ public static void setExtraLevelData(int level, int gameMode, String key, float value) { getPlayerSlot().getPreferences().putFloat(key+"."+gameMode+"."+level, value); flushPrefs(); } /** * Sets some extra level data * @param level * @param gameMode TODO * @param key * @param value */ public static void setExtraLevelData(int level, int gameMode, String key, long value) { getPlayerSlot().getPreferences().putLong(key+"."+gameMode+"."+level, value); flushPrefs(); } /** * Gets extra level data * @param slot TODO * @param level * @param gameMode TODO * @param key * @param default_ * @return */ public static String getExtraLevelData(PlayerSlot slot, int level, int gameMode, String key, String default_) { return slot.getPreferences().get(key+"."+gameMode+"."+level, default_); } /** * Gets extra level data * @param slot TODO * @param level * @param gameMode TODO * @param key * @param default_ * @return */ public static float getExtraLevelData(PlayerSlot slot, int level, int gameMode, String key, float default_) { return slot.getPreferences().getFloat(key+"."+gameMode+"."+level, default_); } /** * Gets extra level data * @param slot TODO * @param level * @param gameMode TODO * @param key * @param default_ * @return */ public static long getExtraLevelData(PlayerSlot slot, int level, int gameMode, String key, long default_) { return slot.getPreferences().getLong(key+"."+gameMode+"."+level, default_); } public static int getExtraLevelData(PlayerSlot slot, int level, int gameMode, String key, int default_) { return slot.getPreferences().getInt(key+"."+gameMode+"."+level, default_); } /** * Utility to calculate gain */ public static float calcGain(float x, float y) { return ATTENUATOR.getVolume(x, y); } /** * Utility to calculate gain for loud noises */ public static float calcLoudGain(float x, float y) { return LOUD_ATTENUATOR.getVolume(x, y); } @Override protected String getSaveGameRegistryMagicLocation() { return "tox"+gameState.getGameMode(); } @Override protected void onGameSaved() { LabelEffect le = new LabelEffect(net.puppygames.applet.Res.getBigFont(), getMessage("ultraworm.worm.game_saved"), new MappedColor("titles.colormap:text-bold"), new MappedColor("titles.colormap:text-dark"), 180, 60); le.setLayer(100); le.setLocation(Game.getWidth() / 2, Game.getHeight() / 2); le.setSound((ALBuffer) Resources.get("gamesaved.buffer")); le.spawn(net.puppygames.applet.screens.TitleScreen.getInstance()); } @Override protected void doRequestExit() { final DialogScreen reallyDialog = (DialogScreen) Resources.get("yescancel.dialog"); reallyDialog.doModal(getMessage("ultraworm.worm.exit_game"), getMessage("ultraworm.worm.exit_question"), new Runnable() { @Override public void run() { if (reallyDialog.getOption() == DialogScreen.OK_OPTION) { Game.exit(); } } }); } @Override protected boolean doIsDemoExpired() { // Never expire the demo return false; } @Override protected boolean doMaybeShowHelp() { // Don't show help return false; } /** * @return autodifficulty setting */ public static boolean getAutoDifficulty() { return autoDifficulty; } /** * @return the showHints */ public static boolean getShowHints() { return showHints; } /** * @return the showInfo */ public static boolean getShowInfo() { return showInfo; } /** * @return the showTooltips */ public static boolean getShowTooltips() { return showTooltips; } /** * @param showHints the showHints to set */ public static void setShowHints(boolean showHints) { Worm.showHints = showHints; saveSlotOptions(); } /** * @param showInfo the showInfo to set */ public static void setShowInfo(boolean showInfo) { Worm.showInfo = showInfo; saveSlotOptions(); } /** * @param showTooltips the showTooltips to set */ public static void setShowTooltips(boolean showTooltips) { Worm.showTooltips = showTooltips; saveSlotOptions(); } public static void setAutoDifficulty(boolean autoDifficulty) { Worm.autoDifficulty = autoDifficulty; saveSlotOptions(); } private static void saveSlotOptions() { getPlayerSlot().getPreferences().putBoolean("showHints", showHints); getPlayerSlot().getPreferences().putBoolean("showTooltips", showTooltips); getPlayerSlot().getPreferences().putBoolean("showInfo", showInfo); getPlayerSlot().getPreferences().putBoolean("autoDifficulty", autoDifficulty); flushPrefs(); } private static void loadSlotOptions() { showHints = getPlayerSlot().getPreferences().getBoolean("showHints", true); showTooltips = getPlayerSlot().getPreferences().getBoolean("showTooltips", true); showInfo = getPlayerSlot().getPreferences().getBoolean("showInfo", true); autoDifficulty = getPlayerSlot().getPreferences().getBoolean("autoDifficulty", true); } @Override protected void onSetPlayerSlot() { loadSlotOptions(); } @Override protected void onBeginNewGame() { // Open the game mode choice dialog if (xmas) { ((ChooseGameModeScreen)Resources.get("choose-game-mode-xmas.screen")).open(); } else { ((ChooseGameModeScreen)Resources.get("choose-game-mode.screen")).open(); } } @Override protected void onPreRegisteredStartup() { if (getPlayerSlot() == null) { MiniGame.showTitleScreen(); return; } super.onPreRegisteredStartup(); } @Override protected String getRestoreFile() { return getPlayerDirectoryPrefix() + "savedGame_"+newMode+".dat"; } /** * Start a new game from scratch * @param mode A game mode (0, 1, 2) */ public static void newGame(int mode) { newMode = mode; // Is there a saved game file? if (MiniGame.isRestoreAvailable()) { MiniGame.restoreGame(); } else { cleanGame(); } } @Override protected GameState createGameState() { return new WormGameState(newMode); } /** * Reset game state (used by survival level select) */ public static void resetGameState() { instance.setGameState(instance.createGameState()); } @Override protected void onRestoreGameFailed(Exception e) { // Clean up the GameScreen GameScreen.getInstance().panic(); } /** * Registration check for allowing Sandbox mode. */ public static boolean isSandboxRegistered() { RegistrationDetails sandboxRegistrationDetails = null; try { sandboxRegistrationDetails = RegistrationDetails.checkRegistration("Sandbox Mode"); } catch (Exception e) { } return (sandboxRegistrationDetails!=null ? true : false); } /** * @return true if it's Christmas! */ public static boolean isXmas() { return xmas; } }