diff --git a/data/json/README.md b/data/json/eventscheduler/README.md similarity index 100% rename from data/json/README.md rename to data/json/eventscheduler/README.md diff --git a/data/json/eventscheduler/events.json b/data/json/eventscheduler/events.json new file mode 100644 index 00000000000..d64ca377176 --- /dev/null +++ b/data/json/eventscheduler/events.json @@ -0,0 +1,184 @@ +{ + "events": [ + { + "name": "Forge Time", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "forgechance": 120 + }, + "description": "Increases the success rate of forges by 20%.", + "colors": { + "colordark": "#2b3e50", + "colorlight": "#3d5a73" + }, + "details": { + "displaypriority": 5, + "isseasonal": 0, + "specialevent": 1 + } + }, + { + "name": "Double Bestiary", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "doublebestiary": true + }, + "description": "Doubles the bestiary counter when defeating monsters.", + "colors": { + "colordark": "#3a4f2a", + "colorlight": "#4f713a" + }, + "details": { + "displaypriority": 5, + "isseasonal": 0, + "specialevent": 1 + } + }, + { + "name": "Fast Exercise", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "doubleexercise": true + }, + "description": "Exercise weapons are faster, doubling their speed.", + "colors": { + "colordark": "#5a3a2a", + "colorlight": "#7b4f3a" + }, + "details": { + "displaypriority": 5, + "isseasonal": 0, + "specialevent": 1 + } + }, + { + "name": "50% Loot Bonus", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "lootrate": 150 + }, + "description": "Increases loot by 50%.", + "colors": { + "colordark": "#2b1e10", + "colorlight": "#4a2e18" + }, + "details": { + "displaypriority": 6, + "isseasonal": 0, + "specialevent": 1 + } + }, + { + "name": "50% Exp Bonus", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "exprate": 150 + }, + "description": "Increases experience by 50%.", + "colors": { + "colordark": "#234d00", + "colorlight": "#3a7500" + }, + "details": { + "displaypriority": 6, + "isseasonal": 0, + "specialevent": 1 + } + }, + { + "name": "Boss Cooldown Reduction", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "bosscooldown": 150 + }, + "description": "Reduces boss cooldown time by 50%.", + "colors": { + "colordark": "#2d3c5a", + "colorlight": "#4a5f7d" + }, + "details": { + "displaypriority": 6, + "isseasonal": 0, + "specialevent": 1 + } + }, + { + "name": "Double Exp", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "exprate": 200 + }, + "description": "Double experience when hunting monsters.", + "colors": { + "colordark": "#002d00", + "colorlight": "#004400" + }, + "details": { + "displaypriority": 6, + "isseasonal": 1, + "specialevent": 1 + } + }, + { + "name": "Double Loot", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "lootrate": 200 + }, + "description": "Doubles the amount of loot obtained from monsters.", + "colors": { + "colordark": "#2a1d00", + "colorlight": "#4c3300" + }, + "details": { + "displaypriority": 6, + "isseasonal": 1, + "specialevent": 1 + } + }, + { + "name": "Double Bosstiary", + "startdate": "11/12/2024", + "enddate": "11/17/2024", + "ingame": { + "doublebosstiary": true + }, + "description": "Doubles the bestiary counter for bosses.", + "colors": { + "colordark": "#3a005a", + "colorlight": "#4e0075" + }, + "details": { + "displaypriority": 5, + "isseasonal": 0, + "specialevent": 1 + } + }, + { + "name": "Fast Respawn", + "startdate": "11/01/2024", + "enddate": "11/15/2024", + "ingame": { + "spawnrate": 200 + }, + "description": "Monsters respawn twice as fast.", + "colors": { + "colordark": "#4d2f00", + "colorlight": "#6e3e00" + }, + "details": { + "displaypriority": 6, + "isseasonal": 1, + "specialevent": 1 + } + } + ] +} diff --git a/data/json/eventscheduler/scripts/example.lua b/data/json/eventscheduler/scripts/example.lua new file mode 100644 index 00000000000..c39ef24c2ea --- /dev/null +++ b/data/json/eventscheduler/scripts/example.lua @@ -0,0 +1,4 @@ +local globalEvent = GlobalEvent("EventScheduleExample") +function globalEvent.onStartup() end + +globalEvent:register() diff --git a/data/json/scripts/boss_cooldown.lua b/data/json/scripts/boss_cooldown.lua deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/data/json/scripts/double_bestiary.lua b/data/json/scripts/double_bestiary.lua deleted file mode 100644 index b267b076f49..00000000000 --- a/data/json/scripts/double_bestiary.lua +++ /dev/null @@ -1,6 +0,0 @@ -local globalEvent = GlobalEvent("EventScheduleDoubleBestiaryKV") -function globalEvent.onStartup() - KV.scoped("eventscheduler"):set("double-bestiary", true) -end - -globalEvent:register() diff --git a/data/json/scripts/double_bosstiary.lua b/data/json/scripts/double_bosstiary.lua deleted file mode 100644 index 749e0d09a64..00000000000 --- a/data/json/scripts/double_bosstiary.lua +++ /dev/null @@ -1,6 +0,0 @@ -local globalEvent = GlobalEvent("EventScheduleDoubleBosstiaryKV") -function globalEvent.onStartup() - KV.scoped("eventscheduler"):set("double-bosstiary", true) -end - -globalEvent:register() diff --git a/data/json/scripts/fast_exercise.lua b/data/json/scripts/fast_exercise.lua deleted file mode 100644 index b376f679c4c..00000000000 --- a/data/json/scripts/fast_exercise.lua +++ /dev/null @@ -1,6 +0,0 @@ -local globalEvent = GlobalEvent("EventScheduleFastExerciseKV") -function globalEvent.onStartup() - KV.scoped("eventscheduler"):set("fast-exercise", true) -end - -globalEvent:register() diff --git a/data/json/scripts/forge_time.lua b/data/json/scripts/forge_time.lua deleted file mode 100644 index f628f52f0b4..00000000000 --- a/data/json/scripts/forge_time.lua +++ /dev/null @@ -1,6 +0,0 @@ -local globalEvent = GlobalEvent("EventScheduleForgeTimeKV") -function globalEvent.onStartup() - KV.scoped("eventscheduler"):set("forge-chance", 20) -end - -globalEvent:register() diff --git a/src/canary_server.cpp b/src/canary_server.cpp index 0c7d75f18c7..ba50f0e6e68 100644 --- a/src/canary_server.cpp +++ b/src/canary_server.cpp @@ -377,7 +377,7 @@ void CanaryServer::loadModules() { // It needs to be loaded after the revscript is read in order to use the scripting interface modulesLoadHelper(g_eventsScheduler().loadScheduleEventFromXml(), "XML/events.xml"); - modulesLoadHelper(g_eventsScheduler().loadScheduleEventFromJson(), "json/events.json"); + modulesLoadHelper(g_eventsScheduler().loadScheduleEventFromJson(), "json/eventscheduler/events.json"); g_game().loadBoostedCreature(); g_ioBosstiary().loadBoostedBoss(); diff --git a/src/game/scheduling/events_scheduler.cpp b/src/game/scheduling/events_scheduler.cpp index 01790789408..01eca7cda8f 100644 --- a/src/game/scheduling/events_scheduler.cpp +++ b/src/game/scheduling/events_scheduler.cpp @@ -19,10 +19,11 @@ bool EventsScheduler::loadScheduleEventFromJson() { g_kv().scoped("eventscheduler")->remove("double-bestiary"); g_kv().scoped("eventscheduler")->remove("double-bosstiary"); g_kv().scoped("eventscheduler")->remove("fast-exercise"); + g_kv().scoped("eventscheduler")->remove("boss-cooldown"); using json = nlohmann::json; auto coreFolder = g_configManager().getString(CORE_DIRECTORY); - auto folder = coreFolder + "/json/events.json"; + auto folder = coreFolder + "/json/eventscheduler/events.json"; std::ifstream file(folder); if (!file.is_open()) { g_logger().error("{} - Unable to open file '{}'", __FUNCTION__, folder); @@ -56,8 +57,7 @@ bool EventsScheduler::loadScheduleEventFromJson() { } int startYear, startMonth, startDay, endYear, endMonth, endDay; - if (sscanf(event["startdate"].get().c_str(), "%d/%d/%d", &startMonth, &startDay, &startYear) != 3 || - sscanf(event["enddate"].get().c_str(), "%d/%d/%d", &endMonth, &endDay, &endYear) != 3) { + if (sscanf(event["startdate"].get().c_str(), "%d/%d/%d", &startMonth, &startDay, &startYear) != 3 || sscanf(event["enddate"].get().c_str(), "%d/%d/%d", &endMonth, &endDay, &endYear) != 3) { g_logger().warn("{} - Invalid date format for event '{}'", __FUNCTION__, eventName); continue; } @@ -94,7 +94,12 @@ bool EventsScheduler::loadScheduleEventFromJson() { static_cast(event.contains("ingame") && event["ingame"].contains("lootrate") ? event["ingame"].value("lootrate", 100) : 100), static_cast(event.contains("ingame") && event["ingame"].contains("bosslootrate") ? event["ingame"].value("bosslootrate", 100) : 100), static_cast(event.contains("ingame") && event["ingame"].contains("spawnrate") ? event["ingame"].value("spawnrate", 100) : 100), - static_cast(event.contains("ingame") && event["ingame"].contains("skillrate") ? event["ingame"].value("skillrate", 100) : 100) + static_cast(event.contains("ingame") && event["ingame"].contains("skillrate") ? event["ingame"].value("skillrate", 100) : 100), + static_cast(event.contains("ingame") && event["ingame"].contains("forgechance") ? event["ingame"].value("forge-chance", 100) : 100), + static_cast(event.contains("ingame") && event["ingame"].contains("bosscooldown") ? event["ingame"].value("bosscooldown", 100) : 100), + event.contains("ingame") && event["ingame"].contains("doublebestiary") ? event["ingame"].value("doublebestiary", false) : false, + event.contains("ingame") && event["ingame"].contains("doublebosstiary") ? event["ingame"].value("doublebosstiary", false) : false, + event.contains("ingame") && event["ingame"].contains("fastexercise") ? event["ingame"].value("fastexercise", false) : false, }; for (const auto &[existingEventName, rates] : eventsOnSameDay) { @@ -102,18 +107,49 @@ bool EventsScheduler::loadScheduleEventFromJson() { if (rates.exprate != 100 && currentEventRates.exprate != 100 && rates.exprate == currentEventRates.exprate) { modifiedRates.emplace_back("exprate"); + g_eventsScheduler().setExpSchedule(rates.exprate); } if (rates.lootrate != 100 && currentEventRates.lootrate != 100 && rates.lootrate == currentEventRates.lootrate) { modifiedRates.emplace_back("lootrate"); + g_eventsScheduler().setLootSchedule(rates.lootrate); } if (rates.bosslootrate != 100 && currentEventRates.bosslootrate != 100 && rates.bosslootrate == currentEventRates.bosslootrate) { modifiedRates.emplace_back("bosslootrate"); + g_eventsScheduler().setBossLootSchedule(rates.bosslootrate); } if (rates.spawnrate != 100 && currentEventRates.spawnrate != 100 && rates.spawnrate == currentEventRates.spawnrate) { modifiedRates.emplace_back("spawnrate"); + g_eventsScheduler().setSpawnMonsterSchedule(rates.spawnrate); } if (rates.skillrate != 100 && currentEventRates.skillrate != 100 && rates.skillrate == currentEventRates.skillrate) { modifiedRates.emplace_back("skillrate"); + g_eventsScheduler().setSkillSchedule(rates.skillrate); + } + + // KV changes + if (rates.forgeChance != 100 && currentEventRates.forgeChance != 100 && rates.forgeChance == currentEventRates.forgeChance) { + modifiedRates.emplace_back("forge-chance"); + g_kv().scoped("eventscheduler")->set("forge-chance", rates.forgeChance - 100); + } + + if (rates.doubleBestiary != false && currentEventRates.doubleBestiary != false && rates.doubleBestiary == currentEventRates.doubleBestiary) { + modifiedRates.emplace_back("double-bestiary"); + g_kv().scoped("eventscheduler")->set("double-bestiary", true); + } + + if (rates.doubleBossTiary != false && currentEventRates.doubleBossTiary != false && rates.doubleBossTiary == currentEventRates.doubleBossTiary) { + modifiedRates.emplace_back("double-bosstiary"); + g_kv().scoped("eventscheduler")->set("double-bosstiary", true); + } + + if (rates.fastExercise != false && currentEventRates.fastExercise != false && rates.fastExercise == currentEventRates.fastExercise) { + modifiedRates.emplace_back("fast-exercise"); + g_kv().scoped("eventscheduler")->set("fast-exercise", true); + } + + if (rates.bosscooldown != 100 && currentEventRates.bosscooldown != 100 && rates.bosscooldown == currentEventRates.bosscooldown) { + modifiedRates.emplace_back("bosscooldown"); + g_kv().scoped("eventscheduler")->set("boss-cooldown", rates.bosscooldown - 100); } if (!modifiedRates.empty()) { diff --git a/src/game/scheduling/events_scheduler.hpp b/src/game/scheduling/events_scheduler.hpp index 4d879b05198..4206896c005 100644 --- a/src/game/scheduling/events_scheduler.hpp +++ b/src/game/scheduling/events_scheduler.hpp @@ -24,6 +24,11 @@ struct EventRates { uint32_t bosslootrate = 100; uint32_t spawnrate = 100; uint16_t skillrate = 100; + uint8_t forgeChance = 100; + uint8_t bosscooldown = 100; + bool doubleBestiary {}; + bool doubleBossTiary {}; + bool fastExercise {}; }; class EventsScheduler { diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 466a2413353..11938b554fb 100644 --- a/src/server/network/protocol/protocolgame.cpp +++ b/src/server/network/protocol/protocolgame.cpp @@ -9131,7 +9131,15 @@ void ProtocolGame::sendBosstiaryCooldownTimer() { if (!timerValue || !timerValue.has_value()) { continue; } + + auto scheduleTimerOpt = g_kv().scoped("eventscheduler")->get("boss-cooldown"); + uint8_t schedulePercentage = 0; + if (scheduleTimerOpt) { + schedulePercentage = static_cast(scheduleTimerOpt->getNumber()); + } + auto timer = timerValue->getNumber(); + timer = static_cast(timer * schedulePercentage / 100); uint64_t sendTimer = timer > 0 ? static_cast(timer) : 0; msg.add(bossRaceId); // bossRaceId msg.add(sendTimer); // Boss cooldown in seconds