// Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Linq; namespace Framework.Dynamic { public class EventMap { /// /// Removes all scheduled events and resets time and phase. /// public void Reset() { _eventMap.Clear(); _time = TimeSpan.Zero; _phase = 0; } /// /// Updates the timer of the event map. /// /// Value in ms to be added to time. public void Update(uint time) { Update(TimeSpan.FromMilliseconds(time)); } /// /// Updates the timer of the event map. /// /// Value in ms to be added to time. void Update(TimeSpan time) { _time += time; } /// /// /// /// Active phases as mask. byte GetPhaseMask() { return _phase; } /// /// /// /// True, if there are no events scheduled. public bool Empty() { return _eventMap.Empty(); } /// /// Sets the phase of the map (absolute). /// /// Phase which should be set. Values: 1 - 8. 0 resets phase. public void SetPhase(byte phase) { if (phase == 0) _phase = 0; else if (phase <= 8) _phase = (byte)(1 << (phase - 1)); } /// /// Activates the given phase (bitwise). /// /// Phase which should be activated. Values: 1 - 8 void AddPhase(byte phase) { if (phase != 0 && phase <= 8) _phase |= (byte)(1 << (phase - 1)); } /// /// Deactivates the given phase (bitwise). /// /// Phase which should be deactivated. Values: 1 - 8. void RemovePhase(byte phase) { if (phase != 0 && phase <= 8) _phase &= (byte)~(1 << (phase - 1)); } /// /// Schedules a new event. /// /// The id of the new event. /// The time in milliseconds until the event occurs. /// The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. /// The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. public void ScheduleEvent(uint eventId, TimeSpan time, uint group = 0, byte phase = 0) { if (group != 0 && group <= 8) eventId |= (uint)(1 << ((int)group + 15)); if (phase != 0 && phase <= 8) eventId |= (uint)(1 << (phase + 23)); _eventMap.Add(_time + time, eventId); } /// /// Schedules a new event. /// /// The id of the new event. /// The minimum time until the event occurs as TimeSpan type. /// The maximum time until the event occurs as TimeSpan type. /// The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. /// The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. public void ScheduleEvent(uint eventId, TimeSpan minTime, TimeSpan maxTime, uint group = 0, byte phase = 0) { ScheduleEvent(eventId, RandomHelper.RandTime(minTime, maxTime), group, phase); } /// /// Cancels the given event and reschedules it. /// /// The id of the event. /// The time in milliseconds as TimeSpan until the event occurs. /// The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. /// The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. public void RescheduleEvent(uint eventId, TimeSpan time, uint group = 0, byte phase = 0) { CancelEvent(eventId); RescheduleEvent(eventId, time, group, phase); } /// /// Cancels the given event and reschedules it. /// /// The id of the event. /// The minimum time until the event occurs as TimeSpan type. /// The maximum time until the event occurs as TimeSpan type. /// The group which the event is associated to. Has to be between 1 and 8. 0 means it has no group. /// The phase in which the event can occur. Has to be between 1 and 8. 0 means it can occur in all phases. public void RescheduleEvent(uint eventId, TimeSpan minTime, TimeSpan maxTime, uint group = 0, byte phase = 0) { RescheduleEvent(eventId, RandomHelper.RandTime(minTime, maxTime), group, phase); } /// /// Repeats the most recently executed event. /// /// Time until the event occurs as TimeSpan. public void Repeat(TimeSpan time) { _eventMap.Add(_time + time, _lastEvent); } /// /// Repeats the most recently executed event. Equivalent to Repeat(urand(minTime, maxTime) /// /// The minimum time until the event occurs as TimeSpan type. /// The maximum time until the event occurs as TimeSpan type. public void Repeat(TimeSpan minTime, TimeSpan maxTime) { Repeat(RandomHelper.RandTime(minTime, maxTime)); } /// /// Returns the next event to be executed and removes it from map. /// /// Id of the event to execute. /// public uint ExecuteEvent() { while (!Empty()) { var pair = _eventMap.FirstOrDefault(); if (pair.Key > _time) return 0; else if (_phase != 0 && Convert.ToBoolean(pair.Value & 0xFF000000) && !Convert.ToBoolean((pair.Value >> 24) & _phase)) _eventMap.Remove(pair); else { uint eventId = (pair.Value & 0x0000FFFF); _lastEvent = pair.Value; // include phase/group _eventMap.Remove(pair); ScheduleNextFromSeries(_lastEvent); return eventId; } } return 0; } public void ExecuteEvents(Action action) { uint id; while ((id = ExecuteEvent()) != 0) action(id); } /// /// Delays all events. /// /// Amount of delay as TimeSpan type. public void DelayEvents(TimeSpan delay) { if (Empty()) return; MultiMap delayed = new(); foreach (var pair in _eventMap.KeyValueList) { delayed.Add(pair.Key + delay, pair.Value); _eventMap.Remove(pair.Key, pair.Value); } foreach (var del in delayed) _eventMap.Add(del); } /// /// Delay all events of the same group. /// /// Amount of delay as TimeSpan type. /// Group of the events. public void DelayEvents(TimeSpan delay, uint group) { if (group == 0 || group > 8 || Empty()) return; MultiMap delayed = new(); foreach (var pair in _eventMap.KeyValueList) { if (Convert.ToBoolean(pair.Value & (1 << (int)(group + 15)))) { delayed.Add(pair.Key + delay, pair.Value); _eventMap.Remove(pair.Key, pair.Value); } } foreach (var del in delayed) _eventMap.Add(del); } /// /// Cancels all events of the specified id. /// /// Event id to cancel. public void CancelEvent(uint eventId) { if (Empty()) return; foreach (var pair in _eventMap.KeyValueList) { if (eventId == (pair.Value & 0x0000FFFF)) _eventMap.Remove(pair.Key, pair.Value); } foreach (var key in _timerSeries.Keys.ToList()) { if (eventId == (key & 0x0000FFFF)) _timerSeries.Remove(key); } } /// /// Cancel events belonging to specified group. /// /// Group to cancel. public void CancelEventGroup(uint group) { if (group == 0 || group > 8 || Empty()) return; foreach (var pair in _eventMap.KeyValueList) { if ((pair.Value & (uint)(1 << ((int)group + 15))) != 0) _eventMap.Remove(pair.Key, pair.Value); } foreach (var key in _timerSeries.Keys.ToList()) { if ((key & (1 << ((int)group + 15))) != 0) _timerSeries.Remove(key); } } /// /// Returns time as TimeSpan type until next event. /// /// Id of the event. /// Time of next event. If event is not scheduled returns public TimeSpan GetTimeUntilEvent(uint eventId) { foreach (var pair in _eventMap) if (eventId == (pair.Value & 0x0000FFFF)) return pair.Key - _time; return TimeSpan.MaxValue; } /// /// Schedules specified event with next timer from series /// /// full event data, including group and phase public void ScheduleNextFromSeries(uint eventData) { if (_timerSeries.TryGetValue(eventData, out Queue queue)) return; if (queue.Count == 0) return; ScheduleEvent(eventData, queue.Dequeue()); } /// /// Schedules specified event with first value of the series and then requeues with the next /// /// eventId of the event. /// group of the event. /// phase of the event. /// timeSeries specifying the times the event should be automatically scheduled after each trigger (first value is initial schedule) public void ScheduleEventSeries(uint eventId, byte group, byte phase, List timeSeries) { if (group != 0 && group <= 8) eventId |= (1u << (group + 15)); if (phase != 0 && phase <= 8) eventId |= (1u << (phase + 23)); foreach (var time in timeSeries) _timerSeries[eventId].Enqueue(time); ScheduleNextFromSeries(eventId); } /// /// Schedules specified event with first value of the series and then requeues with the next /// /// eventId of the event. /// timeSeries specifying the times the event should be automatically scheduled after each trigger (first value is initial schedule) public void ScheduleEventSeries(uint eventId, List timeSeries) { ScheduleEventSeries(eventId, 0, 0, timeSeries); } /// /// Returns wether event map is in specified phase or not. /// /// Wanted phase. /// True, if phase of event map contains specified phase. public bool IsInPhase(byte phase) { return phase <= 8 && (phase == 0 || Convert.ToBoolean(_phase & (1 << (phase - 1)))); } /// /// Internal timer. /// This does not represent the real date/time value. /// It's more like a stopwatch: It can run, it can be stopped, /// it can be resetted and so on. Events occur when this timer /// has reached their time value. Its value is changed in the Update method. /// TimeSpan _time; /// /// Phase mask of the event map. /// Contains the phases the event map is in. Multiple /// phases from 1 to 8 can be set with SetPhase or /// AddPhase. RemovePhase deactives a phase. /// byte _phase; /// /// Stores information on the most recently executed event /// uint _lastEvent; /// /// Key: Time as uint when the event should occur. /// Value: The event data as uint. /// /// Structure of event data: /// - Bit 0 - 15: Event Id. /// - Bit 16 - 23: Group /// - Bit 24 - 31: Phase /// - Pattern: 0xPPGGEEEE /// SortedMultiMap _eventMap = new(); /// /// Stores information about time series which requeue itself until series is empty /// Dictionary> _timerSeries = new(); } }