/* * Aphelion * Copyright (c) 2014 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.shared.physics; import aphelion.shared.gameconfig.GameConfig; import aphelion.shared.physics.entities.ActorPublic; import aphelion.shared.physics.entities.ProjectilePublic; import aphelion.shared.physics.valueobjects.PhysicsPoint; import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import aphelion.shared.physics.valueobjects.PhysicsPositionVector; import org.junit.Test; /** * * @author joris */ public class ForceEmitterTest extends PhysicsTest { public static void applyMoreTestSettings(PhysicsEnvironment env) { List<Object> yamlDocuments; try { yamlDocuments = GameConfig.loadYaml( "- weapon-slot-repel: repelA\n" + " weapon-slot-burst: repelB\n" + " weapon-slot-bomb: bomb\n" + " projectile-force-function: LINEAR\n" + " ship-speed: "+EnvironmentConf.MAX_POSITION+"\n" + "- selector: {weapon: [repelA, repelB]}\n" + " projectile-force-distance-ship: 204800\n" + " projectile-force-velocity-ship: 3840000\n" + " projectile-force-distance-projectile: 409600\n" + " projectile-force-velocity-projectile: 5120000\n" + " projectile-speed: 0\n" + " projectile-hit-ship: false\n" + " projectile-hit-tile: false\n" + " projectile-offset-y: 0\n" + " projectile-expiration-ticks: 2\n" + " projectile-speed-relative: false\n" + "- selector: {weapon: [repelB]}\n" + " projectile-force-velocity-ship: 10\n" + " projectile-force-velocity-projectile: 20\n" + " projectile-expiration-ticks: 4\n" + "- selector: {weapon: [bomb]}\n" + " projectile-speed: 3\n" + " projectile-expiration-ticks: 1000\n" + " projectile-angle-relative: true\n" + " projectile-offset-x: 0\n" + " projectile-offset-y: 0\n" ); } catch (Exception ex) { throw new Error(ex); } env.loadConfig(env.getTick() - env.getConfig().HIGHEST_DELAY, "ForceEmitterTest", yamlDocuments); } @Override public void setUp() { super.setUp(); applyMoreTestSettings(env); } @Test public void testStrongForceOnShip() { SimpleEnvironment env = (SimpleEnvironment) this.env; env.actorNew(0, ACTOR_FIRST, 1234, "NX-01"); env.actorNew(0, ACTOR_SECOND, 1234, "O'Neill"); final PhysicsPoint FIRST_POSITION_START = new PhysicsPoint(20000, 50000); final PhysicsPoint SECOND_POSITION_START = new PhysicsPoint(20000 + 204800 / 2, 50000); final PhysicsPoint SECOND_VEL_START = new PhysicsPoint(100, 150); SECOND_POSITION_START.sub(SECOND_VEL_START); env.actorWarp(0, ACTOR_FIRST , false, FIRST_POSITION_START.x, FIRST_POSITION_START.y, 0, 0, 0); env.actorWarp(0, ACTOR_SECOND, false, SECOND_POSITION_START.x, SECOND_POSITION_START.y, SECOND_VEL_START.x, SECOND_VEL_START.y, 0); env.actorWeapon(1, ACTOR_FIRST, WEAPON_SLOT.REPEL); // repelA (applies force for 2 ticks) ActorPublic firstActor = env.getActor(ACTOR_FIRST); ActorPublic secondActor = env.getActor(ACTOR_SECOND); assertVelocity(SECOND_VEL_START.x, SECOND_VEL_START.y, secondActor); env.tick(); // now at 1 // force point is (20000,50000) assertPosition(FIRST_POSITION_START.x, // (20000, 50000) FIRST_POSITION_START.y, firstActor); assertVelocity(SECOND_VEL_START.x + (3840000 / 2), // (1920100, 150) SECOND_VEL_START.y, secondActor); // should not affect the position yet assertPosition(SECOND_POSITION_START.x + SECOND_VEL_START.x, // (2042500, 50150) SECOND_POSITION_START.y + SECOND_VEL_START.y, // secondActor); env.tick(); // now at 2 assertPosition(FIRST_POSITION_START.x, FIRST_POSITION_START.y, firstActor); // force is applied for 2 ticks, but the force was so strong the emitter is now out of range assertVelocity(SECOND_VEL_START.x + (3840000 / 2), SECOND_VEL_START.y, secondActor); assertPosition(SECOND_POSITION_START.x + SECOND_VEL_START.x * 2 + (3840000 / 2), SECOND_POSITION_START.y + SECOND_VEL_START.y * 2, secondActor); env.tick(); // now at 3 assertPosition(FIRST_POSITION_START.x, FIRST_POSITION_START.y, firstActor); assertVelocity(SECOND_VEL_START.x + (3840000 / 2), SECOND_VEL_START.y, secondActor); assertPosition(SECOND_POSITION_START.x + SECOND_VEL_START.x * 3 + (3840000 / 2) * 2, SECOND_POSITION_START.y + SECOND_VEL_START.y * 3, secondActor); } @Test public void testWeakForceOnShip() { SimpleEnvironment env = (SimpleEnvironment) this.env; env.actorNew(0, ACTOR_FIRST, 1234, "NX-01"); env.actorNew(0, ACTOR_SECOND, 1234, "O'Neill"); final PhysicsPoint FIRST_POSITION_START = new PhysicsPoint(20000, 50000); final PhysicsPoint SECOND_POSITION_START = new PhysicsPoint(20000 + 204800 / 2, 50000); final PhysicsPoint SECOND_VEL_START = new PhysicsPoint(100, 150); SECOND_POSITION_START.sub(SECOND_VEL_START); env.actorWarp(0, ACTOR_FIRST , false, FIRST_POSITION_START.x, FIRST_POSITION_START.y, 0, 0, 0); env.actorWarp(0, ACTOR_SECOND, false, SECOND_POSITION_START.x, SECOND_POSITION_START.y, SECOND_VEL_START.x, SECOND_VEL_START.y, 0); env.actorWeapon(1, ACTOR_FIRST, WEAPON_SLOT.BURST); // repelB (applies force for 4 ticks) ActorPublic firstActor = env.getActor(ACTOR_FIRST); ActorPublic secondActor = env.getActor(ACTOR_SECOND); assertVelocity( SECOND_VEL_START.x, SECOND_VEL_START.y, secondActor); env.tick(); // now at 1 assertPosition(FIRST_POSITION_START.x, FIRST_POSITION_START.y, firstActor); assertVelocity(SECOND_VEL_START.x + 5, // 5 is the force applied SECOND_VEL_START.y, secondActor); // should not affect the position yet assertPosition(SECOND_POSITION_START.x + SECOND_VEL_START.x, SECOND_POSITION_START.y + SECOND_VEL_START.y, secondActor); env.tick(); // now at 2 assertPosition(FIRST_POSITION_START.x, FIRST_POSITION_START.y, firstActor); assertPosition(SECOND_POSITION_START.x + SECOND_VEL_START.x * 2 + 5, SECOND_POSITION_START.y + SECOND_VEL_START.y * 2, secondActor); assertVelocity(SECOND_VEL_START.x + 5 + 4, SECOND_VEL_START.y, secondActor); env.tick(); // now at 3 assertVelocity(SECOND_VEL_START.x + 5 + 4 + 4, SECOND_VEL_START.y, secondActor); env.tick(); // now at 4 (weapon expires at this tick) assertVelocity(SECOND_VEL_START.x + 5 + 4 + 4 + 4, SECOND_VEL_START.y, secondActor); env.tick(); // now at 5 assertVelocity(SECOND_VEL_START.x + 5 + 4 + 4 + 4, SECOND_VEL_START.y, secondActor); env.tick(); // now at 6 assertVelocity(SECOND_VEL_START.x + 5 + 4 + 4 + 4, SECOND_VEL_START.y, secondActor); } @Test public void testLateAsPossibleWithoutTimeWarp_forceOnProjectile_control() { SimpleEnvironment env = (SimpleEnvironment) this.env; env.actorNew(0, ACTOR_FIRST , 1234, "NX-01"); env.actorNew(0, ACTOR_SECOND, 1234, "O'Neill"); final PhysicsPoint FIRST_POSITION_START = new PhysicsPoint(20000 , 50000); final PhysicsPoint SECOND_POSITION_START = new PhysicsPoint(20000 + 204800 / 2, 50000); env.actorWarp(0, ACTOR_FIRST , false, FIRST_POSITION_START.x , FIRST_POSITION_START.y , 0, 0, EnvironmentConf.ROTATION_1_4TH); env.actorWarp(0, ACTOR_SECOND, false, SECOND_POSITION_START.x, SECOND_POSITION_START.y, 0, 0, EnvironmentConf.ROTATION_1_4TH * 3); env.actorWeapon(1, ACTOR_FIRST, WEAPON_SLOT.BOMB); // This is the operation that will arrive late in the next tests: env.actorWeapon(7, ACTOR_SECOND, WEAPON_SLOT.BURST); // repelB ActorPublic firstActor = env.getActor(ACTOR_FIRST); ActorPublic secondActor = env.getActor(ACTOR_SECOND); env.tick(); // 1 env.tick(); // 2 ProjectilePublic projectile = env.projectileIterator().next(); assertEquals(ACTOR_FIRST, projectile.getOwner()); assertVelocity(3, 0, projectile); while (env.getTick() < 6) { env.tick(); } assertVelocity(3, 0, projectile); // force is applied during 4 ticks env.tick(); assertVelocity(3 -15, 0, projectile); env.tick(); assertVelocity(3 -15-15, 0, projectile); env.tick(); assertVelocity(3 -15-15-14, 0, projectile); env.tick(); assertVelocity(3 -15-15-14-14, 0, projectile); env.tick(); assertVelocity(3 -15-15-14-14, 0, projectile); while (env.getTick() < 40) { env.tick(); } assertFalse("Test case needs updating because the history length for projectiles has been increased", projectile.getHistoricPosition(new PhysicsPositionVector(), 6, false)); assertVelocity( 7, 3 -15, 0, projectile); assertVelocity( 8, 3 -15-15, 0, projectile); assertVelocity( 9, 3 -15-15-14, 0, projectile); assertVelocity(10, 3 -15-15-14-14, 0, projectile); assertVelocity(11, 3 -15-15-14-14, 0, projectile); assertVelocity(3 -15-15-14-14, 0, projectile); assertPosition( 18288, FIRST_POSITION_START.y, projectile); // (first actor is also affected by the repel) assertPosition(19463,50000, firstActor); assertVelocity(-17,0, firstActor); assertEquals(env.getTimewarpCount(), 0); } @Test public void testLateAsPossibleWithoutTimeWarp_forceOnProjectile_fail() { SimpleEnvironment env = (SimpleEnvironment) this.env; env.actorNew(0, ACTOR_FIRST , 1234, "NX-01"); env.actorNew(0, ACTOR_SECOND, 1234, "O'Neill"); final PhysicsPoint FIRST_POSITION_START = new PhysicsPoint(20000 , 50000); final PhysicsPoint SECOND_POSITION_START = new PhysicsPoint(20000 + 204800 / 2, 50000); env.actorWarp(0, ACTOR_FIRST , false, FIRST_POSITION_START.x , FIRST_POSITION_START.y , 0, 0, EnvironmentConf.ROTATION_1_4TH); env.actorWarp(0, ACTOR_SECOND, false, SECOND_POSITION_START.x, SECOND_POSITION_START.y, 0, 0, EnvironmentConf.ROTATION_1_4TH * 3); env.actorWeapon(1, ACTOR_FIRST, WEAPON_SLOT.BOMB); ActorPublic firstActor = env.getActor(ACTOR_FIRST); ActorPublic secondActor = env.getActor(ACTOR_SECOND); final int PROJECTILE_HISTORY_LENGTH = env.getConfig().TRAILING_STATE_DELAY + env.getConfig().MINIMUM_HISTORY_TICKS; assert PROJECTILE_HISTORY_LENGTH == 34 : "test case needs adjusting"; env.tick(); // 1 env.tick(); // 2 ProjectilePublic projectile = env.projectileIterator().next(); assertEquals(ACTOR_FIRST, projectile.getOwner()); assertVelocity(3, 0, projectile); while (env.getTick() < 40) { env.tick(); } assertVelocity(3, 0, projectile); assertPosition( FIRST_POSITION_START.x + 3 * ((int) env.getTick() - 1), FIRST_POSITION_START.y, projectile); // the only difference between this test case and the control is that this this operation is late: assertEquals(env.getTick() - PROJECTILE_HISTORY_LENGTH + 1, 7); // this is the most recent tick we can not fix without a timewarp env.actorWeapon(7, ACTOR_SECOND, WEAPON_SLOT.BURST); // repelB assertPosition( FIRST_POSITION_START.x + 3 * ((int) env.getTick() - 1), FIRST_POSITION_START.y, projectile); env.doReexecuteDirtyPositionPath(); assertVelocity( 7, 3, 0, projectile); // it should have been applied to this tick, but this is not possible assertVelocity(3 -15-15-14, 0, projectile); // (first actor is also affected by the repel) assertPosition(19463,50000, firstActor); assertVelocity(-17,0, firstActor); assertEquals(env.getTimewarpCount(), 0); } @Test public void testLateAsPossibleWithoutTimeWarp_forceOnProjectile() { SimpleEnvironment env = (SimpleEnvironment) this.env; env.actorNew(0, ACTOR_FIRST , 1234, "NX-01"); env.actorNew(0, ACTOR_SECOND, 1234, "O'Neill"); final PhysicsPoint FIRST_POSITION_START = new PhysicsPoint(20000 , 50000); final PhysicsPoint SECOND_POSITION_START = new PhysicsPoint(20000 + 204800 / 2, 50000); env.actorWarp(0, ACTOR_FIRST , false, FIRST_POSITION_START.x , FIRST_POSITION_START.y , 0, 0, EnvironmentConf.ROTATION_1_4TH); env.actorWarp(0, ACTOR_SECOND, false, SECOND_POSITION_START.x, SECOND_POSITION_START.y, 0, 0, EnvironmentConf.ROTATION_1_4TH * 3); env.actorWeapon(1, ACTOR_FIRST, WEAPON_SLOT.BOMB); ActorPublic firstActor = env.getActor(ACTOR_FIRST); ActorPublic secondActor = env.getActor(ACTOR_SECOND); final int PROJECTILE_HISTORY_LENGTH = env.getConfig().TRAILING_STATE_DELAY + env.getConfig().MINIMUM_HISTORY_TICKS; assert PROJECTILE_HISTORY_LENGTH == 34 : "test case needs adjusting"; env.tick(); // 1 env.tick(); // 2 ProjectilePublic projectile = env.projectileIterator().next(); assertEquals(ACTOR_FIRST, projectile.getOwner()); assertVelocity(3, 0, projectile); while (env.getTick() < 39) { env.tick(); } // (previous test case has "40") assertVelocity(3, 0, projectile); assertPosition( FIRST_POSITION_START.x + 3 * ((int) env.getTick() - 1), FIRST_POSITION_START.y, projectile); // the only difference between this test case and the control is that this this operation is late: assertEquals(env.getTick() - PROJECTILE_HISTORY_LENGTH + 1, 6); // this is the oldest tick we can fix without a timewarp env.actorWeapon(7, ACTOR_SECOND, WEAPON_SLOT.BURST); // repelB assertPosition( FIRST_POSITION_START.x + 3 * ((int) env.getTick() - 1), FIRST_POSITION_START.y, projectile); env.doReexecuteDirtyPositionPath(); assertVelocity( 7, 3 -15, 0, projectile); assertVelocity( 8, 3 -15-15, 0, projectile); assertVelocity( 9, 3 -15-15-14, 0, projectile); assertVelocity(10, 3 -15-15-14-14, 0, projectile); assertVelocity(11, 3 -15-15-14-14, 0, projectile); assertVelocity(3 -15-15-14-14, 0, projectile); assertPosition( 18343, FIRST_POSITION_START.y, projectile); env.tick(); // 40 assertPosition( 18288, FIRST_POSITION_START.y, projectile); // (first actor is also affected by the repel) assertPosition(19463,50000, firstActor); assertVelocity(-17,0, firstActor); assertEquals(env.getTimewarpCount(), 0); } }