001package com.fs.starfarer.api.impl.campaign;
002
003import java.util.List;
004
005import org.lwjgl.util.vector.Vector2f;
006
007import com.fs.starfarer.api.EveryFrameScript;
008import com.fs.starfarer.api.Global;
009import com.fs.starfarer.api.campaign.CampaignFleetAPI;
010import com.fs.starfarer.api.campaign.FleetAssignment;
011import com.fs.starfarer.api.campaign.SectorEntityToken.VisibilityLevel;
012import com.fs.starfarer.api.campaign.ai.FleetAssignmentDataAPI;
013import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI;
014import com.fs.starfarer.api.campaign.rules.MemoryAPI;
015import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
016import com.fs.starfarer.api.util.IntervalUtil;
017import com.fs.starfarer.api.util.Misc;
018import com.fs.starfarer.api.util.Misc.FleetFilter;
019
020public class HasslePlayerScript implements EveryFrameScript {
021
022        public static final String HASSLE_COMPLETE_KEY = "$hassleComplete";
023        public static final String HASSLE_TIMEOUT_KEY = "$hassleTimeout";
024        
025        private IntervalUtil interval = new IntervalUtil(0.1f, 0.3f);
026        
027        private float currDuration = 0f;
028        private float currElapsed = 0f;
029        private CampaignFleetAPI curr = null;
030        private Vector2f startHyperLoc;
031        private float leashRange = 0f;
032        
033        public void advance(float amount) {
034                float days = Global.getSector().getClock().convertToDays(amount);
035                
036                if (curr != null) {
037                        maintainOngoingHassle(days);
038                        return;
039                }
040                
041                interval.advance(days);
042                if (!interval.intervalElapsed()) return;
043                
044                final float MAX_RANGE_FROM_PLAYER = 2000;
045                
046                final MemoryAPI global = Global.getSector().getMemoryWithoutUpdate();
047                //if (global.contains(HASSLE_TIMEOUT_KEY)) return;
048                
049                final CampaignFleetAPI player = Global.getSector().getPlayerFleet();
050                if (player == null || player.isInHyperspace()) return;
051
052                
053                List<CampaignFleetAPI> fleets = Misc.findNearbyFleets(player, MAX_RANGE_FROM_PLAYER, new FleetFilter() {
054                        public boolean accept(CampaignFleetAPI curr) {
055                                if (curr.getFaction().isPlayerFaction()) return false;
056                                if (curr.isHostileTo(player)) return false;
057                                if (curr.isStationMode()) return false;
058                                if (!curr.getMemoryWithoutUpdate().getBoolean(MemFlags.WILL_HASSLE_PLAYER)) return false;
059                                if (curr.getMemoryWithoutUpdate().getBoolean(MemFlags.FLEET_SPECIAL_ACTION)) return false;
060                                
061                                String type = curr.getMemoryWithoutUpdate().getString(MemFlags.HASSLE_TYPE);
062                                if (type == null || type.isEmpty()) return false;
063                                String timeoutKey = HASSLE_TIMEOUT_KEY + "_" + type;
064                                if (global.contains(timeoutKey)) return false;
065                                
066                                if (curr.getAI() instanceof ModularFleetAIAPI) {
067                                        ModularFleetAIAPI ai = (ModularFleetAIAPI) curr.getAI();
068                                        if (ai.isFleeing()) return false;
069                                        if (curr.getInteractionTarget() instanceof CampaignFleetAPI) return false;
070                                }
071                                
072                                VisibilityLevel vis = player.getVisibilityLevelTo(curr);
073                                if (vis == VisibilityLevel.NONE) return false;
074                                return true;
075                        }
076                });
077                
078                if (fleets.isEmpty()) return;
079                
080                float minDist = Float.MAX_VALUE;
081                CampaignFleetAPI closestHassler = null;
082                for (CampaignFleetAPI curr : fleets) {
083                        float dist = Misc.getDistance(player.getLocation(), curr.getLocation());
084                        if (dist < minDist) {
085                                minDist = dist;
086                                closestHassler = curr;
087                        }
088                }
089                
090                if (closestHassler == null) return;
091                
092                curr = closestHassler;
093
094                boolean hassle = (float) Math.random() < 0.25f;
095                
096                if (hassle) {
097                        currDuration = 10f + (float) Math.random() * 5f;
098                        currElapsed = 0f;
099                        MemoryAPI mem = curr.getMemoryWithoutUpdate();
100                        Misc.setFlagWithReason(mem, MemFlags.MEMORY_KEY_PURSUE_PLAYER, "hassle", true, 1f);
101                        mem.set(MemFlags.FLEET_SPECIAL_ACTION, true, 1f);
102                        Misc.setFlagWithReason(mem, MemFlags.MEMORY_KEY_STICK_WITH_PLAYER_IF_ALREADY_TARGET, "hassle", true, currDuration);
103                        
104                        startHyperLoc = curr.getLocationInHyperspace();
105                        leashRange = Global.getSettings().getFloat("commRelayRangeAroundSystem");
106                        if (curr.isInHyperspace()) {
107                                leashRange *= 2f;
108                        }
109                        
110                        float timeoutDuration = 20f + (float) Math.random() * 10f;
111                        
112                        String type = curr.getMemoryWithoutUpdate().getString(MemFlags.HASSLE_TYPE);
113                        if (type != null && !type.isEmpty()) {
114                                String timeoutKey = HASSLE_TIMEOUT_KEY + "_" + type;
115                                global.set(timeoutKey, true, timeoutDuration);
116                        }
117                        //global.set(HASSLE_TIMEOUT_KEY, true, timeoutDuration);
118                } else {
119                        curr = null;
120                }
121        }
122
123        public void maintainOngoingHassle(float days) {
124                if (!curr.isAlive()) {
125                        cleanUpCurr();
126                        return;
127                }
128                if (curr.isHostileTo(Global.getSector().getPlayerFleet())) {
129                        cleanUpCurr();
130                        return;
131                }
132                
133                currElapsed += days;
134                if (currElapsed > currDuration) {
135                        cleanUpCurr();
136                        return;
137                }
138                
139
140                
141                CampaignFleetAPI player = Global.getSector().getPlayerFleet();
142                if (startHyperLoc != null) {
143                        float dist = Misc.getDistanceLY(player.getLocationInHyperspace(), startHyperLoc);
144                        if (dist > leashRange) {
145                                cleanUpCurr();
146                                return;
147                        }
148                }
149//              if (player.isInHyperspace() || player.isInHyperspaceTransition()) {
150//                      cleanUpCurr();
151//                      return;
152//              }
153                
154                MemoryAPI mem = curr.getMemoryWithoutUpdate();
155                if (mem.getBoolean(HASSLE_COMPLETE_KEY)) { // this happens when fleet interacts w/ player
156                        cleanUpCurr();
157                        return;
158                }
159                Misc.setFlagWithReason(mem, MemFlags.FLEET_BUSY, "hassle", true, 1f);
160                mem.set(MemFlags.FLEET_SPECIAL_ACTION, true, 1f);
161                // if visible, keep extending "pursue player" duration by a day
162                // so, player has to lose the hassling fleet for a day to get away
163                VisibilityLevel vis = player.getVisibilityLevelTo(curr);
164                if (vis != VisibilityLevel.NONE) {
165                        Misc.setFlagWithReason(mem, MemFlags.MEMORY_KEY_PURSUE_PLAYER, "hassle", true, 1f);
166                }
167                
168                
169        }
170        
171        
172        protected void cleanUpCurr() {
173                if (curr != null) {
174                        CampaignFleetAPI pf = Global.getSector().getPlayerFleet();
175                        FleetAssignmentDataAPI a = curr.getCurrentAssignment();
176                        if (a != null && a.getAssignment() == FleetAssignment.INTERCEPT &&
177                                        a.getTarget() == pf) {
178                                curr.removeFirstAssignmentIfItIs(a.getAssignment());
179                        }
180                        curr.setInteractionTarget(null);
181                        if (curr.getAI() instanceof ModularFleetAIAPI) {
182                                ModularFleetAIAPI ai = (ModularFleetAIAPI) curr.getAI();
183                                if (ai.getTacticalModule().getTarget() == pf) {
184                                        ai.getTacticalModule().setTarget(null);
185                                }
186                        }
187                        
188                        MemoryAPI mem = curr.getMemoryWithoutUpdate();
189                        Misc.setFlagWithReason(mem, MemFlags.FLEET_BUSY, "hassle", false, 0f);
190                        mem.unset(MemFlags.FLEET_SPECIAL_ACTION);
191                        Misc.setFlagWithReason(mem, MemFlags.MEMORY_KEY_PURSUE_PLAYER, "hassle", false, 0f);
192                        Misc.setFlagWithReason(mem, MemFlags.MEMORY_KEY_STICK_WITH_PLAYER_IF_ALREADY_TARGET, "hassle", false, 0f);
193                        mem.unset(HASSLE_COMPLETE_KEY);
194                        curr = null;
195                        currDuration = currElapsed = 0f;
196                        startHyperLoc = null;
197                        leashRange = 0f;
198                }
199        }
200        
201
202        public boolean isDone() {
203                return false;
204        }
205
206        public boolean runWhilePaused() {
207                return false;
208        }
209}
210
211
212
213
214
215
216
217
218
219
220
221
222