Starsector API
Loading...
Searching...
No Matches
LidarArrayStats.java
Go to the documentation of this file.
1package com.fs.starfarer.api.impl.combat;
2
3import java.awt.Color;
4import java.util.ArrayList;
5import java.util.Collections;
6import java.util.Comparator;
7import java.util.List;
8
9import com.fs.starfarer.api.Global;
10import com.fs.starfarer.api.combat.MutableShipStatsAPI;
11import com.fs.starfarer.api.combat.ShipAPI;
12import com.fs.starfarer.api.combat.WeaponAPI;
13import com.fs.starfarer.api.combat.WeaponAPI.WeaponType;
14import com.fs.starfarer.api.impl.campaign.ids.Tags;
15import com.fs.starfarer.api.util.Misc;
16
18
19 public static String LIDAR_WINDUP = "lidar_windup";
20
21 public static Color WEAPON_GLOW = new Color(255,50,50,155);
22
23 public static float RANGE_BONUS = 100f;
24 public static float PASSIVE_RANGE_BONUS = 25f;
25 public static float ROF_BONUS = 2f;
26 public static float RECOIL_BONUS = 75f;
27 public static float PROJECTILE_SPEED_BONUS = 50f;
28
29
30 public static class LidarDishData {
31 public float turnDir;
32 public float turnRate;
33 public float angle;
34 public float phase;
35 public float count;
36 public WeaponAPI w;
37 }
38
39 protected List<LidarDishData> dishData = new ArrayList<LidarArrayStats.LidarDishData>();
40 protected boolean needsUnapply = false;
41 protected boolean playedWindup = false;
42
43 protected boolean inited = false;
44 public void init(ShipAPI ship) {
45 if (inited) return;
46 inited = true;
47
48 needsUnapply = true;
49
50 int turnDir = 1;
51 float index = 0f;
52 float count = 0f;
53 for (WeaponAPI w : ship.getAllWeapons()) {
54 if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
55 count++;
56 }
57 }
58 List<WeaponAPI> lidar = new ArrayList<WeaponAPI>();
59 for (WeaponAPI w : ship.getAllWeapons()) {
60 if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
61 lidar.add(w);
62 }
63 }
64 Collections.sort(lidar, new Comparator<WeaponAPI>() {
65 public int compare(WeaponAPI o1, WeaponAPI o2) {
66 return (int) Math.signum(o1.getSlot().getLocation().x - o2.getSlot().getLocation().x);
67 }
68 });
69 for (WeaponAPI w : lidar) {
70 if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
71 w.setSuspendAutomaticTurning(true);
72 LidarDishData data = new LidarDishData();
73 data.turnDir = Math.signum(turnDir);
74 data.turnRate = 0.5f;
75 data.turnRate = 0.1f;
76 data.w = w;
77 data.angle = 0f;
78 data.phase = index / count;
79 data.count = count;
80 dishData.add(data);
81 turnDir = -turnDir;
82 index++;
83 }
84 }
85 }
86
87 public void rotateLidarDishes(boolean active, float effectLevel) {
88 float amount = Global.getCombatEngine().getElapsedInLastFrame();
89
90 float turnRateMult = 1f;
91 if (active) {
92 turnRateMult = 20f;
93 }
94 //turnRateMult = 0.1f;
95 //boolean first = true;
96 for (LidarDishData data : dishData) {
97 float arc = data.w.getArc();
98 float useTurnDir = data.turnDir;
99 if (active) {
100 useTurnDir = Misc.getClosestTurnDirection(data.angle, 0f);
101 }
102 float delta = useTurnDir * amount * data.turnRate * turnRateMult * arc;
103 if (active && effectLevel > 0f && Math.abs(data.angle) < Math.abs(delta * 1.5f)) {
104 data.angle = 0f;
105 } else {
106 data.angle += delta;
107 data.phase += 1f * amount;
108 if (arc < 360f) {
109 if (data.angle > arc/2f && data.turnDir > 0f) {
110 data.angle = arc/2f;
111 data.turnDir = -1f;
112 }
113 if (data.angle < -arc/2f && data.turnDir < 0f) {
114 data.angle = -arc/2f;
115 data.turnDir = 1f;
116 }
117 } else {
118 data.angle = data.angle % 360f;
119 }
120 }
121
122
123 float facing = data.angle + data.w.getArcFacing() + data.w.getShip().getFacing();
124 data.w.setFacing(facing);
125 data.w.updateBeamFromPoints();
126// if (first) {
127// System.out.println("Facing: " + facing);
128// first = false;
129// }
130 }
131 }
132
133 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) {
134 ShipAPI ship = (ShipAPI)stats.getEntity();
135 if (ship == null || ship.isHulk()) {
136 if (needsUnapply) {
137 unmodify(id, stats);
138 for (WeaponAPI w : ship.getAllWeapons()) {
139 if (!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
140 (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY)) {
141 w.setGlowAmount(0, null);
142 }
143 }
144 needsUnapply = false;
145 }
146 return;
147 }
148
149 init(ship);
150
151 //lidarFacingOffset += am
152
153 boolean active = state == State.IN || state == State.ACTIVE || state == State.OUT;
154
155 rotateLidarDishes(active, effectLevel);
156
157 if (active) {
158 modify(id, stats, effectLevel);
159 needsUnapply = true;
160 } else {
161 if (needsUnapply) {
162 unmodify(id, stats);
163 for (WeaponAPI w : ship.getAllWeapons()) {
164 if (w.getSlot().isSystemSlot()) continue;
165 if (!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
166 (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY)) {
167 w.setGlowAmount(0, null);
168 }
169 }
170 needsUnapply = false;
171 }
172 }
173
174 if (!active) return;
175
176
177 for (WeaponAPI w : ship.getAllWeapons()) {
178 if (w.getSlot().isSystemSlot()) continue;
179 if (w.getType() == WeaponType.MISSILE) continue;
180 if (state == State.IN) {
181 if (!(w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR))) {
182 w.setForceNoFireOneFrame(true);
183 }
184 } else {
185 if (!(!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
186 (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY))) {
187 w.setForceNoFireOneFrame(true);
188 }
189 }
190 }
191
192 Color glowColor = WEAPON_GLOW;
193
194 float lidarRange = 500;
195 for (WeaponAPI w : ship.getAllWeapons()) {
196 if (!w.isDecorative() && w.getSlot().isHardpoint() && !w.isBeam() &&
197 (w.getType() == WeaponType.BALLISTIC || w.getType() == WeaponType.ENERGY)) {
198 lidarRange = Math.max(lidarRange, w.getRange());
199 w.setGlowAmount(effectLevel, glowColor);
200 }
201 }
202 lidarRange += 100f;
203 stats.getBeamWeaponRangeBonus().modifyFlat("lidararray", lidarRange);
204// for (WeaponAPI w : ship.getAllWeapons()) {
205// if (w.isDecorative() && w.getSpec().hasTag(Tags.LIDAR)) {
206// if (state == State.IN) {
207// w.setForceFireOneFrame(true);
208// }
209// }
210// }
211
212 // always wait a quarter of a second before starting to fire the targeting lasers
213 // this is the worst-case turn time required for the dishes to face front
214 // doing this to keep the timing of the lidar ping sounds consistent relative
215 // to when the windup sound plays
216 float fireThreshold = 0.25f / 3.25f;
217 fireThreshold += 0.02f; // making sure there's only 4 lidar pings; lines up with the timing of the lidardish weapon
218 //fireThreshold = 0f;
219 for (LidarDishData data : dishData) {
220 boolean skip = data.phase % 1f > 1f / data.count;
221 //skip = data.phase % 1f > 0.67f;
222 skip = false;
223 if (skip) continue;
224 if (data.w.isDecorative() && data.w.getSpec().hasTag(Tags.LIDAR)) {
225 if (state == State.IN && Math.abs(data.angle) < 5f && effectLevel >= fireThreshold) {
226 data.w.setForceFireOneFrame(true);
227 }
228 }
229 }
230
231 if (((state == State.IN && effectLevel > 0.67f) || state == State.ACTIVE) && !playedWindup) {
232 Global.getSoundPlayer().playSound(LIDAR_WINDUP, 1f, 1f, ship.getLocation(), ship.getVelocity());
233 playedWindup = true;
234 }
235 }
236
237
238 protected void modify(String id, MutableShipStatsAPI stats, float effectLevel) {
239 float mult = 1f + ROF_BONUS * effectLevel;
240 //float mult = 1f + ROF_BONUS;
241 stats.getBallisticWeaponRangeBonus().modifyPercent(id, RANGE_BONUS);
242 stats.getEnergyWeaponRangeBonus().modifyPercent(id, RANGE_BONUS);
243 stats.getBallisticRoFMult().modifyMult(id, mult);
244 stats.getEnergyRoFMult().modifyMult(id, mult);
245 //stats.getBallisticWeaponFluxCostMod().modifyMult(id, 1f - (FLUX_REDUCTION * 0.01f));
246 stats.getMaxRecoilMult().modifyMult(id, 1f - (0.01f * RECOIL_BONUS));
247 stats.getRecoilPerShotMult().modifyMult(id, 1f - (0.01f * RECOIL_BONUS));
248 stats.getRecoilDecayMult().modifyMult(id, 1f - (0.01f * RECOIL_BONUS));
249
250 stats.getBallisticProjectileSpeedMult().modifyPercent(id, PROJECTILE_SPEED_BONUS);
251 stats.getEnergyProjectileSpeedMult().modifyPercent(id, PROJECTILE_SPEED_BONUS);
252 }
253 protected void unmodify(String id, MutableShipStatsAPI stats) {
254 stats.getBallisticWeaponRangeBonus().modifyPercent(id, PASSIVE_RANGE_BONUS);
255 stats.getEnergyWeaponRangeBonus().modifyPercent(id, PASSIVE_RANGE_BONUS);
256// stats.getBallisticWeaponRangeBonus().unmodifyPercent(id);
257// stats.getEnergyWeaponRangeBonus().unmodifyPercent(id);
258
259 stats.getBallisticRoFMult().unmodifyMult(id);
260 stats.getEnergyRoFMult().unmodifyMult(id);
261 stats.getMaxRecoilMult().unmodifyMult(id);
262 stats.getRecoilPerShotMult().unmodifyMult(id);
263 stats.getRecoilDecayMult().unmodifyMult(id);
264
265 stats.getBallisticProjectileSpeedMult().unmodifyPercent(id);
266 stats.getEnergyProjectileSpeedMult().unmodifyPercent(id);
267
268 playedWindup = false;
269 }
270
271
272 public void unapply(MutableShipStatsAPI stats, String id) {
273 // never called due to runScriptWhileIdle:true in the .system file
274 }
275
276 public StatusData getStatusData(int index, State state, float effectLevel) {
277 if (state == State.IDLE || state == State.COOLDOWN) {
278 if (index == 3) {
279 return new StatusData("weapon range +" + (int) PASSIVE_RANGE_BONUS + "%", false);
280 }
281 }
282 if (effectLevel <= 0f) return null;
283
284 //float mult = 1f + ROF_BONUS;
285 float mult = 1f + ROF_BONUS;
286 float bonusPercent = (int) ((mult - 1f) * 100f);
287 if (index == 3) {
288 return new StatusData("weapon range +" + (int) RANGE_BONUS + "%", false);
289 }
290 if (index == 2) {
291 return new StatusData("rate of fire +" + (int) bonusPercent + "%", false);
292 }
293// if (index == 1) {
294// return new StatusData("ballistic flux use -" + (int) FLUX_REDUCTION + "%", false);
295// }
296 if (index == 1) {
297 return new StatusData("weapon recoil -" + (int) RECOIL_BONUS + "%", false);
298 }
299 if (index == 0 && PROJECTILE_SPEED_BONUS > 0) {
300 return new StatusData("projectile speed +" + (int) PROJECTILE_SPEED_BONUS + "%", false);
301 }
302 return null;
303 }
304
305 public String getDisplayNameOverride(State state, float effectLevel) {
306 if (state == State.IDLE || state == State.COOLDOWN) {
307 return "lidar array - passive";
308 }
309 return null;
310 }
311
312}
static SoundPlayerAPI getSoundPlayer()
Definition Global.java:43
static CombatEngineAPI getCombatEngine()
Definition Global.java:63
void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel)
void unmodify(String id, MutableShipStatsAPI stats)
void rotateLidarDishes(boolean active, float effectLevel)
void unapply(MutableShipStatsAPI stats, String id)
void modify(String id, MutableShipStatsAPI stats, float effectLevel)
String getDisplayNameOverride(State state, float effectLevel)
StatusData getStatusData(int index, State state, float effectLevel)
SoundAPI playSound(String id, float pitch, float volume, Vector2f loc, Vector2f vel)