001package com.fs.starfarer.api.impl.campaign.intel.bar.events; 002 003import java.util.ArrayList; 004import java.util.HashSet; 005import java.util.LinkedHashMap; 006import java.util.LinkedHashSet; 007import java.util.List; 008import java.util.Set; 009 010import com.fs.starfarer.api.EveryFrameScript; 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.campaign.SectorEntityToken; 013import com.fs.starfarer.api.characters.PersonAPI; 014import com.fs.starfarer.api.impl.campaign.DebugFlags; 015import com.fs.starfarer.api.impl.campaign.intel.bar.PortsideBarData; 016import com.fs.starfarer.api.impl.campaign.intel.bar.PortsideBarEvent; 017import com.fs.starfarer.api.loading.BarEventSpec; 018import com.fs.starfarer.api.util.IntervalUtil; 019import com.fs.starfarer.api.util.Misc; 020import com.fs.starfarer.api.util.TimeoutTracker; 021import com.fs.starfarer.api.util.WeightedRandomPicker; 022 023public class BarEventManager implements EveryFrameScript { 024 025 public static interface GenericBarEventCreator { 026 PortsideBarEvent createBarEvent(); 027 float getBarEventFrequencyWeight(); 028 float getBarEventActiveDuration(); 029 float getBarEventTimeoutDuration(); 030 float getBarEventAcceptedTimeoutDuration(); 031 032 033 /** 034 * Priority events get created before non-priority. Should be used sparingly, for gameplay-essential 035 * events. Having too many priority events could crowd out all other events entirely. 036 * @return 037 */ 038 boolean isPriority(); 039 //void updateSeed(); 040 String getBarEventId(); 041 boolean wasAutoAdded(); 042 } 043 044 045 public static final String KEY = "$core_genericBarEventManager"; 046 047 public static BarEventManager getInstance() { 048 Object test = Global.getSector().getMemoryWithoutUpdate().get(KEY); 049 return (BarEventManager) test; 050 } 051 052 protected List<GenericBarEventCreator> creators = new ArrayList<GenericBarEventCreator>(); 053 protected LinkedHashMap<PortsideBarEvent, GenericBarEventCreator> barEventCreators = new LinkedHashMap<PortsideBarEvent, GenericBarEventCreator>(); 054 055 protected IntervalUtil tracker = new IntervalUtil(0.4f, 0.6f); 056 protected IntervalUtil tracker2 = new IntervalUtil(20f, 40f); 057 protected TimeoutTracker<PortsideBarEvent> active = new TimeoutTracker<PortsideBarEvent>(); 058 protected TimeoutTracker<GenericBarEventCreator> timeout = new TimeoutTracker<GenericBarEventCreator>(); 059 060 protected long seed = Misc.genRandomSeed(); 061 062 public BarEventManager() { 063 super(); 064 Global.getSector().getMemoryWithoutUpdate().set(KEY, this); 065 readResolve(); 066 } 067 068// public long getSeed(SectorEntityToken entity) { 069// //updateSeed(); 070// if (entity == null) return seed; 071// return seed + (long) entity.getId().hashCode() * 181783497276652981L; 072// } 073 074 public long getSeed(SectorEntityToken entity, PersonAPI person, String extra) { 075 //updateSeed(); 076 long mult = 1; 077 if (entity != null) mult *= (long) entity.getName().hashCode(); 078 if (person != null) mult *= (long) person.getNameString().hashCode(); 079 if (extra != null) mult *= (long) extra.hashCode(); 080 081 return seed + mult * 181783497276652981L; 082 } 083 084 public void updateSeed() { 085 seed = Misc.genRandomSeed(); 086 } 087 088 089 protected Object readResolve() { 090 if (timeout == null) { 091 timeout = new TimeoutTracker<GenericBarEventCreator>(); 092 } 093 if (tracker2 == null) { 094 tracker2 = new IntervalUtil(20f, 40f); 095 } 096 if (seed == 0) { 097 updateSeed(); 098 } 099 100 updateBarEventCreatorsFromSpecs(); 101 102 return this; 103 } 104 105 106 public void updateBarEventCreatorsFromSpecs() { 107 List<BarEventSpec> specs = Global.getSettings().getAllBarEventSpecs(); 108 109 Set<String> validEvents = new HashSet<String>(); 110 Set<String> alreadyHaveCreatorsFor = new HashSet<String>(); 111 for (BarEventSpec spec : specs) { 112 validEvents.add(spec.getId()); 113 } 114 115 for (GenericBarEventCreator curr : new ArrayList<GenericBarEventCreator>(creators)) { 116 if (!curr.wasAutoAdded()) continue; 117 118 if (!validEvents.contains(curr.getBarEventId())) { 119 creators.remove(curr); 120 timeout.remove(curr); 121 } else { 122 alreadyHaveCreatorsFor.add(curr.getBarEventId()); 123 } 124 } 125 126 for (BarEventSpec spec : specs) { 127 if (!alreadyHaveCreatorsFor.contains(spec.getId())) { 128 SpecBarEventCreator curr = new SpecBarEventCreator(spec.getId()); 129 curr.setWasAutoAdded(true); 130 creators.add(curr); 131 } 132 } 133 } 134 135 136 public void addEventCreator(GenericBarEventCreator creator) { 137 creators.add(creator); 138 } 139 140 public boolean hasEventCreator(Class<?> clazz) { 141 for (GenericBarEventCreator script : creators) { 142 if (clazz.isInstance(script)) return true; 143 } 144 return false; 145 } 146 147 public List<GenericBarEventCreator> getCreators() { 148 return creators; 149 } 150 151 public TimeoutTracker<PortsideBarEvent> getActive() { 152 return active; 153 } 154 155 public TimeoutTracker<GenericBarEventCreator> getTimeout() { 156 return timeout; 157 } 158 159 public void setTimeout(Class creatorClass, float duration) { 160 for (GenericBarEventCreator curr : creators) { 161 if (curr.getClass().equals(creatorClass)) { 162 timeout.set(curr, duration); 163 break; 164 } 165 } 166 } 167 168 169 public void notifyWasInteractedWith(PortsideBarEvent event) { 170 PortsideBarData.getInstance().removeEvent(event); 171 GenericBarEventCreator creator = getCreatorFor(event); 172 if (creator != null) { 173 float dur = creator.getBarEventAcceptedTimeoutDuration(); 174 dur = Math.max(dur, timeout.getRemaining(creator)); 175 timeout.set(creator, dur); 176 active.remove(event); 177 } 178 } 179 180 public GenericBarEventCreator getCreatorFor(PortsideBarEvent event) { 181 return barEventCreators.get(event); 182 } 183 184 public void advance(float amount) { 185 float days = Misc.getDays(amount); 186 187 //timeout.clear(); 188 189 190 active.advance(days); 191 timeout.advance(days); 192 193 if (DebugFlags.BAR_DEBUG) { 194 days *= 100f; 195 timeout.clear(); 196 } 197 198 tracker.advance(days); 199 200 tracker2.advance(days); 201 if (tracker2.intervalElapsed()) { 202 updateSeed(); 203 } 204 205 if (tracker.intervalElapsed()) { 206 for (int i = 0; i < 5; i++) { 207 List<PortsideBarEvent> orphaned = new ArrayList<PortsideBarEvent>(barEventCreators.keySet()); 208 for (PortsideBarEvent s : active.getItems()) { 209 orphaned.remove(s); 210 } 211 212 for (PortsideBarEvent event : orphaned) { 213 GenericBarEventCreator creator = barEventCreators.remove(event); 214 if (creator != null) { 215 float dur = creator.getBarEventTimeoutDuration(); 216 dur = Math.max(dur, timeout.getRemaining(creator)); 217 timeout.set(creator, dur); 218 } 219 PortsideBarData.getInstance().removeEvent(event); 220 } 221 222 float f = Global.getSettings().getFloat("maxTotalBarEventsAsFractionOfEventTypes"); 223 float max = Math.round(creators.size() * f); 224 225 if (DebugFlags.BAR_DEBUG) { 226 max = 10000f; 227 } 228 229 if (max < 1) max = 1; 230 231 if (active.getItems().size() >= max) return; 232 if (barEventCreators.size() >= creators.size()) return; 233 234 Set<String> activeCreators = new LinkedHashSet<String>(); 235 for (GenericBarEventCreator curr : barEventCreators.values()) { 236 activeCreators.add(curr.getBarEventId()); 237 } 238 239 WeightedRandomPicker<GenericBarEventCreator> priority = new WeightedRandomPicker<GenericBarEventCreator>(); 240 WeightedRandomPicker<GenericBarEventCreator> picker = new WeightedRandomPicker<GenericBarEventCreator>(); 241 for (GenericBarEventCreator curr : creators) { 242 //if (barEventCreators.containsValue(curr)) continue; 243 if (activeCreators.contains(curr.getBarEventId())) continue; 244 if (timeout.contains(curr)) continue; 245 246 if (curr.isPriority()) { 247 priority.add(curr, curr.getBarEventFrequencyWeight()); 248 } else { 249 picker.add(curr, curr.getBarEventFrequencyWeight()); 250 } 251 } 252 253 GenericBarEventCreator pick = priority.pick(); 254 if (pick == null) pick = picker.pick(); 255 if (pick == null) return; 256 257 PortsideBarEvent event = pick.createBarEvent(); 258 if (event == null) return; 259 260 active.add(event, pick.getBarEventActiveDuration()); 261 barEventCreators.put(event, pick); 262 PortsideBarData.getInstance().addEvent(event); 263 } 264 } 265 } 266 267 268 public boolean isDone() { 269 return false; 270 } 271 272 public boolean runWhilePaused() { 273 return false; 274 } 275 276} 277 278 279 280 281 282 283