/* * 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.entities; import java.util.ArrayList; import java.util.List; import net.puppygames.applet.effects.BlastEffect; import net.puppygames.applet.effects.Emitter; import net.puppygames.applet.effects.EmitterFeature; import org.lwjgl.util.Rectangle; import worm.Entity; import worm.GameStateInterface; import worm.features.BombFeature; import worm.screens.GameScreen; import com.shavenpuppy.jglib.openal.ALBuffer; import com.shavenpuppy.jglib.util.FPMath; /** * @author foo * */ public class Bomb extends Entity { private static final long serialVersionUID = 1L; private static final float GRAVITY = 0.01f; private static final float FRICTION = 0.99f; private final List<Entity> hit = new ArrayList<Entity>(); private final BombFeature feature; private final Gidrah carrier; private float z; private float dz; private float vx; private float vy; private int phase; private static final int PHASE_SPAWNING = 0; private static final int PHASE_CARRIED = 1; private static final int PHASE_DROPPING = 2; private static final int PHASE_EXPLODING = 3; private transient BlastEffect blastEffect; private transient Emitter[] emitter; /** * C'tor */ public Bomb(BombFeature feature, Gidrah carrier) { this.feature = feature; this.carrier = carrier; } @Override protected void doSpawn() { doRespawn(); } @Override protected void doRespawn() { float x = carrier.getMapX() + feature.getBombOffset(isMirrored()).getX(); float y = carrier.getMapY() + feature.getBombOffset(isMirrored()).getY(); feature.getAppearance().createSprites(GameScreen.getInstance(), x, y, this); float z = carrier.getFinalYOffset(); getSprite(0).setOffset(0.0f, z); getSprite(0).setChildYOffset(z*1.0f/FPMath.floatValue(getSprite(0).getYScale())); emitter = feature.getAppearance().createEmitters(GameScreen.getInstance(), x, y); if (emitter != null) { for (Emitter element : emitter) { element.setLocation(x, y); element.setYOffset(z); } } } @Override protected void doRemove() { if (emitter != null) { for (Emitter element : emitter) { element.remove(); } emitter = null; } } @Override protected void doTick() { switch (phase) { case PHASE_SPAWNING: tickSpawn(); break; case PHASE_CARRIED: tickCarried(); break; case PHASE_DROPPING: tickDropping(); break; case PHASE_EXPLODING: tickExploding(); break; default: assert false : "Unknown phase "+phase; } } private void tickSpawn() { phase = PHASE_CARRIED; } private void tickCarried() { float x = carrier.getMapX() + feature.getBombOffset(isMirrored()).getX(); float y = carrier.getMapY() + feature.getBombOffset(isMirrored()).getY(); setLocation(x, y); float z = carrier.getFinalYOffset(); getSprite(0).setOffset(0.0f, z); getSprite(0).setChildYOffset(z*1.0f/FPMath.floatValue(getSprite(0).getYScale())); if (emitter != null) { for (Emitter element : emitter) { element.setLocation(x, y); element.setYOffset(z); } } } private void tickDropping() { vx *= FRICTION; vy *= FRICTION; if (Math.abs(vx) < 0.01f) { vx = 0.0f; } if (Math.abs(vy) < 0.01f) { vy = 0.0f; } setLocation(getMapX() + vx, getMapY() + vy); z -= dz; dz += GRAVITY; if (z <= 0.0) { z = 0.0f; explode(); return; } getSprite(0).setOffset(0.0f, z); getSprite(0).setChildYOffset(z*1.0f/FPMath.floatValue(getSprite(0).getYScale())); if (emitter != null) { for (Emitter element : emitter) { element.setYOffset(z); } } } private void explode() { phase = PHASE_EXPLODING; blastEffect = new BlastEffect(getMapX(), getMapY(), 16, 16, feature.getExplosionRadius(), feature.getExplosionRadius(), feature.getExplosionTexture()); blastEffect.setFadeWhenExpanding(true); blastEffect.setOffset(GameScreen.getSpriteOffset()); blastEffect.spawn(GameScreen.getInstance()); EmitterFeature blastEmitterFeature = feature.getExplosion(); Emitter blastEmitter = blastEmitterFeature.spawn(GameScreen.getInstance()); blastEmitter.setLocation(getMapX(), getMapY()); GameScreen.shake(feature.getShake()); setVisible(false); } private void tickExploding() { if (blastEffect.isFinished()) { remove(); } } public void drop(float vx, float vy) { if (phase != PHASE_CARRIED || !isActive()) { return; } ALBuffer dropNoise = feature.getDropNoise(); if (dropNoise != null) { // TODO } phase = PHASE_DROPPING; this.vx = vx; this.vy = vy; z = carrier.getFinalYOffset(); } @Override public float getZ() { return z; } @Override protected void doUpdate() { } @Override public void addToGameState(GameStateInterface gsi) { } @Override public void removeFromGameState(GameStateInterface gsi) { } @Override public boolean canCollide() { return phase == PHASE_EXPLODING; } @Override public void onCollision(Entity entity) { entity.onCollisionWithBomb(this); } @Override public void onCollisionWithBuilding(Building building) { if (hit.contains(building)) { return; } hit.add(building); building.damage(feature.getExplosionDamage()); } @Override public void onCollisionWithGidrah(Gidrah gidrah) { if (hit.contains(gidrah)) { return; } hit.add(gidrah); gidrah.explosionDamage(feature.getExplosionDamage() * 4, false); } @Override public float getRadius() { return canCollide() && blastEffect != null ? blastEffect.getRadius() : 0.0f; } @Override public Rectangle getBounds(Rectangle bounds) { return null; } @Override public boolean isRound() { return true; } @Override public boolean isShootable() { return false; } }