diff --git a/Maple2.File.Ingest/Mapper/MapEntityMapper.cs b/Maple2.File.Ingest/Mapper/MapEntityMapper.cs index 11bcaddee..46dbf5c01 100644 --- a/Maple2.File.Ingest/Mapper/MapEntityMapper.cs +++ b/Maple2.File.Ingest/Mapper/MapEntityMapper.cs @@ -272,7 +272,7 @@ private IEnumerable ParseMap(string xblock, IEnumerable e }; case IMS2TriggerMesh mesh: return new MapEntity(xblock, new Guid(trigger.EntityId), trigger.EntityName) { - Block = new Ms2TriggerMesh(mesh.Scale, mesh.TriggerObjectID, mesh.IsVisible), + Block = new Ms2TriggerMesh(mesh.Scale, mesh.TriggerObjectID, mesh.IsVisible, mesh.MinimapInVisible), }; case IMS2TriggerPortal _: throw new InvalidOperationException("IMS2TriggerPortal should be parsed as IPortal."); diff --git a/Maple2.File.Ingest/Mapper/TriggerMapper.cs b/Maple2.File.Ingest/Mapper/TriggerMapper.cs index 88625654c..c12da62c8 100644 --- a/Maple2.File.Ingest/Mapper/TriggerMapper.cs +++ b/Maple2.File.Ingest/Mapper/TriggerMapper.cs @@ -2,6 +2,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using System.Xml; +using M2dXmlGenerator; using Maple2.File.Ingest.Utils; using Maple2.File.IO; using Maple2.File.IO.Crypto.Common; @@ -60,6 +61,37 @@ protected override IEnumerable Map() { }; public static string NormalizeTriggerXmlNames(XmlDocument xml) { + // check for state nodes with feature attributes and remove disabled ones + List nodesToRemove = new List(); + foreach (XmlNode node in xml.SelectNodes("//state")!) { + XmlAttribute? featureAttr = node.Attributes?["feature"]; + if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { + nodesToRemove.Add(node); + } + } + + // Check for action nodes with feature attributes and remove disabled ones + foreach (XmlNode node in xml.SelectNodes("//action")!) { + XmlAttribute? featureAttr = node.Attributes?["feature"]; + if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { + nodesToRemove.Add(node); + } + } + + // Check for condition nodes with feature attributes and remove disabled ones + foreach (XmlNode node in xml.SelectNodes("//condition")!) { + XmlAttribute? featureAttr = node.Attributes?["feature"]; + if (featureAttr != null && !FeatureLocaleFilter.FeatureEnabled(featureAttr.Value)) { + nodesToRemove.Add(node); + } + } + + // Remove disabled feature nodes + foreach (XmlNode node in nodesToRemove) { + node.ParentNode?.RemoveChild(node); + } + + // Continue with the existing normalization logic foreach (XmlNode node in xml.SelectNodes("//state")!) { XmlAttribute? attr = node.Attributes?["name"]; Debug.Assert(attr?.Value != null, "Unable to find name param"); diff --git a/Maple2.Model/Game/TriggerObject.cs b/Maple2.Model/Game/TriggerObject.cs index 525f27d4a..e7cdb0fbe 100644 --- a/Maple2.Model/Game/TriggerObject.cs +++ b/Maple2.Model/Game/TriggerObject.cs @@ -29,10 +29,16 @@ public virtual void WriteTo(IByteWriter writer) { public class TriggerObjectSound(Ms2TriggerSound metadata) : TriggerObject(metadata); -public class TriggerObjectMesh(Ms2TriggerMesh metadata) : TriggerObject(metadata) { +public class TriggerObjectMesh : TriggerObject { public bool MinimapVisible { get; init; } public int Fade { get; set; } - public float Scale { get; set; } = 1f; + public float Scale { get; set; } + + public TriggerObjectMesh(Ms2TriggerMesh metadata) : base(metadata) { + MinimapVisible = metadata.MinimapInvisible; + this.Scale = metadata.Scale; + this.Visible = metadata.Visible; + } public override void WriteTo(IByteWriter writer) { base.WriteTo(writer); diff --git a/Maple2.Model/Metadata/MapEntity/Trigger.cs b/Maple2.Model/Metadata/MapEntity/Trigger.cs index 7b42ccc6a..eb0546451 100644 --- a/Maple2.Model/Metadata/MapEntity/Trigger.cs +++ b/Maple2.Model/Metadata/MapEntity/Trigger.cs @@ -45,7 +45,8 @@ public record Ms2TriggerLadder( public record Ms2TriggerMesh( float Scale, int TriggerId, - bool Visible) + bool Visible, + bool MinimapInvisible) : Trigger(TriggerId, Visible); public record Ms2TriggerPortal( diff --git a/Maple2.Server.Game/Manager/MasteryManager.cs b/Maple2.Server.Game/Manager/MasteryManager.cs index fccdcb15d..3b6f2deb8 100644 --- a/Maple2.Server.Game/Manager/MasteryManager.cs +++ b/Maple2.Server.Game/Manager/MasteryManager.cs @@ -40,6 +40,7 @@ public int this[MasteryType type] { }; set { short startLevel = GetLevel(type); + int startValue = this[type]; switch (type) { case MasteryType.Fishing: Mastery.Fishing = Math.Clamp(value, Mastery.Fishing, Constant.FishingMasteryMax); @@ -79,14 +80,18 @@ public int this[MasteryType type] { } session.Send(MasteryPacket.UpdateMastery(type, session.Mastery[type])); - if (startLevel < GetLevel(type)) { + int currentLevel = GetLevel(type); + if (startLevel < currentLevel || startValue == 0) { session.ConditionUpdate(ConditionType.mastery_grade, codeLong: (int) type); + } + if (startLevel > currentLevel) { session.ConditionUpdate(ConditionType.set_mastery_grade, codeLong: (int) type); if (type == MasteryType.Music) { session.ConditionUpdate(ConditionType.music_play_grade); } } } + } public short GetLevel(MasteryType type) { diff --git a/Maple2.Server.Game/Model/Field/Entity/FieldInstrument.cs b/Maple2.Server.Game/Model/Field/Entity/FieldInstrument.cs index 2f3bdd722..1ae84ab14 100644 --- a/Maple2.Server.Game/Model/Field/Entity/FieldInstrument.cs +++ b/Maple2.Server.Game/Model/Field/Entity/FieldInstrument.cs @@ -10,5 +10,4 @@ public class FieldInstrument(FieldManager field, int objectId, InstrumentMetadat public long StartTick { get; set; } public bool Ensemble { get; set; } public Item? Score { get; set; } - } diff --git a/Maple2.Server.Game/Model/Field/Widget/GuideWidget.cs b/Maple2.Server.Game/Model/Field/Widget/GuideWidget.cs new file mode 100644 index 000000000..c5e533780 --- /dev/null +++ b/Maple2.Server.Game/Model/Field/Widget/GuideWidget.cs @@ -0,0 +1,23 @@ +using System.Collections.Concurrent; +using System.Reflection; +using Maple2.Server.Game.Manager.Field; + +namespace Maple2.Server.Game.Model.Widget; + +public class GuideWidget : Widget { + + public GuideWidget(FieldManager field) : base(field) { + Conditions = new ConcurrentDictionary(); + } + + public override bool Check(string name, string arg) { + return Conditions.GetValueOrDefault(name) == 1; + } + + public override void Action(string function, int numericArg, string stringArg) { + MethodInfo? method = GetType().GetMethod(function, BindingFlags.NonPublic | BindingFlags.Instance); + if (method != null) { + method.Invoke(this, [stringArg, numericArg]); + } + } +} diff --git a/Maple2.Server.Game/Model/Field/Widget/IWidget.cs b/Maple2.Server.Game/Model/Field/Widget/IWidget.cs index f02ea8388..9f7017606 100644 --- a/Maple2.Server.Game/Model/Field/Widget/IWidget.cs +++ b/Maple2.Server.Game/Model/Field/Widget/IWidget.cs @@ -14,4 +14,5 @@ public interface IWidget { /// A numeric argument for the action. /// A string argument for the action. void Action(string function, int numericArg, string stringArg); + bool Check(string name, string arg); } diff --git a/Maple2.Server.Game/Model/Field/Widget/OxQuizWidget.cs b/Maple2.Server.Game/Model/Field/Widget/OxQuizWidget.cs index fcdf9a75f..4a1df1e89 100644 --- a/Maple2.Server.Game/Model/Field/Widget/OxQuizWidget.cs +++ b/Maple2.Server.Game/Model/Field/Widget/OxQuizWidget.cs @@ -3,6 +3,7 @@ using Maple2.Model.Metadata; using Maple2.Server.Game.Manager.Field; using Maple2.Server.Game.Packets; +using Serilog; namespace Maple2.Server.Game.Model.Widget; @@ -17,10 +18,36 @@ public OxQuizWidget(FieldManager field) : base(field) { questions = new ConcurrentDictionary(); } + public override bool Check(string name, string arg) { + return Conditions.GetValueOrDefault(name) == 1; + } + public override void Action(string function, int numericArg, string stringArg) { - MethodInfo? method = GetType().GetMethod(function, BindingFlags.NonPublic | BindingFlags.Instance); - if (method != null) { - method.Invoke(this, [stringArg, numericArg]); + switch (function) { + case "DevMode": + DevMode(stringArg, numericArg); + break; + case "PickQuiz": + PickQuiz(stringArg, numericArg); + break; + case "ShowQuiz": + ShowQuiz(stringArg, numericArg); + break; + case "ShowAnswer": + ShowAnswer(stringArg, numericArg); + break; + case "PreJudge": + PreJudge(stringArg, numericArg); + break; + case "Judge": + Judge(stringArg, numericArg); + break; + case "Winner": + Winner(stringArg, numericArg); + break; + default: + Log.Logger.Warning("Unknown function called on OxQuizWidget: {Function}", function); + break; } } diff --git a/Maple2.Server.Game/Model/Field/Widget/Widget.cs b/Maple2.Server.Game/Model/Field/Widget/Widget.cs index bbe3ed912..9abe8f42f 100644 --- a/Maple2.Server.Game/Model/Field/Widget/Widget.cs +++ b/Maple2.Server.Game/Model/Field/Widget/Widget.cs @@ -8,6 +8,9 @@ public class Widget : IWidget { public ConcurrentDictionary Conditions { get; set; } public virtual void Action(string function, int numericArg, string stringArg) { } + public virtual bool Check(string name, string arg) { + return false; + } public Widget(FieldManager field) { Field = field; diff --git a/Maple2.Server.Game/Model/Stats.cs b/Maple2.Server.Game/Model/Stats.cs index bc9cffb4f..67647b483 100644 --- a/Maple2.Server.Game/Model/Stats.cs +++ b/Maple2.Server.Game/Model/Stats.cs @@ -45,8 +45,7 @@ public Stats(NpcMetadataStat npcStats) { /// Clears and sets static stats. /// public void SetStaticStats(IReadOnlyDictionary statsDictionary) { - basicValues.Clear(); - specialValues.Clear(); + ClearStats(); foreach (BasicAttribute attribute in statsDictionary.Keys) { this[attribute].AddBase(statsDictionary[attribute]); } @@ -54,8 +53,7 @@ public void SetStaticStats(IReadOnlyDictionary statsDictio } public void Reset(IReadOnlyDictionary metadata, JobCode jobCode) { - basicValues.Clear(); - specialValues.Clear(); + ClearStats(); foreach (BasicAttribute attribute in metadata.Keys) { if (attribute is BasicAttribute.PhysicalAtk or BasicAttribute.MagicalAtk) { @@ -73,7 +71,7 @@ public void Reset(IReadOnlyDictionary metadata, JobCode jo [Obsolete("Use Reset(UserStatMetadata, JobCode) instead.")] public void Reset(JobCode jobCode, short level) { - basicValues.Clear(); + ClearStats(); this[BasicAttribute.Strength].AddBase(BaseStat.Strength(jobCode, level)); this[BasicAttribute.Dexterity].AddBase(BaseStat.Dexterity(jobCode, level)); @@ -149,6 +147,22 @@ public Stat this[SpecialAttribute attribute] { } set => specialValues[attribute] = value; } + + /// + /// Safely clear stats. + /// + private void ClearStats() { + foreach (Stat stat in basicValues.Values) { + stat.AddBase(-stat.Base); + stat.AddTotal(-stat.Total); + stat.AddRate(-stat.Rate); + } + foreach (Stat stat in specialValues.Values) { + stat.AddBase(-stat.Base); + stat.AddTotal(-stat.Total); + stat.AddRate(-stat.Rate); + } + } } public sealed class Stat { diff --git a/Maple2.Server.Game/PacketHandlers/InstrumentHandler.cs b/Maple2.Server.Game/PacketHandlers/InstrumentHandler.cs index de74d45c9..30c5e72f4 100644 --- a/Maple2.Server.Game/PacketHandlers/InstrumentHandler.cs +++ b/Maple2.Server.Game/PacketHandlers/InstrumentHandler.cs @@ -153,11 +153,12 @@ private void HandleStopScore(GameSession session) { return; } + int masteryValue = session.Instrument.Score?.Metadata.Music?.MasteryValue ?? 1; + int masteryValueMax = session.Instrument.Score?.Metadata.Music?.MasteryValueMax ?? 1; + // TODO: Prestige exp long totalTickTime = Environment.TickCount64 - session.Instrument.StartTick; - if (session.ItemMetadata.TryGet(session.Instrument.Value.EquipId, out ItemMetadata? metadata) && metadata.Music != null) { - session.Mastery[MasteryType.Music] += (int) Math.Min(totalTickTime * metadata.Music.MasteryValue / 1000, metadata.Music.MasteryValueMax); - } + session.Mastery[MasteryType.Music] += (int) Math.Min(totalTickTime * masteryValue / 1000, masteryValueMax); short masteryLevel = session.Mastery.GetLevel(MasteryType.Music); ExpType expType = masteryLevel switch { diff --git a/Maple2.Server.Game/Trigger/Helpers/ITriggerContext.cs b/Maple2.Server.Game/Trigger/Helpers/ITriggerContext.cs index a2f614437..a944b3765 100644 --- a/Maple2.Server.Game/Trigger/Helpers/ITriggerContext.cs +++ b/Maple2.Server.Game/Trigger/Helpers/ITriggerContext.cs @@ -246,7 +246,7 @@ public interface ITriggerContext { public bool ScoreBoardScore(int score, OperatorType operatorType); public bool ShadowExpeditionPoints(int score); public bool TimeExpired(string timerId); - public bool UserDetected(int[] boxIds, int jobCode); + public bool UserDetected(int[] boxIds, int jobCode, bool negate); public bool UserValue(string key, int value, bool negate); public bool WaitAndResetTick(int waitTick); public bool WaitSecondsUserValue(string key, string desc); @@ -254,5 +254,5 @@ public interface ITriggerContext { public bool WeddingEntryInField(string entryType, bool isInField); public bool WeddingHallState(string state, bool success); public bool WeddingMutualAgreeResult(string agreeType); - public bool WidgetValue(string type, string widgetName, int condition, bool negate, string desc); + public bool WidgetValue(string type, string widgetName, string widgeArg, bool negate, string desc); } diff --git a/Maple2.Server.Game/Trigger/Helpers/TriggerFunctionMapping.cs b/Maple2.Server.Game/Trigger/Helpers/TriggerFunctionMapping.cs index f38ab516c..b90485594 100644 --- a/Maple2.Server.Game/Trigger/Helpers/TriggerFunctionMapping.cs +++ b/Maple2.Server.Game/Trigger/Helpers/TriggerFunctionMapping.cs @@ -254,7 +254,7 @@ public static class TriggerFunctionMapping { { "score_board_score", (ctx, attrs) => ctx.ScoreBoardScore(ParseInt(attrs?["score"]?.Value), ParseOperatorType(attrs?["compare_op"]?.Value)) }, { "shadow_expedition_points", (ctx, attrs) => ctx.ShadowExpeditionPoints(ParseInt(attrs?["score"]?.Value)) }, { "time_expired", (ctx, attrs) => ctx.TimeExpired(attrs?["timer_id"]?.Value ?? string.Empty) }, - { "user_detected", (ctx, attrs) => ctx.UserDetected(ParseIntArray(attrs?["box_ids"]?.Value), ParseInt(attrs?["job_code"]?.Value)) }, + { "user_detected", (ctx, attrs) => ctx.UserDetected(ParseIntArray(attrs?["box_ids"]?.Value), ParseInt(attrs?["job_code"]?.Value), ParseBool(attrs?["negate"]?.Value)) }, { "user_value", (ctx, attrs) => ctx.UserValue(attrs?["key"]?.Value ?? string.Empty, ParseInt(attrs?["value"]?.Value), ParseBool(attrs?["negate"]?.Value)) }, { "wait_and_reset_tick", (ctx, attrs) => ctx.WaitAndResetTick(ParseInt(attrs?["wait_tick"]?.Value)) }, { "wait_seconds_user_value", (ctx, attrs) => ctx.WaitSecondsUserValue(attrs?["key"]?.Value ?? string.Empty, attrs?["desc"]?.Value ?? string.Empty) }, @@ -262,17 +262,27 @@ public static class TriggerFunctionMapping { { "wedding_entry_in_field", (ctx, attrs) => ctx.WeddingEntryInField(attrs?["entry_type"]?.Value ?? string.Empty, ParseBool(attrs?["is_in_field"]?.Value)) }, { "wedding_hall_state", (ctx, attrs) => ctx.WeddingHallState(attrs?["hallState"]?.Value ?? string.Empty, ParseBool(attrs?["success"]?.Value)) }, { "wedding_mutual_agree_result", (ctx, attrs) => ctx.WeddingMutualAgreeResult(attrs?["agree_type"]?.Value ?? string.Empty) }, - { "widget_value", (ctx, attrs) => ctx.WidgetValue(attrs?["type"]?.Value ?? string.Empty, attrs?["widget_name"]?.Value ?? string.Empty, ParseInt(attrs?["condition"]?.Value), ParseBool(attrs?["negate"]?.Value), attrs?["desc"]?.Value ?? string.Empty) }, + { "widget_value", (ctx, attrs) => ctx.WidgetValue(attrs?["type"]?.Value ?? string.Empty, attrs?["widget_name"]?.Value ?? string.Empty, attrs?["condition"]?.Value, ParseBool(attrs?["negate"]?.Value), attrs?["desc"]?.Value ?? string.Empty) }, }; private static Weather ParseWeather(string? value) { if (string.IsNullOrEmpty(value)) return Weather.Clear; - return (Weather) Enum.Parse(typeof(Weather), value, true); + return Enum.TryParse(value, true, out Weather weather) ? weather : Weather.Clear; } private static BannerType ParseBannerType(string? value) { - if (string.IsNullOrEmpty(value)) return BannerType.Lose; - return (BannerType) Enum.Parse(typeof(BannerType), value, true); + if (string.IsNullOrEmpty(value)) return BannerType.Text; + return value switch { + // "0" + "1" => BannerType.Text, + // "2" + "3" => BannerType.Winner, + "4" => BannerType.Lose, + "5" => BannerType.GameOver, + "6" => BannerType.Bonus, + "7" => BannerType.Success, + _ => BannerType.Text, + }; } private static Locale ParseLocale(string? value) { diff --git a/Maple2.Server.Game/Trigger/TriggerContext.Interface.cs b/Maple2.Server.Game/Trigger/TriggerContext.Interface.cs index f70fdedf2..3c1dec8bc 100644 --- a/Maple2.Server.Game/Trigger/TriggerContext.Interface.cs +++ b/Maple2.Server.Game/Trigger/TriggerContext.Interface.cs @@ -194,14 +194,13 @@ public void WidgetAction(string type, string func, string widgetArg, string desc } #region Conditions - public bool WidgetValue(string type, string widgetName, int condition, bool negate, string desc = "") { + public bool WidgetValue(string type, string widgetName, string widgetArg, bool negate, string desc = "") { DebugLog("[WidgetValue] type:{Type}, widgetName:{Name}, desc:{Desc}", type, widgetName, desc); if (!Field.Widgets.TryGetValue(type, out Widget? widget)) { return negate; } - - bool result = widget.Conditions.GetValueOrDefault(widgetName) == condition; + bool result = widget.Check(widgetName, widgetArg); if (negate) { return !result; } diff --git a/Maple2.Server.Game/Trigger/TriggerContext.Player.cs b/Maple2.Server.Game/Trigger/TriggerContext.Player.cs index f977a9205..88a1a097d 100644 --- a/Maple2.Server.Game/Trigger/TriggerContext.Player.cs +++ b/Maple2.Server.Game/Trigger/TriggerContext.Player.cs @@ -234,6 +234,7 @@ public bool QuestUserDetected(int[] boxIds, int[] questIds, int[] questStates, i DebugLog("[QuestUserDetected] boxIds:{BoxIds}, questIds:{QuestIds}, questStates:{QuestStates}, jobCode:{JobCode}", string.Join(", ", boxIds), string.Join(", ", questIds), string.Join(", ", questStates), (JobCode) jobCode); + foreach (FieldPlayer player in PlayersInBox(boxIds)) { foreach (int questId in questIds) { if (!player.Session.Quest.TryGetQuest(questId, out Quest? quest)) { @@ -263,18 +264,21 @@ public bool QuestUserDetected(int[] boxIds, int[] questIds, int[] questStates, i return negate; } - public bool UserDetected(int[] boxIds, int jobCode) { + public bool UserDetected(int[] boxIds, int jobCode, bool negate) { DebugLog("[UserDetected] boxIds:{BoxIds}, jobCode:{JobCode}", string.Join(", ", boxIds), (JobCode) jobCode); IEnumerable boxes = boxIds .Select(boxId => Objects.Boxes.GetValueOrDefault(boxId)) .Where(box => box != null)!; + bool result; if (jobCode != 0) { - return Field.Players.Values + result = Field.Players.Values .Any(player => player.Value.Character.Job.Code() == (JobCode) jobCode && boxes.Any(box => box.Contains(player.Position))); + } else { + result = Field.Players.Values.Any(player => boxes.Any(box => box.Contains(player.Position))); } - return Field.Players.Values.Any(player => boxes.Any(box => box.Contains(player.Position))); + return negate ? !result : result; } public bool WaitSecondsUserValue(string key, string desc) { diff --git a/Maple2.Server.World/WorldServer.cs b/Maple2.Server.World/WorldServer.cs index 9b312c2bc..6eb2c972c 100644 --- a/Maple2.Server.World/WorldServer.cs +++ b/Maple2.Server.World/WorldServer.cs @@ -2,12 +2,10 @@ using Grpc.Core; using Maple2.Database.Extensions; using Maple2.Database.Storage; -using Maple2.Model.Enum; using Maple2.Model.Game; using Maple2.Model.Game.Event; using Maple2.Model.Metadata; using Maple2.Server.Channel.Service; -using Maple2.Server.Core.Sync; using Maple2.Server.World.Containers; using Maple2.Tools.Scheduler; using Serilog; @@ -187,18 +185,17 @@ private void StartWorldEvents() { if (startTime > eventData.EndTime) { continue; } - scheduler.Schedule(() => GlobalPortal(eventData), (int) (startTime - DateTime.Now).TotalMilliseconds); + scheduler.Schedule(() => GlobalPortal(eventData, startTime), (int) (startTime - DateTime.Now).TotalMilliseconds); } } } - private void GlobalPortal(GlobalPortalMetadata data) { - DateTime now = DateTime.Now; - + private void GlobalPortal(GlobalPortalMetadata data, DateTime startTime) { // check probability bool run = !(data.Probability < 100 && Random.Shared.Next(100) > data.Probability); if (run) { + DateTime now = DateTime.Now; globalPortalLookup.Create(data, (long) (now.ToEpochSeconds() + data.LifeTime.TotalMilliseconds), out int eventId); if (!globalPortalLookup.TryGet(out GlobalPortalManager? manager)) { logger.Error("Failed to create global portal"); @@ -215,7 +212,7 @@ private void GlobalPortal(GlobalPortalMetadata data) { }); } - DateTime nextRunTime = now + data.CycleTime; + DateTime nextRunTime = startTime + data.CycleTime; if (data.RandomTime > TimeSpan.Zero) { nextRunTime += TimeSpan.FromMilliseconds(Random.Shared.Next((int) data.RandomTime.TotalMilliseconds)); } @@ -224,7 +221,7 @@ private void GlobalPortal(GlobalPortalMetadata data) { return; } - scheduler.Schedule(() => GlobalPortal(data), (int) (nextRunTime - DateTime.Now).TotalMilliseconds); + scheduler.Schedule(() => GlobalPortal(data, nextRunTime), (int) (nextRunTime - DateTime.Now).TotalMilliseconds); } private void ScheduleGameEvents() {