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