/* * Aphelion * Copyright (c) 2013 Joris van der Wel * * This file is part of Aphelion * * Aphelion is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, version 3 of the License. * * Aphelion is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with Aphelion. If not, see <http://www.gnu.org/licenses/>. * * In addition, the following supplemental terms apply, based on section 7 of * the GNU Affero General Public License (version 3): * a) Preservation of all legal notices and author attributions * b) Prohibition of misrepresentation of the origin of this material, and * modified versions are required to be marked in reasonable ways as * different from the original version (for example by appending a copyright notice). * * Linking this library statically or dynamically with other modules is making a * combined work based on this library. Thus, the terms and conditions of the * GNU Affero General Public License cover the whole combination. * * As a special exception, the copyright holders of this library give you * permission to link this library with independent modules to produce an * executable, regardless of the license terms of these independent modules, * and to copy and distribute the resulting executable under terms of your * choice, provided that you also meet, for each linked independent module, * the terms and conditions of the license of that module. An independent * module is a module which is not derived from or based on this library. */ package aphelion.client.graphics.world.event; import aphelion.client.RENDER_LAYER; import aphelion.client.graphics.world.ActorShip; import aphelion.client.graphics.world.GCImageAnimation; import aphelion.client.graphics.world.MapEntities; import aphelion.shared.gameconfig.GCImage; import aphelion.shared.gameconfig.GCInteger; import aphelion.shared.physics.EnvironmentConf; import aphelion.shared.physics.PhysicsEnvironment; import aphelion.shared.physics.entities.ActorPublic; import aphelion.shared.physics.events.pub.ActorDiedPublic; import aphelion.shared.physics.valueobjects.PhysicsShipPosition; import aphelion.shared.resource.ResourceDB; import javax.annotation.Nonnull; /** * * @author Joris */ public class ActorDiedTracker implements EventTracker { private final ResourceDB resourceDB; private final PhysicsEnvironment physicsEnv; private final MapEntities mapEntities; private boolean firstRun = true; private ActorDiedPublic event; /** the latest correct animation. * (we might have spawned other animations that are no longer correct) */ private GCImageAnimation latestAnim; /** the anim is playing for this pid. */ private int latestAnim_died; /** Used to track old animations incase a new animation is spawned somewhere else. */ private int spawnID = 0; private long renderDelay; // Todo: setting?: private static final float TIMEWARP_ALPHA_VELOCITY = 0.025f; public ActorDiedTracker(@Nonnull ResourceDB resourceDB, @Nonnull PhysicsEnvironment physicsEnv, @Nonnull MapEntities mapEntities) { this.resourceDB = resourceDB; this.physicsEnv = physicsEnv; this.mapEntities = mapEntities; } public void update(@Nonnull ActorDiedPublic event) { if (this.event == null) { this.event = event; } assert this.event == event; int pid_state0 = event.getDied(0); if (firstRun) { if (pid_state0 == 0) { return; // try again next tick } ActorShip ship = mapEntities.getActorShip(pid_state0); if (ship == null) { return; // try again next tick } // do not update the render delay after it has been set renderDelay = ship.renderDelay_current; if (renderDelay >= physicsEnv.getConfig().HIGHEST_DELAY) { return; // too old } } firstRun = false; if (latestAnim != null && this.latestAnim_died != event.getDied(0)) { // respawn, a different player died latestAnim = null; latestAnim_died = 0; } if (latestAnim == null) { if (pid_state0 != 0 && event.hasOccurred(0) && event.getOccurredAt(0) <= physicsEnv.getTick() - renderDelay) { spawnAnimations(); } } } private void spawnAnimations() { ActorShip ship = mapEntities.getActorShip(event.getDied(0)); if (ship == null) { return; } ++spawnID; this.latestAnim_died = event.getDied(0); ActorPublic actor = ship.getActor(); GCImage image = actor.getActorConfigImage("ship-explosion-animation", resourceDB); final PhysicsShipPosition actorPos = new PhysicsShipPosition(); if (image != null && actor.getHistoricPosition(actorPos, ship.renderingAt_tick, false)) { latestAnim = new MyAnimation(spawnID, resourceDB, image); latestAnim.setPositionFromPhysics(actorPos.smooth_x, actorPos.smooth_y); latestAnim.setVelocityFromPhysics(actorPos.x_vel, actorPos.y_vel); latestAnim.setStopOnHit(true); latestAnim.setAlpha(ship.getAlpha()); GCInteger radius = actor.getActorConfigInteger("ship-radius"); GCInteger bounceFriction = actor.getActorConfigInteger("ship-bounce-friction"); GCInteger bounceOtherAxisFriction = actor.getActorConfigInteger("ship-bounce-friction-other-axis"); latestAnim.setMapCollision( mapEntities.collision, physicsEnv.getMap(), radius.get(), bounceFriction.get(), bounceOtherAxisFriction.get()); mapEntities.addAnimation(RENDER_LAYER.AFTER_PROJECTILES, latestAnim, null); ship.activeDeathAnimation = latestAnim; } } private class MyAnimation extends GCImageAnimation { private final int mySpawnID; MyAnimation(int spawnID, @Nonnull ResourceDB db, @Nonnull GCImage image) { super(db, image); this.mySpawnID = spawnID; } @Override public void tick(long tick) { super.tick(tick); // fade out if the event was timewarped (or: fade it back in) // Note: currently three animations are spawned in the case of : // 1. Pid 1 dies // 2. Timewarp: pid 2 dies // 3. Timewarp: pid 1 dies (original situation was correct after all) // Two animations might better, but this issue is probably a rare occurance if (spawnID == this.mySpawnID && event.hasOccurred(0)) { this.setAlphaVelocity(TIMEWARP_ALPHA_VELOCITY); } else { this.setAlphaVelocity(-TIMEWARP_ALPHA_VELOCITY); } } } }