/* * 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.HashSet; import java.util.Iterator; import java.util.Set; import org.lwjgl.util.Rectangle; import worm.Entity; import worm.MapRenderer; import worm.Worm; import worm.WormGameState; import worm.features.GidrahFeature; import com.shavenpuppy.jglib.interpolators.LinearInterpolator; import com.shavenpuppy.jglib.interpolators.OpenLinearInterpolator; /** * For moving flying gidrahs */ class FlyingMovement implements Movement { private static final long serialVersionUID = 1L; private static final Rectangle TEMP = new Rectangle(); private static final ArrayList<Entity> COLLISIONS = new ArrayList<Entity>(); private static final int MAX_RAMP_UP_DURATION = 7200; private static final int RAMP_UP_DURATION = 3600; private static final int RAMP_UP_PER_LEVEL = 60; private static final float MARGIN = 64.0f; private static final int BOMB_CHECK = 16; private static final int DROP_TIME = 16; private static final float BOSS_DROP_ATTACK_RANGE = 4.0f; final Set<Building> bombed = new HashSet<Building>(); final Gidrah gidrah; final float ox, oy; int rampTick; Building target; Building bombTarget; double tx, ty, angle; int bombTick, dropTick; boolean dropping; /** * C'tor * @param gidrah */ FlyingMovement(Gidrah gidrah) { this.gidrah = gidrah; this.ox = gidrah.getMapX(); this.oy = gidrah.getMapY(); } @Override public void remove() { } @Override public void reset() { } private void drop() { dropping = true; gidrah.initAttack(bombTarget); } /** * We're dropping onto our target to squish it */ private void doDrop() { dropTick ++; } @Override public void tick() { if (target == null) { gidrah.findTarget(); target = (Building) gidrah.getTarget(); if (target == null) { return; } tx = target.getMapX() + target.getCollisionX(); ty = target.getMapY() + target.getCollisionY(); double gx = gidrah.getMapX() + MapRenderer.TILE_SIZE * 0.5; double gy = gidrah.getMapY() + MapRenderer.TILE_SIZE * 0.5; double dx = tx - gx; double dy = ty - gy; angle = Math.atan2(dy, dx); } if (!Worm.getGameState().isLevelActive()) { rampTick ++; } double mx = Math.cos(angle) * getSpeed() * 0.016666666666666666666666666666667; double my = Math.sin(angle) * getSpeed() * 0.016666666666666666666666666666667; gidrah.setMirrored(mx < 0.0); float newX = (float) (gidrah.getMapX() + mx); float newY = (float) (gidrah.getMapY() + my); if (dropping) { doDrop(); } else if ( newX > Worm.getGameState().getMap().getWidth() * MapRenderer.TILE_SIZE + MARGIN || newY > Worm.getGameState().getMap().getHeight() * MapRenderer.TILE_SIZE + MARGIN || newX < -MARGIN || newY < -MARGIN ) { // Go back to the start and retarget newX = ox; newY = oy; bombed.clear(); gidrah.spawnBomb(); bombTarget = null; target = null; } else { if (bombTarget != null) { bombTick ++; if (bombTick >= DROP_TIME) { bombTick = 0; // Drop a bomb! Or jump onto target. if (gidrah.getFeature().isDropAttack()) { drop(); } else { gidrah.dropBomb((float) mx, (float) my); bombTarget = null; } } } else if (gidrah.getFeature().isDropAttack() && gidrah.getFeature().isBoss()) { // Drop attack boss only drops onto its one target - the base if (target.getDistanceTo(gidrah.getMapX() + MapRenderer.TILE_SIZE / 2, gidrah.getMapY() + MapRenderer.TILE_SIZE / 2) < BOSS_DROP_ATTACK_RANGE) { bombTarget = target; bombTick = 0; } } else { // Are we over a building? bombTick ++; TEMP.setBounds((int) newX + MapRenderer.TILE_SIZE / 2, (int) newY + MapRenderer.TILE_SIZE / 2, MapRenderer.TILE_SIZE / 2, MapRenderer.TILE_SIZE / 2); Entity.getCollisions(TEMP, COLLISIONS); if (bombed.size() > 0) { for (Iterator<Building> i = bombed.iterator(); i.hasNext(); ) { Building b = i.next(); if (!b.isActive()) { i.remove(); } } if (bombed.size() > 0 && COLLISIONS.size() > 0) { COLLISIONS.removeAll(bombed); } } int n = COLLISIONS.size(); for (int i = 0; i < n; i ++) { Entity entity = COLLISIONS.get(i); if (entity == gidrah || !entity.isActive() || !entity.canCollide()) { return; } if (entity instanceof Building) { Building building = (Building) entity; if (building.isWorthAttacking() && !building.isBarricade() && !building.isMineField()) { bombTarget = building; bombTick = 0; bombed.add(building); } } } } } gidrah.setLocation(newX, newY); } float getSpeed() { WormGameState gameState = Worm.getGameState(); GidrahFeature feature = gidrah.getFeature(); float minSpeed = feature.getSpeed(); float maxSpeed = feature.getSpeed() * MAX_SPEED_MULTIPLIER; float ret; ret = OpenLinearInterpolator.instance.interpolate(minSpeed, maxSpeed, gameState.getDifficulty()); if (!gameState.isLevelActive()) { ret = LinearInterpolator.instance.interpolate ( ret, Math.max(120.0f, ret * LinearInterpolator.instance.interpolate(1.0f, 1.5f, gameState.getGidrahDeathRatio())), (float) rampTick / (float) Math.min(MAX_RAMP_UP_DURATION, RAMP_UP_DURATION + gameState.getLevel() * RAMP_UP_PER_LEVEL) // ramp up duration takes longer and longer ); } if (dropping) { // Tend to zero as we drop ret = LinearInterpolator.instance.interpolate(ret, 0.0f, dropTick / gidrah.getFeature().getHeight()); } return ret; } @Override public void maybeRethink(Rectangle bounds) { } @Override public void adjust(float newX, float newY) { } @Override public boolean isMoving() { return true; } @Override public void attack() { // No need to do anything } @Override public void dontAttack() { // Drop the bomb! double mx = Math.cos(angle) * getSpeed() * 0.016666666666666666666666666666667; double my = Math.sin(angle) * getSpeed() * 0.016666666666666666666666666666667; gidrah.dropBomb((float) mx, (float) my); } }