/* * Copyright (C) 2012-2020 CypherCore * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ 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. 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); 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); } } /// /// 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 (Convert.ToBoolean(pair.Value & (uint)(1 << ((int)group + 15)))) _eventMap.Remove(pair.Key, pair.Value); } } /// /// 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; } /// /// 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(); } }