001package com.fs.starfarer.api.impl.campaign.intel; 002 003import java.util.ArrayList; 004import java.util.HashMap; 005import java.util.List; 006import java.util.Map; 007import java.util.Random; 008 009import com.fs.starfarer.api.EveryFrameScript; 010import com.fs.starfarer.api.Global; 011import com.fs.starfarer.api.campaign.FactionAPI; 012import com.fs.starfarer.api.campaign.RepLevel; 013import com.fs.starfarer.api.impl.campaign.ids.Factions; 014import com.fs.starfarer.api.util.Misc; 015import com.fs.starfarer.api.util.Pair; 016import com.fs.starfarer.api.util.TimeoutTracker; 017import com.fs.starfarer.api.util.WeightedRandomPicker; 018 019public class FactionHostilityManager extends BaseEventManager { 020 021 public static String getConflictId(FactionAPI a, FactionAPI b) { 022 String id1 = a.getId(); 023 String id2 = b.getId(); 024 if (id1.compareTo(id2) < 0) { 025 return id1 + "_" + id2; 026 } 027 return id2 + "_" + id1; 028 } 029 030 public static final String KEY = "$core_factionHostilityManager"; 031 032 public static final float TIMEOUT_AFTER_ENDING = 30f; 033 public static final float CHECK_DAYS = 10f; 034 public static final float CHECK_PROB = 0.5f; 035 036 public static FactionHostilityManager getInstance() { 037 Object test = Global.getSector().getMemoryWithoutUpdate().get(KEY); 038 return (FactionHostilityManager) test; 039 } 040 041 public FactionHostilityManager() { 042 Global.getSector().getMemoryWithoutUpdate().set(KEY, this); 043 } 044 045 public boolean areHostilitiesOngoing(FactionAPI a, FactionAPI b) { 046 FactionHostilityIntel intel = getHostilties(a, b); 047 return intel != null && !intel.isEnding(); 048 } 049 050 public FactionHostilityIntel getHostilties(FactionAPI a, FactionAPI b) { 051 String id = getConflictId(a, b); 052 for (EveryFrameScript s : getActive()) { 053 FactionHostilityIntel intel = (FactionHostilityIntel) s; 054 if (intel.getId().equals(id)) { 055 return intel; 056 } 057 } 058 return null; 059 } 060 061 public List<FactionHostilityIntel> getHostilitiesInvolving(FactionAPI faction) { 062 List<FactionHostilityIntel> result = new ArrayList<FactionHostilityIntel>(); 063 for (EveryFrameScript s : getActive()) { 064 FactionHostilityIntel intel = (FactionHostilityIntel) s; 065 if (intel.getOne() == faction || intel.getTwo() == faction) { 066 result.add(intel); 067 } 068 } 069 return result; 070 } 071 072 073 @Override 074 protected int getMinConcurrent() { 075 List<FactionAPI> factions = getEligibleFactions(true); 076 077 float mult = Global.getSettings().getFloat("minFactionHostilitiesMult"); 078 return (int) Math.ceil(factions.size() * mult); 079 } 080 081 @Override 082 protected int getMaxConcurrent() { 083 List<FactionAPI> factions = getEligibleFactions(true); 084 085 float mult = Global.getSettings().getFloat("maxFactionHostilitiesMult"); 086 return (int) Math.ceil(factions.size() * mult); 087 } 088 089 protected List<FactionAPI> getEligibleFactions(boolean checkNumMarkets) { 090 List<FactionAPI> factions = Global.getSector().getAllFactions(); 091 List<FactionAPI> result = new ArrayList<FactionAPI>(); 092 for (int i = 0; i < factions.size(); i++) { 093 FactionAPI faction = factions.get(i); 094 if (!faction.getCustom().optBoolean(Factions.CUSTOM_ENGAGES_IN_HOSTILITIES)) { 095 continue; 096 } 097 098 if (faction.isPlayerFaction()) continue; 099 100 if (checkNumMarkets) { 101 int count = Misc.getFactionMarkets(faction).size(); 102 if (count <= 0) continue; 103 } 104 105 result.add(faction); 106 } 107 108 return result; 109 } 110 111 112 @Override 113 protected float getBaseInterval() { 114 return CHECK_DAYS; 115 } 116 117 public void startHostilities(String a, String b) { 118 startHostilities(Global.getSector().getFaction(a), Global.getSector().getFaction(b)); 119 120 } 121 public void startHostilities(FactionAPI a, FactionAPI b) { 122 if (areHostilitiesOngoing(a, b)) return; 123 124 FactionHostilityIntel intel = new FactionHostilityIntel(a, b); 125 addActive(intel); 126 } 127 128 protected Random random = new Random(); 129 130 @Override 131 protected EveryFrameScript createEvent() { 132 if (random.nextFloat() < CHECK_PROB) return null; 133 134 Pair<FactionAPI, FactionAPI> pick = pickFactions(); 135 if (pick == null) return null; 136 137 FactionHostilityIntel intel = new FactionHostilityIntel(pick.one, pick.two); 138 139 return intel; 140 } 141 142 public void notifyRecentlyEnded(String id) { 143 recentlyEnded.add(id, TIMEOUT_AFTER_ENDING); 144 } 145 146 public TimeoutTracker<String> getRecentlyEnded() { 147 return recentlyEnded; 148 } 149 150 protected TimeoutTracker<String> recentlyEnded = new TimeoutTracker<String>(); 151 @Override 152 public void advance(float amount) { 153 //amount *= 100f; 154 super.advance(amount); 155 156 float days = Global.getSector().getClock().convertToDays(amount); 157 recentlyEnded.advance(days); 158 } 159 160 protected Pair<FactionAPI, FactionAPI> pickFactions() { 161 List<FactionAPI> factions = new ArrayList<FactionAPI>(); 162 Map<FactionAPI, Integer> marketCounts = new HashMap<FactionAPI, Integer>(); 163 164 for (FactionAPI faction : getEligibleFactions(false)) { 165 int count = Misc.getFactionMarkets(faction).size(); 166 if (count <= 0) continue; 167 marketCounts.put(faction, count); 168 169 factions.add(faction); 170 } 171 172 WeightedRandomPicker<FactionAPI> picker = new WeightedRandomPicker<FactionAPI>(random); 173 174 boolean weighCommissionedMore = false; 175 FactionAPI commission = Misc.getCommissionFaction(); 176 if (commission != null && getHostilitiesInvolving(commission).isEmpty()) { 177 weighCommissionedMore = true; 178 } 179 180 for (FactionAPI faction : factions) { 181 float w = 1f; 182 if (commission == faction && weighCommissionedMore) { 183 w = factions.size(); 184 } 185 picker.add(faction, w); 186 } 187 188 FactionAPI one = picker.pick(); 189 if (one == null) return null; 190 191 picker.clear(); 192 for (FactionAPI faction : factions) { 193 if (faction == one) continue; 194 if (faction.isHostileTo(one)) continue; 195 if (faction.isAtWorst(one, RepLevel.COOPERATIVE)) continue; 196 197 String id = getConflictId(one, faction); 198 if (recentlyEnded.contains(id)) continue; 199 200 float w = marketCounts.get(faction); 201 picker.add(faction, w); 202 } 203 204 FactionAPI two = picker.pick(); 205 if (two == null) return null; 206 207 return new Pair<FactionAPI, FactionAPI>(one, two); 208 209 } 210 211} 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226