001package com.fs.starfarer.api.impl.combat.dweller; 002 003import org.lwjgl.util.vector.Vector2f; 004 005import com.fs.starfarer.api.Global; 006import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType; 007import com.fs.starfarer.api.campaign.CargoStackAPI; 008import com.fs.starfarer.api.campaign.SpecialItemData; 009import com.fs.starfarer.api.combat.CombatEngineAPI; 010import com.fs.starfarer.api.combat.MutableShipStatsAPI; 011import com.fs.starfarer.api.combat.ShipAPI; 012import com.fs.starfarer.api.combat.ShipAPI.HullSize; 013import com.fs.starfarer.api.combat.ShipCommand; 014import com.fs.starfarer.api.impl.campaign.ids.Items; 015import com.fs.starfarer.api.impl.combat.threat.RoilingSwarmEffect.SwarmMember; 016import com.fs.starfarer.api.util.IntervalUtil; 017import com.fs.starfarer.api.util.Misc; 018 019/** 020 * For non-dweller ships with a sort-of shroud. 021 */ 022public class ShroudedMantleHullmod extends HumanShipShroudedHullmod { 023 024 public static float HEAL_MULT = 0.5f; 025 026 public static float LUNGE_COOLDOWN = 10f; 027 public static float LUNGE_DUR = 4f; 028 public static float LUNGE_SPEED = 250f; 029 030 @Override 031 public void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id) { 032 super.applyEffectsBeforeShipCreation(hullSize, stats, id); 033 034 boolean sMod = isSMod(stats); 035 if (sMod) { 036 stats.getDynamic().getMod(AssayingRiftEffect.HUNGERING_RIFT_HEAL_MOD_HUMAN_SHIPS).modifyFlat(id, HEAL_MULT); 037 } 038 } 039 040 public String getDescriptionParam(int index, HullSize hullSize) { 041 if (index == 0) return "" + (int) CREW_CASUALTIES + "%"; 042 return null; 043 } 044 045 @Override 046 public String getSModDescriptionParam(int index, HullSize hullSize, ShipAPI ship) { 047 if (index == 0) return "" + (int) Math.round(HEAL_MULT * 100f) + "%"; 048 return null; 049 } 050 051 @Override 052 public CargoStackAPI getRequiredItem() { 053 return Global.getSettings().createCargoStack(CargoItemType.SPECIAL, 054 new SpecialItemData(Items.SHROUDED_MANTLE, null), null); 055 } 056 057 058 public static String DATA_KEY = "core_ShroudedMantleHullmod_data_key"; 059 public static class ShroudedMantleHullmodData { 060 IntervalUtil interval = new IntervalUtil(0.75f, 1.25f); 061 float hullAtPrevLunge = 1f; 062 float cooldown = 0f; 063 boolean lunging = false; 064 Vector2f lungeDest; 065 float lungeElapsed = 0f; 066 boolean fadedFlash = false; 067 } 068 069 @Override 070 public void advanceInCombat(ShipAPI ship, float amount) { 071 super.advanceInCombat(ship, amount); 072 073 if (!ship.isAlive()) return; 074 if (amount <= 0f) return; 075 076 CombatEngineAPI engine = Global.getCombatEngine(); 077 078 String key = DATA_KEY + "_" + ship.getId(); 079 ShroudedMantleHullmodData data = (ShroudedMantleHullmodData) engine.getCustomData().get(key); 080 if (data == null) { 081 data = new ShroudedMantleHullmodData(); 082 engine.getCustomData().put(key, data); 083 } 084 085 boolean forceUse = false; 086 087// if (Keyboard.isKeyDown(Keyboard.KEY_K)) { 088// forceUse = true; 089// data.cooldown = 0f; 090// } 091 092 if (data.cooldown > 0) { 093 data.cooldown -= amount; 094 if (data.cooldown < 0) data.cooldown = 0; 095 if (data.cooldown > 0) { 096 return; 097 } 098 } 099 100 if (ship.getFluxLevel() > 0.95f && ship.getHullLevel() > 0.25f && 101 ship.getShield() != null && ship.getShield().isOn()) { 102 forceUse = true; 103 } 104 if (ship.getFluxTracker().isOverloaded()) { 105 forceUse = true; 106 } 107 108 data.interval.advance(amount * 2f); 109 if ((data.interval.intervalElapsed() || forceUse) && !data.lunging) { 110 float hull = ship.getHullLevel(); 111 data.hullAtPrevLunge = Math.max(hull, data.hullAtPrevLunge); 112 if (hull <= data.hullAtPrevLunge - ConvulsiveLungeSystemAI.HULL_LOSS_FOR_PULLBACK || forceUse) { 113 data.lunging = true; 114 data.lungeElapsed = 0f; 115 data.lungeDest = null; 116 data.fadedFlash = false; 117 data.hullAtPrevLunge = hull; 118 } 119 } 120 121 doLunge(data, ship, amount); 122 } 123 124 protected void doLunge(ShroudedMantleHullmodData data, ShipAPI ship, float amount) { 125 if (!data.lunging) return; 126 //if (ship.getFluxTracker().isOverloadedOrVenting()) return; 127 128 DwellerShroud shroud = DwellerShroud.getShroudFor(ship); 129 130 if (data.lungeElapsed < 1f || data.lungeDest == null) { 131 if (data.lungeDest == null) { 132 data.lungeDest = Misc.getUnitVectorAtDegreeAngle(Global.getSettings().getSafeMovementDir(ship)); 133 data.lungeDest.scale(ConvulsiveLungeSystemScript.PULL_DIST); 134 Vector2f.add(data.lungeDest, ship.getLocation(), data.lungeDest); 135 } 136 137 if (data.lungeDest != null) { 138 if (shroud != null) { 139 Vector2f dir = Misc.getUnitVector(ship.getLocation(), data.lungeDest); 140 float accel = ConvulsiveLungeSystemScript.PARTICLE_WINDUP_ACCEL * amount * 1f; 141 if (!ship.isFrigate()) accel *= 2f; 142 boolean affect = true; 143 for (SwarmMember p : shroud.getMembers()) { 144 if (affect) { 145 p.vel.x += dir.x * accel; 146 p.vel.y += dir.y * accel; 147 } 148 if (ship.isFrigate()) { 149 affect = !affect; 150 } 151 } 152 } 153 } 154 } else if (data.lungeDest != null) { 155 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(ship.getLocation(), data.lungeDest)); 156 157 boolean slowdown = data.lungeElapsed > LUNGE_DUR - 1f; 158 if (slowdown) { 159 dir = Misc.normalise(new Vector2f(ship.getVelocity())); 160 dir.negate(); 161 } 162 163 if (!data.fadedFlash) { 164 if (shroud != null) { 165 for (SwarmMember p : shroud.getMembers()) { 166 if (p.flash != null) { 167 p.flash.fadeOut(); 168 } 169 } 170 } 171 data.fadedFlash = true; 172 } 173 174 Vector2f loc = ship.getLocation(); 175 float dist = Misc.getDistance(loc, data.lungeDest); 176 177 //Vector2f perp = new Vector2f(-dir.y, dir.x); 178 179 float friction = ConvulsiveLungeSystemScript.FRICTION; 180 float k = ConvulsiveLungeSystemScript.SPRING_CONSTANT; 181 float freeLength = 0f; 182 float stretch = dist - freeLength; 183 184 float forceMag = k * Math.abs(stretch); 185 186 float speedInDir = Vector2f.dot(dir, ship.getVelocity()); 187 if (speedInDir > LUNGE_SPEED) { 188 float mult = 1f - Math.min(1f, (speedInDir - LUNGE_SPEED) / 100f); 189 forceMag *= mult; 190 } 191 192 193 float forceMagReduction = Math.min(Math.abs(forceMag), friction); 194 forceMag -= forceMagReduction; 195 friction -= forceMagReduction; 196 197 Vector2f force = new Vector2f(dir); 198 if (slowdown) { 199 forceMag = ship.getVelocity().length() * 2f; 200 force.scale(forceMag); 201 } else { 202 force.scale(forceMag * Math.signum(stretch)); 203 } 204 205 Vector2f acc = new Vector2f(force); 206 acc.scale(amount); 207 Vector2f.add(ship.getVelocity(), acc, ship.getVelocity()); 208 } 209 210 ship.giveCommand(ShipCommand.DECELERATE, null, 0); 211 212 data.lungeElapsed += amount; 213 if (data.lungeElapsed > LUNGE_DUR) { 214 data.lunging = false; 215 data.cooldown = LUNGE_COOLDOWN; 216 } 217 } 218 219} 220 221 222 223 224 225 226 227 228 229 230 231 232 233