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 }