diff --git a/conf/mod-needs-system.conf.dist b/conf/mod-needs-system.conf.dist new file mode 100644 index 0000000..1e53cc3 --- /dev/null +++ b/conf/mod-needs-system.conf.dist @@ -0,0 +1,9 @@ +NeedsSystem.Enable = 1 +NeedsSystem.MaxValue = 255 +NeedsSystem.LowValue = 16 +NeedsSystem.CriticalValue = 5 +NeedsSystem.DropRate = 60 +NeedsSystem.DropValueHunger = 1 +NeedsSystem.DropValueThirst = 1 +NeedsSystem.NewPlayerHunger = 64 +NeedsSystem.NewPlayerThirst = 64 diff --git a/conf/my_custom.conf.dist b/conf/my_custom.conf.dist deleted file mode 100644 index eb614f9..0000000 --- a/conf/my_custom.conf.dist +++ /dev/null @@ -1,17 +0,0 @@ -# -# Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 -# - -[worldserver] - -######################################## -# My module configuration -######################################## -# -# MyModule.Enable -# Description: Enable my module and print "Hello World" message at server start -# Default: 0 - Disabled -# 1 - Enabled -# - -MyModule.Enable = 1 diff --git a/data/sql/db-characters/base/needs.sql b/data/sql/db-characters/base/needs.sql new file mode 100644 index 0000000..e03a559 --- /dev/null +++ b/data/sql/db-characters/base/needs.sql @@ -0,0 +1,6 @@ +CREATE TABLE `acore_characters`.`mod_needs` ( + `guid` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `hunger` TINYINT(3) UNSIGNED NOT NULL DEFAULT '64', + `thirst` TINYINT(3) UNSIGNED NOT NULL DEFAULT '64', + PRIMARY KEY (`guid`) USING BTREE +) diff --git a/data/sql/db-world/base/needs_commands.sql b/data/sql/db-world/base/needs_commands.sql new file mode 100644 index 0000000..69e46ba --- /dev/null +++ b/data/sql/db-world/base/needs_commands.sql @@ -0,0 +1,6 @@ +DELETE FROM `command` WHERE `name` IN ('needs', 'needs set', 'needs view'); + +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('needs', 0, 'Syntax: .needs [subcommand]\nType .help needs to see a list of subcommands\nor .help needs [subcommand] to see info on the subcommand.'), +('needs set', 2, 'Syntax: .needs set [hunger] [thirst]\nSet your current hunger and thirst levels.'), +('needs view', 0, 'Syntax: .needs view\nView your current needs levels.'); diff --git a/data/sql/db-world/base/needs_sustenance.sql b/data/sql/db-world/base/needs_sustenance.sql new file mode 100644 index 0000000..f2b6437 --- /dev/null +++ b/data/sql/db-world/base/needs_sustenance.sql @@ -0,0 +1,7 @@ +CREATE TABLE `mod_sustenance` ( + `guid` INT(10) UNSIGNED NOT NULL DEFAULT '0' COMMENT 'Item ID', + `hunger` SMALLINT(6) NULL DEFAULT NULL, + `thirst` SMALLINT(6) NULL DEFAULT NULL, + PRIMARY KEY (`guid`) USING BTREE +) + diff --git a/data/sql/db-world/base/skeleton_module_acore_string.sql b/data/sql/db-world/base/skeleton_module_acore_string.sql deleted file mode 100644 index 4ac0cce..0000000 --- a/data/sql/db-world/base/skeleton_module_acore_string.sql +++ /dev/null @@ -1,4 +0,0 @@ -SET @ENTRY:=35410; -DELETE FROM `acore_string` WHERE `entry`=@ENTRY; -INSERT INTO `acore_string` (`entry`, `content_default`, `locale_koKR`, `locale_frFR`, `locale_deDE`, `locale_zhCN`, `locale_zhTW`, `locale_esES`, `locale_esMX`, `locale_ruRU`) VALUES -(@ENTRY, 'Hello World from Skeleton-Module!', '', '', '', '', '', '¡Hola Mundo desde Skeleton-Module!', '¡Hola Mundo desde Skeleton-Module!', ''); diff --git a/src/MP_loader.cpp b/src/MP_loader.cpp deleted file mode 100644 index a640a26..0000000 --- a/src/MP_loader.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 - */ - -// From SC -void AddMyPlayerScripts(); - -// Add all -// cf. the naming convention https://github.com/azerothcore/azerothcore-wotlk/blob/master/doc/changelog/master.md#how-to-upgrade-4 -// additionally replace all '-' in the module folder name with '_' here -void Addskeleton_moduleScripts() -{ - AddMyPlayerScripts(); -} - diff --git a/src/MyPlayer.cpp b/src/MyPlayer.cpp deleted file mode 100644 index e23ed59..0000000 --- a/src/MyPlayer.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2016+ AzerothCore , released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3 - */ - -#include "ScriptMgr.h" -#include "Player.h" -#include "Config.h" -#include "Chat.h" - -enum MyPlayerAcoreString -{ - HELLO_WORLD = 35410 -}; - -// Add player scripts -class MyPlayer : public PlayerScript -{ -public: - MyPlayer() : PlayerScript("MyPlayer") { } - - void OnLogin(Player* player) override - { - if (sConfigMgr->GetOption("MyModule.Enable", false)) - { - ChatHandler(player->GetSession()).PSendSysMessage(HELLO_WORLD); - } - } -}; - -// Add all scripts in one -void AddMyPlayerScripts() -{ - new MyPlayer(); -} diff --git a/src/NeedsSystem.cpp b/src/NeedsSystem.cpp new file mode 100644 index 0000000..c414ee7 --- /dev/null +++ b/src/NeedsSystem.cpp @@ -0,0 +1,330 @@ +#include "ScriptMgr.h" +#include "SpellScript.h" +#include "Player.h" +#include "Config.h" +#include "Chat.h" +#include "Object.h" +#include "DataMap.h" + +#include + +using namespace Acore::ChatCommands; + +bool NeedsEnabled = true; +uint8 NeedsMaxValue = 255; +uint8 NeedsLowValue = 16; +uint8 NeedsCriticalValue = 5; +uint32 NeedsDropRate = 60; +uint8 NeedsDropValueHunger = 1; +uint8 NeedsDropValueThirst = 1; +uint8 NeedsNewPlayerHunger = 64; +uint8 NeedsNewPlayerThirst = 64; + +#define NEEDS_TICK (NeedsDropRate * 1000) + +enum +{ + SPELL_MILDLY_HUNGRY = 1000000, + SPELL_MILDLY_THIRSTY, + SPELL_HUNGRY, + SPELL_THIRSTY, + SPELL_STARVING, + SPELL_PARCHED, +}; + +struct SustenanceData +{ + SustenanceData() {} + SustenanceData(int16 hunger, int16 thirst): hunger(hunger), thirst(thirst) {} + int16 hunger = 0; + int16 thirst = 0; +}; + +std::unordered_map Sustenance; + +class NeedsConf: public WorldScript +{ +public: + NeedsConf() : WorldScript("NeedsConf") {} + + void OnBeforeConfigLoad(bool /* reload */) override + { + NeedsEnabled = sConfigMgr->GetOption("NeedsSystem.Enabled", true); + NeedsMaxValue = sConfigMgr->GetOption("NeedsSystem.MaxValue", 255); + NeedsLowValue = sConfigMgr->GetOption("NeedsSystem.LowValue", 16); + NeedsCriticalValue = sConfigMgr->GetOption("NeedsSystem.CriticalValue", 5); + NeedsDropRate = sConfigMgr->GetOption("NeedsSystem.DropRate", 60); + NeedsDropValueHunger = sConfigMgr->GetOption("NeedsSystem.DropValueHunger", 1); + NeedsDropValueThirst = sConfigMgr->GetOption("NeedsSystem.DropValueThirst", 1); + NeedsNewPlayerHunger = sConfigMgr->GetOption("NeedsSystem.NewPlayerHunger", 64); + NeedsNewPlayerThirst = sConfigMgr->GetOption("NeedsSystem.NewPlayerThirst", 64); + } + + void OnStartup() override + { + LOG_INFO("server.loading", "Loading sustenance..."); + uint32 msTime = getMSTime(); + QueryResult result = WorldDatabase.Query("SELECT `guid`,`hunger`,`thirst` FROM `mod_sustenance`"); + + if (!result) + { + LOG_ERROR("server.loading", "MySQL table 'mod_sustenance' is missing from the world DB or is empty!"); + NeedsEnabled = false; + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + Sustenance[fields[0].Get()] = SustenanceData(fields[1].Get(), fields[2].Get()); + ++count; + } + while (result->NextRow()); + + LOG_INFO("server.loading", "Loaded {} sustenance in {} ms", count, GetMSTimeDiffToNow(msTime)); + } +}; + +class PlayerNeeds: public DataMap::Base +{ +public: + PlayerNeeds() {} + PlayerNeeds(uint8 hunger, uint8 thirst): hunger(hunger), thirst(thirst) + { + nextTick = getMSTime() + NEEDS_TICK; + } + uint8 hunger = 64; + uint8 thirst = 64; + uint32 nextTick = 0; +}; + +class NeedsSystem : public PlayerScript +{ +public: + NeedsSystem() : PlayerScript("NeedsSystemScript") { } + + void OnLogin(Player* player) override + { + QueryResult result = CharacterDatabase.Query("SELECT `hunger`,`thirst` FROM `mod_needs` WHERE `guid`='{}'", player->GetGUID().GetCounter()); + + if (!result) + { + PlayerNeeds *defaultNeeds = player->CustomData.GetDefault("Needs"); + defaultNeeds->hunger = NeedsNewPlayerHunger; + defaultNeeds->thirst = NeedsNewPlayerThirst; + defaultNeeds->nextTick = getMSTime() + NEEDS_TICK; + } + else + { + Field* fields = result->Fetch(); + player->CustomData.Set("Needs", new PlayerNeeds(fields[0].Get(), fields[1].Get())); + } + + } + + void SaveData(Player* player) + { + if (PlayerNeeds* data = player->CustomData.Get("Needs")) + { + CharacterDatabase.DirectExecute("REPLACE INTO `mod_needs` (`guid`,`hunger`,`thirst`) VALUES ('{}', '{}', '{}');", player->GetGUID().GetCounter(), data->hunger, data->thirst); + } + } + + void OnLogout(Player* player) override + { + SaveData(player); + } + + static void HandleBuffs(Player* player, PlayerNeeds *needs) + { + player->RemoveOwnedAura(SPELL_MILDLY_HUNGRY); + player->RemoveOwnedAura(SPELL_MILDLY_THIRSTY); + player->RemoveOwnedAura(SPELL_HUNGRY); + player->RemoveOwnedAura(SPELL_THIRSTY); + player->RemoveOwnedAura(SPELL_STARVING); + player->RemoveOwnedAura(SPELL_PARCHED); + + if (!player->IsAlive()) + { + return; + } + + if (needs->hunger == 0) + { + player->CastSpell(player, SPELL_STARVING, true); + } + else if (needs->hunger <= NeedsCriticalValue) + { + player->CastSpell(player, SPELL_HUNGRY, true); + } + else if (needs->hunger <= NeedsLowValue) + { + player->CastSpell(player, SPELL_MILDLY_HUNGRY, true); + } + + if (needs->thirst == 0) + { + player->CastSpell(player, SPELL_PARCHED, true); + } + else if (needs->thirst <= NeedsCriticalValue) + { + player->CastSpell(player, SPELL_THIRSTY, true); + } + else if (needs->thirst <= NeedsLowValue) + { + player->CastSpell(player, SPELL_MILDLY_THIRSTY, true); + } + } + + void OnSpellCast(Player* player, Spell* spell, bool /*skipCheck*/) override + { + if (!NeedsEnabled || !spell->m_CastItem) + { + return; + } + + uint32 itemId = spell->m_CastItem->GetTemplate()->ItemId; + + if (Sustenance.find(itemId) != Sustenance.end()) + { + SustenanceData data = Sustenance[itemId]; + PlayerNeeds *needs = player->CustomData.Get("Needs"); + + int32 hungerVal = needs->hunger; + int32 thirstVal = needs->thirst; + + if (hungerVal + data.hunger < 0) + needs->hunger = 0; + else if (hungerVal + data.hunger > NeedsMaxValue) + needs->hunger = NeedsMaxValue; + else + needs->hunger += data.hunger; + + if (thirstVal + data.thirst < 0) + needs->thirst = 0; + else if (thirstVal + data.thirst > NeedsMaxValue) + needs->thirst = NeedsMaxValue; + else + needs->thirst += data.thirst; + + ChatHandler(player->GetSession()).PSendSysMessage("You feel invirogated!\nCurrent needs:\nHunger: %d\nThirst: %d", needs->hunger, needs->thirst); + + HandleBuffs(player, needs); + } + } + + void OnBeforeUpdate(Player* player, uint32 /* p_time */) override + { + if (!NeedsEnabled || !player->IsInWorld() || !player->IsAlive()) + { + return; + } + + PlayerNeeds *needs = player->CustomData.Get("Needs"); + + if (needs->nextTick < getMSTime()) + { + needs->nextTick = getMSTime() + NEEDS_TICK; + + int32 hungerVal = needs->hunger; + int32 thirstVal = needs->thirst; + + if (hungerVal - NeedsDropValueHunger < 0) + needs->hunger = 0; + else + needs->hunger -= NeedsDropValueHunger; + + if (thirstVal - NeedsDropValueThirst < 0) + needs->thirst = 0; + else + needs->thirst -= NeedsDropValueThirst; + + HandleBuffs(player, needs); + } + } + + void OnPlayerJustDied(Player* player) override + { + if (!NeedsEnabled || !player->IsInWorld()) + { + return; + } + + PlayerNeeds *needs = player->CustomData.Get("Needs"); + needs->hunger = NeedsNewPlayerHunger; + needs->thirst = NeedsNewPlayerThirst; + HandleBuffs(player, needs); + } +}; + +class NeedsCommand : public CommandScript +{ +public: + NeedsCommand() : CommandScript("NeedsCommand") {} + + ChatCommandTable GetCommands() const override + { + static ChatCommandTable NeedsCommandTable = + { + { "view", HandleViewCommand, SEC_PLAYER, Console::No }, + { "set", HandleSetCommand, SEC_GAMEMASTER, Console::No }, + }; + + static ChatCommandTable NeedsBaseTable = + { + { "needs", NeedsCommandTable } + }; + + return NeedsBaseTable; + } + + static bool HandleViewCommand(ChatHandler* handler) + { + if (!NeedsEnabled) + { + handler->PSendSysMessage("Needs system is disabled!"); + handler->SetSentErrorMessage(true); + return false; + } + + Player* player = handler->GetSession()->GetPlayer(); + + if (!player) + return false; + + PlayerNeeds* needs = player->CustomData.Get("Needs"); + ChatHandler(player->GetSession()).PSendSysMessage("Current needs:\nHunger: %d\nThirst: %d", needs->hunger, needs->thirst); + return true; + } + + static bool HandleSetCommand(ChatHandler* handler, uint8 hunger, uint8 thirst) + { + if (!NeedsEnabled) + { + handler->PSendSysMessage("Needs system is disabled!"); + handler->SetSentErrorMessage(true); + return false; + } + + Player* player = handler->GetSession()->GetPlayer(); + + if (!player) + return false; + + PlayerNeeds* needs = player->CustomData.Get("Needs"); + needs->hunger = hunger; + needs->thirst = thirst; + NeedsSystem::HandleBuffs(player, needs); + ChatHandler(player->GetSession()).PSendSysMessage("Need levels set!"); + return true; + } +}; + +// Add all scripts in one +void AddNeedsSystemScripts() +{ + new NeedsConf(); + new NeedsSystem(); + new NeedsCommand(); +} diff --git a/src/Needs_loader.cpp b/src/Needs_loader.cpp new file mode 100644 index 0000000..0e26b3d --- /dev/null +++ b/src/Needs_loader.cpp @@ -0,0 +1,7 @@ +void AddNeedsSystemScripts(); + +void Addmod_needs_systemScripts() +{ + AddNeedsSystemScripts(); +} +