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