001package com.fs.starfarer.api.impl.combat.dweller; 002 003import java.util.List; 004 005import java.awt.Color; 006 007import org.lwjgl.util.vector.Vector2f; 008 009import com.fs.starfarer.api.Global; 010import com.fs.starfarer.api.combat.CombatEngineAPI; 011import com.fs.starfarer.api.combat.CombatEntityAPI; 012import com.fs.starfarer.api.combat.DamagingProjectileAPI; 013import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin; 014import com.fs.starfarer.api.combat.MissileAIPlugin; 015import com.fs.starfarer.api.combat.MissileAPI; 016import com.fs.starfarer.api.combat.OnFireEffectPlugin; 017import com.fs.starfarer.api.combat.OnHitEffectPlugin; 018import com.fs.starfarer.api.combat.ShipAPI; 019import com.fs.starfarer.api.combat.WeaponAPI; 020import com.fs.starfarer.api.combat.listeners.ApplyDamageResultAPI; 021import com.fs.starfarer.api.impl.campaign.ids.Tags; 022import com.fs.starfarer.api.impl.combat.NegativeExplosionVisual.NEParams; 023import com.fs.starfarer.api.impl.combat.RiftCascadeEffect; 024import com.fs.starfarer.api.impl.combat.RiftCascadeMineExplosion; 025import com.fs.starfarer.api.impl.combat.RiftLanceEffect; 026import com.fs.starfarer.api.impl.combat.RiftTrailEffect; 027import com.fs.starfarer.api.input.InputEventAPI; 028import com.fs.starfarer.api.loading.MissileSpecAPI; 029import com.fs.starfarer.api.util.WeightedRandomPicker; 030 031/** 032 * IMPORTANT: will be multiple instances of this, one for the the OnFire (per weapon) and one for the OnHit (per missile) effects. 033 * 034 * (Well, no data members, so not *that* important.) 035 */ 036public class AssayingRiftEffect implements OnFireEffectPlugin, OnHitEffectPlugin, EveryFrameWeaponEffectPlugin { 037 038 039 public static String HUNGERING_RIFT_HEAL_MULT_STAT = "hungering_rift_heal_mult_stat"; 040 public static String HUNGERING_RIFT_HEAL_MOD_HUMAN_SHIPS = "hungering_rift_heal_mod"; 041 042 public static float HEAL_AMOUNT = 1000f; 043 044 //public static float HITPOINTS_MULT_WHEN_BY_DWELLER_SHIP = 2f; 045 046 public static String ASSAYING_RIFT = "assaying_rift"; 047 048 /** 049 * One Hungering Rift weapon can produce up to 5 or so rifts at a time if they're fired non-stop and don't hit anything early. 050 */ 051 public static int MAX_RIFTS = 10; 052 053 054 public static class AssayingRiftCount { 055 int count = 0; 056 float totalElapsed = 0; 057 058 public void update() { 059 float elapsed = Global.getCombatEngine().getTotalElapsedTime(false); 060 if (totalElapsed >= elapsed) return; 061 062 totalElapsed = elapsed; 063 064 count = 0; 065 for (MissileAPI m : Global.getCombatEngine().getMissiles()) { 066 if (m.hasTag(ASSAYING_RIFT)) { 067 count++; 068 } 069 } 070 } 071 } 072 073 074 @Override 075 public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) { 076 if (weapon.getShip() != null && weapon.getShip().getHullSpec().hasTag(Tags.DWELLER)) { 077 return; 078 } 079 080 String key = "AssayingRiftSharedDataKey"; 081 AssayingRiftCount data = (AssayingRiftCount) engine.getCustomData().get(key); 082 if (data == null) { 083 data = new AssayingRiftCount(); 084 engine.getCustomData().put(key, data); 085 } 086 087 data.update(); 088 089 boolean disable = data.count >= MAX_RIFTS; 090 weapon.setForceDisabled(disable); 091 } 092 093 public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target, Vector2f point, boolean shieldHit, ApplyDamageResultAPI damageResult, CombatEngineAPI engine) { 094 Color color = RiftCascadeEffect.STANDARD_RIFT_COLOR; 095 Object o = projectile.getWeapon().getSpec().getProjectileSpec(); 096 if (o instanceof MissileSpecAPI) { 097 MissileSpecAPI spec = (MissileSpecAPI) o; 098 color = spec.getExplosionColor(); 099 } 100 101 if (!shieldHit && target instanceof ShipAPI) { 102 ShipAPI targetShip = (ShipAPI) target; 103 ShipAPI source = projectile.getSource(); 104 105// DwellerShroud shroud = DwellerShroud.getShroudFor(source); 106// if (shroud != null && source != null && !source.isHulk() && !targetShip.isHulk()) { 107// source.setHitpoints(Math.min(source.getMaxHitpoints(), source.getHitpoints() + HEAL_AMOUNT)); 108// } 109 110 if (!targetShip.isHulk()) { 111 WeightedRandomPicker<ShipAPI> healTargets = new WeightedRandomPicker<>(); 112 WeightedRandomPicker<ShipAPI> healNeedLess = new WeightedRandomPicker<>(); 113 for (ShipAPI other : Global.getCombatEngine().getShips()) { 114 if (other.isHulk()) continue; 115 if (other.isFighter()) continue; 116 if (other.getOwner() != source.getOwner()) continue; 117 118 if (getHealMult(other) <= 0) continue; 119 120 DwellerShroud otherShroud = DwellerShroud.getShroudFor(source); 121 if (otherShroud == null) continue; 122 123 float missingHp = other.getMaxHitpoints() - other.getHitpoints(); 124 if (missingHp < HEAL_AMOUNT * 0.7f && missingHp > 0f) { 125 healNeedLess.add(other, missingHp); 126 } else { 127 healTargets.add(other, missingHp); 128 } 129 } 130 131 ShipAPI toHeal = healTargets.pick(); 132 if (toHeal == null) toHeal = healNeedLess.pick(); 133 if (toHeal != null) { 134 float healAmount = HEAL_AMOUNT; 135 healAmount *= getHealMult(toHeal); 136 toHeal.setHitpoints(Math.min(toHeal.getMaxHitpoints(), toHeal.getHitpoints() + healAmount)); 137 } 138 } 139 } 140 141 NEParams p = RiftCascadeMineExplosion.createStandardRiftParams(color, 15f); 142 p.fadeOut = 1f; 143 p.hitGlowSizeMult = 1f; 144 //p.invertForDarkening = NSProjEffect.STANDARD_RIFT_COLOR; 145 RiftCascadeMineExplosion.spawnStandardRift(projectile, p); 146 147 Vector2f vel = new Vector2f(); 148 if (target != null) vel.set(target.getVelocity()); 149 Global.getSoundPlayer().playSound("assaying_rift_explosion", 1f, 1f, point, vel); 150 } 151 152 public static float getHealMult(ShipAPI toHeal) { 153 float base = toHeal.getMutableStats().getDynamic().getValue(HUNGERING_RIFT_HEAL_MULT_STAT); 154 base += toHeal.getMutableStats().getDynamic().getValue(HUNGERING_RIFT_HEAL_MOD_HUMAN_SHIPS, 0f); 155 return base; 156 } 157 158 159 public void onFire(DamagingProjectileAPI projectile, WeaponAPI weapon, CombatEngineAPI engine) { 160 MissileAIPlugin proxAI = Global.getCombatEngine().createProximityFuseAI((MissileAPI)projectile); 161 RiftTrailEffect trail = new RiftTrailEffect((MissileAPI) projectile, null) { 162 boolean exploded = false; 163 float elapsed = 0f; 164 @Override 165 public void advance(float amount, List<InputEventAPI> events) { 166 super.advance(amount, events); 167 proxAI.advance(amount); 168 if (!exploded && !missile.didDamage() && missile.wasRemoved()) {// !engine.isMissileAlive(missile)) { 169 onHit(missile, null, missile.getLocation(), false, null, engine); 170 exploded = true; 171 } 172 173// if (!exploded) { 174// elapsed += amount; 175// if (elapsed > 1f) { 176// float speedBonus = Math.min(100f, (elapsed - 1f) * 100f); 177// String id = "AssayingRiftSpeedBonus"; 178// missile.getEngineStats().getMaxSpeed().modifyFlat(id, speedBonus); 179// missile.getEngineStats().getAcceleration().modifyFlat(id, speedBonus * 2f); 180// missile.getEngineStats().getDeceleration().modifyFlat(id, speedBonus * 2f); 181// missile.updateMaxSpeed(); 182// } 183// } 184 } 185 protected Color getUndercolor() { 186 //return new Color(100, 0, 20, 255); 187 return DwellerShroud.SHROUD_COLOR; 188 } 189 protected Color getDarkeningColor() { 190 return RiftLanceEffect.getColorForDarkening(getUndercolor()); 191 } 192 @Override 193 protected float getBaseParticleDuration() { 194 return 1.5f; 195 } 196 197 198 }; 199 200 MissileAPI missile = ((MissileAPI) projectile); 201 202 missile.setEmpResistance(1000); 203 missile.setEccmChanceOverride(1f); 204 missile.addTag(ASSAYING_RIFT); 205 206// if (weapon.getShip().getHullSpec().hasTag(Tags.DWELLER)) { 207// missile.setHitpoints(missile.getHitpoints() * HITPOINTS_MULT_WHEN_BY_DWELLER_SHIP); 208// } 209 210 Global.getCombatEngine().addPlugin(trail); 211 } 212 213} 214 215 216 217 218 219 220 221 222 223 224 225 226 227