001package com.fs.starfarer.api.impl.campaign;
002
003import java.util.Random;
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.ai.FleetAssignmentDataAPI;
012import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
013import com.fs.starfarer.api.impl.campaign.rulecmd.ApplyCRDamage;
014import com.fs.starfarer.api.util.IntervalUtil;
015import com.fs.starfarer.api.util.Misc;
016
017/**
018 * Unlike HasslePlayerScript, this is a fleet-fleet interaction and needs to be initiated
019 * by some other script.
020 *
021 * Copyright 2023 Fractal Softworks, LLC
022 */
023public class HassleNPCScript implements EveryFrameScript {
024
025        public static String HASSLE_ASSIGNMENT_ID = "hassle_assignment_id";
026        
027        public static class HassleParams {
028                public String fleetAction = "conducting an inspection";
029                public String targetAction = "standing by for inspection";
030                public float minDurDays = 2f;
031                public float maxDurDays = 3f;
032                public float crDamageMult = 1f;
033                
034                // used by NPCHassler, *not* by HassleNPCScript, but convenient to put here
035                public String timeoutKey = "$NPCHassleTimeout";
036                public float minGlobalTimeout = 1, maxGlobalTimeout = 2;
037                public float minTargetTimeout = 25, maxTargetTimeout = 35;
038        }
039        
040        protected CampaignFleetAPI fleet;
041        protected CampaignFleetAPI target;
042        protected Vector2f loc1, loc2;
043        protected HassleParams params = new HassleParams();
044        protected float durDays = 3f;
045
046        private IntervalUtil interval = new IntervalUtil(0.1f, 0.3f);
047        boolean done = false;
048        
049        public HassleNPCScript(CampaignFleetAPI fleet, CampaignFleetAPI target) {
050                this(fleet, target, "conducting an inspection", "standing by for inspection");
051        }
052
053        public HassleNPCScript(CampaignFleetAPI fleet, CampaignFleetAPI target, HassleParams params) {
054                this.fleet = fleet;
055                this.target = target;
056                
057                this.params = params;
058                
059                durDays = params.minDurDays + (params.maxDurDays - params.minDurDays) * (float) Math.random();
060                interval.forceIntervalElapsed();
061        }
062        
063        public HassleNPCScript(CampaignFleetAPI fleet, CampaignFleetAPI target, String fleetAction, String targetAction) {
064                this.fleet = fleet;
065                this.target = target;
066                
067                this.params.fleetAction = fleetAction;
068                this.params.targetAction = targetAction;
069                
070                durDays = params.minDurDays + (params.maxDurDays - params.minDurDays) * (float) Math.random();
071                
072                interval.forceIntervalElapsed();
073        }
074
075
076        public float getDurDays() {
077                return durDays;
078        }
079
080        public void setDurDays(float durDays) {
081                this.durDays = durDays;
082        }
083
084        public void abort() {
085                done = true;
086                
087                cleanUpFleet(fleet);
088                cleanUpFleet(target);
089        }
090        
091        protected void cleanUpFleet(CampaignFleetAPI fleet) {
092                if (fleet.getAI() != null) {
093                        Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.FLEET_BUSY, "npc_hassle", false, 0f);
094                        fleet.getMemoryWithoutUpdate().unset(MemFlags.FLEET_SPECIAL_ACTION);
095                        fleet.getMemoryWithoutUpdate().unset(MemFlags.MEMORY_KEY_FLEET_DO_NOT_GET_SIDETRACKED);
096                        
097                        FleetAssignmentDataAPI curr = fleet.getAI().getCurrentAssignment();
098                        if (curr != null && HASSLE_ASSIGNMENT_ID.equals(curr.getCustom())) {
099                                fleet.removeFirstAssignment();
100                        }
101                }
102        }
103
104        public void advance(float amount) {
105                if (done) return;
106                
107                
108                if (target.isPlayerFleet()) {
109                        abort();
110                        return;
111                }
112                if (fleet.getBattle() != null || target.getBattle() != null) {
113                        abort();
114                        return;
115                }
116                if (fleet.getAI() != null && (fleet.getAI().isFleeing() || fleet.getAI().isMaintainingContact())) {
117                        abort();
118                        return;
119                }
120                if (target.getAI() != null && (target.getAI().isFleeing() || target.getAI().isMaintainingContact())) {
121                        abort();
122                        return;
123                }
124                if (fleet.getContainingLocation() != target.getContainingLocation()) {
125                        abort();
126                        return;
127                }
128                if (fleet.isHostileTo(target) || fleet.getFaction() == target.getFaction()) {
129                        abort();
130                        return;
131                }
132                
133                
134                float days = Global.getSector().getClock().convertToDays(amount);
135                durDays -= days;
136                if (durDays <= 0) {
137                        abort();
138                        if (params.crDamageMult > 0) {
139                                float damageFP = fleet.getFleetPoints() * 0.2f;
140                                ApplyCRDamage.applyCRDamage(target, damageFP, params.crDamageMult, "Vindictive inspection", null, new Random());
141                        }
142                        return;
143                }
144                
145                if (loc1 == null) {
146                        loc1 = new Vector2f(target.getLocation());
147                        Vector2f.add(loc1, fleet.getLocation(), loc1);
148                        loc1.scale(0.5f);
149                        loc2 = Misc.getPointAtRadius(loc1, fleet.getRadius() + target.getRadius());
150                }
151                
152                fleet.setMoveDestinationOverride(loc2.x, loc2.y);
153                target.setMoveDestinationOverride(loc1.x, loc1.y);
154                
155                interval.advance(days);
156                if (!interval.intervalElapsed()) return;
157                
158                
159                
160                if (fleet.getAI() != null) {
161                        Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.FLEET_BUSY, "npc_hassle", true, 0.4f);
162                        fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_FLEET_DO_NOT_GET_SIDETRACKED, true, 0.4f);
163                        fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_SPECIAL_ACTION, true, 0.4f);
164                        FleetAssignmentDataAPI curr = fleet.getAI().getCurrentAssignment();
165                        if (curr == null || !HASSLE_ASSIGNMENT_ID.equals(curr.getCustom())) {
166                                fleet.clearAssignments();
167                                fleet.addAssignmentAtStart(FleetAssignment.HOLD, null, durDays + 1f, params.fleetAction, null);
168                                fleet.getCurrentAssignment().setCustom(HASSLE_ASSIGNMENT_ID);
169                        }
170                }
171                if (target.getAI() != null) {
172                        Misc.setFlagWithReason(target.getMemoryWithoutUpdate(), MemFlags.FLEET_BUSY, "npc_hassle", true, 0.4f);
173                        target.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_FLEET_DO_NOT_GET_SIDETRACKED, true, 0.4f);
174                        target.getMemoryWithoutUpdate().set(MemFlags.FLEET_SPECIAL_ACTION, true, 0.4f);
175                        
176                        FleetAssignmentDataAPI curr = target.getAI().getCurrentAssignment();
177                        curr = null;
178                        if (curr == null || !HASSLE_ASSIGNMENT_ID.equals(curr.getCustom())) {
179                                target.clearAssignments();
180                                target.addAssignmentAtStart(FleetAssignment.HOLD, null, durDays + 1f, params.targetAction, null);
181                                target.getCurrentAssignment().setCustom(HASSLE_ASSIGNMENT_ID);
182                        }
183                }
184        }
185
186        public boolean isDone() {
187                return done;
188        }
189
190        public boolean runWhilePaused() {
191                return false;
192        }
193
194        public float getCrDamageMult() {
195                return params.crDamageMult;
196        }
197
198        public void setCrDamageMult(float crDamageMult) {
199                this.params.crDamageMult = crDamageMult;
200        }
201
202        public HassleParams getParams() {
203                return params;
204        }
205        
206        
207}
208
209
210
211
212
213
214
215
216
217
218
219
220