001package com.fs.starfarer.api.impl.campaign.fleets;
002
003import java.util.ArrayList;
004import java.util.Iterator;
005import java.util.List;
006
007import org.lwjgl.util.vector.Vector2f;
008
009import com.fs.starfarer.api.EveryFrameScript;
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.campaign.BattleAPI;
012import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
013import com.fs.starfarer.api.campaign.CampaignFleetAPI;
014import com.fs.starfarer.api.campaign.SectorEntityToken;
015import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
016import com.fs.starfarer.api.util.Misc;
017
018/**
019 * Up to a configurable number of fleets. Instant despawn when player is far enough.
020 * 
021 * New fleets generated after respawnDelay if some are destroyed.
022 * 
023 * @author Alex Mosolov
024 *
025 * Copyright 2017 Fractal Softworks, LLC
026 */
027public abstract class SourceBasedFleetManager implements FleetEventListener, EveryFrameScript {
028
029        public static float DESPAWN_THRESHOLD_PAD_LY = 1;
030        public static float DESPAWN_MIN_DIST_LY = 3;
031        
032        protected List<CampaignFleetAPI> fleets = new ArrayList<CampaignFleetAPI>();
033        protected float thresholdLY = 4f;
034        protected SectorEntityToken source;
035        
036        public static boolean DEBUG = true;
037        protected int minFleets;
038        protected int maxFleets;
039        protected float respawnDelay;
040        
041        protected float destroyed = 0;
042        
043        protected Vector2f sourceLocation = new Vector2f();
044        
045        public SourceBasedFleetManager(SectorEntityToken source, float thresholdLY, int minFleets, int maxFleets, float respawnDelay) {
046                this.source = source;
047                this.thresholdLY = thresholdLY;
048                this.minFleets = minFleets;
049                this.maxFleets = maxFleets;
050                this.respawnDelay = respawnDelay;
051        }
052        
053        public float getThresholdLY() {
054                return thresholdLY;
055        }
056
057        public SectorEntityToken getSource() {
058                return source;
059        }
060
061        protected abstract CampaignFleetAPI spawnFleet();
062
063        public void advance(float amount) {
064                CampaignFleetAPI player = Global.getSector().getPlayerFleet();
065                
066//              if (destroyed > 0) {
067//                      System.out.println("destroyed: " + destroyed);
068//              }
069                float days = Global.getSector().getClock().convertToDays(amount);
070                destroyed -= days / respawnDelay;
071                if (destroyed < 0) destroyed = 0;
072                
073                
074                // clean up orphaned, juuust in case - could've been directly removed elsewhere, say, instead of despawned.
075                Iterator<CampaignFleetAPI> iter = fleets.iterator();
076                while (iter.hasNext()) {
077                        if (!iter.next().isAlive()) {
078                                iter.remove();
079                        }
080                }
081
082//              if (source != null && source.getContainingLocation().getName().toLowerCase().contains("idimmeron")) {
083//                      System.out.println("wefwefwefw");
084//              }
085                
086                if (source != null) {
087                        if (!source.isAlive()) {
088                                source = null;
089                        } else {
090                                sourceLocation.set(source.getLocationInHyperspace());
091                        }
092                }
093                
094                float distFromSource = Misc.getDistanceLY(player.getLocationInHyperspace(), sourceLocation);
095                float f = 0f;
096                if (distFromSource < thresholdLY) {
097                        f = (thresholdLY - distFromSource) / (thresholdLY * 0.1f);
098                        if (f > 1) f = 1;
099                }
100                int currMax = minFleets + Math.round((maxFleets - minFleets) * f);
101                currMax -= Math.ceil(destroyed);
102                
103                if (source == null) {
104                        currMax = 0;
105                }
106                
107                // try to despawn some fleets if above maximum
108                if (currMax < fleets.size()) {
109                        for (CampaignFleetAPI fleet : new ArrayList<CampaignFleetAPI>(fleets)) {
110                                float distFromPlayer = Misc.getDistanceLY(player.getLocationInHyperspace(), fleet.getLocationInHyperspace());
111                                if (distFromPlayer > DESPAWN_MIN_DIST_LY && distFromPlayer > thresholdLY + DESPAWN_THRESHOLD_PAD_LY) {
112                                        fleet.despawn(FleetDespawnReason.PLAYER_FAR_AWAY, null);
113                                        if (fleets.size() <= currMax) break;
114                                }
115                        }
116                        
117                }
118                
119                // spawn some if below maximum
120                if (currMax > fleets.size()) {
121                        CampaignFleetAPI fleet = spawnFleet();
122                        if (fleet != null) {
123                                fleets.add(fleet);
124                                //if (shouldAddEventListenerToFleet()) {
125                                        fleet.addEventListener(this);
126                                //}
127                        }
128                }
129                
130                if (source == null && fleets.size() == 0) {
131                        setDone(true);
132                }
133        }
134        
135//      protected boolean shouldAddEventListenerToFleet() {
136//              return true;
137//      }
138
139        private boolean done = false;
140        public boolean isDone() {
141                return done;
142        }
143        
144        public void setDone(boolean done) {
145                this.done = done;
146        }
147
148        public boolean runWhilePaused() {
149                return false;
150                //return Global.getSettings().isDevMode();
151        }
152
153        
154        public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) {
155                if (reason == FleetDespawnReason.DESTROYED_BY_BATTLE) {
156                        destroyed++;
157                }
158                fleets.remove(fleet);
159        }
160        
161        public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) {
162                
163        }
164        
165//      public static void main(String[] args) {
166//              int minFleets = 4;
167//              int maxFleets = 10;
168//              float thresholdLY = 1f;
169//              
170//              for (float d = 0; d < 3f; d += 0.03f) {
171//                      float f = 0f;
172//                      if (d < thresholdLY) {
173//                              f = (thresholdLY - d) / (thresholdLY * 0.1f);
174//                              if (f > 1) f = 1;
175//                      }
176//                      int numFleets = minFleets + Math.round((maxFleets - minFleets) * f);
177//                      System.out.println("Num fleets: " + numFleets + " at range " + d);
178//              } 
179//      }
180}
181
182
183
184