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