Coverage Summary for Class: NPCShip (com.mygdx.game.Entitys)
| Class | Class, % | Method, % | Line, % |
|---|---|---|---|
| NPCShip | 100% (1/1) | 100% (12/12) | 89.9% (71/79) |
1 package com.mygdx.game.Entitys; 2 3 import com.badlogic.gdx.ai.fsm.DefaultStateMachine; 4 import com.badlogic.gdx.ai.fsm.StateMachine; 5 import com.badlogic.gdx.ai.steer.behaviors.Arrive; 6 import com.badlogic.gdx.math.Vector2; 7 import com.badlogic.gdx.utils.JsonValue; 8 import com.badlogic.gdx.utils.TimeUtils; 9 import com.mygdx.game.AI.EnemyState; 10 import com.mygdx.game.Components.*; 11 import com.mygdx.game.Managers.GameManager; 12 import com.mygdx.game.Physics.CollisionCallBack; 13 import com.mygdx.game.Physics.CollisionInfo; 14 import com.mygdx.utils.QueueFIFO; 15 import com.mygdx.utils.Utilities; 16 17 import java.util.Objects; 18 19 /** 20 * NPC ship entity class. 21 */ 22 public class NPCShip extends Ship implements CollisionCallBack { 23 public StateMachine<NPCShip, EnemyState> stateMachine; 24 private static JsonValue AISettings; 25 private long lastShootTime; //Added for Assessment 2, stores the time when the ship last attacked 26 27 /** 28 * Creates an initial state machine 29 * Changed for Assessment 2: 30 * - Added lastShootTime to store the time when the ship last attacked 31 */ 32 public NPCShip() { 33 super(); 34 //QueueFIFO<Vector2> path = new QueueFIFO<>(); 35 36 if (AISettings == null) { 37 AISettings = GameManager.getSettings().get("AI"); 38 } 39 40 stateMachine = new DefaultStateMachine<>(this, EnemyState.WANDER); 41 42 setName("NPC"); 43 AINavigation nav = new AINavigation(); 44 45 addComponent(nav); 46 47 48 RigidBody rb = getComponent(RigidBody.class); 49 // rb.setCallback(this); 50 51 JsonValue starting = GameManager.getSettings().get("starting"); 52 53 // aggro trigger 54 rb.addTrigger(Utilities.tilesToDistance(starting.getFloat("argoRange_tiles")), "aggro"); 55 getComponent(Pirate.class).setInfiniteAmmo(true); 56 57 lastShootTime = TimeUtils.millis() / 1000; 58 } 59 60 /** 61 * gets the top of targets from pirate component 62 * 63 * @return the top target 64 */ 65 private Ship getTarget() { 66 return getComponent(Pirate.class).getTarget(); 67 } 68 69 /** 70 * Amended for Assessment 2: Added check for life and kills stateMachine and ship in case of death 71 * updates the state machine 72 */ 73 @Override 74 public void update() { 75 if(!isAlive()) { 76 kill(); 77 stateMachine = null; 78 }else{ 79 super.update(); 80 stateMachine.update(); 81 } 82 } 83 84 /** 85 * Added for Assessment 2 86 * Removes the ship from play 87 */ 88 private void kill() { 89 getComponent(Renderable.class).hide(); 90 Transform t = getComponent(Transform.class); 91 t.setPosition(20000, 20000); 92 93 RigidBody rb = getComponent(RigidBody.class); 94 rb.setPosition(t.getPosition()); 95 rb.setVelocity(0, 0); 96 97 dispose(); 98 } 99 100 /** 101 * creates a new steering behaviour that will make the NPC beeline for the target (doesn't factor in obstacles) 102 */ 103 public void followTarget() { 104 if (getTarget() == null) { 105 stopMovement(); 106 return; 107 } 108 AINavigation nav = getComponent(AINavigation.class); 109 110 Arrive<Vector2> arrives = new Arrive<>(nav, 111 getTarget().getComponent(Transform.class)) 112 .setTimeToTarget(AISettings.getFloat("accelerationTime")) 113 .setArrivalTolerance(AISettings.getFloat("arrivalTolerance")) 114 .setDecelerationRadius(AISettings.getFloat("slowRadius")); 115 116 nav.setBehavior(arrives); 117 } 118 119 /** 120 * Added for Assessment 2 121 * Creates a new steering behaviour that will make the NPC circle the college it is attached to (doesn't factor in obstacles) 122 */ 123 public void circleOrigin() { 124 College origin = GameManager.getCollege(getFaction().id); 125 Vector2 dirToOrigin = new Vector2( (origin.getPosition().x - getPosition().x),(origin.getPosition().y - getPosition().y) ); 126 double currentAngle = Utilities.vectorToAngle(dirToOrigin); 127 128 double theta; 129 if (dirToOrigin.x > 0){ 130 theta = currentAngle - (Math.PI)/12; 131 }else{ 132 theta = currentAngle + (Math.PI)/12; 133 } 134 135 Transform target = new Transform(); 136 float radius = Utilities.tilesToDistance(30); 137 float xPos = (float) (origin.getPosition().x + (radius * Math.cos(theta))); 138 float yPos = (float) (origin.getPosition().y + (radius * Math.sin(theta))); 139 target.setPosition(xPos,yPos); 140 141 AINavigation nav = getComponent(AINavigation.class); 142 143 Arrive<Vector2> arrives = new Arrive<>(nav,target) 144 .setTimeToTarget(AISettings.getFloat("accelerationTime")) 145 .setArrivalTolerance(AISettings.getFloat("arrivalTolerance")) 146 .setDecelerationRadius(AISettings.getFloat("slowRadius")); 147 148 nav.setBehavior(arrives); 149 } 150 151 /** 152 * stops all movement and sets the behaviour to null 153 */ 154 public void stopMovement() { 155 AINavigation nav = getComponent(AINavigation.class); 156 nav.setBehavior(null); 157 nav.stop(); 158 } 159 160 /** 161 * Added for Assessment 2, calculates the direction an Enemy Ship is in 162 */ 163 public Vector2 directionToShip(Ship ship) { 164 Vector2 shipLocale = ship.getPosition(); 165 Vector2 thisPosition = getPosition(); 166 167 float xDiff = shipLocale.x-thisPosition.x; 168 float yDiff = shipLocale.y-thisPosition.y; 169 170 return new Vector2(xDiff,yDiff); 171 } 172 173 /** 174 * Added for Assessment 2, shoots a cannonball towards the player ship 175 */ 176 public void attackShip(Ship ship) { 177 long current = TimeUtils.millis() / 1000; 178 if (current > lastShootTime) { 179 Vector2 direction = directionToShip(ship); 180 shoot(direction); 181 } 182 lastShootTime = current; 183 } 184 185 /** 186 * @return the current state the NPCShip is in 187 */ 188 public EnemyState getCurrentState() { 189 return stateMachine.getCurrentState(); 190 } 191 192 /** 193 * if the aggro fixture hit a ship set it as the target 194 * @param info the collision info 195 */ 196 @Override 197 public void EnterTrigger(CollisionInfo info) { 198 super.EnterTrigger(info); 199 if (info.a instanceof Ship) { 200 Ship other = (Ship) info.a; 201 if (Objects.equals(other.getComponent(Pirate.class).getFaction(), getComponent(Pirate.class).getFaction())) { 202 // is the same faction 203 return; 204 } 205 // add the new collision as a new target 206 Pirate pirate = getComponent(Pirate.class); 207 pirate.addTarget(other); 208 } 209 } 210 211 /** 212 * if a target has left remove it from the potential targets Queue 213 * @param info collision info 214 */ 215 @Override 216 public void ExitTrigger(CollisionInfo info) { 217 if (!(info.a instanceof Ship)) { 218 return; 219 } 220 Pirate pirate = getComponent(Pirate.class); 221 Ship o = (Ship) info.a; 222 // remove the object from the targets list 223 for (Ship targ : pirate.getTargets()) { 224 if (targ == o) { 225 pirate.getTargets().remove(targ); 226 break; 227 } 228 } 229 } 230 }