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