001package com.fs.starfarer.api.impl.campaign.terrain; 002 003import java.util.ArrayList; 004import java.util.List; 005import java.util.Random; 006 007import org.lwjgl.util.vector.Vector2f; 008 009import com.fs.starfarer.api.Global; 010import com.fs.starfarer.api.campaign.CampaignFleetAPI; 011import com.fs.starfarer.api.campaign.LocationAPI; 012import com.fs.starfarer.api.campaign.StarSystemAPI; 013import com.fs.starfarer.api.impl.campaign.enc.EncounterPoint; 014import com.fs.starfarer.api.impl.campaign.enc.EncounterPointProvider; 015import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator.StarSystemType; 016import com.fs.starfarer.api.util.Misc; 017 018public class HyperspaceAbyssPluginImpl extends BaseHyperspaceAbyssPlugin implements EncounterPointProvider { 019 020 public static String EP_TYPE_ABYSSAL = "ep_type_abyssal"; 021 022 public static float ENCOUNTER_NEAR_ABYSSAL_SYSTEM_DIST = 2000f; 023 024 025 public static float NASCENT_WELL_DETECTED_RANGE = 1000f; 026 public static float JUMP_POINT_DETECTED_RANGE = 1300f; 027 public static float GAS_GIANT_DETECTED_RANGE = 1600f; 028 public static float STAR_DETECTED_RANGE = 2000f; 029 030 031 public static float DEPTH_THRESHOLD_FOR_ENCOUNTER = 0.25f; 032 public static float DEPTH_THRESHOLD_FOR_DWELLER_LIGHT = 3f; 033 public static float DEPTH_THRESHOLD_FOR_ABYSSAL_LIGHT = 1f; 034 public static float DEPTH_THRESHOLD_FOR_ABYSSAL_STELLAR_OBJECT = 1f; 035 public static float DEPTH_THRESHOLD_FOR_ABYSSAL_STAR_SYSTEM = 0.5f; 036 public static float DEPTH_THRESHOLD_FOR_NO_DUST_PARTICLES_IN_COMBAT = 0.5f; 037 public static float DEPTH_THRESHOLD_FOR_FLEETS_FLEEING = 0.5f; 038 039 040 public static class AbyssalEPData { 041 /** 042 * The depth is uncapped. 043 */ 044 public float depth; 045 public Random random; 046 public StarSystemAPI nearest = null; 047 public float distToNearest = Float.MAX_VALUE; 048 049 public AbyssalEPData() { 050 051 } 052 } 053 054 055 public static float PLAYER_DIST_TRAVELLED_TO_REGEN_EPS = 1000f; // units not light-years 056 057 protected Vector2f playerLocWhenGeneratingEPs = null; 058 protected List<EncounterPoint> encounterPoints = null; 059 protected Random random = new Random(); 060 061 public HyperspaceAbyssPluginImpl() { 062 Global.getSector().getListenerManager().addListener(this); 063 } 064 065 protected Object readResolve() { 066 if (random == null) { 067 random = new Random(); 068 } 069 return this; 070 } 071 072 public float getAbyssalDepth(Vector2f loc, boolean uncapped) { 073 float w = Global.getSettings().getFloat("sectorWidth"); 074 float h = Global.getSettings().getFloat("sectorHeight"); 075 076 // Orion-Perseus Abyss, lower left of the map, plus whatever area outside the map 077 float baseW = 100000f; 078 float baseH = 50000f; 079 080 float normalizedX = (loc.x + w/2f) / baseW; 081 float normalizedY = (loc.y + h/2f) / baseH; 082 083 float test = (float) (Math.sqrt(Math.max(0f, normalizedX)) + Math.sqrt(Math.max(0f, normalizedY))); 084// float test = 1f; 085// if (normalizedX >= 0 && normalizedY >= 0) { 086// test = (float) (Math.sqrt(normalizedX) + Math.sqrt(normalizedY)); 087// } else if (normalizedX < 0 && (loc.y + h/2f) < baseH) { 088// test = 0f; 089// } else if (normalizedY < 0 && (loc.x + w/2f) < baseW) { 090// test = 0f; 091// } 092 093// boolean player = Misc.getDistance(Global.getSector().getPlayerFleet().getLocationInHyperspace(), loc) < 5f; 094 095 float depthBasedOnCorner = 0f; 096 if (test < 1f) { 097 float threshold = 0.95f; 098// if (player) { 099// System.out.println("Depth: " + (1f - (test - threshold) / (1f - threshold))); 100// } 101 if (uncapped) { 102 //return (1f - (test - threshold) / (1f - threshold)); 103 depthBasedOnCorner = (1f - (test - threshold) / (1f - threshold)); 104 } else { 105 if (test < threshold) depthBasedOnCorner = 1f; 106 else depthBasedOnCorner = 1f - (test - threshold) / (1f - threshold); 107 } 108// if (test < threshold) return 1f; 109// return 1f - (test - threshold) / (1f - threshold); 110 } 111 112 // outside the map area 113 float left = -w/2f - loc.x; 114 float below = -h/2f - loc.y; 115 float right = loc.x - w/2f; 116 float above = loc.y - h/2f; 117 118 float max = Math.max(left, Math.max(right, Math.max(above, below))); 119 if (max > 0) { 120// if (player) { 121// System.out.println("Depth: " + max/2000f); 122// } 123// if (uncapped) return max / 2000f; 124// return Math.min(1f, max / 2000f); 125 if (uncapped) { 126 return Math.max(depthBasedOnCorner, max / 2000f); 127 } 128 return Math.min(1f, Math.max(depthBasedOnCorner, max / 2000f)); 129 } 130 131 // inside the map, outside the Abyss area 132 //return 0f; 133 return depthBasedOnCorner; 134 } 135 136 /** 137 * @param amount 138 */ 139 public void advance(float amount) { 140 if (!Global.getSector().getListenerManager().hasListener(this)) { 141 Global.getSector().getListenerManager().addListener(this); 142 } 143 144 } 145 146 public List<StarSystemAPI> getAbyssalSystems() { 147 List<StarSystemAPI> abyssal = new ArrayList<StarSystemAPI>(); 148 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 149 float depth = Misc.getAbyssalDepth(system.getLocation()); 150 if (depth > DEPTH_THRESHOLD_FOR_ABYSSAL_STAR_SYSTEM) { 151 abyssal.add(system); 152 } 153 } 154 return abyssal; 155 } 156 157 158 159 public List<EncounterPoint> generateEncounterPoints(LocationAPI where) { 160 if (!where.isHyperspace()) return null; 161 162 boolean regenerate = encounterPoints == null || playerLocWhenGeneratingEPs == null; 163 CampaignFleetAPI pf = Global.getSector().getPlayerFleet(); 164 165 if (!regenerate) { 166 Vector2f loc = pf.getLocation(); 167 float dist = Misc.getDistance(loc, playerLocWhenGeneratingEPs); 168 regenerate = dist > PLAYER_DIST_TRAVELLED_TO_REGEN_EPS; 169 } 170 171 if (!regenerate) return encounterPoints; 172 173 List<StarSystemAPI> abyssal = getAbyssalSystems(); 174 175 playerLocWhenGeneratingEPs = new Vector2f(pf.getLocation()); 176 encounterPoints = new ArrayList<EncounterPoint>(); 177 178 float startAngle = random.nextFloat() * 360f; 179 for (float angle = startAngle; angle < startAngle + 360f; angle += 30f) { 180 Vector2f loc = Misc.getUnitVectorAtDegreeAngle(angle); 181 float dist = 3000f; 182 loc.scale(dist); 183 Vector2f.add(loc, playerLocWhenGeneratingEPs, loc); 184 loc = Misc.getPointWithinRadius(loc, 1000f, random); 185 186 float depth = getAbyssalDepth(loc, true); 187 if (depth < DEPTH_THRESHOLD_FOR_ENCOUNTER) continue; 188 189 // Can match ids of other points, or have different ids for points that are 190 // very close to each other if they're across 1000-boundary 191 // this is fine. Just a way to *somewhat* limit repeated spawns in the same location 192 193 String id = "abyssal_" + (int)(loc.x/1000f) + "_" + (int)(loc.y/1000f); 194 EncounterPoint point = new EncounterPoint(id, where, loc, EP_TYPE_ABYSSAL); 195 196 AbyssalEPData data = new AbyssalEPData(); 197 data.depth = depth; 198 data.random = Misc.getRandom(random.nextLong(), 7); 199 200 float minDist = Float.MAX_VALUE; 201 StarSystemAPI nearest = null; 202 for (StarSystemAPI system : abyssal) { 203 float distToSystem = Misc.getDistance(system.getLocation(), loc); 204 float testDist = ENCOUNTER_NEAR_ABYSSAL_SYSTEM_DIST; 205 if (system.getType() == StarSystemType.DEEP_SPACE) { 206 testDist *= 0.5f; 207 } 208 if (distToSystem < testDist && distToSystem < minDist) { 209 minDist = distToSystem; 210 nearest = system; 211 } 212 } 213 if (nearest != null) { 214 data.nearest = nearest; 215 data.distToNearest = minDist; 216 } 217 218 point.custom = data; 219 encounterPoints.add(point); 220 } 221 222 return encounterPoints; 223 } 224 225} 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245