001package com.fs.starfarer.api.impl.combat.threat;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import java.awt.Color;
007
008import org.lwjgl.util.vector.Vector2f;
009
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.campaign.FactionAPI;
012import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
013import com.fs.starfarer.api.combat.CollisionClass;
014import com.fs.starfarer.api.combat.CombatEngineAPI;
015import com.fs.starfarer.api.combat.CombatFleetManagerAPI;
016import com.fs.starfarer.api.combat.DamageType;
017import com.fs.starfarer.api.combat.ShipAPI;
018import com.fs.starfarer.api.combat.ShipCommand;
019import com.fs.starfarer.api.combat.WeaponGroupAPI;
020import com.fs.starfarer.api.impl.campaign.ids.Factions;
021import com.fs.starfarer.api.impl.combat.RiftLanceEffect;
022import com.fs.starfarer.api.impl.combat.threat.RoilingSwarmEffect.SwarmMember;
023import com.fs.starfarer.api.input.InputEventAPI;
024import com.fs.starfarer.api.util.IntervalUtil;
025import com.fs.starfarer.api.util.Misc;
026
027public class ThreatShipConstructionScript extends BaseEveryFrameCombatPlugin {
028        
029        public static String SWARM_CONSTRUCTING_SHIP = "swarm_constructing_ship";
030        public static String SHIP_UNDER_CONSTRUCTION = "ship_under_construction";
031        
032        public static float FADE_IN_RATE_MULT_WHEN_DESTROYED = 10f;
033        
034        protected float elapsed = 0f;
035        protected ShipAPI ship = null;
036        protected CollisionClass collisionClass;
037        
038        protected String variantId;
039        protected ShipAPI source;
040        protected float delay;
041        protected float fadeInTime;
042        protected float origMaxSpeed = 500f;
043        protected List<ShipAPI> explodedPieces = new ArrayList<>();
044        
045        protected IntervalUtil interval = new IntervalUtil(0.075f, 0.125f);
046        
047        public ThreatShipConstructionScript(String variantId, ShipAPI source, float delay, float fadeInTime) {
048                this.variantId = variantId;
049                this.source = source;
050                this.delay = delay;
051                this.fadeInTime = fadeInTime;
052                
053                interval.forceIntervalElapsed();
054                
055                spawnShip();
056        }
057        
058        public ShipAPI getShip() {
059                return ship;
060        }
061
062        protected void spawnShip() {
063                float facing = source.getFacing() + 15f * ((float) Math.random() - 0.5f);
064                
065                Vector2f loc = new Vector2f(source.getLocation());
066                
067                CombatEngineAPI engine = Global.getCombatEngine();
068                CombatFleetManagerAPI fleetManager = engine.getFleetManager(source.getOriginalOwner());
069                boolean wasSuppressed = fleetManager.isSuppressDeploymentMessages();
070                fleetManager.setSuppressDeploymentMessages(true);
071        
072                ship = engine.getFleetManager(source.getOriginalOwner()).spawnShipOrWing(variantId, loc, facing, 0f, null);
073                if (Global.getCombatEngine().isInCampaign() || Global.getCombatEngine().isInCampaignSim()) {
074                        FactionAPI faction = Global.getSector().getFaction(Factions.THREAT);
075                        if (faction != null) {
076                                String name = faction.pickRandomShipName();
077                                ship.setName(name);
078                        }
079                }
080                fleetManager.setSuppressDeploymentMessages(wasSuppressed);
081                collisionClass = ship.getCollisionClass();
082                
083                
084                RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
085                RoilingSwarmEffect sourceSwarm = RoilingSwarmEffect.getSwarmFor(source);
086                if (swarm != null) {
087                        swarm.params.withInitialMembers = false;
088                        swarm.params.withRespawn = false;
089                }
090                if (sourceSwarm != null) {
091                        origMaxSpeed = sourceSwarm.params.maxSpeed;
092//                      sourceSwarm.params.maxSpeed *= 0.25f;
093                        sourceSwarm.params.outspeedAttachedEntityBy = 0f;
094                        if (swarm != null) {
095                                swarm.params.withInitialMembers = false;
096                                swarm.params.flashFringeColor   = sourceSwarm.params.flashFringeColor;
097                        }
098                }
099                
100                ship.addTag(SHIP_UNDER_CONSTRUCTION);
101                source.addTag(SWARM_CONSTRUCTING_SHIP);
102                source.setCollisionClass(CollisionClass.NONE);
103                source.getMutableStats().getHullDamageTakenMult().modifyMult("ThreatShipConstructionScript", 0f);
104
105                ship.setShipAI(null);
106                for (WeaponGroupAPI g : ship.getWeaponGroupsCopy()) {
107                        g.toggleOff();
108                }
109        }
110        
111        protected float hulkFor = 0f;
112        
113        @Override
114        public void advance(float amount, List<InputEventAPI> events) {
115                if (Global.getCombatEngine().isPaused()) return;
116        
117                if (ship.isHulk()) {
118                        hulkFor += amount;
119                        amount *= FADE_IN_RATE_MULT_WHEN_DESTROYED;
120                        // ship splitting into pieces doesn't happen immediately
121                        if (explodedPieces.isEmpty() || hulkFor < 0.25f) {
122                                explodedPieces.clear();
123                                for (ShipAPI curr : Global.getCombatEngine().getShips()) {
124                                        if (curr.getFleetMember() == ship.getFleetMember() ||
125                                                        (curr.getParentPieceId() != null && curr.getParentPieceId().equals(ship.getId()))) {
126                                                explodedPieces.add(curr);
127                                        }
128                                }
129                        }
130                        //elapsed += delay + fadeInTime; // instant fade in while hidden by the explosion
131                }
132                elapsed += amount;
133                if (elapsed < delay) return;
134                
135                CombatEngineAPI engine = Global.getCombatEngine();
136
137                float progress = (elapsed - delay) / fadeInTime;
138                if (progress > 1f) progress = 1f;
139                
140                float remaining = fadeInTime - (elapsed - delay);
141                
142                ship.setAlphaMult(progress);
143//              if (!explodedPieces.isEmpty()) {
144//                      System.out.println("Pieces: " + explodedPieces.size());
145//              }
146                for (ShipAPI curr : explodedPieces) {
147                        curr.setAlphaMult(progress);
148                }
149                ship.getMutableStats().getEffectiveArmorBonus().modifyMult("ThreatShipConstructionScript", progress * progress);
150                
151                Global.getSoundPlayer().playLoop("construction_swarm_loop", ship, 1f, 1f, ship.getLocation(), ship.getVelocity());
152                
153                
154                
155                if (remaining > 1f) {
156                        Vector2f deltaLoc = Vector2f.sub(ship.getLocation(), source.getLocation(), new Vector2f());
157                        source.getLocation().set(ship.getLocation());
158                        RoilingSwarmEffect sourceSwarm = RoilingSwarmEffect.getSwarmFor(source);
159                        if (sourceSwarm != null) {
160                                for (SwarmMember p : sourceSwarm.members) {
161                                        Vector2f.add(p.loc, deltaLoc, p.loc);
162                                }
163                        }
164                        ship.giveCommand(ShipCommand.DECELERATE, null, 0);
165                }
166                
167                float jitterLevel = progress;
168                if (fadeInTime <= 4f) {
169                        if (jitterLevel < 0.5f) {
170                                jitterLevel *= 2f;
171                        } else {
172                                jitterLevel = (1f - jitterLevel) * 2f;
173                        }
174                } else {
175                        if (jitterLevel < 0.5f) {
176                                jitterLevel *= 2f;
177                        } else if (remaining <= 2f) {
178                                jitterLevel = remaining / 2f;
179                        } else {
180                                jitterLevel = 1f;
181                        }
182                }
183                jitterLevel = (float) Math.sqrt(jitterLevel);
184                
185                //float jitterRange = 1f - progress;
186                float jitterRange = 1f;
187                if (remaining < 2f) {
188                        jitterRange = remaining / 2f;
189                } else {
190                        jitterRange = (elapsed - delay) / Math.max(1f, fadeInTime - 2f);
191                }
192                float maxRangeBonus = 25f;
193                float jitterRangeBonus = jitterRange * maxRangeBonus;
194                
195                Color c = VoltaicDischargeOnFireEffect.EMP_FRINGE_COLOR;
196                c = Misc.setAlpha(c, 127);
197                
198                ship.setJitter(this, c, jitterLevel, 3, 0f, jitterRangeBonus);
199                ship.getEngineController().fadeToOtherColor(this, Misc.zeroColor, Misc.zeroColor, 1f, 1f);
200                
201                
202                RoilingSwarmEffect sourceSwarm = RoilingSwarmEffect.getSwarmFor(source);
203                if (sourceSwarm != null) {
204                        float speedMult = 0.25f + 0.75f * Math.max(0f, 1f - (elapsed - delay) / 2f);
205                        sourceSwarm.params.maxSpeed = origMaxSpeed * speedMult;
206                                        
207                        if (remaining > 3f) {
208                                float numFragMult = sourceSwarm.params.initialMembers / 150f;
209                                if (numFragMult < 0.25f) numFragMult = 0.25f;
210                                if (numFragMult > 1f) numFragMult = 1f;
211                                sourceSwarm.params.flashFrequency = 5f * progress * 2f * numFragMult;
212                                sourceSwarm.params.flashProbability = 1f;
213                                sourceSwarm.params.flashFringeColor = VoltaicDischargeOnFireEffect.EMP_FRINGE_COLOR;
214                                sourceSwarm.params.flashFringeColor = Misc.setAlpha(sourceSwarm.params.flashFringeColor, 200);
215                                //sourceSwarm.params.flashCoreRadiusMult = 0f;
216                                sourceSwarm.params.flashCoreRadiusMult = 1.5f;
217                                sourceSwarm.params.flashRadius = 50f;
218                                sourceSwarm.params.renderFlashOnSameLayer = true;
219                        } else {
220                                sourceSwarm.params.flashFrequency = 1f;
221                                sourceSwarm.params.flashProbability = 0f;
222                        }
223                }
224                
225                
226                spawnParticles(amount);
227                
228
229                if (elapsed > fadeInTime + delay) {
230                        ship.setDefaultAI(null);
231                        ship.removeTag(SHIP_UNDER_CONSTRUCTION);
232                        ship.setAlphaMult(1f);
233                        ship.setHoldFire(false);
234                        ship.setCollisionClass(collisionClass);
235                        ship.getMutableStats().getEffectiveArmorBonus().unmodifyMult("ThreatShipConstructionScript");
236                        engine.removePlugin(this);
237                        
238                        if (sourceSwarm != null) {
239                                sourceSwarm.getParams().despawnSound = null;
240                        }
241                        
242                        RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(ship);
243                        //RoilingSwarmEffect sourceSwarm = RoilingSwarmEffect.getSwarmFor(source);
244                        if (swarm != null && sourceSwarm != null) {
245                                int transfer = Math.min(swarm.params.baseMembersToMaintain, sourceSwarm.getNumActiveMembers());
246                                sourceSwarm.transferMembersTo(swarm, transfer);
247                        }
248                        if (swarm != null) {
249                                swarm.params.withRespawn = true; 
250                                swarm.params.withInitialMembers = true; 
251                        }
252                        
253                        source.setHitpoints(0f);
254                        source.setSpawnDebris(false);
255                        engine.applyDamage(source, source.getLocation(), 100f, DamageType.ENERGY, 0f, true, false, source, false);
256                }
257        }
258        
259        protected void spawnParticles(float amount) {
260                if (ship == null) return;
261                
262                float remaining = fadeInTime - (elapsed - delay);
263                
264                interval.advance(amount);
265                if (interval.intervalElapsed() && remaining > 1f) {
266                        
267                        RoilingSwarmEffect swarm = RoilingSwarmEffect.getSwarmFor(source);
268                        if (swarm != null) {
269                                for (SwarmMember p : swarm.members) {
270                                        if ((float) Math.random() > 0.9f) {
271                                                p.rollOffset(swarm.params, ship);
272                                        }
273                                }
274                        }
275                        
276                        CombatEngineAPI engine = Global.getCombatEngine();
277                        
278                        Color c = RiftLanceEffect.getColorForDarkening(VoltaicDischargeOnFireEffect.EMP_FRINGE_COLOR);
279                        c = Misc.setAlpha(c, 50);
280                        float baseDuration = 2f;
281                        Vector2f vel = new Vector2f(ship.getVelocity());
282                        //float size = ship.getCollisionRadius() * 0.35f;
283                        float size = ship.getCollisionRadius() * 0.33f;
284                        
285                        float extraDur = 0f;
286                        if (remaining < 1f) extraDur = 1f;
287                        
288                        //for (int i = 0; i < 3; i++) {
289                        for (int i = 0; i < 11; i++) {
290                                Vector2f point = new Vector2f(ship.getLocation());
291                                point = Misc.getPointWithinRadiusUniform(point, ship.getCollisionRadius() * 0.75f, Misc.random);
292                                float dur = baseDuration + baseDuration * (float) Math.random();
293                                dur += extraDur;
294                                float nSize = size;
295                                Vector2f pt = Misc.getPointWithinRadius(point, nSize * 0.5f);
296                                Vector2f v = Misc.getUnitVectorAtDegreeAngle((float) Math.random() * 360f);
297                                v.scale(nSize + nSize * (float) Math.random() * 0.5f);
298                                v.scale(0.2f);
299                                Vector2f.add(vel, v, v);
300                                
301                                float maxSpeed = nSize * 1.5f * 0.2f; 
302                                float minSpeed = nSize * 1f * 0.2f; 
303                                float overMin = v.length() - minSpeed;
304                                if (overMin > 0) {
305                                        float durMult = 1f - overMin / (maxSpeed - minSpeed);
306                                        if (durMult < 0.1f) durMult = 0.1f;
307                                        dur *= 0.5f + 0.5f * durMult;
308                                }
309                                engine.addNegativeNebulaParticle(pt, v, nSize * 1f, 2f,
310                                                                                                0.5f / dur, 0f, dur, c);
311                        }
312                }
313        }
314        
315        
316}