From 837bb046e2e0b084fa02f33fefea379899cc3ffc Mon Sep 17 00:00:00 2001 From: Eduardo Dantas Date: Wed, 13 Nov 2024 01:52:37 -0300 Subject: [PATCH] improve: eventscheduler --- data/json/{ => eventscheduler}/README.md | 0 data/json/eventscheduler/events.json | 184 ++++++++++++++++++ data/json/eventscheduler/scripts/example.lua | 5 + data/json/scripts/boss_cooldown.lua | 0 data/json/scripts/double_bestiary.lua | 6 - data/json/scripts/double_bosstiary.lua | 6 - data/json/scripts/fast_exercise.lua | 6 - data/json/scripts/forge_time.lua | 6 - src/canary_server.cpp | 2 +- src/game/scheduling/events_scheduler.cpp | 37 +++- src/game/scheduling/events_scheduler.hpp | 5 + .../functions/core/game/game_functions.cpp | 25 +++ src/server/network/protocol/protocolgame.cpp | 8 + 13 files changed, 261 insertions(+), 29 deletions(-) rename data/json/{ => eventscheduler}/README.md (100%) create mode 100644 data/json/eventscheduler/events.json create mode 100644 data/json/eventscheduler/scripts/example.lua delete mode 100644 data/json/scripts/boss_cooldown.lua delete mode 100644 data/json/scripts/double_bestiary.lua delete mode 100644 data/json/scripts/double_bosstiary.lua delete mode 100644 data/json/scripts/fast_exercise.lua delete mode 100644 data/json/scripts/forge_time.lua 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..6f0a24ffa03 --- /dev/null +++ b/data/json/eventscheduler/scripts/example.lua @@ -0,0 +1,5 @@ +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..e106f455712 100644 --- a/src/game/scheduling/events_scheduler.cpp +++ b/src/game/scheduling/events_scheduler.cpp @@ -22,7 +22,7 @@ bool EventsScheduler::loadScheduleEventFromJson() { 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 +56,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 +93,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) { @@ -116,6 +120,31 @@ bool EventsScheduler::loadScheduleEventFromJson() { modifiedRates.emplace_back("skillrate"); } + 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("bosscooldown", rates.bosscooldown - 100); + } + if (!modifiedRates.empty()) { std::string ratesString = join(modifiedRates, ", "); g_logger().warn("{} - Events '{}' and '{}' have the same rates [{}] on the same day.", __FUNCTION__, eventName, existingEventName, ratesString); 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/lua/functions/core/game/game_functions.cpp b/src/lua/functions/core/game/game_functions.cpp index aad4b71d06c..a9074525f3f 100644 --- a/src/lua/functions/core/game/game_functions.cpp +++ b/src/lua/functions/core/game/game_functions.cpp @@ -994,3 +994,28 @@ int GameFunctions::luaGameGetAchievements(lua_State* L) { } return 1; } + +int luaGameGetOutfits(lua_State* L) { + // Game.getOutfits() + lua_createtable(L, 0, 0); + + const auto &maleOutfits = Outfits::getInstance().getOutfits(PLAYERSEX_MALE); + int index = 0; + for (const auto &outfit : maleOutfits) { + lua_createtable(L, 0, 3); + Lua::setField(L, "id", outfit->lookType); + Lua::setField(L, "name", outfit->name); + Lua::setField(L, "sex", PLAYERSEX_MALE); + lua_rawseti(L, -2, ++index); + } + + const auto &femaleOutfits = Outfits::getInstance().getOutfits(PLAYERSEX_FEMALE); + for (const auto &outfit : femaleOutfits) { + lua_createtable(L, 0, 3); + Lua::setField(L, "id", outfit->lookType); + Lua::setField(L, "name", outfit->name); + Lua::setField(L, "sex", PLAYERSEX_FEMALE); + lua_rawseti(L, -2, ++index); + } + return 1; +} diff --git a/src/server/network/protocol/protocolgame.cpp b/src/server/network/protocol/protocolgame.cpp index 466a2413353..7c6badfe9be 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("bosscooldown"); + 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