//todo xml
 // if the given initiative is still being used by any EventScheduling, throws, unless forceCancelEvents is true.
 // ...if forceCancelEvents is true, all events using this initiative will first be canceled and removed.
 // also throws if the currently executing one is using this initiative.
 public bool UnregisterInitiative(Initiative initiative, bool forceCancelEvents = false)
 {
     if (initiative == null)
     {
         throw new ArgumentNullException(nameof(initiative));
     }
     if (!oc.Contains(initiative))
     {
         return(false);
     }
     if (currentlyExecuting?.Initiative == initiative)
     {
         throw new InvalidOperationException("Can't unregister an initiative while the currently executing EventScheduling is still using it");
     }
     EventScheduling[] schedulings = scheduledEventsForInitiatives[initiative].ToArray();
     if (schedulings.Length > 0)
     {
         if (!forceCancelEvents)
         {
             throw new InvalidOperationException("Can't unregister an initiative while an EventScheduling is still using it");
         }
         foreach (EventScheduling eventScheduling in schedulings)
         {
             InternalEventScheduling es = eventScheduling as InternalEventScheduling;
             if (!es.IsLive)
             {
                 continue;
             }
             es.Canceled = true;
         }
         scheduledEventsForInitiatives.Clear(initiative);
     }
     return(oc.Remove(initiative));
 }
        /// <summary> Creates the InternalEventScheduling, adds it to the list of events for its initiative, adds it to the PQ, and returns it. </summary>
        private InternalEventScheduling CreateAndSchedule(IEvent scheduledEvent, long ticksInFuture, Initiative init)
        {
            var es = new InternalEventScheduling(scheduledEvent, currentTick, ticksInFuture, init);

            scheduledEventsForInitiatives.Add(init, es);
            pq.Enqueue(es);
            return(es);
        }
 private void RemoveSchedulingForInitiative(InternalEventScheduling es)
 {
     if (es?.Initiative is InternalInitiative init)
     {
         scheduledEventsForInitiatives.Remove(init, es);
         if (init.AutoRemove && !scheduledEventsForInitiatives.AnyValues(init))
         {
             // Get rid of an AutoRemove Initiative when there are no more scheduled events referencing it
             scheduledEventsForInitiatives.Clear(init);
             oc.Remove(init);
         }
     }
 }
        private int CompareEventSchedulings(InternalEventScheduling first, InternalEventScheduling second)
        {
            int tickCompare = first.ExecutionTick.CompareTo(second.ExecutionTick);

            if (tickCompare != 0)
            {
                return(tickCompare);
            }
            else
            {
                return(oc.Compare(first.Initiative, second.Initiative));
            }
        }
        public void ExecuteNextEvent()
        {
            InternalEventScheduling es = pq.Dequeue();

            while (es.Canceled)
            {
                es = pq.Dequeue();
            }
            currentTick        = es.ExecutionTick;
            currentlyExecuting = es;
            currentlyExecuting.Event.ExecuteEvent();
            currentlyExecuting = null;
            es.Executed        = true;
            RemoveSchedulingForInitiative(es);
        }
        public static EventScheduler Deserialize(BinaryReader reader,
                                                 Action <EventScheduling, BinaryReader> onLoadEventScheduling,
                                                 Action <Initiative, BinaryReader> onLoadInitiative,
                                                 Func <BinaryReader, IEvent> loadIEvent)
        {
            if (loadIEvent == null)
            {
                throw new ArgumentNullException(nameof(loadIEvent));
            }
            if (reader == null)
            {
                throw new ArgumentNullException(nameof(reader));
            }
            //dict of id -> obj this time...
            var objectsById = new Dictionary <int, object>();
            //read OC count...for each one...
            int ocCount = reader.ReadInt32();
            var inits   = new List <Initiative>();

            for (int i = 0; i < ocCount; ++i)
            {
                int        id     = reader.ReadInt32();
                bool       isAuto = reader.ReadBoolean();
                Initiative init   = new InternalInitiative {
                    AutoRemove = isAuto
                };
                if (!isAuto)
                {
                    onLoadInitiative?.Invoke(init, reader);
                }
                objectsById.Add(id, init);
                inits.Add(init);
            }
            // 'inits' now ready for use
            // (the OC should now be complete, and all non-auto inits have been associated with their user-assigned IDs again)
            //read PQ count...for each...
            int pqCount = reader.ReadInt32();
            var events  = new List <InternalEventScheduling>();

            for (int i = 0; i < pqCount; ++i)
            {
                //read the ID...
                int id = reader.ReadInt32();
                //call loadIEvent...
                IEvent ievent = loadIEvent(reader);
                //read creation tick, delay, and init ID.
                long       creationTick = reader.ReadInt64();
                long       delay        = reader.ReadInt64();
                int        initId       = reader.ReadInt32();
                Initiative init         = (objectsById[initId] as Initiative) ?? throw new Exception("Initiative not loaded properly");
                var        es           = new InternalEventScheduling(ievent, creationTick, delay, init);
                objectsById.Add(id, es);
                // then call onLoadEventScheduling.
                onLoadEventScheduling?.Invoke(es, reader);
                events.Add(es);
            }
            // 'events' now ready for use
            // (the PQ should now be complete)
            // finally, the MVD:
            //read a count of groups...
            int eventsForInitsCount = reader.ReadInt32();
            var eventsForInits      = new List <IGrouping <Initiative, InternalEventScheduling> >();

            for (int i = 0; i < eventsForInitsCount; ++i)
            {
                //read an init ID for each group...
                int initId = reader.ReadInt32();
                var init   = (objectsById[initId] as Initiative) ?? throw new Exception("Initiative not loaded correctly");
                //read a count for each group...
                int groupCount = reader.ReadInt32();
                var esGroup    = new List <InternalEventScheduling>();
                //read ES ids
                for (int j = 0; j < groupCount; ++j)
                {
                    int esId = reader.ReadInt32();
                    var es   = (objectsById[esId] as InternalEventScheduling) ?? throw new Exception("Event scheduling not loaded correctly");
                    esGroup.Add(es);
                }
                eventsForInits.Add(new Grouping <Initiative, InternalEventScheduling>(init, esGroup));
            }
            // 'eventsForInits' now ready for use
            return(new EventScheduler(inits, events, eventsForInits));
        }