From e4112bfe6c26ae40be5b85d8c0ce8c5a47aa22b4 Mon Sep 17 00:00:00 2001 From: kokekanon <114332266+kokekanon@users.noreply.github.com> Date: Tue, 16 Apr 2024 11:49:28 -0400 Subject: [PATCH] feat: [game_outfit] Shader (#9) --- data/XML/shaders.xml | 9 ++ data/talkactions/scripts/reload.lua | 3 + schema.sql | 9 ++ src/const.h | 1 + src/creature.h | 1 + src/enums.h | 2 + src/game.cpp | 25 ++++- src/game.h | 5 +- src/iologindata.cpp | 43 +++++++- src/luagame.cpp | 16 +++ src/luaplayer.cpp | 92 ++++++++++++++++ src/luascript.cpp | 10 ++ src/luascript.h | 2 + src/player.cpp | 164 ++++++++++++++++++++++++++++ src/player.h | 19 +++- src/protocolgame.cpp | 32 +++++- src/shaders.cpp | 52 +++++++++ src/shaders.h | 30 +++++ vc17/theforgottenserver.vcxproj | 2 + 19 files changed, 508 insertions(+), 9 deletions(-) create mode 100644 data/XML/shaders.xml create mode 100644 src/shaders.cpp create mode 100644 src/shaders.h diff --git a/data/XML/shaders.xml b/data/XML/shaders.xml new file mode 100644 index 0000000..38dbb14 --- /dev/null +++ b/data/XML/shaders.xml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/data/talkactions/scripts/reload.lua b/data/talkactions/scripts/reload.lua index 7c79e67..18af896 100644 --- a/data/talkactions/scripts/reload.lua +++ b/data/talkactions/scripts/reload.lua @@ -38,6 +38,9 @@ local reloadTypes = { ["aura"] = RELOAD_TYPE_AURA, ["auras"] = RELOAD_TYPE_AURA, + ["shader"] = RELOAD_TYPE_SHADERS, + ["shaders"] = RELOAD_TYPE_SHADERS, + ["move"] = RELOAD_TYPE_MOVEMENTS, ["movement"] = RELOAD_TYPE_MOVEMENTS, ["movements"] = RELOAD_TYPE_MOVEMENTS, diff --git a/schema.sql b/schema.sql index 2036084..a771296 100644 --- a/schema.sql +++ b/schema.sql @@ -36,6 +36,8 @@ CREATE TABLE IF NOT EXISTS `players` ( `randomizeaura` tinyint NOT NULL DEFAULT '0', `currenteffect` smallint UNSIGNED NOT NULL DEFAULT '0', `randomizeeffect` tinyint NOT NULL DEFAULT '0', + `currentshader` smallint UNSIGNED NOT NULL DEFAULT '0', + `randomizeshader` tinyint NOT NULL DEFAULT '0'; `direction` tinyint unsigned NOT NULL DEFAULT '2', `maglevel` int NOT NULL DEFAULT '0', `mana` int NOT NULL DEFAULT '0', @@ -385,6 +387,13 @@ CREATE TABLE IF NOT EXISTS `player_auras` ( FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; +CREATE TABLE IF NOT EXISTS `player_shaders` ( + `player_id` int(11) NOT NULL DEFAULT 0, + `shader_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 + PRIMARY KEY (`player_id`, `shader_id`), + FOREIGN KEY (`player_id`) REFERENCES `players`(`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARACTER SET=utf8; + CREATE TABLE IF NOT EXISTS `server_config` ( `config` varchar(50) NOT NULL, `value` varchar(256) NOT NULL DEFAULT '', diff --git a/src/const.h b/src/const.h index a2497da..1ffade7 100644 --- a/src/const.h +++ b/src/const.h @@ -480,6 +480,7 @@ enum ReloadTypes_t : uint8_t RELOAD_TYPE_WINGS, RELOAD_TYPE_AURAS, RELOAD_TYPE_EFFECTS, + RELOAD_TYPE_SHADERS, RELOAD_TYPE_MOVEMENTS, RELOAD_TYPE_NPCS, RELOAD_TYPE_QUESTS, diff --git a/src/creature.h b/src/creature.h index d960811..f41cf77 100644 --- a/src/creature.h +++ b/src/creature.h @@ -415,6 +415,7 @@ class Creature : virtual public Thing uint16_t currentWing; uint16_t currentAura; uint16_t currentEffect; + uint16_t currentShader; Position lastPosition; LightInfo internalLight; diff --git a/src/enums.h b/src/enums.h index d15be46..ea2f178 100644 --- a/src/enums.h +++ b/src/enums.h @@ -506,6 +506,8 @@ struct Outfit_t uint16_t lookWing = 0; uint16_t lookAura = 0; uint16_t lookEffect = 0; + uint16_t lookShader = 0; + uint8_t lookHead = 0; uint8_t lookBody = 0; uint8_t lookLegs = 0; diff --git a/src/game.cpp b/src/game.cpp index 529a63a..bea4da6 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -79,6 +79,7 @@ void Game::setGameState(GameState_t newState) wings.loadFromXml(); auras.loadFromXml(); effects.loadFromXml(); + shaders.loadFromXml(); raids.loadFromXml(); raids.startup(); @@ -3526,7 +3527,29 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit, bool randomize player->detachEffectById(player->getCurrentAura()); player->wasAuraed = false; } - // @ + // @ + /// shaders + if (outfit.lookShader != 0) { + Shader* shader = shaders.getShaderByID(outfit.lookShader); + if (!shader) { + return; + } + + if (!player->hasShader(shader)) { + return; + } + + player->setCurrentShader(shader->id); + player->sendShader(player, shader->name); + + + } else { + if (player->isShadered()) { + player->disshader(); + } + player->sendShader(player, "Outfit - Default"); + player->wasShadered = false; + } if (player->canWear(outfit.lookType, outfit.lookAddons)) { player->defaultOutfit = outfit; diff --git a/src/game.h b/src/game.h index 1a95c2d..87984fb 100644 --- a/src/game.h +++ b/src/game.h @@ -14,6 +14,7 @@ #include "wings.h" #include "auras.h" #include "effects.h" +#include "shaders.h" #include "npc.h" #include "player.h" #include "position.h" @@ -490,8 +491,10 @@ class Game Raids raids; Mounts mounts; Wings wings; - Effects effects; Auras auras; + Effects effects; + Shaders shaders; + std::forward_list toDecayItems; diff --git a/src/iologindata.cpp b/src/iologindata.cpp index f1d5844..35fa8d4 100644 --- a/src/iologindata.cpp +++ b/src/iologindata.cpp @@ -229,7 +229,7 @@ bool IOLoginData::loadPlayerById(Player* player, uint32_t id) return loadPlayer( player, db.storeQuery(fmt::format( - "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `currentmount`, `randomizemount`, `currentwing`, `randomizewing`,`currenteffect`, `randomizeeffect`,`currentaura`, `randomizeaura`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `id` = {:d}", + "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `currentmount`, `randomizemount`, `currentwing`, `randomizewing`,`currenteffect`, `randomizeeffect`,`currentaura`, `randomizeaura`,`currentshader`, `randomizeshader`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `id` = {:d}", id))); } @@ -239,7 +239,7 @@ bool IOLoginData::loadPlayerByName(Player* player, std::string_view name) return loadPlayer( player, db.storeQuery(fmt::format( - "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `currentmount`, `randomizemount`, `currentwing`, `randomizewing`,`currenteffect`, `randomizeeffect`,`currentaura`, `randomizeaura`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `name` = {:s}", + "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `currentmount`, `randomizemount`, `currentwing`, `randomizewing`,`currenteffect`, `randomizeeffect`,`currentaura`, `randomizeaura`,`currentshader`, `randomizeshader`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `name` = {:s}", db.escapeString(name)))); } @@ -364,11 +364,13 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) player->currentWing = result->getNumber("currentwing"); player->currentEffect = result->getNumber("currenteffect"); player->currentAura = result->getNumber("currentaura"); + player->currentShader = result->getNumber("currentshader"); player->direction = static_cast(result->getNumber("direction")); player->randomizeMount = result->getNumber("randomizemount") != 0; player->randomizeWing = result->getNumber("randomizewing") != 0; player->randomizeAura = result->getNumber("randomizeaura") != 0; player->randomizeEffect = result->getNumber("randomizeeffect") != 0; + player->randomizeShader = result->getNumber("randomizeshader") != 0; if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { const time_t skullSeconds = result->getNumber("skulltime") - time(nullptr); if (skullSeconds > 0) { @@ -647,6 +649,24 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) } // @-- + // load Shaders + if ((result = db.storeQuery( + fmt::format("SELECT `shader_id` FROM `player_shaders` WHERE `player_id` = {:d}", player->getGUID())))) { + do { + player->tameShader(result->getNumber("shader_id")); + } while (result->next()); + } + + auto currentShaderID = player->getCurrentShader(); + + if (currentShaderID && currentShaderID != 0) { + Shader* shader = g_game.shaders.getShaderByID(currentShaderID); + + if (shader && shader->name != "Outfit - Default") { + player->setShader(shader->name); + } + } + player->updateBaseSpeed(); player->updateInventoryWeight(); player->updateItemsLight(true); @@ -760,6 +780,8 @@ bool IOLoginData::savePlayer(Player* player) query << "`randomizeeffect` = " << player->randomizeEffect << ","; query << "`currentaura` = " << static_cast(player->currentAura) << ','; query << "`randomizeaura` = " << player->randomizeAura << ","; + query << "`currentshader` = " << static_cast(player->currentShader) << ','; + query << "`randomizeshader` = " << player->randomizeShader << ","; query << "`maglevel` = " << player->magLevel << ','; query << "`mana` = " << player->mana << ','; query << "`manamax` = " << player->manaMax << ','; @@ -1029,6 +1051,23 @@ bool IOLoginData::savePlayer(Player* player) } // --@ + // save shaders + if (!db.executeQuery(fmt::format("DELETE FROM `player_shaders` WHERE `player_id` = {:d}", player->getGUID()))) { + return false; + } + + DBInsert shaderQuery("INSERT INTO `player_shaders` (`player_id`, `shader_id`) VALUES "); + + for (const auto& it : player->shaders) { + if (!shaderQuery.addRow(fmt::format("{:d}, {:d}", player->getGUID(), it))) { + return false; + } + } + + if (!shaderQuery.execute()) { + return false; + } + // End the transaction return transaction.commit(); } diff --git a/src/luagame.cpp b/src/luagame.cpp index e2f9165..b980214 100644 --- a/src/luagame.cpp +++ b/src/luagame.cpp @@ -268,6 +268,21 @@ int luaGameGetWings(lua_State* L) return 1; } +// shaders +int luaGameGetShaders(lua_State* L) +{ + // Game.getShaders() + const auto& shaders = g_game.shaders.getShaders(); + lua_createtable(L, shaders.size(), 0); + + int index = 0; + for (const auto& shader : shaders) { + pushShader(L, &shader); + lua_rawseti(L, -2, ++index); + } + + return 1; +} int luaGameGetAuras(lua_State* L) { @@ -738,6 +753,7 @@ void LuaScriptInterface::registerGame() registerMethod("Game", "getWings", luaGameGetWings); registerMethod("Game", "getEffects", luaGameGetEffects); registerMethod("Game", "getAuras", luaGameGetAuras); + registerMethod("Game", "getShaders", luaGameGetShaders); registerMethod("Game", "getGameState", luaGameGetGameState); registerMethod("Game", "setGameState", luaGameSetGameState); diff --git a/src/luaplayer.cpp b/src/luaplayer.cpp index 2f5ed24..19cc4a5 100644 --- a/src/luaplayer.cpp +++ b/src/luaplayer.cpp @@ -14,6 +14,7 @@ #include "wings.h" #include "auras.h" #include "effects.h" +#include "shaders.h" extern Chat* g_chat; extern Game g_game; @@ -1869,6 +1870,93 @@ int luaPlayerToggleEffect(lua_State* L) } // @ +int luaPlayerAddShader(lua_State* L) +{ + // player:addShader(shaderId or shaderName) + Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + uint16_t shaderId; + if (isInteger(L, 2)) { + shaderId = getInteger(L, 2); + } else { + Shader* shader = g_game.shaders.getShaderByName(getString(L, 2)); + if (!shader) { + lua_pushnil(L); + return 1; + } + shaderId = shader->id; + } + + pushBoolean(L, player->tameShader(shaderId)); + return 1; +} + +int luaPlayerRemoveShader(lua_State* L) +{ + // player:removeShader(shaderId or shaderName) + Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + uint16_t shaderId; + if (isInteger(L, 2)) { + shaderId = getInteger(L, 2); + } else { + Shader* shader = g_game.shaders.getShaderByName(getString(L, 2)); + if (!shader) { + lua_pushnil(L); + return 1; + } + shaderId = shader->id; + } + + pushBoolean(L, player->untameShader(shaderId)); + return 1; +} + +int luaPlayerHasShader(lua_State* L) +{ + // player:hasShader(shaderId or shaderName) + const Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + Shader* shader = nullptr; + if (isInteger(L, 2)) { + shader = g_game.shaders.getShaderByID(getInteger(L, 2)); + } else { + shader = g_game.shaders.getShaderByName(getString(L, 2)); + } + + if (shader) { + pushBoolean(L, player->hasShader(shader)); + } else { + lua_pushnil(L); + } + return 1; +} + +int luaPlayerToggleShader(lua_State* L) +{ + // player:toggleShader(shader) + Player* player = getUserdata(L, 1); + if (!player) { + lua_pushnil(L); + return 1; + } + + bool shader = getBoolean(L, 2); + pushBoolean(L, player->toggleShader(shader)); + return 1; +} int luaPlayerGetPremiumEndsAt(lua_State* L) { @@ -2661,6 +2749,10 @@ void LuaScriptInterface::registerPlayer() registerMethod("Player", "hasEffect", luaPlayerHasEffect); registerMethod("Player", "toggleEffect", luaPlayerToggleEffect); // @ + registerMethod("Player", "addShader", luaPlayerAddShader); + registerMethod("Player", "removeShader", luaPlayerRemoveShader); + registerMethod("Player", "hasShader", luaPlayerHasShader); + registerMethod("Player", "toggleShader", luaPlayerToggleShader); registerMethod("Player", "getPremiumEndsAt", luaPlayerGetPremiumEndsAt); registerMethod("Player", "setPremiumEndsAt", luaPlayerSetPremiumEndsAt); diff --git a/src/luascript.cpp b/src/luascript.cpp index 62c349d..fecbbc7 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -1007,6 +1007,7 @@ void Lua::pushOutfit(lua_State* L, const Outfit_t& outfit) setField(L, "lookWing", outfit.lookWing); setField(L, "lookAura", outfit.lookAura); setField(L, "lookEffect", outfit.lookEffect); + setField(L, "lookShader", outfit.lookShader); setField(L, "lookHead", outfit.lookHead); setField(L, "lookBody", outfit.lookBody); setField(L, "lookLegs", outfit.lookLegs); @@ -1043,6 +1044,14 @@ void Lua::pushWing(lua_State* L, const Wing* wing) setField(L, "id", wing->id); setField(L, "premium", wing->premium); } +void Lua::pushShader(lua_State* L, const Shader* shader) +{ + lua_createtable(L, 0, 5); + setField(L, "name", shader->name); + setField(L, "id", shader->id); + setField(L, "premium", shader->premium); +} + void Lua::pushAura(lua_State* L, const Aura* aura) { lua_createtable(L, 0, 5); @@ -1977,6 +1986,7 @@ void LuaScriptInterface::registerFunctions() registerEnum(RELOAD_TYPE_ITEMS); registerEnum(RELOAD_TYPE_MONSTERS); registerEnum(RELOAD_TYPE_MOUNTS); + registerEnum(RELOAD_TYPE_SHADERS); registerEnum(RELOAD_TYPE_WINGS); registerEnum(RELOAD_TYPE_AURAS); registerEnum(RELOAD_TYPE_EFFECTS); diff --git a/src/luascript.h b/src/luascript.h index 43a6759..7682631 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -119,6 +119,7 @@ struct Wing; struct Aura; struct Effect; struct Outfit; +struct Shader; struct Position; using Combat_ptr = std::shared_ptr; @@ -782,6 +783,7 @@ void pushMount(lua_State* L, const Mount* mount); void pushWing(lua_State* L, const Wing* wing); void pushAura(lua_State* L, const Aura* aura); void pushEffect(lua_State* L, const Effect* effect); +void pushShader(lua_State* L, const Shader* shader); void pushLoot(lua_State* L, const std::vector& lootList); void pushReflect(lua_State* L, const Reflect& reflect); diff --git a/src/player.cpp b/src/player.cpp index be3d3bd..cb63d16 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -4599,6 +4599,170 @@ bool Player::hasEffects() const } void Player::diseffect() { defaultOutfit.lookEffect = 0; } + +uint16_t Player::getRandomShader() const +{ + std::vector shadersId; + for (const Shader& shader : g_game.shaders.getShaders()) { + if (hasShader(&shader)) { + shadersId.push_back(shader.id); + } + } + + return shadersId[uniform_random(0, shadersId.size() - 1)]; +} + + +uint16_t Player::getCurrentShader() const { return currentShader; } + + +void Player::setCurrentShader(uint16_t shaderId) { currentShader = shaderId; } + +bool Player::toggleShader(bool shader) +{ + if ((OTSYS_TIME() - lastToggleShader) < 3000 && !wasShadered) { + sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED); + return false; + } + + if (shader) { + if (isShadered()) { + return false; + } + + if (!group->access && tile->hasFlag(TILESTATE_PROTECTIONZONE)) { + sendCancelMessage(RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE); + return false; + } + + const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(defaultOutfit.lookType); + if (!playerOutfit) { + return false; + } + + uint16_t currentShaderId = getCurrentShader(); + if (currentShaderId == 0) { + sendOutfitWindow(); + return false; + } + + Shader* currentShader = g_game.shaders.getShaderByID(currentShaderId); + if (!currentShader) { + return false; + } + + if (!hasShader(currentShader)) { + setCurrentShader(0); + sendOutfitWindow(); + return false; + } + + if (currentShader->premium && !isPremium()) { + sendCancelMessage(RETURNVALUE_YOUNEEDPREMIUMACCOUNT); + return false; + } + + if (hasCondition(CONDITION_OUTFIT)) { + sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return false; + } + + defaultOutfit.lookShader = currentShader->id; + + } else { + if (!isShadered()) { + return false; + } + + disshader(); + } + + g_game.internalCreatureChangeOutfit(this, defaultOutfit); + lastToggleShader = OTSYS_TIME(); + return true; +} + +bool Player::tameShader(uint16_t shaderId) +{ + if (!g_game.shaders.getShaderByID(shaderId)) { + return false; + } + + Shader* shader = g_game.shaders.getShaderByID(shaderId); + if (hasShader(shader)) { + return false; + } + + shaders.insert(shaderId); + return true; +} + +bool Player::untameShader(uint16_t shaderId) +{ + if (!g_game.shaders.getShaderByID(shaderId)) { + return false; + } + + Shader* shader = g_game.shaders.getShaderByID(shaderId); + if (!hasShader(shader)) { + return false; + } + + shaders.erase(shaderId); + + if (getCurrentShader() == shaderId) { + if (isShadered()) { + disshader(); + g_game.internalCreatureChangeOutfit(this, defaultOutfit); + } + + setCurrentShader(0); + } + + return true; +} + +bool Player::hasShader(const Shader* shader) const +{ + if (isAccessPlayer()) { + return true; + } + + if (shader->premium && !isPremium()) { + return false; + } + + return shaders.find(shader->id) != shaders.end(); +} + +bool Player::hasShaders() const +{ + for (const Shader& shader : g_game.shaders.getShaders()) { + if (hasShader(&shader)) { + return true; + } + } + return false; +} + +void Player::disshader() { defaultOutfit.lookShader = 0; } + +std::string Player::getCurrentShader_NAME() const +{ + + uint16_t currentShaderId = getCurrentShader(); + + Shader* currentShader = g_game.shaders.getShaderByID(static_cast(currentShaderId)); + + if (currentShader != nullptr) { + + return currentShader->name; + } else { + + return "Outfit - Default"; + } +} + /*bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries) { if (tries == 0 || skill == SKILL_LEVEL) { diff --git a/src/player.h b/src/player.h index 79377d2..27c18ef 100644 --- a/src/player.h +++ b/src/player.h @@ -162,7 +162,20 @@ class Player final : public Creature, public Cylinder bool hasEffect(const Effect* effect) const; bool hasEffects() const; void diseffect(); - // -- @ + // -- @ + // -- @ Shader + uint16_t getRandomShader() const; + uint16_t getCurrentShader() const; + void setCurrentShader(uint16_t shaderId); + bool isShadered() const { return defaultOutfit.lookShader != 0; } + bool toggleShader(bool shader); + bool tameShader(uint16_t shaderId); + bool untameShader(uint16_t shaderId); + bool hasShader(const Shader* shader) const; + bool hasShaders() const; + void disshader(); + std::string getCurrentShader_NAME() const; + // -- @ void sendFYIBox(std::string_view message) { if (client) { @@ -1109,6 +1122,7 @@ class Player final : public Creature, public Cylinder std::unordered_set wings; std::unordered_set auras; std::unordered_set effects; + std::unordered_set shaders; GuildWarVector guildWarVector; @@ -1144,6 +1158,7 @@ class Player final : public Creature, public Cylinder int64_t lastToggleWing = 0; int64_t lastToggleEffect = 0; int64_t lastToggleAura = 0; + int64_t lastToggleShader = 0; int64_t lastPing; int64_t lastPong; @@ -1217,6 +1232,7 @@ class Player final : public Creature, public Cylinder bool wasWinged = false; bool wasAuraed = false; bool wasEffected = false; + bool wasShadered = false; bool pzLocked = false; bool isConnecting = false; @@ -1225,6 +1241,7 @@ class Player final : public Creature, public Cylinder bool randomizeWing = false; bool randomizeAura = false; bool randomizeEffect = false; + bool randomizeShader = false; bool inventoryAbilities[CONST_SLOT_LAST + 1] = {}; diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index de58308..4bd3435 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1073,6 +1073,13 @@ void ProtocolGame::parseSetOutfit(NetworkMessage& msg) newOutfit.lookWing = isOTC ? msg.get() : 0; newOutfit.lookAura = isOTC ? msg.get() : 0; newOutfit.lookEffect = isOTC ? msg.get() : 0; + + std::string_view shaderName = isOTC ? msg.getString() : ""; + Shader* shader = nullptr; + if (!shaderName.empty()) { + shader = g_game.shaders.getShaderByName(shaderName); + newOutfit.lookShader = shader ? shader->id : 0; + } g_dispatcher.addTask([=, playerID = player->getID()]() { g_game.playerChangeOutfit(playerID, newOutfit); }); } @@ -2401,7 +2408,10 @@ void ProtocolGame::sendOutfitWindow() if (currentEffect) { currentOutfit.lookEffect = currentEffect->id; } - + Shader* currentShader = g_game.shaders.getShaderByID(player->getCurrentShader()); + if (currentShader) { + currentOutfit.lookShader = currentShader->id; + } /*bool mounted; if (player->wasMounted) { mounted = currentOutfit.lookMount != 0; @@ -2464,7 +2474,7 @@ void ProtocolGame::sendOutfitWindow() msg.addString(wing->name); } - // auras + // auras std::vector auras; for (const Aura& aura : g_game.auras.getAuras()) { if (player->hasAura(&aura)) { @@ -2478,8 +2488,7 @@ void ProtocolGame::sendOutfitWindow() msg.addString(aura->name); } - - // effects + // effects std::vector effects; for (const Effect& effect : g_game.effects.getEffects()) { if (player->hasEffect(&effect)) { @@ -2492,7 +2501,19 @@ void ProtocolGame::sendOutfitWindow() msg.add(effect->id); msg.addString(effect->name); } + // shader + std::vector shaders; + for (const Shader& shader : g_game.shaders.getShaders()) { + if (player->hasShader(&shader)) { + shaders.push_back(&shader); + } + } + msg.addByte(shaders.size()); + for (const Shader* shader : shaders) { + msg.add(shader->id); + msg.addString(shader->name); + } } writeToOutputBuffer(msg); @@ -2656,6 +2677,9 @@ void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit) msg.add(outfit.lookWing); msg.add(outfit.lookAura); msg.add(outfit.lookEffect); + + Shader* shader = g_game.shaders.getShaderByID(outfit.lookShader); + msg.addString(shader ? shader->name : ""); } } diff --git a/src/shaders.cpp b/src/shaders.cpp new file mode 100644 index 0000000..fe1deda --- /dev/null +++ b/src/shaders.cpp @@ -0,0 +1,52 @@ +#include "otpch.h" + +#include "shaders.h" + +#include "pugicast.h" +#include "tools.h" + +bool Shaders::reload() +{ + shaders.clear(); + return loadFromXml(); +} + +bool Shaders::loadFromXml() +{ + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_file("data/XML/shaders.xml"); + if (!result) { + printXMLError("Error - Shaders::loadFromXml", "data/XML/shaders.xml", result); + return false; + } + + for (auto shaderNode : doc.child("shaders").children()) { + shaders.emplace_back( + static_cast(pugi::cast(shaderNode.attribute("id").value())), + shaderNode.attribute("name").as_string(), + shaderNode.attribute("premium").as_bool() + ); + } + shaders.shrink_to_fit(); + return true; +} + +Shader* Shaders::getShaderByID(uint8_t id) +{ + auto it = std::find_if(shaders.begin(), shaders.end(), [id](const Shader& shader) { + return shader.id == id; + }); + + return it != shaders.end() ? &*it : nullptr; +} + +Shader* Shaders::getShaderByName(const std::string_view &name) +{ + for (auto& it : shaders) { + if (caseInsensitiveEqual(name, it.name)) { + return ⁢ + } + } + return nullptr; + } + diff --git a/src/shaders.h b/src/shaders.h new file mode 100644 index 0000000..590ab75 --- /dev/null +++ b/src/shaders.h @@ -0,0 +1,30 @@ +#ifndef FS_SHADERS_H +#define FS_SHADERS_H + +struct Shader +{ + Shader(uint8_t id, std::string name, bool premium) : + name(std::move(name)), id(id), premium(premium) {} + + uint8_t id; + std::string name; + bool premium; +}; + +class Shaders +{ + public: + bool reload(); + bool loadFromXml(); + Shader* getShaderByID(uint8_t id); + Shader* getShaderByName(const std::string_view &name); + + const std::vector& getShaders() const { + return shaders; + } + + private: + std::vector shaders; +}; + +#endif diff --git a/vc17/theforgottenserver.vcxproj b/vc17/theforgottenserver.vcxproj index 7075553..41bd665 100644 --- a/vc17/theforgottenserver.vcxproj +++ b/vc17/theforgottenserver.vcxproj @@ -239,6 +239,7 @@ + @@ -329,6 +330,7 @@ +