diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b375b16..5beaa34 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/ban.cpp ${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp ${CMAKE_CURRENT_LIST_DIR}/bed.cpp + ${CMAKE_CURRENT_LIST_DIR}/behaviourdatabase.cpp ${CMAKE_CURRENT_LIST_DIR}/chat.cpp ${CMAKE_CURRENT_LIST_DIR}/combat.cpp ${CMAKE_CURRENT_LIST_DIR}/condition.cpp @@ -16,7 +17,6 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/database.cpp ${CMAKE_CURRENT_LIST_DIR}/databasemanager.cpp ${CMAKE_CURRENT_LIST_DIR}/databasetasks.cpp - ${CMAKE_CURRENT_LIST_DIR}/depotchest.cpp ${CMAKE_CURRENT_LIST_DIR}/depotlocker.cpp ${CMAKE_CURRENT_LIST_DIR}/events.cpp ${CMAKE_CURRENT_LIST_DIR}/fileloader.cpp @@ -26,12 +26,10 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/groups.cpp ${CMAKE_CURRENT_LIST_DIR}/house.cpp ${CMAKE_CURRENT_LIST_DIR}/housetile.cpp - ${CMAKE_CURRENT_LIST_DIR}/inbox.cpp ${CMAKE_CURRENT_LIST_DIR}/ioguild.cpp ${CMAKE_CURRENT_LIST_DIR}/iologindata.cpp ${CMAKE_CURRENT_LIST_DIR}/iomap.cpp ${CMAKE_CURRENT_LIST_DIR}/iomapserialize.cpp - ${CMAKE_CURRENT_LIST_DIR}/iomarket.cpp ${CMAKE_CURRENT_LIST_DIR}/item.cpp ${CMAKE_CURRENT_LIST_DIR}/items.cpp ${CMAKE_CURRENT_LIST_DIR}/luascript.cpp @@ -39,7 +37,6 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/map.cpp ${CMAKE_CURRENT_LIST_DIR}/monster.cpp ${CMAKE_CURRENT_LIST_DIR}/monsters.cpp - ${CMAKE_CURRENT_LIST_DIR}/mounts.cpp ${CMAKE_CURRENT_LIST_DIR}/movement.cpp ${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp ${CMAKE_CURRENT_LIST_DIR}/npc.cpp @@ -52,29 +49,24 @@ set(tfs_SRC ${CMAKE_CURRENT_LIST_DIR}/protocol.cpp ${CMAKE_CURRENT_LIST_DIR}/protocolgame.cpp ${CMAKE_CURRENT_LIST_DIR}/protocollogin.cpp - ${CMAKE_CURRENT_LIST_DIR}/protocolold.cpp ${CMAKE_CURRENT_LIST_DIR}/protocolstatus.cpp - ${CMAKE_CURRENT_LIST_DIR}/quests.cpp ${CMAKE_CURRENT_LIST_DIR}/raids.cpp ${CMAKE_CURRENT_LIST_DIR}/rsa.cpp ${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp ${CMAKE_CURRENT_LIST_DIR}/scriptmanager.cpp - ${CMAKE_CURRENT_LIST_DIR}/script.cpp ${CMAKE_CURRENT_LIST_DIR}/server.cpp - ${CMAKE_CURRENT_LIST_DIR}/signals.cpp ${CMAKE_CURRENT_LIST_DIR}/spawn.cpp ${CMAKE_CURRENT_LIST_DIR}/spells.cpp + ${CMAKE_CURRENT_LIST_DIR}/script.cpp ${CMAKE_CURRENT_LIST_DIR}/talkaction.cpp ${CMAKE_CURRENT_LIST_DIR}/tasks.cpp ${CMAKE_CURRENT_LIST_DIR}/teleport.cpp ${CMAKE_CURRENT_LIST_DIR}/thing.cpp ${CMAKE_CURRENT_LIST_DIR}/tile.cpp ${CMAKE_CURRENT_LIST_DIR}/tools.cpp - ${CMAKE_CURRENT_LIST_DIR}/trashholder.cpp ${CMAKE_CURRENT_LIST_DIR}/vocation.cpp ${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp - ${CMAKE_CURRENT_LIST_DIR}/weapons.cpp ${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp ${CMAKE_CURRENT_LIST_DIR}/xtea.cpp - PARENT_SCOPE) +) diff --git a/src/account.h b/src/account.h index 7e14962..5bdc53a 100644 --- a/src/account.h +++ b/src/account.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,8 +24,6 @@ struct Account { std::vector characters; - std::string name; - std::string key; time_t lastDay = 0; uint32_t id = 0; uint16_t premiumDays = 0; diff --git a/src/actions.cpp b/src/actions.cpp index 9a02048..c23df0d 100644 --- a/src/actions.cpp +++ b/src/actions.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,27 +40,29 @@ Actions::Actions() : Actions::~Actions() { - clear(false); + clear(); } -void Actions::clearMap(ActionUseMap& map, bool fromLua) +inline void Actions::clearMap(ActionUseMap& map) { - for (auto it = map.begin(); it != map.end(); ) { - if (fromLua == it->second.fromLua) { - it = map.erase(it); - } else { - ++it; - } + // Filter out duplicates to avoid double-free + std::unordered_set set; + for (const auto& it : map) { + set.insert(it.second); + } + map.clear(); + + for (Action* action : set) { + delete action; } } -void Actions::clear(bool fromLua) +void Actions::clear() { - clearMap(useItemMap, fromLua); - clearMap(uniqueItemMap, fromLua); - clearMap(actionItemMap, fromLua); + clearMap(useItemMap); + clearMap(actionItemMap); - reInitState(fromLua); + scriptInterface.reInitState(); } LuaScriptInterface& Actions::getScriptInterface() @@ -73,23 +75,23 @@ std::string Actions::getScriptBaseName() const return "actions"; } -Event_ptr Actions::getEvent(const std::string& nodeName) +Event* Actions::getEvent(const std::string& nodeName) { if (strcasecmp(nodeName.c_str(), "action") != 0) { return nullptr; } - return Event_ptr(new Action(&scriptInterface)); + return new Action(&scriptInterface); } -bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node) +bool Actions::registerEvent(Event* event, const pugi::xml_node& node) { - Action_ptr action{static_cast(event.release())}; //event is guaranteed to be an Action + Action* action = static_cast(event); //event is guaranteed to be an Action pugi::xml_attribute attr; if ((attr = node.attribute("itemid"))) { uint16_t id = pugi::cast(attr.value()); - auto result = useItemMap.emplace(id, std::move(*action)); + auto result = useItemMap.emplace(id, action); if (!result.second) { std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << id << std::endl; } @@ -105,14 +107,14 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node) uint16_t iterId = fromId; uint16_t toId = pugi::cast(toIdAttribute.value()); - auto result = useItemMap.emplace(iterId, *action); + auto result = useItemMap.emplace(iterId, action); if (!result.second) { std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl; } bool success = result.second; while (++iterId <= toId) { - result = useItemMap.emplace(iterId, *action); + result = useItemMap.emplace(iterId, action); if (!result.second) { std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl; continue; @@ -120,44 +122,10 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node) success = true; } return success; - } else if ((attr = node.attribute("uniqueid"))) { - uint16_t uid = pugi::cast(attr.value()); - - auto result = uniqueItemMap.emplace(uid, std::move(*action)); - if (!result.second) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with uniqueid: " << uid << std::endl; - } - return result.second; - } else if ((attr = node.attribute("fromuid"))) { - pugi::xml_attribute toUidAttribute = node.attribute("touid"); - if (!toUidAttribute) { - std::cout << "[Warning - Actions::registerEvent] Missing touid in fromuid: " << attr.as_string() << std::endl; - return false; - } - - uint16_t fromUid = pugi::cast(attr.value()); - uint16_t iterUid = fromUid; - uint16_t toUid = pugi::cast(toUidAttribute.value()); - - auto result = uniqueItemMap.emplace(iterUid, *action); - if (!result.second) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl; - } - - bool success = result.second; - while (++iterUid <= toUid) { - result = uniqueItemMap.emplace(iterUid, *action); - if (!result.second) { - std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl; - continue; - } - success = true; - } - return success; } else if ((attr = node.attribute("actionid"))) { uint16_t aid = pugi::cast(attr.value()); - auto result = actionItemMap.emplace(aid, std::move(*action)); + auto result = actionItemMap.emplace(aid, action); if (!result.second) { std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with actionid: " << aid << std::endl; } @@ -173,14 +141,14 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node) uint16_t iterAid = fromAid; uint16_t toAid = pugi::cast(toAidAttribute.value()); - auto result = actionItemMap.emplace(iterAid, *action); + auto result = actionItemMap.emplace(iterAid, action); if (!result.second) { std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl; } bool success = result.second; while (++iterAid <= toAid) { - result = actionItemMap.emplace(iterAid, *action); + result = actionItemMap.emplace(iterAid, action); if (!result.second) { std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl; continue; @@ -192,69 +160,6 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node) return false; } -bool Actions::registerLuaEvent(Action* event) -{ - Action_ptr action{ event }; - if (action->getItemIdRange().size() > 0) { - if (action->getItemIdRange().size() == 1) { - auto result = useItemMap.emplace(action->getItemIdRange().at(0), std::move(*action)); - if (!result.second) { - std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with id: " << action->getItemIdRange().at(0) << std::endl; - } - return result.second; - } else { - auto v = action->getItemIdRange(); - for (auto i = v.begin(); i != v.end(); i++) { - auto result = useItemMap.emplace(*i, std::move(*action)); - if (!result.second) { - std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with id: " << *i << " in range from id: " << v.at(0) << ", to id: " << v.at(v.size() - 1) << std::endl; - continue; - } - } - return true; - } - } else if (action->getUniqueIdRange().size() > 0) { - if (action->getUniqueIdRange().size() == 1) { - auto result = uniqueItemMap.emplace(action->getUniqueIdRange().at(0), std::move(*action)); - if (!result.second) { - std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with uid: " << action->getUniqueIdRange().at(0) << std::endl; - } - return result.second; - } else { - auto v = action->getUniqueIdRange(); - for (auto i = v.begin(); i != v.end(); i++) { - auto result = uniqueItemMap.emplace(*i, std::move(*action)); - if (!result.second) { - std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with uid: " << *i << " in range from uid: " << v.at(0) << ", to uid: " << v.at(v.size() - 1) << std::endl; - continue; - } - } - return true; - } - } else if (action->getActionIdRange().size() > 0) { - if (action->getActionIdRange().size() == 1) { - auto result = actionItemMap.emplace(action->getActionIdRange().at(0), std::move(*action)); - if (!result.second) { - std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with aid: " << action->getActionIdRange().at(0) << std::endl; - } - return result.second; - } else { - auto v = action->getActionIdRange(); - for (auto i = v.begin(); i != v.end(); i++) { - auto result = actionItemMap.emplace(*i, std::move(*action)); - if (!result.second) { - std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with aid: " << *i << " in range from aid: " << v.at(0) << ", to aid: " << v.at(v.size() - 1) << std::endl; - continue; - } - } - return true; - } - } else { - std::cout << "[Warning - Actions::registerLuaEvent] There is no id / aid / uid set for this event" << std::endl; - return false; - } -} - ReturnValue Actions::canUse(const Player* player, const Position& pos) { if (pos.x != 0xFFFF) { @@ -303,23 +208,16 @@ ReturnValue Actions::canUseFar(const Creature* creature, const Position& toPos, Action* Actions::getAction(const Item* item) { - if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { - auto it = uniqueItemMap.find(item->getUniqueId()); - if (it != uniqueItemMap.end()) { - return &it->second; - } - } - if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) { auto it = actionItemMap.find(item->getActionId()); if (it != actionItemMap.end()) { - return &it->second; + return it->second; } } auto it = useItemMap.find(item->getID()); if (it != useItemMap.end()) { - return &it->second; + return it->second; } //rune items @@ -358,41 +256,44 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ if (bed->trySleep(player)) { player->setBedItem(bed); - g_game.sendOfflineTrainingDialog(player); + if (!bed->sleep(player)) { + return RETURNVALUE_CANNOTUSETHISOBJECT; + } } return RETURNVALUE_NOERROR; } if (Container* container = item->getContainer()) { - Container* openContainer; + if (!item->isChestQuest()) { + Container* openContainer; - //depot container - if (DepotLocker* depot = container->getDepotLocker()) { - DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId()); - myDepotLocker->setParent(depot->getParent()->getTile()); - openContainer = myDepotLocker; - player->setLastDepotId(depot->getDepotId()); - } else { - openContainer = container; + //depot container + if (DepotLocker* depot = container->getDepotLocker()) { + DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId(), true); + myDepotLocker->setParent(depot->getParent()->getTile()); + openContainer = myDepotLocker; + } else { + openContainer = container; + } + + uint32_t corpseOwner = container->getCorpseOwner(); + if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) { + return RETURNVALUE_YOUARENOTTHEOWNER; + } + + //open/close container + int32_t oldContainerId = player->getContainerID(openContainer); + if (oldContainerId != -1) { + player->onCloseContainer(openContainer); + player->closeContainer(oldContainerId); + } else { + player->addContainer(index, openContainer); + player->onSendContainer(openContainer); + } + + return RETURNVALUE_NOERROR; } - - uint32_t corpseOwner = container->getCorpseOwner(); - if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) { - return RETURNVALUE_YOUARENOTTHEOWNER; - } - - //open/close container - int32_t oldContainerId = player->getContainerID(openContainer); - if (oldContainerId != -1) { - player->onCloseContainer(openContainer); - player->closeContainer(oldContainerId); - } else { - player->addContainer(index, openContainer); - player->onSendContainer(openContainer); - } - - return RETURNVALUE_NOERROR; } const ItemType& it = Item::items[item->getID()]; @@ -406,6 +307,12 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_ } return RETURNVALUE_NOERROR; + } else if (it.changeUse) { + if (it.transformToOnUse) { + g_game.transformItem(item, it.transformToOnUse); + g_game.startDecay(item); + return RETURNVALUE_NOERROR; + } } return RETURNVALUE_CANNOTUSETHISOBJECT; @@ -417,7 +324,7 @@ bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item* player->stopWalk(); if (isHotkey) { - showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), item->getSubType())); + showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1)); } ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey); @@ -447,7 +354,7 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position& } if (isHotkey) { - showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), item->getSubType())); + showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1)); } if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos, isHotkey)) { @@ -466,9 +373,11 @@ void Actions::showUseHotkeyMessage(Player* player, const Item* item, uint32_t co const ItemType& it = Item::items[item->getID()]; if (!it.showCount) { ss << "Using one of " << item->getName() << "..."; - } else if (count == 1) { + } + else if (count == 1) { ss << "Using the last " << item->getName() << "..."; - } else { + } + else { ss << "Using one of " << count << ' ' << item->getPluralName() << "..."; } player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); @@ -477,6 +386,9 @@ void Actions::showUseHotkeyMessage(Player* player, const Item* item, uint32_t co Action::Action(LuaScriptInterface* interface) : Event(interface), function(nullptr), allowFarUse(false), checkFloor(true), checkLineOfSight(true) {} +Action::Action(const Action* copy) : + Event(copy), allowFarUse(copy->allowFarUse), checkFloor(copy->checkFloor), checkLineOfSight(copy->checkLineOfSight) {} + bool Action::configureEvent(const pugi::xml_node& node) { pugi::xml_attribute allowFarUseAttr = node.attribute("allowfaruse"); @@ -497,38 +409,6 @@ bool Action::configureEvent(const pugi::xml_node& node) return true; } -namespace { - -bool enterMarket(Player* player, Item*, const Position&, Thing*, const Position&, bool) -{ - if (player->getLastDepotId() == -1) { - return false; - } - - player->sendMarketEnter(player->getLastDepotId()); - return true; -} - -} - -bool Action::loadFunction(const pugi::xml_attribute& attr, bool isScripted) -{ - const char* functionName = attr.as_string(); - if (strcasecmp(functionName, "market") == 0) { - function = enterMarket; - } else { - if (!isScripted) { - std::cout << "[Warning - Action::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; - return false; - } - } - - if (!isScripted) { - scripted = false; - } - return true; -} - std::string Action::getScriptEventName() const { return "onUse"; @@ -538,7 +418,8 @@ ReturnValue Action::canExecuteAction(const Player* player, const Position& toPos { if (!allowFarUse) { return g_actions->canUse(player, toPos); - } else { + } + else { return g_actions->canUseFar(player, toPos, checkLineOfSight, checkFloor); } } @@ -577,4 +458,4 @@ bool Action::executeUse(Player* player, Item* item, const Position& fromPosition LuaScriptInterface::pushBoolean(L, isHotkey); return scriptInterface->callFunction(6); -} +} \ No newline at end of file diff --git a/src/actions.h b/src/actions.h index 69ca1a9..6f03677 100644 --- a/src/actions.h +++ b/src/actions.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,14 +31,15 @@ using ActionFunction = std::function; + typedef std::map ActionUseMap; ActionUseMap useItemMap; - ActionUseMap uniqueItemMap; ActionUseMap actionItemMap; Action* getAction(const Item* item); - void clearMap(ActionUseMap& map, bool fromLua); + void clearMap(ActionUseMap& map); LuaScriptInterface scriptInterface; }; diff --git a/src/ban.cpp b/src/ban.cpp index 0b75ceb..547ad29 100644 --- a/src/ban.cpp +++ b/src/ban.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,15 +24,15 @@ #include "databasetasks.h" #include "tools.h" -bool Ban::acceptConnection(uint32_t clientIP) +bool Ban::acceptConnection(uint32_t clientip) { std::lock_guard lockClass(lock); uint64_t currentTime = OTSYS_TIME(); - auto it = ipConnectMap.find(clientIP); + auto it = ipConnectMap.find(clientip); if (it == ipConnectMap.end()) { - ipConnectMap.emplace(clientIP, ConnectBlock(currentTime, 0, 1)); + ipConnectMap.emplace(clientip, ConnectBlock(currentTime, 0, 1)); return true; } @@ -60,12 +60,12 @@ bool Ban::acceptConnection(uint32_t clientIP) bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; query << "SELECT `reason`, `expires_at`, `banned_at`, `banned_by`, (SELECT `name` FROM `players` WHERE `id` = `banned_by`) AS `name` FROM `account_bans` WHERE `account_id` = " << accountId; - DBResult_ptr result = db.storeQuery(query.str()); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -74,7 +74,7 @@ bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo) if (expiresAt != 0 && time(nullptr) > expiresAt) { // Move the ban to history if it has expired query.str(std::string()); - query << "INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, `expired_at`, `banned_by`) VALUES (" << accountId << ',' << db.escapeString(result->getString("reason")) << ',' << result->getNumber("banned_at") << ',' << expiresAt << ',' << result->getNumber("banned_by") << ')'; + query << "INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, `expired_at`, `banned_by`) VALUES (" << accountId << ',' << db->escapeString(result->getString("reason")) << ',' << result->getNumber("banned_at") << ',' << expiresAt << ',' << result->getNumber("banned_by") << ')'; g_databaseTasks.addTask(query.str()); query.str(std::string()); @@ -89,18 +89,18 @@ bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo) return true; } -bool IOBan::isIpBanned(uint32_t clientIP, BanInfo& banInfo) +bool IOBan::isIpBanned(uint32_t clientip, BanInfo& banInfo) { - if (clientIP == 0) { + if (clientip == 0) { return false; } - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `reason`, `expires_at`, (SELECT `name` FROM `players` WHERE `id` = `banned_by`) AS `name` FROM `ip_bans` WHERE `ip` = " << clientIP; + query << "SELECT `reason`, `expires_at`, (SELECT `name` FROM `players` WHERE `id` = `banned_by`) AS `name` FROM `ip_bans` WHERE `ip` = " << clientip; - DBResult_ptr result = db.storeQuery(query.str()); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -108,7 +108,7 @@ bool IOBan::isIpBanned(uint32_t clientIP, BanInfo& banInfo) int64_t expiresAt = result->getNumber("expires_at"); if (expiresAt != 0 && time(nullptr) > expiresAt) { query.str(std::string()); - query << "DELETE FROM `ip_bans` WHERE `ip` = " << clientIP; + query << "DELETE FROM `ip_bans` WHERE `ip` = " << clientip; g_databaseTasks.addTask(query.str()); return false; } @@ -123,5 +123,5 @@ bool IOBan::isPlayerNamelocked(uint32_t playerId) { std::ostringstream query; query << "SELECT 1 FROM `player_namelocks` WHERE `player_id` = " << playerId; - return Database::getInstance().storeQuery(query.str()).get() != nullptr; + return Database::getInstance()->storeQuery(query.str()).get() != nullptr; } diff --git a/src/ban.h b/src/ban.h index 850d7cc..86535a6 100644 --- a/src/ban.h +++ b/src/ban.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,14 +35,14 @@ struct ConnectBlock { uint32_t count; }; -using IpConnectMap = std::map; +typedef std::map IpConnectMap; class Ban { public: - bool acceptConnection(uint32_t clientIP); + bool acceptConnection(uint32_t clientip); - private: + protected: IpConnectMap ipConnectMap; std::recursive_mutex lock; }; @@ -51,7 +51,7 @@ class IOBan { public: static bool isAccountBanned(uint32_t accountId, BanInfo& banInfo); - static bool isIpBanned(uint32_t clientIP, BanInfo& banInfo); + static bool isIpBanned(uint32_t ip, BanInfo& banInfo); static bool isPlayerNamelocked(uint32_t playerId); }; diff --git a/src/baseevents.cpp b/src/baseevents.cpp index 4dc648c..c99243e 100644 --- a/src/baseevents.cpp +++ b/src/baseevents.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,13 +51,14 @@ bool BaseEvents::loadFromXml() loaded = true; for (auto node : doc.child(scriptsName.c_str()).children()) { - Event_ptr event = getEvent(node.name()); + Event* event = getEvent(node.name()); if (!event) { continue; } if (!event->configureEvent(node)) { std::cout << "[Warning - BaseEvents::loadFromXml] Failed to configure event" << std::endl; + delete event; continue; } @@ -67,15 +68,12 @@ bool BaseEvents::loadFromXml() if (scriptAttribute) { std::string scriptFile = "scripts/" + std::string(scriptAttribute.as_string()); success = event->checkScript(basePath, scriptsName, scriptFile) && event->loadScript(basePath + scriptFile); - if (node.attribute("function")) { - event->loadFunction(node.attribute("function"), true); - } } else { - success = event->loadFunction(node.attribute("function"), false); + success = event->loadFunction(node.attribute("function")); } - if (success) { - registerEvent(std::move(event), node); + if (!success || !registerEvent(event, node)) { + delete event; } } return true; @@ -84,19 +82,15 @@ bool BaseEvents::loadFromXml() bool BaseEvents::reload() { loaded = false; - clear(false); + clear(); return loadFromXml(); } -void BaseEvents::reInitState(bool fromLua) -{ - if (!fromLua) { - getScriptInterface().reInitState(); - } -} - Event::Event(LuaScriptInterface* interface) : scriptInterface(interface) {} +Event::Event(const Event* copy) : + scripted(copy->scripted), scriptId(copy->scriptId), scriptInterface(copy->scriptInterface) {} + bool Event::checkScript(const std::string& basePath, const std::string& scriptsName, const std::string& scriptFile) const { LuaScriptInterface* testInterface = g_luaEnvironment.getTestInterface(); @@ -149,24 +143,6 @@ bool Event::loadScript(const std::string& scriptFile) return true; } -bool Event::loadCallback() -{ - if (!scriptInterface || scriptId != 0) { - std::cout << "Failure: [Event::loadCallback] scriptInterface == nullptr. scriptid = " << scriptId << std::endl; - return false; - } - - int32_t id = scriptInterface->getEvent(); - if (id == -1) { - std::cout << "[Warning - Event::loadCallback] Event " << getScriptEventName() << " not found. " << std::endl; - return false; - } - - scripted = true; - scriptId = id; - return true; -} - bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string& name) { if (!interface) { @@ -182,6 +158,7 @@ bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string& na return false; } + callbackName = name; scriptId = id; loaded = true; return true; diff --git a/src/baseevents.h b/src/baseevents.h index bf74c97..c5b528f 100644 --- a/src/baseevents.h +++ b/src/baseevents.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,21 +22,18 @@ #include "luascript.h" -class Event; -using Event_ptr = std::unique_ptr; - class Event { public: explicit Event(LuaScriptInterface* interface); + explicit Event(const Event* copy); virtual ~Event() = default; virtual bool configureEvent(const pugi::xml_node& node) = 0; bool checkScript(const std::string& basePath, const std::string& scriptsName, const std::string& scriptFile) const; bool loadScript(const std::string& scriptFile); - bool loadCallback(); - virtual bool loadFunction(const pugi::xml_attribute&, bool) { + virtual bool loadFunction(const pugi::xml_attribute&) { return false; } @@ -44,12 +41,10 @@ class Event return scripted; } - bool scripted = false; - bool fromLua = false; - protected: virtual std::string getScriptEventName() const = 0; + bool scripted = false; int32_t scriptId = 0; LuaScriptInterface* scriptInterface = nullptr; }; @@ -65,14 +60,13 @@ class BaseEvents bool isLoaded() const { return loaded; } - void reInitState(bool fromLua); - private: + protected: virtual LuaScriptInterface& getScriptInterface() = 0; virtual std::string getScriptBaseName() const = 0; - virtual Event_ptr getEvent(const std::string& nodeName) = 0; - virtual bool registerEvent(Event_ptr event, const pugi::xml_node& node) = 0; - virtual void clear(bool) = 0; + virtual Event* getEvent(const std::string& nodeName) = 0; + virtual bool registerEvent(Event* event, const pugi::xml_node& node) = 0; + virtual void clear() = 0; bool loaded = false; }; @@ -88,8 +82,9 @@ class CallBack int32_t scriptId = 0; LuaScriptInterface* scriptInterface = nullptr; - private: bool loaded = false; + + std::string callbackName; }; #endif diff --git a/src/bed.cpp b/src/bed.cpp index a66a573..90629a9 100644 --- a/src/bed.cpp +++ b/src/bed.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -159,8 +159,8 @@ bool BedItem::sleep(Player* player) // make the player walk onto the bed g_game.map.moveCreature(*player, *getTile()); - // display 'Zzzz'/sleep effect - g_game.addMagicEffect(player->getPosition(), CONST_ME_SLEEP); + // display poff effect + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); // kick player after he sees himself walk onto the bed and it change id uint32_t playerId = player->getID(); @@ -246,10 +246,10 @@ void BedItem::updateAppearance(const Player* player) { const ItemType& it = Item::items[id]; if (it.type == ITEM_TYPE_BED) { - if (player && it.transformToOnUse[player->getSex()] != 0) { - const ItemType& newType = Item::items[it.transformToOnUse[player->getSex()]]; + if (player && it.transformToOnUse != 0) { + const ItemType& newType = Item::items[it.transformToOnUse]; if (newType.type == ITEM_TYPE_BED) { - g_game.transformItem(this, it.transformToOnUse[player->getSex()]); + g_game.transformItem(this, it.transformToOnUse); } } else if (it.transformToFree != 0) { const ItemType& newType = Item::items[it.transformToFree]; diff --git a/src/bed.h b/src/bed.h index ccc37cc..f1f836d 100644 --- a/src/bed.h +++ b/src/bed.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,17 +30,17 @@ class BedItem final : public Item public: explicit BedItem(uint16_t id); - BedItem* getBed() override { + BedItem* getBed() final { return this; } - const BedItem* getBed() const override { + const BedItem* getBed() const final { return this; } - Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; - void serializeAttr(PropWriteStream& propWriteStream) const override; + Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final; + void serializeAttr(PropWriteStream& propWriteStream) const final; - bool canRemove() const override { + bool canRemove() const final { return house == nullptr; } @@ -48,6 +48,9 @@ class BedItem final : public Item return sleeperGUID; } + House* getHouse() const { + return house; + } void setHouse(House* h) { house = h; } @@ -60,7 +63,7 @@ class BedItem final : public Item BedItem* getNextBedItem() const; - private: + protected: void updateAppearance(const Player* player); void regeneratePlayer(Player* player) const; void internalSetSleeper(const Player* player); diff --git a/src/chat.cpp b/src/chat.cpp index 48f5338..59973c8 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -54,10 +54,6 @@ void PrivateChatChannel::invitePlayer(const Player& player, Player& invitePlayer ss.str(std::string()); ss << invitePlayer.getName() << " has been invited."; player.sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); - - for (const auto& it : users) { - it.second->sendChannelEvent(id, invitePlayer.getName(), CHANNELEVENT_INVITE); - } } void PrivateChatChannel::excludePlayer(const Player& player, Player& excludePlayer) @@ -73,10 +69,6 @@ void PrivateChatChannel::excludePlayer(const Player& player, Player& excludePlay player.sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); excludePlayer.sendClosePrivate(id); - - for (const auto& it : users) { - it.second->sendChannelEvent(id, excludePlayer.getName(), CHANNELEVENT_EXCLUDE); - } } void PrivateChatChannel::closeChannel() const @@ -96,20 +88,6 @@ bool ChatChannel::addUser(Player& player) return false; } - // TODO: Move to script when guild channels can be scripted - if (id == CHANNEL_GUILD) { - Guild* guild = player.getGuild(); - if (guild && !guild->getMotd().empty()) { - g_scheduler.addEvent(createSchedulerTask(150, std::bind(&Game::sendGuildMotd, &g_game, player.getID()))); - } - } - - if (!publicChannel) { - for (const auto& it : users) { - it.second->sendChannelEvent(id, player.getName(), CHANNELEVENT_JOIN); - } - } - users[player.getID()] = &player; return true; } @@ -123,27 +101,10 @@ bool ChatChannel::removeUser(const Player& player) users.erase(iter); - if (!publicChannel) { - for (const auto& it : users) { - it.second->sendChannelEvent(id, player.getName(), CHANNELEVENT_LEAVE); - } - } - executeOnLeaveEvent(player); return true; } -bool ChatChannel::hasUser(const Player& player) { - return users.find(player.getID()) != users.end(); -} - -void ChatChannel::sendToAll(const std::string& message, SpeakClasses type) const -{ - for (const auto& it : users) { - it.second->sendChannelMessage("", message, type, id); - } -} - bool ChatChannel::talk(const Player& fromPlayer, SpeakClasses type, const std::string& text) { if (users.find(fromPlayer.getID()) == users.end()) { @@ -294,39 +255,21 @@ bool Chat::load() return false; } + std::forward_list removedChannels; + for (auto& channelEntry : normalChannels) { + ChatChannel& channel = channelEntry.second; + channel.onSpeakEvent = -1; + channel.canJoinEvent = -1; + channel.onJoinEvent = -1; + channel.onLeaveEvent = -1; + removedChannels.push_front(channelEntry.first); + } + for (auto channelNode : doc.child("channels").children()) { - uint16_t channelId = pugi::cast(channelNode.attribute("id").value()); - std::string channelName = channelNode.attribute("name").as_string(); - bool isPublic = channelNode.attribute("public").as_bool(); + ChatChannel channel(pugi::cast(channelNode.attribute("id").value()), channelNode.attribute("name").as_string()); + channel.publicChannel = channelNode.attribute("public").as_bool(); + pugi::xml_attribute scriptAttribute = channelNode.attribute("script"); - - auto it = normalChannels.find(channelId); - if (it != normalChannels.end()) { - ChatChannel& channel = it->second; - channel.publicChannel = isPublic; - channel.name = channelName; - - if (scriptAttribute) { - if (scriptInterface.loadFile("data/chatchannels/scripts/" + std::string(scriptAttribute.as_string())) == 0) { - channel.onSpeakEvent = scriptInterface.getEvent("onSpeak"); - channel.canJoinEvent = scriptInterface.getEvent("canJoin"); - channel.onJoinEvent = scriptInterface.getEvent("onJoin"); - channel.onLeaveEvent = scriptInterface.getEvent("onLeave"); - } else { - std::cout << "[Warning - Chat::load] Can not load script: " << scriptAttribute.as_string() << std::endl; - } - } - - UsersMap tempUserMap = std::move(channel.users); - for (const auto& pair : tempUserMap) { - channel.addUser(*pair.second); - } - continue; - } - - ChatChannel channel(channelId, channelName); - channel.publicChannel = isPublic; - if (scriptAttribute) { if (scriptInterface.loadFile("data/chatchannels/scripts/" + std::string(scriptAttribute.as_string())) == 0) { channel.onSpeakEvent = scriptInterface.getEvent("onSpeak"); @@ -338,8 +281,13 @@ bool Chat::load() } } + removedChannels.remove(channel.id); normalChannels[channel.id] = channel; } + + for (uint16_t channelId : removedChannels) { + normalChannels.erase(channelId); + } return true; } diff --git a/src/chat.h b/src/chat.h index 2f4c603..e181142 100644 --- a/src/chat.h +++ b/src/chat.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,24 +26,23 @@ class Party; class Player; -using UsersMap = std::map; -using InvitedMap = std::map; +typedef std::map UsersMap; +typedef std::map InvitedMap; class ChatChannel { public: ChatChannel() = default; ChatChannel(uint16_t channelId, std::string channelName): - id{channelId}, name{std::move(channelName)} {} + name(std::move(channelName)), + id(channelId) {} virtual ~ChatChannel() = default; bool addUser(Player& player); bool removeUser(const Player& player); - bool hasUser(const Player& player); bool talk(const Player& fromPlayer, SpeakClasses type, const std::string& text); - void sendToAll(const std::string& message, SpeakClasses type) const; const std::string& getName() const { return name; @@ -72,9 +71,6 @@ class ChatChannel protected: UsersMap users; - uint16_t id; - - private: std::string name; int32_t canJoinEvent = -1; @@ -82,6 +78,7 @@ class ChatChannel int32_t onLeaveEvent = -1; int32_t onSpeakEvent = -1; + uint16_t id; bool publicChannel = false; friend class Chat; @@ -92,7 +89,7 @@ class PrivateChatChannel final : public ChatChannel public: PrivateChatChannel(uint16_t channelId, std::string channelName) : ChatChannel(channelId, channelName) {} - uint32_t getOwner() const override { + uint32_t getOwner() const final { return owner; } void setOwner(uint32_t owner) { @@ -108,16 +105,16 @@ class PrivateChatChannel final : public ChatChannel void closeChannel() const; - const InvitedMap* getInvitedUsers() const override { + const InvitedMap* getInvitedUsers() const final { return &invites; } - private: + protected: InvitedMap invites; uint32_t owner = 0; }; -using ChannelList = std::list; +typedef std::list ChannelList; class Chat { diff --git a/src/combat.cpp b/src/combat.cpp index f033057..e63bc0f 100644 --- a/src/combat.cpp +++ b/src/combat.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,61 +22,30 @@ #include "combat.h" #include "game.h" -#include "weapons.h" #include "configmanager.h" +#include "monster.h" #include "events.h" extern Game g_game; -extern Weapons* g_weapons; extern ConfigManager g_config; extern Events* g_events; -CombatDamage Combat::getCombatDamage(Creature* creature, Creature* target) const +CombatDamage Combat::getCombatDamage(Creature* creature) const { CombatDamage damage; damage.origin = params.origin; - damage.primary.type = params.combatType; + damage.type = params.combatType; if (formulaType == COMBAT_FORMULA_DAMAGE) { - damage.primary.value = normal_random( - static_cast(mina), - static_cast(maxa) - ); + damage.min = static_cast(mina); + damage.max = static_cast(maxa); } else if (creature) { int32_t min, max; if (creature->getCombatValues(min, max)) { - damage.primary.value = normal_random(min, max); + damage.min = min; + damage.max = max; } else if (Player* player = creature->getPlayer()) { if (params.valueCallback) { params.valueCallback->getMinMaxValues(player, damage, params.useCharges); - } else if (formulaType == COMBAT_FORMULA_LEVELMAGIC) { - int32_t levelFormula = player->getLevel() * 2 + player->getMagicLevel() * 3; - damage.primary.value = normal_random( - static_cast(levelFormula * mina + minb), - static_cast(levelFormula * maxa + maxb) - ); - } else if (formulaType == COMBAT_FORMULA_SKILL) { - Item* tool = player->getWeapon(); - const Weapon* weapon = g_weapons->getWeapon(tool); - if (weapon) { - damage.primary.value = normal_random( - static_cast(minb), - static_cast(weapon->getWeaponDamage(player, target, tool, true) * maxa + maxb) - ); - - damage.secondary.type = weapon->getElementType(); - damage.secondary.value = weapon->getElementDamage(player, target, tool); - if (params.useCharges) { - uint16_t charges = tool->getCharges(); - if (charges != 0) { - g_game.transformItem(tool, tool->getID(), charges - 1); - } - } - } else { - damage.primary.value = normal_random( - static_cast(minb), - static_cast(maxb) - ); - } } } } @@ -110,23 +79,11 @@ CombatType_t Combat::ConditionToDamageType(ConditionType_t type) case CONDITION_ENERGY: return COMBAT_ENERGYDAMAGE; - case CONDITION_BLEEDING: - return COMBAT_PHYSICALDAMAGE; - - case CONDITION_DROWN: - return COMBAT_DROWNDAMAGE; - case CONDITION_POISON: return COMBAT_EARTHDAMAGE; - case CONDITION_FREEZING: - return COMBAT_ICEDAMAGE; - - case CONDITION_DAZZLED: - return COMBAT_HOLYDAMAGE; - - case CONDITION_CURSED: - return COMBAT_DEATHDAMAGE; + case CONDITION_DROWN: + return COMBAT_DROWNDAMAGE; default: break; @@ -144,23 +101,11 @@ ConditionType_t Combat::DamageToConditionType(CombatType_t type) case COMBAT_ENERGYDAMAGE: return CONDITION_ENERGY; - case COMBAT_DROWNDAMAGE: - return CONDITION_DROWN; - case COMBAT_EARTHDAMAGE: return CONDITION_POISON; - case COMBAT_ICEDAMAGE: - return CONDITION_FREEZING; - - case COMBAT_HOLYDAMAGE: - return CONDITION_DAZZLED; - - case COMBAT_DEATHDAMAGE: - return CONDITION_CURSED; - - case COMBAT_PHYSICALDAMAGE: - return CONDITION_BLEEDING; + case COMBAT_DROWNDAMAGE: + return CONDITION_DROWN; default: return CONDITION_NONE; @@ -180,15 +125,15 @@ bool Combat::isPlayerCombat(const Creature* target) return false; } -ReturnValue Combat::canTargetCreature(Player* attacker, Creature* target) +ReturnValue Combat::canTargetCreature(Player* player, Creature* target) { - if (attacker == target) { + if (player == target) { return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } - if (!attacker->hasFlag(PlayerFlag_IgnoreProtectionZone)) { + if (!player->hasFlag(PlayerFlag_IgnoreProtectionZone)) { //pz-zone - if (attacker->getZone() == ZONE_PROTECTION) { + if (player->getZone() == ZONE_PROTECTION) { return RETURNVALUE_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE; } @@ -198,7 +143,7 @@ ReturnValue Combat::canTargetCreature(Player* attacker, Creature* target) //nopvp-zone if (isPlayerCombat(target)) { - if (attacker->getZone() == ZONE_NOPVP) { + if (player->getZone() == ZONE_NOPVP) { return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; } @@ -208,7 +153,7 @@ ReturnValue Combat::canTargetCreature(Player* attacker, Creature* target) } } - if (attacker->hasFlag(PlayerFlag_CannotUseCombat) || !target->isAttackable()) { + if (player->hasFlag(PlayerFlag_CannotUseCombat) || !target->isAttackable()) { if (target->getPlayer()) { return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } else { @@ -217,16 +162,16 @@ ReturnValue Combat::canTargetCreature(Player* attacker, Creature* target) } if (target->getPlayer()) { - if (isProtected(attacker, target->getPlayer())) { + if (isProtected(player, target->getPlayer())) { return RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER; } - if (attacker->hasSecureMode() && !Combat::isInPvpZone(attacker, target) && attacker->getSkullClient(target->getPlayer()) == SKULL_NONE) { + if (player->hasSecureMode() && !Combat::isInPvpZone(player, target) && player->getSkullClient(target->getPlayer()) == SKULL_NONE) { return RETURNVALUE_TURNSECUREMODETOATTACKUNMARKEDPLAYERS; } } - return Combat::canDoCombat(attacker, target); + return Combat::canDoCombat(player, target); } ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive) @@ -235,7 +180,11 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive) return RETURNVALUE_NOTENOUGHROOM; } - if (tile->hasFlag(TILESTATE_FLOORCHANGE)) { + if (tile->hasProperty(CONST_PROP_BLOCKPROJECTILE) && tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID)) { + return RETURNVALUE_NOTENOUGHROOM; + } + + if (tile->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID) && tile->hasProperty(CONST_PROP_UNLAY)) { return RETURNVALUE_NOTENOUGHROOM; } @@ -248,7 +197,8 @@ ReturnValue Combat::canDoCombat(Creature* caster, Tile* tile, bool aggressive) const Position& tilePosition = tile->getPosition(); if (casterPosition.z < tilePosition.z) { return RETURNVALUE_FIRSTGODOWNSTAIRS; - } else if (casterPosition.z > tilePosition.z) { + } + else if (casterPosition.z > tilePosition.z) { return RETURNVALUE_FIRSTGOUPSTAIRS; } @@ -283,10 +233,6 @@ bool Combat::isProtected(const Player* attacker, const Player* target) return true; } - if (attacker->getSkull() == SKULL_BLACK && attacker->getSkullClient(target) == SKULL_NONE) { - return true; - } - return false; } @@ -314,7 +260,8 @@ ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target) const Tile* targetPlayerTile = targetPlayer->getTile(); if (targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE)) { return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; - } else if (attackerPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE) && !targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE | TILESTATE_PROTECTIONZONE)) { + } + else if (attackerPlayer->getTile()->hasFlag(TILESTATE_NOPVPZONE) && !targetPlayerTile->hasFlag(TILESTATE_NOPVPZONE | TILESTATE_PROTECTIONZONE)) { return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; } } @@ -334,7 +281,8 @@ ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target) } } } - } else if (target->getMonster()) { + } + else if (target->getMonster()) { if (const Player* attackerPlayer = attacker->getPlayer()) { if (attackerPlayer->hasFlag(PlayerFlag_CannotAttackMonster)) { return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; @@ -343,16 +291,6 @@ ReturnValue Combat::canDoCombat(Creature* attacker, Creature* target) if (target->isSummon() && target->getMaster()->getPlayer() && target->getZone() == ZONE_NOPVP) { return RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE; } - } else if (attacker->getMonster()) { - const Creature* targetMaster = target->getMaster(); - - if (!targetMaster || !targetMaster->getPlayer()) { - const Creature* attackerMaster = attacker->getMaster(); - - if (!attackerMaster || !attackerMaster->getPlayer()) { - return RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE; - } - } } } @@ -435,6 +373,16 @@ bool Combat::setParam(CombatParam_t param, uint32_t value) params.useCharges = (value != 0); return true; } + + case COMBAT_PARAM_DECREASEDAMAGE: { + params.decreaseDamage = static_cast(value); + return true; + } + + case COMBAT_PARAM_MAXIMUMDECREASEDDAMAGE: { + params.maximumDecreasedDamage = static_cast(value); + return true; + } } return false; } @@ -488,35 +436,44 @@ void Combat::CombatHealthFunc(Creature* caster, Creature* target, const CombatPa { assert(data); CombatDamage damage = *data; - if (g_game.combatBlockHit(damage, caster, target, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) { - return; + + if (damage.value == 0) { + damage.value = normal_random(damage.min, damage.max); } - if ((damage.primary.value < 0 || damage.secondary.value < 0) && caster) { + if (damage.value < 0 && caster) { Player* targetPlayer = target->getPlayer(); - if (targetPlayer && caster->getPlayer() && targetPlayer->getSkull() != SKULL_BLACK) { - damage.primary.value /= 2; - damage.secondary.value /= 2; + if (targetPlayer && caster->getPlayer()) { + damage.value /= 2; } } + if (g_game.combatBlockHit(damage, caster, target, params.blockedByShield, params.blockedByArmor, params.itemId != 0)) { + return; + } + if (g_game.combatChangeHealth(caster, target, damage)) { CombatConditionFunc(caster, target, params, &damage); CombatDispelFunc(caster, target, params, nullptr); } } -void Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage) +void Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) { - assert(damage); - CombatDamage damageCopy = *damage; - if (damageCopy.primary.value < 0) { + assert(data); + CombatDamage damage = *data; + + if (damage.value == 0) { + damage.value = normal_random(damage.min, damage.max); + } + + if (damage.value < 0) { if (caster && caster->getPlayer() && target->getPlayer()) { - damageCopy.primary.value /= 2; + damage.value /= 2; } } - if (g_game.combatChangeMana(caster, target, damageCopy)) { + if (g_game.combatChangeMana(caster, target, damage)) { CombatConditionFunc(caster, target, params, nullptr); CombatDispelFunc(caster, target, params, nullptr); } @@ -524,7 +481,7 @@ void Combat::CombatManaFunc(Creature* caster, Creature* target, const CombatPara void Combat::CombatConditionFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data) { - if (params.origin == ORIGIN_MELEE && data && data->primary.value == 0 && data->secondary.value == 0) { + if (params.origin == ORIGIN_MELEE && data && data->value == 0) { return; } @@ -552,7 +509,7 @@ void Combat::CombatNullFunc(Creature* caster, Creature* target, const CombatPara CombatDispelFunc(caster, target, params, nullptr); } -void Combat::combatTileEffects(const SpectatorVec& spectators, Creature* caster, Tile* tile, const CombatParams& params) +void Combat::combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params) { if (params.itemId != 0) { uint16_t itemId = params.itemId; @@ -630,51 +587,25 @@ void Combat::combatTileEffects(const SpectatorVec& spectators, Creature* caster, } if (params.impactEffect != CONST_ME_NONE) { - Game::addMagicEffect(spectators, tile->getPosition(), params.impactEffect); + Game::addMagicEffect(list, tile->getPosition(), params.impactEffect); } } void Combat::postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params) { if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), pos, params.distanceEffect); + addDistanceEffect(caster->getPosition(), pos, params.distanceEffect); } } -void Combat::addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, uint8_t effect) +void Combat::addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect) { - if (effect == CONST_ANI_WEAPONTYPE) { - if (!caster) { - return; - } - - Player* player = caster->getPlayer(); - if (!player) { - return; - } - - switch (player->getWeaponType()) { - case WEAPON_AXE: - effect = CONST_ANI_WHIRLWINDAXE; - break; - case WEAPON_SWORD: - effect = CONST_ANI_WHIRLWINDSWORD; - break; - case WEAPON_CLUB: - effect = CONST_ANI_WHIRLWINDCLUB; - break; - default: - effect = CONST_ANI_NONE; - break; - } - } - if (effect != CONST_ANI_NONE) { g_game.addDistanceEffect(fromPos, toPos, effect); } } -void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, CombatFunction func, CombatDamage* data) +void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, COMBATFUNC func, CombatDamage* data) { std::forward_list tileList; @@ -684,6 +615,7 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* getCombatArea(pos, pos, area, tileList); } + SpectatorVec list; uint32_t maxX = 0; uint32_t maxY = 0; @@ -704,18 +636,100 @@ void Combat::CombatFunc(Creature* caster, const Position& pos, const AreaCombat* const int32_t rangeX = maxX + Map::maxViewportX; const int32_t rangeY = maxY + Map::maxViewportY; - - SpectatorVec spectators; - g_game.map.getSpectators(spectators, pos, true, true, rangeX, rangeX, rangeY, rangeY); + g_game.map.getSpectators(list, pos, true, true, rangeX, rangeX, rangeY, rangeY); postCombatEffects(caster, pos, params); + uint16_t decreasedDamage = 0; + const uint16_t maximumDecreasedDamage = params.maximumDecreasedDamage; + + bool firstCreature = true; + + if (params.decreaseDamage && data) { + for (Tile* tile : tileList) { + if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) { + continue; + } + + if (CreatureVector* creatures = tile->getCreatures()) { + const Creature* topCreature = tile->getTopCreature(); + for (Creature* creature : *creatures) { + if (params.targetCasterOrTopMost) { + if (caster && caster->getTile() == tile) { + if (creature != caster) { + continue; + } + } else if (creature != topCreature) { + continue; + } + } + + if (!params.aggressive || (caster != creature && Combat::canDoCombat(caster, creature) == RETURNVALUE_NOERROR)) { + if (firstCreature) { + firstCreature = false; + continue; + } + + // only apply to players + if (creature->getPlayer()) { + if (maximumDecreasedDamage && decreasedDamage >= maximumDecreasedDamage) { + break; + } + + decreasedDamage += params.decreaseDamage; + } + } + } + } + } + + // actually decrease total damage output + if (data->value == 0) { + int32_t decreasedMinDamage = std::abs(data->min) * decreasedDamage / 100; + int32_t decreasedMaxDamage = std::abs(data->max) * decreasedDamage / 100; + + if (data->min < 0) { + // damaging spell, get as close as zero as we can get + // do not allow healing values + data->min += decreasedMinDamage; + data->max += decreasedMaxDamage; + + data->min = std::min(0, data->min); + data->max = std::min(0, data->max); + } else { + // healing spell, get as close as zero as we can get + // do not allow damaging values + data->min -= decreasedMinDamage; + data->max -= decreasedMaxDamage; + + data->min = std::max(0, data->min); + data->max = std::max(0, data->max); + } + } else { + int32_t decreasedValue = (std::abs(data->value) * decreasedDamage) / 100; + + if (data->value < 0) { + // damaging spell, get as close as zero as we can get + // do not allow healing values + data->value += decreasedValue; + + data->value = std::min(0, data->value); + } else { + // healing spell, get as close as zero as we can get + // do not allow damaging values + data->value -= decreasedValue; + + data->value = std::max(0, data->value); + } + } + } + for (Tile* tile : tileList) { if (canDoCombat(caster, tile, params.aggressive) != RETURNVALUE_NOERROR) { continue; } - combatTileEffects(spectators, caster, tile, params); + combatTileEffects(list, caster, tile, params); if (CreatureVector* creatures = tile->getCreatures()) { const Creature* topCreature = tile->getTopCreature(); @@ -749,8 +763,8 @@ void Combat::doCombat(Creature* caster, Creature* target) const { //target combat callback function if (params.combatType != COMBAT_NONE) { - CombatDamage damage = getCombatDamage(caster, target); - if (damage.primary.type != COMBAT_MANADRAIN) { + CombatDamage damage = getCombatDamage(caster); + if (damage.type != COMBAT_MANADRAIN) { doCombatHealth(caster, target, damage, params); } else { doCombatMana(caster, target, damage, params); @@ -764,8 +778,8 @@ void Combat::doCombat(Creature* caster, const Position& position) const { //area combat callback function if (params.combatType != COMBAT_NONE) { - CombatDamage damage = getCombatDamage(caster, nullptr); - if (damage.primary.type != COMBAT_MANADRAIN) { + CombatDamage damage = getCombatDamage(caster); + if (damage.type != COMBAT_MANADRAIN) { doCombatHealth(caster, position, area.get(), damage, params); } else { doCombatMana(caster, position, area.get(), damage, params); @@ -775,7 +789,501 @@ void Combat::doCombat(Creature* caster, const Position& position) const } } -void Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params) +int32_t Combat::computeDamage(Creature* creature, int32_t strength, int32_t variation) +{ + int32_t damage = strength; + if (variation) { + damage = normal_random(-variation, variation) + strength; + } + + if (creature) { + if (Player* player = creature->getPlayer()) { + int32_t formula = 3 * player->getMagicLevel() + 2 * player->getLevel(); + damage = formula * damage / 100; + } + } + + return damage; +} + +int32_t Combat::getTotalDamage(int32_t attackSkill, int32_t attackValue, fightMode_t fightMode) +{ + int32_t damage = attackValue; + + switch (fightMode) { + case FIGHTMODE_ATTACK: + damage += 2 * damage / 10; + break; + case FIGHTMODE_DEFENSE: + damage -= 4 * damage / 10; + break; + default: break; + } + + int32_t formula = (5 * (attackSkill) + 50) * damage; + int32_t randresult = rand() % 100; + int32_t totalDamage = -(ceil(formula * ((rand() % 100 + randresult) / 2) / 10000.)); + return totalDamage; +} + +bool Combat::attack(Creature* attacker, Creature* target) +{ + if (Player* player = attacker->getPlayer()) { + Item* weapon = player->getWeapon(); + if (weapon) { + if (weapon->getWeaponType() == WEAPON_DISTANCE || weapon->getWeaponType() == WEAPON_WAND) { + return rangeAttack(attacker, target, player->getFightMode()); + } + } + + return closeAttack(attacker, target, player->getFightMode()); + } + + return false; +} + +bool Combat::closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode) +{ + const Position& attackerPos = attacker->getPosition(); + const Position& targetPos = target->getPosition(); + if (attackerPos.z != targetPos.z) { + return false; + } + + if (std::max(Position::getDistanceX(attackerPos, targetPos), Position::getDistanceY(attackerPos, targetPos)) > 1) { + return false; + } + + Item* weapon = nullptr; + + if (Player* player = attacker->getPlayer()) { + weapon = player->getWeapon(); + if (weapon && !Combat::canUseWeapon(player, weapon)) { + return false; + } + } + + uint32_t attackValue = 0; + uint32_t skillValue = 0; + uint8_t skill = SKILL_FIST; + + Combat::getAttackValue(attacker, attackValue, skillValue, skill); + + int32_t defense = target->getDefense(); + + if (OTSYS_TIME() < target->earliestDefendTime) { + defense = 0; + } + + CombatParams combatParams; + combatParams.blockedByArmor = true; + combatParams.blockedByShield = true; + combatParams.combatType = COMBAT_PHYSICALDAMAGE; + + CombatDamage combatDamage; + combatDamage.type = combatParams.combatType; + int32_t totalDamage = Combat::getTotalDamage(skillValue, attackValue, fightMode); + combatDamage.value = totalDamage; + combatDamage.origin = ORIGIN_MELEE; + + bool hit = Combat::doCombatHealth(attacker, target, combatDamage, combatParams); + + if (Monster* monster = attacker->getMonster()) { + int32_t poison = monster->mType->info.poison; + if (poison) { + int32_t randTest = rand(); + + if (hit || ((-totalDamage > defense) && (randTest == 5 * (randTest / 5)))) { + poison = normal_random(poison / 2, poison); + if (poison) { + ConditionDamage* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_POISON, 0, 0)); + condition->setParam(CONDITION_PARAM_OWNER, attacker->getID()); + condition->setParam(CONDITION_PARAM_CYCLE, poison); + condition->setParam(CONDITION_PARAM_COUNT, 3); + condition->setParam(CONDITION_PARAM_MAX_COUNT, 3); + target->addCombatCondition(condition); + } + } + } + } + + if (Player* player = attacker->getPlayer()) { + // skills advancing + if (!player->hasFlag(PlayerFlag_NotGainSkill)) { + if (player->getAddAttackSkill() && player->getLastAttackBlockType() != BLOCK_IMMUNITY) { + player->addSkillAdvance(static_cast(skill), 1); + } + } + + // weapon + if (weapon) { + if (weapon->getCharges() > 0) { + int32_t charges = weapon->getCharges() - 1; + if (charges <= 0) { + g_game.internalRemoveItem(weapon); + } else { + g_game.transformItem(weapon, weapon->getID(), charges); + } + } + } + } + + if (Player* player = attacker->getPlayer()) { + Combat::postWeaponEffects(player, weapon); + } + + return true; +} + +bool Combat::rangeAttack(Creature* attacker, Creature* target, fightMode_t fightMode) +{ + const Position& attackerPos = attacker->getPosition(); + const Position& targetPos = target->getPosition(); + if (attackerPos.z != targetPos.z) { + return false; + } + + uint8_t range = 0; + uint8_t hitChance = 0; + uint8_t distanceEffect = 0; + uint8_t specialEffect = 0; + int32_t attackStrength = 0; + int32_t attackVariation = 0; + + Item* weapon = nullptr; + Item* ammunition = nullptr; + + bool moveWeapon = true; + + if (Player* player = attacker->getPlayer()) { + weapon = player->getWeapon(); + if (!weapon) { + return false; + } + + if (!Combat::canUseWeapon(player, weapon)) { + return false; + } + + range = weapon->getShootRange(); + distanceEffect = weapon->getMissileType(); + + if (weapon->getWeaponType() == WEAPON_DISTANCE) { + ammunition = player->getAmmunition(); + if (weapon->getAmmoType() != AMMO_NONE) { + if (!ammunition || weapon->getAmmoType() != ammunition->getAmmoType()) { + // redirect to fist fighting + return closeAttack(attacker, target, fightMode); + } + + distanceEffect = ammunition->getMissileType(); + } + } + } + + if (std::max(Position::getDistanceX(attackerPos, targetPos), Position::getDistanceY(attackerPos, targetPos)) > range) { + return false; + } + + if (weapon->getWeaponType() == WEAPON_DISTANCE) { + uint32_t attackValue = 0; + uint32_t skillValue = 0; + uint8_t skill = SKILL_FIST; + + Combat::getAttackValue(attacker, attackValue, skillValue, skill); + + CombatParams combatParams; + combatParams.blockedByArmor = true; + combatParams.blockedByShield = false; + combatParams.combatType = COMBAT_PHYSICALDAMAGE; + + CombatDamage combatDamage; + combatDamage.type = combatParams.combatType; + combatDamage.value = Combat::getTotalDamage(skillValue, attackValue, fightMode); + combatDamage.origin = ORIGIN_RANGED; + + if (weapon) { + hitChance = 75; // throwables and such + specialEffect = weapon->getWeaponSpecialEffect(); + attackStrength = weapon->getAttackStrength(); + attackVariation = weapon->getAttackVariation(); + if (weapon->getFragility()) { + if (normal_random(0, 99) <= weapon->getFragility()) { + uint16_t count = weapon->getItemCount(); + if (count > 1) { + g_game.transformItem(weapon, weapon->getID(), count - 1); + } else { + g_game.internalRemoveItem(weapon); + } + + moveWeapon = false; + } + } + } + + if (ammunition && weapon->getAmmoType() != AMMO_NONE && weapon->getAmmoType() == ammunition->getAmmoType()) { + hitChance = 90; // bows and crossbows + specialEffect = ammunition->getWeaponSpecialEffect(); + attackStrength = ammunition->getAttackStrength(); + attackVariation = ammunition->getAttackVariation(); + if (normal_random(0, 100) <= ammunition->getFragility()) { + uint16_t count = ammunition->getItemCount(); + if (count > 1) { + g_game.transformItem(ammunition, ammunition->getID(), count - 1); + } else { + g_game.internalRemoveItem(ammunition); + } + } + + moveWeapon = false; + } + + int32_t distance = std::max(Position::getDistanceX(attackerPos, targetPos), Position::getDistanceY(attackerPos, targetPos)); + if (distance <= 1) { + distance = 5; + } + + distance *= 15; + + bool hit = false; + + int32_t random = rand(); + if (random % distance <= static_cast(skillValue)) { + hit = random % 100 <= hitChance; + } + + + if (Player* player = attacker->getPlayer()) { + if (player->getAddAttackSkill()) { + switch (player->getLastAttackBlockType()) { + case BLOCK_NONE: { + player->addSkillAdvance(SKILL_DISTANCE, 2); + break; + } + + case BLOCK_DEFENSE: + case BLOCK_ARMOR: { + player->addSkillAdvance(SKILL_DISTANCE, 1); + break; + } + + default: break; + } + } + } + + if (specialEffect == 1) { + if (hit) { + const int32_t rounds = ammunition ? ammunition->getAttackStrength() : weapon->getAttackStrength(); + + ConditionDamage* poisonDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_POISON); + poisonDamage->setParam(CONDITION_PARAM_OWNER, attacker->getID()); + poisonDamage->setParam(CONDITION_PARAM_CYCLE, rounds); + poisonDamage->setParam(CONDITION_PARAM_COUNT, 3); + poisonDamage->setParam(CONDITION_PARAM_MAX_COUNT, 3); + + target->addCombatCondition(poisonDamage); + } + } else if (specialEffect == 2) { + DamageImpact impact; + impact.actor = attacker; + impact.damage.type = COMBAT_PHYSICALDAMAGE; + impact.damage.value = -Combat::computeDamage(attacker, attackStrength, attackVariation); + impact.params.blockedByArmor = true; + impact.params.blockedByShield = false; + circleShapeSpell(attacker, target->getPosition(), 0xFF, 0, 3, &impact, 7); + } + + if (!hit) { + Tile* destTile = target->getTile(); + + if (!Position::areInRange<1, 1, 0>(attacker->getPosition(), target->getPosition())) { + static std::vector> destList{ + { -1, -1 },{ 0, -1 },{ 1, -1 }, + { -1, 0 },{ 0, 0 },{ 1, 0 }, + { -1, 1 },{ 0, 1 },{ 1, 1 } + }; + std::shuffle(destList.begin(), destList.end(), getRandomGenerator()); + + Position destPos = target->getPosition(); + + for (const auto& dir : destList) { + // Blocking tiles or tiles without ground ain't valid targets for spears + Tile* tmpTile = g_game.map.getTile(destPos.x + dir.first, destPos.y + dir.second, destPos.z); + if (tmpTile && !tmpTile->hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID) && tmpTile->getGround() != nullptr) { + destTile = tmpTile; + break; + } + } + } + + g_game.addMagicEffect(destTile->getPosition(), CONST_ME_POFF); + g_game.addDistanceEffect(attackerPos, destTile->getPosition(), distanceEffect); + + if (moveWeapon) { + g_game.internalMoveItem(weapon->getParent(), destTile, INDEX_WHEREEVER, weapon, 1, nullptr, FLAG_NOLIMIT); + } + + return true; + } + + g_game.addDistanceEffect(attackerPos, targetPos, distanceEffect); + Combat::doCombatHealth(attacker, target, combatDamage, combatParams); + + if (moveWeapon) { + g_game.internalMoveItem(weapon->getParent(), target->getTile(), INDEX_WHEREEVER, weapon, 1, nullptr, FLAG_NOLIMIT); + } + } else if (weapon->getWeaponType() == WEAPON_WAND) { + int32_t variation = normal_random(-weapon->getAttackVariation(), weapon->getAttackVariation()); + + CombatParams combatParams; + combatParams.combatType = weapon->getDamageType(); + + CombatDamage combatDamage; + combatDamage.type = combatParams.combatType; + combatDamage.value = -(variation + weapon->getAttackStrength()); + combatDamage.origin = ORIGIN_RANGED; + + g_game.addDistanceEffect(attackerPos, targetPos, distanceEffect); + Combat::doCombatHealth(attacker, target, combatDamage, combatParams); + } + + if (Player* player = attacker->getPlayer()) { + Combat::postWeaponEffects(player, weapon); + } + + return true; +} + +void Combat::circleShapeSpell(Creature* attacker, const Position& toPos, int32_t range, int32_t animation, int32_t radius, DamageImpact* impact, int32_t effect) +{ + const Position& fromPos = attacker->getPosition(); + if (fromPos.z != toPos.z) { + return; + } + + int32_t distance = std::max(Position::getDistanceX(fromPos, toPos), Position::getDistanceY(fromPos, toPos)); + if (distance > range) { + return; + } + + if (animation && fromPos != toPos) { + g_game.addDistanceEffect(fromPos, toPos, animation); + } + + std::forward_list tiles; + + AreaCombat areaCombat; + areaCombat.setupArea(radius); + + areaCombat.getList(toPos, toPos, tiles); + + for (Tile* tile : tiles) { + if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) { + continue; + } + + if (CreatureVector* creatures = tile->getCreatures()) { + for (Creature* creature : *creatures) { + impact->handleCreature(creature); + } + } + + if (effect) { + g_game.addMagicEffect(tile->getPosition(), effect); + } + } +} + +void Combat::getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill) +{ + skill = SKILL_FIST; + + if (Player* player = creature->getPlayer()) { + Item* weapon = player->getWeapon(); + if (weapon) { + switch (weapon->getWeaponType()) { + case WEAPON_AXE: { + skill = SKILL_AXE; + attackValue = weapon->getAttack(); + break; + } + case WEAPON_SWORD: { + skill = SKILL_SWORD; + attackValue = weapon->getAttack(); + break; + } + case WEAPON_CLUB: { + skill = SKILL_CLUB; + attackValue = weapon->getAttack(); + break; + } + case WEAPON_DISTANCE: { + skill = SKILL_DISTANCE; + attackValue = weapon->getAttack(); + + if (weapon->getAmmoType() != AMMO_NONE) { + Item* ammunition = player->getAmmunition(); + if (ammunition && ammunition->getAmmoType() == weapon->getAmmoType()) { + attackValue += ammunition->getAttack(); + } + } + break; + } + default: + attackValue = 7; + break; + } + + skillValue = player->getSkillLevel(skill); + } else { + attackValue = 7; + skillValue = player->getSkillLevel(skill); + } + } else if (Monster* monster = creature->getMonster()) { + attackValue = monster->mType->info.attack; + skillValue = monster->mType->info.skill; + } +} + +bool Combat::canUseWeapon(Player* player, Item* weapon) +{ + if (player->hasFlag(PlayerFlag_IgnoreWeaponCheck)) { + return true; + } + + if (player->getLevel() < weapon->getMinimumLevel()) { + return false; + } + + if (!player->hasFlag(PlayerFlag_HasInfiniteMana) && static_cast(player->getMana()) < weapon->getManaConsumption()) { + return false; + } + + const ItemType& itemType = Item::items[weapon->getID()]; + if (hasBitSet(WIELDINFO_VOCREQ, itemType.wieldInfo)) { + if (!hasBitSet(player->getVocationFlagId(), itemType.vocations)) { + return false; + } + } + + return true; +} + +void Combat::postWeaponEffects(Player* player, Item* weapon) +{ + if (!weapon || player->hasFlag(PlayerFlag_IgnoreWeaponCheck)) { + return; + } + + int32_t manaConsumption = weapon->getManaConsumption(); + if (manaConsumption) { + player->addManaSpent(manaConsumption); + player->changeMana(-manaConsumption); + } +} + +bool Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params) { bool canCombat = !params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR); if ((caster == target || canCombat) && params.impactEffect != CONST_ME_NONE) { @@ -784,7 +1292,7 @@ void Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& da if (canCombat) { if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); } CombatHealthFunc(caster, target, params, &damage); @@ -792,6 +1300,8 @@ void Combat::doCombatHealth(Creature* caster, Creature* target, CombatDamage& da params.targetCallback->onTargetCombat(caster, target); } } + + return canCombat; } void Combat::doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params) @@ -808,7 +1318,7 @@ void Combat::doCombatMana(Creature* caster, Creature* target, CombatDamage& dama if (canCombat) { if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); } CombatManaFunc(caster, target, params, &damage); @@ -837,7 +1347,7 @@ void Combat::doCombatCondition(Creature* caster, Creature* target, const CombatP if (canCombat) { if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); } CombatConditionFunc(caster, target, params, nullptr); @@ -866,7 +1376,7 @@ void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatPara } if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); } } } @@ -874,11 +1384,11 @@ void Combat::doCombatDispel(Creature* caster, Creature* target, const CombatPara void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatParams& params) { if (!params.aggressive || (caster != target && Combat::canDoCombat(caster, target) == RETURNVALUE_NOERROR)) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, target->getPosition(), true, true); + SpectatorVec list; + g_game.map.getSpectators(list, target->getPosition(), true, true); CombatNullFunc(caster, target, params, nullptr); - combatTileEffects(spectators, caster, target->getTile(), params); + combatTileEffects(list, caster, target->getTile(), params); if (params.targetCallback) { params.targetCallback->onTargetCombat(caster, target); @@ -891,7 +1401,7 @@ void Combat::doCombatDefault(Creature* caster, Creature* target, const CombatPar */ if (caster && params.distanceEffect != CONST_ANI_NONE) { - addDistanceEffect(caster, caster->getPosition(), target->getPosition(), params.distanceEffect); + addDistanceEffect(caster->getPosition(), target->getPosition(), params.distanceEffect); } } } @@ -930,33 +1440,29 @@ void ValueCallback::getMinMaxValues(Player* player, CombatDamage& damage, bool u } case COMBAT_FORMULA_SKILL: { - //onGetPlayerMinMaxValues(player, attackSkill, attackValue, attackFactor) - Item* tool = player->getWeapon(); - const Weapon* weapon = g_weapons->getWeapon(tool); + //onGetPlayerMinMaxValues(player, attackSkill, attackValue, fightMode) + uint32_t attackValue = 7; + uint32_t attackSkill = 0; + uint8_t skill = 0; - int32_t attackValue = 7; - if (weapon) { - attackValue = tool->getAttack(); - if (tool->getWeaponType() == WEAPON_AMMO) { - Item* item = player->getWeapon(true); - if (item) { - attackValue += item->getAttack(); - } - } + Combat::getAttackValue(player, attackValue, attackSkill, skill); - damage.secondary.type = weapon->getElementType(); - damage.secondary.value = weapon->getElementDamage(player, nullptr, tool); - if (useCharges) { - uint16_t charges = tool->getCharges(); - if (charges != 0) { - g_game.transformItem(tool, tool->getID(), charges - 1); + Item* weapon = player->getWeapon(); + if (useCharges && weapon) { + const ItemType& itemType = Item::items.getItemType(weapon->getID()); + if (itemType.charges) { + int32_t newCount = std::max(0, weapon->getCharges() - 1); + if (newCount <= 0) { + g_game.internalRemoveItem(weapon); + } else { + g_game.transformItem(weapon, weapon->getID(), newCount); } } } - lua_pushnumber(L, player->getWeaponSkill(tool)); + lua_pushnumber(L, attackSkill); lua_pushnumber(L, attackValue); - lua_pushnumber(L, player->getAttackFactor()); + lua_pushnumber(L, player->getFightMode()); parameters += 3; break; } @@ -972,10 +1478,8 @@ void ValueCallback::getMinMaxValues(Player* player, CombatDamage& damage, bool u if (lua_pcall(L, parameters, 2, 0) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); } else { - damage.primary.value = normal_random( - LuaScriptInterface::getNumber(L, -2), - LuaScriptInterface::getNumber(L, -1) - ); + damage.min = LuaScriptInterface::getNumber(L, -2); + damage.max = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 2); } @@ -1112,7 +1616,7 @@ void AreaCombat::getList(const Position& centerPos, const Position& targetPos, s } } -void AreaCombat::copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) +void AreaCombat::copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const { uint32_t centerY, centerX; input->getCenter(centerY, centerX); @@ -1238,17 +1742,17 @@ void AreaCombat::setupArea(const std::list& list, uint32_t rows) //SOUTH MatrixArea* southArea = new MatrixArea(maxOutput, maxOutput); - AreaCombat::copyArea(area, southArea, MATRIXOPERATION_ROTATE180); + copyArea(area, southArea, MATRIXOPERATION_ROTATE180); areas[DIRECTION_SOUTH] = southArea; //EAST MatrixArea* eastArea = new MatrixArea(maxOutput, maxOutput); - AreaCombat::copyArea(area, eastArea, MATRIXOPERATION_ROTATE90); + copyArea(area, eastArea, MATRIXOPERATION_ROTATE90); areas[DIRECTION_EAST] = eastArea; //WEST MatrixArea* westArea = new MatrixArea(maxOutput, maxOutput); - AreaCombat::copyArea(area, westArea, MATRIXOPERATION_ROTATE270); + copyArea(area, westArea, MATRIXOPERATION_ROTATE270); areas[DIRECTION_WEST] = westArea; } @@ -1338,17 +1842,17 @@ void AreaCombat::setupExtArea(const std::list& list, uint32_t rows) //NORTH-EAST MatrixArea* neArea = new MatrixArea(maxOutput, maxOutput); - AreaCombat::copyArea(area, neArea, MATRIXOPERATION_MIRROR); + copyArea(area, neArea, MATRIXOPERATION_MIRROR); areas[DIRECTION_NORTHEAST] = neArea; //SOUTH-WEST MatrixArea* swArea = new MatrixArea(maxOutput, maxOutput); - AreaCombat::copyArea(area, swArea, MATRIXOPERATION_FLIP); + copyArea(area, swArea, MATRIXOPERATION_FLIP); areas[DIRECTION_SOUTHWEST] = swArea; //SOUTH-EAST MatrixArea* seArea = new MatrixArea(maxOutput, maxOutput); - AreaCombat::copyArea(swArea, seArea, MATRIXOPERATION_MIRROR); + copyArea(swArea, seArea, MATRIXOPERATION_MIRROR); areas[DIRECTION_SOUTHEAST] = seArea; } @@ -1357,7 +1861,7 @@ void AreaCombat::setupExtArea(const std::list& list, uint32_t rows) void MagicField::onStepInField(Creature* creature) { //remove magic walls/wild growth - if (id == ITEM_MAGICWALL || id == ITEM_WILDGROWTH || id == ITEM_MAGICWALL_SAFE || id == ITEM_WILDGROWTH_SAFE || isBlocking()) { + if (id == ITEM_MAGICWALL || id == ITEM_WILDGROWTH || isBlocking()) { if (!creature->isInGhostMode()) { g_game.internalRemoveItem(this, 1); } @@ -1399,3 +1903,26 @@ void MagicField::onStepInField(Creature* creature) creature->addCondition(conditionCopy); } } + +void DamageImpact::handleCreature(Creature* target) +{ + Combat::doCombatHealth(actor, target, damage, params); +} + +void SpeedImpact::handleCreature(Creature* target) +{ + ConditionType_t conditionType = CONDITION_PARALYZE; + if (percent > 0) { + conditionType = CONDITION_HASTE; + } + + ConditionSpeed* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, conditionType, duration)); + condition->setSpeedDelta(percent); + target->addCondition(condition); +} + +void DunkenImpact::handleCreature(Creature* target) +{ + Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_DRUNK, duration); + target->addCondition(condition); +} diff --git a/src/combat.h b/src/combat.h index c6b4dff..a6b6b70 100644 --- a/src/combat.h +++ b/src/combat.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,7 +38,7 @@ class ValueCallback final : public CallBack explicit ValueCallback(formulaType_t type): type(type) {} void getMinMaxValues(Player* player, CombatDamage& damage, bool useCharges) const; - private: + protected: formulaType_t type; }; @@ -46,12 +46,18 @@ class TileCallback final : public CallBack { public: void onTileCombat(Creature* creature, Tile* tile) const; + + protected: + formulaType_t type; }; class TargetCallback final : public CallBack { public: void onTargetCombat(Creature* creature, Creature* target) const; + + protected: + formulaType_t type; }; struct CombatParams { @@ -62,6 +68,8 @@ struct CombatParams { std::unique_ptr targetCallback; uint16_t itemId = 0; + uint16_t decreaseDamage = 0; + uint16_t maximumDecreasedDamage = 0; ConditionType_t dispelType = CONDITION_NONE; CombatType_t combatType = COMBAT_NONE; @@ -77,7 +85,39 @@ struct CombatParams { bool useCharges = false; }; -using CombatFunction = std::function; +struct Impact +{ + Creature* actor = nullptr; + + virtual void handleCreature(Creature*) { + // + } +}; + +struct DamageImpact : Impact +{ + CombatParams params; + CombatDamage damage; + + void handleCreature(Creature* target) final; +}; + +struct SpeedImpact : Impact +{ + int32_t percent = 0; + int32_t duration = 0; + + void handleCreature(Creature* target) final; +}; + +struct DunkenImpact : Impact +{ + int32_t duration = 0; + + void handleCreature(Creature* target) final; +}; + +typedef void(*COMBATFUNC)(Creature*, Creature*, const CombatParams&, CombatDamage*); class MatrixArea { @@ -145,14 +185,14 @@ class MatrixArea return cols; } - const bool* operator[](uint32_t i) const { + inline const bool* operator[](uint32_t i) const { return data_[i]; } - bool* operator[](uint32_t i) { + inline bool* operator[](uint32_t i) { return data_[i]; } - private: + protected: uint32_t centerX; uint32_t centerY; @@ -182,7 +222,7 @@ class AreaCombat void setupExtArea(const std::list& list, uint32_t rows); void clear(); - private: + protected: enum MatrixOperation_t { MATRIXOPERATION_COPY, MATRIXOPERATION_MIRROR, @@ -193,7 +233,7 @@ class AreaCombat }; MatrixArea* createArea(const std::list& list, uint32_t rows); - static void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op); + void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const; MatrixArea* getArea(const Position& centerPos, const Position& targetPos) const { int32_t dx = Position::getOffsetX(targetPos, centerPos); @@ -242,7 +282,18 @@ class Combat Combat(const Combat&) = delete; Combat& operator=(const Combat&) = delete; - static void doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); + static int32_t computeDamage(Creature* creature, int32_t strength, int32_t variation); + static int32_t getTotalDamage(int32_t attackSkill, int32_t attackValue, fightMode_t fightMode); + + static bool attack(Creature* attacker, Creature* target); + static bool closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode); + static bool rangeAttack(Creature* attacker, Creature* target, fightMode_t fightMode); + + static void circleShapeSpell(Creature* attacker, const Position& toPos, int32_t range, int32_t animation, int32_t radius, DamageImpact* impact, int32_t effect); + + static void getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill); + + static bool doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); static void doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params); static void doCombatMana(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params); @@ -266,10 +317,10 @@ class Combat static ReturnValue canDoCombat(Creature* attacker, Creature* target); static void postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params); - static void addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, uint8_t effect); + static void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect); void doCombat(Creature* caster, Creature* target) const; - void doCombat(Creature* caster, const Position& position) const; + void doCombat(Creature* caster, const Position& pos) const; bool setCallback(CallBackParam_t key); CallBack* getCallback(CallBackParam_t key); @@ -281,12 +332,9 @@ class Combat bool hasArea() const { return area != nullptr; } - void addCondition(const Condition* condition) { + void setCondition(const Condition* condition) { params.conditionList.emplace_front(condition); } - void clearConditions() { - params.conditionList.clear(); - } void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb); void postCombatEffects(Creature* caster, const Position& pos) const { postCombatEffects(caster, pos, params); @@ -296,10 +344,13 @@ class Combat params.origin = origin; } - private: + protected: + static bool canUseWeapon(Player* player, Item* weapon); + static void postWeaponEffects(Player* player, Item* weapon); + static void doCombatDefault(Creature* caster, Creature* target, const CombatParams& params); - static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, CombatFunction func, CombatDamage* data); + static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, COMBATFUNC func, CombatDamage* data); static void CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); static void CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage); @@ -307,8 +358,8 @@ class Combat static void CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); static void CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data); - static void combatTileEffects(const SpectatorVec& spectators, Creature* caster, Tile* tile, const CombatParams& params); - CombatDamage getCombatDamage(Creature* creature, Creature* target) const; + static void combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params); + CombatDamage getCombatDamage(Creature* creature) const; //configureable CombatParams params; @@ -328,10 +379,10 @@ class MagicField final : public Item public: explicit MagicField(uint16_t type) : Item(type), createTime(OTSYS_TIME()) {} - MagicField* getMagicField() override { + MagicField* getMagicField() final { return this; } - const MagicField* getMagicField() const override { + const MagicField* getMagicField() const final { return this; } @@ -342,13 +393,6 @@ class MagicField final : public Item const ItemType& it = items[getID()]; return it.combatType; } - int32_t getDamage() const { - const ItemType& it = items[getID()]; - if (it.conditionDamage) { - return it.conditionDamage->getTotalDamage(); - } - return 0; - } void onStepInField(Creature* creature); private: diff --git a/src/condition.cpp b/src/condition.cpp index 8862e96..7487e6a 100644 --- a/src/condition.cpp +++ b/src/condition.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,11 +32,6 @@ bool Condition::setParam(ConditionParam_t param, int32_t value) return true; } - case CONDITION_PARAM_BUFF_SPELL: { - isBuff = (value != 0); - return true; - } - case CONDITION_PARAM_SUBID: { subId = value; return true; @@ -86,16 +81,6 @@ bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream) return propStream.read(ticks); } - case CONDITIONATTR_ISBUFF: { - uint8_t value; - if (!propStream.read(value)) { - return false; - } - - isBuff = (value != 0); - return true; - } - case CONDITIONATTR_SUBID: { return propStream.read(subId); } @@ -119,9 +104,6 @@ void Condition::serialize(PropWriteStream& propWriteStream) propWriteStream.write(CONDITIONATTR_TICKS); propWriteStream.write(ticks); - propWriteStream.write(CONDITIONATTR_ISBUFF); - propWriteStream.write(isBuff); - propWriteStream.write(CONDITIONATTR_SUBID); propWriteStream.write(subId); } @@ -143,58 +125,47 @@ bool Condition::executeCondition(Creature*, int32_t interval) return getEndTime() >= OTSYS_TIME(); } -Condition* Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param/* = 0*/, bool buff/* = false*/, uint32_t subId/* = 0*/) +Condition* Condition::createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param/* = 0*/, uint32_t subId/* = 0*/) { switch (type) { case CONDITION_POISON: case CONDITION_FIRE: case CONDITION_ENERGY: case CONDITION_DROWN: - case CONDITION_FREEZING: - case CONDITION_DAZZLED: - case CONDITION_CURSED: - case CONDITION_BLEEDING: - return new ConditionDamage(id, type, buff, subId); + return new ConditionDamage(id, type, subId); case CONDITION_HASTE: case CONDITION_PARALYZE: - return new ConditionSpeed(id, type, ticks, buff, subId, param); + return new ConditionSpeed(id, type, ticks, subId, param); case CONDITION_INVISIBLE: - return new ConditionInvisible(id, type, ticks, buff, subId); + return new ConditionInvisible(id, type, ticks, subId); case CONDITION_OUTFIT: - return new ConditionOutfit(id, type, ticks, buff, subId); + return new ConditionOutfit(id, type, ticks, subId); case CONDITION_LIGHT: - return new ConditionLight(id, type, ticks, buff, subId, param & 0xFF, (param & 0xFF00) >> 8); + return new ConditionLight(id, type, ticks, subId, param & 0xFF, (param & 0xFF00) >> 8); case CONDITION_REGENERATION: - return new ConditionRegeneration(id, type, ticks, buff, subId); + return new ConditionRegeneration(id, type, ticks, subId); case CONDITION_SOUL: - return new ConditionSoul(id, type, ticks, buff, subId); + return new ConditionSoul(id, type, ticks, subId); case CONDITION_ATTRIBUTES: - return new ConditionAttributes(id, type, ticks, buff, subId); - - case CONDITION_SPELLCOOLDOWN: - return new ConditionSpellCooldown(id, type, ticks, buff, subId); - - case CONDITION_SPELLGROUPCOOLDOWN: - return new ConditionSpellGroupCooldown(id, type, ticks, buff, subId); + return new ConditionAttributes(id, type, ticks, subId); case CONDITION_INFIGHT: case CONDITION_DRUNK: - case CONDITION_EXHAUST_WEAPON: - case CONDITION_EXHAUST_COMBAT: - case CONDITION_EXHAUST_HEAL: + case CONDITION_EXHAUST: case CONDITION_MUTED: case CONDITION_CHANNELMUTEDTICKS: case CONDITION_YELLTICKS: case CONDITION_PACIFIED: case CONDITION_MANASHIELD: - return new ConditionGeneric(id, type, ticks, buff, subId); + case CONDITION_AGGRESSIVE: + return new ConditionGeneric(id, type, ticks, subId); default: return nullptr; @@ -231,15 +202,6 @@ Condition* Condition::createCondition(PropStream& propStream) return nullptr; } - if (!propStream.read(attr) || attr != CONDITIONATTR_ISBUFF) { - return nullptr; - } - - uint8_t buff; - if (!propStream.read(buff)) { - return nullptr; - } - if (!propStream.read(attr) || attr != CONDITIONATTR_SUBID) { return nullptr; } @@ -249,7 +211,7 @@ Condition* Condition::createCondition(PropStream& propStream) return nullptr; } - return createCondition(static_cast(id), static_cast(type), ticks, 0, buff != 0, subId); + return createCondition(static_cast(id), static_cast(type), ticks, 0, subId); } bool Condition::startCondition(Creature*) @@ -275,7 +237,7 @@ bool Condition::isPersistent() const uint32_t Condition::getIcons() const { - return isBuff ? ICON_PARTY_BUFF : 0; + return 0; } bool Condition::updateCondition(const Condition* addCondition) @@ -310,10 +272,10 @@ void ConditionGeneric::endCondition(Creature*) // } -void ConditionGeneric::addCondition(Creature*, const Condition* condition) +void ConditionGeneric::addCondition(Creature*, const Condition* addCondition) { - if (updateCondition(condition)) { - setTicks(condition->getTicks()); + if (updateCondition(addCondition)) { + setTicks(addCondition->getTicks()); } } @@ -341,22 +303,20 @@ uint32_t ConditionGeneric::getIcons() const return icons; } -void ConditionAttributes::addCondition(Creature* creature, const Condition* condition) +void ConditionAttributes::addCondition(Creature* creature, const Condition* addCondition) { - if (updateCondition(condition)) { - setTicks(condition->getTicks()); + if (updateCondition(addCondition)) { + setTicks(addCondition->getTicks()); - const ConditionAttributes& conditionAttrs = static_cast(*condition); + const ConditionAttributes& conditionAttrs = static_cast(*addCondition); //Remove the old condition endCondition(creature); //Apply the new one memcpy(skills, conditionAttrs.skills, sizeof(skills)); - memcpy(specialSkills, conditionAttrs.specialSkills, sizeof(specialSkills)); memcpy(skillsPercent, conditionAttrs.skillsPercent, sizeof(skillsPercent)); memcpy(stats, conditionAttrs.stats, sizeof(stats)); memcpy(statsPercent, conditionAttrs.statsPercent, sizeof(statsPercent)); - disableDefense = conditionAttrs.disableDefense; if (Player* player = creature->getPlayer()) { updatePercentSkills(player); @@ -398,8 +358,6 @@ bool ConditionAttributes::startCondition(Creature* creature) return false; } - creature->setUseDefense(!disableDefense); - if (Player* player = creature->getPlayer()) { updatePercentSkills(player); updateSkills(player); @@ -427,7 +385,7 @@ void ConditionAttributes::updatePercentStats(Player* player) break; case STAT_MAGICPOINTS: - stats[i] = static_cast(player->getBaseMagicLevel() * ((statsPercent[i] - 100) / 100.f)); + stats[i] = static_cast(player->getMagicLevel() * ((statsPercent[i] - 100) / 100.f)); break; } } @@ -472,13 +430,6 @@ void ConditionAttributes::updateSkills(Player* player) } } - for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { - if (specialSkills[i]) { - needUpdateSkills = true; - player->setVarSpecialSkill(static_cast(i), specialSkills[i]); - } - } - if (needUpdateSkills) { player->sendSkills(); } @@ -502,13 +453,6 @@ void ConditionAttributes::endCondition(Creature* creature) } } - for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { - if (specialSkills[i]) { - needUpdateSkills = true; - player->setVarSpecialSkill(static_cast(i), -specialSkills[i]); - } - } - if (needUpdateSkills) { player->sendSkills(); } @@ -526,10 +470,6 @@ void ConditionAttributes::endCondition(Creature* creature) player->sendStats(); } } - - if (disableDefense) { - creature->setUseDefense(true); - } } bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value) @@ -651,52 +591,17 @@ bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value) return true; } - case CONDITION_PARAM_DISABLE_DEFENSE: { - disableDefense = (value != 0); - return true; - } - - case CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE: { - specialSkills[SPECIALSKILL_CRITICALHITCHANCE] = value; - return true; - } - - case CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT: { - specialSkills[SPECIALSKILL_CRITICALHITAMOUNT] = value; - return true; - } - - case CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE: { - specialSkills[SPECIALSKILL_LIFELEECHCHANCE] = value; - return true; - } - - case CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT: { - specialSkills[SPECIALSKILL_LIFELEECHAMOUNT] = value; - return true; - } - - case CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE: { - specialSkills[SPECIALSKILL_MANALEECHCHANCE] = value; - return true; - } - - case CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT: { - specialSkills[SPECIALSKILL_MANALEECHAMOUNT] = value; - return true; - } - default: return ret; } } -void ConditionRegeneration::addCondition(Creature*, const Condition* condition) +void ConditionRegeneration::addCondition(Creature*, const Condition* addCondition) { - if (updateCondition(condition)) { - setTicks(condition->getTicks()); + if (updateCondition(addCondition)) { + setTicks(addCondition->getTicks()); - const ConditionRegeneration& conditionRegen = static_cast(*condition); + const ConditionRegeneration& conditionRegen = static_cast(*addCondition); healthTicks = conditionRegen.healthTicks; manaTicks = conditionRegen.manaTicks; @@ -742,69 +647,17 @@ bool ConditionRegeneration::executeCondition(Creature* creature, int32_t interva internalHealthTicks += interval; internalManaTicks += interval; - if (creature->getZone() == ZONE_PROTECTION) { - return ConditionGeneric::executeCondition(creature, interval); - } + if (creature->getZone() != ZONE_PROTECTION) { + if (internalHealthTicks >= healthTicks) { + internalHealthTicks = 0; - if (internalHealthTicks >= healthTicks) { - internalHealthTicks = 0; - - int32_t realHealthGain = creature->getHealth(); - creature->changeHealth(healthGain); - realHealthGain = creature->getHealth() - realHealthGain; - - if (isBuff && realHealthGain > 0) { - Player* player = creature->getPlayer(); - if (player) { - std::string healString = std::to_string(realHealthGain) + (realHealthGain != 1 ? " hitpoints." : " hitpoint."); - - TextMessage message(MESSAGE_HEALED, "You were healed for " + healString); - message.position = player->getPosition(); - message.primary.value = realHealthGain; - message.primary.color = TEXTCOLOR_MAYABLUE; - player->sendTextMessage(message); - - SpectatorVec spectators; - g_game.map.getSpectators(spectators, player->getPosition(), false, true); - spectators.erase(player); - if (!spectators.empty()) { - message.type = MESSAGE_HEALED_OTHERS; - message.text = player->getName() + " was healed for " + healString; - for (Creature* spectator : spectators) { - spectator->getPlayer()->sendTextMessage(message); - } - } - } + creature->changeHealth(healthGain); } - } - if (internalManaTicks >= manaTicks) { - internalManaTicks = 0; - - if (Player* player = creature->getPlayer()) { - int32_t realManaGain = player->getMana(); - player->changeMana(manaGain); - realManaGain = player->getMana() - realManaGain; - - if (isBuff && realManaGain > 0) { - std::string manaGainString = std::to_string(realManaGain); - - TextMessage message(MESSAGE_HEALED, "You gained " + manaGainString + " mana."); - message.position = player->getPosition(); - message.primary.value = realManaGain; - message.primary.color = TEXTCOLOR_MAYABLUE; - player->sendTextMessage(message); - - SpectatorVec spectators; - g_game.map.getSpectators(spectators, player->getPosition(), false, true); - spectators.erase(player); - if (!spectators.empty()) { - message.type = MESSAGE_HEALED_OTHERS; - message.text = player->getName() + " gained " + manaGainString + " mana."; - for (Creature* spectator : spectators) { - spectator->getPlayer()->sendTextMessage(message); - } - } + if (internalManaTicks >= manaTicks) { + internalManaTicks = 0; + if (Player* player = creature->getPlayer()) { + player->changeMana(manaGain); } } } @@ -838,12 +691,12 @@ bool ConditionRegeneration::setParam(ConditionParam_t param, int32_t value) } } -void ConditionSoul::addCondition(Creature*, const Condition* condition) +void ConditionSoul::addCondition(Creature*, const Condition* addCondition) { - if (updateCondition(condition)) { - setTicks(condition->getTicks()); + if (updateCondition(addCondition)) { + setTicks(addCondition->getTicks()); - const ConditionSoul& conditionSoul = static_cast(*condition); + const ConditionSoul& conditionSoul = static_cast(*addCondition); soulTicks = conditionSoul.soulTicks; soulGain = conditionSoul.soulGain; @@ -906,78 +759,62 @@ bool ConditionSoul::setParam(ConditionParam_t param, int32_t value) bool ConditionDamage::setParam(ConditionParam_t param, int32_t value) { - bool ret = Condition::setParam(param, value); - switch (param) { case CONDITION_PARAM_OWNER: owner = value; return true; - case CONDITION_PARAM_FORCEUPDATE: - forceUpdate = (value != 0); + case CONDITION_PARAM_CYCLE: + cycle = value; return true; - case CONDITION_PARAM_DELAYED: - delayed = (value != 0); + case CONDITION_PARAM_COUNT: + count = value; return true; - case CONDITION_PARAM_MAXVALUE: - maxDamage = std::abs(value); - break; + case CONDITION_PARAM_MAX_COUNT: + max_count = value; + return true; - case CONDITION_PARAM_MINVALUE: - minDamage = std::abs(value); - break; - - case CONDITION_PARAM_STARTVALUE: - startDamage = std::abs(value); - break; - - case CONDITION_PARAM_TICKINTERVAL: - tickInterval = std::abs(value); - break; - - case CONDITION_PARAM_PERIODICDAMAGE: - periodDamage = value; - break; - - case CONDITION_PARAM_FIELD: - field = (value != 0); - break; + case CONDITION_PARAM_HIT_DAMAGE: + hit_damage = value; + return true; default: return false; } - - return ret; } bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { - if (attr == CONDITIONATTR_DELAYED) { - uint8_t value; - if (!propStream.read(value)) { - return false; - } - - delayed = (value != 0); - return true; - } else if (attr == CONDITIONATTR_PERIODDAMAGE) { - return propStream.read(periodDamage); - } else if (attr == CONDITIONATTR_OWNER) { + if (attr == CONDITIONATTR_OWNER) { return propStream.skip(4); - } else if (attr == CONDITIONATTR_INTERVALDATA) { - IntervalInfo damageInfo; - if (!propStream.read(damageInfo)) { + } else if (attr == CONDITIONATTR_CYCLE) { + if (!propStream.read(cycle)) { return false; } - damageList.push_back(damageInfo); - if (ticks != -1) { - setTicks(ticks + damageInfo.interval); + return true; + } else if (attr == CONDITIONATTR_COUNT) { + if (!propStream.read(count)) { + return false; } + + return true; + } else if (attr == CONDITIONATTR_MAX_COUNT) { + if (!propStream.read(max_count)) { + return false; + } + + return true; + } else if (attr == CONDITIONATTR_FACTOR_PERCENT) { + if (!propStream.read(factor_percent)) { + return false; + } + return true; } + return Condition::unserializeProp(attr, propStream); } @@ -985,91 +822,23 @@ void ConditionDamage::serialize(PropWriteStream& propWriteStream) { Condition::serialize(propWriteStream); - propWriteStream.write(CONDITIONATTR_DELAYED); - propWriteStream.write(delayed); + propWriteStream.write(CONDITIONATTR_CYCLE); + propWriteStream.write(cycle); - propWriteStream.write(CONDITIONATTR_PERIODDAMAGE); - propWriteStream.write(periodDamage); + propWriteStream.write(CONDITIONATTR_COUNT); + propWriteStream.write(count); - for (const IntervalInfo& intervalInfo : damageList) { - propWriteStream.write(CONDITIONATTR_INTERVALDATA); - propWriteStream.write(intervalInfo); - } + propWriteStream.write(CONDITIONATTR_MAX_COUNT); + propWriteStream.write(max_count); + + propWriteStream.write(CONDITIONATTR_FACTOR_PERCENT); + propWriteStream.write(factor_percent); } bool ConditionDamage::updateCondition(const Condition* addCondition) { const ConditionDamage& conditionDamage = static_cast(*addCondition); - if (conditionDamage.doForceUpdate()) { - return true; - } - - if (ticks == -1 && conditionDamage.ticks > 0) { - return false; - } - - return conditionDamage.getTotalDamage() > getTotalDamage(); -} - -bool ConditionDamage::addDamage(int32_t rounds, int32_t time, int32_t value) -{ - time = std::max(time, EVENT_CREATURE_THINK_INTERVAL); - if (rounds == -1) { - //periodic damage - periodDamage = value; - setParam(CONDITION_PARAM_TICKINTERVAL, time); - setParam(CONDITION_PARAM_TICKS, -1); - return true; - } - - if (periodDamage > 0) { - return false; - } - - //rounds, time, damage - for (int32_t i = 0; i < rounds; ++i) { - IntervalInfo damageInfo; - damageInfo.interval = time; - damageInfo.timeLeft = time; - damageInfo.value = value; - - damageList.push_back(damageInfo); - - if (ticks != -1) { - setTicks(ticks + damageInfo.interval); - } - } - - return true; -} - -bool ConditionDamage::init() -{ - if (periodDamage != 0) { - return true; - } - - if (!damageList.empty()) { - return true; - } - - setTicks(0); - - int32_t amount = uniform_random(minDamage, maxDamage); - if (amount != 0) { - if (startDamage > maxDamage) { - startDamage = maxDamage; - } else if (startDamage == 0) { - startDamage = std::max(1, std::ceil(amount / 20.0)); - } - - std::list list; - ConditionDamage::generateDamageList(amount, startDamage, list); - for (int32_t value : list) { - addDamage(1, tickInterval, -value); - } - } - return !damageList.empty(); + return conditionDamage.getTotalDamage() >= getTotalDamage(); } bool ConditionDamage::startCondition(Creature* creature) @@ -1078,73 +847,112 @@ bool ConditionDamage::startCondition(Creature* creature) return false; } - if (!init()) { - return false; + creature->onAttacked(); + + setParam(CONDITION_PARAM_TICKINTERVAL, 1000); + + if (factor_percent == -1) { + factor_percent = 50; } - if (!delayed) { - int32_t damage; - if (getNextDamage(damage)) { - return doDamage(creature, damage); - } + if (factor_percent <= 9) { + factor_percent = 10; } + + if (factor_percent >= 1001) { + factor_percent = 1000; + } + + if (hit_damage) { + doDamage(creature, -hit_damage); + } + return true; } -bool ConditionDamage::executeCondition(Creature* creature, int32_t interval) +bool ConditionDamage::executeCondition(Creature* creature, int32_t) { - if (periodDamage != 0) { - periodDamageTick += interval; - - if (periodDamageTick >= tickInterval) { - periodDamageTick = 0; - doDamage(creature, periodDamage); + if (conditionType == CONDITION_FIRE) { + if (creature->isImmune(CONDITION_FIRE)) { + return false; } - } else if (!damageList.empty()) { - IntervalInfo& damageInfo = damageList.front(); - bool bRemove = (ticks != -1); - creature->onTickCondition(getType(), bRemove); - damageInfo.timeLeft -= interval; - - if (damageInfo.timeLeft <= 0) { - int32_t damage = damageInfo.value; - - if (bRemove) { - damageList.pop_front(); + const int32_t r_cycle = cycle; + if (r_cycle) { + if (count <= 0) { + count = max_count; + cycle = r_cycle + 2 * (r_cycle <= 0) - 1; + doDamage(creature, -10); } else { - damageInfo.timeLeft = damageInfo.interval; + --count; } - - doDamage(creature, damage); + } else { + return false; + } + } else if (conditionType == CONDITION_POISON) { + if (creature->isImmune(CONDITION_POISON)) { + return false; } - if (!bRemove) { - if (ticks > 0) { - endTime += interval; - } + const int32_t r_cycle = cycle; + if (r_cycle) { + if (count <= 0) { + count = max_count; + int32_t f = factor_percent * r_cycle / 1000; + if (!f) { + f = 2 * (r_cycle > 0) - 1; + } - interval = 0; + cycle = r_cycle - f; + doDamage(creature, -f); + } else { + --count; + } + } else { + return false; + } + } else if (conditionType == CONDITION_ENERGY) { + if (creature->isImmune(CONDITION_ENERGY)) { + return false; + } + + const int32_t r_cycle = cycle; + if (r_cycle) { + if (count <= 0) { + count = max_count; + cycle = r_cycle + 2 * (r_cycle <= 0) - 1; + doDamage(creature, -25); + } else { + --count; + } + } else { + return false; + } + } else if (conditionType == CONDITION_DROWN) { + if (isFirstCycle && count - max_count == -2) { + doDamage(creature, -20); + isFirstCycle = false; + count = max_count; + return true; + } + + const int32_t r_cycle = cycle; + if (r_cycle) { + if (count <= 0) { + count = max_count; + cycle = r_cycle + 2 * (r_cycle <= 0) - 1; + doDamage(creature, -20); + } + else { + --count; + } + } + else { + return false; } } - return Condition::executeCondition(creature, interval); -} - -bool ConditionDamage::getNextDamage(int32_t& damage) -{ - if (periodDamage != 0) { - damage = periodDamage; - return true; - } else if (!damageList.empty()) { - IntervalInfo& damageInfo = damageList.front(); - damage = damageInfo.value; - if (ticks != -1) { - damageList.pop_front(); - } - return true; - } - return false; + return true; } bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange) @@ -1155,13 +963,10 @@ bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange) CombatDamage damage; damage.origin = ORIGIN_CONDITION; - damage.primary.value = healthChange; - damage.primary.type = Combat::ConditionToDamageType(conditionType); + damage.value = healthChange; + damage.type = Combat::ConditionToDamageType(conditionType); Creature* attacker = g_game.getCreatureByID(owner); - if (field && creature->getPlayer() && attacker && attacker->getPlayer()) { - damage.primary.value = static_cast(std::round(damage.primary.value / 2.)); - } if (!creature->isAttackable() || Combat::canDoCombat(attacker, creature) != RETURNVALUE_NOERROR) { if (!creature->isInGhostMode()) { @@ -1170,7 +975,7 @@ bool ConditionDamage::doDamage(Creature* creature, int32_t healthChange) return false; } - if (g_game.combatBlockHit(damage, attacker, creature, false, false, field)) { + if (g_game.combatBlockHit(damage, attacker, creature, false, false, true)) { return false; } return g_game.combatChangeHealth(attacker, creature, damage); @@ -1181,64 +986,31 @@ void ConditionDamage::endCondition(Creature*) // } -void ConditionDamage::addCondition(Creature* creature, const Condition* condition) +void ConditionDamage::addCondition(Creature* creature, const Condition* addCondition) { - if (condition->getType() != conditionType) { + if (addCondition->getType() != conditionType) { return; } - if (!updateCondition(condition)) { + const ConditionDamage& conditionDamage = static_cast(*addCondition); + + if (hit_damage) { + doDamage(creature, -conditionDamage.hit_damage); + } + + if (!updateCondition(addCondition)) { return; } - const ConditionDamage& conditionDamage = static_cast(*condition); - - setTicks(condition->getTicks()); owner = conditionDamage.owner; - maxDamage = conditionDamage.maxDamage; - minDamage = conditionDamage.minDamage; - startDamage = conditionDamage.startDamage; - tickInterval = conditionDamage.tickInterval; - periodDamage = conditionDamage.periodDamage; - int32_t nextTimeLeft = tickInterval; - - if (!damageList.empty()) { - //save previous timeLeft - IntervalInfo& damageInfo = damageList.front(); - nextTimeLeft = damageInfo.timeLeft; - damageList.clear(); - } - - damageList = conditionDamage.damageList; - - if (init()) { - if (!damageList.empty()) { - //restore last timeLeft - IntervalInfo& damageInfo = damageList.front(); - damageInfo.timeLeft = nextTimeLeft; - } - - if (!delayed) { - int32_t damage; - if (getNextDamage(damage)) { - doDamage(creature, damage); - } - } - } + cycle = conditionDamage.cycle; + count = conditionDamage.count; + max_count = conditionDamage.max_count; } int32_t ConditionDamage::getTotalDamage() const { - int32_t result; - if (!damageList.empty()) { - result = 0; - for (const IntervalInfo& intervalInfo : damageList) { - result += intervalInfo.value; - } - } else { - result = minDamage + (maxDamage - minDamage) / 2; - } - return std::abs(result); + return cycle; } uint32_t ConditionDamage::getIcons() const @@ -1253,28 +1025,12 @@ uint32_t ConditionDamage::getIcons() const icons |= ICON_ENERGY; break; - case CONDITION_DROWN: - icons |= ICON_DROWNING; - break; - case CONDITION_POISON: icons |= ICON_POISON; break; - case CONDITION_FREEZING: - icons |= ICON_FREEZING; - break; - - case CONDITION_DAZZLED: - icons |= ICON_DAZZLED; - break; - - case CONDITION_CURSED: - icons |= ICON_CURSED; - break; - - case CONDITION_BLEEDING: - icons |= ICON_BLEEDING; + case CONDITION_DROWN: + icons |= ICON_DROWNING; break; default: @@ -1283,69 +1039,12 @@ uint32_t ConditionDamage::getIcons() const return icons; } -void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::list& list) -{ - amount = std::abs(amount); - int32_t sum = 0; - double x1, x2; - - for (int32_t i = start; i > 0; --i) { - int32_t n = start + 1 - i; - int32_t med = (n * amount) / start; - - do { - sum += i; - list.push_back(i); - - x1 = std::fabs(1.0 - ((static_cast(sum)) + i) / med); - x2 = std::fabs(1.0 - (static_cast(sum) / med)); - } while (x1 < x2); - } -} - -void ConditionSpeed::setFormulaVars(float mina, float minb, float maxa, float maxb) -{ - this->mina = mina; - this->minb = minb; - this->maxa = maxa; - this->maxb = maxb; -} - -void ConditionSpeed::getFormulaValues(int32_t var, int32_t& min, int32_t& max) const -{ - min = (var * mina) + minb; - max = (var * maxa) + maxb; -} - -bool ConditionSpeed::setParam(ConditionParam_t param, int32_t value) -{ - Condition::setParam(param, value); - if (param != CONDITION_PARAM_SPEED) { - return false; - } - - speedDelta = value; - - if (value > 0) { - conditionType = CONDITION_HASTE; - } else { - conditionType = CONDITION_PARALYZE; - } - return true; -} - bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStream) { if (attr == CONDITIONATTR_SPEEDDELTA) { return propStream.read(speedDelta); - } else if (attr == CONDITIONATTR_FORMULA_MINA) { - return propStream.read(mina); - } else if (attr == CONDITIONATTR_FORMULA_MINB) { - return propStream.read(minb); - } else if (attr == CONDITIONATTR_FORMULA_MAXA) { - return propStream.read(maxa); - } else if (attr == CONDITIONATTR_FORMULA_MAXB) { - return propStream.read(maxb); + } else if (attr == CONDITIONATTR_APPLIEDSPEEDDELTA) { + return propStream.read(appliedSpeedDelta); } return Condition::unserializeProp(attr, propStream); } @@ -1357,17 +1056,8 @@ void ConditionSpeed::serialize(PropWriteStream& propWriteStream) propWriteStream.write(CONDITIONATTR_SPEEDDELTA); propWriteStream.write(speedDelta); - propWriteStream.write(CONDITIONATTR_FORMULA_MINA); - propWriteStream.write(mina); - - propWriteStream.write(CONDITIONATTR_FORMULA_MINB); - propWriteStream.write(minb); - - propWriteStream.write(CONDITIONATTR_FORMULA_MAXA); - propWriteStream.write(maxa); - - propWriteStream.write(CONDITIONATTR_FORMULA_MAXB); - propWriteStream.write(maxb); + propWriteStream.write(CONDITIONATTR_APPLIEDSPEEDDELTA); + propWriteStream.write(appliedSpeedDelta); } bool ConditionSpeed::startCondition(Creature* creature) @@ -1376,10 +1066,18 @@ bool ConditionSpeed::startCondition(Creature* creature) return false; } - if (speedDelta == 0) { - int32_t min, max; - getFormulaValues(creature->getBaseSpeed(), min, max); - speedDelta = uniform_random(min, max); + if (appliedSpeedDelta == 0) { + speedDelta = normal_random(-variation, variation) + speedDelta; + + if (speedDelta >= -100) { + speedDelta = static_cast(creature->getBaseSpeed()) * speedDelta / 100; + } else { + speedDelta = -20 - creature->getBaseSpeed(); + } + + appliedSpeedDelta = speedDelta; + } else { + speedDelta = appliedSpeedDelta; } g_game.changeSpeed(creature, speedDelta); @@ -1393,40 +1091,41 @@ bool ConditionSpeed::executeCondition(Creature* creature, int32_t interval) void ConditionSpeed::endCondition(Creature* creature) { - g_game.changeSpeed(creature, -speedDelta); + g_game.changeSpeed(creature, -appliedSpeedDelta); } -void ConditionSpeed::addCondition(Creature* creature, const Condition* condition) +void ConditionSpeed::addCondition(Creature* creature, const Condition* addCondition) { - if (conditionType != condition->getType()) { + if (conditionType != addCondition->getType()) { return; } - if (ticks == -1 && condition->getTicks() > 0) { + if (ticks == -1 && addCondition->getTicks() > 0) { return; } - setTicks(condition->getTicks()); + const ConditionSpeed& conditionSpeed = static_cast(*addCondition); - const ConditionSpeed& conditionSpeed = static_cast(*condition); - int32_t oldSpeedDelta = speedDelta; - speedDelta = conditionSpeed.speedDelta; - mina = conditionSpeed.mina; - maxa = conditionSpeed.maxa; - minb = conditionSpeed.minb; - maxb = conditionSpeed.maxb; + int32_t newVariation = conditionSpeed.variation; + int32_t newSpeedDelta = conditionSpeed.speedDelta; - if (speedDelta == 0) { - int32_t min; - int32_t max; - getFormulaValues(creature->getBaseSpeed(), min, max); - speedDelta = uniform_random(min, max); + newSpeedDelta = normal_random(-newVariation, newVariation) + newSpeedDelta; + + // update ticks + setTicks(addCondition->getTicks()); + + if (newSpeedDelta >= -100) { + newSpeedDelta = static_cast(creature->getBaseSpeed()) * newSpeedDelta / 100; + } else { + newSpeedDelta = -20 - creature->getBaseSpeed(); } - int32_t newSpeedChange = (speedDelta - oldSpeedDelta); - if (newSpeedChange != 0) { - g_game.changeSpeed(creature, newSpeedChange); - } + creature->setSpeed(-appliedSpeedDelta); + + appliedSpeedDelta = newSpeedDelta; + speedDelta = newSpeedDelta; + + g_game.changeSpeed(creature, newSpeedDelta); } uint32_t ConditionSpeed::getIcons() const @@ -1505,12 +1204,12 @@ void ConditionOutfit::endCondition(Creature* creature) g_game.internalCreatureChangeOutfit(creature, creature->getDefaultOutfit()); } -void ConditionOutfit::addCondition(Creature* creature, const Condition* condition) +void ConditionOutfit::addCondition(Creature* creature, const Condition* addCondition) { - if (updateCondition(condition)) { - setTicks(condition->getTicks()); + if (updateCondition(addCondition)) { + setTicks(addCondition->getTicks()); - const ConditionOutfit& conditionOutfit = static_cast(*condition); + const ConditionOutfit& conditionOutfit = static_cast(*addCondition); outfit = conditionOutfit.outfit; g_game.internalCreatureChangeOutfit(creature, outfit); @@ -1536,11 +1235,12 @@ bool ConditionLight::executeCondition(Creature* creature, int32_t interval) if (internalLightTicks >= lightChangeInterval) { internalLightTicks = 0; - LightInfo lightInfo = creature->getCreatureLight(); + LightInfo creatureLight; + creature->getCreatureLight(creatureLight); - if (lightInfo.level > 0) { - --lightInfo.level; - creature->setCreatureLight(lightInfo); + if (creatureLight.level > 0) { + --creatureLight.level; + creature->setCreatureLight(creatureLight); g_game.changeLight(creature); } } @@ -1554,12 +1254,12 @@ void ConditionLight::endCondition(Creature* creature) g_game.changeLight(creature); } -void ConditionLight::addCondition(Creature* creature, const Condition* condition) +void ConditionLight::addCondition(Creature* creature, const Condition* addCondition) { - if (updateCondition(condition)) { - setTicks(condition->getTicks()); + if (updateCondition(addCondition)) { + setTicks(addCondition->getTicks()); - const ConditionLight& conditionLight = static_cast(*condition); + const ConditionLight& conditionLight = static_cast(*addCondition); lightInfo.level = conditionLight.lightInfo.level; lightInfo.color = conditionLight.lightInfo.color; lightChangeInterval = ticks / lightInfo.level; @@ -1635,61 +1335,3 @@ void ConditionLight::serialize(PropWriteStream& propWriteStream) propWriteStream.write(CONDITIONATTR_LIGHTINTERVAL); propWriteStream.write(lightChangeInterval); } - -void ConditionSpellCooldown::addCondition(Creature* creature, const Condition* condition) -{ - if (updateCondition(condition)) { - setTicks(condition->getTicks()); - - if (subId != 0 && ticks > 0) { - Player* player = creature->getPlayer(); - if (player) { - player->sendSpellCooldown(subId, ticks); - } - } - } -} - -bool ConditionSpellCooldown::startCondition(Creature* creature) -{ - if (!Condition::startCondition(creature)) { - return false; - } - - if (subId != 0 && ticks > 0) { - Player* player = creature->getPlayer(); - if (player) { - player->sendSpellCooldown(subId, ticks); - } - } - return true; -} - -void ConditionSpellGroupCooldown::addCondition(Creature* creature, const Condition* condition) -{ - if (updateCondition(condition)) { - setTicks(condition->getTicks()); - - if (subId != 0 && ticks > 0) { - Player* player = creature->getPlayer(); - if (player) { - player->sendSpellGroupCooldown(static_cast(subId), ticks); - } - } - } -} - -bool ConditionSpellGroupCooldown::startCondition(Creature* creature) -{ - if (!Condition::startCondition(creature)) { - return false; - } - - if (subId != 0 && ticks > 0) { - Player* player = creature->getPlayer(); - if (player) { - player->sendSpellGroupCooldown(static_cast(subId), ticks); - } - } - return true; -} diff --git a/src/condition.h b/src/condition.h index cc99c5e..ed33877 100644 --- a/src/condition.h +++ b/src/condition.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,10 +35,13 @@ enum ConditionAttr_t { CONDITIONATTR_HEALTHGAIN, CONDITIONATTR_MANATICKS, CONDITIONATTR_MANAGAIN, - CONDITIONATTR_DELAYED, CONDITIONATTR_OWNER, - CONDITIONATTR_INTERVALDATA, + CONDITIONATTR_CYCLE, + CONDITIONATTR_COUNT, + CONDITIONATTR_MAX_COUNT, + CONDITIONATTR_FACTOR_PERCENT, CONDITIONATTR_SPEEDDELTA, + CONDITIONATTR_APPLIEDSPEEDDELTA, CONDITIONATTR_FORMULA_MINA, CONDITIONATTR_FORMULA_MINB, CONDITIONATTR_FORMULA_MAXA, @@ -52,8 +55,6 @@ enum ConditionAttr_t { CONDITIONATTR_SKILLS, CONDITIONATTR_STATS, CONDITIONATTR_OUTFIT, - CONDITIONATTR_PERIODDAMAGE, - CONDITIONATTR_ISBUFF, CONDITIONATTR_SUBID, //reserved for serialization @@ -70,9 +71,9 @@ class Condition { public: Condition() = default; - Condition(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : + Condition(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) : endTime(ticks == -1 ? std::numeric_limits::max() : 0), - subId(subId), ticks(ticks), conditionType(type), isBuff(buff), id(id) {} + subId(subId), ticks(ticks), conditionType(type), id(id) {} virtual ~Condition() = default; virtual bool startCondition(Creature* creature); @@ -100,7 +101,7 @@ class Condition } void setTicks(int32_t newTicks); - static Condition* createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param = 0, bool buff = false, uint32_t subId = 0); + static Condition* createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param = 0, uint32_t subId = 0); static Condition* createCondition(PropStream& propStream); virtual bool setParam(ConditionParam_t param, int32_t value); @@ -113,23 +114,20 @@ class Condition bool isPersistent() const; protected: - virtual bool updateCondition(const Condition* addCondition); - int64_t endTime; uint32_t subId; int32_t ticks; ConditionType_t conditionType; - bool isBuff; - - private: ConditionId_t id; + + virtual bool updateCondition(const Condition* addCondition); }; class ConditionGeneric : public Condition { public: - ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0): - Condition(id, type, ticks, buff, subId) {} + ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0): + Condition(id, type, ticks, subId) {} bool startCondition(Creature* creature) override; bool executeCondition(Creature* creature, int32_t interval) override; @@ -145,35 +143,32 @@ class ConditionGeneric : public Condition class ConditionAttributes final : public ConditionGeneric { public: - ConditionAttributes(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : - ConditionGeneric(id, type, ticks, buff, subId) {} + ConditionAttributes(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) : + ConditionGeneric(id, type, ticks, subId) {} - bool startCondition(Creature* creature) override; - bool executeCondition(Creature* creature, int32_t interval) override; - void endCondition(Creature* creature) override; - void addCondition(Creature* creature, const Condition* condition) override; + bool startCondition(Creature* creature) final; + bool executeCondition(Creature* creature, int32_t interval) final; + void endCondition(Creature* creature) final; + void addCondition(Creature* creature, const Condition* condition) final; - bool setParam(ConditionParam_t param, int32_t value) override; + bool setParam(ConditionParam_t param, int32_t value) final; - ConditionAttributes* clone() const override { + ConditionAttributes* clone() const final { return new ConditionAttributes(*this); } //serialization - void serialize(PropWriteStream& propWriteStream) override; - bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; + void serialize(PropWriteStream& propWriteStream) final; + bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; - private: + protected: int32_t skills[SKILL_LAST + 1] = {}; int32_t skillsPercent[SKILL_LAST + 1] = {}; - int32_t specialSkills[SPECIALSKILL_LAST + 1] = {}; int32_t stats[STAT_LAST + 1] = {}; int32_t statsPercent[STAT_LAST + 1] = {}; int32_t currentSkill = 0; int32_t currentStat = 0; - bool disableDefense = false; - void updatePercentStats(Player* player); void updateStats(Player* player); void updatePercentSkills(Player* player); @@ -183,23 +178,23 @@ class ConditionAttributes final : public ConditionGeneric class ConditionRegeneration final : public ConditionGeneric { public: - ConditionRegeneration(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0): - ConditionGeneric(id, type, ticks, buff, subId) {} + ConditionRegeneration(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0): + ConditionGeneric(id, type, ticks, subId) {} - void addCondition(Creature* creature, const Condition* condition) override; - bool executeCondition(Creature* creature, int32_t interval) override; + void addCondition(Creature* creature, const Condition* addCondition) final; + bool executeCondition(Creature* creature, int32_t interval) final; - bool setParam(ConditionParam_t param, int32_t value) override; + bool setParam(ConditionParam_t param, int32_t value) final; - ConditionRegeneration* clone() const override { + ConditionRegeneration* clone() const final { return new ConditionRegeneration(*this); } //serialization - void serialize(PropWriteStream& propWriteStream) override; - bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; + void serialize(PropWriteStream& propWriteStream) final; + bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; - private: + protected: uint32_t internalHealthTicks = 0; uint32_t internalManaTicks = 0; @@ -212,23 +207,23 @@ class ConditionRegeneration final : public ConditionGeneric class ConditionSoul final : public ConditionGeneric { public: - ConditionSoul(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : - ConditionGeneric(id, type, ticks, buff, subId) {} + ConditionSoul(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) : + ConditionGeneric(id, type, ticks, subId) {} - void addCondition(Creature* creature, const Condition* condition) override; - bool executeCondition(Creature* creature, int32_t interval) override; + void addCondition(Creature* creature, const Condition* addCondition) final; + bool executeCondition(Creature* creature, int32_t interval) final; - bool setParam(ConditionParam_t param, int32_t value) override; + bool setParam(ConditionParam_t param, int32_t value) final; - ConditionSoul* clone() const override { + ConditionSoul* clone() const final { return new ConditionSoul(*this); } //serialization - void serialize(PropWriteStream& propWriteStream) override; - bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; + void serialize(PropWriteStream& propWriteStream) final; + bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; - private: + protected: uint32_t internalSoulTicks = 0; uint32_t soulTicks = 0; uint32_t soulGain = 0; @@ -237,13 +232,13 @@ class ConditionSoul final : public ConditionGeneric class ConditionInvisible final : public ConditionGeneric { public: - ConditionInvisible(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : - ConditionGeneric(id, type, ticks, buff, subId) {} + ConditionInvisible(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) : + ConditionGeneric(id, type, ticks, subId) {} - bool startCondition(Creature* creature) override; - void endCondition(Creature* creature) override; + bool startCondition(Creature* creature) final; + void endCondition(Creature* creature) final; - ConditionInvisible* clone() const override { + ConditionInvisible* clone() const final { return new ConditionInvisible(*this); } }; @@ -252,170 +247,133 @@ class ConditionDamage final : public Condition { public: ConditionDamage() = default; - ConditionDamage(ConditionId_t id, ConditionType_t type, bool buff = false, uint32_t subId = 0) : - Condition(id, type, 0, buff, subId) {} + ConditionDamage(ConditionId_t id, ConditionType_t type, uint32_t subId = 0) : + Condition(id, type, 0, subId) { + if (type == CONDITION_POISON) { + count = max_count = 3; + } else if (type == CONDITION_FIRE) { + count = max_count = 8; + } else if (type == CONDITION_ENERGY) { + count = max_count = 10; + } else if (type == CONDITION_DROWN) { + count = max_count = 3; + } + } - static void generateDamageList(int32_t amount, int32_t start, std::list& list); + bool startCondition(Creature* creature) final; + bool executeCondition(Creature* creature, int32_t interval) final; + void endCondition(Creature* creature) final; + void addCondition(Creature* creature, const Condition* condition) final; + uint32_t getIcons() const final; - bool startCondition(Creature* creature) override; - bool executeCondition(Creature* creature, int32_t interval) override; - void endCondition(Creature* creature) override; - void addCondition(Creature* creature, const Condition* condition) override; - uint32_t getIcons() const override; - - ConditionDamage* clone() const override { + ConditionDamage* clone() const final { return new ConditionDamage(*this); } - bool setParam(ConditionParam_t param, int32_t value) override; + bool setParam(ConditionParam_t param, int32_t value) final; - bool addDamage(int32_t rounds, int32_t time, int32_t value); - bool doForceUpdate() const { - return forceUpdate; - } int32_t getTotalDamage() const; //serialization - void serialize(PropWriteStream& propWriteStream) override; - bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; - - private: - int32_t maxDamage = 0; - int32_t minDamage = 0; - int32_t startDamage = 0; - int32_t periodDamage = 0; - int32_t periodDamageTick = 0; - int32_t tickInterval = 2000; - - bool forceUpdate = false; - bool delayed = false; - bool field = false; + void serialize(PropWriteStream& propWriteStream) final; + bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; + protected: + int32_t cycle = 0; + int32_t count = 0; + int32_t max_count = 0; + int32_t factor_percent = -1; + int32_t hit_damage = 0; + bool isFirstCycle = true; uint32_t owner = 0; - bool init(); - - std::list damageList; - - bool getNextDamage(int32_t& damage); bool doDamage(Creature* creature, int32_t healthChange); - bool updateCondition(const Condition* addCondition) override; + bool updateCondition(const Condition* addCondition) final; }; class ConditionSpeed final : public Condition { public: - ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, int32_t changeSpeed) : - Condition(id, type, ticks, buff, subId), speedDelta(changeSpeed) {} + ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId, int32_t changeSpeed) : + Condition(id, type, ticks, subId), speedDelta(changeSpeed) {} - bool startCondition(Creature* creature) override; - bool executeCondition(Creature* creature, int32_t interval) override; - void endCondition(Creature* creature) override; - void addCondition(Creature* creature, const Condition* condition) override; - uint32_t getIcons() const override; + bool startCondition(Creature* creature) final; + bool executeCondition(Creature* creature, int32_t interval) final; + void endCondition(Creature* creature) final; + void addCondition(Creature* creature, const Condition* condition) final; + uint32_t getIcons() const final; - ConditionSpeed* clone() const override { + ConditionSpeed* clone() const final { return new ConditionSpeed(*this); } - bool setParam(ConditionParam_t param, int32_t value) override; - - void setFormulaVars(float mina, float minb, float maxa, float maxb); + void setVariation(int32_t newVariation) { + variation = newVariation; + } + void setSpeedDelta(int32_t newSpeedDelta) { + speedDelta = newSpeedDelta; + } //serialization - void serialize(PropWriteStream& propWriteStream) override; - bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; + void serialize(PropWriteStream& propWriteStream) final; + bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; - private: - void getFormulaValues(int32_t var, int32_t& min, int32_t& max) const; - - int32_t speedDelta; - - //formula variables - float mina = 0.0f; - float minb = 0.0f; - float maxa = 0.0f; - float maxb = 0.0f; + protected: + int32_t appliedSpeedDelta = 0; + int32_t speedDelta = 0; + int32_t variation = 0; }; class ConditionOutfit final : public Condition { public: - ConditionOutfit(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : - Condition(id, type, ticks, buff, subId) {} + ConditionOutfit(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) : + Condition(id, type, ticks, subId) {} - bool startCondition(Creature* creature) override; - bool executeCondition(Creature* creature, int32_t interval) override; - void endCondition(Creature* creature) override; - void addCondition(Creature* creature, const Condition* condition) override; + bool startCondition(Creature* creature) final; + bool executeCondition(Creature* creature, int32_t interval) final; + void endCondition(Creature* creature) final; + void addCondition(Creature* creature, const Condition* condition) final; - ConditionOutfit* clone() const override { + ConditionOutfit* clone() const final { return new ConditionOutfit(*this); } void setOutfit(const Outfit_t& outfit); //serialization - void serialize(PropWriteStream& propWriteStream) override; - bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; + void serialize(PropWriteStream& propWriteStream) final; + bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; - private: + protected: Outfit_t outfit; }; class ConditionLight final : public Condition { public: - ConditionLight(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) : - Condition(id, type, ticks, buff, subId), lightInfo(lightlevel, lightcolor) {} + ConditionLight(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) : + Condition(id, type, ticks, subId), lightInfo(lightlevel, lightcolor) {} - bool startCondition(Creature* creature) override; - bool executeCondition(Creature* creature, int32_t interval) override; - void endCondition(Creature* creature) override; - void addCondition(Creature* creature, const Condition* condition) override; + bool startCondition(Creature* creature) final; + bool executeCondition(Creature* creature, int32_t interval) final; + void endCondition(Creature* creature) final; + void addCondition(Creature* creature, const Condition* addCondition) final; - ConditionLight* clone() const override { + ConditionLight* clone() const final { return new ConditionLight(*this); } - bool setParam(ConditionParam_t param, int32_t value) override; + bool setParam(ConditionParam_t param, int32_t value) final; //serialization - void serialize(PropWriteStream& propWriteStream) override; - bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; + void serialize(PropWriteStream& propWriteStream) final; + bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; - private: + protected: LightInfo lightInfo; uint32_t internalLightTicks = 0; uint32_t lightChangeInterval = 0; }; -class ConditionSpellCooldown final : public ConditionGeneric -{ - public: - ConditionSpellCooldown(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : - ConditionGeneric(id, type, ticks, buff, subId) {} - - bool startCondition(Creature* creature) override; - void addCondition(Creature* creature, const Condition* condition) override; - - ConditionSpellCooldown* clone() const override { - return new ConditionSpellCooldown(*this); - } -}; - -class ConditionSpellGroupCooldown final : public ConditionGeneric -{ - public: - ConditionSpellGroupCooldown(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : - ConditionGeneric(id, type, ticks, buff, subId) {} - - bool startCondition(Creature* creature) override; - void addCondition(Creature* creature, const Condition* condition) override; - - ConditionSpellGroupCooldown* clone() const override { - return new ConditionSpellGroupCooldown(*this); - } -}; - #endif diff --git a/src/configmanager.cpp b/src/configmanager.cpp index 3b41920..6610b74 100644 --- a/src/configmanager.cpp +++ b/src/configmanager.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,12 +19,6 @@ #include "otpch.h" -#if __has_include("luajit/lua.hpp") -#include -#else -#include -#endif - #include "configmanager.h" #include "game.h" @@ -35,57 +29,6 @@ extern Game g_game; -namespace { - -std::string getGlobalString(lua_State* L, const char* identifier, const char* defaultValue) -{ - lua_getglobal(L, identifier); - if (!lua_isstring(L, -1)) { - lua_pop(L, 1); - return defaultValue; - } - - size_t len = lua_strlen(L, -1); - std::string ret(lua_tostring(L, -1), len); - lua_pop(L, 1); - return ret; -} - -int32_t getGlobalNumber(lua_State* L, const char* identifier, const int32_t defaultValue = 0) -{ - lua_getglobal(L, identifier); - if (!lua_isnumber(L, -1)) { - lua_pop(L, 1); - return defaultValue; - } - - int32_t val = lua_tonumber(L, -1); - lua_pop(L, 1); - return val; -} - -bool getGlobalBoolean(lua_State* L, const char* identifier, const bool defaultValue) -{ - lua_getglobal(L, identifier); - if (!lua_isboolean(L, -1)) { - if (!lua_isstring(L, -1)) { - lua_pop(L, 1); - return defaultValue; - } - - size_t len = lua_strlen(L, -1); - std::string ret(lua_tostring(L, -1), len); - lua_pop(L, 1); - return booleanString(ret); - } - - int val = lua_toboolean(L, -1); - lua_pop(L, 1); - return val != 0; -} - -} - bool ConfigManager::load() { lua_State* L = luaL_newstate(); @@ -120,10 +63,9 @@ bool ConfigManager::load() integer[GAME_PORT] = getGlobalNumber(L, "gameProtocolPort", 7172); integer[LOGIN_PORT] = getGlobalNumber(L, "loginProtocolPort", 7171); integer[STATUS_PORT] = getGlobalNumber(L, "statusProtocolPort", 7171); - - integer[MARKET_OFFER_DURATION] = getGlobalNumber(L, "marketOfferDuration", 30 * 24 * 60 * 60); } + boolean[SHOW_MONSTER_LOOT] = getGlobalBoolean(L, "showMonsterLoot", true); boolean[ALLOW_CHANGEOUTFIT] = getGlobalBoolean(L, "allowChangeOutfit", true); boolean[ONE_PLAYER_ON_ACCOUNT] = getGlobalBoolean(L, "onePlayerOnlinePerAccount", true); boolean[AIMBOT_HOTKEY_ENABLED] = getGlobalBoolean(L, "hotkeyAimbotEnabled", true); @@ -132,19 +74,14 @@ bool ConfigManager::load() boolean[FREE_PREMIUM] = getGlobalBoolean(L, "freePremium", false); boolean[REPLACE_KICK_ON_LOGIN] = getGlobalBoolean(L, "replaceKickOnLogin", true); boolean[ALLOW_CLONES] = getGlobalBoolean(L, "allowClones", false); - boolean[MARKET_PREMIUM] = getGlobalBoolean(L, "premiumToCreateMarketOffer", true); - boolean[EMOTE_SPELLS] = getGlobalBoolean(L, "emoteSpells", false); boolean[STAMINA_SYSTEM] = getGlobalBoolean(L, "staminaSystem", true); boolean[WARN_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "warnUnsafeScripts", true); boolean[CONVERT_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "convertUnsafeScripts", true); - boolean[CLASSIC_EQUIPMENT_SLOTS] = getGlobalBoolean(L, "classicEquipmentSlots", false); - boolean[CLASSIC_ATTACK_SPEED] = getGlobalBoolean(L, "classicAttackSpeed", false); - boolean[SCRIPTS_CONSOLE_LOGS] = getGlobalBoolean(L, "showScriptsLogInConsole", true); - boolean[SERVER_SAVE_NOTIFY_MESSAGE] = getGlobalBoolean(L, "serverSaveNotifyMessage", true); - boolean[SERVER_SAVE_CLEAN_MAP] = getGlobalBoolean(L, "serverSaveCleanMap", false); - boolean[SERVER_SAVE_CLOSE] = getGlobalBoolean(L, "serverSaveClose", false); - boolean[SERVER_SAVE_SHUTDOWN] = getGlobalBoolean(L, "serverSaveShutdown", true); - boolean[ONLINE_OFFLINE_CHARLIST] = getGlobalBoolean(L, "showOnlineStatusInCharlist", false); + boolean[TELEPORT_NEWBIES] = getGlobalBoolean(L, "teleportNewbies", true); + boolean[STACK_CUMULATIVES] = getGlobalBoolean(L, "autoStackCumulatives", false); + boolean[BLOCK_HEIGHT] = getGlobalBoolean(L, "blockHeight", false); + boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false); + string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high"); string[SERVER_NAME] = getGlobalString(L, "serverName", ""); @@ -164,9 +101,7 @@ bool ConfigManager::load() integer[RATE_LOOT] = getGlobalNumber(L, "rateLoot", 2); integer[RATE_MAGIC] = getGlobalNumber(L, "rateMagic", 3); integer[RATE_SPAWN] = getGlobalNumber(L, "rateSpawn", 1); - integer[HOUSE_PRICE] = getGlobalNumber(L, "housePriceEachSQM", 1000); - integer[KILLS_TO_RED] = getGlobalNumber(L, "killsToRedSkull", 3); - integer[KILLS_TO_BLACK] = getGlobalNumber(L, "killsToBlackSkull", 6); + integer[BAN_LENGTH] = getGlobalNumber(L, "banLength", 30 * 24 * 60 * 60); integer[ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenActions", 200); integer[EX_ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenExActions", 1000); integer[MAX_MESSAGEBUFFER] = getGlobalNumber(L, "maxMessageBuffer", 4); @@ -174,14 +109,20 @@ bool ConfigManager::load() integer[PROTECTION_LEVEL] = getGlobalNumber(L, "protectionLevel", 1); integer[DEATH_LOSE_PERCENT] = getGlobalNumber(L, "deathLosePercent", -1); integer[STATUSQUERY_TIMEOUT] = getGlobalNumber(L, "statusTimeout", 5000); - integer[FRAG_TIME] = getGlobalNumber(L, "timeToDecreaseFrags", 24 * 60 * 60 * 1000); - integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60 * 1000); + integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60); + integer[RED_SKULL_TIME] = getGlobalNumber(L, "redSkullTime", 30 * 24 * 60 * 60); + integer[KILLS_DAY_RED_SKULL] = getGlobalNumber(L, "killsDayRedSkull", 3); + integer[KILLS_WEEK_RED_SKULL] = getGlobalNumber(L, "killsWeekRedSkull", 5); + integer[KILLS_MONTH_RED_SKULL] = getGlobalNumber(L, "killsMonthRedSkull", 10); + integer[KILLS_DAY_BANISHMENT] = getGlobalNumber(L, "killsDayBanishment", 5); + integer[KILLS_WEEK_BANISHMENT] = getGlobalNumber(L, "killsWeekBanishment", 8); + integer[KILLS_MONTH_BANISHMENT] = getGlobalNumber(L, "killsMonthBanishment", 10); integer[STAIRHOP_DELAY] = getGlobalNumber(L, "stairJumpExhaustion", 2000); integer[EXP_FROM_PLAYERS_LEVEL_RANGE] = getGlobalNumber(L, "expFromPlayersLevelRange", 75); - integer[CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES] = getGlobalNumber(L, "checkExpiredMarketOffersEachMinutes", 60); - integer[MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER] = getGlobalNumber(L, "maxMarketOffersAtATimePerPlayer", 100); integer[MAX_PACKETS_PER_SECOND] = getGlobalNumber(L, "maxPacketsPerSecond", 25); - integer[SERVER_SAVE_NOTIFY_DURATION] = getGlobalNumber(L, "serverSaveNotifyDuration", 5); + integer[NEWBIE_TOWN] = getGlobalNumber(L, "newbieTownId", 1); + integer[NEWBIE_LEVEL_THRESHOLD] = getGlobalNumber(L, "newbieLevelThreshold", 5); + integer[MONEY_RATE] = getGlobalNumber(L, "moneyRate", 1); loaded = true; lua_close(L); @@ -197,13 +138,11 @@ bool ConfigManager::reload() return result; } -static std::string dummyStr; - const std::string& ConfigManager::getString(string_config_t what) const { if (what >= LAST_STRING_CONFIG) { std::cout << "[Warning - ConfigManager::getString] Accessing invalid index: " << what << std::endl; - return dummyStr; + return string[DUMMY_STR]; } return string[what]; } @@ -225,3 +164,47 @@ bool ConfigManager::getBoolean(boolean_config_t what) const } return boolean[what]; } + +std::string ConfigManager::getGlobalString(lua_State* L, const char* identifier, const char* defaultValue) +{ + lua_getglobal(L, identifier); + if (!lua_isstring(L, -1)) { + return defaultValue; + } + + size_t len = lua_strlen(L, -1); + std::string ret(lua_tostring(L, -1), len); + lua_pop(L, 1); + return ret; +} + +int32_t ConfigManager::getGlobalNumber(lua_State* L, const char* identifier, const int32_t defaultValue) +{ + lua_getglobal(L, identifier); + if (!lua_isnumber(L, -1)) { + return defaultValue; + } + + int32_t val = lua_tonumber(L, -1); + lua_pop(L, 1); + return val; +} + +bool ConfigManager::getGlobalBoolean(lua_State* L, const char* identifier, const bool defaultValue) +{ + lua_getglobal(L, identifier); + if (!lua_isboolean(L, -1)) { + if (!lua_isstring(L, -1)) { + return defaultValue; + } + + size_t len = lua_strlen(L, -1); + std::string ret(lua_tostring(L, -1), len); + lua_pop(L, 1); + return booleanString(ret); + } + + int val = lua_toboolean(L, -1); + lua_pop(L, 1); + return val != 0; +} diff --git a/src/configmanager.h b/src/configmanager.h index 0b47566..2d7b0db 100644 --- a/src/configmanager.h +++ b/src/configmanager.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,10 +20,13 @@ #ifndef FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39 #define FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39 +#include + class ConfigManager { public: enum boolean_config_t { + SHOW_MONSTER_LOOT, ALLOW_CHANGEOUTFIT, ONE_PLAYER_ON_ACCOUNT, AIMBOT_HOTKEY_ENABLED, @@ -34,24 +37,19 @@ class ConfigManager ALLOW_CLONES, BIND_ONLY_GLOBAL_ADDRESS, OPTIMIZE_DATABASE, - MARKET_PREMIUM, - EMOTE_SPELLS, STAMINA_SYSTEM, WARN_UNSAFE_SCRIPTS, CONVERT_UNSAFE_SCRIPTS, - CLASSIC_EQUIPMENT_SLOTS, - CLASSIC_ATTACK_SPEED, - SCRIPTS_CONSOLE_LOGS, - SERVER_SAVE_NOTIFY_MESSAGE, - SERVER_SAVE_CLEAN_MAP, - SERVER_SAVE_CLOSE, - SERVER_SAVE_SHUTDOWN, - ONLINE_OFFLINE_CHARLIST, + TELEPORT_NEWBIES, + STACK_CUMULATIVES, + BLOCK_HEIGHT, + DROP_ITEMS, LAST_BOOLEAN_CONFIG /* this must be the last one */ }; enum string_config_t { + DUMMY_STR, MAP_NAME, HOUSE_RENT_PERIOD, SERVER_NAME, @@ -84,9 +82,7 @@ class ConfigManager RATE_LOOT, RATE_MAGIC, RATE_SPAWN, - HOUSE_PRICE, - KILLS_TO_RED, - KILLS_TO_BLACK, + BAN_LENGTH, MAX_MESSAGEBUFFER, ACTIONS_DELAY_INTERVAL, EX_ACTIONS_DELAY_INTERVAL, @@ -94,18 +90,23 @@ class ConfigManager PROTECTION_LEVEL, DEATH_LOSE_PERCENT, STATUSQUERY_TIMEOUT, - FRAG_TIME, WHITE_SKULL_TIME, + RED_SKULL_TIME, + KILLS_DAY_RED_SKULL, + KILLS_WEEK_RED_SKULL, + KILLS_MONTH_RED_SKULL, + KILLS_DAY_BANISHMENT, + KILLS_WEEK_BANISHMENT, + KILLS_MONTH_BANISHMENT, GAME_PORT, LOGIN_PORT, STATUS_PORT, STAIRHOP_DELAY, - MARKET_OFFER_DURATION, - CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES, - MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER, EXP_FROM_PLAYERS_LEVEL_RANGE, MAX_PACKETS_PER_SECOND, - SERVER_SAVE_NOTIFY_DURATION, + NEWBIE_TOWN, + NEWBIE_LEVEL_THRESHOLD, + MONEY_RATE, LAST_INTEGER_CONFIG /* this must be the last one */ }; @@ -118,6 +119,10 @@ class ConfigManager bool getBoolean(boolean_config_t what) const; private: + static std::string getGlobalString(lua_State* L, const char* identifier, const char* defaultValue); + static int32_t getGlobalNumber(lua_State* L, const char* identifier, const int32_t defaultValue = 0); + static bool getGlobalBoolean(lua_State* L, const char* identifier, const bool defaultValue); + std::string string[LAST_STRING_CONFIG] = {}; int32_t integer[LAST_INTEGER_CONFIG] = {}; bool boolean[LAST_BOOLEAN_CONFIG] = {}; diff --git a/src/connection.cpp b/src/connection.cpp index 2224742..dce33eb 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -182,60 +182,51 @@ void Connection::parsePacket(const boost::system::error_code& error) if (error) { close(FORCE_CLOSE); return; - } else if (connectionState != CONNECTION_STATE_OPEN) { + } + else if (connectionState != CONNECTION_STATE_OPEN) { return; } - //Check packet checksum - uint32_t checksum; - int32_t len = msg.getLength() - msg.getBufferPosition() - NetworkMessage::CHECKSUM_LENGTH; - if (len > 0) { - checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + NetworkMessage::CHECKSUM_LENGTH, len); - } else { - checksum = 0; - } - - uint32_t recvChecksum = msg.get(); - if (recvChecksum != checksum) { - // it might not have been the checksum, step back - msg.skipBytes(-NetworkMessage::CHECKSUM_LENGTH); - } - if (!receivedFirst) { // First message received receivedFirst = true; if (!protocol) { // Game protocol has already been created at this point - protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this()); + protocol = service_port->make_protocol(msg, shared_from_this()); if (!protocol) { close(FORCE_CLOSE); return; } - } else { + } + else { msg.skipBytes(1); // Skip protocol ID } protocol->onRecvFirstMessage(msg); - } else { + } + else { protocol->onRecvMessage(msg); // Send the packet to the current protocol } try { readTimer.expires_from_now(boost::posix_time::seconds(CONNECTION_READ_TIMEOUT)); readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr(shared_from_this()), - std::placeholders::_1)); + std::placeholders::_1)); // Wait to the next packet boost::asio::async_read(socket, - boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH), - std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); - } catch (boost::system::system_error& e) { + boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH), + std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); + } + catch (boost::system::system_error& e) { std::cout << "[Network error - Connection::parsePacket] " << e.what() << std::endl; close(FORCE_CLOSE); } } + + void Connection::send(const OutputMessage_ptr& msg) { std::lock_guard lockClass(connectionLock); diff --git a/src/connection.h b/src/connection.h index 36dc24d..b885749 100644 --- a/src/connection.h +++ b/src/connection.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,17 +28,17 @@ static constexpr int32_t CONNECTION_WRITE_TIMEOUT = 30; static constexpr int32_t CONNECTION_READ_TIMEOUT = 30; class Protocol; -using Protocol_ptr = std::shared_ptr; +typedef std::shared_ptr Protocol_ptr; class OutputMessage; -using OutputMessage_ptr = std::shared_ptr; +typedef std::shared_ptr OutputMessage_ptr; class Connection; -using Connection_ptr = std::shared_ptr ; -using ConnectionWeak_ptr = std::weak_ptr; +typedef std::shared_ptr Connection_ptr; +typedef std::weak_ptr ConnectionWeak_ptr; class ServiceBase; -using Service_ptr = std::shared_ptr; +typedef std::shared_ptr Service_ptr; class ServicePort; -using ServicePort_ptr = std::shared_ptr; -using ConstServicePort_ptr = std::shared_ptr; +typedef std::shared_ptr ServicePort_ptr; +typedef std::shared_ptr ConstServicePort_ptr; class ConnectionManager { @@ -52,7 +52,7 @@ class ConnectionManager void releaseConnection(const Connection_ptr& connection); void closeAll(); - private: + protected: ConnectionManager() = default; std::unordered_set connections; @@ -78,8 +78,12 @@ class Connection : public std::enable_shared_from_this readTimer(io_service), writeTimer(io_service), service_port(std::move(service_port)), - socket(io_service), - timeConnected(time(nullptr)) {} + socket(io_service) { + connectionState = CONNECTION_STATE_OPEN; + receivedFirst = false; + packetsSent = 0; + timeConnected = time(nullptr); + } ~Connection(); friend class ConnectionManager; @@ -124,10 +128,10 @@ class Connection : public std::enable_shared_from_this boost::asio::ip::tcp::socket socket; time_t timeConnected; - uint32_t packetsSent = 0; + uint32_t packetsSent; - bool connectionState = CONNECTION_STATE_OPEN; - bool receivedFirst = false; + bool connectionState; + bool receivedFirst; }; #endif diff --git a/src/const.h b/src/const.h index 412669a..c4dffb2 100644 --- a/src/const.h +++ b/src/const.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -52,67 +52,6 @@ enum MagicEffectClasses : uint8_t { CONST_ME_SOUND_WHITE = 25, CONST_ME_BUBBLES = 26, CONST_ME_CRAPS = 27, - CONST_ME_GIFT_WRAPS = 28, - CONST_ME_FIREWORK_YELLOW = 29, - CONST_ME_FIREWORK_RED = 30, - CONST_ME_FIREWORK_BLUE = 31, - CONST_ME_STUN = 32, - CONST_ME_SLEEP = 33, - CONST_ME_WATERCREATURE = 34, - CONST_ME_GROUNDSHAKER = 35, - CONST_ME_HEARTS = 36, - CONST_ME_FIREATTACK = 37, - CONST_ME_ENERGYAREA = 38, - CONST_ME_SMALLCLOUDS = 39, - CONST_ME_HOLYDAMAGE = 40, - CONST_ME_BIGCLOUDS = 41, - CONST_ME_ICEAREA = 42, - CONST_ME_ICETORNADO = 43, - CONST_ME_ICEATTACK = 44, - CONST_ME_STONES = 45, - CONST_ME_SMALLPLANTS = 46, - CONST_ME_CARNIPHILA = 47, - CONST_ME_PURPLEENERGY = 48, - CONST_ME_YELLOWENERGY = 49, - CONST_ME_HOLYAREA = 50, - CONST_ME_BIGPLANTS = 51, - CONST_ME_CAKE = 52, - CONST_ME_GIANTICE = 53, - CONST_ME_WATERSPLASH = 54, - CONST_ME_PLANTATTACK = 55, - CONST_ME_TUTORIALARROW = 56, - CONST_ME_TUTORIALSQUARE = 57, - CONST_ME_MIRRORHORIZONTAL = 58, - CONST_ME_MIRRORVERTICAL = 59, - CONST_ME_SKULLHORIZONTAL = 60, - CONST_ME_SKULLVERTICAL = 61, - CONST_ME_ASSASSIN = 62, - CONST_ME_STEPSHORIZONTAL = 63, - CONST_ME_BLOODYSTEPS = 64, - CONST_ME_STEPSVERTICAL = 65, - CONST_ME_YALAHARIGHOST = 66, - CONST_ME_BATS = 67, - CONST_ME_SMOKE = 68, - CONST_ME_INSECTS = 69, - CONST_ME_DRAGONHEAD = 70, - CONST_ME_ORCSHAMAN = 71, - CONST_ME_ORCSHAMAN_FIRE = 72, - CONST_ME_THUNDER = 73, - CONST_ME_FERUMBRAS = 74, - CONST_ME_CONFETTI_HORIZONTAL = 75, - CONST_ME_CONFETTI_VERTICAL = 76, - // 77-157 are empty - CONST_ME_BLACKSMOKE = 158, - // 159-166 are empty - CONST_ME_REDSMOKE = 167, - CONST_ME_YELLOWSMOKE = 168, - CONST_ME_GREENSMOKE = 169, - CONST_ME_PURPLESMOKE = 170, - CONST_ME_EARLY_THUNDER = 171, - CONST_ME_RAGIAZ_BONECAPSULE = 172, - CONST_ME_CRITICAL_DAMAGE = 173, - // 174 is empty - CONST_ME_PLUNGING_FISH = 175, }; enum ShootType_t : uint8_t { @@ -133,192 +72,75 @@ enum ShootType_t : uint8_t { CONST_ANI_SNOWBALL = 13, CONST_ANI_POWERBOLT = 14, CONST_ANI_POISON = 15, - CONST_ANI_INFERNALBOLT = 16, - CONST_ANI_HUNTINGSPEAR = 17, - CONST_ANI_ENCHANTEDSPEAR = 18, - CONST_ANI_REDSTAR = 19, - CONST_ANI_GREENSTAR = 20, - CONST_ANI_ROYALSPEAR = 21, - CONST_ANI_SNIPERARROW = 22, - CONST_ANI_ONYXARROW = 23, - CONST_ANI_PIERCINGBOLT = 24, - CONST_ANI_WHIRLWINDSWORD = 25, - CONST_ANI_WHIRLWINDAXE = 26, - CONST_ANI_WHIRLWINDCLUB = 27, - CONST_ANI_ETHEREALSPEAR = 28, - CONST_ANI_ICE = 29, - CONST_ANI_EARTH = 30, - CONST_ANI_HOLY = 31, - CONST_ANI_SUDDENDEATH = 32, - CONST_ANI_FLASHARROW = 33, - CONST_ANI_FLAMMINGARROW = 34, - CONST_ANI_SHIVERARROW = 35, - CONST_ANI_ENERGYBALL = 36, - CONST_ANI_SMALLICE = 37, - CONST_ANI_SMALLHOLY = 38, - CONST_ANI_SMALLEARTH = 39, - CONST_ANI_EARTHARROW = 40, - CONST_ANI_EXPLOSION = 41, - CONST_ANI_CAKE = 42, - - CONST_ANI_TARSALARROW = 44, - CONST_ANI_VORTEXBOLT = 45, - - CONST_ANI_PRISMATICBOLT = 48, - CONST_ANI_CRYSTALLINEARROW = 49, - CONST_ANI_DRILLBOLT = 50, - CONST_ANI_ENVENOMEDARROW = 51, - - CONST_ANI_GLOOTHSPEAR = 53, - CONST_ANI_SIMPLEARROW = 54, - - // for internal use, don't send to client - CONST_ANI_WEAPONTYPE = 0xFE, // 254 }; enum SpeakClasses : uint8_t { TALKTYPE_SAY = 1, TALKTYPE_WHISPER = 2, TALKTYPE_YELL = 3, - TALKTYPE_PRIVATE_FROM = 4, - TALKTYPE_PRIVATE_TO = 5, - TALKTYPE_CHANNEL_Y = 7, - TALKTYPE_CHANNEL_O = 8, - TALKTYPE_PRIVATE_NP = 10, - TALKTYPE_PRIVATE_PN = 12, - TALKTYPE_BROADCAST = 13, - TALKTYPE_CHANNEL_R1 = 14, //red - #c text - TALKTYPE_PRIVATE_RED_FROM = 15, //@name@text - TALKTYPE_PRIVATE_RED_TO = 16, //@name@text - TALKTYPE_MONSTER_SAY = 36, - TALKTYPE_MONSTER_YELL = 37, - - TALKTYPE_CHANNEL_R2 = 0xFF, //#d + TALKTYPE_PRIVATE = 4, + TALKTYPE_CHANNEL_Y = 5, // Yellow + TALKTYPE_RVR_CHANNEL = 6, + TALKTYPE_RVR_ANSWER = 7, + TALKTYPE_RVR_CONTINUE = 8, + TALKTYPE_BROADCAST = 9, + TALKTYPE_CHANNEL_R1 = 10, // Red - #c text + TALKTYPE_PRIVATE_RED = 11, // @name@text + TALKTYPE_CHANNEL_O = 12, // orange + TALKTYPE_CHANNEL_R2 = 13, // red anonymous - #d text + TALKTYPE_MONSTER_YELL = 0x10, + TALKTYPE_MONSTER_SAY = 0x11, }; enum MessageClasses : uint8_t { - MESSAGE_STATUS_CONSOLE_BLUE = 4, /*FIXME Blue message in the console*/ + MESSAGE_STATUS_CONSOLE_YELLOW = 0x01, //Yellow message in the console + MESSAGE_STATUS_CONSOLE_LBLUE = 0x04, //Light blue message in the console + MESSAGE_STATUS_CONSOLE_ORANGE = 0x11, //Orange message in the console + MESSAGE_STATUS_WARNING = 0x12, //Red message in game window and in the console + MESSAGE_EVENT_ADVANCE = 0x13, //White message in game window and in the console + MESSAGE_EVENT_DEFAULT = 0x14, //White message at the bottom of the game window and in the console + MESSAGE_STATUS_DEFAULT = 0x15, //White message at the bottom of the game window and in the console + MESSAGE_INFO_DESCR = 0x16, //Green message in game window and in the console + MESSAGE_STATUS_SMALL = 0x17, //White message at the bottom of the game window" + MESSAGE_STATUS_CONSOLE_BLUE = 0x18, //Blue message in the console + MESSAGE_STATUS_CONSOLE_RED = 0x19, //Red message in the console - MESSAGE_STATUS_CONSOLE_RED = 13, /*Red message in the console*/ - - MESSAGE_STATUS_DEFAULT = 17, /*White message at the bottom of the game window and in the console*/ - MESSAGE_STATUS_WARNING = 18, /*Red message in game window and in the console*/ - MESSAGE_EVENT_ADVANCE = 19, /*White message in game window and in the console*/ - - MESSAGE_STATUS_SMALL = 21, /*White message at the bottom of the game window"*/ - MESSAGE_INFO_DESCR = 22, /*Green message in game window and in the console*/ - MESSAGE_DAMAGE_DEALT = 23, - MESSAGE_DAMAGE_RECEIVED = 24, - MESSAGE_HEALED = 25, - MESSAGE_EXPERIENCE = 26, - MESSAGE_DAMAGE_OTHERS = 27, - MESSAGE_HEALED_OTHERS = 28, - MESSAGE_EXPERIENCE_OTHERS = 29, - MESSAGE_EVENT_DEFAULT = 30, /*White message at the bottom of the game window and in the console*/ - MESSAGE_LOOT = 31, - - MESSAGE_GUILD = 33, /*White message in channel (+ channelId)*/ - MESSAGE_PARTY_MANAGEMENT = 34, /*White message in channel (+ channelId)*/ - MESSAGE_PARTY = 35, /*White message in channel (+ channelId)*/ - MESSAGE_EVENT_ORANGE = 36, /*Orange message in the console*/ - MESSAGE_STATUS_CONSOLE_ORANGE = 37, /*Orange message in the console*/ + MESSAGE_CLASS_FIRST = MESSAGE_STATUS_CONSOLE_YELLOW, + MESSAGE_CLASS_LAST = MESSAGE_STATUS_CONSOLE_RED, }; -enum FluidColors_t : uint8_t { - FLUID_EMPTY, - FLUID_BLUE, - FLUID_RED, - FLUID_BROWN, - FLUID_GREEN, - FLUID_YELLOW, - FLUID_WHITE, - FLUID_PURPLE, -}; - -enum FluidTypes_t : uint8_t { - FLUID_NONE = FLUID_EMPTY, - FLUID_WATER = FLUID_BLUE, - FLUID_BLOOD = FLUID_RED, - FLUID_BEER = FLUID_BROWN, - FLUID_SLIME = FLUID_GREEN, - FLUID_LEMONADE = FLUID_YELLOW, - FLUID_MILK = FLUID_WHITE, - FLUID_MANA = FLUID_PURPLE, - - FLUID_LIFE = FLUID_RED + 8, - FLUID_OIL = FLUID_BROWN + 8, - FLUID_URINE = FLUID_YELLOW + 8, - FLUID_COCONUTMILK = FLUID_WHITE + 8, - FLUID_WINE = FLUID_PURPLE + 8, - - FLUID_MUD = FLUID_BROWN + 16, - FLUID_FRUITJUICE = FLUID_YELLOW + 16, - - FLUID_LAVA = FLUID_RED + 24, - FLUID_RUM = FLUID_BROWN + 24, - FLUID_SWAMP = FLUID_GREEN + 24, - - FLUID_TEA = FLUID_BROWN + 32, - - FLUID_MEAD = FLUID_BROWN + 40, -}; - -const uint8_t reverseFluidMap[] = { - FLUID_EMPTY, +enum FluidTypes_t : uint8_t +{ + FLUID_NONE = 0, FLUID_WATER, - FLUID_MANA, - FLUID_BEER, - FLUID_EMPTY, - FLUID_BLOOD, - FLUID_SLIME, - FLUID_EMPTY, - FLUID_LEMONADE, - FLUID_MILK, -}; - -const uint8_t clientToServerFluidMap[] = { - FLUID_EMPTY, - FLUID_WATER, - FLUID_MANA, + FLUID_WINE, FLUID_BEER, FLUID_MUD, FLUID_BLOOD, FLUID_SLIME, - FLUID_RUM, - FLUID_LEMONADE, - FLUID_MILK, - FLUID_WINE, - FLUID_LIFE, - FLUID_URINE, FLUID_OIL, - FLUID_FRUITJUICE, + FLUID_URINE, + FLUID_MILK, + FLUID_MANAFLUID, + FLUID_LIFEFLUID, + FLUID_LEMONADE, + FLUID_RUM, FLUID_COCONUTMILK, - FLUID_TEA, - FLUID_MEAD, + FLUID_FRUITJUICE, }; -enum ClientFluidTypes_t : uint8_t { - CLIENTFLUID_EMPTY = 0, - CLIENTFLUID_BLUE = 1, - CLIENTFLUID_PURPLE = 2, - CLIENTFLUID_BROWN_1 = 3, - CLIENTFLUID_BROWN_2 = 4, - CLIENTFLUID_RED = 5, - CLIENTFLUID_GREEN = 6, - CLIENTFLUID_BROWN = 7, - CLIENTFLUID_YELLOW = 8, - CLIENTFLUID_WHITE = 9, -}; - -const uint8_t fluidMap[] = { - CLIENTFLUID_EMPTY, - CLIENTFLUID_BLUE, - CLIENTFLUID_RED, - CLIENTFLUID_BROWN_1, - CLIENTFLUID_GREEN, - CLIENTFLUID_YELLOW, - CLIENTFLUID_WHITE, - CLIENTFLUID_PURPLE, +enum FluidColor_t : uint8_t +{ + FLUID_COLOR_NONE = 0, + FLUID_COLOR_BLUE = 1, + FLUID_COLOR_PURPLE = 2, + FLUID_COLOR_BROWN = 3, + FLUID_COLOR_BROWN1 = 4, + FLUID_COLOR_RED = 5, + FLUID_COLOR_GREEN = 6, + FLUID_COLOR_BROWN2 = 7, + FLUID_COLOR_YELLOW = 8, + FLUID_COLOR_WHITE = 9, }; enum SquareColor_t : uint8_t { @@ -333,10 +155,8 @@ enum TextColor_t : uint8_t { TEXTCOLOR_DARKRED = 108, TEXTCOLOR_LIGHTGREY = 129, TEXTCOLOR_SKYBLUE = 143, - TEXTCOLOR_PURPLE = 154, - TEXTCOLOR_ELECTRICPURPLE = 155, + TEXTCOLOR_PURPLE = 155, TEXTCOLOR_RED = 180, - TEXTCOLOR_PASTELRED = 194, TEXTCOLOR_ORANGE = 198, TEXTCOLOR_YELLOW = 210, TEXTCOLOR_WHITE_EXP = 215, @@ -353,13 +173,6 @@ enum Icons_t { ICON_HASTE = 1 << 6, ICON_SWORDS = 1 << 7, ICON_DROWNING = 1 << 8, - ICON_FREEZING = 1 << 9, - ICON_DAZZLED = 1 << 10, - ICON_CURSED = 1 << 11, - ICON_PARTY_BUFF = 1 << 12, - ICON_REDSWORDS = 1 << 13, - ICON_PIGEON = 1 << 14, - ICON_BLEEDING = 1 << 15, }; enum WeaponType_t : uint8_t { @@ -392,7 +205,6 @@ enum WeaponAction_t : uint8_t { }; enum WieldInfo_t { - WIELDINFO_NONE = 0 << 0, WIELDINFO_LEVEL = 1 << 0, WIELDINFO_MAGLV = 1 << 1, WIELDINFO_VOCREQ = 1 << 2, @@ -405,8 +217,6 @@ enum Skulls_t : uint8_t { SKULL_GREEN = 2, SKULL_WHITE = 3, SKULL_RED = 4, - SKULL_BLACK = 5, - SKULL_ORANGE = 6, }; enum PartyShields_t : uint8_t { @@ -414,97 +224,54 @@ enum PartyShields_t : uint8_t { SHIELD_WHITEYELLOW = 1, SHIELD_WHITEBLUE = 2, SHIELD_BLUE = 3, - SHIELD_YELLOW = 4, - SHIELD_BLUE_SHAREDEXP = 5, - SHIELD_YELLOW_SHAREDEXP = 6, - SHIELD_BLUE_NOSHAREDEXP_BLINK = 7, - SHIELD_YELLOW_NOSHAREDEXP_BLINK = 8, - SHIELD_BLUE_NOSHAREDEXP = 9, - SHIELD_YELLOW_NOSHAREDEXP = 10, - SHIELD_GRAY = 11, -}; - -enum GuildEmblems_t : uint8_t { - GUILDEMBLEM_NONE = 0, - GUILDEMBLEM_ALLY = 1, - GUILDEMBLEM_ENEMY = 2, - GUILDEMBLEM_NEUTRAL = 3, - GUILDEMBLEM_MEMBER = 4, - GUILDEMBLEM_OTHER = 5, + SHIELD_YELLOW = 4 }; enum item_t : uint16_t { - ITEM_BROWSEFIELD = 460, // for internal use + ITEM_FIREFIELD_PVP_FULL = 2118, + ITEM_FIREFIELD_PVP_MEDIUM = 2119, + ITEM_FIREFIELD_PVP_SMALL = 2120, + ITEM_FIREFIELD_PERSISTENT_FULL = 2123, + ITEM_FIREFIELD_PERSISTENT_MEDIUM = 2124, + ITEM_FIREFIELD_PERSISTENT_SMALL = 2125, + ITEM_FIREFIELD_NOPVP = 2131, - ITEM_FIREFIELD_PVP_FULL = 1487, - ITEM_FIREFIELD_PVP_MEDIUM = 1488, - ITEM_FIREFIELD_PVP_SMALL = 1489, - ITEM_FIREFIELD_PERSISTENT_FULL = 1492, - ITEM_FIREFIELD_PERSISTENT_MEDIUM = 1493, - ITEM_FIREFIELD_PERSISTENT_SMALL = 1494, - ITEM_FIREFIELD_NOPVP = 1500, + ITEM_POISONFIELD_PVP = 2121, + ITEM_POISONFIELD_PERSISTENT = 2127, + ITEM_POISONFIELD_NOPVP = 2134, - ITEM_POISONFIELD_PVP = 1490, - ITEM_POISONFIELD_PERSISTENT = 1496, - ITEM_POISONFIELD_NOPVP = 1503, + ITEM_ENERGYFIELD_PVP = 2122, + ITEM_ENERGYFIELD_PERSISTENT = 2126, + ITEM_ENERGYFIELD_NOPVP = 2135, - ITEM_ENERGYFIELD_PVP = 1491, - ITEM_ENERGYFIELD_PERSISTENT = 1495, - ITEM_ENERGYFIELD_NOPVP = 1504, + ITEM_MAGICWALL = 2128, + ITEM_MAGICWALL_PERSISTENT = 2128, - ITEM_MAGICWALL = 1497, - ITEM_MAGICWALL_PERSISTENT = 1498, - ITEM_MAGICWALL_SAFE = 11098, + ITEM_WILDGROWTH = 2130, + ITEM_WILDGROWTH_PERSISTENT = 2130, - ITEM_WILDGROWTH = 1499, - ITEM_WILDGROWTH_PERSISTENT = 2721, - ITEM_WILDGROWTH_SAFE = 11099, + ITEM_GOLD_COIN = 3031, + ITEM_PLATINUM_COIN = 3035, + ITEM_CRYSTAL_COIN = 3043, - ITEM_BAG = 1987, - ITEM_SHOPPING_BAG = 23782, + ITEM_DEPOT = 3502, + ITEM_LOCKER1 = 3497, - ITEM_GOLD_COIN = 2148, - ITEM_PLATINUM_COIN = 2152, - ITEM_CRYSTAL_COIN = 2160, - ITEM_STORE_COIN = 24774, // in-game store currency + ITEM_MALE_CORPSE = 4240, + ITEM_FEMALE_CORPSE = 4247, - ITEM_DEPOT = 2594, - ITEM_LOCKER1 = 2589, - ITEM_INBOX = 14404, - ITEM_MARKET = 14405, - ITEM_STORE_INBOX = 26052, - ITEM_DEPOT_BOX_I = 25453, - ITEM_DEPOT_BOX_II = 25454, - ITEM_DEPOT_BOX_III = 25455, - ITEM_DEPOT_BOX_IV = 25456, - ITEM_DEPOT_BOX_V = 25457, - ITEM_DEPOT_BOX_VI = 25458, - ITEM_DEPOT_BOX_VII = 25459, - ITEM_DEPOT_BOX_VIII = 25460, - ITEM_DEPOT_BOX_IX = 25461, - ITEM_DEPOT_BOX_X = 25462, - ITEM_DEPOT_BOX_XI = 25463, - ITEM_DEPOT_BOX_XII = 25464, - ITEM_DEPOT_BOX_XIII = 25465, - ITEM_DEPOT_BOX_XIV = 25466, - ITEM_DEPOT_BOX_XV = 25467, - ITEM_DEPOT_BOX_XVI = 25468, - ITEM_DEPOT_BOX_XVII = 25469, + ITEM_FULLSPLASH = 2886, + ITEM_SMALLSPLASH = 2889, - ITEM_MALE_CORPSE = 3058, - ITEM_FEMALE_CORPSE = 3065, + ITEM_PARCEL = 3503, + ITEM_PARCEL_STAMPED = 3504, + ITEM_LETTER = 3505, + ITEM_LETTER_STAMPED = 3506, + ITEM_LABEL = 3507, - ITEM_FULLSPLASH = 2016, - ITEM_SMALLSPLASH = 2019, + ITEM_AMULETOFLOSS = 3057, - ITEM_PARCEL = 2595, - ITEM_LETTER = 2597, - ITEM_LETTER_STAMPED = 2598, - ITEM_LABEL = 2599, - - ITEM_AMULETOFLOSS = 2173, - - ITEM_DOCUMENT_RO = 1968, //read-only + ITEM_DOCUMENT_RO = 2819, //read-only }; enum PlayerFlags : uint64_t { @@ -546,12 +313,14 @@ enum PlayerFlags : uint64_t { PlayerFlag_IgnoreWeaponCheck = static_cast(1) << 35, PlayerFlag_CannotBeMuted = static_cast(1) << 36, PlayerFlag_IsAlwaysPremium = static_cast(1) << 37, + PlayerFlag_SpecialMoveUse = static_cast(1) << 38, }; -enum ReloadTypes_t : uint8_t { +enum ReloadTypes_t : uint8_t { RELOAD_TYPE_ALL, RELOAD_TYPE_ACTIONS, RELOAD_TYPE_CHAT, + RELOAD_TYPE_COMMANDS, RELOAD_TYPE_CONFIG, RELOAD_TYPE_CREATURESCRIPTS, RELOAD_TYPE_EVENTS, @@ -564,7 +333,6 @@ enum ReloadTypes_t : uint8_t { RELOAD_TYPE_NPCS, RELOAD_TYPE_QUESTS, RELOAD_TYPE_RAIDS, - RELOAD_TYPE_SCRIPTS, RELOAD_TYPE_SPELLS, RELOAD_TYPE_TALKACTIONS, RELOAD_TYPE_WEAPONS, @@ -572,6 +340,7 @@ enum ReloadTypes_t : uint8_t { static constexpr int32_t CHANNEL_GUILD = 0x00; static constexpr int32_t CHANNEL_PARTY = 0x01; +static constexpr int32_t CHANNEL_RULE_REP = 0x02; static constexpr int32_t CHANNEL_PRIVATE = 0xFFFF; //Reserved player storage key ranges; @@ -581,11 +350,6 @@ static constexpr int32_t PSTRG_RESERVED_RANGE_SIZE = 10000000; //[1000 - 1500]; static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000); static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500; -//[2001 - 2011]; -static constexpr int32_t PSTRG_MOUNTS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2001); -static constexpr int32_t PSTRG_MOUNTS_RANGE_SIZE = 10; -static constexpr int32_t PSTRG_MOUNTS_CURRENTMOUNT = (PSTRG_MOUNTS_RANGE_START + 10); - #define IS_IN_KEYRANGE(key, range) (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE)) diff --git a/src/container.cpp b/src/container.cpp index dfcecad..c694871 100644 --- a/src/container.cpp +++ b/src/container.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,41 +28,16 @@ extern Game g_game; Container::Container(uint16_t type) : Container(type, items[type].maxItems) {} -Container::Container(uint16_t type, uint16_t size, bool unlocked /*= true*/, bool pagination /*= false*/) : +Container::Container(uint16_t type, uint16_t size) : Item(type), - maxSize(size), - unlocked(unlocked), - pagination(pagination) + maxSize(size) {} -Container::Container(Tile* tile) : Container(ITEM_BROWSEFIELD, 30, false, true) -{ - TileItemVector* itemVector = tile->getItemList(); - if (itemVector) { - for (Item* item : *itemVector) { - if ((item->getContainer() || item->hasProperty(CONST_PROP_MOVEABLE)) && !item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { - itemlist.push_front(item); - item->setParent(this); - } - } - } - - setParent(tile); -} - Container::~Container() { - if (getID() == ITEM_BROWSEFIELD) { - g_game.browseFields.erase(getTile()); - - for (Item* item : itemlist) { - item->setParent(parent); - } - } else { - for (Item* item : itemlist) { - item->setParent(nullptr); - item->decrementReferenceCounter(); - } + for (Item* item : itemlist) { + item->setParent(nullptr); + item->decrementReferenceCounter(); } } @@ -87,7 +62,7 @@ Container* Container::getParentContainer() bool Container::hasParent() const { - return getID() != ITEM_BROWSEFIELD && dynamic_cast(getParent()) == nullptr; + return dynamic_cast(getParent()) != nullptr; } void Container::addItem(Item* item) @@ -107,22 +82,24 @@ Attr_ReadValue Container::readAttr(AttrTypes_t attr, PropStream& propStream) return Item::readAttr(attr, propStream); } -bool Container::unserializeItemNode(OTB::Loader& loader, const OTB::Node& node, PropStream& propStream) +bool Container::unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream) { - bool ret = Item::unserializeItemNode(loader, node, propStream); + bool ret = Item::unserializeItemNode(f, node, propStream); if (!ret) { return false; } - for (auto& itemNode : node.children) { + uint32_t type; + NODE nodeItem = f.getChildNode(node, type); + while (nodeItem) { //load container items - if (itemNode.type != OTBM_ITEM) { + if (type != OTBM_ITEM) { // unknown type return false; } PropStream itemPropStream; - if (!loader.getProps(itemNode, itemPropStream)) { + if (!f.getProps(nodeItem, itemPropStream)) { return false; } @@ -131,12 +108,14 @@ bool Container::unserializeItemNode(OTB::Loader& loader, const OTB::Node& node, return false; } - if (!item->unserializeItemNode(loader, itemNode, itemPropStream)) { + if (!item->unserializeItemNode(f, nodeItem, itemPropStream)) { return false; } addItem(item); updateItemWeight(item->getWeight()); + + nodeItem = f.getNextNode(nodeItem, type); } return true; } @@ -215,48 +194,48 @@ bool Container::isHoldingItem(const Item* item) const void Container::onAddContainerItem(Item* item) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2); + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2); //send to client - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->sendAddContainerItem(this, item); } //event methods - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->onAddContainerItem(item); } } void Container::onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2); + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2); //send to client - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->sendUpdateContainerItem(this, index, newItem); } //event methods - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->onUpdateContainerItem(this, oldItem, newItem); } } void Container::onRemoveContainerItem(uint32_t index, Item* item) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2); + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2); //send change to client - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->sendRemoveContainerItem(this, index); } //event methods - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->onRemoveContainerItem(this, item); } } @@ -271,10 +250,6 @@ ReturnValue Container::queryAdd(int32_t index, const Thing& thing, uint32_t coun return RETURNVALUE_NOERROR; } - if (!unlocked) { - return RETURNVALUE_NOTPOSSIBLE; - } - const Item* item = thing.getItem(); if (item == nullptr) { return RETURNVALUE_NOTPOSSIBLE; @@ -295,10 +270,6 @@ ReturnValue Container::queryAdd(int32_t index, const Thing& thing, uint32_t coun return RETURNVALUE_THISISIMPOSSIBLE; } - if (dynamic_cast(cylinder)) { - return RETURNVALUE_CONTAINERNOTENOUGHROOM; - } - cylinder = cylinder->getParent(); } @@ -355,7 +326,7 @@ ReturnValue Container::queryMaxCount(int32_t index, const Thing& thing, uint32_t } } else { const Item* destItem = getItemByIndex(index); - if (item->equals(destItem) && destItem->getItemCount() < 100) { + if (item->equals(destItem) && !destItem->isRune() && destItem->getItemCount() < 100) { uint32_t remainder = 100 - destItem->getItemCount(); if (queryAdd(index, *item, remainder, flags) == RETURNVALUE_NOERROR) { n = remainder; @@ -398,14 +369,9 @@ ReturnValue Container::queryRemove(const Thing& thing, uint32_t count, uint32_t return RETURNVALUE_NOERROR; } -Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item** destItem, - uint32_t& flags) +Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** destItem, + uint32_t& flags) { - if (!unlocked) { - *destItem = nullptr; - return this; - } - if (index == 254 /*move up*/) { index = INDEX_WHEREEVER; *destItem = nullptr; @@ -420,7 +386,8 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item** if (index == 255 /*add wherever*/) { index = INDEX_WHEREEVER; *destItem = nullptr; - } else if (index >= static_cast(capacity())) { + } + else if (index >= static_cast(capacity())) { /* if you have a container, maximize it to show all 20 slots then you open a bag that is inside the container you will have a bag with 8 slots @@ -451,19 +418,22 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item** } } - bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags); - if (autoStack && item->isStackable() && item->getParent() != this) { - //try find a suitable item to stack with - uint32_t n = 0; - for (Item* listItem : itemlist) { - if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) { - *destItem = listItem; - index = n; - return this; + if (g_config.getBoolean(ConfigManager::STACK_CUMULATIVES)) { + bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags); + if (autoStack && item->isStackable() && item->getParent() != this) { + //try find a suitable item to stack with + uint32_t n = 0; + for (Item* listItem : itemlist) { + if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) { + *destItem = listItem; + index = n; + return this; + } + ++n; } - ++n; } } + return this; } diff --git a/src/container.h b/src/container.h index c4a6838..2330c26 100644 --- a/src/container.h +++ b/src/container.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,6 @@ #include "item.h" class Container; -class DepotChest; class DepotLocker; class ContainerIterator @@ -39,7 +38,7 @@ class ContainerIterator void advance(); Item* operator*(); - private: + protected: std::list over; ItemDeque::const_iterator cur; @@ -50,32 +49,31 @@ class Container : public Item, public Cylinder { public: explicit Container(uint16_t type); - Container(uint16_t type, uint16_t size, bool unlocked = true, bool pagination = false); - explicit Container(Tile* tile); + Container(uint16_t type, uint16_t size); ~Container(); // non-copyable Container(const Container&) = delete; Container& operator=(const Container&) = delete; - Item* clone() const override final; + Item* clone() const final; - Container* getContainer() override final { + Container* getContainer() final { return this; } - const Container* getContainer() const override final { + const Container* getContainer() const final { return this; } - virtual DepotLocker* getDepotLocker() { + virtual DepotLocker* getDepotLocker() override { return nullptr; } - virtual const DepotLocker* getDepotLocker() const { + virtual const DepotLocker* getDepotLocker() const override { return nullptr; } Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; - bool unserializeItemNode(OTB::Loader& loader, const OTB::Node& node, PropStream& propStream) override; + bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream) override; std::string getContentDescription() const; size_t size() const { @@ -107,60 +105,41 @@ class Container : public Item, public Cylinder bool isHoldingItem(const Item* item) const; uint32_t getItemHoldingCount() const; - uint32_t getWeight() const override final; - - bool isUnlocked() const { - return unlocked; - } - bool hasPagination() const { - return pagination; - } + uint32_t getWeight() const final; //cylinder implementations virtual ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor = nullptr) const override; ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount, - uint32_t flags) const override final; - ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override final; + uint32_t flags) const final; + ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, - uint32_t& flags) override final; + uint32_t& flags) final; - void addThing(Thing* thing) override final; - void addThing(int32_t index, Thing* thing) override final; + void addThing(Thing* thing) final; + void addThing(int32_t index, Thing* thing) final; void addItemBack(Item* item); - void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override final; - void replaceThing(uint32_t index, Thing* thing) override final; + void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; + void replaceThing(uint32_t index, Thing* thing) final; - void removeThing(Thing* thing, uint32_t count) override final; + void removeThing(Thing* thing, uint32_t count) final; - int32_t getThingIndex(const Thing* thing) const override final; - size_t getFirstIndex() const override final; - size_t getLastIndex() const override final; - uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override final; - std::map& getAllItemTypeCount(std::map& countMap) const override final; - Thing* getThing(size_t index) const override final; + int32_t getThingIndex(const Thing* thing) const final; + size_t getFirstIndex() const final; + size_t getLastIndex() const final; + uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final; + std::map& getAllItemTypeCount(std::map& countMap) const final; + Thing* getThing(size_t index) const final; void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void internalAddThing(Thing* thing) override final; - void internalAddThing(uint32_t index, Thing* thing) override final; - void startDecaying() override final; - - protected: - ItemDeque itemlist; + void internalAddThing(Thing* thing) final; + void internalAddThing(uint32_t index, Thing* thing) final; + void startDecaying() final; private: - std::ostringstream& getContentDescription(std::ostringstream& os) const; - - uint32_t maxSize; - uint32_t totalWeight = 0; - uint32_t serializationCount = 0; - - bool unlocked; - bool pagination; - void onAddContainerItem(Item* item); void onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem); void onRemoveContainerItem(uint32_t index, Item* item); @@ -168,6 +147,14 @@ class Container : public Item, public Cylinder Container* getParentContainer(); void updateItemWeight(int32_t diff); + protected: + std::ostringstream& getContentDescription(std::ostringstream& os) const; + + uint32_t maxSize; + uint32_t totalWeight = 0; + ItemDeque itemlist; + uint32_t serializationCount = 0; + friend class ContainerIterator; friend class IOMapSerialize; }; diff --git a/src/creature.cpp b/src/creature.cpp index 81dd0ff..da8bf6d 100644 --- a/src/creature.cpp +++ b/src/creature.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,10 +25,6 @@ #include "configmanager.h" #include "scheduler.h" -double Creature::speedA = 857.36; -double Creature::speedB = 261.29; -double Creature::speedC = -4795.01; - extern Game g_game; extern ConfigManager g_config; extern CreatureEvents* g_creatureEvents; @@ -42,7 +38,8 @@ Creature::~Creature() { for (Creature* summon : summons) { summon->setAttackedCreature(nullptr); - summon->removeMaster(); + summon->setMaster(nullptr); + summon->decrementReferenceCounter(); } for (Condition* condition : conditions) { @@ -230,7 +227,7 @@ void Creature::onWalk(Direction& dir) if (r < DIRECTION_DIAGONAL_MASK) { dir = static_cast(r); } - g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, "Hicks!", false); + g_game.internalCreatureSay(this, TALKTYPE_SAY, "Hicks!", false); } } } @@ -310,7 +307,7 @@ void Creature::updateMapCache() void Creature::updateTileCache(const Tile* tile, int32_t dx, int32_t dy) { if (std::abs(dx) <= maxWalkCacheWidth && std::abs(dy) <= maxWalkCacheHeight) { - localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx] = tile && tile->queryAdd(0, *this, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) == RETURNVALUE_NOERROR; + localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx] = tile && tile->queryAdd(0, *this, 1, FLAG_PATHFINDING) == RETURNVALUE_NOERROR; } } @@ -412,7 +409,7 @@ void Creature::onRemoveCreature(Creature* creature, bool) onCreatureDisappear(creature, true); if (creature == this) { if (master && !master->isRemoved()) { - setMaster(nullptr); + master->removeSummon(this); } } else if (isMapLoaded) { if (creature->getPosition().z == getPosition().z) { @@ -585,6 +582,11 @@ void Creature::onCreatureMove(Creature* creature, const Tile* newTile, const Pos if (creature == followCreature || (creature == this && followCreature)) { if (hasFollowPath) { isUpdatingPath = true; + // this updates following walking + if (lastWalkUpdate == 0 || OTSYS_TIME() - lastWalkUpdate >= 250) { + g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game, getID()))); + lastWalkUpdate = OTSYS_TIME(); + } } if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) { @@ -617,7 +619,8 @@ void Creature::onDeath() if (lastHitCreature) { lastHitUnjustified = lastHitCreature->onKilledCreature(this); lastHitCreatureMaster = lastHitCreature->getMaster(); - } else { + } + else { lastHitCreatureMaster = nullptr; } @@ -639,7 +642,6 @@ void Creature::onDeath() uint64_t gainExp = getGainedExperience(attacker); if (Player* attackerPlayer = attacker->getPlayer()) { attackerPlayer->removeAttacked(getPlayer()); - Party* party = attackerPlayer->getParty(); if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) { attacker = party->getLeader(); @@ -649,7 +651,8 @@ void Creature::onDeath() auto tmpIt = experienceMap.find(attacker); if (tmpIt == experienceMap.end()) { experienceMap[attacker] = gainExp; - } else { + } + else { tmpIt->second += gainExp; } } @@ -673,7 +676,7 @@ void Creature::onDeath() death(lastHitCreature); if (master) { - setMaster(nullptr); + master->removeSummon(this); } if (droppedCorpse) { @@ -683,53 +686,41 @@ void Creature::onDeath() bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified) { - if (!lootDrop && getMonster()) { - if (master) { - //scripting event - onDeath - const CreatureEventList& deathEvents = getCreatureEvents(CREATURE_EVENT_DEATH); - for (CreatureEvent* deathEvent : deathEvents) { - deathEvent->executeOnDeath(this, nullptr, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); - } - } + Item* splash; + switch (getRace()) { + case RACE_VENOM: + splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME); + break; - g_game.addMagicEffect(getPosition(), CONST_ME_POFF); - } else { - Item* splash; - switch (getRace()) { - case RACE_VENOM: - splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME); - break; + case RACE_BLOOD: + splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD); + break; - case RACE_BLOOD: - splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD); - break; + default: + splash = nullptr; + break; + } - default: - splash = nullptr; - break; - } + Tile* tile = getTile(); - Tile* tile = getTile(); + if (splash) { + g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT); + g_game.startDecay(splash); + } - if (splash) { - g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT); - g_game.startDecay(splash); - } + Item* corpse = getCorpse(lastHitCreature, mostDamageCreature); + if (corpse) { + g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT); + g_game.startDecay(corpse); + } - Item* corpse = getCorpse(lastHitCreature, mostDamageCreature); - if (corpse) { - g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT); - g_game.startDecay(corpse); - } + //scripting event - onDeath + for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) { + deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); + } - //scripting event - onDeath - for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) { - deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); - } - - if (corpse) { - dropLoot(corpse->getContainer(), lastHitCreature); - } + if (corpse) { + dropLoot(corpse->getContainer(), lastHitCreature); } return true; @@ -790,6 +781,28 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3 damage = 0; blockType = BLOCK_IMMUNITY; } else if (checkDefense || checkArmor) { + if (checkDefense && OTSYS_TIME() >= earliestDefendTime) { + damage -= getDefense(); + + earliestDefendTime = lastDefendTime + 2000; + lastDefendTime = OTSYS_TIME(); + + if (damage <= 0) { + damage = 0; + blockType = BLOCK_DEFENSE; + } + } + + if (checkArmor) { + if (damage > 0 && combatType == COMBAT_PHYSICALDAMAGE) { + damage -= getArmor(); + if (damage <= 0) { + damage = 0; + blockType = BLOCK_ARMOR; + } + } + } + bool hasDefense = false; if (blockCount > 0) { @@ -797,30 +810,6 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3 hasDefense = true; } - if (checkDefense && hasDefense && canUseDefense) { - int32_t defense = getDefense(); - damage -= uniform_random(defense / 2, defense); - if (damage <= 0) { - damage = 0; - blockType = BLOCK_DEFENSE; - checkArmor = false; - } - } - - if (checkArmor) { - int32_t armor = getArmor(); - if (armor > 3) { - damage -= uniform_random(armor / 2, armor - (armor % 2 + 1)); - } else if (armor > 0) { - --damage; - } - - if (damage <= 0) { - damage = 0; - blockType = BLOCK_ARMOR; - } - } - if (hasDefense && blockType != BLOCK_NONE) { onBlockHit(); } @@ -844,6 +833,16 @@ bool Creature::setAttackedCreature(Creature* creature) return false; } + if (isSummon() && master) { + if (Monster* monster = master->getMonster()) { + if (monster->mType->info.targetDistance <= 1) { + if (!monster->hasFollowPath && !monster->followCreature) { + return false; + } + } + } + } + attackedCreature = creature; onAttackedCreature(attackedCreature); attackedCreature->onAttacked(); @@ -1027,21 +1026,9 @@ void Creature::onTickCondition(ConditionType_t type, bool& bRemove) case CONDITION_POISON: bRemove = (field->getCombatType() != COMBAT_EARTHDAMAGE); break; - case CONDITION_FREEZING: - bRemove = (field->getCombatType() != COMBAT_ICEDAMAGE); - break; - case CONDITION_DAZZLED: - bRemove = (field->getCombatType() != COMBAT_HOLYDAMAGE); - break; - case CONDITION_CURSED: - bRemove = (field->getCombatType() != COMBAT_DEATHDAMAGE); - break; case CONDITION_DROWN: bRemove = (field->getCombatType() != COMBAT_DROWNDAMAGE); break; - case CONDITION_BLEEDING: - bRemove = (field->getCombatType() != COMBAT_PHYSICALDAMAGE); - break; default: break; } @@ -1064,8 +1051,15 @@ void Creature::onAttackedCreatureDrainHealth(Creature* target, int32_t points) bool Creature::onKilledCreature(Creature* target, bool) { + if (latestKillEvent == target->getID()) { + return false; + } + + latestKillEvent = target->getID(); + if (master) { master->onKilledCreature(target); + return false; } //scripting event - onKill @@ -1085,43 +1079,28 @@ void Creature::onGainExperience(uint64_t gainExp, Creature* target) gainExp /= 2; master->onGainExperience(gainExp, target); - SpectatorVec spectators; - g_game.map.getSpectators(spectators, position, false, true); - if (spectators.empty()) { - return; - } - - TextMessage message(MESSAGE_EXPERIENCE_OTHERS, ucfirst(getNameDescription()) + " gained " + std::to_string(gainExp) + (gainExp != 1 ? " experience points." : " experience point.")); - message.position = position; - message.primary.color = TEXTCOLOR_WHITE_EXP; - message.primary.value = gainExp; - - for (Creature* spectator : spectators) { - spectator->getPlayer()->sendTextMessage(message); - } + g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(gainExp)); } -bool Creature::setMaster(Creature* newMaster) { - if (!newMaster && !master) { - return false; - } +void Creature::addSummon(Creature* creature) +{ + creature->setDropLoot(false); + creature->setLossSkill(false); + creature->setMaster(this); + creature->incrementReferenceCounter(); + summons.push_back(creature); +} - if (newMaster) { - incrementReferenceCounter(); - newMaster->summons.push_back(this); +void Creature::removeSummon(Creature* creature) +{ + auto cit = std::find(summons.begin(), summons.end(), creature); + if (cit != summons.end()) { + creature->setDropLoot(true); + creature->setLossSkill(true); + creature->setMaster(nullptr); + creature->decrementReferenceCounter(); + summons.erase(cit); } - - Creature* oldMaster = master; - master = newMaster; - - if (oldMaster) { - auto summon = std::find(oldMaster->summons.begin(), oldMaster->summons.end(), this); - if (summon != oldMaster->summons.end()) { - oldMaster->summons.erase(summon); - decrementReferenceCounter(); - } - } - return true; } bool Creature::addCondition(Condition* condition, bool force/* = false*/) @@ -1280,21 +1259,20 @@ Condition* Creature::getCondition(ConditionType_t type, ConditionId_t conditionI void Creature::executeConditions(uint32_t interval) { - ConditionList tempConditions{ conditions }; - for (Condition* condition : tempConditions) { - auto it = std::find(conditions.begin(), conditions.end(), condition); - if (it == conditions.end()) { - continue; - } - + auto it = conditions.begin(), end = conditions.end(); + while (it != end) { + Condition* condition = *it; if (!condition->executeCondition(this, interval)) { - it = std::find(conditions.begin(), conditions.end(), condition); - if (it != conditions.end()) { - conditions.erase(it); - condition->endCondition(this); - onEndCondition(condition->getType()); - delete condition; - } + ConditionType_t type = condition->getType(); + + it = conditions.erase(it); + + condition->endCondition(this); + delete condition; + + onEndCondition(type); + } else { + ++it; } } } @@ -1311,7 +1289,7 @@ bool Creature::hasCondition(ConditionType_t type, uint32_t subId/* = 0*/) const continue; } - if (condition->getEndTime() >= timeNow || condition->getTicks() == -1) { + if (condition->getEndTime() >= timeNow) { return true; } } @@ -1348,18 +1326,8 @@ int64_t Creature::getStepDuration() const return 0; } - uint32_t calculatedStepSpeed; uint32_t groundSpeed; - int32_t stepSpeed = getStepSpeed(); - if (stepSpeed > -Creature::speedB) { - calculatedStepSpeed = floor((Creature::speedA * log((stepSpeed / 2) + Creature::speedB) + Creature::speedC) + 0.5); - if (calculatedStepSpeed == 0) { - calculatedStepSpeed = 1; - } - } else { - calculatedStepSpeed = 1; - } Item* ground = tile->getGround(); if (ground) { @@ -1371,12 +1339,12 @@ int64_t Creature::getStepDuration() const groundSpeed = 150; } - double duration = std::floor(1000 * groundSpeed / calculatedStepSpeed); + double duration = std::floor(1000 * groundSpeed) / stepSpeed; int64_t stepDuration = std::ceil(duration / 50) * 50; const Monster* monster = getMonster(); if (monster && monster->isTargetNearby() && !monster->isFleeing() && !monster->getMaster()) { - stepDuration *= 2; + stepDuration *= 3; } return stepDuration; @@ -1396,18 +1364,15 @@ int64_t Creature::getEventStepTicks(bool onlyDelay) const return ret; } -LightInfo Creature::getCreatureLight() const +void Creature::getCreatureLight(LightInfo& light) const { - return internalLight; -} - -void Creature::setCreatureLight(LightInfo lightInfo) { - internalLight = std::move(lightInfo); + light = internalLight; } void Creature::setNormalCreatureLight() { - internalLight = {}; + internalLight.level = 0; + internalLight.color = 0; } bool Creature::registerCreatureEvent(const std::string& name) @@ -1475,10 +1440,6 @@ CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type) } for (CreatureEvent* creatureEvent : eventsList) { - if (!creatureEvent->isLoaded()) { - continue; - } - if (creatureEvent->getEventType() == type) { tmpEventList.push_back(creatureEvent); } diff --git a/src/creature.h b/src/creature.h index 1d65ac2..26f5627 100644 --- a/src/creature.h +++ b/src/creature.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,8 +28,8 @@ #include "enums.h" #include "creatureevent.h" -using ConditionList = std::list; -using CreatureEventList = std::list; +typedef std::list ConditionList; +typedef std::list CreatureEventList; enum slots_t : uint8_t { CONST_SLOT_WHEREEVER = 0, @@ -66,6 +66,7 @@ class Monster; class Npc; class Item; class Tile; +class Combat; static constexpr int32_t EVENT_CREATURECOUNT = 10; static constexpr int32_t EVENT_CREATURE_THINK_INTERVAL = 1000; @@ -82,7 +83,7 @@ class FrozenPathingConditionCall bool isInRange(const Position& startPos, const Position& testPos, const FindPathParams& fpp) const; - private: + protected: Position targetPos; }; @@ -96,18 +97,16 @@ class Creature : virtual public Thing Creature(); public: - static double speedA, speedB, speedC; - virtual ~Creature(); // non-copyable Creature(const Creature&) = delete; Creature& operator=(const Creature&) = delete; - Creature* getCreature() override final { + Creature* getCreature() final { return this; } - const Creature* getCreature() const override final { + const Creature* getCreature() const final { return this; } virtual Player* getPlayer() { @@ -132,8 +131,6 @@ class Creature : virtual public Thing virtual const std::string& getName() const = 0; virtual const std::string& getNameDescription() const = 0; - virtual CreatureType_t getType() const = 0; - virtual void setID() = 0; void setRemoved() { isInternalRemoved = true; @@ -172,13 +169,13 @@ class Creature : virtual public Thing hiddenHealth = b; } - int32_t getThrowRange() const override final { + int32_t getThrowRange() const final { return 1; } bool isPushable() const override { return getWalkDelay() <= 0; } - bool isRemoved() const override final { + bool isRemoved() const final { return isInternalRemoved; } virtual bool canSeeInvisibility() const { @@ -199,11 +196,15 @@ class Creature : virtual public Thing return getSpeed(); } int32_t getSpeed() const { - return baseSpeed + varSpeed; + if (baseSpeed == 0) { + return 0; + } + + return (2 * (varSpeed + baseSpeed)) + 80; } void setSpeed(int32_t varSpeedDelta) { int32_t oldSpeed = getSpeed(); - varSpeed = varSpeedDelta; + varSpeed += varSpeedDelta; if (getSpeed() <= 0) { stopEventWalk(); @@ -270,15 +271,9 @@ class Creature : virtual public Thing virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, bool checkDefense = false, bool checkArmor = false, bool field = false); - bool setMaster(Creature* newMaster); - - void removeMaster() { - if (master) { - master = nullptr; - decrementReferenceCounter(); - } + void setMaster(Creature* creature) { + master = creature; } - bool isSummon() const { return master != nullptr; } @@ -286,6 +281,8 @@ class Creature : virtual public Thing return master; } + void addSummon(Creature* creature); + void removeSummon(Creature* creature); const std::list& getSummons() const { return summons; } @@ -293,19 +290,9 @@ class Creature : virtual public Thing virtual int32_t getArmor() const { return 0; } - virtual int32_t getDefense() const { + virtual int32_t getDefense() { return 0; } - virtual float getAttackFactor() const { - return 1.0f; - } - virtual float getDefenseFactor() const { - return 1.0f; - } - - virtual uint8_t getSpeechBubble() const { - return SPEECHBUBBLE_NONE; - } bool addCondition(Condition* condition, bool force = false); bool addCombatCondition(Condition* condition); @@ -335,12 +322,15 @@ class Creature : virtual public Thing virtual void changeHealth(int32_t healthChange, bool sendHealthChange = true); - void gainHealth(Creature* healer, int32_t healthGain); + void gainHealth(Creature* attacker, int32_t healthGain); virtual void drainHealth(Creature* attacker, int32_t damage); virtual bool challengeCreature(Creature*) { return false; } + virtual bool convinceCreature(Creature*) { + return false; + } void onDeath(); virtual uint64_t getGainedExperience(Creature* attacker) const; @@ -365,9 +355,11 @@ class Creature : virtual public Thing virtual void onAttackedCreatureChangeZone(ZoneType_t zone); virtual void onIdleStatus(); - virtual LightInfo getCreatureLight() const; + virtual void getCreatureLight(LightInfo& light) const; virtual void setNormalCreatureLight(); - void setCreatureLight(LightInfo lightInfo); + void setCreatureLight(LightInfo light) { + internalLight = light; + } virtual void onThink(uint32_t interval); void onAttacking(uint32_t interval); @@ -390,6 +382,7 @@ class Creature : virtual public Thing virtual void onCreatureSay(Creature*, SpeakClasses, const std::string&) {} + virtual void onCreatureConvinced(const Creature*, const Creature*) {} virtual void onPlacedCreature() {} virtual bool getCombatValues(int32_t&, int32_t&) { @@ -402,33 +395,30 @@ class Creature : virtual public Thing void setDropLoot(bool lootDrop) { this->lootDrop = lootDrop; } - void setSkillLoss(bool skillLoss) { + void setLossSkill(bool skillLoss) { this->skillLoss = skillLoss; } - void setUseDefense(bool useDefense) { - canUseDefense = useDefense; - } //creature script events bool registerCreatureEvent(const std::string& name); bool unregisterCreatureEvent(const std::string& name); - Cylinder* getParent() const override final { + Cylinder* getParent() const final { return tile; } - void setParent(Cylinder* cylinder) override final { + void setParent(Cylinder* cylinder) final { tile = static_cast(cylinder); position = tile->getPosition(); } - const Position& getPosition() const override final { + inline const Position& getPosition() const final { return position; } - Tile* getTile() override final { + Tile* getTile() final { return tile; } - const Tile* getTile() const override final { + const Tile* getTile() const final { return tile; } @@ -474,7 +464,7 @@ class Creature : virtual public Thing Position position; - using CountMap = std::map; + typedef std::map CountMap; CountMap damageMap; std::list summons; @@ -488,6 +478,10 @@ class Creature : virtual public Thing Creature* master = nullptr; Creature* followCreature = nullptr; + int64_t earliestDefendTime = 0; + int64_t lastDefendTime = 0; + + uint64_t lastWalkUpdate = 0; uint64_t lastStep = 0; uint32_t referenceCounter = 0; uint32_t id = 0; @@ -498,7 +492,8 @@ class Creature : virtual public Thing uint32_t blockCount = 0; uint32_t blockTicks = 0; uint32_t lastStepCost = 1; - uint32_t baseSpeed = 220; + uint32_t baseSpeed = 70; + uint32_t latestKillEvent = 0; int32_t varSpeed = 0; int32_t health = 1000; int32_t healthMax = 1000; @@ -524,7 +519,6 @@ class Creature : virtual public Thing bool hasFollowPath = false; bool forceUpdateFollowPath = false; bool hiddenHealth = false; - bool canUseDefense = true; //creature script events bool hasEventRegistered(CreatureEventType_t event) const { @@ -556,6 +550,7 @@ class Creature : virtual public Thing friend class Game; friend class Map; friend class LuaScriptInterface; + friend class Combat; }; #endif diff --git a/src/creatureevent.cpp b/src/creatureevent.cpp index 3e4e7b6..eb72b28 100644 --- a/src/creatureevent.cpp +++ b/src/creatureevent.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,15 +29,22 @@ CreatureEvents::CreatureEvents() : scriptInterface.initState(); } -void CreatureEvents::clear(bool fromLua) +CreatureEvents::~CreatureEvents() { - for (auto it = creatureEvents.begin(); it != creatureEvents.end(); ++it) { - if (fromLua == it->second.fromLua) { - it->second.clearEvent(); - } + for (const auto& it : creatureEvents) { + delete it.second; + } +} + +void CreatureEvents::clear() +{ + //clear creature events + for (const auto& it : creatureEvents) { + it.second->clearEvent(); } - reInitState(fromLua); + //clear lua state + scriptInterface.reInitState(); } LuaScriptInterface& CreatureEvents::getScriptInterface() @@ -50,17 +57,17 @@ std::string CreatureEvents::getScriptBaseName() const return "creaturescripts"; } -Event_ptr CreatureEvents::getEvent(const std::string& nodeName) +Event* CreatureEvents::getEvent(const std::string& nodeName) { if (strcasecmp(nodeName.c_str(), "event") != 0) { return nullptr; } - return Event_ptr(new CreatureEvent(&scriptInterface)); + return new CreatureEvent(&scriptInterface); } -bool CreatureEvents::registerEvent(Event_ptr event, const pugi::xml_node&) +bool CreatureEvents::registerEvent(Event* event, const pugi::xml_node&) { - CreatureEvent_ptr creatureEvent{static_cast(event.release())}; //event is guaranteed to be a CreatureEvent + CreatureEvent* creatureEvent = static_cast(event); //event is guaranteed to be a CreatureEvent if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) { std::cout << "Error: [CreatureEvents::registerEvent] Trying to register event without type!" << std::endl; return false; @@ -71,37 +78,13 @@ bool CreatureEvents::registerEvent(Event_ptr event, const pugi::xml_node&) //if there was an event with the same that is not loaded //(happens when realoading), it is reused if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) { - oldEvent->copyEvent(creatureEvent.get()); + oldEvent->copyEvent(creatureEvent); } return false; } else { //if not, register it normally - creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent)); - return true; - } -} - -bool CreatureEvents::registerLuaEvent(CreatureEvent* event) -{ - CreatureEvent_ptr creatureEvent{ event }; - if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) { - std::cout << "Error: [CreatureEvents::registerLuaEvent] Trying to register event without type!" << std::endl; - return false; - } - - CreatureEvent* oldEvent = getEventByName(creatureEvent->getName(), false); - if (oldEvent) { - //if there was an event with the same that is not loaded - //(happens when realoading), it is reused - if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) { - oldEvent->copyEvent(creatureEvent.get()); - } - - return false; - } else { - //if not, register it normally - creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent)); + creatureEvents[creatureEvent->getName()] = creatureEvent; return true; } } @@ -110,8 +93,8 @@ CreatureEvent* CreatureEvents::getEventByName(const std::string& name, bool forc { auto it = creatureEvents.find(name); if (it != creatureEvents.end()) { - if (!forceLoaded || it->second.isLoaded()) { - return &it->second; + if (!forceLoaded || it->second->isLoaded()) { + return it->second; } } return nullptr; @@ -121,8 +104,8 @@ bool CreatureEvents::playerLogin(Player* player) const { //fire global event if is registered for (const auto& it : creatureEvents) { - if (it.second.getEventType() == CREATURE_EVENT_LOGIN) { - if (!it.second.executeOnLogin(player)) { + if (it.second->getEventType() == CREATURE_EVENT_LOGIN) { + if (!it.second->executeOnLogin(player)) { return false; } } @@ -134,8 +117,8 @@ bool CreatureEvents::playerLogout(Player* player) const { //fire global event if is registered for (const auto& it : creatureEvents) { - if (it.second.getEventType() == CREATURE_EVENT_LOGOUT) { - if (!it.second.executeOnLogout(player)) { + if (it.second->getEventType() == CREATURE_EVENT_LOGOUT) { + if (!it.second->executeOnLogout(player)) { return false; } } @@ -146,9 +129,9 @@ bool CreatureEvents::playerLogout(Player* player) const bool CreatureEvents::playerAdvance(Player* player, skills_t skill, uint32_t oldLevel, uint32_t newLevel) { - for (auto& it : creatureEvents) { - if (it.second.getEventType() == CREATURE_EVENT_ADVANCE) { - if (!it.second.executeAdvance(player, skill, oldLevel, newLevel)) { + for (const auto& it : creatureEvents) { + if (it.second->getEventType() == CREATURE_EVENT_ADVANCE) { + if (!it.second->executeAdvance(player, skill, oldLevel, newLevel)) { return false; } } @@ -194,10 +177,6 @@ bool CreatureEvent::configureEvent(const pugi::xml_node& node) type = CREATURE_EVENT_KILL; } else if (tmpStr == "advance") { type = CREATURE_EVENT_ADVANCE; - } else if (tmpStr == "modalwindow") { - type = CREATURE_EVENT_MODALWINDOW; - } else if (tmpStr == "textedit") { - type = CREATURE_EVENT_TEXTEDIT; } else if (tmpStr == "healthchange") { type = CREATURE_EVENT_HEALTHCHANGE; } else if (tmpStr == "manachange") { @@ -238,12 +217,6 @@ std::string CreatureEvent::getScriptEventName() const case CREATURE_EVENT_ADVANCE: return "onAdvance"; - case CREATURE_EVENT_MODALWINDOW: - return "onModalWindow"; - - case CREATURE_EVENT_TEXTEDIT: - return "onTextEdit"; - case CREATURE_EVENT_HEALTHCHANGE: return "onHealthChange"; @@ -275,7 +248,7 @@ void CreatureEvent::clearEvent() loaded = false; } -bool CreatureEvent::executeOnLogin(Player* player) const +bool CreatureEvent::executeOnLogin(Player* player) { //onLogin(player) if (!scriptInterface->reserveScriptEnv()) { @@ -294,7 +267,7 @@ bool CreatureEvent::executeOnLogin(Player* player) const return scriptInterface->callFunction(1); } -bool CreatureEvent::executeOnLogout(Player* player) const +bool CreatureEvent::executeOnLogout(Player* player) { //onLogout(player) if (!scriptInterface->reserveScriptEnv()) { @@ -446,56 +419,9 @@ void CreatureEvent::executeOnKill(Creature* creature, Creature* target) scriptInterface->callVoidFunction(2); } -void CreatureEvent::executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId) -{ - //onModalWindow(player, modalWindowId, buttonId, choiceId) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - CreatureEvent::executeModalWindow] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(scriptId, scriptInterface); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(scriptId); - - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - - lua_pushnumber(L, modalWindowId); - lua_pushnumber(L, buttonId); - lua_pushnumber(L, choiceId); - - scriptInterface->callVoidFunction(4); -} - -bool CreatureEvent::executeTextEdit(Player* player, Item* item, const std::string& text) -{ - //onTextEdit(player, item, text) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - CreatureEvent::executeTextEdit] Call stack overflow" << std::endl; - return false; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(scriptId, scriptInterface); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(scriptId); - - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - - LuaScriptInterface::pushThing(L, item); - LuaScriptInterface::pushString(L, text); - - return scriptInterface->callFunction(3); -} - void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage) { - //onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) + //onHealthChange(creature, attacker, value, type, min, max, origin) if (!scriptInterface->reserveScriptEnv()) { std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl; return; @@ -512,7 +438,8 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, if (attacker) { LuaScriptInterface::pushUserdata(L, attacker); LuaScriptInterface::setCreatureMetatable(L, -1, attacker); - } else { + } + else { lua_pushnil(L); } @@ -520,16 +447,16 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, if (scriptInterface->protectedCall(L, 7, 4) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { - damage.primary.value = std::abs(LuaScriptInterface::getNumber(L, -4)); - damage.primary.type = LuaScriptInterface::getNumber(L, -3); - damage.secondary.value = std::abs(LuaScriptInterface::getNumber(L, -2)); - damage.secondary.type = LuaScriptInterface::getNumber(L, -1); + } + else { + damage.value = std::abs(LuaScriptInterface::getNumber(L, -4)); + damage.type = LuaScriptInterface::getNumber(L, -3); + damage.min = std::abs(LuaScriptInterface::getNumber(L, -2)); + damage.max = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 4); - if (damage.primary.type != COMBAT_HEALING) { - damage.primary.value = -damage.primary.value; - damage.secondary.value = -damage.secondary.value; + if (damage.type != COMBAT_HEALING) { + damage.value = -damage.value; } } @@ -537,7 +464,7 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, } void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage) { - //onManaChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin) + //onManaChange(creature, attacker, value, type, min, max, origin) if (!scriptInterface->reserveScriptEnv()) { std::cout << "[Error - CreatureEvent::executeManaChange] Call stack overflow" << std::endl; return; @@ -554,7 +481,8 @@ void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, Co if (attacker) { LuaScriptInterface::pushUserdata(L, attacker); LuaScriptInterface::setCreatureMetatable(L, -1, attacker); - } else { + } + else { lua_pushnil(L); } @@ -562,12 +490,9 @@ void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, Co if (scriptInterface->protectedCall(L, 7, 4) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { - damage.primary.value = LuaScriptInterface::getNumber(L, -4); - damage.primary.type = LuaScriptInterface::getNumber(L, -3); - damage.secondary.value = LuaScriptInterface::getNumber(L, -2); - damage.secondary.type = LuaScriptInterface::getNumber(L, -1); - lua_pop(L, 4); + } + else { + damage = LuaScriptInterface::getCombatDamage(L); } scriptInterface->resetScriptEnv(); diff --git a/src/creatureevent.h b/src/creatureevent.h index 643ac85..6dbdeac 100644 --- a/src/creatureevent.h +++ b/src/creatureevent.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,9 +24,6 @@ #include "baseevents.h" #include "enums.h" -class CreatureEvent; -using CreatureEvent_ptr = std::unique_ptr; - enum CreatureEventType_t { CREATURE_EVENT_NONE, CREATURE_EVENT_LOGIN, @@ -36,69 +33,18 @@ enum CreatureEventType_t { CREATURE_EVENT_DEATH, CREATURE_EVENT_KILL, CREATURE_EVENT_ADVANCE, - CREATURE_EVENT_MODALWINDOW, - CREATURE_EVENT_TEXTEDIT, CREATURE_EVENT_HEALTHCHANGE, CREATURE_EVENT_MANACHANGE, CREATURE_EVENT_EXTENDED_OPCODE, // otclient additional network opcodes }; -class CreatureEvent final : public Event -{ - public: - explicit CreatureEvent(LuaScriptInterface* interface); - - bool configureEvent(const pugi::xml_node& node) override; - - CreatureEventType_t getEventType() const { - return type; - } - void setEventType(CreatureEventType_t eventType) { - type = eventType; - } - const std::string& getName() const { - return eventName; - } - void setName(const std::string& name) { - eventName = name; - } - bool isLoaded() const { - return loaded; - } - void setLoaded(bool b) { - loaded = b; - } - - void clearEvent(); - void copyEvent(CreatureEvent* creatureEvent); - - //scripting - bool executeOnLogin(Player* player) const; - bool executeOnLogout(Player* player) const; - bool executeOnThink(Creature* creature, uint32_t interval); - bool executeOnPrepareDeath(Creature* creature, Creature* killer); - bool executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified); - void executeOnKill(Creature* creature, Creature* target); - bool executeAdvance(Player* player, skills_t, uint32_t, uint32_t); - void executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId); - bool executeTextEdit(Player* player, Item* item, const std::string& text); - void executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage); - void executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage); - void executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer); - // - - private: - std::string getScriptEventName() const override; - - std::string eventName; - CreatureEventType_t type; - bool loaded; -}; +class CreatureEvent; class CreatureEvents final : public BaseEvents { public: CreatureEvents(); + ~CreatureEvents(); // non-copyable CreatureEvents(const CreatureEvents&) = delete; @@ -111,20 +57,59 @@ class CreatureEvents final : public BaseEvents CreatureEvent* getEventByName(const std::string& name, bool forceLoaded = true); - bool registerLuaEvent(CreatureEvent* event); - void clear(bool fromLua) override final; - - private: - LuaScriptInterface& getScriptInterface() override; - std::string getScriptBaseName() const override; - Event_ptr getEvent(const std::string& nodeName) override; - bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; + protected: + LuaScriptInterface& getScriptInterface() final; + std::string getScriptBaseName() const final; + Event* getEvent(const std::string& nodeName) final; + bool registerEvent(Event* event, const pugi::xml_node& node) final; + void clear() final; //creature events - using CreatureEventMap = std::map; - CreatureEventMap creatureEvents; + typedef std::map CreatureEventList; + CreatureEventList creatureEvents; LuaScriptInterface scriptInterface; }; +class CreatureEvent final : public Event +{ + public: + explicit CreatureEvent(LuaScriptInterface* interface); + + bool configureEvent(const pugi::xml_node& node) final; + + CreatureEventType_t getEventType() const { + return type; + } + const std::string& getName() const { + return eventName; + } + bool isLoaded() const { + return loaded; + } + + void clearEvent(); + void copyEvent(CreatureEvent* creatureEvent); + + //scripting + bool executeOnLogin(Player* player); + bool executeOnLogout(Player* player); + bool executeOnThink(Creature* creature, uint32_t interval); + bool executeOnPrepareDeath(Creature* creature, Creature* killer); + bool executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified); + void executeOnKill(Creature* creature, Creature* target); + bool executeAdvance(Player* player, skills_t, uint32_t, uint32_t); + void executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage); + void executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage); + void executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer); + // + + protected: + std::string getScriptEventName() const final; + + std::string eventName; + CreatureEventType_t type; + bool loaded; +}; + #endif diff --git a/src/cylinder.cpp b/src/cylinder.cpp index 4032343..406ca9a 100644 --- a/src/cylinder.cpp +++ b/src/cylinder.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/cylinder.h b/src/cylinder.h index 63a794e..872609c 100644 --- a/src/cylinder.h +++ b/src/cylinder.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,6 +37,7 @@ enum cylinderflags_t { FLAG_IGNOREFIELDDAMAGE = 1 << 5, //Bypass field damage checks FLAG_IGNORENOTMOVEABLE = 1 << 6, //Bypass check for mobility FLAG_IGNOREAUTOSTACK = 1 << 7, //queryDestination will not try to stack items together + FLAG_PLACECHECK = 1 << 8, //Special check for placing the monster }; enum cylinderlink_t { diff --git a/src/database.cpp b/src/database.cpp index 8e74801..0dfc590 100644 --- a/src/database.cpp +++ b/src/database.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -255,7 +255,7 @@ bool DBInsert::addRow(const std::string& row) // adds new row to buffer const size_t rowLength = row.length(); length += rowLength; - if (length > Database::getInstance().getMaxPacketSize() && !execute()) { + if (length > Database::getInstance()->getMaxPacketSize() && !execute()) { return false; } @@ -288,7 +288,7 @@ bool DBInsert::execute() } // executes buffer - bool res = Database::getInstance().executeQuery(query + values); + bool res = Database::getInstance()->executeQuery(query + values); values.clear(); length = query.length(); return res; diff --git a/src/database.h b/src/database.h index 51c3d78..e0168ba 100644 --- a/src/database.h +++ b/src/database.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,7 +25,7 @@ #include class DBResult; -using DBResult_ptr = std::shared_ptr; +typedef std::shared_ptr DBResult_ptr; class Database { @@ -42,10 +42,10 @@ class Database * * @return database connection handler singleton */ - static Database& getInstance() + static Database* getInstance() { static Database instance; - return instance; + return &instance; } /** @@ -117,7 +117,7 @@ class Database return maxPacketSize; } - private: + protected: /** * Transaction related methods. * @@ -129,6 +129,7 @@ class Database bool rollback(); bool commit(); + private: MYSQL* handle = nullptr; std::recursive_mutex databaseLock; uint64_t maxPacketSize = 1048576; @@ -194,7 +195,7 @@ class DBInsert bool addRow(std::ostringstream& row); bool execute(); - private: + protected: std::string query; std::string values; size_t length; @@ -207,7 +208,7 @@ class DBTransaction ~DBTransaction() { if (state == STATE_START) { - Database::getInstance().rollback(); + Database::getInstance()->rollback(); } } @@ -217,7 +218,7 @@ class DBTransaction bool begin() { state = STATE_START; - return Database::getInstance().beginTransaction(); + return Database::getInstance()->beginTransaction(); } bool commit() { @@ -225,15 +226,15 @@ class DBTransaction return false; } - state = STATE_COMMIT; - return Database::getInstance().commit(); + state = STEATE_COMMIT; + return Database::getInstance()->commit(); } private: enum TransactionStates_t { STATE_NO_START, STATE_START, - STATE_COMMIT, + STEATE_COMMIT, }; TransactionStates_t state = STATE_NO_START; diff --git a/src/databasemanager.cpp b/src/databasemanager.cpp index 6a57652..dc38b37 100644 --- a/src/databasemanager.cpp +++ b/src/databasemanager.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,11 +27,11 @@ extern ConfigManager g_config; bool DatabaseManager::optimizeTables() { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `DATA_FREE` > 0"; - DBResult_ptr result = db.storeQuery(query.str()); + query << "SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = " << db->escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `DATA_FREE` > 0"; + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -43,7 +43,7 @@ bool DatabaseManager::optimizeTables() query.str(std::string()); query << "OPTIMIZE TABLE `" << tableName << '`'; - if (db.executeQuery(query.str())) { + if (db->executeQuery(query.str())) { std::cout << " [success]" << std::endl; } else { std::cout << " [failed]" << std::endl; @@ -54,27 +54,27 @@ bool DatabaseManager::optimizeTables() bool DatabaseManager::tableExists(const std::string& tableName) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `TABLE_NAME` = " << db.escapeString(tableName) << " LIMIT 1"; - return db.storeQuery(query.str()).get() != nullptr; + query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db->escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `TABLE_NAME` = " << db->escapeString(tableName) << " LIMIT 1"; + return db->storeQuery(query.str()).get() != nullptr; } bool DatabaseManager::isDatabaseSetup() { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)); - return db.storeQuery(query.str()).get() != nullptr; + query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db->escapeString(g_config.getString(ConfigManager::MYSQL_DB)); + return db->storeQuery(query.str()).get() != nullptr; } int32_t DatabaseManager::getDatabaseVersion() { if (!tableExists("server_config")) { - Database& db = Database::getInstance(); - db.executeQuery("CREATE TABLE `server_config` (`config` VARCHAR(50) NOT NULL, `value` VARCHAR(256) NOT NULL DEFAULT '', UNIQUE(`config`)) ENGINE = InnoDB"); - db.executeQuery("INSERT INTO `server_config` VALUES ('db_version', 0)"); + Database* db = Database::getInstance(); + db->executeQuery("CREATE TABLE `server_config` (`config` VARCHAR(50) NOT NULL, `value` VARCHAR(256) NOT NULL DEFAULT '', UNIQUE(`config`)) ENGINE = InnoDB"); + db->executeQuery("INSERT INTO `server_config` VALUES ('db_version', 0)"); return 0; } @@ -85,68 +85,13 @@ int32_t DatabaseManager::getDatabaseVersion() return -1; } -void DatabaseManager::updateDatabase() -{ - lua_State* L = luaL_newstate(); - if (!L) { - return; - } - - luaL_openlibs(L); - -#ifndef LUAJIT_VERSION - //bit operations for Lua, based on bitlib project release 24 - //bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshift, bit.rshift - luaL_register(L, "bit", LuaScriptInterface::luaBitReg); -#endif - - //db table - luaL_register(L, "db", LuaScriptInterface::luaDatabaseTable); - - //result table - luaL_register(L, "result", LuaScriptInterface::luaResultTable); - - int32_t version = getDatabaseVersion(); - do { - std::ostringstream ss; - ss << "data/migrations/" << version << ".lua"; - if (luaL_dofile(L, ss.str().c_str()) != 0) { - std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl; - break; - } - - if (!LuaScriptInterface::reserveScriptEnv()) { - break; - } - - lua_getglobal(L, "onUpdateDatabase"); - if (lua_pcall(L, 0, 1, 0) != 0) { - LuaScriptInterface::resetScriptEnv(); - std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl; - break; - } - - if (!LuaScriptInterface::getBoolean(L, -1, false)) { - LuaScriptInterface::resetScriptEnv(); - break; - } - - version++; - std::cout << "> Database has been updated to version " << version << '.' << std::endl; - registerDatabaseConfig("db_version", version); - - LuaScriptInterface::resetScriptEnv(); - } while (true); - lua_close(L); -} - bool DatabaseManager::getDatabaseConfig(const std::string& config, int32_t& value) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `value` FROM `server_config` WHERE `config` = " << db.escapeString(config); + query << "SELECT `value` FROM `server_config` WHERE `config` = " << db->escapeString(config); - DBResult_ptr result = db.storeQuery(query.str()); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -157,16 +102,16 @@ bool DatabaseManager::getDatabaseConfig(const std::string& config, int32_t& valu void DatabaseManager::registerDatabaseConfig(const std::string& config, int32_t value) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; int32_t tmp; if (!getDatabaseConfig(config, tmp)) { - query << "INSERT INTO `server_config` VALUES (" << db.escapeString(config) << ", '" << value << "')"; + query << "INSERT INTO `server_config` VALUES (" << db->escapeString(config) << ", '" << value << "')"; } else { - query << "UPDATE `server_config` SET `value` = '" << value << "' WHERE `config` = " << db.escapeString(config); + query << "UPDATE `server_config` SET `value` = '" << value << "' WHERE `config` = " << db->escapeString(config); } - db.executeQuery(query.str()); + db->executeQuery(query.str()); } diff --git a/src/databasemanager.h b/src/databasemanager.h index 2e60c44..d15f1ec 100644 --- a/src/databasemanager.h +++ b/src/databasemanager.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,13 +24,12 @@ class DatabaseManager { public: - static bool tableExists(const std::string& tableName); + static bool tableExists(const std::string& table); static int32_t getDatabaseVersion(); static bool isDatabaseSetup(); static bool optimizeTables(); - static void updateDatabase(); static bool getDatabaseConfig(const std::string& config, int32_t& value); static void registerDatabaseConfig(const std::string& config, int32_t value); diff --git a/src/databasetasks.cpp b/src/databasetasks.cpp index 60969b1..37f1176 100644 --- a/src/databasetasks.cpp +++ b/src/databasetasks.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,13 +51,13 @@ void DatabaseTasks::threadMain() } } -void DatabaseTasks::addTask(std::string query, std::function callback/* = nullptr*/, bool store/* = false*/) +void DatabaseTasks::addTask(const std::string& query, const std::function& callback/* = nullptr*/, bool store/* = false*/) { bool signal = false; taskLock.lock(); if (getState() == THREAD_STATE_RUNNING) { signal = tasks.empty(); - tasks.emplace_back(std::move(query), std::move(callback), store); + tasks.emplace_back(query, callback, store); } taskLock.unlock(); @@ -85,13 +85,9 @@ void DatabaseTasks::runTask(const DatabaseTask& task) void DatabaseTasks::flush() { - std::unique_lock guard{ taskLock }; while (!tasks.empty()) { - auto task = std::move(tasks.front()); + runTask(tasks.front()); tasks.pop_front(); - guard.unlock(); - runTask(task); - guard.lock(); } } @@ -99,7 +95,7 @@ void DatabaseTasks::shutdown() { taskLock.lock(); setState(THREAD_STATE_TERMINATED); - taskLock.unlock(); flush(); + taskLock.unlock(); taskSignal.notify_one(); } diff --git a/src/databasetasks.h b/src/databasetasks.h index 2d19442..42b36e2 100644 --- a/src/databasetasks.h +++ b/src/databasetasks.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ #include "enums.h" struct DatabaseTask { - DatabaseTask(std::string&& query, std::function&& callback, bool store) : + DatabaseTask(std::string query, std::function callback, bool store) : query(std::move(query)), callback(std::move(callback)), store(store) {} std::string query; @@ -42,7 +42,7 @@ class DatabaseTasks : public ThreadHolder void flush(); void shutdown(); - void addTask(std::string query, std::function callback = nullptr, bool store = false); + void addTask(const std::string& query, const std::function& callback = nullptr, bool store = false); void threadMain(); private: diff --git a/src/definitions.h b/src/definitions.h index b779f71..63c9e08 100644 --- a/src/definitions.h +++ b/src/definitions.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,13 +20,13 @@ #ifndef FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963 #define FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963 -static constexpr auto STATUS_SERVER_NAME = "The Forgotten Server"; -static constexpr auto STATUS_SERVER_VERSION = "1.3"; -static constexpr auto STATUS_SERVER_DEVELOPERS = "Mark Samman"; +static constexpr auto STATUS_SERVER_NAME = "Sabrehaven"; +static constexpr auto STATUS_SERVER_VERSION = "1.0"; +static constexpr auto STATUS_SERVER_DEVELOPERS = "OTLand community & Sabrehaven Developers Team"; -static constexpr auto CLIENT_VERSION_MIN = 1097; -static constexpr auto CLIENT_VERSION_MAX = 1098; -static constexpr auto CLIENT_VERSION_STR = "10.98"; +static constexpr auto CLIENT_VERSION_MIN = 781; +static constexpr auto CLIENT_VERSION_MAX = 781; +static constexpr auto CLIENT_VERSION_STR = "7.81"; static constexpr auto AUTHENTICATOR_DIGITS = 6U; static constexpr auto AUTHENTICATOR_PERIOD = 30U; @@ -60,6 +60,7 @@ static constexpr auto AUTHENTICATOR_PERIOD = 30U; #pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data #pragma warning(disable:4351) // new behavior: elements of array will be default initialized #pragma warning(disable:4458) // declaration hides class member +#pragma warning(disable:4996) // inetpton warning #endif #define strcasecmp _stricmp diff --git a/src/depotchest.cpp b/src/depotchest.cpp deleted file mode 100644 index 8d28e22..0000000 --- a/src/depotchest.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "depotchest.h" -#include "tools.h" - -DepotChest::DepotChest(uint16_t type) : - Container(type), maxDepotItems(1500) {} - -ReturnValue DepotChest::queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor/* = nullptr*/) const -{ - const Item* item = thing.getItem(); - if (item == nullptr) { - return RETURNVALUE_NOTPOSSIBLE; - } - - bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags); - if (!skipLimit) { - int32_t addCount = 0; - - if ((item->isStackable() && item->getItemCount() != count)) { - addCount = 1; - } - - if (item->getTopParent() != this) { - if (const Container* container = item->getContainer()) { - addCount = container->getItemHoldingCount() + 1; - } else { - addCount = 1; - } - } - - if (getItemHoldingCount() + addCount > maxDepotItems) { - return RETURNVALUE_DEPOTISFULL; - } - } - - return Container::queryAdd(index, thing, count, flags, actor); -} - -void DepotChest::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t) -{ - Cylinder* parent = getParent(); - if (parent != nullptr) { - parent->postAddNotification(thing, oldParent, index, LINK_PARENT); - } -} - -void DepotChest::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t) -{ - Cylinder* parent = getParent(); - if (parent != nullptr) { - parent->postRemoveNotification(thing, newParent, index, LINK_PARENT); - } -} - -Cylinder* DepotChest::getParent() const -{ - if (parent) { - return parent->getParent(); - } - return nullptr; -} diff --git a/src/depotchest.h b/src/depotchest.h deleted file mode 100644 index b1db4ea..0000000 --- a/src/depotchest.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_DEPOTCHEST_H_6538526014684E3DBC92CC12815B6766 -#define FS_DEPOTCHEST_H_6538526014684E3DBC92CC12815B6766 - -#include "container.h" - -class DepotChest final : public Container -{ - public: - explicit DepotChest(uint16_t type); - - //serialization - void setMaxDepotItems(uint32_t maxitems) { - maxDepotItems = maxitems; - } - - //cylinder implementations - ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor = nullptr) const override; - - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - - //overrides - bool canRemove() const override { - return false; - } - - Cylinder* getParent() const override; - Cylinder* getRealParent() const override { - return parent; - } - - private: - uint32_t maxDepotItems; -}; - -#endif - diff --git a/src/depotlocker.cpp b/src/depotlocker.cpp index 6cce856..09189a1 100644 --- a/src/depotlocker.cpp +++ b/src/depotlocker.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,9 +20,12 @@ #include "otpch.h" #include "depotlocker.h" +#include "creature.h" +#include "player.h" +#include "tools.h" DepotLocker::DepotLocker(uint16_t type) : - Container(type, 3), depotId(0) {} + Container(type, 30), depotId(0) {} Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream) { @@ -35,9 +38,40 @@ Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream) return Item::readAttr(attr, propStream); } -ReturnValue DepotLocker::queryAdd(int32_t, const Thing&, uint32_t, uint32_t, Creature*) const +ReturnValue DepotLocker::queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor) const { - return RETURNVALUE_NOTENOUGHROOM; + const Item* item = thing.getItem(); + if (item == nullptr) { + return RETURNVALUE_NOTPOSSIBLE; + } + + bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags); + if (!skipLimit) { + int32_t addCount = 0; + + if ((item->isStackable() && item->getItemCount() != count)) { + addCount = 1; + } + + if (item->getTopParent() != this) { + if (const Container* container = item->getContainer()) { + addCount = container->getItemHoldingCount() + 1; + } else { + addCount = 1; + } + } + + if (actor) { + Player* player = actor->getPlayer(); + if (player) { + if (getItemHoldingCount() + addCount > player->getMaxDepotItems()) { + return RETURNVALUE_DEPOTISFULL; + } + } + } + } + + return Container::queryAdd(index, thing, count, flags, actor); } void DepotLocker::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t) @@ -53,12 +87,3 @@ void DepotLocker::postRemoveNotification(Thing* thing, const Cylinder* newParent parent->postRemoveNotification(thing, newParent, index, LINK_PARENT); } } - -void DepotLocker::removeInbox(Inbox* inbox) -{ - auto cit = std::find(itemlist.begin(), itemlist.end(), inbox); - if (cit == itemlist.end()) { - return; - } - itemlist.erase(cit); -} diff --git a/src/depotlocker.h b/src/depotlocker.h index 750ec42..094defc 100644 --- a/src/depotlocker.h +++ b/src/depotlocker.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,24 +21,21 @@ #define FS_DEPOTLOCKER_H_53AD8E0606A34070B87F792611F4F3F8 #include "container.h" -#include "inbox.h" class DepotLocker final : public Container { public: explicit DepotLocker(uint16_t type); - DepotLocker* getDepotLocker() override { + DepotLocker* getDepotLocker() final { return this; } - const DepotLocker* getDepotLocker() const override { + const DepotLocker* getDepotLocker() const final { return this; } - void removeInbox(Inbox* inbox); - //serialization - Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; + Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final; uint16_t getDepotId() const { return depotId; @@ -49,12 +46,12 @@ class DepotLocker final : public Container //cylinder implementations ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor = nullptr) const override; + uint32_t flags, Creature* actor = nullptr) const final; - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; + void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; + void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; - bool canRemove() const override { + bool canRemove() const final { return false; } diff --git a/src/enums.h b/src/enums.h index 579a3c9..d6bf53a 100644 --- a/src/enums.h +++ b/src/enums.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,43 +20,6 @@ #ifndef FS_ENUMS_H_003445999FEE4A67BCECBE918B0124CE #define FS_ENUMS_H_003445999FEE4A67BCECBE918B0124CE -enum RuleViolationType_t : uint8_t { - REPORT_TYPE_NAME = 0, - REPORT_TYPE_STATEMENT = 1, - REPORT_TYPE_BOT = 2 -}; - -enum RuleViolationReasons_t : uint8_t { - REPORT_REASON_NAMEINAPPROPRIATE = 0, - REPORT_REASON_NAMEPOORFORMATTED = 1, - REPORT_REASON_NAMEADVERTISING = 2, - REPORT_REASON_NAMEUNFITTING = 3, - REPORT_REASON_NAMERULEVIOLATION = 4, - REPORT_REASON_INSULTINGSTATEMENT = 5, - REPORT_REASON_SPAMMING = 6, - REPORT_REASON_ADVERTISINGSTATEMENT = 7, - REPORT_REASON_UNFITTINGSTATEMENT = 8, - REPORT_REASON_LANGUAGESTATEMENT = 9, - REPORT_REASON_DISCLOSURE = 10, - REPORT_REASON_RULEVIOLATION = 11, - REPORT_REASON_STATEMENT_BUGABUSE = 12, - REPORT_REASON_UNOFFICIALSOFTWARE = 13, - REPORT_REASON_PRETENDING = 14, - REPORT_REASON_HARASSINGOWNERS = 15, - REPORT_REASON_FALSEINFO = 16, - REPORT_REASON_ACCOUNTSHARING = 17, - REPORT_REASON_STEALINGDATA = 18, - REPORT_REASON_SERVICEATTACKING = 19, - REPORT_REASON_SERVICEAGREEMENT = 20 -}; - -enum BugReportType_t : uint8_t { - BUG_CATEGORY_MAP = 0, - BUG_CATEGORY_TYPO = 1, - BUG_CATEGORY_TECHNICAL = 2, - BUG_CATEGORY_OTHER = 3 -}; - enum ThreadState { THREAD_STATE_RUNNING, THREAD_STATE_CLOSING, @@ -67,7 +30,7 @@ enum itemAttrTypes : uint32_t { ITEM_ATTRIBUTE_NONE, ITEM_ATTRIBUTE_ACTIONID = 1 << 0, - ITEM_ATTRIBUTE_UNIQUEID = 1 << 1, + ITEM_ATTRIBUTE_MOVEMENTID = 1 << 1, ITEM_ATTRIBUTE_DESCRIPTION = 1 << 2, ITEM_ATTRIBUTE_TEXT = 1 << 3, ITEM_ATTRIBUTE_DATE = 1 << 4, @@ -78,60 +41,26 @@ enum itemAttrTypes : uint32_t { ITEM_ATTRIBUTE_WEIGHT = 1 << 9, ITEM_ATTRIBUTE_ATTACK = 1 << 10, ITEM_ATTRIBUTE_DEFENSE = 1 << 11, - ITEM_ATTRIBUTE_EXTRADEFENSE = 1 << 12, - ITEM_ATTRIBUTE_ARMOR = 1 << 13, - ITEM_ATTRIBUTE_HITCHANCE = 1 << 14, - ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 15, - ITEM_ATTRIBUTE_OWNER = 1 << 16, - ITEM_ATTRIBUTE_DURATION = 1 << 17, - ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18, - ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 19, - ITEM_ATTRIBUTE_CHARGES = 1 << 20, - ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 21, - ITEM_ATTRIBUTE_DOORID = 1 << 22, - ITEM_ATTRIBUTE_DECAYTO = 1 << 23, - - ITEM_ATTRIBUTE_CUSTOM = 1U << 31 + ITEM_ATTRIBUTE_ARMOR = 1 << 12, + ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 13, + ITEM_ATTRIBUTE_OWNER = 1 << 14, + ITEM_ATTRIBUTE_DURATION = 1 << 15, + ITEM_ATTRIBUTE_DECAYSTATE = 1 << 16, + ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 17, + ITEM_ATTRIBUTE_CHARGES = 1 << 18, + ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 19, + ITEM_ATTRIBUTE_DOORID = 1 << 20, + ITEM_ATTRIBUTE_KEYNUMBER = 1 << 21, + ITEM_ATTRIBUTE_KEYHOLENUMBER = 1 << 22, + ITEM_ATTRIBUTE_DOORQUESTNUMBER = 1 << 23, + ITEM_ATTRIBUTE_DOORQUESTVALUE = 1 << 24, + ITEM_ATTRIBUTE_DOORLEVEL = 1 << 25, + ITEM_ATTRIBUTE_CHESTQUESTNUMBER = 1 << 26, }; enum VipStatus_t : uint8_t { VIPSTATUS_OFFLINE = 0, VIPSTATUS_ONLINE = 1, - VIPSTATUS_PENDING = 2 -}; - -enum MarketAction_t { - MARKETACTION_BUY = 0, - MARKETACTION_SELL = 1, -}; - -enum MarketRequest_t { - MARKETREQUEST_OWN_OFFERS = 0xFFFE, - MARKETREQUEST_OWN_HISTORY = 0xFFFF, -}; - -enum MarketOfferState_t { - OFFERSTATE_ACTIVE = 0, - OFFERSTATE_CANCELLED = 1, - OFFERSTATE_EXPIRED = 2, - OFFERSTATE_ACCEPTED = 3, - - OFFERSTATE_ACCEPTEDEX = 255, -}; - -enum ChannelEvent_t : uint8_t { - CHANNELEVENT_JOIN = 0, - CHANNELEVENT_LEAVE = 1, - CHANNELEVENT_INVITE = 2, - CHANNELEVENT_EXCLUDE = 3, -}; - -enum CreatureType_t : uint8_t { - CREATURETYPE_PLAYER = 0, - CREATURETYPE_MONSTER = 1, - CREATURETYPE_NPC = 2, - CREATURETYPE_SUMMON_OWN = 3, - CREATURETYPE_SUMMON_OTHERS = 4, }; enum OperatingSystem_t : uint8_t { @@ -146,20 +75,6 @@ enum OperatingSystem_t : uint8_t { CLIENTOS_OTCLIENT_MAC = 12, }; -enum SpellGroup_t : uint8_t { - SPELLGROUP_NONE = 0, - SPELLGROUP_ATTACK = 1, - SPELLGROUP_HEALING = 2, - SPELLGROUP_SUPPORT = 3, - SPELLGROUP_SPECIAL = 4, -}; - -enum SpellType_t : uint8_t { - SPELL_UNDEFINED = 0, - SPELL_INSTANT = 1, - SPELL_RUNE = 2, -}; - enum AccountType_t : uint8_t { ACCOUNT_TYPE_NORMAL = 1, ACCOUNT_TYPE_TUTOR = 2, @@ -174,7 +89,6 @@ enum RaceType_t : uint8_t { RACE_BLOOD, RACE_UNDEAD, RACE_FIRE, - RACE_ENERGY, }; enum CombatType_t : uint16_t { @@ -189,11 +103,8 @@ enum CombatType_t : uint16_t { COMBAT_MANADRAIN = 1 << 6, COMBAT_HEALING = 1 << 7, COMBAT_DROWNDAMAGE = 1 << 8, - COMBAT_ICEDAMAGE = 1 << 9, - COMBAT_HOLYDAMAGE = 1 << 10, - COMBAT_DEATHDAMAGE = 1 << 11, - COMBAT_COUNT = 12 + COMBAT_COUNT = 10 }; enum CombatParam_t { @@ -207,6 +118,14 @@ enum CombatParam_t { COMBAT_PARAM_AGGRESSIVE, COMBAT_PARAM_DISPEL, COMBAT_PARAM_USECHARGES, + COMBAT_PARAM_DECREASEDAMAGE, + COMBAT_PARAM_MAXIMUMDECREASEDDAMAGE, +}; + +enum fightMode_t : uint8_t { + FIGHTMODE_ATTACK = 1, + FIGHTMODE_BALANCED = 2, + FIGHTMODE_DEFENSE = 3, }; enum CallBackParam_t { @@ -234,7 +153,6 @@ enum ConditionParam_t { CONDITION_PARAM_MAXVALUE = 15, CONDITION_PARAM_STARTVALUE = 16, CONDITION_PARAM_TICKINTERVAL = 17, - CONDITION_PARAM_FORCEUPDATE = 18, CONDITION_PARAM_SKILL_MELEE = 19, CONDITION_PARAM_SKILL_FIST = 20, CONDITION_PARAM_SKILL_CLUB = 21, @@ -260,16 +178,13 @@ enum ConditionParam_t { CONDITION_PARAM_SKILL_DISTANCEPERCENT = 41, CONDITION_PARAM_SKILL_SHIELDPERCENT = 42, CONDITION_PARAM_SKILL_FISHINGPERCENT = 43, - CONDITION_PARAM_BUFF_SPELL = 44, + // CONDITION_PARAM_BUFF_SPELL = 44, CONDITION_PARAM_SUBID = 45, CONDITION_PARAM_FIELD = 46, - CONDITION_PARAM_DISABLE_DEFENSE = 47, - CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE = 48, - CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT = 49, - CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE = 50, - CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT = 51, - CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE = 52, - CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT = 53, + CONDITION_PARAM_CYCLE = 47, + CONDITION_PARAM_HIT_DAMAGE = 48, + CONDITION_PARAM_COUNT = 49, + CONDITION_PARAM_MAX_COUNT = 50, }; enum BlockType_t : uint8_t { @@ -305,18 +220,6 @@ enum stats_t { STAT_LAST = STAT_MAGICPOINTS }; -enum SpecialSkills_t { - SPECIALSKILL_CRITICALHITCHANCE, - SPECIALSKILL_CRITICALHITAMOUNT, - SPECIALSKILL_LIFELEECHCHANCE, - SPECIALSKILL_LIFELEECHAMOUNT, - SPECIALSKILL_MANALEECHCHANCE, - SPECIALSKILL_MANALEECHAMOUNT, - - SPECIALSKILL_FIRST = SPECIALSKILL_CRITICALHITCHANCE, - SPECIALSKILL_LAST = SPECIALSKILL_MANALEECHAMOUNT -}; - enum formulaType_t { COMBAT_FORMULA_UNDEFINED, COMBAT_FORMULA_LEVELMAGIC, @@ -330,31 +233,24 @@ enum ConditionType_t { CONDITION_POISON = 1 << 0, CONDITION_FIRE = 1 << 1, CONDITION_ENERGY = 1 << 2, - CONDITION_BLEEDING = 1 << 3, - CONDITION_HASTE = 1 << 4, - CONDITION_PARALYZE = 1 << 5, - CONDITION_OUTFIT = 1 << 6, - CONDITION_INVISIBLE = 1 << 7, - CONDITION_LIGHT = 1 << 8, - CONDITION_MANASHIELD = 1 << 9, - CONDITION_INFIGHT = 1 << 10, - CONDITION_DRUNK = 1 << 11, - CONDITION_EXHAUST_WEAPON = 1 << 12, // unused - CONDITION_REGENERATION = 1 << 13, - CONDITION_SOUL = 1 << 14, - CONDITION_DROWN = 1 << 15, - CONDITION_MUTED = 1 << 16, - CONDITION_CHANNELMUTEDTICKS = 1 << 17, - CONDITION_YELLTICKS = 1 << 18, - CONDITION_ATTRIBUTES = 1 << 19, - CONDITION_FREEZING = 1 << 20, - CONDITION_DAZZLED = 1 << 21, - CONDITION_CURSED = 1 << 22, - CONDITION_EXHAUST_COMBAT = 1 << 23, // unused - CONDITION_EXHAUST_HEAL = 1 << 24, // unused - CONDITION_PACIFIED = 1 << 25, - CONDITION_SPELLCOOLDOWN = 1 << 26, - CONDITION_SPELLGROUPCOOLDOWN = 1 << 27, + CONDITION_HASTE = 1 << 3, + CONDITION_PARALYZE = 1 << 4, + CONDITION_OUTFIT = 1 << 5, + CONDITION_INVISIBLE = 1 << 6, + CONDITION_LIGHT = 1 << 7, + CONDITION_MANASHIELD = 1 << 8, + CONDITION_INFIGHT = 1 << 9, + CONDITION_DRUNK = 1 << 10, + CONDITION_REGENERATION = 1 << 11, + CONDITION_SOUL = 1 << 12, + CONDITION_MUTED = 1 << 13, + CONDITION_CHANNELMUTEDTICKS = 1 << 14, + CONDITION_YELLTICKS = 1 << 15, + CONDITION_ATTRIBUTES = 1 << 16, + CONDITION_EXHAUST = 1 << 17, + CONDITION_PACIFIED = 1 << 18, + CONDITION_AGGRESSIVE = 1 << 19, + CONDITION_DROWN = 1 << 20, }; enum ConditionId_t : int8_t { @@ -380,7 +276,11 @@ enum PlayerSex_t : uint8_t { }; enum Vocation_t : uint16_t { - VOCATION_NONE = 0 + VOCATION_NONE, + VOCATION_SORCERER = 1 << 0, + VOCATION_DRUID = 1 << 1, + VOCATION_PALADIN = 1 << 2, + VOCATION_KNIGHT = 1 << 3, }; enum ReturnValue { @@ -443,12 +343,10 @@ enum ReturnValue { RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL, RETURNVALUE_CANNOTCONJUREITEMHERE, RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS, - RETURNVALUE_NAMEISTOOAMBIGUOUS, + RETURNVALUE_NAMEISTOOAMBIGIOUS, RETURNVALUE_CANONLYUSEONESHIELD, RETURNVALUE_NOPARTYMEMBERSINRANGE, RETURNVALUE_YOUARENOTTHEOWNER, - RETURNVALUE_NOSUCHRAIDEXISTS, - RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING, RETURNVALUE_TRADEPLAYERFARAWAY, RETURNVALUE_YOUDONTOWNTHISHOUSE, RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE, @@ -456,43 +354,9 @@ enum ReturnValue { RETURNVALUE_YOUCANNOTTRADETHISHOUSE, }; -enum SpeechBubble_t -{ - SPEECHBUBBLE_NONE = 0, - SPEECHBUBBLE_NORMAL = 1, - SPEECHBUBBLE_TRADE = 2, - SPEECHBUBBLE_QUEST = 3, - SPEECHBUBBLE_QUESTTRADER = 4, -}; - -enum MapMark_t -{ - MAPMARK_TICK = 0, - MAPMARK_QUESTION = 1, - MAPMARK_EXCLAMATION = 2, - MAPMARK_STAR = 3, - MAPMARK_CROSS = 4, - MAPMARK_TEMPLE = 5, - MAPMARK_KISS = 6, - MAPMARK_SHOVEL = 7, - MAPMARK_SWORD = 8, - MAPMARK_FLAG = 9, - MAPMARK_LOCK = 10, - MAPMARK_BAG = 11, - MAPMARK_SKULL = 12, - MAPMARK_DOLLAR = 13, - MAPMARK_REDNORTH = 14, - MAPMARK_REDSOUTH = 15, - MAPMARK_REDEAST = 16, - MAPMARK_REDWEST = 17, - MAPMARK_GREENNORTH = 18, - MAPMARK_GREENSOUTH = 19, -}; - struct Outfit_t { uint16_t lookType = 0; uint16_t lookTypeEx = 0; - uint16_t lookMount = 0; uint8_t lookHead = 0; uint8_t lookBody = 0; uint8_t lookLegs = 0; @@ -507,85 +371,6 @@ struct LightInfo { constexpr LightInfo(uint8_t level, uint8_t color) : level(level), color(color) {} }; -struct ShopInfo { - uint16_t itemId; - int32_t subType; - uint32_t buyPrice; - uint32_t sellPrice; - std::string realName; - - ShopInfo() { - itemId = 0; - subType = 1; - buyPrice = 0; - sellPrice = 0; - } - - ShopInfo(uint16_t itemId, int32_t subType = 0, uint32_t buyPrice = 0, uint32_t sellPrice = 0, std::string realName = "") - : itemId(itemId), subType(subType), buyPrice(buyPrice), sellPrice(sellPrice), realName(std::move(realName)) {} -}; - -struct MarketOffer { - uint32_t price; - uint32_t timestamp; - uint16_t amount; - uint16_t counter; - uint16_t itemId; - std::string playerName; -}; - -struct MarketOfferEx { - MarketOfferEx() = default; - MarketOfferEx(MarketOfferEx&& other) : - id(other.id), playerId(other.playerId), timestamp(other.timestamp), price(other.price), - amount(other.amount), counter(other.counter), itemId(other.itemId), type(other.type), - playerName(std::move(other.playerName)) {} - - uint32_t id; - uint32_t playerId; - uint32_t timestamp; - uint32_t price; - uint16_t amount; - uint16_t counter; - uint16_t itemId; - MarketAction_t type; - std::string playerName; -}; - -struct HistoryMarketOffer { - uint32_t timestamp; - uint32_t price; - uint16_t itemId; - uint16_t amount; - MarketOfferState_t state; -}; - -struct MarketStatistics { - MarketStatistics() { - numTransactions = 0; - highestPrice = 0; - totalPrice = 0; - lowestPrice = 0; - } - - uint32_t numTransactions; - uint32_t highestPrice; - uint64_t totalPrice; - uint32_t lowestPrice; -}; - -struct ModalWindow -{ - std::list> buttons, choices; - std::string title, message; - uint32_t id; - uint8_t defaultEnterButton, defaultEscapeButton; - bool priority; - - ModalWindow(uint32_t id, std::string title, std::string message) - : title(std::move(title)), message(std::move(message)), id(id), defaultEnterButton(0xFF), defaultEscapeButton(0xFF), priority(false) {} -}; - enum CombatOrigin { ORIGIN_NONE, @@ -597,31 +382,20 @@ enum CombatOrigin struct CombatDamage { - struct { - CombatType_t type; - int32_t value; - } primary, secondary; - + CombatType_t type; + int32_t value; + int32_t min; + int32_t max; CombatOrigin origin; + CombatDamage() { origin = ORIGIN_NONE; - primary.type = secondary.type = COMBAT_NONE; - primary.value = secondary.value = 0; + type = COMBAT_NONE; + value = 0; + min = 0; + max = 0; } }; -using MarketOfferList = std::list; -using HistoryMarketOfferList = std::list; -using ShopInfoList = std::list; - -enum MonstersEvent_t : uint8_t { - MONSTERS_EVENT_NONE = 0, - MONSTERS_EVENT_THINK = 1, - MONSTERS_EVENT_APPEAR = 2, - MONSTERS_EVENT_DISAPPEAR = 3, - MONSTERS_EVENT_MOVE = 4, - MONSTERS_EVENT_SAY = 5, -}; - #endif diff --git a/src/events.cpp b/src/events.cpp index 9b9c910..868901e 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -1,6 +1,6 @@ /** * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Copyright (C) 2016 Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,9 +29,39 @@ Events::Events() : scriptInterface("Event Interface") { + clear(); scriptInterface.initState(); } +void Events::clear() +{ + // Creature + creatureOnChangeOutfit = -1; + creatureOnAreaCombat = -1; + creatureOnTargetCombat = -1; + + // Party + partyOnJoin = -1; + partyOnLeave = -1; + partyOnDisband = -1; + + // Player + playerOnLook = -1; + playerOnLookInBattleList = -1; + playerOnLookInTrade = -1; + playerOnMoveItem = -1; + playerOnItemMoved = -1; + playerOnMoveCreature = -1; + playerOnTurn = -1; + playerOnTradeRequest = -1; + playerOnTradeAccept = -1; + playerOnGainExperience = -1; + playerOnLoseExperience = -1; + playerOnGainSkillTries = -1; + playerOnReportBug = -1; + +} + bool Events::load() { pugi::xml_document doc; @@ -41,8 +71,6 @@ bool Events::load() return false; } - info = {}; - std::set classes; for (auto eventNode : doc.child("events").children()) { if (!eventNode.attribute("enabled").as_bool()) { @@ -63,69 +91,80 @@ bool Events::load() const int32_t event = scriptInterface.getMetaEvent(className, methodName); if (className == "Creature") { if (methodName == "onChangeOutfit") { - info.creatureOnChangeOutfit = event; - } else if (methodName == "onAreaCombat") { - info.creatureOnAreaCombat = event; - } else if (methodName == "onTargetCombat") { - info.creatureOnTargetCombat = event; - } else { + creatureOnChangeOutfit = event; + } + else if (methodName == "onAreaCombat") { + creatureOnAreaCombat = event; + } + else if (methodName == "onTargetCombat") { + creatureOnTargetCombat = event; + } + else { std::cout << "[Warning - Events::load] Unknown creature method: " << methodName << std::endl; } - } else if (className == "Party") { + } + else if (className == "Party") { if (methodName == "onJoin") { - info.partyOnJoin = event; - } else if (methodName == "onLeave") { - info.partyOnLeave = event; - } else if (methodName == "onDisband") { - info.partyOnDisband = event; - } else if (methodName == "onShareExperience") { - info.partyOnShareExperience = event; - } else { + partyOnJoin = event; + } + else if (methodName == "onLeave") { + partyOnLeave = event; + } + else if (methodName == "onDisband") { + partyOnDisband = event; + } + else if (methodName == "onShareExperience") { + partyOnShareExperience = event; + } + else { std::cout << "[Warning - Events::load] Unknown party method: " << methodName << std::endl; } - } else if (className == "Player") { - if (methodName == "onBrowseField") { - info.playerOnBrowseField = event; - } else if (methodName == "onLook") { - info.playerOnLook = event; - } else if (methodName == "onLookInBattleList") { - info.playerOnLookInBattleList = event; - } else if (methodName == "onLookInTrade") { - info.playerOnLookInTrade = event; - } else if (methodName == "onLookInShop") { - info.playerOnLookInShop = event; - } else if (methodName == "onTradeRequest") { - info.playerOnTradeRequest = event; - } else if (methodName == "onTradeAccept") { - info.playerOnTradeAccept = event; - } else if (methodName == "onMoveItem") { - info.playerOnMoveItem = event; - } else if (methodName == "onItemMoved") { - info.playerOnItemMoved = event; - } else if (methodName == "onMoveCreature") { - info.playerOnMoveCreature = event; - } else if (methodName == "onReportRuleViolation") { - info.playerOnReportRuleViolation = event; - } else if (methodName == "onReportBug") { - info.playerOnReportBug = event; - } else if (methodName == "onTurn") { - info.playerOnTurn = event; - } else if (methodName == "onGainExperience") { - info.playerOnGainExperience = event; - } else if (methodName == "onLoseExperience") { - info.playerOnLoseExperience = event; - } else if (methodName == "onGainSkillTries") { - info.playerOnGainSkillTries = event; - } else { + } + else if (className == "Player") { + if (methodName == "onLook") { + playerOnLook = event; + } + else if (methodName == "onLookInBattleList") { + playerOnLookInBattleList = event; + } + else if (methodName == "onLookInTrade") { + playerOnLookInTrade = event; + } + else if (methodName == "onTradeRequest") { + playerOnTradeRequest = event; + } + else if (methodName == "onTradeAccept") { + playerOnTradeAccept = event; + } + else if (methodName == "onMoveItem") { + playerOnMoveItem = event; + } + else if (methodName == "onItemMoved") { + playerOnItemMoved = event; + } + else if (methodName == "onMoveCreature") { + playerOnMoveCreature = event; + } + else if (methodName == "onReportBug") { + playerOnReportBug = event; + } + else if (methodName == "onTurn") { + playerOnTurn = event; + } + else if (methodName == "onGainExperience") { + playerOnGainExperience = event; + } + else if (methodName == "onLoseExperience") { + playerOnLoseExperience = event; + } + else if (methodName == "onGainSkillTries") { + playerOnGainSkillTries = event; + } + else { std::cout << "[Warning - Events::load] Unknown player method: " << methodName << std::endl; } - } else if (className == "Monster") { - if (methodName == "onDropLoot") { - info.monsterOnDropLoot = event; - } else { - std::cout << "[Warning - Events::load] Unknown monster method: " << methodName << std::endl; - } - } else { + } + else { std::cout << "[Warning - Events::load] Unknown class: " << className << std::endl; } } @@ -136,7 +175,7 @@ bool Events::load() bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit) { // Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit) - if (info.creatureOnChangeOutfit == -1) { + if (creatureOnChangeOutfit == -1) { return true; } @@ -146,10 +185,10 @@ bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& out } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.creatureOnChangeOutfit, &scriptInterface); + env->setScriptId(creatureOnChangeOutfit, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.creatureOnChangeOutfit); + scriptInterface.pushFunction(creatureOnChangeOutfit); LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); @@ -162,7 +201,7 @@ bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& out ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive) { // Creature:onAreaCombat(tile, aggressive) or Creature.onAreaCombat(self, tile, aggressive) - if (info.creatureOnAreaCombat == -1) { + if (creatureOnAreaCombat == -1) { return RETURNVALUE_NOERROR; } @@ -172,15 +211,16 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.creatureOnAreaCombat, &scriptInterface); + env->setScriptId(creatureOnAreaCombat, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.creatureOnAreaCombat); + scriptInterface.pushFunction(creatureOnAreaCombat); if (creature) { LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); - } else { + } + else { lua_pushnil(L); } @@ -193,7 +233,8 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo if (scriptInterface.protectedCall(L, 3, 1) != 0) { returnValue = RETURNVALUE_NOTPOSSIBLE; LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { + } + else { returnValue = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } @@ -205,7 +246,7 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* target) { // Creature:onTargetCombat(target) or Creature.onTargetCombat(self, target) - if (info.creatureOnTargetCombat == -1) { + if (creatureOnTargetCombat == -1) { return RETURNVALUE_NOERROR; } @@ -215,15 +256,16 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.creatureOnTargetCombat, &scriptInterface); + env->setScriptId(creatureOnTargetCombat, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.creatureOnTargetCombat); + scriptInterface.pushFunction(creatureOnTargetCombat); if (creature) { LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); - } else { + } + else { lua_pushnil(L); } @@ -234,7 +276,8 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta if (scriptInterface.protectedCall(L, 2, 1) != 0) { returnValue = RETURNVALUE_NOTPOSSIBLE; LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { + } + else { returnValue = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } @@ -247,7 +290,7 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta bool Events::eventPartyOnJoin(Party* party, Player* player) { // Party:onJoin(player) or Party.onJoin(self, player) - if (info.partyOnJoin == -1) { + if (partyOnJoin == -1) { return true; } @@ -257,10 +300,10 @@ bool Events::eventPartyOnJoin(Party* party, Player* player) } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.partyOnJoin, &scriptInterface); + env->setScriptId(partyOnJoin, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.partyOnJoin); + scriptInterface.pushFunction(partyOnJoin); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); @@ -274,7 +317,7 @@ bool Events::eventPartyOnJoin(Party* party, Player* player) bool Events::eventPartyOnLeave(Party* party, Player* player) { // Party:onLeave(player) or Party.onLeave(self, player) - if (info.partyOnLeave == -1) { + if (partyOnLeave == -1) { return true; } @@ -284,10 +327,10 @@ bool Events::eventPartyOnLeave(Party* party, Player* player) } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.partyOnLeave, &scriptInterface); + env->setScriptId(partyOnLeave, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.partyOnLeave); + scriptInterface.pushFunction(partyOnLeave); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); @@ -301,7 +344,7 @@ bool Events::eventPartyOnLeave(Party* party, Player* player) bool Events::eventPartyOnDisband(Party* party) { // Party:onDisband() or Party.onDisband(self) - if (info.partyOnDisband == -1) { + if (partyOnDisband == -1) { return true; } @@ -311,10 +354,10 @@ bool Events::eventPartyOnDisband(Party* party) } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.partyOnDisband, &scriptInterface); + env->setScriptId(partyOnDisband, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.partyOnDisband); + scriptInterface.pushFunction(partyOnDisband); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); @@ -325,7 +368,7 @@ bool Events::eventPartyOnDisband(Party* party) void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp) { // Party:onShareExperience(exp) or Party.onShareExperience(self, exp) - if (info.partyOnShareExperience == -1) { + if (partyOnShareExperience == -1) { return; } @@ -335,10 +378,10 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp) } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.partyOnShareExperience, &scriptInterface); + env->setScriptId(partyOnShareExperience, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.partyOnShareExperience); + scriptInterface.pushFunction(partyOnShareExperience); LuaScriptInterface::pushUserdata(L, party); LuaScriptInterface::setMetatable(L, -1, "Party"); @@ -347,7 +390,8 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp) if (scriptInterface.protectedCall(L, 2, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { + } + else { exp = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } @@ -355,37 +399,10 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp) scriptInterface.resetScriptEnv(); } -// Player -bool Events::eventPlayerOnBrowseField(Player* player, const Position& position) -{ - // Player:onBrowseField(position) or Player.onBrowseField(self, position) - if (info.playerOnBrowseField == -1) { - return true; - } - - if (!scriptInterface.reserveScriptEnv()) { - std::cout << "[Error - Events::eventPlayerOnBrowseField] Call stack overflow" << std::endl; - return false; - } - - ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnBrowseField, &scriptInterface); - - lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnBrowseField); - - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - - LuaScriptInterface::pushPosition(L, position); - - return scriptInterface.callFunction(2); -} - void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance) { // Player:onLook(thing, position, distance) or Player.onLook(self, thing, position, distance) - if (info.playerOnLook == -1) { + if (playerOnLook == -1) { return; } @@ -395,10 +412,10 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnLook, &scriptInterface); + env->setScriptId(playerOnLook, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnLook); + scriptInterface.pushFunction(playerOnLook); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -406,10 +423,12 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* if (Creature* creature = thing->getCreature()) { LuaScriptInterface::pushUserdata(L, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature); - } else if (Item* item = thing->getItem()) { + } + else if (Item* item = thing->getItem()) { LuaScriptInterface::pushUserdata(L, item); LuaScriptInterface::setItemMetatable(L, -1, item); - } else { + } + else { lua_pushnil(L); } @@ -422,7 +441,7 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance) { // Player:onLookInBattleList(creature, position, distance) or Player.onLookInBattleList(self, creature, position, distance) - if (info.playerOnLookInBattleList == -1) { + if (playerOnLookInBattleList == -1) { return; } @@ -432,10 +451,10 @@ void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, i } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnLookInBattleList, &scriptInterface); + env->setScriptId(playerOnLookInBattleList, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnLookInBattleList); + scriptInterface.pushFunction(playerOnLookInBattleList); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -451,7 +470,7 @@ void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, i void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance) { // Player:onLookInTrade(partner, item, distance) or Player.onLookInTrade(self, partner, item, distance) - if (info.playerOnLookInTrade == -1) { + if (playerOnLookInTrade == -1) { return; } @@ -461,10 +480,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnLookInTrade, &scriptInterface); + env->setScriptId(playerOnLookInTrade, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnLookInTrade); + scriptInterface.pushFunction(playerOnLookInTrade); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -480,39 +499,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite scriptInterface.callVoidFunction(4); } -bool Events::eventPlayerOnLookInShop(Player* player, const ItemType* itemType, uint8_t count) -{ - // Player:onLookInShop(itemType, count) or Player.onLookInShop(self, itemType, count) - if (info.playerOnLookInShop == -1) { - return true; - } - - if (!scriptInterface.reserveScriptEnv()) { - std::cout << "[Error - Events::eventPlayerOnLookInShop] Call stack overflow" << std::endl; - return false; - } - - ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnLookInShop, &scriptInterface); - - lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnLookInShop); - - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - - LuaScriptInterface::pushUserdata(L, itemType); - LuaScriptInterface::setMetatable(L, -1, "ItemType"); - - lua_pushnumber(L, count); - - return scriptInterface.callFunction(3); -} - bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder) { // Player:onMoveItem(item, count, fromPosition, toPosition) or Player.onMoveItem(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder) - if (info.playerOnMoveItem == -1) { + if (playerOnMoveItem == -1) { return true; } @@ -522,10 +512,10 @@ bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, c } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnMoveItem, &scriptInterface); + env->setScriptId(playerOnMoveItem, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnMoveItem); + scriptInterface.pushFunction(playerOnMoveItem); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -546,7 +536,7 @@ bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, c void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder) { // Player:onItemMoved(item, count, fromPosition, toPosition) or Player.onItemMoved(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder) - if (info.playerOnItemMoved == -1) { + if (playerOnItemMoved == -1) { return; } @@ -556,10 +546,10 @@ void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnItemMoved, &scriptInterface); + env->setScriptId(playerOnItemMoved, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnItemMoved); + scriptInterface.pushFunction(playerOnItemMoved); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -580,7 +570,7 @@ void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition) { // Player:onMoveCreature(creature, fromPosition, toPosition) or Player.onMoveCreature(self, creature, fromPosition, toPosition) - if (info.playerOnMoveCreature == -1) { + if (playerOnMoveCreature == -1) { return true; } @@ -590,10 +580,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnMoveCreature, &scriptInterface); + env->setScriptId(playerOnMoveCreature, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnMoveCreature); + scriptInterface.pushFunction(playerOnMoveCreature); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -607,42 +597,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const return scriptInterface.callFunction(4); } -void Events::eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation) -{ - // Player:onReportRuleViolation(targetName, reportType, reportReason, comment, translation) - if (info.playerOnReportRuleViolation == -1) { - return; - } - - if (!scriptInterface.reserveScriptEnv()) { - std::cout << "[Error - Events::eventPlayerOnReportRuleViolation] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnReportRuleViolation, &scriptInterface); - - lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnReportRuleViolation); - - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - - LuaScriptInterface::pushString(L, targetName); - - lua_pushnumber(L, reportType); - lua_pushnumber(L, reportReason); - - LuaScriptInterface::pushString(L, comment); - LuaScriptInterface::pushString(L, translation); - - scriptInterface.callVoidFunction(6); -} - -bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position, uint8_t category) +bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position) { // Player:onReportBug(message, position, category) - if (info.playerOnReportBug == -1) { + if (playerOnReportBug == -1) { return true; } @@ -652,25 +610,24 @@ bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnReportBug, &scriptInterface); + env->setScriptId(playerOnReportBug, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnReportBug); + scriptInterface.pushFunction(playerOnReportBug); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushString(L, message); LuaScriptInterface::pushPosition(L, position); - lua_pushnumber(L, category); - return scriptInterface.callFunction(4); + return scriptInterface.callFunction(3); } bool Events::eventPlayerOnTurn(Player* player, Direction direction) { // Player:onTurn(direction) or Player.onTurn(self, direction) - if (info.playerOnTurn == -1) { + if (playerOnTurn == -1) { return true; } @@ -680,10 +637,10 @@ bool Events::eventPlayerOnTurn(Player* player, Direction direction) } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnTurn, &scriptInterface); + env->setScriptId(playerOnTurn, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnTurn); + scriptInterface.pushFunction(playerOnTurn); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -696,7 +653,7 @@ bool Events::eventPlayerOnTurn(Player* player, Direction direction) bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* item) { // Player:onTradeRequest(target, item) - if (info.playerOnTradeRequest == -1) { + if (playerOnTradeRequest == -1) { return true; } @@ -706,10 +663,10 @@ bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* ite } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnTradeRequest, &scriptInterface); + env->setScriptId(playerOnTradeRequest, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnTradeRequest); + scriptInterface.pushFunction(playerOnTradeRequest); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -726,7 +683,7 @@ bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* ite bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem) { // Player:onTradeAccept(target, item, targetItem) - if (info.playerOnTradeAccept == -1) { + if (playerOnTradeAccept == -1) { return true; } @@ -736,10 +693,10 @@ bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnTradeAccept, &scriptInterface); + env->setScriptId(playerOnTradeAccept, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnTradeAccept); + scriptInterface.pushFunction(playerOnTradeAccept); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -760,7 +717,7 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6 { // Player:onGainExperience(source, exp, rawExp) // rawExp gives the original exp which is not multiplied - if (info.playerOnGainExperience == -1) { + if (playerOnGainExperience == -1) { return; } @@ -770,10 +727,10 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6 } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnGainExperience, &scriptInterface); + env->setScriptId(playerOnGainExperience, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnGainExperience); + scriptInterface.pushFunction(playerOnGainExperience); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -781,7 +738,8 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6 if (source) { LuaScriptInterface::pushUserdata(L, source); LuaScriptInterface::setCreatureMetatable(L, -1, source); - } else { + } + else { lua_pushnil(L); } @@ -790,7 +748,8 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6 if (scriptInterface.protectedCall(L, 4, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { + } + else { exp = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } @@ -801,7 +760,7 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6 void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp) { // Player:onLoseExperience(exp) - if (info.playerOnLoseExperience == -1) { + if (playerOnLoseExperience == -1) { return; } @@ -811,10 +770,10 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp) } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnLoseExperience, &scriptInterface); + env->setScriptId(playerOnLoseExperience, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnLoseExperience); + scriptInterface.pushFunction(playerOnLoseExperience); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -823,7 +782,8 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp) if (scriptInterface.protectedCall(L, 2, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { + } + else { exp = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } @@ -834,7 +794,7 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp) void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries) { // Player:onGainSkillTries(skill, tries) - if (info.playerOnGainSkillTries == -1) { + if (playerOnGainSkillTries == -1) { return; } @@ -844,10 +804,10 @@ void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_ } ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.playerOnGainSkillTries, &scriptInterface); + env->setScriptId(playerOnGainSkillTries, &scriptInterface); lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.playerOnGainSkillTries); + scriptInterface.pushFunction(playerOnGainSkillTries); LuaScriptInterface::pushUserdata(L, player); LuaScriptInterface::setMetatable(L, -1, "Player"); @@ -857,37 +817,11 @@ void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_ if (scriptInterface.protectedCall(L, 3, 1) != 0) { LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); - } else { + } + else { tries = LuaScriptInterface::getNumber(L, -1); lua_pop(L, 1); } scriptInterface.resetScriptEnv(); -} - -void Events::eventMonsterOnDropLoot(Monster* monster, Container* corpse) -{ - // Monster:onDropLoot(corpse) - if (info.monsterOnDropLoot == -1) { - return; - } - - if (!scriptInterface.reserveScriptEnv()) { - std::cout << "[Error - Events::eventMonsterOnDropLoot] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface.getScriptEnv(); - env->setScriptId(info.monsterOnDropLoot, &scriptInterface); - - lua_State* L = scriptInterface.getLuaState(); - scriptInterface.pushFunction(info.monsterOnDropLoot); - - LuaScriptInterface::pushUserdata(L, monster); - LuaScriptInterface::setMetatable(L, -1, "Monster"); - - LuaScriptInterface::pushUserdata(L, corpse); - LuaScriptInterface::setMetatable(L, -1, "Container"); - - return scriptInterface.callVoidFunction(2); -} +} \ No newline at end of file diff --git a/src/events.h b/src/events.h index 2f81b6e..7582c29 100644 --- a/src/events.h +++ b/src/events.h @@ -1,6 +1,6 @@ /** * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Copyright (C) 2016 Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,80 +28,68 @@ class Tile; class Events { - struct EventsInfo { - // Creature - int32_t creatureOnChangeOutfit = -1; - int32_t creatureOnAreaCombat = -1; - int32_t creatureOnTargetCombat = -1; +public: + Events(); - // Party - int32_t partyOnJoin = -1; - int32_t partyOnLeave = -1; - int32_t partyOnDisband = -1; - int32_t partyOnShareExperience = -1; + void clear(); + bool load(); - // Player - int32_t playerOnBrowseField = -1; - int32_t playerOnLook = -1; - int32_t playerOnLookInBattleList = -1; - int32_t playerOnLookInTrade = -1; - int32_t playerOnLookInShop = -1; - int32_t playerOnMoveItem = -1; - int32_t playerOnItemMoved = -1; - int32_t playerOnMoveCreature = -1; - int32_t playerOnReportRuleViolation = -1; - int32_t playerOnReportBug = -1; - int32_t playerOnTurn = -1; - int32_t playerOnTradeRequest = -1; - int32_t playerOnTradeAccept = -1; - int32_t playerOnGainExperience = -1; - int32_t playerOnLoseExperience = -1; - int32_t playerOnGainSkillTries = -1; + // Creature + bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit); + ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive); + ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target); - // Monster - int32_t monsterOnDropLoot = -1; - }; + // Party + bool eventPartyOnJoin(Party* party, Player* player); + bool eventPartyOnLeave(Party* party, Player* player); + bool eventPartyOnDisband(Party* party); + void eventPartyOnShareExperience(Party* party, uint64_t& exp); - public: - Events(); + // Player + void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance); + void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance); + void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance); + bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); + void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); + bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition); + void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation); + bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position); + bool eventPlayerOnTurn(Player* player, Direction direction); + bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item); + bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem); + void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp); + void eventPlayerOnLoseExperience(Player* player, uint64_t& exp); + void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries); - bool load(); +private: + LuaScriptInterface scriptInterface; - // Creature - bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit); - ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive); - ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target); + // Creature + int32_t creatureOnChangeOutfit; + int32_t creatureOnAreaCombat; + int32_t creatureOnTargetCombat; - // Party - bool eventPartyOnJoin(Party* party, Player* player); - bool eventPartyOnLeave(Party* party, Player* player); - bool eventPartyOnDisband(Party* party); - void eventPartyOnShareExperience(Party* party, uint64_t& exp); + // Party + int32_t partyOnJoin; + int32_t partyOnLeave; + int32_t partyOnDisband; + int32_t partyOnShareExperience; - // Player - bool eventPlayerOnBrowseField(Player* player, const Position& position); - void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance); - void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance); - void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance); - bool eventPlayerOnLookInShop(Player* player, const ItemType* itemType, uint8_t count); - bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); - void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); - bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition); - void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation); - bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position, uint8_t category); - bool eventPlayerOnTurn(Player* player, Direction direction); - bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item); - bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem); - void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp); - void eventPlayerOnLoseExperience(Player* player, uint64_t& exp); - void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries); - - // Monster - void eventMonsterOnDropLoot(Monster* monster, Container* corpse); - - private: - LuaScriptInterface scriptInterface; - EventsInfo info; + // Player + int32_t playerOnLook; + int32_t playerOnLookInBattleList; + int32_t playerOnLookInTrade; + int32_t playerOnMoveItem; + int32_t playerOnItemMoved; + int32_t playerOnMoveCreature; + int32_t playerOnReportRuleViolation; + int32_t playerOnReportBug; + int32_t playerOnTurn; + int32_t playerOnTradeRequest; + int32_t playerOnTradeAccept; + int32_t playerOnGainExperience; + int32_t playerOnLoseExperience; + int32_t playerOnGainSkillTries; }; -#endif +#endif \ No newline at end of file diff --git a/src/fileloader.cpp b/src/fileloader.cpp index 5c754fd..be25ecf 100644 --- a/src/fileloader.cpp +++ b/src/fileloader.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,106 +19,387 @@ #include "otpch.h" -#include #include "fileloader.h" - -namespace OTB { - -constexpr Identifier wildcard = {{'\0', '\0', '\0', '\0'}}; - -Loader::Loader(const std::string& fileName, const Identifier& acceptedIdentifier): - fileContents(fileName) +FileLoader::~FileLoader() { - constexpr auto minimalSize = sizeof(Identifier) + sizeof(Node::START) + sizeof(Node::type) + sizeof(Node::END); - if (fileContents.size() <= minimalSize) { - throw InvalidOTBFormat{}; + if (file) { + fclose(file); + file = nullptr; } - Identifier fileIdentifier; - std::copy(fileContents.begin(), fileContents.begin() + fileIdentifier.size(), fileIdentifier.begin()); - if (fileIdentifier != acceptedIdentifier && fileIdentifier != wildcard) { - throw InvalidOTBFormat{}; + NodeStruct::clearNet(root); + delete[] buffer; + + for (auto& i : cached_data) { + delete[] i.data; } } -using NodeStack = std::stack>; -static Node& getCurrentNode(const NodeStack& nodeStack) { - if (nodeStack.empty()) { - throw InvalidOTBFormat{}; +bool FileLoader::openFile(const char* filename, const char* accept_identifier) +{ + file = fopen(filename, "rb"); + if (!file) { + lastError = ERROR_CAN_NOT_OPEN; + return false; } - return *nodeStack.top(); + + char identifier[4]; + if (fread(identifier, 1, 4, file) < 4) { + fclose(file); + file = nullptr; + lastError = ERROR_EOF; + return false; + } + + // The first four bytes must either match the accept identifier or be 0x00000000 (wildcard) + if (memcmp(identifier, accept_identifier, 4) != 0 && memcmp(identifier, "\0\0\0\0", 4) != 0) { + fclose(file); + file = nullptr; + lastError = ERROR_INVALID_FILE_VERSION; + return false; + } + + fseek(file, 0, SEEK_END); + int32_t file_size = ftell(file); + cache_size = std::min(32768, std::max(file_size / 20, 8192)) & ~0x1FFF; + + if (!safeSeek(4)) { + lastError = ERROR_INVALID_FORMAT; + return false; + } + + delete root; + root = new NodeStruct(); + root->start = 4; + + int32_t byte; + if (safeSeek(4) && readByte(byte) && byte == NODE_START) { + return parseNode(root); + } + + return false; } -const Node& Loader::parseTree() +bool FileLoader::parseNode(NODE node) { - auto it = fileContents.begin() + sizeof(Identifier); - if (static_cast(*it) != Node::START) { - throw InvalidOTBFormat{}; - } - root.type = *(++it); - root.propsBegin = ++it; - NodeStack parseStack; - parseStack.push(&root); + int32_t byte, pos; + NODE currentNode = node; - for (; it != fileContents.end(); ++it) { - switch(static_cast(*it)) { - case Node::START: { - auto& currentNode = getCurrentNode(parseStack); - if (currentNode.children.empty()) { - currentNode.propsEnd = it; - } - currentNode.children.emplace_back(); - auto& child = currentNode.children.back(); - if (++it == fileContents.end()) { - throw InvalidOTBFormat{}; - } - child.type = *it; - child.propsBegin = it + sizeof(Node::type); - parseStack.push(&child); - break; + while (readByte(byte)) { + currentNode->type = byte; + bool setPropsSize = false; + + while (true) { + if (!readByte(byte)) { + return false; } - case Node::END: { - auto& currentNode = getCurrentNode(parseStack); - if (currentNode.children.empty()) { - currentNode.propsEnd = it; + + bool skipNode = false; + + switch (byte) { + case NODE_START: { + //child node start + if (!safeTell(pos)) { + return false; + } + + NODE childNode = new NodeStruct(); + childNode->start = pos; + currentNode->propsSize = pos - currentNode->start - 2; + currentNode->child = childNode; + + setPropsSize = true; + + if (!parseNode(childNode)) { + return false; + } + + break; } - parseStack.pop(); - break; - } - case Node::ESCAPE: { - if (++it == fileContents.end()) { - throw InvalidOTBFormat{}; + + case NODE_END: { + //current node end + if (!setPropsSize) { + if (!safeTell(pos)) { + return false; + } + + currentNode->propsSize = pos - currentNode->start - 2; + } + + if (!readByte(byte)) { + return true; + } + + switch (byte) { + case NODE_START: { + //starts next node + if (!safeTell(pos)) { + return false; + } + + skipNode = true; + NODE nextNode = new NodeStruct(); + nextNode->start = pos; + currentNode->next = nextNode; + currentNode = nextNode; + break; + } + + case NODE_END: + return safeTell(pos) && safeSeek(pos); + + default: + lastError = ERROR_INVALID_FORMAT; + return false; + } + + break; } - break; + + case ESCAPE_CHAR: { + if (!readByte(byte)) { + return false; + } + + break; + } + + default: + break; } - default: { + + if (skipNode) { break; } } } - if (!parseStack.empty()) { - throw InvalidOTBFormat{}; + return false; +} + +const uint8_t* FileLoader::getProps(const NODE node, size_t& size) +{ + if (!node) { + return nullptr; } + if (node->propsSize >= buffer_size) { + delete[] buffer; + + while (node->propsSize >= buffer_size) { + buffer_size *= 2; + } + + buffer = new uint8_t[buffer_size]; + } + + //get buffer + if (!readBytes(node->propsSize, node->start + 2)) { + return nullptr; + } + + //unscape buffer + size_t j = 0; + bool escaped = false; + for (uint32_t i = 0; i < node->propsSize; ++i, ++j) { + if (buffer[i] == ESCAPE_CHAR) { + //escape char found, skip it and write next + buffer[j] = buffer[++i]; + //is neede a displacement for next bytes + escaped = true; + } else if (escaped) { + //perform that displacement + buffer[j] = buffer[i]; + } + } + + size = j; + return buffer; +} + +bool FileLoader::getProps(const NODE node, PropStream& props) +{ + size_t size; + if (const uint8_t* a = getProps(node, size)) { + props.init(reinterpret_cast(a), size); // does not break strict aliasing + return true; + } + + props.init(nullptr, 0); + return false; +} + +NODE FileLoader::getChildNode(const NODE parent, uint32_t& type) +{ + if (parent) { + NODE child = parent->child; + if (child) { + type = child->type; + } + + return child; + } + + type = root->type; return root; } -bool Loader::getProps(const Node& node, PropStream& props) +NODE FileLoader::getNextNode(const NODE prev, uint32_t& type) { - auto size = std::distance(node.propsBegin, node.propsEnd); - if (size == 0) { + if (!prev) { + return NO_NODE; + } + + NODE next = prev->next; + if (next) { + type = next->type; + } + return next; +} + +inline bool FileLoader::readByte(int32_t& value) +{ + if (cache_index == NO_VALID_CACHE) { + lastError = ERROR_CACHE_ERROR; return false; } - propBuffer.resize(size); - bool lastEscaped = false; - auto escapedPropEnd = std::copy_if(node.propsBegin, node.propsEnd, propBuffer.begin(), [&lastEscaped](const char& byte) { - lastEscaped = byte == static_cast(Node::ESCAPE) && !lastEscaped; - return !lastEscaped; - }); - props.init(&propBuffer[0], std::distance(propBuffer.begin(), escapedPropEnd)); + if (cache_offset >= cached_data[cache_index].size) { + int32_t pos = cache_offset + cached_data[cache_index].base; + int32_t tmp = getCacheBlock(pos); + if (tmp < 0) { + return false; + } + + cache_index = tmp; + cache_offset = pos - cached_data[cache_index].base; + if (cache_offset >= cached_data[cache_index].size) { + return false; + } + } + + value = cached_data[cache_index].data[cache_offset++]; return true; } -} //namespace OTB +inline bool FileLoader::readBytes(uint32_t size, int32_t pos) +{ + //seek at pos + uint32_t remain = size; + uint8_t* buf = this->buffer; + do { + //prepare cache + uint32_t i = getCacheBlock(pos); + if (i == NO_VALID_CACHE) { + return false; + } + + cache_index = i; + cache_offset = pos - cached_data[i].base; + + //get maximum read block size and calculate remaining bytes + uint32_t reading = std::min(remain, cached_data[i].size - cache_offset); + remain -= reading; + + //read it + memcpy(buf, cached_data[cache_index].data + cache_offset, reading); + + //update variables + cache_offset += reading; + buf += reading; + pos += reading; + } while (remain > 0); + return true; +} + +inline bool FileLoader::safeSeek(uint32_t pos) +{ + uint32_t i = getCacheBlock(pos); + if (i == NO_VALID_CACHE) { + return false; + } + + cache_index = i; + cache_offset = pos - cached_data[i].base; + return true; +} + +inline bool FileLoader::safeTell(int32_t& pos) +{ + if (cache_index == NO_VALID_CACHE) { + lastError = ERROR_CACHE_ERROR; + return false; + } + + pos = cached_data[cache_index].base + cache_offset - 1; + return true; +} + +inline uint32_t FileLoader::getCacheBlock(uint32_t pos) +{ + bool found = false; + uint32_t i, base_pos = pos & ~(cache_size - 1); + + for (i = 0; i < CACHE_BLOCKS; i++) { + if (cached_data[i].loaded) { + if (cached_data[i].base == base_pos) { + found = true; + break; + } + } + } + + if (!found) { + i = loadCacheBlock(pos); + } + + return i; +} + +int32_t FileLoader::loadCacheBlock(uint32_t pos) +{ + int32_t i, loading_cache = -1, base_pos = pos & ~(cache_size - 1); + + for (i = 0; i < CACHE_BLOCKS; i++) { + if (!cached_data[i].loaded) { + loading_cache = i; + break; + } + } + + if (loading_cache == -1) { + for (i = 0; i < CACHE_BLOCKS; i++) { + if (std::abs(static_cast(cached_data[i].base) - base_pos) > static_cast(2 * cache_size)) { + loading_cache = i; + break; + } + } + + if (loading_cache == -1) { + loading_cache = 0; + } + } + + if (cached_data[loading_cache].data == nullptr) { + cached_data[loading_cache].data = new uint8_t[cache_size]; + } + + cached_data[loading_cache].base = base_pos; + + if (fseek(file, cached_data[loading_cache].base, SEEK_SET) != 0) { + lastError = ERROR_SEEK_ERROR; + return -1; + } + + uint32_t size = fread(cached_data[loading_cache].data, 1, cache_size, file); + cached_data[loading_cache].size = size; + + if (size < (pos - cached_data[loading_cache].base)) { + lastError = ERROR_SEEK_ERROR; + return -1; + } + + cached_data[loading_cache].loaded = 1; + return loading_cache; +} diff --git a/src/fileloader.h b/src/fileloader.h index c55f624..74872b0 100644 --- a/src/fileloader.h +++ b/src/fileloader.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,53 +22,133 @@ #include #include -#include + +struct NodeStruct; + +typedef NodeStruct* NODE; + +struct NodeStruct { + uint32_t start = 0; + uint32_t propsSize = 0; + uint32_t type = 0; + NodeStruct* next = nullptr; + NodeStruct* child = nullptr; + + static void clearNet(NodeStruct* root) { + if (root) { + clearChild(root); + } + } + + private: + static void clearNext(NodeStruct* node) { + NodeStruct* deleteNode = node; + NodeStruct* nextNode; + + while (deleteNode) { + if (deleteNode->child) { + clearChild(deleteNode->child); + } + + nextNode = deleteNode->next; + delete deleteNode; + deleteNode = nextNode; + } + } + + static void clearChild(NodeStruct* node) { + if (node->child) { + clearChild(node->child); + } + + if (node->next) { + clearNext(node->next); + } + + delete node; + } +}; + +static constexpr auto NO_NODE = nullptr; + +enum FILELOADER_ERRORS { + ERROR_NONE, + ERROR_INVALID_FILE_VERSION, + ERROR_CAN_NOT_OPEN, + ERROR_CAN_NOT_CREATE, + ERROR_EOF, + ERROR_SEEK_ERROR, + ERROR_NOT_OPEN, + ERROR_INVALID_NODE, + ERROR_INVALID_FORMAT, + ERROR_TELL_ERROR, + ERROR_COULDNOTWRITE, + ERROR_CACHE_ERROR, +}; class PropStream; -namespace OTB { -using MappedFile = boost::iostreams::mapped_file_source; -using ContentIt = MappedFile::iterator; -using Identifier = std::array; - -struct Node +class FileLoader { - using ChildrenVector = std::vector; + public: + FileLoader() = default; + ~FileLoader(); - ChildrenVector children; - ContentIt propsBegin; - ContentIt propsEnd; - uint8_t type; - enum NodeChar: uint8_t - { - ESCAPE = 0xFD, - START = 0xFE, - END = 0xFF, - }; + // non-copyable + FileLoader(const FileLoader&) = delete; + FileLoader& operator=(const FileLoader&) = delete; + + bool openFile(const char* filename, const char* identifier); + const uint8_t* getProps(const NODE, size_t& size); + bool getProps(const NODE, PropStream& props); + NODE getChildNode(const NODE parent, uint32_t& type); + NODE getNextNode(const NODE prev, uint32_t& type); + + FILELOADER_ERRORS getError() const { + return lastError; + } + + protected: + enum SPECIAL_BYTES { + ESCAPE_CHAR = 0xFD, + NODE_START = 0xFE, + NODE_END = 0xFF, + }; + + bool parseNode(NODE node); + + inline bool readByte(int32_t& value); + inline bool readBytes(uint32_t size, int32_t pos); + inline bool safeSeek(uint32_t pos); + inline bool safeTell(int32_t& pos); + + protected: + struct cache { + uint8_t* data; + uint32_t loaded; + uint32_t base; + uint32_t size; + }; + + static constexpr int32_t CACHE_BLOCKS = 3; + cache cached_data[CACHE_BLOCKS] = {}; + + uint8_t* buffer = new uint8_t[1024]; + NODE root = nullptr; + FILE* file = nullptr; + + FILELOADER_ERRORS lastError = ERROR_NONE; + uint32_t buffer_size = 1024; + + uint32_t cache_size = 0; + static constexpr uint32_t NO_VALID_CACHE = std::numeric_limits::max(); + uint32_t cache_index = NO_VALID_CACHE; + uint32_t cache_offset = NO_VALID_CACHE; + + inline uint32_t getCacheBlock(uint32_t pos); + int32_t loadCacheBlock(uint32_t pos); }; -struct LoadError : std::exception { - const char* what() const noexcept override = 0; -}; - -struct InvalidOTBFormat final : LoadError { - const char* what() const noexcept override { - return "Invalid OTBM file format"; - } -}; - -class Loader { - MappedFile fileContents; - Node root; - std::vector propBuffer; -public: - Loader(const std::string& fileName, const Identifier& acceptedIdentifier); - bool getProps(const Node& node, PropStream& props); - const Node& parseTree(); -}; - -} //namespace OTB - class PropStream { public: @@ -120,7 +200,7 @@ class PropStream return true; } - private: + protected: const char* p = nullptr; const char* end = nullptr; }; @@ -160,7 +240,7 @@ class PropWriteStream std::copy(str.begin(), str.end(), std::back_inserter(buffer)); } - private: + protected: std::vector buffer; }; diff --git a/src/game.cpp b/src/game.cpp index be8bf46..114b961 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,26 +21,22 @@ #include "pugicast.h" -#include "actions.h" -#include "bed.h" -#include "configmanager.h" +#include "items.h" #include "creature.h" -#include "creatureevent.h" -#include "databasetasks.h" +#include "monster.h" #include "events.h" #include "game.h" -#include "globalevent.h" +#include "actions.h" #include "iologindata.h" -#include "iomarket.h" -#include "items.h" -#include "monster.h" -#include "movement.h" -#include "scheduler.h" -#include "server.h" -#include "spells.h" #include "talkaction.h" -#include "weapons.h" -#include "script.h" +#include "spells.h" +#include "configmanager.h" +#include "server.h" +#include "globalevent.h" +#include "bed.h" +#include "scheduler.h" +#include "databasetasks.h" +#include "movement.h" extern ConfigManager g_config; extern Actions* g_actions; @@ -49,26 +45,10 @@ extern TalkActions* g_talkActions; extern Spells* g_spells; extern Vocations g_vocations; extern GlobalEvents* g_globalEvents; -extern CreatureEvents* g_creatureEvents; extern Events* g_events; +extern CreatureEvents* g_creatureEvents; extern Monsters g_monsters; extern MoveEvents* g_moveEvents; -extern Weapons* g_weapons; -extern Scripts* g_scripts; - -Game::Game() -{ - offlineTrainingWindow.choices.emplace_back("Sword Fighting and Shielding", SKILL_SWORD); - offlineTrainingWindow.choices.emplace_back("Axe Fighting and Shielding", SKILL_AXE); - offlineTrainingWindow.choices.emplace_back("Club Fighting and Shielding", SKILL_CLUB); - offlineTrainingWindow.choices.emplace_back("Distance Fighting and Shielding", SKILL_DISTANCE); - offlineTrainingWindow.choices.emplace_back("Magic Level and Shielding", SKILL_MAGLEVEL); - offlineTrainingWindow.buttons.emplace_back("Okay", 1); - offlineTrainingWindow.buttons.emplace_back("Cancel", 0); - offlineTrainingWindow.defaultEnterButton = 1; - offlineTrainingWindow.defaultEscapeButton = 0; - offlineTrainingWindow.priority = true; -} Game::~Game() { @@ -119,9 +99,6 @@ void Game::setGameState(GameState_t newState) raids.loadFromXml(); raids.startup(); - quests.loadFromXml(); - mounts.loadFromXml(); - loadMotdNum(); loadPlayersRecord(); @@ -187,8 +164,6 @@ void Game::saveGameState() Map::save(); - g_databaseTasks.flush(); - if (gameState == GAME_STATE_MAINTAIN) { setGameState(GAME_STATE_NORMAL); } @@ -257,7 +232,7 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index } case STACKPOS_USETARGET: { - thing = tile->getTopVisibleCreature(player); + thing = tile->getTopCreature(); if (!thing) { thing = tile->getUseItem(index); } @@ -296,32 +271,17 @@ Thing* Game::internalGetThing(Player* player, const Position& pos, int32_t index return nullptr; } - if (parentContainer->getID() == ITEM_BROWSEFIELD) { - Tile* tile = parentContainer->getTile(); - if (tile && tile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) { - if (tile->hasProperty(CONST_PROP_ISVERTICAL)) { - if (player->getPosition().x + 1 == tile->getPosition().x) { - return nullptr; - } - } else { // horizontal - if (player->getPosition().y + 1 == tile->getPosition().y) { - return nullptr; - } - } - } - } - uint8_t slot = pos.z; return parentContainer->getItemByIndex(player->getContainerIndex(fromCid) + slot); } else if (pos.y == 0 && pos.z == 0) { - const ItemType& it = Item::items.getItemIdByClientId(spriteId); + const ItemType& it = Item::items.getItemType(spriteId); if (it.id == 0) { return nullptr; } int32_t subType; - if (it.isFluidContainer() && index < static_cast(sizeof(reverseFluidMap) / sizeof(uint8_t))) { - subType = reverseFluidMap[index]; + if (it.isFluidContainer()) { + subType = static_cast(index); } else { subType = -1; } @@ -541,15 +501,15 @@ bool Game::placeCreature(Creature* creature, const Position& pos, bool extendedP return false; } - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, creature->getPosition(), true); + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { tmpPlayer->sendCreatureAppear(creature, creature->getPosition(), true); } } - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onCreatureAppear(creature, true); } @@ -570,9 +530,9 @@ bool Game::removeCreature(Creature* creature, bool isLogout/* = true*/) std::vector oldStackPosVector; - SpectatorVec spectators; - map.getSpectators(spectators, tile->getPosition(), true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, tile->getPosition(), true); + for (Creature* spectator : list) { if (Player* player = spectator->getPlayer()) { oldStackPosVector.push_back(player->canSeeCreature(creature) ? tile->getStackposOfCreature(player, creature) : -1); } @@ -584,14 +544,14 @@ bool Game::removeCreature(Creature* creature, bool isLogout/* = true*/) //send to client size_t i = 0; - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* player = spectator->getPlayer()) { player->sendRemoveTileThing(tilePosition, oldStackPosVector[i++]); } } //event method - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onRemoveCreature(creature, isLogout); } @@ -604,7 +564,7 @@ bool Game::removeCreature(Creature* creature, bool isLogout/* = true*/) removeCreatureCheck(creature); for (Creature* summon : creature->summons) { - summon->setSkillLoss(false); + summon->setLossSkill(false); removeCreature(summon); } return true; @@ -694,6 +654,13 @@ void Game::playerMoveCreature(Player* player, Creature* movingCreature, const Po player->setNextActionTask(nullptr); + if (g_config.getBoolean(ConfigManager::BLOCK_HEIGHT)) { + if (toTile->getHeight() > 1) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return; + } + } + if (!Position::areInRange<1, 1, 0>(movingCreatureOrigPos, player->getPosition())) { //need to walk to the creature first before moving it std::forward_list listDir; @@ -763,45 +730,64 @@ ReturnValue Game::internalMoveCreature(Creature* creature, Direction direction, creature->setLastPosition(creature->getPosition()); const Position& currentPos = creature->getPosition(); Position destPos = getNextPosition(direction, currentPos); - Player* player = creature->getPlayer(); bool diagonalMovement = (direction & DIRECTION_DIAGONAL_MASK) != 0; - if (player && !diagonalMovement) { + if (creature->getPlayer() && !diagonalMovement) { //try go up if (currentPos.z != 8 && creature->getTile()->hasHeight(3)) { Tile* tmpTile = map.getTile(currentPos.x, currentPos.y, currentPos.getZ() - 1); if (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID))) { tmpTile = map.getTile(destPos.x, destPos.y, destPos.getZ() - 1); if (tmpTile && tmpTile->getGround() && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)) { - flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE; - - if (!tmpTile->hasFlag(TILESTATE_FLOORCHANGE)) { - player->setDirection(direction); - destPos.z--; - } + destPos.z--; + internalCreatureTurn(creature, DIRECTION_NORTH); } } } - - //try go down - if (currentPos.z != 7 && currentPos.z == destPos.z) { + else { + //try go down Tile* tmpTile = map.getTile(destPos.x, destPos.y, destPos.z); - if (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID))) { + if (currentPos.z != 7 && (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID)))) { tmpTile = map.getTile(destPos.x, destPos.y, destPos.z + 1); if (tmpTile && tmpTile->hasHeight(3)) { - flags |= FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE; - player->setDirection(direction); destPos.z++; + internalCreatureTurn(creature, DIRECTION_SOUTH); } } } } + ReturnValue ret = RETURNVALUE_NOTPOSSIBLE; Tile* toTile = map.getTile(destPos); - if (!toTile) { - return RETURNVALUE_NOTPOSSIBLE; + + Tile* toPos = map.getTile(destPos.x, destPos.y, destPos.z); + Tile* fromPos = map.getTile(currentPos.x, currentPos.y, currentPos.z); + + if (g_config.getBoolean(ConfigManager::BLOCK_HEIGHT)) { + if (toTile) { + if (currentPos.z > destPos.z && toPos->getHeight() > 1); + // not possible + else if ((((toPos->getHeight() - fromPos->getHeight()) < 2)) || + (fromPos->hasHeight(3) && (currentPos.z == destPos.z)) || + ((currentPos.z < destPos.z) && (toPos->hasHeight(3) && (fromPos->getHeight() < 2)))) + ret = internalMoveCreature(*creature, *toTile, flags); + } + + if (ret != RETURNVALUE_NOERROR) { + if (Player* player = creature->getPlayer()) { + player->sendCancelMessage(ret); + player->sendCancelWalk(); + } + } + + return ret; + } + else { + if (!toTile) { + return RETURNVALUE_NOTPOSSIBLE; + } + return internalMoveCreature(*creature, *toTile, flags); } - return internalMoveCreature(*creature, *toTile, flags); } ReturnValue Game::internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags /*= 0*/) @@ -900,7 +886,7 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, item = thing->getItem(); } - if (item->getClientID() != spriteId) { + if ((item->isDisguised() && item->getDisguiseId() != spriteId) || (!item->isDisguised() && item->getID() != spriteId)) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); return; } @@ -919,7 +905,7 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, } } - if (!item->isPushable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { + if (!item->isPushable()) { player->sendCancelMessage(RETURNVALUE_NOTMOVEABLE); return; } @@ -1032,25 +1018,17 @@ void Game::playerMoveItem(Player* player, const Position& fromPos, } } - ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, count, nullptr, 0, player); + ReturnValue ret = internalMoveItem(fromCylinder, toCylinder, toIndex, item, item->isRune() ? item->getItemCount() : count, nullptr, 0, player); if (ret != RETURNVALUE_NOERROR) { player->sendCancelMessage(ret); } else { - g_events->eventPlayerOnItemMoved(player, item, count, fromPos, toPos, fromCylinder, toCylinder); + g_events->eventPlayerOnItemMoved(player, item, item->isRune() ? item->getItemCount() : count, fromPos, toPos, fromCylinder, toCylinder); } } ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index, Item* item, uint32_t count, Item** _moveItem, uint32_t flags /*= 0*/, Creature* actor/* = nullptr*/, Item* tradeItem/* = nullptr*/) { - Tile* fromTile = fromCylinder->getTile(); - if (fromTile) { - auto it = browseFields.find(fromTile); - if (it != browseFields.end() && it->second == fromCylinder) { - fromCylinder = fromTile; - } - } - Item* toItem = nullptr; Cylinder* subCylinder; @@ -1118,8 +1096,14 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, uint32_t m; if (item->isStackable()) { - m = std::min(count, maxQueryCount); - } else { + if (item->isRune()) { + m = std::min(item->getItemCount(), maxQueryCount); + } + else { + m = std::min(count, maxQueryCount); + } + } + else { m = maxQueryCount; } @@ -1155,11 +1139,12 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, if (item->isStackable()) { uint32_t n; - if (item->equals(toItem)) { + if (!item->isRune() && item->equals(toItem)) { n = std::min(100 - toItem->getItemCount(), m); toCylinder->updateThing(toItem, toItem->getID(), toItem->getItemCount() + n); updateItem = toItem; - } else { + } + else { n = 0; } @@ -1212,14 +1197,6 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, return retMaxCount; } - if (moveItem && moveItem->getDuration() > 0) { - if (moveItem->getDecaying() != DECAYING_TRUE) { - moveItem->incrementReferenceCounter(); - moveItem->setDecaying(DECAYING_TRUE); - toDecayItems.push_front(moveItem); - } - } - return ret; } @@ -1262,7 +1239,7 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde return RETURNVALUE_NOERROR; } - if (item->isStackable() && item->equals(toItem)) { + if (item->isStackable() && !item->isRune() && item->equals(toItem)) { uint32_t m = std::min(item->getItemCount(), maxQueryCount); uint32_t n = std::min(100 - toItem->getItemCount(), m); @@ -1277,7 +1254,8 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde ReleaseItem(remainderItem); remainderCount = count; } - } else { + } + else { toCylinder->addThing(index, item); int32_t itemIndex = toCylinder->getThingIndex(item); @@ -1285,7 +1263,8 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde toCylinder->postAddNotification(item, nullptr, itemIndex); } } - } else { + } + else { //fully merged with toItem, item will be destroyed item->onRemoved(); ReleaseItem(item); @@ -1295,7 +1274,8 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde toCylinder->postAddNotification(toItem, nullptr, itemIndex); } } - } else { + } + else { toCylinder->addThing(index, item); int32_t itemIndex = toCylinder->getThingIndex(item); @@ -1304,12 +1284,6 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde } } - if (item->getDuration() > 0) { - item->incrementReferenceCounter(); - item->setDecaying(DECAYING_TRUE); - toDecayItems.push_front(item); - } - return RETURNVALUE_NOERROR; } @@ -1320,14 +1294,6 @@ ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool te return RETURNVALUE_NOTPOSSIBLE; } - Tile* fromTile = cylinder->getTile(); - if (fromTile) { - auto it = browseFields.find(fromTile); - if (it != browseFields.end() && it->second == cylinder) { - cylinder = fromTile; - } - } - if (count == -1) { count = item->getItemCount(); } @@ -1349,16 +1315,13 @@ ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool te cylinder->removeThing(item, count); if (item->isRemoved()) { - item->onRemoved(); - if (item->canDecay()) { - decayItems->remove(item); - } ReleaseItem(item); } cylinder->postRemoveNotification(item, nullptr, index); } + item->onRemoved(); return RETURNVALUE_NOERROR; } @@ -1562,14 +1525,6 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) return nullptr; } - Tile* fromTile = cylinder->getTile(); - if (fromTile) { - auto it = browseFields.find(fromTile); - if (it != browseFields.end() && it->second == cylinder) { - cylinder = fromTile; - } - } - int32_t itemIndex = cylinder->getThingIndex(item); if (itemIndex == -1) { return item; @@ -1616,7 +1571,7 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) } else { int32_t newItemId = newId; if (curType.id == newType.id) { - newItemId = item->getDecayTo(); + newItemId = curType.decayTo; } if (newItemId < 0) { @@ -1682,14 +1637,6 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) cylinder->postRemoveNotification(item, cylinder, itemIndex); ReleaseItem(item); - if (newItem->getDuration() > 0) { - if (newItem->getDecaying() != DECAYING_TRUE) { - newItem->incrementReferenceCounter(); - newItem->setDecaying(DECAYING_TRUE); - toDecayItems.push_front(newItem); - } - } - return newItem; } @@ -1712,6 +1659,19 @@ ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pu return ret; } + Position fromPos = creature->getPosition(); + if (Position::getOffsetX(fromPos, newPos) <= 0) { + if (Position::getOffsetX(fromPos, newPos) < 0) { + internalCreatureTurn(creature, DIRECTION_EAST); + } else if (Position::getOffsetY(fromPos, newPos) < 0) { + internalCreatureTurn(creature, DIRECTION_SOUTH); + } else if (Position::getOffsetY(fromPos, newPos) > 0) { + internalCreatureTurn(creature, DIRECTION_NORTH); + } + } else { + internalCreatureTurn(creature, DIRECTION_WEST); + } + map.moveCreature(*creature, *toTile, !pushMove); return RETURNVALUE_NOERROR; } else if (Item* item = thing->getItem()) { @@ -1720,75 +1680,7 @@ ReturnValue Game::internalTeleport(Thing* thing, const Position& newPos, bool pu return RETURNVALUE_NOTPOSSIBLE; } -Item* searchForItem(Container* container, uint16_t itemId) -{ - for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { - if ((*it)->getID() == itemId) { - return *it; - } - } - - return nullptr; -} - -slots_t getSlotType(const ItemType& it) -{ - slots_t slot = CONST_SLOT_RIGHT; - if (it.weaponType != WeaponType_t::WEAPON_SHIELD) { - int32_t slotPosition = it.slotPosition; - - if (slotPosition & SLOTP_HEAD) { - slot = CONST_SLOT_HEAD; - } else if (slotPosition & SLOTP_NECKLACE) { - slot = CONST_SLOT_NECKLACE; - } else if (slotPosition & SLOTP_ARMOR) { - slot = CONST_SLOT_ARMOR; - } else if (slotPosition & SLOTP_LEGS) { - slot = CONST_SLOT_LEGS; - } else if (slotPosition & SLOTP_FEET) { - slot = CONST_SLOT_FEET ; - } else if (slotPosition & SLOTP_RING) { - slot = CONST_SLOT_RING; - } else if (slotPosition & SLOTP_AMMO) { - slot = CONST_SLOT_AMMO; - } else if (slotPosition & SLOTP_TWO_HAND || slotPosition & SLOTP_LEFT) { - slot = CONST_SLOT_LEFT; - } - } - - return slot; -} - //Implementation of player invoked events -void Game::playerEquipItem(uint32_t playerId, uint16_t spriteId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - Item* item = player->getInventoryItem(CONST_SLOT_BACKPACK); - if (!item) { - return; - } - - Container* backpack = item->getContainer(); - if (!backpack) { - return; - } - - const ItemType& it = Item::items.getItemIdByClientId(spriteId); - slots_t slot = getSlotType(it); - - Item* slotItem = player->getInventoryItem(slot); - Item* equipItem = searchForItem(backpack, it.id); - if (slotItem && slotItem->getID() == it.id && (!it.stackable || slotItem->getItemCount() == 100 || !equipItem)) { - internalMoveItem(slotItem->getParent(), player, CONST_SLOT_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr); - } else if (equipItem) { - internalMoveItem(equipItem->getParent(), player, slot, equipItem, equipItem->getItemCount(), nullptr); - } -} - void Game::playerMove(uint32_t playerId, Direction direction) { Player* player = getPlayerByID(playerId); @@ -1902,15 +1794,11 @@ void Game::playerOpenChannel(uint32_t playerId, uint16_t channelId) return; } - const InvitedMap* invitedUsers = channel->getInvitedUsers(); - const UsersMap* users; - if (!channel->isPublicChannel()) { - users = &channel->getUsers(); + if (channel->getId() == CHANNEL_RULE_REP) { + player->sendRuleViolationsChannel(channel->getId()); } else { - users = nullptr; + player->sendChannel(channel->getId(), channel->getName()); } - - player->sendChannel(channel->getId(), channel->getName(), users, invitedUsers); } void Game::playerCloseChannel(uint32_t playerId, uint16_t channelId) @@ -1935,30 +1823,9 @@ void Game::playerOpenPrivateChannel(uint32_t playerId, std::string& receiver) return; } - if (player->getName() == receiver) { - player->sendCancelMessage("You cannot set up a private message channel with yourself."); - return; - } - player->sendOpenPrivateChannel(receiver); } -void Game::playerCloseNpcChannel(uint32_t playerId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - SpectatorVec spectators; - map.getSpectators(spectators, player->getPosition()); - for (Creature* spectator : spectators) { - if (Npc* npc = spectator->getNpc()) { - npc->onPlayerCloseChannel(player); - } - } -} - void Game::playerReceivePing(uint32_t playerId) { Player* player = getPlayerByID(playerId); @@ -2021,7 +1888,7 @@ void Game::playerUseItemEx(uint32_t playerId, const Position& fromPos, uint8_t f } Item* item = thing->getItem(); - if (!item || !item->isUseable() || item->getClientID() != fromSpriteId) { + if (!item || (item->isDisguised() && item->getDisguiseId() != fromSpriteId) || (!item->isDisguised() && item->getID() != fromSpriteId)) { player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT); return; } @@ -2105,7 +1972,7 @@ void Game::playerUseItem(uint32_t playerId, const Position& pos, uint8_t stackPo } Item* item = thing->getItem(); - if (!item || item->isUseable() || item->getClientID() != spriteId) { + if (!item || (item->isDisguised() && item->getDisguiseId() != spriteId) || (!item->isDisguised() && item->getID() != spriteId)) { player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT); return; } @@ -2176,7 +2043,7 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position& fromPos, uin } Item* item = thing->getItem(); - if (!item || !item->isUseable() || item->getClientID() != spriteId) { + if (!item || (item->isDisguised() && item->getDisguiseId() != spriteId) || (!item->isDisguised() && item->getID() != spriteId)) { player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT); return; } @@ -2265,20 +2132,7 @@ void Game::playerMoveUpContainer(uint32_t playerId, uint8_t cid) Container* parentContainer = dynamic_cast(container->getRealParent()); if (!parentContainer) { - Tile* tile = container->getTile(); - if (!tile) { - return; - } - - auto it = browseFields.find(tile); - if (it == browseFields.end()) { - parentContainer = new Container(tile); - parentContainer->incrementReferenceCounter(); - browseFields[tile] = parentContainer; - g_scheduler.addEvent(createSchedulerTask(30000, std::bind(&Game::decreaseBrowseFieldRef, this, tile->getPosition()))); - } else { - parentContainer = it->second; - } + return; } player->addContainer(cid, parentContainer); @@ -2313,7 +2167,7 @@ void Game::playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stac } Item* item = thing->getItem(); - if (!item || item->getClientID() != spriteId || !item->isRotatable() || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { + if (!item || (item->isDisguised() && item->getDisguiseId() != spriteId) || !item->isRotatable() || (!item->isDisguised() && item->getID() != spriteId)) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); return; } @@ -2372,13 +2226,6 @@ void Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std:: return; } - for (auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_TEXTEDIT)) { - if (!creatureEvent->executeTextEdit(player, writeItem, text)) { - player->setWriteItem(nullptr); - return; - } - } - if (!text.empty()) { if (writeItem->getText() != text) { writeItem->setText(text); @@ -2399,66 +2246,6 @@ void Game::playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std:: player->setWriteItem(nullptr); } -void Game::playerBrowseField(uint32_t playerId, const Position& pos) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - const Position& playerPos = player->getPosition(); - if (playerPos.z != pos.z) { - player->sendCancelMessage(playerPos.z > pos.z ? RETURNVALUE_FIRSTGOUPSTAIRS : RETURNVALUE_FIRSTGODOWNSTAIRS); - return; - } - - if (!Position::areInRange<1, 1>(playerPos, pos)) { - std::forward_list listDir; - if (player->getPathTo(pos, listDir, 0, 1, true, true)) { - g_dispatcher.addTask(createTask(std::bind(&Game::playerAutoWalk, - this, player->getID(), listDir))); - SchedulerTask* task = createSchedulerTask(400, std::bind( - &Game::playerBrowseField, this, playerId, pos - )); - player->setNextWalkActionTask(task); - } else { - player->sendCancelMessage(RETURNVALUE_THEREISNOWAY); - } - return; - } - - Tile* tile = map.getTile(pos); - if (!tile) { - return; - } - - if (!g_events->eventPlayerOnBrowseField(player, pos)) { - return; - } - - Container* container; - - auto it = browseFields.find(tile); - if (it == browseFields.end()) { - container = new Container(tile); - container->incrementReferenceCounter(); - browseFields[tile] = container; - g_scheduler.addEvent(createSchedulerTask(30000, std::bind(&Game::decreaseBrowseFieldRef, this, tile->getPosition()))); - } else { - container = it->second; - } - - uint8_t dummyContainerId = 0xF - ((pos.x % 3) * 3 + (pos.y % 3)); - Container* openContainer = player->getContainerByID(dummyContainerId); - if (openContainer) { - player->onCloseContainer(openContainer); - player->closeContainer(dummyContainerId); - } else { - player->addContainer(dummyContainerId, container); - player->sendContainer(dummyContainerId, container, false, 0); - } -} - void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index) { Player* player = getPlayerByID(playerId); @@ -2467,7 +2254,7 @@ void Game::playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_ } Container* container = player->getContainerByID(containerId); - if (!container || !container->hasPagination()) { + if (!container) { return; } @@ -2530,7 +2317,7 @@ void Game::playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t st } Item* tradeItem = tradeThing->getItem(); - if (tradeItem->getClientID() != spriteId || !tradeItem->isPickupable() || tradeItem->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { + if (!tradeItem->isPickupable() || (tradeItem->isDisguised() && tradeItem->getDisguiseId() != spriteId) || (!tradeItem->isDisguised() && tradeItem->getID() != spriteId)) { player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); return; } @@ -2663,10 +2450,10 @@ void Game::playerAcceptTrade(uint32_t playerId) player->setTradeState(TRADE_ACCEPT); if (tradePartner->getTradeState() == TRADE_ACCEPT) { - Item* playerTradeItem = player->tradeItem; - Item* partnerTradeItem = tradePartner->tradeItem; + Item* tradeItem1 = player->tradeItem; + Item* tradeItem2 = tradePartner->tradeItem; - if (!g_events->eventPlayerOnTradeAccept(player, tradePartner, playerTradeItem, partnerTradeItem)) { + if (!g_events->eventPlayerOnTradeAccept(player, tradePartner, tradeItem1, tradeItem2)) { internalCloseTrade(player); return; } @@ -2674,13 +2461,13 @@ void Game::playerAcceptTrade(uint32_t playerId) player->setTradeState(TRADE_TRANSFER); tradePartner->setTradeState(TRADE_TRANSFER); - auto it = tradeItems.find(playerTradeItem); + auto it = tradeItems.find(tradeItem1); if (it != tradeItems.end()) { ReleaseItem(it->first); tradeItems.erase(it); } - it = tradeItems.find(partnerTradeItem); + it = tradeItems.find(tradeItem2); if (it != tradeItems.end()) { ReleaseItem(it->first); tradeItems.erase(it); @@ -2688,17 +2475,25 @@ void Game::playerAcceptTrade(uint32_t playerId) bool isSuccess = false; - ReturnValue tradePartnerRet = internalAddItem(tradePartner, playerTradeItem, INDEX_WHEREEVER, 0, true); - ReturnValue playerRet = internalAddItem(player, partnerTradeItem, INDEX_WHEREEVER, 0, true); - if (tradePartnerRet == RETURNVALUE_NOERROR && playerRet == RETURNVALUE_NOERROR) { - playerRet = internalRemoveItem(playerTradeItem, playerTradeItem->getItemCount(), true); - tradePartnerRet = internalRemoveItem(partnerTradeItem, partnerTradeItem->getItemCount(), true); - if (tradePartnerRet == RETURNVALUE_NOERROR && playerRet == RETURNVALUE_NOERROR) { - tradePartnerRet = internalMoveItem(playerTradeItem->getParent(), tradePartner, INDEX_WHEREEVER, playerTradeItem, playerTradeItem->getItemCount(), nullptr, FLAG_IGNOREAUTOSTACK, nullptr, partnerTradeItem); - if (tradePartnerRet == RETURNVALUE_NOERROR) { - internalMoveItem(partnerTradeItem->getParent(), player, INDEX_WHEREEVER, partnerTradeItem, partnerTradeItem->getItemCount(), nullptr, FLAG_IGNOREAUTOSTACK); - playerTradeItem->onTradeEvent(ON_TRADE_TRANSFER, tradePartner); - partnerTradeItem->onTradeEvent(ON_TRADE_TRANSFER, player); + ReturnValue ret1 = internalAddItem(tradePartner, tradeItem1, INDEX_WHEREEVER, 0, true); + ReturnValue ret2 = internalAddItem(player, tradeItem2, INDEX_WHEREEVER, 0, true); + if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) { + ret1 = internalRemoveItem(tradeItem1, tradeItem1->getItemCount(), true); + ret2 = internalRemoveItem(tradeItem2, tradeItem2->getItemCount(), true); + if (ret1 == RETURNVALUE_NOERROR && ret2 == RETURNVALUE_NOERROR) { + Cylinder* cylinder1 = tradeItem1->getParent(); + Cylinder* cylinder2 = tradeItem2->getParent(); + + uint32_t count1 = tradeItem1->getItemCount(); + uint32_t count2 = tradeItem2->getItemCount(); + + ret1 = internalMoveItem(cylinder1, tradePartner, INDEX_WHEREEVER, tradeItem1, count1, nullptr, FLAG_IGNOREAUTOSTACK, nullptr, tradeItem2); + if (ret1 == RETURNVALUE_NOERROR) { + internalMoveItem(cylinder2, player, INDEX_WHEREEVER, tradeItem2, count2, nullptr, FLAG_IGNOREAUTOSTACK); + + tradeItem1->onTradeEvent(ON_TRADE_TRANSFER, tradePartner); + tradeItem2->onTradeEvent(ON_TRADE_TRANSFER, player); + isSuccess = true; } } @@ -2708,13 +2503,13 @@ void Game::playerAcceptTrade(uint32_t playerId) std::string errorDescription; if (tradePartner->tradeItem) { - errorDescription = getTradeErrorDescription(tradePartnerRet, playerTradeItem); + errorDescription = getTradeErrorDescription(ret1, tradeItem1); tradePartner->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription); tradePartner->tradeItem->onTradeEvent(ON_TRADE_CANCEL, tradePartner); } if (player->tradeItem) { - errorDescription = getTradeErrorDescription(playerRet, partnerTradeItem); + errorDescription = getTradeErrorDescription(ret2, tradeItem2); player->sendTextMessage(MESSAGE_EVENT_ADVANCE, errorDescription); player->tradeItem->onTradeEvent(ON_TRADE_CANCEL, player); } @@ -2745,7 +2540,7 @@ std::string Game::getTradeErrorDescription(ReturnValue ret, Item* item) ss << " this object."; } - ss << "\n " << item->getWeightDescription(); + ss << std::endl << ' ' << item->getWeightDescription(); return ss.str(); } else if (ret == RETURNVALUE_NOTENOUGHROOM || ret == RETURNVALUE_CONTAINERNOTENOUGHROOM) { std::ostringstream ss; @@ -2791,6 +2586,7 @@ void Game::playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t int32_t lookDistance = std::max(Position::getDistanceX(playerPosition, tradeItemPosition), Position::getDistanceY(playerPosition, tradeItemPosition)); + if (index == 0) { g_events->eventPlayerOnLookInTrade(player, tradePartner, tradeItem, lookDistance); return; @@ -2873,126 +2669,6 @@ void Game::internalCloseTrade(Player* player) } } -void Game::playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, - bool ignoreCap/* = false*/, bool inBackpacks/* = false*/) -{ - if (amount == 0 || amount > 100) { - return; - } - - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - int32_t onBuy, onSell; - - Npc* merchant = player->getShopOwner(onBuy, onSell); - if (!merchant) { - return; - } - - const ItemType& it = Item::items.getItemIdByClientId(spriteId); - if (it.id == 0) { - return; - } - - uint8_t subType; - if (it.isSplash() || it.isFluidContainer()) { - subType = clientFluidToServer(count); - } else { - subType = count; - } - - if (!player->hasShopItemForSale(it.id, subType)) { - return; - } - - merchant->onPlayerTrade(player, onBuy, it.id, subType, amount, ignoreCap, inBackpacks); -} - -void Game::playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, bool ignoreEquipped) -{ - if (amount == 0 || amount > 100) { - return; - } - - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - int32_t onBuy, onSell; - - Npc* merchant = player->getShopOwner(onBuy, onSell); - if (!merchant) { - return; - } - - const ItemType& it = Item::items.getItemIdByClientId(spriteId); - if (it.id == 0) { - return; - } - - uint8_t subType; - if (it.isSplash() || it.isFluidContainer()) { - subType = clientFluidToServer(count); - } else { - subType = count; - } - - merchant->onPlayerTrade(player, onSell, it.id, subType, amount, ignoreEquipped); -} - -void Game::playerCloseShop(uint32_t playerId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - player->closeShopWindow(); -} - -void Game::playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - int32_t onBuy, onSell; - - Npc* merchant = player->getShopOwner(onBuy, onSell); - if (!merchant) { - return; - } - - const ItemType& it = Item::items.getItemIdByClientId(spriteId); - if (it.id == 0) { - return; - } - - int32_t subType; - if (it.isFluidContainer() || it.isSplash()) { - subType = clientFluidToServer(count); - } else { - subType = count; - } - - if (!player->hasShopItemForSale(it.id, subType)) { - return; - } - - if (!g_events->eventPlayerOnLookInShop(player, &it, subType)) { - return; - } - - std::ostringstream ss; - ss << "You see " << Item::getDescription(it, 1, nullptr, subType); - player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); -} - void Game::playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos) { Player* player = getPlayerByID(playerId); @@ -3118,7 +2794,7 @@ void Game::playerFollowCreature(uint32_t playerId, uint32_t creatureId) player->setFollowCreature(getCreatureByID(creatureId)); } -void Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, bool chaseMode, bool secureMode) +void Game::playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, bool secureMode) { Player* player = getPlayerByID(playerId); if (!player) { @@ -3181,16 +2857,6 @@ void Game::playerRequestRemoveVip(uint32_t playerId, uint32_t guid) player->removeVIP(guid); } -void Game::playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string& description, uint32_t icon, bool notify) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - player->editVIP(guid, description, icon, notify); -} - void Game::playerTurn(uint32_t playerId, Direction dir) { Player* player = getPlayerByID(playerId); @@ -3220,16 +2886,6 @@ void Game::playerRequestOutfit(uint32_t playerId) player->sendOutfitWindow(); } -void Game::playerToggleMount(uint32_t playerId, bool mount) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - player->toggleMount(mount); -} - void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit) { if (!g_config.getBoolean(ConfigManager::ALLOW_CHANGEOUTFIT)) { @@ -3241,36 +2897,6 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit) return; } - const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(player->getSex(), outfit.lookType); - if (!playerOutfit) { - outfit.lookMount = 0; - } - - if (outfit.lookMount != 0) { - Mount* mount = mounts.getMountByClientID(outfit.lookMount); - if (!mount) { - return; - } - - if (!player->hasMount(mount)) { - return; - } - - if (player->isMounted()) { - Mount* prevMount = mounts.getMountByID(player->getCurrentMount()); - if (prevMount) { - changeSpeed(player, mount->speed - prevMount->speed); - } - - player->setCurrentMount(mount->id); - } else { - player->setCurrentMount(mount->id); - outfit.lookMount = 0; - } - } else if (player->isMounted()) { - player->dismount(); - } - if (player->canWear(outfit.lookType, outfit.lookAddons)) { player->defaultOutfit = outfit; @@ -3282,31 +2908,6 @@ void Game::playerChangeOutfit(uint32_t playerId, Outfit_t outfit) } } -void Game::playerShowQuestLog(uint32_t playerId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - player->sendQuestLog(); -} - -void Game::playerShowQuestLine(uint32_t playerId, uint16_t questId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - Quest* quest = quests.getQuestByID(questId); - if (!quest) { - return; - } - - player->sendQuestLine(quest); -} - void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, const std::string& receiver, const std::string& text) { @@ -3333,9 +2934,7 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, return; } - if (type != TALKTYPE_PRIVATE_PN) { - player->removeMessageBuffer(); - } + player->removeMessageBuffer(); switch (type) { case TALKTYPE_SAY: @@ -3350,25 +2949,35 @@ void Game::playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, playerYell(player, text); break; - case TALKTYPE_PRIVATE_TO: - case TALKTYPE_PRIVATE_RED_TO: + case TALKTYPE_PRIVATE: + case TALKTYPE_PRIVATE_RED: + case TALKTYPE_RVR_ANSWER: playerSpeakTo(player, type, receiver, text); break; case TALKTYPE_CHANNEL_O: case TALKTYPE_CHANNEL_Y: case TALKTYPE_CHANNEL_R1: - g_chat->talkToChannel(*player, type, text, channelId); - break; - - case TALKTYPE_PRIVATE_PN: - playerSpeakToNpc(player, text); + case TALKTYPE_CHANNEL_R2: + if (channelId == CHANNEL_RULE_REP) { + playerSay(playerId, 0, TALKTYPE_SAY, receiver, text); + } else { + g_chat->talkToChannel(*player, type, text, channelId); + } break; case TALKTYPE_BROADCAST: playerBroadcastMessage(player, text); break; + case TALKTYPE_RVR_CHANNEL: + playerReportRuleViolationReport(player, text); + break; + + case TALKTYPE_RVR_CONTINUE: + playerContinueRuleViolationReport(player, text); + break; + default: break; } @@ -3385,12 +2994,7 @@ bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& result = g_spells->playerSaySpell(player, words); if (result == TALKACTION_BREAK) { - if (!g_config.getBoolean(ConfigManager::EMOTE_SPELLS)) { - return internalCreatureSay(player, TALKTYPE_SAY, words, false); - } else { - return internalCreatureSay(player, TALKTYPE_MONSTER_SAY, words, false); - } - + return internalCreatureSay(player, TALKTYPE_SAY, text, false); } else if (result == TALKACTION_FAILED) { return true; } @@ -3400,13 +3004,13 @@ bool Game::playerSaySpell(Player* player, SpeakClasses type, const std::string& void Game::playerWhisper(Player* player, const std::string& text) { - SpectatorVec spectators; - map.getSpectators(spectators, player->getPosition(), false, false, + SpectatorVec list; + map.getSpectators(list, player->getPosition(), false, false, Map::maxClientViewportX, Map::maxClientViewportX, Map::maxClientViewportY, Map::maxClientViewportY); //send to client - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* spectatorPlayer = spectator->getPlayer()) { if (!Position::areInRange<1, 1>(player->getPosition(), spectatorPlayer->getPosition())) { spectatorPlayer->sendCreatureSay(player, TALKTYPE_WHISPER, "pspsps"); @@ -3417,7 +3021,7 @@ void Game::playerWhisper(Player* player, const std::string& text) } //event method - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onCreatureSay(player, TALKTYPE_WHISPER, text); } } @@ -3452,10 +3056,8 @@ bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& r return false; } - if (type == TALKTYPE_PRIVATE_RED_TO && (player->hasFlag(PlayerFlag_CanTalkRedPrivate) || player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER)) { - type = TALKTYPE_PRIVATE_RED_FROM; - } else { - type = TALKTYPE_PRIVATE_FROM; + if (type == TALKTYPE_PRIVATE_RED && (!player->hasFlag(PlayerFlag_CanTalkRedPrivate) || player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER)) { + type = TALKTYPE_PRIVATE; } toPlayer->sendPrivateMessage(player, type, text); @@ -3471,17 +3073,6 @@ bool Game::playerSpeakTo(Player* player, SpeakClasses type, const std::string& r return true; } -void Game::playerSpeakToNpc(Player* player, const std::string& text) -{ - SpectatorVec spectators; - map.getSpectators(spectators, player->getPosition()); - for (Creature* spectator : spectators) { - if (spectator->getNpc()) { - spectator->onCreatureSay(player, TALKTYPE_PRIVATE_PN, text); - } - } -} - //-- bool Game::canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight /*= true*/, int32_t rangex /*= Map::maxClientViewportX*/, int32_t rangey /*= Map::maxClientViewportY*/) const @@ -3503,16 +3094,16 @@ bool Game::internalCreatureTurn(Creature* creature, Direction dir) creature->setDirection(dir); //send to client - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, creature->getPosition(), true, true); + for (Creature* spectator : list) { spectator->getPlayer()->sendCreatureTurn(creature); } return true; } bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, - bool ghostMode, SpectatorVec* spectatorsPtr/* = nullptr*/, const Position* pos/* = nullptr*/) + bool ghostMode, SpectatorVec* listPtr/* = nullptr*/, const Position* pos/* = nullptr*/) { if (text.empty()) { return false; @@ -3522,26 +3113,26 @@ bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std: pos = &creature->getPosition(); } - SpectatorVec spectators; + SpectatorVec list; - if (!spectatorsPtr || spectatorsPtr->empty()) { + if (!listPtr || listPtr->empty()) { // This somewhat complex construct ensures that the cached SpectatorVec // is used if available and if it can be used, else a local vector is // used (hopefully the compiler will optimize away the construction of // the temporary when it's not used). if (type != TALKTYPE_YELL && type != TALKTYPE_MONSTER_YELL) { - map.getSpectators(spectators, *pos, false, false, + map.getSpectators(list, *pos, false, false, Map::maxClientViewportX, Map::maxClientViewportX, Map::maxClientViewportY, Map::maxClientViewportY); } else { - map.getSpectators(spectators, *pos, true, false, 18, 18, 14, 14); + map.getSpectators(list, *pos, true, false, 30, 30, 30, 30); } } else { - spectators = (*spectatorsPtr); + list = (*listPtr); } //send to client - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { if (!ghostMode || tmpPlayer->canSeeCreature(creature)) { tmpPlayer->sendCreatureSay(creature, type, text, pos); @@ -3550,7 +3141,7 @@ bool Game::internalCreatureSay(Creature* creature, SpeakClasses type, const std: } //event method - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onCreatureSay(creature, type, text); } return true; @@ -3631,15 +3222,12 @@ void Game::checkCreatures(size_t index) void Game::changeSpeed(Creature* creature, int32_t varSpeedDelta) { - int32_t varSpeed = creature->getSpeed() - creature->getBaseSpeed(); - varSpeed += varSpeedDelta; - - creature->setSpeed(varSpeed); + creature->setSpeed(varSpeedDelta); //send to clients - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), false, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, creature->getPosition(), false, true); + for (Creature* spectator : list) { spectator->getPlayer()->sendChangeSpeed(creature, creature->getStepSpeed()); } } @@ -3657,9 +3245,9 @@ void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outf } //send to clients - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, creature->getPosition(), true, true); + for (Creature* spectator : list) { spectator->getPlayer()->sendCreatureChangeOutfit(creature, outfit); } } @@ -3667,9 +3255,9 @@ void Game::internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outf void Game::internalCreatureChangeVisible(Creature* creature, bool visible) { //send to clients - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, creature->getPosition(), true, true); + for (Creature* spectator : list) { spectator->getPlayer()->sendCreatureChangeVisible(creature, visible); } } @@ -3677,16 +3265,16 @@ void Game::internalCreatureChangeVisible(Creature* creature, bool visible) void Game::changeLight(const Creature* creature) { //send to clients - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, creature->getPosition(), true, true); + for (Creature* spectator : list) { spectator->getPlayer()->sendCreatureLight(creature); } } bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* target, bool checkDefense, bool checkArmor, bool field) { - if (damage.primary.type == COMBAT_NONE && damage.secondary.type == COMBAT_NONE) { + if (damage.type == COMBAT_NONE) { return true; } @@ -3694,7 +3282,7 @@ bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* ta return true; } - if (damage.primary.value > 0) { + if (damage.value > 0) { return false; } @@ -3711,9 +3299,7 @@ bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* ta } case COMBAT_ENERGYDAMAGE: case COMBAT_FIREDAMAGE: - case COMBAT_PHYSICALDAMAGE: - case COMBAT_ICEDAMAGE: - case COMBAT_DEATHDAMAGE: { + case COMBAT_PHYSICALDAMAGE: { hitEffect = CONST_ME_BLOCKHIT; break; } @@ -3721,10 +3307,6 @@ bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* ta hitEffect = CONST_ME_GREEN_RINGS; break; } - case COMBAT_HOLYDAMAGE: { - hitEffect = CONST_ME_HOLYDAMAGE; - break; - } default: { hitEffect = CONST_ME_POFF; break; @@ -3734,27 +3316,18 @@ bool Game::combatBlockHit(CombatDamage& damage, Creature* attacker, Creature* ta } }; - BlockType_t primaryBlockType, secondaryBlockType; - if (damage.primary.type != COMBAT_NONE) { - damage.primary.value = -damage.primary.value; - primaryBlockType = target->blockHit(attacker, damage.primary.type, damage.primary.value, checkDefense, checkArmor, field); + BlockType_t primaryBlockType; + if (damage.type != COMBAT_NONE) { + damage.value = -damage.value; + primaryBlockType = target->blockHit(attacker, damage.type, damage.value, checkDefense, checkArmor, field); - damage.primary.value = -damage.primary.value; - sendBlockEffect(primaryBlockType, damage.primary.type, target->getPosition()); + damage.value = -damage.value; + sendBlockEffect(primaryBlockType, damage.type, target->getPosition()); } else { primaryBlockType = BLOCK_NONE; } - if (damage.secondary.type != COMBAT_NONE) { - damage.secondary.value = -damage.secondary.value; - secondaryBlockType = target->blockHit(attacker, damage.secondary.type, damage.secondary.value, false, false, field); - - damage.secondary.value = -damage.secondary.value; - sendBlockEffect(secondaryBlockType, damage.secondary.type, target->getPosition()); - } else { - secondaryBlockType = BLOCK_NONE; - } - return (primaryBlockType != BLOCK_NONE) && (secondaryBlockType != BLOCK_NONE); + return (primaryBlockType != BLOCK_NONE); } void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColor_t& color, uint8_t& effect) @@ -3771,11 +3344,7 @@ void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColo case RACE_BLOOD: color = TEXTCOLOR_RED; effect = CONST_ME_DRAWBLOOD; - if (const Tile* tile = target->getTile()) { - if (!tile->hasFlag(TILESTATE_PROTECTIONZONE)) { - splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD); - } - } + splash = Item::CreateItem(ITEM_SMALLSPLASH, FLUID_BLOOD); break; case RACE_UNDEAD: color = TEXTCOLOR_LIGHTGREY; @@ -3785,10 +3354,6 @@ void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColo color = TEXTCOLOR_ORANGE; effect = CONST_ME_DRAWBLOOD; break; - case RACE_ENERGY: - color = TEXTCOLOR_ELECTRICPURPLE; - effect = CONST_ME_ENERGYHIT; - break; default: color = TEXTCOLOR_NONE; effect = CONST_ME_NONE; @@ -3804,7 +3369,7 @@ void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColo } case COMBAT_ENERGYDAMAGE: { - color = TEXTCOLOR_ELECTRICPURPLE; + color = TEXTCOLOR_LIGHTBLUE; effect = CONST_ME_ENERGYHIT; break; } @@ -3815,36 +3380,23 @@ void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColo break; } - case COMBAT_DROWNDAMAGE: { - color = TEXTCOLOR_LIGHTBLUE; - effect = CONST_ME_LOSEENERGY; - break; - } case COMBAT_FIREDAMAGE: { color = TEXTCOLOR_ORANGE; effect = CONST_ME_HITBYFIRE; break; } - case COMBAT_ICEDAMAGE: { - color = TEXTCOLOR_SKYBLUE; - effect = CONST_ME_ICEATTACK; - break; - } - case COMBAT_HOLYDAMAGE: { - color = TEXTCOLOR_YELLOW; - effect = CONST_ME_HOLYDAMAGE; - break; - } - case COMBAT_DEATHDAMAGE: { - color = TEXTCOLOR_DARKRED; - effect = CONST_ME_SMALLCLOUDS; - break; - } + case COMBAT_LIFEDRAIN: { color = TEXTCOLOR_RED; effect = CONST_ME_MAGIC_RED; break; } + + case COMBAT_DROWNDAMAGE: { + color = TEXTCOLOR_LIGHTBLUE; + effect = CONST_ME_LOSEENERGY; + break; + } default: { color = TEXTCOLOR_NONE; effect = CONST_ME_NONE; @@ -3856,23 +3408,11 @@ void Game::combatGetTypeInfo(CombatType_t combatType, Creature* target, TextColo bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage& damage) { const Position& targetPos = target->getPosition(); - if (damage.primary.value > 0) { + if (damage.value > 0) { if (target->getHealth() <= 0) { return false; } - Player* attackerPlayer; - if (attacker) { - attackerPlayer = attacker->getPlayer(); - } else { - attackerPlayer = nullptr; - } - - Player* targetPlayer = target->getPlayer(); - if (attackerPlayer && targetPlayer && attackerPlayer->getSkull() == SKULL_BLACK && attackerPlayer->getSkullClient(targetPlayer) == SKULL_NONE) { - return false; - } - if (damage.origin != ORIGIN_NONE) { const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE); if (!events.empty()) { @@ -3885,66 +3425,24 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage } int32_t realHealthChange = target->getHealth(); - target->gainHealth(attacker, damage.primary.value); + target->gainHealth(attacker, damage.value); realHealthChange = target->getHealth() - realHealthChange; if (realHealthChange > 0 && !target->isInGhostMode()) { - std::stringstream ss; - - ss << realHealthChange << (realHealthChange != 1 ? " hitpoints." : " hitpoint."); - std::string damageString = ss.str(); - - std::string spectatorMessage; - - TextMessage message; - message.position = targetPos; - message.primary.value = realHealthChange; - message.primary.color = TEXTCOLOR_PASTELRED; - - SpectatorVec spectators; - map.getSpectators(spectators, targetPos, false, true); - for (Creature* spectator : spectators) { - Player* tmpPlayer = spectator->getPlayer(); - if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) { - ss.str({}); - ss << "You heal " << target->getNameDescription() << " for " << damageString; - message.type = MESSAGE_HEALED; - message.text = ss.str(); - } else if (tmpPlayer == targetPlayer) { - ss.str({}); - if (!attacker) { - ss << "You were healed"; - } else if (targetPlayer == attackerPlayer) { - ss << "You healed yourself"; - } else { - ss << "You were healed by " << attacker->getNameDescription(); - } - ss << " for " << damageString; - message.type = MESSAGE_HEALED; - message.text = ss.str(); - } else { - if (spectatorMessage.empty()) { - ss.str({}); - if (!attacker) { - ss << ucfirst(target->getNameDescription()) << " was healed"; - } else { - ss << ucfirst(attacker->getNameDescription()) << " healed "; - if (attacker == target) { - ss << (targetPlayer ? (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "herself" : "himself") : "itself"); - } else { - ss << target->getNameDescription(); - } - } - ss << " for " << damageString; - spectatorMessage = ss.str(); - } - message.type = MESSAGE_HEALED_OTHERS; - message.text = spectatorMessage; - } - tmpPlayer->sendTextMessage(message); + addMagicEffect(targetPos, CONST_ME_MAGIC_BLUE); + } + } + else { + if (Monster* monster = target->getMonster()) { + // makes monsters aggressive when damaged + // basically stands for UNDERATTACK stance under CipSoft servers + // the attacker must be valid everytime (avoid field ticks damage to trigger condition) + if (!monster->hasCondition(CONDITION_AGGRESSIVE) && attacker) { + Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_AGGRESSIVE, 3000); + monster->addCondition(condition, true); } } - } else { + if (!target->isAttackable()) { if (!target->isInGhostMode()) { addMagicEffect(targetPos, CONST_ME_POFF); @@ -3955,50 +3453,19 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage Player* attackerPlayer; if (attacker) { attackerPlayer = attacker->getPlayer(); - } else { + } + else { attackerPlayer = nullptr; } + damage.value = std::abs(damage.value); - Player* targetPlayer = target->getPlayer(); - if (attackerPlayer && targetPlayer && attackerPlayer->getSkull() == SKULL_BLACK && attackerPlayer->getSkullClient(targetPlayer) == SKULL_NONE) { - return false; - } - - damage.primary.value = std::abs(damage.primary.value); - damage.secondary.value = std::abs(damage.secondary.value); - - int32_t healthChange = damage.primary.value + damage.secondary.value; + int32_t healthChange = damage.value; if (healthChange == 0) { return true; } - - if (attackerPlayer) { - uint16_t chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_LIFELEECHCHANCE); - if (chance != 0 && uniform_random(1, 100) <= chance) { - CombatDamage lifeLeech; - lifeLeech.primary.value = std::round(healthChange * (attackerPlayer->getSpecialSkill(SPECIALSKILL_LIFELEECHAMOUNT) / 100.)); - combatChangeHealth(nullptr, attackerPlayer, lifeLeech); - } - - chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_MANALEECHCHANCE); - if (chance != 0 && uniform_random(1, 100) <= chance) { - CombatDamage manaLeech; - manaLeech.primary.value = std::round(healthChange * (attackerPlayer->getSpecialSkill(SPECIALSKILL_MANALEECHAMOUNT) / 100.)); - combatChangeMana(nullptr, attackerPlayer, manaLeech); - } - - chance = attackerPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITCHANCE); - if (chance != 0 && uniform_random(1, 100) <= chance) { - healthChange += std::round(healthChange * (attackerPlayer->getSpecialSkill(SPECIALSKILL_CRITICALHITAMOUNT) / 100.)); - addMagicEffect(target->getPosition(), CONST_ME_CRITICAL_DAMAGE); - } - } - - TextMessage message; - message.position = targetPos; - - SpectatorVec spectators; - if (targetPlayer && target->hasCondition(CONDITION_MANASHIELD) && damage.primary.type != COMBAT_UNDEFINEDDAMAGE) { + Player* targetPlayer = target->getPlayer(); + SpectatorVec list; + if (target->hasCondition(CONDITION_MANASHIELD) && damage.type != COMBAT_UNDEFINEDDAMAGE) { int32_t manaDamage = std::min(targetPlayer->getMana(), healthChange); if (manaDamage != 0) { if (damage.origin != ORIGIN_NONE) { @@ -4007,186 +3474,93 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage for (CreatureEvent* creatureEvent : events) { creatureEvent->executeManaChange(target, attacker, damage); } - healthChange = damage.primary.value + damage.secondary.value; + healthChange = damage.value; if (healthChange == 0) { return true; } manaDamage = std::min(targetPlayer->getMana(), healthChange); } } - targetPlayer->drainMana(attacker, manaDamage); - map.getSpectators(spectators, targetPos, true, true); - addMagicEffect(spectators, targetPos, CONST_ME_LOSEENERGY); - - std::stringstream ss; + map.getSpectators(list, targetPos, true, true); + addMagicEffect(list, targetPos, CONST_ME_LOSEENERGY); std::string damageString = std::to_string(manaDamage); - std::string spectatorMessage; - - message.primary.value = manaDamage; - message.primary.color = TEXTCOLOR_BLUE; - - for (Creature* spectator : spectators) { - Player* tmpPlayer = spectator->getPlayer(); - if (tmpPlayer->getPosition().z != targetPos.z) { - continue; + if (targetPlayer) { + std::stringstream ss; + if (!attacker) { + ss << "You lose " << damageString << " mana."; } - - if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) { - ss.str({}); - ss << ucfirst(target->getNameDescription()) << " loses " << damageString + " mana due to your attack."; - message.type = MESSAGE_DAMAGE_DEALT; - message.text = ss.str(); - } else if (tmpPlayer == targetPlayer) { - ss.str({}); - ss << "You lose " << damageString << " mana"; - if (!attacker) { - ss << '.'; - } else if (targetPlayer == attackerPlayer) { - ss << " due to your own attack."; - } else { - ss << " due to an attack by " << attacker->getNameDescription() << '.'; - } - message.type = MESSAGE_DAMAGE_RECEIVED; - message.text = ss.str(); - } else { - if (spectatorMessage.empty()) { - ss.str({}); - ss << ucfirst(target->getNameDescription()) << " loses " << damageString + " mana"; - if (attacker) { - ss << " due to "; - if (attacker == target) { - ss << (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack"); - } else { - ss << "an attack by " << attacker->getNameDescription(); - } - } - ss << '.'; - spectatorMessage = ss.str(); - } - message.type = MESSAGE_DAMAGE_OTHERS; - message.text = spectatorMessage; + else if (targetPlayer == attackerPlayer) { + ss << "You lose " << damageString << " mana due to your own attack."; } - tmpPlayer->sendTextMessage(message); + else { + ss << "You lose " << damageString << " mana due to an attack by " << attacker->getNameDescription() << '.'; + } + targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str()); } - damage.primary.value -= manaDamage; - if (damage.primary.value < 0) { - damage.secondary.value = std::max(0, damage.secondary.value + damage.primary.value); - damage.primary.value = 0; + for (Creature* spectator : list) { + Player* tmpPlayer = spectator->getPlayer(); + tmpPlayer->sendAnimatedText(targetPos, TEXTCOLOR_BLUE, damageString); + } + + damage.value -= manaDamage; + if (damage.value < 0) { + damage.value = 0; } } } - int32_t realDamage = damage.primary.value + damage.secondary.value; + int32_t realDamage = damage.value; if (realDamage == 0) { return true; } - if (damage.origin != ORIGIN_NONE) { - const auto& events = target->getCreatureEvents(CREATURE_EVENT_HEALTHCHANGE); - if (!events.empty()) { - for (CreatureEvent* creatureEvent : events) { - creatureEvent->executeHealthChange(target, attacker, damage); - } - damage.origin = ORIGIN_NONE; - return combatChangeHealth(attacker, target, damage); - } - } - int32_t targetHealth = target->getHealth(); - if (damage.primary.value >= targetHealth) { - damage.primary.value = targetHealth; - damage.secondary.value = 0; - } else if (damage.secondary.value) { - damage.secondary.value = std::min(damage.secondary.value, targetHealth - damage.primary.value); + if (damage.value >= targetHealth) { + damage.value = targetHealth; } - realDamage = damage.primary.value + damage.secondary.value; + realDamage = damage.value; if (realDamage == 0) { return true; } - if (spectators.empty()) { - map.getSpectators(spectators, targetPos, true, true); + if (list.empty()) { + map.getSpectators(list, targetPos, true, true); } - message.primary.value = damage.primary.value; - message.secondary.value = damage.secondary.value; - + TextColor_t color = TEXTCOLOR_NONE; uint8_t hitEffect; - if (message.primary.value) { - combatGetTypeInfo(damage.primary.type, target, message.primary.color, hitEffect); + if (damage.value) { + combatGetTypeInfo(damage.type, target, color, hitEffect); if (hitEffect != CONST_ME_NONE) { - addMagicEffect(spectators, targetPos, hitEffect); + addMagicEffect(list, targetPos, hitEffect); } } - if (message.secondary.value) { - combatGetTypeInfo(damage.secondary.type, target, message.secondary.color, hitEffect); - if (hitEffect != CONST_ME_NONE) { - addMagicEffect(spectators, targetPos, hitEffect); + if (color != TEXTCOLOR_NONE) { + std::string damageString = std::to_string(realDamage) + (realDamage != 1 ? " hitpoints" : " hitpoint"); + if (targetPlayer) { + std::stringstream ss; + if (!attacker) { + ss << "You lose " << damageString << "."; + } + else if (targetPlayer == attackerPlayer) { + ss << "You lose " << damageString << " due to your own attack."; + } + else { + ss << "You lose " << damageString << " due to an attack by " << attacker->getNameDescription() << '.'; + } + targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str()); } - } - if (message.primary.color != TEXTCOLOR_NONE || message.secondary.color != TEXTCOLOR_NONE) { - std::stringstream ss; - - ss << realDamage << (realDamage != 1 ? " hitpoints" : " hitpoint"); - std::string damageString = ss.str(); - - std::string spectatorMessage; - - for (Creature* spectator : spectators) { + std::string realDamageStr = std::to_string(realDamage); + for (Creature* spectator : list) { Player* tmpPlayer = spectator->getPlayer(); - if (tmpPlayer->getPosition().z != targetPos.z) { - continue; - } - - if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) { - ss.str({}); - ss << ucfirst(target->getNameDescription()) << " loses " << damageString << " due to your attack."; - message.type = MESSAGE_DAMAGE_DEALT; - message.text = ss.str(); - } else if (tmpPlayer == targetPlayer) { - ss.str({}); - ss << "You lose " << damageString; - if (!attacker) { - ss << '.'; - } else if (targetPlayer == attackerPlayer) { - ss << " due to your own attack."; - } else { - ss << " due to an attack by " << attacker->getNameDescription() << '.'; - } - message.type = MESSAGE_DAMAGE_RECEIVED; - message.text = ss.str(); - } else { - message.type = MESSAGE_DAMAGE_OTHERS; - - if (spectatorMessage.empty()) { - ss.str({}); - ss << ucfirst(target->getNameDescription()) << " loses " << damageString; - if (attacker) { - ss << " due to "; - if (attacker == target) { - if (targetPlayer) { - ss << (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack"); - } else { - ss << "its own attack"; - } - } else { - ss << "an attack by " << attacker->getNameDescription(); - } - } - ss << '.'; - spectatorMessage = ss.str(); - } - - message.text = spectatorMessage; - } - tmpPlayer->sendTextMessage(message); + tmpPlayer->sendAnimatedText(targetPos, color, realDamageStr); } } @@ -4199,7 +3573,7 @@ bool Game::combatChangeHealth(Creature* attacker, Creature* target, CombatDamage } target->drainHealth(attacker, realDamage); - addCreatureHealth(spectators, target); + addCreatureHealth(list, target); } return true; @@ -4211,16 +3585,8 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage& if (!targetPlayer) { return true; } - - int32_t manaChange = damage.primary.value + damage.secondary.value; + int32_t manaChange = damage.value; if (manaChange > 0) { - if (attacker) { - const Player* attackerPlayer = attacker->getPlayer(); - if (attackerPlayer && attackerPlayer->getSkull() == SKULL_BLACK && attackerPlayer->getSkullClient(target) == SKULL_NONE) { - return false; - } - } - if (damage.origin != ORIGIN_NONE) { const auto& events = target->getCreatureEvents(CREATURE_EVENT_MANACHANGE); if (!events.empty()) { @@ -4231,19 +3597,9 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage& return combatChangeMana(attacker, target, damage); } } - - int32_t realManaChange = targetPlayer->getMana(); targetPlayer->changeMana(manaChange); - realManaChange = targetPlayer->getMana() - realManaChange; - - if (realManaChange > 0 && !targetPlayer->isInGhostMode()) { - TextMessage message(MESSAGE_HEALED, "You gained " + std::to_string(realManaChange) + " mana."); - message.position = target->getPosition(); - message.primary.value = realManaChange; - message.primary.color = TEXTCOLOR_MAYABLUE; - targetPlayer->sendTextMessage(message); - } - } else { + } + else { const Position& targetPos = target->getPosition(); if (!target->isAttackable()) { if (!target->isInGhostMode()) { @@ -4255,12 +3611,9 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage& Player* attackerPlayer; if (attacker) { attackerPlayer = attacker->getPlayer(); - } else { - attackerPlayer = nullptr; } - - if (attackerPlayer && attackerPlayer->getSkull() == SKULL_BLACK && attackerPlayer->getSkullClient(targetPlayer) == SKULL_NONE) { - return false; + else { + attackerPlayer = nullptr; } int32_t manaLoss = std::min(targetPlayer->getMana(), -manaChange); @@ -4287,57 +3640,27 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage& targetPlayer->drainMana(attacker, manaLoss); - std::stringstream ss; - std::string damageString = std::to_string(manaLoss); - std::string spectatorMessage; - - TextMessage message; - message.position = targetPos; - message.primary.value = manaLoss; - message.primary.color = TEXTCOLOR_BLUE; - - SpectatorVec spectators; - map.getSpectators(spectators, targetPos, false, true); - for (Creature* spectator : spectators) { - Player* tmpPlayer = spectator->getPlayer(); - if (tmpPlayer == attackerPlayer && attackerPlayer != targetPlayer) { - ss.str({}); - ss << ucfirst(target->getNameDescription()) << " loses " << damageString << " mana due to your attack."; - message.type = MESSAGE_DAMAGE_DEALT; - message.text = ss.str(); - } else if (tmpPlayer == targetPlayer) { - ss.str({}); - ss << "You lose " << damageString << " mana"; - if (!attacker) { - ss << '.'; - } else if (targetPlayer == attackerPlayer) { - ss << " due to your own attack."; - } else { - ss << " mana due to an attack by " << attacker->getNameDescription() << '.'; - } - message.type = MESSAGE_DAMAGE_RECEIVED; - message.text = ss.str(); - } else { - if (spectatorMessage.empty()) { - ss.str({}); - ss << ucfirst(target->getNameDescription()) << " loses " << damageString << " mana"; - if (attacker) { - ss << " due to "; - if (attacker == target) { - ss << (targetPlayer->getSex() == PLAYERSEX_FEMALE ? "her own attack" : "his own attack"); - } else { - ss << "an attack by " << attacker->getNameDescription(); - } - } - ss << '.'; - spectatorMessage = ss.str(); - } - message.type = MESSAGE_DAMAGE_OTHERS; - message.text = spectatorMessage; + SpectatorVec list; + map.getSpectators(list, targetPos, false, true); + if (targetPlayer) { + std::stringstream ss; + if (!attacker) { + ss << "You lose " << damageString << " mana."; } - tmpPlayer->sendTextMessage(message); + else if (targetPlayer == attackerPlayer) { + ss << "You lose " << damageString << " mana due to your own attack."; + } + else { + ss << "You lose " << damageString << " mana due to an attack by " << attacker->getNameDescription() << '.'; + } + targetPlayer->sendTextMessage(MESSAGE_EVENT_DEFAULT, ss.str()); + } + + for (Creature* spectator : list) { + Player* tmpPlayer = spectator->getPlayer(); + tmpPlayer->sendAnimatedText(targetPos, TEXTCOLOR_BLUE, damageString); } } @@ -4346,14 +3669,14 @@ bool Game::combatChangeMana(Creature* attacker, Creature* target, CombatDamage& void Game::addCreatureHealth(const Creature* target) { - SpectatorVec spectators; - map.getSpectators(spectators, target->getPosition(), true, true); - addCreatureHealth(spectators, target); + SpectatorVec list; + map.getSpectators(list, target->getPosition(), true, true); + addCreatureHealth(list, target); } -void Game::addCreatureHealth(const SpectatorVec& spectators, const Creature* target) +void Game::addCreatureHealth(const SpectatorVec& list, const Creature* target) { - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { tmpPlayer->sendCreatureHealth(target); } @@ -4362,14 +3685,14 @@ void Game::addCreatureHealth(const SpectatorVec& spectators, const Creature* tar void Game::addMagicEffect(const Position& pos, uint8_t effect) { - SpectatorVec spectators; - map.getSpectators(spectators, pos, true, true); - addMagicEffect(spectators, pos, effect); + SpectatorVec list; + map.getSpectators(list, pos, true, true); + addMagicEffect(list, pos, effect); } -void Game::addMagicEffect(const SpectatorVec& spectators, const Position& pos, uint8_t effect) +void Game::addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect) { - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { tmpPlayer->sendMagicEffect(pos, effect); } @@ -4378,23 +3701,49 @@ void Game::addMagicEffect(const SpectatorVec& spectators, const Position& pos, u void Game::addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect) { - SpectatorVec spectators, toPosSpectators; - map.getSpectators(spectators, fromPos, false, true); - map.getSpectators(toPosSpectators, toPos, false, true); - spectators.addSpectators(toPosSpectators); - - addDistanceEffect(spectators, fromPos, toPos, effect); + SpectatorVec list; + map.getSpectators(list, fromPos, false, true); + map.getSpectators(list, toPos, false, true); + addDistanceEffect(list, fromPos, toPos, effect); } -void Game::addDistanceEffect(const SpectatorVec& spectators, const Position& fromPos, const Position& toPos, uint8_t effect) +void Game::addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect) { - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { tmpPlayer->sendDistanceShoot(fromPos, toPos, effect); } } } +void Game::addAnimatedText(const Position& pos, uint8_t color, const std::string& text) +{ + SpectatorVec list; + map.getSpectators(list, pos, false, true); + addAnimatedText(list, pos, color, text); +} + +void Game::addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t color, const std::string& text) +{ + for (Creature* spectator : list) { + if (Player* tmpPlayer = spectator->getPlayer()) { + tmpPlayer->sendAnimatedText(pos, color, text); + } + } +} + +void Game::addMonsterSayText(const Position& pos, const std::string& text) +{ + SpectatorVec list; + map.getSpectators(list, pos, false, true); + + for (Creature* spectator : list) { + if (Player* tmpPlayer = spectator->getPlayer()) { + tmpPlayer->sendCreatureSay(tmpPlayer, TALKTYPE_MONSTER_SAY, text, &pos); + } + } +} + void Game::startDecay(Item* item) { if (!item || !item->canDecay()) { @@ -4419,7 +3768,7 @@ void Game::internalDecayItem(Item* item) { const ItemType& it = Item::items[item->getID()]; if (it.decayTo != 0) { - Item* newItem = transformItem(item, item->getDecayTo()); + Item* newItem = transformItem(item, it.decayTo); startDecay(newItem); } else { ReturnValue ret = internalRemoveItem(item); @@ -4518,7 +3867,8 @@ void Game::checkLight() } if (lightChange) { - LightInfo lightInfo = getWorldLightInfo(); + LightInfo lightInfo; + getWorldLightInfo(lightInfo); for (const auto& it : players) { it.second->sendWorldLight(lightInfo); @@ -4526,13 +3876,15 @@ void Game::checkLight() } } -LightInfo Game::getWorldLightInfo() const +void Game::getWorldLightInfo(LightInfo& lightInfo) const { - return {lightLevel, 0xD7}; + lightInfo.level = lightLevel; + lightInfo.color = 0xD7; } void Game::shutdown() { + saveGameState(); std::cout << "Shutting down..." << std::flush; g_scheduler.shutdown(); @@ -4594,87 +3946,28 @@ void Game::broadcastMessage(const std::string& text, MessageClasses type) const } } -void Game::updateCreatureWalkthrough(const Creature* creature) -{ - //send to clients - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true, true); - for (Creature* spectator : spectators) { - Player* tmpPlayer = spectator->getPlayer(); - tmpPlayer->sendCreatureWalkthrough(creature, tmpPlayer->canWalkthroughEx(creature)); - } -} - void Game::updateCreatureSkull(const Creature* creature) { if (getWorldType() != WORLD_TYPE_PVP) { return; } - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, creature->getPosition(), true, true); + for (Creature* spectator : list) { spectator->getPlayer()->sendCreatureSkull(creature); } } void Game::updatePlayerShield(Player* player) { - SpectatorVec spectators; - map.getSpectators(spectators, player->getPosition(), true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + map.getSpectators(list, player->getPosition(), true, true); + for (Creature* spectator : list) { spectator->getPlayer()->sendCreatureShield(player); } } -void Game::updatePlayerHelpers(const Player& player) -{ - uint32_t creatureId = player.getID(); - uint16_t helpers = player.getHelpers(); - - SpectatorVec spectators; - map.getSpectators(spectators, player.getPosition(), true, true); - for (Creature* spectator : spectators) { - spectator->getPlayer()->sendCreatureHelpers(creatureId, helpers); - } -} - -void Game::updateCreatureType(Creature* creature) -{ - const Player* masterPlayer = nullptr; - - uint32_t creatureId = creature->getID(); - CreatureType_t creatureType = creature->getType(); - if (creatureType == CREATURETYPE_MONSTER) { - const Creature* master = creature->getMaster(); - if (master) { - masterPlayer = master->getPlayer(); - if (masterPlayer) { - creatureType = CREATURETYPE_SUMMON_OTHERS; - } - } - } - - //send to clients - SpectatorVec spectators; - map.getSpectators(spectators, creature->getPosition(), true, true); - - if (creatureType == CREATURETYPE_SUMMON_OTHERS) { - for (Creature* spectator : spectators) { - Player* player = spectator->getPlayer(); - if (masterPlayer == player) { - player->sendCreatureType(creatureId, CREATURETYPE_SUMMON_OWN); - } else { - player->sendCreatureType(creatureId, creatureType); - } - } - } else { - for (Creature* spectator : spectators) { - spectator->getPlayer()->sendCreatureType(creatureId, creatureType); - } - } -} - void Game::updatePremium(Account& account) { bool save = false; @@ -4705,43 +3998,43 @@ void Game::updatePremium(Account& account) } if (save && !IOLoginData::saveAccount(account)) { - std::cout << "> ERROR: Failed to save account: " << account.name << "!" << std::endl; + std::cout << "> ERROR: Failed to save account: " << account.id << "!" << std::endl; } } void Game::loadMotdNum() { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); - DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_num'"); + DBResult_ptr result = db->storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_num'"); if (result) { motdNum = result->getNumber("value"); } else { - db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_num', '0')"); + db->executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_num', '0')"); } - result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_hash'"); + result = db->storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'motd_hash'"); if (result) { motdHash = result->getString("value"); if (motdHash != transformToSHA1(g_config.getString(ConfigManager::MOTD))) { ++motdNum; } } else { - db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_hash', '')"); + db->executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('motd_hash', '')"); } } void Game::saveMotdNum() const { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; query << "UPDATE `server_config` SET `value` = '" << motdNum << "' WHERE `config` = 'motd_num'"; - db.executeQuery(query.str()); + db->executeQuery(query.str()); query.str(std::string()); query << "UPDATE `server_config` SET `value` = '" << transformToSHA1(g_config.getString(ConfigManager::MOTD)) << "' WHERE `config` = 'motd_hash'"; - db.executeQuery(query.str()); + db->executeQuery(query.str()); } void Game::checkPlayersRecord() @@ -4751,8 +4044,8 @@ void Game::checkPlayersRecord() uint32_t previousRecord = playersRecord; playersRecord = playersOnline; - for (auto& it : g_globalEvents->getEventMap(GLOBALEVENT_RECORD)) { - it.second.executeRecord(playersRecord, previousRecord); + for (const auto& it : g_globalEvents->getEventMap(GLOBALEVENT_RECORD)) { + it.second->executeRecord(playersRecord, previousRecord); } updatePlayersRecord(); } @@ -4760,22 +4053,22 @@ void Game::checkPlayersRecord() void Game::updatePlayersRecord() const { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; query << "UPDATE `server_config` SET `value` = '" << playersRecord << "' WHERE `config` = 'players_record'"; - db.executeQuery(query.str()); + db->executeQuery(query.str()); } void Game::loadPlayersRecord() { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); - DBResult_ptr result = db.storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'players_record'"); + DBResult_ptr result = db->storeQuery("SELECT `value` FROM `server_config` WHERE `config` = 'players_record'"); if (result) { playersRecord = result->getNumber("value"); } else { - db.executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('players_record', '0')"); + db->executeQuery("INSERT INTO `server_config` (`config`, `value`) VALUES ('players_record', '0')"); } } @@ -4844,10 +4137,6 @@ bool Game::loadExperienceStages() void Game::playerInviteToParty(uint32_t playerId, uint32_t invitedId) { - if (playerId == invitedId) { - return; - } - Player* player = getPlayerByID(playerId); if (!player) { return; @@ -4963,24 +4252,113 @@ void Game::playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpAc } Party* party = player->getParty(); - if (!party || (player->hasCondition(CONDITION_INFIGHT) && player->getZone() != ZONE_PROTECTION)) { + if (!party || player->hasCondition(CONDITION_INFIGHT)) { return; } party->setSharedExperience(player, sharedExpActive); } -void Game::sendGuildMotd(uint32_t playerId) +void Game::playerProcessRuleViolationReport(uint32_t playerId, const std::string& name) { Player* player = getPlayerByID(playerId); if (!player) { return; } - Guild* guild = player->getGuild(); - if (guild) { - player->sendChannelMessage("Message of the Day", guild->getMotd(), TALKTYPE_CHANNEL_R1, CHANNEL_GUILD); + if (player->getAccountType() < ACCOUNT_TYPE_GAMEMASTER) { + return; } + + Player* reporter = getPlayerByName(name); + if (!reporter) { + return; + } + + auto it = ruleViolations.find(reporter->getID()); + if (it == ruleViolations.end()) { + return; + } + + RuleViolation& ruleViolation = it->second; + if (!ruleViolation.pending) { + return; + } + + ruleViolation.gamemasterId = player->getID(); + ruleViolation.pending = false; + + ChatChannel* channel = g_chat->getChannelById(CHANNEL_RULE_REP); + if (channel) { + for (auto userPtr : channel->getUsers()) { + if (userPtr.second) { + userPtr.second->sendRemoveRuleViolationReport(reporter->getName()); + } + } + } +} + +void Game::playerCloseRuleViolationReport(uint32_t playerId, const std::string& name) +{ + Player* player = getPlayerByID(playerId); + if (!player) { + return; + } + + Player* reporter = getPlayerByName(name); + if (!reporter) { + return; + } + + closeRuleViolationReport(reporter); +} + +void Game::playerCancelRuleViolationReport(uint32_t playerId) +{ + Player* player = getPlayerByID(playerId); + if (!player) { + return; + } + + cancelRuleViolationReport(player); +} + +void Game::playerReportRuleViolationReport(Player* player, const std::string& text) +{ + auto it = ruleViolations.find(player->getID()); + if (it != ruleViolations.end()) { + player->sendCancelMessage("You already have a pending rule violation report. Close it before starting a new one."); + return; + } + + RuleViolation ruleViolation = RuleViolation(player->getID(), text); + ruleViolations[player->getID()] = ruleViolation; + + ChatChannel* channel = g_chat->getChannelById(CHANNEL_RULE_REP); + if (channel) { + for (auto userPtr : channel->getUsers()) { + if (userPtr.second) { + userPtr.second->sendToChannel(player, TALKTYPE_RVR_CHANNEL, text, CHANNEL_RULE_REP); + } + } + } +} + +void Game::playerContinueRuleViolationReport(Player* player, const std::string& text) +{ + auto it = ruleViolations.find(player->getID()); + if (it == ruleViolations.end()) { + return; + } + + RuleViolation& rvr = it->second; + Player* toPlayer = getPlayerByID(rvr.gamemasterId); + if (!toPlayer) { + return; + } + + toPlayer->sendCreatureSay(player, TALKTYPE_RVR_CONTINUE, text, 0); + player->sendTextMessage(MESSAGE_STATUS_SMALL, "Message sent to Counsellor."); } void Game::kickPlayer(uint32_t playerId, bool displayEffect) @@ -4993,24 +4371,15 @@ void Game::kickPlayer(uint32_t playerId, bool displayEffect) player->kickPlayer(displayEffect); } -void Game::playerReportRuleViolation(uint32_t playerId, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation) +void Game::playerReportBug(uint32_t playerId, const std::string& message) { Player* player = getPlayerByID(playerId); if (!player) { return; } - g_events->eventPlayerOnReportRuleViolation(player, targetName, reportType, reportReason, comment, translation); -} - -void Game::playerReportBug(uint32_t playerId, const std::string& message, const Position& position, uint8_t category) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - g_events->eventPlayerOnReportBug(player, message, position, category); + const Position& position = player->getPosition(); + g_events->eventPlayerOnReportBug(player, message, position); } void Game::playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment) @@ -5029,411 +4398,6 @@ void Game::playerDebugAssert(uint32_t playerId, const std::string& assertLine, c } } -void Game::playerLeaveMarket(uint32_t playerId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - player->setInMarket(false); -} - -void Game::playerBrowseMarket(uint32_t playerId, uint16_t spriteId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - if (!player->isInMarket()) { - return; - } - - const ItemType& it = Item::items.getItemIdByClientId(spriteId); - if (it.id == 0) { - return; - } - - if (it.wareId == 0) { - return; - } - - const MarketOfferList& buyOffers = IOMarket::getActiveOffers(MARKETACTION_BUY, it.id); - const MarketOfferList& sellOffers = IOMarket::getActiveOffers(MARKETACTION_SELL, it.id); - player->sendMarketBrowseItem(it.id, buyOffers, sellOffers); - player->sendMarketDetail(it.id); -} - -void Game::playerBrowseMarketOwnOffers(uint32_t playerId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - if (!player->isInMarket()) { - return; - } - - const MarketOfferList& buyOffers = IOMarket::getOwnOffers(MARKETACTION_BUY, player->getGUID()); - const MarketOfferList& sellOffers = IOMarket::getOwnOffers(MARKETACTION_SELL, player->getGUID()); - player->sendMarketBrowseOwnOffers(buyOffers, sellOffers); -} - -void Game::playerBrowseMarketOwnHistory(uint32_t playerId) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - if (!player->isInMarket()) { - return; - } - - const HistoryMarketOfferList& buyOffers = IOMarket::getOwnHistory(MARKETACTION_BUY, player->getGUID()); - const HistoryMarketOfferList& sellOffers = IOMarket::getOwnHistory(MARKETACTION_SELL, player->getGUID()); - player->sendMarketBrowseOwnHistory(buyOffers, sellOffers); -} - -void Game::playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t spriteId, uint16_t amount, uint32_t price, bool anonymous) -{ - if (amount == 0 || amount > 64000) { - return; - } - - if (price == 0 || price > 999999999) { - return; - } - - if (type != MARKETACTION_BUY && type != MARKETACTION_SELL) { - return; - } - - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - if (!player->isInMarket()) { - return; - } - - if (g_config.getBoolean(ConfigManager::MARKET_PREMIUM) && !player->isPremium()) { - player->sendMarketLeave(); - return; - } - - const ItemType& itt = Item::items.getItemIdByClientId(spriteId); - if (itt.id == 0 || itt.wareId == 0) { - return; - } - - const ItemType& it = Item::items.getItemIdByClientId(itt.wareId); - if (it.id == 0 || it.wareId == 0) { - return; - } - - if (!it.stackable && amount > 2000) { - return; - } - - const uint32_t maxOfferCount = g_config.getNumber(ConfigManager::MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER); - if (maxOfferCount != 0 && IOMarket::getPlayerOfferCount(player->getGUID()) >= maxOfferCount) { - return; - } - - uint64_t fee = (price / 100.) * amount; - if (fee < 20) { - fee = 20; - } else if (fee > 1000) { - fee = 1000; - } - - if (type == MARKETACTION_SELL) { - if (fee > player->bankBalance) { - return; - } - - DepotChest* depotChest = player->getDepotChest(player->getLastDepotId(), false); - if (!depotChest) { - return; - } - - std::forward_list itemList = getMarketItemList(it.wareId, amount, depotChest, player->getInbox()); - if (itemList.empty()) { - return; - } - - if (it.stackable) { - uint16_t tmpAmount = amount; - for (Item* item : itemList) { - uint16_t removeCount = std::min(tmpAmount, item->getItemCount()); - tmpAmount -= removeCount; - internalRemoveItem(item, removeCount); - - if (tmpAmount == 0) { - break; - } - } - } else { - for (Item* item : itemList) { - internalRemoveItem(item); - } - } - - player->bankBalance -= fee; - } else { - uint64_t totalPrice = static_cast(price) * amount; - totalPrice += fee; - if (totalPrice > player->bankBalance) { - return; - } - - player->bankBalance -= totalPrice; - } - - IOMarket::createOffer(player->getGUID(), static_cast(type), it.id, amount, price, anonymous); - - player->sendMarketEnter(player->getLastDepotId()); - const MarketOfferList& buyOffers = IOMarket::getActiveOffers(MARKETACTION_BUY, it.id); - const MarketOfferList& sellOffers = IOMarket::getActiveOffers(MARKETACTION_SELL, it.id); - player->sendMarketBrowseItem(it.id, buyOffers, sellOffers); -} - -void Game::playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - if (!player->isInMarket()) { - return; - } - - MarketOfferEx offer = IOMarket::getOfferByCounter(timestamp, counter); - if (offer.id == 0 || offer.playerId != player->getGUID()) { - return; - } - - if (offer.type == MARKETACTION_BUY) { - player->bankBalance += static_cast(offer.price) * offer.amount; - player->sendMarketEnter(player->getLastDepotId()); - } else { - const ItemType& it = Item::items[offer.itemId]; - if (it.id == 0) { - return; - } - - if (it.stackable) { - uint16_t tmpAmount = offer.amount; - while (tmpAmount > 0) { - int32_t stackCount = std::min(100, tmpAmount); - Item* item = Item::CreateItem(it.id, stackCount); - if (internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - - tmpAmount -= stackCount; - } - } else { - int32_t subType; - if (it.charges != 0) { - subType = it.charges; - } else { - subType = -1; - } - - for (uint16_t i = 0; i < offer.amount; ++i) { - Item* item = Item::CreateItem(it.id, subType); - if (internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - } - } - } - - IOMarket::moveOfferToHistory(offer.id, OFFERSTATE_CANCELLED); - offer.amount = 0; - offer.timestamp += g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); - player->sendMarketCancelOffer(offer); - player->sendMarketEnter(player->getLastDepotId()); -} - -void Game::playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount) -{ - if (amount == 0 || amount > 64000) { - return; - } - - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - if (!player->isInMarket()) { - return; - } - - MarketOfferEx offer = IOMarket::getOfferByCounter(timestamp, counter); - if (offer.id == 0) { - return; - } - - if (amount > offer.amount) { - return; - } - - const ItemType& it = Item::items[offer.itemId]; - if (it.id == 0) { - return; - } - - uint64_t totalPrice = static_cast(offer.price) * amount; - - if (offer.type == MARKETACTION_BUY) { - DepotChest* depotChest = player->getDepotChest(player->getLastDepotId(), false); - if (!depotChest) { - return; - } - - std::forward_list itemList = getMarketItemList(it.wareId, amount, depotChest, player->getInbox()); - if (itemList.empty()) { - return; - } - - Player* buyerPlayer = getPlayerByGUID(offer.playerId); - if (!buyerPlayer) { - buyerPlayer = new Player(nullptr); - if (!IOLoginData::loadPlayerById(buyerPlayer, offer.playerId)) { - delete buyerPlayer; - return; - } - } - - if (it.stackable) { - uint16_t tmpAmount = amount; - for (Item* item : itemList) { - uint16_t removeCount = std::min(tmpAmount, item->getItemCount()); - tmpAmount -= removeCount; - internalRemoveItem(item, removeCount); - - if (tmpAmount == 0) { - break; - } - } - } else { - for (Item* item : itemList) { - internalRemoveItem(item); - } - } - - player->bankBalance += totalPrice; - - if (it.stackable) { - uint16_t tmpAmount = amount; - while (tmpAmount > 0) { - uint16_t stackCount = std::min(100, tmpAmount); - Item* item = Item::CreateItem(it.id, stackCount); - if (internalAddItem(buyerPlayer->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - - tmpAmount -= stackCount; - } - } else { - int32_t subType; - if (it.charges != 0) { - subType = it.charges; - } else { - subType = -1; - } - - for (uint16_t i = 0; i < amount; ++i) { - Item* item = Item::CreateItem(it.id, subType); - if (internalAddItem(buyerPlayer->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - } - } - - if (buyerPlayer->isOffline()) { - IOLoginData::savePlayer(buyerPlayer); - delete buyerPlayer; - } else { - buyerPlayer->onReceiveMail(); - } - } else { - if (totalPrice > player->bankBalance) { - return; - } - - player->bankBalance -= totalPrice; - - if (it.stackable) { - uint16_t tmpAmount = amount; - while (tmpAmount > 0) { - uint16_t stackCount = std::min(100, tmpAmount); - Item* item = Item::CreateItem(it.id, stackCount); - if (internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - - tmpAmount -= stackCount; - } - } else { - int32_t subType; - if (it.charges != 0) { - subType = it.charges; - } else { - subType = -1; - } - - for (uint16_t i = 0; i < amount; ++i) { - Item* item = Item::CreateItem(it.id, subType); - if (internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - } - } - - Player* sellerPlayer = getPlayerByGUID(offer.playerId); - if (sellerPlayer) { - sellerPlayer->bankBalance += totalPrice; - } else { - IOLoginData::increaseBankBalance(offer.playerId, totalPrice); - } - - player->onReceiveMail(); - } - - const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); - - IOMarket::appendHistory(player->getGUID(), (offer.type == MARKETACTION_BUY ? MARKETACTION_SELL : MARKETACTION_BUY), offer.itemId, amount, offer.price, offer.timestamp + marketOfferDuration, OFFERSTATE_ACCEPTEDEX); - - IOMarket::appendHistory(offer.playerId, offer.type, offer.itemId, amount, offer.price, offer.timestamp + marketOfferDuration, OFFERSTATE_ACCEPTED); - - offer.amount -= amount; - - if (offer.amount == 0) { - IOMarket::deleteOffer(offer.id); - } else { - IOMarket::acceptOffer(offer.id, amount); - } - - player->sendMarketEnter(player->getLastDepotId()); - offer.timestamp += marketOfferDuration; - player->sendMarketAcceptOffer(offer); -} - void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer) { Player* player = getPlayerByID(playerId); @@ -5446,45 +4410,52 @@ void Game::parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const st } } -std::forward_list Game::getMarketItemList(uint16_t wareId, uint16_t sufficientCount, DepotChest* depotChest, Inbox* inbox) +void Game::closeRuleViolationReport(Player* player) { - std::forward_list itemList; - uint16_t count = 0; + const auto it = ruleViolations.find(player->getID()); + if (it == ruleViolations.end()) { + return; + } - std::list containers { depotChest, inbox }; - do { - Container* container = containers.front(); - containers.pop_front(); + ruleViolations.erase(it); + player->sendLockRuleViolationReport(); - for (Item* item : container->getItemList()) { - Container* c = item->getContainer(); - if (c && !c->empty()) { - containers.push_back(c); - continue; - } - - const ItemType& itemType = Item::items[item->getID()]; - if (itemType.wareId != wareId) { - continue; - } - - if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems)) { - continue; - } - - if (!item->hasMarketAttributes()) { - continue; - } - - itemList.push_front(item); - - count += Item::countByType(item, -1); - if (count >= sufficientCount) { - return itemList; + ChatChannel* channel = g_chat->getChannelById(CHANNEL_RULE_REP); + if (channel) { + for (UsersMap::const_iterator ut = channel->getUsers().begin(); ut != channel->getUsers().end(); ++ut) { + if (ut->second) { + ut->second->sendRemoveRuleViolationReport(player->getName()); } } - } while (!containers.empty()); - return std::forward_list(); + } +} + +void Game::cancelRuleViolationReport(Player* player) +{ + const auto it = ruleViolations.find(player->getID()); + if (it == ruleViolations.end()) { + return; + } + + RuleViolation& ruleViolation = it->second; + Player* gamemaster = getPlayerByID(ruleViolation.gamemasterId); + if (!ruleViolation.pending && gamemaster) { + // Send to the responder + gamemaster->sendRuleViolationCancel(player->getName()); + } + + // Send to channel + ChatChannel* channel = g_chat->getChannelById(CHANNEL_RULE_REP); + if (channel) { + for (UsersMap::const_iterator ut = channel->getUsers().begin(); ut != channel->getUsers().end(); ++ut) { + if (ut->second) { + ut->second->sendRemoveRuleViolationReport(player->getName()); + } + } + } + + // Erase it + ruleViolations.erase(it); } void Game::forceAddCondition(uint32_t creatureId, Condition* condition) @@ -5508,52 +4479,6 @@ void Game::forceRemoveCondition(uint32_t creatureId, ConditionType_t type) creature->removeCondition(type, true); } -void Game::sendOfflineTrainingDialog(Player* player) -{ - if (!player) { - return; - } - - if (!player->hasModalWindowOpen(offlineTrainingWindow.id)) { - player->sendModalWindow(offlineTrainingWindow); - } -} - -void Game::playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, uint8_t button, uint8_t choice) -{ - Player* player = getPlayerByID(playerId); - if (!player) { - return; - } - - if (!player->hasModalWindowOpen(modalWindowId)) { - return; - } - - player->onModalWindowHandled(modalWindowId); - - // offline training, hardcoded - if (modalWindowId == std::numeric_limits::max()) { - if (button == 1) { - if (choice == SKILL_SWORD || choice == SKILL_AXE || choice == SKILL_CLUB || choice == SKILL_DISTANCE || choice == SKILL_MAGLEVEL) { - BedItem* bedItem = player->getBedItem(); - if (bedItem && bedItem->sleep(player)) { - player->setOfflineTrainingSkill(choice); - return; - } - } - } else { - player->sendTextMessage(MESSAGE_EVENT_ADVANCE, "Offline training aborted."); - } - - player->setBedItem(nullptr); - } else { - for (auto creatureEvent : player->getCreatureEvents(CREATURE_EVENT_MODALWINDOW)) { - creatureEvent->executeModalWindow(player, modalWindowId, button, choice); - } - } -} - void Game::addPlayer(Player* player) { const std::string& lowercase_name = asLowerCaseString(player->getName()); @@ -5609,19 +4534,6 @@ void Game::removeGuild(uint32_t guildId) guilds.erase(guildId); } -void Game::decreaseBrowseFieldRef(const Position& pos) -{ - Tile* tile = map.getTile(pos.x, pos.y, pos.z); - if (!tile) { - return; - } - - auto it = browseFields.find(tile); - if (it != browseFields.end()) { - it->second->decrementReferenceCounter(); - } -} - void Game::internalRemoveItems(std::vector itemList, uint32_t amount, bool stackable) { if (stackable) { @@ -5663,132 +4575,63 @@ void Game::removeBedSleeper(uint32_t guid) } } -Item* Game::getUniqueItem(uint16_t uniqueId) -{ - auto it = uniqueItems.find(uniqueId); - if (it == uniqueItems.end()) { - return nullptr; - } - return it->second; -} - -bool Game::addUniqueItem(uint16_t uniqueId, Item* item) -{ - auto result = uniqueItems.emplace(uniqueId, item); - if (!result.second) { - std::cout << "Duplicate unique id: " << uniqueId << std::endl; - } - return result.second; -} - -void Game::removeUniqueItem(uint16_t uniqueId) -{ - auto it = uniqueItems.find(uniqueId); - if (it != uniqueItems.end()) { - uniqueItems.erase(it); - } -} - bool Game::reload(ReloadTypes_t reloadType) { switch (reloadType) { - case RELOAD_TYPE_ACTIONS: return g_actions->reload(); - case RELOAD_TYPE_CHAT: return g_chat->load(); - case RELOAD_TYPE_CONFIG: return g_config.reload(); - case RELOAD_TYPE_CREATURESCRIPTS: return g_creatureEvents->reload(); - case RELOAD_TYPE_EVENTS: return g_events->load(); - case RELOAD_TYPE_GLOBALEVENTS: return g_globalEvents->reload(); - case RELOAD_TYPE_ITEMS: return Item::items.reload(); - case RELOAD_TYPE_MONSTERS: return g_monsters.reload(); - case RELOAD_TYPE_MOUNTS: return mounts.reload(); - case RELOAD_TYPE_MOVEMENTS: return g_moveEvents->reload(); - case RELOAD_TYPE_NPCS: { - Npcs::reload(); - return true; - } - - case RELOAD_TYPE_QUESTS: return quests.reload(); - case RELOAD_TYPE_RAIDS: return raids.reload() && raids.startup(); - - case RELOAD_TYPE_SPELLS: { - if (!g_spells->reload()) { - std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl; - std::terminate(); - } else if (!g_monsters.reload()) { - std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl; - std::terminate(); - } - return true; - } - - case RELOAD_TYPE_TALKACTIONS: return g_talkActions->reload(); - - case RELOAD_TYPE_WEAPONS: { - bool results = g_weapons->reload(); - g_weapons->loadDefaults(); - return results; - } - - case RELOAD_TYPE_SCRIPTS: { - // commented out stuff is TODO, once we approach further in revscriptsys - g_actions->clear(true); - g_creatureEvents->clear(true); - g_moveEvents->clear(true); - g_talkActions->clear(true); - g_globalEvents->clear(true); - g_weapons->clear(true); - g_weapons->loadDefaults(); - g_spells->clear(true); - g_scripts->loadScripts("scripts", false, true); - /* - Npcs::reload(); - raids.reload() && raids.startup(); - Item::items.reload(); - quests.reload(); - mounts.reload(); - g_config.reload(); - g_events->load(); - g_chat->load(); - */ - return true; - } - - default: { - if (!g_spells->reload()) { - std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl; - std::terminate(); - } else if (!g_monsters.reload()) { - std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl; - std::terminate(); - } - - g_actions->reload(); - g_config.reload(); - g_creatureEvents->reload(); - g_monsters.reload(); - g_moveEvents->reload(); - Npcs::reload(); - raids.reload() && raids.startup(); - g_talkActions->reload(); - Item::items.reload(); - g_weapons->reload(); - g_weapons->clear(true); - g_weapons->loadDefaults(); - quests.reload(); - mounts.reload(); - g_globalEvents->reload(); - g_events->load(); - g_chat->load(); - g_actions->clear(true); - g_creatureEvents->clear(true); - g_moveEvents->clear(true); - g_talkActions->clear(true); - g_globalEvents->clear(true); - g_spells->clear(true); - g_scripts->loadScripts("scripts", false, true); - return true; - } + case RELOAD_TYPE_ACTIONS: return g_actions->reload(); + case RELOAD_TYPE_CHAT: return g_chat->load(); + case RELOAD_TYPE_CONFIG: return g_config.reload(); + case RELOAD_TYPE_CREATURESCRIPTS: return g_creatureEvents->reload(); + case RELOAD_TYPE_EVENTS: return g_events->load(); + case RELOAD_TYPE_GLOBALEVENTS: return g_globalEvents->reload(); + case RELOAD_TYPE_ITEMS: return Item::items.reload(); + case RELOAD_TYPE_MONSTERS: return g_monsters.reload(); + case RELOAD_TYPE_MOVEMENTS: return g_moveEvents->reload(); + case RELOAD_TYPE_NPCS: { + Npcs::reload(); + return true; } - return true; -} + case RELOAD_TYPE_RAIDS: return raids.reload() && raids.startup(); + case RELOAD_TYPE_SPELLS: { + if (!g_spells->reload()) { + std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl; + std::terminate(); + } + else if (!g_monsters.reload()) { + std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl; + std::terminate(); + } + return true; + } + + case RELOAD_TYPE_TALKACTIONS: return g_talkActions->reload(); + + default: { + if (!g_spells->reload()) { + std::cout << "[Error - Game::reload] Failed to reload spells." << std::endl; + std::terminate(); + return false; + } + else if (!g_monsters.reload()) { + std::cout << "[Error - Game::reload] Failed to reload monsters." << std::endl; + std::terminate(); + return false; + } + + g_actions->reload(); + g_config.reload(); + g_creatureEvents->reload(); + g_monsters.reload(); + g_moveEvents->reload(); + Npcs::reload(); + raids.reload() && raids.startup(); + g_talkActions->reload(); + Item::items.reload(); + g_globalEvents->reload(); + g_events->load(); + g_chat->load(); + return true; + } + } +} diff --git a/src/game.h b/src/game.h index 83d5e55..633b6a7 100644 --- a/src/game.h +++ b/src/game.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,6 @@ #include "raids.h" #include "npc.h" #include "wildcardtree.h" -#include "quests.h" class ServiceManager; class Creature; @@ -70,6 +69,22 @@ enum LightState_t { LIGHT_STATE_SUNRISE, }; +struct RuleViolation { + RuleViolation() = default; + RuleViolation(uint32_t _reporterId, const std::string& _text) : + reporterId(_reporterId), + gamemasterId(0), + text(_text), + pending(true) + { + } + + uint32_t reporterId; + uint32_t gamemasterId; + std::string text; + bool pending; +}; + static constexpr int32_t EVENT_LIGHTINTERVAL = 10000; static constexpr int32_t EVENT_DECAYINTERVAL = 250; static constexpr int32_t EVENT_DECAY_BUCKETS = 4; @@ -82,7 +97,7 @@ static constexpr int32_t EVENT_DECAY_BUCKETS = 4; class Game { public: - Game(); + Game() = default; ~Game(); // non-copyable @@ -204,7 +219,7 @@ class Game * \param extendedPos If true, the creature will in first-hand be placed 2 tiles away * \param force If true, placing the creature will not fail because of obstacles (creatures/items) */ - bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool forced = false); + bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool force = false); /** * Remove Creature from the map. @@ -229,7 +244,7 @@ class Game return playersRecord; } - LightInfo getWorldLightInfo() const; + void getWorldLightInfo(LightInfo& lightInfo) const; ReturnValue internalMoveCreature(Creature* creature, Direction direction, uint32_t flags = 0); ReturnValue internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags = 0); @@ -307,19 +322,16 @@ class Game * \param text The text to say */ bool internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, - bool ghostMode, SpectatorVec* spectatorsPtr = nullptr, const Position* pos = nullptr); + bool ghostMode, SpectatorVec* listPtr = nullptr, const Position* pos = nullptr); void loadPlayersRecord(); void checkPlayersRecord(); - void sendGuildMotd(uint32_t playerId); void kickPlayer(uint32_t playerId, bool displayEffect); - void playerReportBug(uint32_t playerId, const std::string& message, const Position& position, uint8_t category); + void playerReportBug(uint32_t playerId, const std::string& message); void playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment); - void playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, uint8_t button, uint8_t choice); - void playerReportRuleViolation(uint32_t playerId, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation); - bool internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem); + bool internalStartTrade(Player* player, Player* partner, Item* tradeItem); void internalCloseTrade(Player* player); bool playerBroadcastMessage(Player* player, const std::string& text) const; void broadcastMessage(const std::string& text, MessageClasses type) const; @@ -328,11 +340,10 @@ class Game void playerMoveThing(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count); void playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, const Position& movingCreatureOrigPos, const Position& toPos); - void playerMoveCreature(Player* player, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile); + void playerMoveCreature(Player* playerId, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile); void playerMoveItemByPlayerID(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count); void playerMoveItem(Player* player, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count, Item* item, Cylinder* toCylinder); - void playerEquipItem(uint32_t playerId, uint16_t spriteId); void playerMove(uint32_t playerId, Direction direction); void playerCreatePrivateChannel(uint32_t playerId); void playerChannelInvite(uint32_t playerId, const std::string& name); @@ -341,7 +352,6 @@ class Game void playerOpenChannel(uint32_t playerId, uint16_t channelId); void playerCloseChannel(uint32_t playerId, uint16_t channelId); void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver); - void playerCloseNpcChannel(uint32_t playerId); void playerReceivePing(uint32_t playerId); void playerReceivePingBack(uint32_t playerId); void playerAutoWalk(uint32_t playerId, const std::forward_list& listDir); @@ -355,33 +365,23 @@ class Game void playerUpdateContainer(uint32_t playerId, uint8_t cid); void playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stackPos, const uint16_t spriteId); void playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text); - void playerBrowseField(uint32_t playerId, const Position& pos); void playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index); void playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text); void playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t stackPos, uint32_t tradePlayerId, uint16_t spriteId); void playerAcceptTrade(uint32_t playerId); void playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index); - void playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount, - bool ignoreCap = false, bool inBackpacks = false); - void playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count, - uint8_t amount, bool ignoreEquipped = false); - void playerCloseShop(uint32_t playerId); - void playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count); void playerCloseTrade(uint32_t playerId); void playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId); void playerFollowCreature(uint32_t playerId, uint32_t creatureId); void playerCancelAttackAndFollow(uint32_t playerId); - void playerSetFightModes(uint32_t playerId, fightMode_t fightMode, bool chaseMode, bool secureMode); + void playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, bool secureMode); void playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos); void playerLookInBattleList(uint32_t playerId, uint32_t creatureId); void playerRequestAddVip(uint32_t playerId, const std::string& name); void playerRequestRemoveVip(uint32_t playerId, uint32_t guid); - void playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string& description, uint32_t icon, bool notify); void playerTurn(uint32_t playerId, Direction dir); void playerRequestOutfit(uint32_t playerId); - void playerShowQuestLog(uint32_t playerId); - void playerShowQuestLine(uint32_t playerId, uint16_t questId); void playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type, const std::string& receiver, const std::string& text); void playerChangeOutfit(uint32_t playerId, Outfit_t outfit); @@ -391,18 +391,15 @@ class Game void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId); void playerLeaveParty(uint32_t playerId); void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive); - void playerToggleMount(uint32_t playerId, bool mount); - void playerLeaveMarket(uint32_t playerId); - void playerBrowseMarket(uint32_t playerId, uint16_t spriteId); - void playerBrowseMarketOwnOffers(uint32_t playerId); - void playerBrowseMarketOwnHistory(uint32_t playerId); - void playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t spriteId, uint16_t amount, uint32_t price, bool anonymous); - void playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter); - void playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount); - + void playerProcessRuleViolationReport(uint32_t playerId, const std::string& name); + void playerCloseRuleViolationReport(uint32_t playerId, const std::string& name); + void playerCancelRuleViolationReport(uint32_t playerId); + void playerReportRuleViolationReport(Player* player, const std::string& text); + void playerContinueRuleViolationReport(Player* player, const std::string& text); void parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer); - std::forward_list getMarketItemList(uint16_t wareId, uint16_t sufficientCount, DepotChest* depotChest, Inbox* inbox); + void closeRuleViolationReport(Player* player); + void cancelRuleViolationReport(Player* player); static void updatePremium(Account& account); @@ -413,17 +410,14 @@ class Game bool canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight = true, int32_t rangex = Map::maxClientViewportX, int32_t rangey = Map::maxClientViewportY) const; - bool isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck) const; + bool isSightClear(const Position& fromPos, const Position& toPos, bool sameFloor) const; void changeSpeed(Creature* creature, int32_t varSpeedDelta); - void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit); + void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& oufit); void internalCreatureChangeVisible(Creature* creature, bool visible); void changeLight(const Creature* creature); - void updateCreatureSkull(const Creature* creature); + void updateCreatureSkull(const Creature* player); void updatePlayerShield(Player* player); - void updatePlayerHelpers(const Player& player); - void updateCreatureType(Creature* creature); - void updateCreatureWalkthrough(const Creature* creature); GameState_t getGameState() const; void setGameState(GameState_t newState); @@ -445,11 +439,14 @@ class Game //animation help functions void addCreatureHealth(const Creature* target); - static void addCreatureHealth(const SpectatorVec& spectators, const Creature* target); + static void addCreatureHealth(const SpectatorVec& list, const Creature* target); void addMagicEffect(const Position& pos, uint8_t effect); - static void addMagicEffect(const SpectatorVec& spectators, const Position& pos, uint8_t effect); + static void addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect); void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect); - static void addDistanceEffect(const SpectatorVec& spectators, const Position& fromPos, const Position& toPos, uint8_t effect); + static void addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect); + void addAnimatedText(const Position& pos, uint8_t color, const std::string& text); + static void addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t color, const std::string& text); + void addMonsterSayText(const Position& pos, const std::string& text); void startDecay(Item* item); int32_t getLightHour() const { @@ -465,8 +462,7 @@ class Game uint32_t getMotdNum() const { return motdNum; } void incrementMotdNum() { motdNum++; } - void sendOfflineTrainingDialog(Player* player); - + const std::unordered_map& getRuleViolationReports() const { return ruleViolations; } const std::unordered_map& getPlayers() const { return players; } const std::map& getNpcs() const { return npcs; } @@ -476,55 +472,45 @@ class Game void addNpc(Npc* npc); void removeNpc(Npc* npc); - void addMonster(Monster* monster); - void removeMonster(Monster* monster); + void addMonster(Monster* npc); + void removeMonster(Monster* npc); Guild* getGuild(uint32_t id) const; void addGuild(Guild* guild); void removeGuild(uint32_t guildId); - void decreaseBrowseFieldRef(const Position& pos); - - std::unordered_map browseFields; void internalRemoveItems(std::vector itemList, uint32_t amount, bool stackable); BedItem* getBedBySleeper(uint32_t guid) const; void setBedSleeper(BedItem* bed, uint32_t guid); void removeBedSleeper(uint32_t guid); - - Item* getUniqueItem(uint16_t uniqueId); - bool addUniqueItem(uint16_t uniqueId, Item* item); - void removeUniqueItem(uint16_t uniqueId); - bool reload(ReloadTypes_t reloadType); - Groups groups; Map map; - Mounts mounts; Raids raids; - Quests quests; - std::forward_list toDecayItems; - - private: + protected: bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text); void playerWhisper(Player* player, const std::string& text); bool playerYell(Player* player, const std::string& text); bool playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, const std::string& text); - void playerSpeakToNpc(Player* player, const std::string& text); void checkDecay(); void internalDecayItem(Item* item); + //list of reported rule violations, for correct channel listing + std::unordered_map ruleViolations; + std::unordered_map players; std::unordered_map mappedPlayerNames; std::unordered_map guilds; - std::unordered_map uniqueItems; std::map stages; std::list decayItems[EVENT_DECAY_BUCKETS]; std::list checkCreatureLists[EVENT_CREATURECOUNT]; + std::forward_list toDecayItems; + std::vector ToReleaseCreatures; std::vector ToReleaseItems; @@ -540,8 +526,6 @@ class Game std::map bedSleepersMap; - ModalWindow offlineTrainingWindow { std::numeric_limits::max(), "Choose a Skill", "Please choose a skill:" }; - static constexpr int32_t LIGHT_LEVEL_DAY = 250; static constexpr int32_t LIGHT_LEVEL_NIGHT = 40; static constexpr int32_t SUNSET = 1305; diff --git a/src/globalevent.cpp b/src/globalevent.cpp index daa233e..96c9d71 100644 --- a/src/globalevent.cpp +++ b/src/globalevent.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,47 +35,44 @@ GlobalEvents::GlobalEvents() : GlobalEvents::~GlobalEvents() { - clear(false); + clear(); } -void GlobalEvents::clearMap(GlobalEventMap& map, bool fromLua) +void GlobalEvents::clearMap(GlobalEventMap& map) { - for (auto it = map.begin(); it != map.end(); ) { - if (fromLua == it->second.fromLua) { - it = map.erase(it); - } else { - ++it; - } + for (const auto& it : map) { + delete it.second; } + map.clear(); } -void GlobalEvents::clear(bool fromLua) +void GlobalEvents::clear() { g_scheduler.stopEvent(thinkEventId); thinkEventId = 0; g_scheduler.stopEvent(timerEventId); timerEventId = 0; - clearMap(thinkMap, fromLua); - clearMap(serverMap, fromLua); - clearMap(timerMap, fromLua); + clearMap(thinkMap); + clearMap(serverMap); + clearMap(timerMap); - reInitState(fromLua); + scriptInterface.reInitState(); } -Event_ptr GlobalEvents::getEvent(const std::string& nodeName) +Event* GlobalEvents::getEvent(const std::string& nodeName) { if (strcasecmp(nodeName.c_str(), "globalevent") != 0) { return nullptr; } - return Event_ptr(new GlobalEvent(&scriptInterface)); + return new GlobalEvent(&scriptInterface); } -bool GlobalEvents::registerEvent(Event_ptr event, const pugi::xml_node&) +bool GlobalEvents::registerEvent(Event* event, const pugi::xml_node&) { - GlobalEvent_ptr globalEvent{static_cast(event.release())}; //event is guaranteed to be a GlobalEvent + GlobalEvent* globalEvent = static_cast(event); //event is guaranteed to be a GlobalEvent if (globalEvent->getEventType() == GLOBALEVENT_TIMER) { - auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent)); + auto result = timerMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { if (timerEventId == 0) { timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this))); @@ -83,42 +80,12 @@ bool GlobalEvents::registerEvent(Event_ptr event, const pugi::xml_node&) return true; } } else if (globalEvent->getEventType() != GLOBALEVENT_NONE) { - auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent)); + auto result = serverMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { return true; } } else { // think event - auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent)); - if (result.second) { - if (thinkEventId == 0) { - thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this))); - } - return true; - } - } - - std::cout << "[Warning - GlobalEvents::configureEvent] Duplicate registered globalevent with name: " << globalEvent->getName() << std::endl; - return false; -} - -bool GlobalEvents::registerLuaEvent(GlobalEvent* event) -{ - GlobalEvent_ptr globalEvent{ event }; - if (globalEvent->getEventType() == GLOBALEVENT_TIMER) { - auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent)); - if (result.second) { - if (timerEventId == 0) { - timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this))); - } - return true; - } - } else if (globalEvent->getEventType() != GLOBALEVENT_NONE) { - auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent)); - if (result.second) { - return true; - } - } else { // think event - auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent)); + auto result = thinkMap.emplace(globalEvent->getName(), globalEvent); if (result.second) { if (thinkEventId == 0) { thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this))); @@ -144,9 +111,9 @@ void GlobalEvents::timer() auto it = timerMap.begin(); while (it != timerMap.end()) { - GlobalEvent& globalEvent = it->second; + GlobalEvent* globalEvent = it->second; - int64_t nextExecutionTime = globalEvent.getNextExecution() - now; + int64_t nextExecutionTime = globalEvent->getNextExecution() - now; if (nextExecutionTime > 0) { if (nextExecutionTime < nextScheduledTime) { nextScheduledTime = nextExecutionTime; @@ -156,7 +123,7 @@ void GlobalEvents::timer() continue; } - if (!globalEvent.executeEvent()) { + if (!globalEvent->executeEvent()) { it = timerMap.erase(it); continue; } @@ -166,7 +133,7 @@ void GlobalEvents::timer() nextScheduledTime = nextExecutionTime; } - globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); + globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime); ++it; } @@ -182,10 +149,10 @@ void GlobalEvents::think() int64_t now = OTSYS_TIME(); int64_t nextScheduledTime = std::numeric_limits::max(); - for (auto& it : thinkMap) { - GlobalEvent& globalEvent = it.second; + for (const auto& it : thinkMap) { + GlobalEvent* globalEvent = it.second; - int64_t nextExecutionTime = globalEvent.getNextExecution() - now; + int64_t nextExecutionTime = globalEvent->getNextExecution() - now; if (nextExecutionTime > 0) { if (nextExecutionTime < nextScheduledTime) { nextScheduledTime = nextExecutionTime; @@ -193,16 +160,16 @@ void GlobalEvents::think() continue; } - if (!globalEvent.executeEvent()) { - std::cout << "[Error - GlobalEvents::think] Failed to execute event: " << globalEvent.getName() << std::endl; + if (!globalEvent->executeEvent()) { + std::cout << "[Error - GlobalEvents::think] Failed to execute event: " << globalEvent->getName() << std::endl; } - nextExecutionTime = globalEvent.getInterval(); + nextExecutionTime = globalEvent->getInterval(); if (nextExecutionTime < nextScheduledTime) { nextScheduledTime = nextExecutionTime; } - globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); + globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime); } if (nextScheduledTime != std::numeric_limits::max()) { @@ -213,16 +180,15 @@ void GlobalEvents::think() void GlobalEvents::execute(GlobalEvent_t type) const { for (const auto& it : serverMap) { - const GlobalEvent& globalEvent = it.second; - if (globalEvent.getEventType() == type) { - globalEvent.executeEvent(); + GlobalEvent* globalEvent = it.second; + if (globalEvent->getEventType() == type) { + globalEvent->executeEvent(); } } } GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) { - // TODO: This should be better implemented. Maybe have a map for every type. switch (type) { case GLOBALEVENT_NONE: return thinkMap; case GLOBALEVENT_TIMER: return timerMap; @@ -231,8 +197,8 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) case GLOBALEVENT_RECORD: { GlobalEventMap retMap; for (const auto& it : serverMap) { - if (it.second.getEventType() == type) { - retMap.emplace(it.first, it.second); + if (it.second->getEventType() == type) { + retMap[it.first] = it.second; } } return retMap; @@ -349,7 +315,7 @@ bool GlobalEvent::executeRecord(uint32_t current, uint32_t old) return scriptInterface->callFunction(2); } -bool GlobalEvent::executeEvent() const +bool GlobalEvent::executeEvent() { if (!scriptInterface->reserveScriptEnv()) { std::cout << "[Error - GlobalEvent::executeEvent] Call stack overflow" << std::endl; diff --git a/src/globalevent.h b/src/globalevent.h index 6c65482..b4ba1cb 100644 --- a/src/globalevent.h +++ b/src/globalevent.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,8 +33,7 @@ enum GlobalEvent_t { }; class GlobalEvent; -using GlobalEvent_ptr = std::unique_ptr; -using GlobalEventMap = std::map; +typedef std::map GlobalEventMap; class GlobalEvents final : public BaseEvents { @@ -53,20 +52,18 @@ class GlobalEvents final : public BaseEvents void execute(GlobalEvent_t type) const; GlobalEventMap getEventMap(GlobalEvent_t type); - static void clearMap(GlobalEventMap& map, bool fromLua); + static void clearMap(GlobalEventMap& map); - bool registerLuaEvent(GlobalEvent* event); - void clear(bool fromLua) override final; - - private: - std::string getScriptBaseName() const override { + protected: + std::string getScriptBaseName() const final { return "globalevents"; } + void clear() final; - Event_ptr getEvent(const std::string& nodeName) override; - bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; + Event* getEvent(const std::string& nodeName) final; + bool registerEvent(Event* event, const pugi::xml_node& node) final; - LuaScriptInterface& getScriptInterface() override { + LuaScriptInterface& getScriptInterface() final { return scriptInterface; } LuaScriptInterface scriptInterface; @@ -80,31 +77,22 @@ class GlobalEvent final : public Event public: explicit GlobalEvent(LuaScriptInterface* interface); - bool configureEvent(const pugi::xml_node& node) override; + bool configureEvent(const pugi::xml_node& node) final; bool executeRecord(uint32_t current, uint32_t old); - bool executeEvent() const; + bool executeEvent(); GlobalEvent_t getEventType() const { return eventType; } - void setEventType(GlobalEvent_t type) { - eventType = type; - } const std::string& getName() const { return name; } - void setName(std::string eventName) { - name = eventName; - } uint32_t getInterval() const { return interval; } - void setInterval(uint32_t eventInterval) { - interval |= eventInterval; - } int64_t getNextExecution() const { return nextExecution; @@ -113,10 +101,10 @@ class GlobalEvent final : public Event nextExecution = time; } - private: + protected: GlobalEvent_t eventType = GLOBALEVENT_NONE; - std::string getScriptEventName() const override; + std::string getScriptEventName() const final; std::string name; int64_t nextExecution = 0; diff --git a/src/groups.cpp b/src/groups.cpp index 2908bd0..8b4460d 100644 --- a/src/groups.cpp +++ b/src/groups.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,47 +24,6 @@ #include "pugicast.h" #include "tools.h" -const std::unordered_map ParsePlayerFlagMap = { - {"cannotusecombat", PlayerFlag_CannotUseCombat}, - {"cannotattackplayer", PlayerFlag_CannotAttackPlayer}, - {"cannotattackmonster", PlayerFlag_CannotAttackMonster}, - {"cannotbeattacked", PlayerFlag_CannotBeAttacked}, - {"canconvinceall", PlayerFlag_CanConvinceAll}, - {"cansummonall", PlayerFlag_CanSummonAll}, - {"canillusionall", PlayerFlag_CanIllusionAll}, - {"cansenseinvisibility", PlayerFlag_CanSenseInvisibility}, - {"ignoredbymonsters", PlayerFlag_IgnoredByMonsters}, - {"notgaininfight", PlayerFlag_NotGainInFight}, - {"hasinfinitemana", PlayerFlag_HasInfiniteMana}, - {"hasinfinitesoul", PlayerFlag_HasInfiniteSoul}, - {"hasnoexhaustion", PlayerFlag_HasNoExhaustion}, - {"cannotusespells", PlayerFlag_CannotUseSpells}, - {"cannotpickupitem", PlayerFlag_CannotPickupItem}, - {"canalwayslogin", PlayerFlag_CanAlwaysLogin}, - {"canbroadcast", PlayerFlag_CanBroadcast}, - {"canedithouses", PlayerFlag_CanEditHouses}, - {"cannotbebanned", PlayerFlag_CannotBeBanned}, - {"cannotbepushed", PlayerFlag_CannotBePushed}, - {"hasinfinitecapacity", PlayerFlag_HasInfiniteCapacity}, - {"cannotpushallcreatures", PlayerFlag_CanPushAllCreatures}, - {"cantalkredprivate", PlayerFlag_CanTalkRedPrivate}, - {"cantalkredchannel", PlayerFlag_CanTalkRedChannel}, - {"talkorangehelpchannel", PlayerFlag_TalkOrangeHelpChannel}, - {"notgainexperience", PlayerFlag_NotGainExperience}, - {"notgainmana", PlayerFlag_NotGainMana}, - {"notgainhealth", PlayerFlag_NotGainHealth}, - {"notgainskill", PlayerFlag_NotGainSkill}, - {"setmaxspeed", PlayerFlag_SetMaxSpeed}, - {"specialvip", PlayerFlag_SpecialVIP}, - {"notgenerateloot", PlayerFlag_NotGenerateLoot}, - {"cantalkredchannelanonymous", PlayerFlag_CanTalkRedChannelAnonymous}, - {"ignoreprotectionzone", PlayerFlag_IgnoreProtectionZone}, - {"ignorespellcheck", PlayerFlag_IgnoreSpellCheck}, - {"ignoreweaponcheck", PlayerFlag_IgnoreWeaponCheck}, - {"cannotbemuted", PlayerFlag_CannotBeMuted}, - {"isalwayspremium", PlayerFlag_IsAlwaysPremium} -}; - bool Groups::load() { pugi::xml_document doc; @@ -78,24 +37,10 @@ bool Groups::load() Group group; group.id = pugi::cast(groupNode.attribute("id").value()); group.name = groupNode.attribute("name").as_string(); + group.flags = pugi::cast(groupNode.attribute("flags").value()); group.access = groupNode.attribute("access").as_bool(); group.maxDepotItems = pugi::cast(groupNode.attribute("maxdepotitems").value()); group.maxVipEntries = pugi::cast(groupNode.attribute("maxvipentries").value()); - group.flags = pugi::cast(groupNode.attribute("flags").value()); - if (pugi::xml_node node = groupNode.child("flags")) { - for (auto flagNode : node.children()) { - pugi::xml_attribute attr = flagNode.first_attribute(); - if (!attr || (attr && !attr.as_bool())) { - continue; - } - - auto parseFlag = ParsePlayerFlagMap.find(attr.name()); - if (parseFlag != ParsePlayerFlagMap.end()) { - group.flags |= parseFlag->second; - } - } - } - groups.push_back(group); } return true; diff --git a/src/groups.h b/src/groups.h index 3297ed5..e16adfb 100644 --- a/src/groups.h +++ b/src/groups.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/guild.cpp b/src/guild.cpp index 6e27c57..e6bed21 100644 --- a/src/guild.cpp +++ b/src/guild.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,18 +28,11 @@ extern Game g_game; void Guild::addMember(Player* player) { membersOnline.push_back(player); - for (Player* member : membersOnline) { - g_game.updatePlayerHelpers(*member); - } } void Guild::removeMember(Player* player) { membersOnline.remove(player); - for (Player* member : membersOnline) { - g_game.updatePlayerHelpers(*member); - } - g_game.updatePlayerHelpers(*player); if (membersOnline.empty()) { g_game.removeGuild(id); @@ -57,16 +50,6 @@ GuildRank* Guild::getRankById(uint32_t rankId) return nullptr; } -const GuildRank* Guild::getRankByName(const std::string& name) const -{ - for (const auto& rank : ranks) { - if (rank.name == name) { - return &rank; - } - } - return nullptr; -} - const GuildRank* Guild::getRankByLevel(uint8_t level) const { for (const auto& rank : ranks) { diff --git a/src/guild.h b/src/guild.h index e0a6f12..3085ec9 100644 --- a/src/guild.h +++ b/src/guild.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -55,26 +55,14 @@ class Guild memberCount = count; } - const std::vector& getRanks() const { - return ranks; - } - GuildRank* getRankById(uint32_t rankId); - const GuildRank* getRankByName(const std::string& name) const; + GuildRank* getRankById(uint32_t id); const GuildRank* getRankByLevel(uint8_t level) const; - void addRank(uint32_t rankId, const std::string& rankName, uint8_t level); - - const std::string& getMotd() const { - return motd; - } - void setMotd(const std::string& motd) { - this->motd = motd; - } + void addRank(uint32_t id, const std::string& name, uint8_t level); private: std::list membersOnline; std::vector ranks; std::string name; - std::string motd; uint32_t id; uint32_t memberCount = 0; }; diff --git a/src/house.cpp b/src/house.cpp index e37befb..29f375c 100644 --- a/src/house.cpp +++ b/src/house.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -41,11 +41,11 @@ void House::addTile(HouseTile* tile) void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* player/* = nullptr*/) { if (updateDatabase && owner != guid) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; query << "UPDATE `houses` SET `owner` = " << guid << ", `bid` = 0, `bid_end` = 0, `last_bid` = 0, `highest_bidder` = 0 WHERE `id` = " << id; - db.executeQuery(query.str()); + db->executeQuery(query.str()); } if (isLoaded && owner == guid) { @@ -85,26 +85,12 @@ void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* play for (Door* door : doorSet) { door->setAccessList(""); } - } else { - std::string strRentPeriod = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD)); - time_t currentTime = time(nullptr); - if (strRentPeriod == "yearly") { - currentTime += 24 * 60 * 60 * 365; - } else if (strRentPeriod == "monthly") { - currentTime += 24 * 60 * 60 * 30; - } else if (strRentPeriod == "weekly") { - currentTime += 24 * 60 * 60 * 7; - } else if (strRentPeriod == "daily") { - currentTime += 24 * 60 * 60; - } else { - currentTime = 0; - } - paidUntil = currentTime; + //reset paid date + paidUntil = 0; + rentWarnings = 0; } - rentWarnings = 0; - if (guid != 0) { std::string name = IOLoginData::getNameByGuid(guid); if (!name.empty()) { @@ -124,9 +110,9 @@ void House::updateDoorDescription() const } else { ss << "It belongs to house '" << houseName << "'. Nobody owns this house."; - const int32_t housePrice = g_config.getNumber(ConfigManager::HOUSE_PRICE); + const int32_t housePrice = getRent(); if (housePrice != -1) { - ss << " It costs " << (houseTiles.size() * housePrice) << " gold coins."; + ss << " It costs " << housePrice * 5 << " gold coins."; } } @@ -230,6 +216,7 @@ bool House::transferToDepot() const transferToDepot(&tmpPlayer); IOLoginData::savePlayer(&tmpPlayer); } + return true; } @@ -258,8 +245,9 @@ bool House::transferToDepot(Player* player) const } for (Item* item : moveItemList) { - g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT); + g_game.internalMoveItem(item->getParent(), player->getDepotLocker(getTownId(), true), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT); } + return true; } @@ -408,7 +396,7 @@ bool House::executeTransfer(HouseTransferItem* item, Player* newOwner) void AccessList::parseList(const std::string& list) { playerList.clear(); - guildRankList.clear(); + guildList.clear(); expressionList.clear(); regExList.clear(); this->list = list; @@ -433,11 +421,7 @@ void AccessList::parseList(const std::string& list) std::string::size_type at_pos = line.find("@"); if (at_pos != std::string::npos) { - if (at_pos == 0) { - addGuild(line.substr(1)); - } else { - addGuildRank(line.substr(0, at_pos - 1), line.substr(at_pos + 1)); - } + addGuild(line.substr(at_pos + 1)); } else if (line.find("!") != std::string::npos || line.find("*") != std::string::npos || line.find("?") != std::string::npos) { addExpression(line); } else { @@ -459,43 +443,11 @@ void AccessList::addPlayer(const std::string& name) } } -namespace { - -const Guild* getGuildByName(const std::string& name) -{ - uint32_t guildId = IOGuild::getGuildIdByName(name); - if (guildId == 0) { - return nullptr; - } - - const Guild* guild = g_game.getGuild(guildId); - if (guild) { - return guild; - } - - return IOGuild::loadGuild(guildId); -} - -} - void AccessList::addGuild(const std::string& name) { - const Guild* guild = getGuildByName(name); - if (guild) { - for (const auto& rank : guild->getRanks()) { - guildRankList.insert(rank.id); - } - } -} - -void AccessList::addGuildRank(const std::string& name, const std::string& rankName) -{ - const Guild* guild = getGuildByName(name); - if (guild) { - const GuildRank* rank = guild->getRankByName(rankName); - if (rank) { - guildRankList.insert(rank->id); - } + uint32_t guildId = IOGuild::getGuildIdByName(name); + if (guildId != 0) { + guildList.insert(guildId); } } @@ -550,8 +502,8 @@ bool AccessList::isInList(const Player* player) return true; } - const GuildRank* rank = player->getGuildRank(); - return rank && guildRankList.find(rank->id) != guildRankList.end(); + const Guild* guild = player->getGuild(); + return guild && guildList.find(guild->getId()) != guildList.end(); } void AccessList::getList(std::string& list) const @@ -715,9 +667,7 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const continue; } - if (player.getBankBalance() >= rent) { - player.setBankBalance(player.getBankBalance() - rent); - + if (g_game.removeMoney(player.getDepotLocker(house->getTownId(), true), house->getRent(), FLAG_NOLIMIT)) { time_t paidUntil = currentTime; switch (rentPeriod) { case RENTPERIOD_DAILY: @@ -768,7 +718,7 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const std::ostringstream ss; ss << "Warning! \nThe " << period << " rent of " << house->getRent() << " gold for your house \"" << house->getName() << "\" is payable. Have it within " << daysLeft << " days or you will lose this house."; letter->setText(ss.str()); - g_game.internalAddItem(player.getInbox(), letter, INDEX_WHEREEVER, FLAG_NOLIMIT); + g_game.internalAddItem(player.getDepotLocker(house->getTownId(), true), letter, INDEX_WHEREEVER, FLAG_NOLIMIT); house->setPayRentWarnings(house->getPayRentWarnings() + 1); } else { house->setOwner(0, true, &player); diff --git a/src/house.h b/src/house.h index 386ef1f..3d9b543 100644 --- a/src/house.h +++ b/src/house.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ #include #include -#include #include "container.h" #include "housetile.h" @@ -38,7 +37,6 @@ class AccessList void parseList(const std::string& list); void addPlayer(const std::string& name); void addGuild(const std::string& name); - void addGuildRank(const std::string& name, const std::string& rankName); void addExpression(const std::string& expression); bool isInList(const Player* player); @@ -48,7 +46,7 @@ class AccessList private: std::string list; std::unordered_set playerList; - std::unordered_set guildRankList; + std::unordered_set guildList; // TODO: include ranks std::list expressionList; std::list> regExList; }; @@ -62,10 +60,10 @@ class Door final : public Item Door(const Door&) = delete; Door& operator=(const Door&) = delete; - Door* getDoor() override { + Door* getDoor() final { return this; } - const Door* getDoor() const override { + const Door* getDoor() const final { return this; } @@ -74,8 +72,8 @@ class Door final : public Item } //serialization - Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; - void serializeAttr(PropWriteStream&) const override {} + Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final; + void serializeAttr(PropWriteStream&) const final {} void setDoorId(uint32_t doorId) { setIntAttr(ITEM_ATTRIBUTE_DOORID, doorId); @@ -89,11 +87,12 @@ class Door final : public Item void setAccessList(const std::string& textlist); bool getAccessList(std::string& list) const; - void onRemoved() override; + void onRemoved() final; - private: + protected: void setHouse(House* house); + private: House* house = nullptr; std::unique_ptr accessList; friend class House; @@ -111,8 +110,8 @@ enum AccessHouseLevel_t { HOUSE_OWNER = 3, }; -using HouseTileList = std::list; -using HouseBedItemList = std::list; +typedef std::list HouseTileList; +typedef std::list HouseBedItemList; class HouseTransferItem final : public Item { @@ -121,12 +120,12 @@ class HouseTransferItem final : public Item explicit HouseTransferItem(House* house) : Item(0), house(house) {} - void onTradeEvent(TradeEvents_t event, Player* owner) override; - bool canTransform() const override { + void onTradeEvent(TradeEvents_t event, Player* owner) final; + bool canTransform() const final { return false; } - private: + protected: House* house; }; @@ -208,7 +207,7 @@ class House HouseTransferItem* getTransferItem(); void resetTransferItem(); - bool executeTransfer(HouseTransferItem* item, Player* newOwner); + bool executeTransfer(HouseTransferItem* item, Player* player); const HouseTileList& getTiles() const { return houseTiles; @@ -218,6 +217,7 @@ class House return doorSet; } + void addBed(BedItem* bed); const HouseBedItemList& getBeds() const { return bedsList; @@ -257,7 +257,7 @@ class House bool isLoaded = false; }; -using HouseMap = std::map; +typedef std::map HouseMap; enum RentPeriod_t { RENTPERIOD_DAILY, diff --git a/src/housetile.cpp b/src/housetile.cpp index 3cf8993..f3fccf5 100644 --- a/src/housetile.cpp +++ b/src/housetile.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/housetile.h b/src/housetile.h index fcb6d22..17ef634 100644 --- a/src/housetile.h +++ b/src/housetile.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,13 +31,13 @@ class HouseTile final : public DynamicTile //cylinder implementations ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor = nullptr) const override; + uint32_t flags, Creature* actor = nullptr) const final; Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem, - uint32_t& flags) override; + uint32_t& flags) final; - void addThing(int32_t index, Thing* thing) override; - void internalAddThing(uint32_t index, Thing* thing) override; + void addThing(int32_t index, Thing* thing) final; + void internalAddThing(uint32_t index, Thing* thing) final; House* getHouse() { return house; diff --git a/src/inbox.cpp b/src/inbox.cpp deleted file mode 100644 index 42dff7d..0000000 --- a/src/inbox.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "inbox.h" -#include "tools.h" - -Inbox::Inbox(uint16_t type) : Container(type, 30, false, true) {} - -ReturnValue Inbox::queryAdd(int32_t, const Thing& thing, uint32_t, - uint32_t flags, Creature*) const -{ - if (!hasBitSet(FLAG_NOLIMIT, flags)) { - return RETURNVALUE_CONTAINERNOTENOUGHROOM; - } - - const Item* item = thing.getItem(); - if (!item) { - return RETURNVALUE_NOTPOSSIBLE; - } - - if (item == this) { - return RETURNVALUE_THISISIMPOSSIBLE; - } - - if (!item->isPickupable()) { - return RETURNVALUE_CANNOTPICKUP; - } - - return RETURNVALUE_NOERROR; -} - -void Inbox::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t) -{ - Cylinder* parent = getParent(); - if (parent != nullptr) { - parent->postAddNotification(thing, oldParent, index, LINK_PARENT); - } -} - -void Inbox::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t) -{ - Cylinder* parent = getParent(); - if (parent != nullptr) { - parent->postRemoveNotification(thing, newParent, index, LINK_PARENT); - } -} - -Cylinder* Inbox::getParent() const -{ - if (parent) { - return parent->getParent(); - } - return nullptr; -} diff --git a/src/inbox.h b/src/inbox.h deleted file mode 100644 index 8ad746a..0000000 --- a/src/inbox.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_INBOX_H_C3EF10190329447883B9C3479234EE5C -#define FS_INBOX_H_C3EF10190329447883B9C3479234EE5C - -#include "container.h" - -class Inbox final : public Container -{ - public: - explicit Inbox(uint16_t type); - - //cylinder implementations - ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor = nullptr) const override; - - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - - //overrides - bool canRemove() const override { - return false; - } - - Cylinder* getParent() const override; - Cylinder* getRealParent() const override { - return parent; - } -}; - -#endif - diff --git a/src/ioguild.cpp b/src/ioguild.cpp index ed48b14..b4ef9b5 100644 --- a/src/ioguild.cpp +++ b/src/ioguild.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,51 +19,29 @@ #include "otpch.h" -#include "database.h" -#include "guild.h" #include "ioguild.h" - -Guild* IOGuild::loadGuild(uint32_t guildId) -{ - Database& db = Database::getInstance(); - std::ostringstream query; - query << "SELECT `name` FROM `guilds` WHERE `id` = " << guildId; - if (DBResult_ptr result = db.storeQuery(query.str())) { - Guild* guild = new Guild(guildId, result->getString("name")); - - query.str(std::string()); - query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = " << guildId; - - if ((result = db.storeQuery(query.str()))) { - do { - guild->addRank(result->getNumber("id"), result->getString("name"), result->getNumber("level")); - } while (result->next()); - } - return guild; - } - return nullptr; -} +#include "database.h" uint32_t IOGuild::getGuildIdByName(const std::string& name) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `id` FROM `guilds` WHERE `name` = " << db.escapeString(name); + query << "SELECT `id` FROM `guilds` WHERE `name` = " << db->escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return 0; } return result->getNumber("id"); } -void IOGuild::getWarList(uint32_t guildId, GuildWarVector& guildWarVector) +void IOGuild::getWarList(uint32_t guildId, GuildWarList& guildWarList) { std::ostringstream query; query << "SELECT `guild1`, `guild2` FROM `guild_wars` WHERE (`guild1` = " << guildId << " OR `guild2` = " << guildId << ") AND `ended` = 0 AND `status` = 1"; - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + DBResult_ptr result = Database::getInstance()->storeQuery(query.str()); if (!result) { return; } @@ -71,9 +49,9 @@ void IOGuild::getWarList(uint32_t guildId, GuildWarVector& guildWarVector) do { uint32_t guild1 = result->getNumber("guild1"); if (guildId != guild1) { - guildWarVector.push_back(guild1); + guildWarList.push_back(guild1); } else { - guildWarVector.push_back(result->getNumber("guild2")); + guildWarList.push_back(result->getNumber("guild2")); } } while (result->next()); } diff --git a/src/ioguild.h b/src/ioguild.h index 66e4339..e5f058c 100644 --- a/src/ioguild.h +++ b/src/ioguild.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,15 +20,13 @@ #ifndef FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF #define FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF -class Guild; -using GuildWarVector = std::vector; +typedef std::vector GuildWarList; class IOGuild { public: - static Guild* loadGuild(uint32_t guildId); static uint32_t getGuildIdByName(const std::string& name); - static void getWarList(uint32_t guildId, GuildWarVector& guildWarVector); + static void getWarList(uint32_t guildId, GuildWarList& guildWarList); }; #endif diff --git a/src/iologindata.cpp b/src/iologindata.cpp index 040688c..74c6f88 100644 --- a/src/iologindata.cpp +++ b/src/iologindata.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,14 +31,13 @@ Account IOLoginData::loadAccount(uint32_t accno) Account account; std::ostringstream query; - query << "SELECT `id`, `name`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno; - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno; + DBResult_ptr result = Database::getInstance()->storeQuery(query.str()); if (!result) { return account; } account.id = result->getNumber("id"); - account.name = result->getString("name"); account.accountType = static_cast(result->getNumber("type")); account.premiumDays = result->getNumber("premdays"); account.lastDay = result->getNumber("lastday"); @@ -49,45 +48,16 @@ bool IOLoginData::saveAccount(const Account& acc) { std::ostringstream query; query << "UPDATE `accounts` SET `premdays` = " << acc.premiumDays << ", `lastday` = " << acc.lastDay << " WHERE `id` = " << acc.id; - return Database::getInstance().executeQuery(query.str()); + return Database::getInstance()->executeQuery(query.str()); } -std::string decodeSecret(const std::string& secret) +bool IOLoginData::loginserverAuthentication(uint32_t accountNumber, const std::string& password, Account& account) { - // simple base32 decoding - std::string key; - key.reserve(10); - - uint32_t buffer = 0, left = 0; - for (const auto& ch : secret) { - buffer <<= 5; - if (ch >= 'A' && ch <= 'Z') { - buffer |= (ch & 0x1F) - 1; - } else if (ch >= '2' && ch <= '7') { - buffer |= ch - 24; - } else { - // if a key is broken, return empty and the comparison - // will always be false since the token must not be empty - return {}; - } - - left += 5; - if (left >= 8) { - left -= 8; - key.push_back(static_cast(buffer >> left)); - } - } - - return key; -} - -bool IOLoginData::loginserverAuthentication(const std::string& name, const std::string& password, Account& account) -{ - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `id`, `name`, `password`, `secret`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `name` = " << db.escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); + query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accountNumber; + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -97,15 +67,13 @@ bool IOLoginData::loginserverAuthentication(const std::string& name, const std:: } account.id = result->getNumber("id"); - account.name = result->getString("name"); - account.key = decodeSecret(result->getString("secret")); account.accountType = static_cast(result->getNumber("type")); account.premiumDays = result->getNumber("premdays"); account.lastDay = result->getNumber("lastday"); query.str(std::string()); query << "SELECT `name`, `deletion` FROM `players` WHERE `account_id` = " << account.id; - result = db.storeQuery(query.str()); + result = db->storeQuery(query.str()); if (result) { do { if (result->getNumber("deletion") == 0) { @@ -117,29 +85,17 @@ bool IOLoginData::loginserverAuthentication(const std::string& name, const std:: return true; } -uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName, std::string& token, uint32_t tokenTime) +uint32_t IOLoginData::gameworldAuthentication(uint32_t accountNumber, const std::string& password, std::string& characterName) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `id`, `password`, `secret` FROM `accounts` WHERE `name` = " << db.escapeString(accountName); - DBResult_ptr result = db.storeQuery(query.str()); + query << "SELECT `id`, `password` FROM `accounts` WHERE `id` = " << accountNumber; + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return 0; } - std::string secret = decodeSecret(result->getString("secret")); - if (!secret.empty()) { - if (token.empty()) { - return 0; - } - - bool tokenValid = token == generateToken(secret, tokenTime) || token == generateToken(secret, tokenTime - 1) || token == generateToken(secret, tokenTime + 1); - if (!tokenValid) { - return 0; - } - } - if (transformToSHA1(password) != result->getString("password")) { return 0; } @@ -147,8 +103,8 @@ uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, co uint32_t accountId = result->getNumber("id"); query.str(std::string()); - query << "SELECT `account_id`, `name`, `deletion` FROM `players` WHERE `name` = " << db.escapeString(characterName); - result = db.storeQuery(query.str()); + query << "SELECT `account_id`, `name`, `deletion` FROM `players` WHERE `name` = " << db->escapeString(characterName); + result = db->storeQuery(query.str()); if (!result) { return 0; } @@ -164,7 +120,7 @@ AccountType_t IOLoginData::getAccountType(uint32_t accountId) { std::ostringstream query; query << "SELECT `type` FROM `accounts` WHERE `id` = " << accountId; - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + DBResult_ptr result = Database::getInstance()->storeQuery(query.str()); if (!result) { return ACCOUNT_TYPE_NORMAL; } @@ -175,7 +131,7 @@ void IOLoginData::setAccountType(uint32_t accountId, AccountType_t accountType) { std::ostringstream query; query << "UPDATE `accounts` SET `type` = " << static_cast(accountType) << " WHERE `id` = " << accountId; - Database::getInstance().executeQuery(query.str()); + Database::getInstance()->executeQuery(query.str()); } void IOLoginData::updateOnlineStatus(uint32_t guid, bool login) @@ -190,20 +146,20 @@ void IOLoginData::updateOnlineStatus(uint32_t guid, bool login) } else { query << "DELETE FROM `players_online` WHERE `player_id` = " << guid; } - Database::getInstance().executeQuery(query.str()); + Database::getInstance()->executeQuery(query.str()); } bool IOLoginData::preloadPlayer(Player* player, const std::string& name) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; query << "SELECT `id`, `account_id`, `group_id`, `deletion`, (SELECT `type` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `account_type`"; if (!g_config.getBoolean(ConfigManager::FREE_PREMIUM)) { query << ", (SELECT `premdays` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days`"; } - query << " FROM `players` WHERE `name` = " << db.escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); + query << " FROM `players` WHERE `name` = " << db->escapeString(name); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -231,18 +187,17 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string& name) bool IOLoginData::loadPlayerById(Player* player, uint32_t id) { - Database& db = Database::getInstance(); std::ostringstream query; - query << "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`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `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` = " << id; - return loadPlayer(player, db.storeQuery(query.str())); + query << "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`, `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` FROM `players` WHERE `id` = " << id; + return loadPlayer(player, Database::getInstance()->storeQuery(query.str())); } bool IOLoginData::loadPlayerByName(Player* player, const std::string& name) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "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`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `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` = " << db.escapeString(name); - return loadPlayer(player, db.storeQuery(query.str())); + query << "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`, `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` FROM `players` WHERE `name` = " << db->escapeString(name); + return loadPlayer(player, db->storeQuery(query.str())); } bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) @@ -251,7 +206,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) return false; } - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); uint32_t accno = result->getNumber("account_id"); Account acc = loadAccount(accno); @@ -297,7 +252,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) } player->soul = result->getNumber("soul"); - player->capacity = result->getNumber("cap") * 100; + player->capacity = std::max(400, result->getNumber("cap")) * 100; player->blessings = result->getNumber("blessings"); unsigned long conditionsSize; @@ -343,20 +298,17 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) player->defaultOutfit.lookFeet = result->getNumber("lookfeet"); player->defaultOutfit.lookAddons = result->getNumber("lookaddons"); player->currentOutfit = player->defaultOutfit; - player->direction = static_cast (result->getNumber("direction")); if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { - const time_t skullSeconds = result->getNumber("skulltime") - time(nullptr); - if (skullSeconds > 0) { - //ensure that we round up the number of ticks - player->skullTicks = (skullSeconds + 2); + player->playerKillerEnd = result->getNumber("skulltime"); - uint16_t skull = result->getNumber("skull"); - if (skull == SKULL_RED) { - player->skull = SKULL_RED; - } else if (skull == SKULL_BLACK) { - player->skull = SKULL_BLACK; - } + uint16_t skull = result->getNumber("skull"); + if (skull == SKULL_RED) { + player->skull = SKULL_RED; + } + + if (player->playerKillerEnd == 0) { + player->skull = SKULL_NONE; } } @@ -367,9 +319,6 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) player->lastLoginSaved = result->getNumber("lastlogin"); player->lastLogout = result->getNumber("lastlogout"); - player->offlineTrainingTime = result->getNumber("offlinetraining_time") * 1000; - player->offlineTrainingSkill = result->getNumber("offlinetraining_skill"); - Town* town = g_game.map.towns.getTown(result->getNumber("town_id")); if (!town) { std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Town ID " << result->getNumber("town_id") << " which doesn't exist" << std::endl; @@ -402,16 +351,38 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) } std::ostringstream query; + + query << "SELECT `date` FROM `player_murders` WHERE `player_id` = " << player->getGUID() << " ORDER BY `date` ASC"; + if ((result = db->storeQuery(query.str()))) { + do { + player->murderTimeStamps.push_back(result->getNumber("date")); + } while (result->next()); + } + + query.str(std::string()); query << "SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { uint32_t guildId = result->getNumber("guild_id"); uint32_t playerRankId = result->getNumber("rank_id"); player->guildNick = result->getString("nick"); Guild* guild = g_game.getGuild(guildId); if (!guild) { - guild = IOGuild::loadGuild(guildId); - g_game.addGuild(guild); + query.str(std::string()); + query << "SELECT `name` FROM `guilds` WHERE `id` = " << guildId; + if ((result = db->storeQuery(query.str()))) { + guild = new Guild(guildId, result->getString("name")); + g_game.addGuild(guild); + + query.str(std::string()); + query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = " << guildId; + + if ((result = db->storeQuery(query.str()))) { + do { + guild->addRank(result->getNumber("id"), result->getString("name"), result->getNumber("level")); + } while (result->next()); + } + } } if (guild) { @@ -421,7 +392,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) query.str(std::string()); query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = " << playerRankId; - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { guild->addRank(result->getNumber("id"), result->getString("name"), result->getNumber("level")); } @@ -433,11 +404,11 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) player->guildRank = rank; - IOGuild::getWarList(guildId, player->guildWarVector); + IOGuild::getWarList(guildId, player->guildWarList); query.str(std::string()); query << "SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = " << guildId; - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { guild->setMemberCount(result->getNumber("members")); } } @@ -445,7 +416,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) query.str(std::string()); query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { do { player->learnedInstantSpellList.emplace_front(result->getString("name")); } while (result->next()); @@ -456,7 +427,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) query.str(std::string()); query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { loadItems(itemMap, result); for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { @@ -484,7 +455,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) query.str(std::string()); query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { loadItems(itemMap, result); for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { @@ -493,9 +464,15 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) int32_t pid = pair.second; if (pid >= 0 && pid < 100) { - DepotChest* depotChest = player->getDepotChest(pid, true); - if (depotChest) { - depotChest->internalAddThing(item); + Container* itemContainer = item->getContainer(); + if (itemContainer) { + DepotLocker* locker = itemContainer->getDepotLocker(); + if (locker) { + DepotLocker* existingLocker = player->getDepotLocker(pid, false); + if (!existingLocker) { + player->depotLockerMap[pid] = locker; + } + } } } else { ItemMap::const_iterator it2 = itemMap.find(pid); @@ -511,49 +488,19 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) } } - //load inbox items - itemMap.clear(); - - query.str(std::string()); - query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; - if ((result = db.storeQuery(query.str()))) { - loadItems(itemMap, result); - - for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { - const std::pair& pair = it->second; - Item* item = pair.first; - int32_t pid = pair.second; - - if (pid >= 0 && pid < 100) { - player->getInbox()->internalAddThing(item); - } else { - ItemMap::const_iterator it2 = itemMap.find(pid); - - if (it2 == itemMap.end()) { - continue; - } - - Container* container = it2->second.first->getContainer(); - if (container) { - container->internalAddThing(item); - } - } - } - } - //load storage map query.str(std::string()); query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID(); - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { do { - player->addStorageValue(result->getNumber("key"), result->getNumber("value"), true); + player->addStorageValue(result->getNumber("key"), result->getNumber("value")); } while (result->next()); } //load vip query.str(std::string()); query << "SELECT `player_id` FROM `account_viplist` WHERE `account_id` = " << player->getAccount(); - if ((result = db.storeQuery(query.str()))) { + if ((result = db->storeQuery(query.str()))) { do { player->addVIPInternal(result->getNumber("player_id")); } while (result->next()); @@ -569,12 +516,12 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, { std::ostringstream ss; - using ContainerBlock = std::pair; - std::list queue; + typedef std::pair containerBlock; + std::list queue; int32_t runningId = 100; - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); for (const auto& it : itemList) { int32_t pid = it.first; Item* item = it.second; @@ -586,7 +533,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, size_t attributesSize; const char* attributes = propWriteStream.getStream(attributesSize); - ss << player->getGUID() << ',' << pid << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize); + ss << player->getGUID() << ',' << pid << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db->escapeBlob(attributes, attributesSize); if (!query_insert.addRow(ss)) { return false; } @@ -597,7 +544,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, } while (!queue.empty()) { - const ContainerBlock& cb = queue.front(); + const containerBlock& cb = queue.front(); Container* container = cb.first; int32_t parentId = cb.second; queue.pop_front(); @@ -616,7 +563,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList, size_t attributesSize; const char* attributes = propWriteStream.getStream(attributesSize); - ss << player->getGUID() << ',' << parentId << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize); + ss << player->getGUID() << ',' << parentId << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db->escapeBlob(attributes, attributesSize); if (!query_insert.addRow(ss)) { return false; } @@ -631,11 +578,11 @@ bool IOLoginData::savePlayer(Player* player) player->changeHealth(1); } - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID(); - DBResult_ptr result = db.storeQuery(query.str()); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -643,7 +590,7 @@ bool IOLoginData::savePlayer(Player* player) if (result->getNumber("save") == 0) { query.str(std::string()); query << "UPDATE `players` SET `lastlogin` = " << player->lastLoginSaved << ", `lastip` = " << player->lastIP << " WHERE `id` = " << player->getGUID(); - return db.executeQuery(query.str()); + return db->executeQuery(query.str()); } //serialize conditions @@ -686,7 +633,7 @@ bool IOLoginData::savePlayer(Player* player) query << "`posz` = " << loginPosition.getZ() << ','; query << "`cap` = " << (player->capacity / 100) << ','; - query << "`sex` = " << static_cast(player->sex) << ','; + query << "`sex` = " << player->sex << ','; if (player->lastLoginSaved != 0) { query << "`lastlogin` = " << player->lastLoginSaved << ','; @@ -696,29 +643,21 @@ bool IOLoginData::savePlayer(Player* player) query << "`lastip` = " << player->lastIP << ','; } - query << "`conditions` = " << db.escapeBlob(conditions, conditionsSize) << ','; + query << "`conditions` = " << db->escapeBlob(conditions, conditionsSize) << ','; if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { - int64_t skullTime = 0; - - if (player->skullTicks > 0) { - skullTime = time(nullptr) + player->skullTicks; - } - query << "`skulltime` = " << skullTime << ','; + query << "`skulltime` = " << player->getPlayerKillerEnd() << ','; Skulls_t skull = SKULL_NONE; if (player->skull == SKULL_RED) { skull = SKULL_RED; - } else if (player->skull == SKULL_BLACK) { - skull = SKULL_BLACK; } - query << "`skull` = " << static_cast(skull) << ','; + + query << "`skull` = " << static_cast(skull) << ','; } query << "`lastlogout` = " << player->getLastLogout() << ','; query << "`balance` = " << player->bankBalance << ','; - query << "`offlinetraining_time` = " << player->getOfflineTrainingTime() / 1000 << ','; - query << "`offlinetraining_skill` = " << player->getOfflineTrainingSkill() << ','; query << "`stamina` = " << player->getStaminaMinutes() << ','; query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ','; @@ -735,7 +674,6 @@ bool IOLoginData::savePlayer(Player* player) query << "`skill_shielding_tries` = " << player->skills[SKILL_SHIELD].tries << ','; query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ','; query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ','; - query << "`direction` = " << static_cast (player->getDirection()) << ','; if (!player->isOffline()) { query << "`onlinetime` = `onlinetime` + " << (time(nullptr) - player->lastLoginSaved) << ','; @@ -748,14 +686,14 @@ bool IOLoginData::savePlayer(Player* player) return false; } - if (!db.executeQuery(query.str())) { + if (!db->executeQuery(query.str())) { return false; } // learned spells query.str(std::string()); query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { + if (!db->executeQuery(query.str())) { return false; } @@ -763,7 +701,7 @@ bool IOLoginData::savePlayer(Player* player) DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES "); for (const std::string& spellName : player->learnedInstantSpellList) { - query << player->getGUID() << ',' << db.escapeString(spellName); + query << player->getGUID() << ',' << db->escapeString(spellName); if (!spellsQuery.addRow(query)) { return false; } @@ -773,9 +711,31 @@ bool IOLoginData::savePlayer(Player* player) return false; } + query.str(std::string()); + query << "DELETE FROM `player_murders` WHERE `player_id` = " << player->getGUID(); + + if (!db->executeQuery(query.str())) { + return false; + } + + query.str(std::string()); + + DBInsert murdersQuery("INSERT INTO `player_murders`(`id`, `player_id`, `date`) VALUES "); + for (time_t timestamp : player->murderTimeStamps) { + query << "NULL," << player->getGUID() << ',' << timestamp; + if (!murdersQuery.addRow(query)) { + return false; + } + } + + if (!murdersQuery.execute()) { + return false; + } + //item saving + query.str(std::string()); query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { + if (!db->executeQuery(query.str())) { return false; } @@ -793,51 +753,28 @@ bool IOLoginData::savePlayer(Player* player) return false; } - if (player->lastDepotId != -1) { - //save depot items - query.str(std::string()); - query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID(); - - if (!db.executeQuery(query.str())) { - return false; - } - - DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); - itemList.clear(); - - for (const auto& it : player->depotChests) { - DepotChest* depotChest = it.second; - for (Item* item : depotChest->getItemList()) { - itemList.emplace_back(it.first, item); - } - } - - if (!saveItems(player, itemList, depotQuery, propWriteStream)) { - return false; - } - } - - //save inbox items + //save depot items query.str(std::string()); - query << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { + query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID(); + + if (!db->executeQuery(query.str())) { return false; } - DBInsert inboxQuery("INSERT INTO `player_inboxitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); + DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); itemList.clear(); - for (Item* item : player->getInbox()->getItemList()) { - itemList.emplace_back(0, item); + for (const auto& it : player->depotLockerMap) { + itemList.emplace_back(it.first, it.second); } - if (!saveItems(player, itemList, inboxQuery, propWriteStream)) { + if (!saveItems(player, itemList, depotQuery, propWriteStream)) { return false; } query.str(std::string()); query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID(); - if (!db.executeQuery(query.str())) { + if (!db->executeQuery(query.str())) { return false; } @@ -865,7 +802,7 @@ std::string IOLoginData::getNameByGuid(uint32_t guid) { std::ostringstream query; query << "SELECT `name` FROM `players` WHERE `id` = " << guid; - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + DBResult_ptr result = Database::getInstance()->storeQuery(query.str()); if (!result) { return std::string(); } @@ -874,11 +811,11 @@ std::string IOLoginData::getNameByGuid(uint32_t guid) uint32_t IOLoginData::getGuidByName(const std::string& name) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `id` FROM `players` WHERE `name` = " << db.escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); + query << "SELECT `id` FROM `players` WHERE `name` = " << db->escapeString(name); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return 0; } @@ -887,11 +824,11 @@ uint32_t IOLoginData::getGuidByName(const std::string& name) bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string& name) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = " << db.escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); + query << "SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = " << db->escapeString(name); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -913,12 +850,12 @@ bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string& bool IOLoginData::formatPlayerName(std::string& name) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "SELECT `name` FROM `players` WHERE `name` = " << db.escapeString(name); + query << "SELECT `name` FROM `players` WHERE `name` = " << db->escapeString(name); - DBResult_ptr result = db.storeQuery(query.str()); + DBResult_ptr result = db->storeQuery(query.str()); if (!result) { return false; } @@ -957,16 +894,16 @@ void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance) { std::ostringstream query; query << "UPDATE `players` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid; - Database::getInstance().executeQuery(query.str()); + Database::getInstance()->executeQuery(query.str()); } bool IOLoginData::hasBiddedOnHouse(uint32_t guid) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; query << "SELECT `id` FROM `houses` WHERE `highest_bidder` = " << guid << " LIMIT 1"; - return db.storeQuery(query.str()).get() != nullptr; + return db->storeQuery(query.str()).get() != nullptr; } std::forward_list IOLoginData::getVIPEntries(uint32_t accountId) @@ -974,58 +911,46 @@ std::forward_list IOLoginData::getVIPEntries(uint32_t accountId) std::forward_list entries; std::ostringstream query; - query << "SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = " << accountId; + query << "SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name` FROM `account_viplist` WHERE `account_id` = " << accountId; - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); + DBResult_ptr result = Database::getInstance()->storeQuery(query.str()); if (result) { do { entries.emplace_front( result->getNumber("player_id"), - result->getString("name"), - result->getString("description"), - result->getNumber("icon"), - result->getNumber("notify") != 0 + result->getString("name") ); } while (result->next()); } return entries; } -void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify) +void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid) { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; - query << "INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES (" << accountId << ',' << guid << ',' << db.escapeString(description) << ',' << icon << ',' << notify << ')'; - db.executeQuery(query.str()); -} - -void IOLoginData::editVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify) -{ - Database& db = Database::getInstance(); - - std::ostringstream query; - query << "UPDATE `account_viplist` SET `description` = " << db.escapeString(description) << ", `icon` = " << icon << ", `notify` = " << notify << " WHERE `account_id` = " << accountId << " AND `player_id` = " << guid; - db.executeQuery(query.str()); + query << "INSERT INTO `account_viplist` (`account_id`, `player_id`) VALUES (" << accountId << ',' << guid << ')'; + db->executeQuery(query.str()); } void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid) { std::ostringstream query; query << "DELETE FROM `account_viplist` WHERE `account_id` = " << accountId << " AND `player_id` = " << guid; - Database::getInstance().executeQuery(query.str()); + Database::getInstance()->executeQuery(query.str()); } void IOLoginData::addPremiumDays(uint32_t accountId, int32_t addDays) { std::ostringstream query; query << "UPDATE `accounts` SET `premdays` = `premdays` + " << addDays << " WHERE `id` = " << accountId; - Database::getInstance().executeQuery(query.str()); + Database::getInstance()->executeQuery(query.str()); } void IOLoginData::removePremiumDays(uint32_t accountId, int32_t removeDays) { std::ostringstream query; query << "UPDATE `accounts` SET `premdays` = `premdays` - " << removeDays << " WHERE `id` = " << accountId; - Database::getInstance().executeQuery(query.str()); + Database::getInstance()->executeQuery(query.str()); } diff --git a/src/iologindata.h b/src/iologindata.h index 446e361..d207507 100644 --- a/src/iologindata.h +++ b/src/iologindata.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,7 +24,7 @@ #include "player.h" #include "database.h" -using ItemBlockList = std::list>; +typedef std::list> ItemBlockList; class IOLoginData { @@ -32,8 +32,8 @@ class IOLoginData static Account loadAccount(uint32_t accno); static bool saveAccount(const Account& acc); - static bool loginserverAuthentication(const std::string& name, const std::string& password, Account& account); - static uint32_t gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName, std::string& token, uint32_t tokenTime); + static bool loginserverAuthentication(uint32_t accountNumber, const std::string& password, Account& account); + static uint32_t gameworldAuthentication(uint32_t accountNumber, const std::string& password, std::string& characterName); static AccountType_t getAccountType(uint32_t accountId); static void setAccountType(uint32_t accountId, AccountType_t accountType); @@ -52,18 +52,17 @@ class IOLoginData static bool hasBiddedOnHouse(uint32_t guid); static std::forward_list getVIPEntries(uint32_t accountId); - static void addVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify); - static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify); + static void addVIPEntry(uint32_t accountId, uint32_t guid); static void removeVIPEntry(uint32_t accountId, uint32_t guid); static void addPremiumDays(uint32_t accountId, int32_t addDays); static void removePremiumDays(uint32_t accountId, int32_t removeDays); - private: - using ItemMap = std::map>; + protected: + typedef std::map> ItemMap; static void loadItems(ItemMap& itemMap, DBResult_ptr result); - static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream); + static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& stream); }; #endif diff --git a/src/iomap.cpp b/src/iomap.cpp index 9c40761..197bf97 100644 --- a/src/iomap.cpp +++ b/src/iomap.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -66,14 +66,23 @@ Tile* IOMap::createTile(Item*& ground, Item* item, uint16_t x, uint16_t y, uint8 return tile; } -bool IOMap::loadMap(Map* map, const std::string& fileName) +bool IOMap::loadMap(Map* map, const std::string& identifier) { int64_t start = OTSYS_TIME(); - OTB::Loader loader{fileName, OTB::Identifier{{'O', 'T', 'B', 'M'}}}; - auto& root = loader.parseTree(); + FileLoader f; + if (!f.openFile(identifier.c_str(), "OTBM")) { + std::ostringstream ss; + ss << "Could not open the file " << identifier << '.'; + setLastErrorString(ss.str()); + return false; + } + + uint32_t type; PropStream propStream; - if (!loader.getProps(root, propStream)) { + + NODE root = f.getChildNode(nullptr, type); + if (!f.getProps(root, propStream)) { setLastErrorString("Could not read root property."); return false; } @@ -85,7 +94,7 @@ bool IOMap::loadMap(Map* map, const std::string& fileName) } uint32_t headerVersion = root_header.version; - if (headerVersion == 0) { + if (headerVersion <= 0) { //In otbm version 1 the count variable after splashes/fluidcontainers and stackables //are saved as attributes instead, this solves alot of problems with items //that is changed (stackable/charges/fluidcontainer/splash) during an update. @@ -98,66 +107,17 @@ bool IOMap::loadMap(Map* map, const std::string& fileName) return false; } - if (root_header.majorVersionItems < 3) { - setLastErrorString("This map need to be upgraded by using the latest map editor version to be able to load correctly."); - return false; - } - - if (root_header.majorVersionItems > Item::items.majorVersion) { - setLastErrorString("The map was saved with a different items.otb version, an upgraded items.otb is required."); - return false; - } - - if (root_header.minorVersionItems < CLIENT_VERSION_810) { - setLastErrorString("This map needs to be updated."); - return false; - } - - if (root_header.minorVersionItems > Item::items.minorVersion) { - std::cout << "[Warning - IOMap::loadMap] This map needs an updated items.otb." << std::endl; - } - std::cout << "> Map size: " << root_header.width << "x" << root_header.height << '.' << std::endl; map->width = root_header.width; map->height = root_header.height; - if (root.children.size() != 1 || root.children[0].type != OTBM_MAP_DATA) { + NODE nodeMap = f.getChildNode(root, type); + if (type != OTBM_MAP_DATA) { setLastErrorString("Could not read data node."); return false; } - auto& mapNode = root.children[0]; - if (!parseMapDataAttributes(loader, mapNode, *map, fileName)) { - return false; - } - - for (auto& mapDataNode : mapNode.children) { - if (mapDataNode.type == OTBM_TILE_AREA) { - if (!parseTileArea(loader, mapDataNode, *map)) { - return false; - } - } else if (mapDataNode.type == OTBM_TOWNS) { - if (!parseTowns(loader, mapDataNode, *map)) { - return false; - } - } else if (mapDataNode.type == OTBM_WAYPOINTS && headerVersion > 1) { - if (!parseWaypoints(loader, mapDataNode, *map)) { - return false; - } - } else { - setLastErrorString("Unknown map node."); - return false; - } - } - - std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; - return true; -} - -bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName) -{ - PropStream propStream; - if (!loader.getProps(mapNode, propStream)) { + if (!f.getProps(nodeMap, propStream)) { setLastErrorString("Could not read map data attributes."); return false; } @@ -181,8 +141,8 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode return false; } - map.spawnfile = fileName.substr(0, fileName.rfind('/') + 1); - map.spawnfile += tmp; + map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1); + map->spawnfile += tmp; break; case OTBM_ATTR_EXT_HOUSE_FILE: @@ -191,8 +151,8 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode return false; } - map.housefile = fileName.substr(0, fileName.rfind('/') + 1); - map.housefile += tmp; + map->housefile = identifier.substr(0, identifier.rfind('/') + 1); + map->housefile += tmp; break; default: @@ -200,104 +160,172 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode return false; } } - return true; -} -bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map) -{ - PropStream propStream; - if (!loader.getProps(tileAreaNode, propStream)) { - setLastErrorString("Invalid map node."); - return false; - } - - OTBM_Destination_coords area_coord; - if (!propStream.read(area_coord)) { - setLastErrorString("Invalid map node."); - return false; - } - - uint16_t base_x = area_coord.x; - uint16_t base_y = area_coord.y; - uint16_t z = area_coord.z; - - for (auto& tileNode : tileAreaNode.children) { - if (tileNode.type != OTBM_TILE && tileNode.type != OTBM_HOUSETILE) { - setLastErrorString("Unknown tile node."); + NODE nodeMapData = f.getChildNode(nodeMap, type); + while (nodeMapData != NO_NODE) { + if (f.getError() != ERROR_NONE) { + setLastErrorString("Invalid map node."); return false; } - if (!loader.getProps(tileNode, propStream)) { - setLastErrorString("Could not read node data."); - return false; - } - - OTBM_Tile_coords tile_coord; - if (!propStream.read(tile_coord)) { - setLastErrorString("Could not read tile position."); - return false; - } - - uint16_t x = base_x + tile_coord.x; - uint16_t y = base_y + tile_coord.y; - - bool isHouseTile = false; - House* house = nullptr; - Tile* tile = nullptr; - Item* ground_item = nullptr; - uint32_t tileflags = TILESTATE_NONE; - - if (tileNode.type == OTBM_HOUSETILE) { - uint32_t houseId; - if (!propStream.read(houseId)) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not read house id."; - setLastErrorString(ss.str()); + if (type == OTBM_TILE_AREA) { + if (!f.getProps(nodeMapData, propStream)) { + setLastErrorString("Invalid map node."); return false; } - house = map.houses.addHouse(houseId); - if (!house) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId; - setLastErrorString(ss.str()); + OTBM_Destination_coords area_coord; + if (!propStream.read(area_coord)) { + setLastErrorString("Invalid map node."); return false; } - tile = new HouseTile(x, y, z, house); - house->addTile(static_cast(tile)); - isHouseTile = true; - } + uint16_t base_x = area_coord.x; + uint16_t base_y = area_coord.y; + uint16_t z = area_coord.z; - uint8_t attribute; - //read tile attributes - while (propStream.read(attribute)) { - switch (attribute) { - case OTBM_ATTR_TILE_FLAGS: { - uint32_t flags; - if (!propStream.read(flags)) { + NODE nodeTile = f.getChildNode(nodeMapData, type); + while (nodeTile != NO_NODE) { + if (f.getError() != ERROR_NONE) { + setLastErrorString("Could not read node data."); + return false; + } + + if (type != OTBM_TILE && type != OTBM_HOUSETILE) { + setLastErrorString("Unknown tile node."); + return false; + } + + if (!f.getProps(nodeTile, propStream)) { + setLastErrorString("Could not read node data."); + return false; + } + + OTBM_Tile_coords tile_coord; + if (!propStream.read(tile_coord)) { + setLastErrorString("Could not read tile position."); + return false; + } + + uint16_t x = base_x + tile_coord.x; + uint16_t y = base_y + tile_coord.y; + + bool isHouseTile = false; + House* house = nullptr; + Tile* tile = nullptr; + Item* ground_item = nullptr; + uint32_t tileflags = TILESTATE_NONE; + + if (type == OTBM_HOUSETILE) { + uint32_t houseId; + if (!propStream.read(houseId)) { std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags."; + ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not read house id."; setLastErrorString(ss.str()); return false; } - if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) { - tileflags |= TILESTATE_PROTECTIONZONE; - } else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) { - tileflags |= TILESTATE_NOPVPZONE; - } else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) { - tileflags |= TILESTATE_PVPZONE; + house = map->houses.addHouse(houseId); + if (!house) { + std::ostringstream ss; + ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId; + setLastErrorString(ss.str()); + return false; } - if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) { - tileflags |= TILESTATE_NOLOGOUT; - } - break; + tile = new HouseTile(x, y, z, house); + house->addTile(static_cast(tile)); + isHouseTile = true; } - case OTBM_ATTR_ITEM: { - Item* item = Item::CreateItem(propStream); + //read tile attributes + while (propStream.read(attribute)) { + switch (attribute) { + case OTBM_ATTR_TILE_FLAGS: { + uint32_t flags; + if (!propStream.read(flags)) { + std::ostringstream ss; + ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags."; + setLastErrorString(ss.str()); + return false; + } + + if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) { + tileflags |= TILESTATE_PROTECTIONZONE; + } else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) { + tileflags |= TILESTATE_NOPVPZONE; + } else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) { + tileflags |= TILESTATE_PVPZONE; + } + + if ((flags & OTBM_TILEFLAG_REFRESH) != 0) { + tileflags |= TILESTATE_REFRESH; + } + + if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) { + tileflags |= TILESTATE_NOLOGOUT; + } + break; + } + + case OTBM_ATTR_ITEM: { + Item* item = Item::CreateItem(propStream); + if (!item) { + std::ostringstream ss; + ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; + setLastErrorString(ss.str()); + return false; + } + + if (isHouseTile && item->isMoveable()) { + //std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl; + delete item; + } else { + if (item->getItemCount() <= 0) { + item->setItemCount(1); + } + + if (tile) { + tile->internalAddThing(item); + item->startDecaying(); + item->setLoadedFromMap(true); + } else if (item->isGroundTile()) { + delete ground_item; + ground_item = item; + } else { + tile = createTile(ground_item, item, x, y, z); + tile->internalAddThing(item); + item->startDecaying(); + item->setLoadedFromMap(true); + } + } + break; + } + + default: + std::ostringstream ss; + ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute."; + setLastErrorString(ss.str()); + return false; + } + } + + NODE nodeItem = f.getChildNode(nodeTile, type); + while (nodeItem) { + if (type != OTBM_ITEM) { + std::ostringstream ss; + ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown node type."; + setLastErrorString(ss.str()); + return false; + } + + PropStream stream; + if (!f.getProps(nodeItem, stream)) { + setLastErrorString("Invalid item node."); + return false; + } + + Item* item = Item::CreateItem(stream); if (!item) { std::ostringstream ss; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; @@ -305,11 +333,19 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma return false; } + if (!item->unserializeItemNode(f, nodeItem, stream)) { + std::ostringstream ss; + ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to load item " << item->getID() << '.'; + setLastErrorString(ss.str()); + delete item; + return false; + } + if (isHouseTile && item->isMoveable()) { - std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl; + //std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl; delete item; } else { - if (item->getItemCount() == 0) { + if (item->getItemCount() <= 0) { item->setItemCount(1); } @@ -327,156 +363,100 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma item->setLoadedFromMap(true); } } - break; + + nodeItem = f.getNextNode(nodeItem, type); } - default: - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute."; - setLastErrorString(ss.str()); + if (!tile) { + tile = createTile(ground_item, nullptr, x, y, z); + } + + tile->setFlag(static_cast(tileflags)); + + map->setTile(x, y, z, tile); + + nodeTile = f.getNextNode(nodeTile, type); + } + } else if (type == OTBM_TOWNS) { + NODE nodeTown = f.getChildNode(nodeMapData, type); + while (nodeTown != NO_NODE) { + if (type != OTBM_TOWN) { + setLastErrorString("Unknown town node."); return false; - } - } - - for (auto& itemNode : tileNode.children) { - if (itemNode.type != OTBM_ITEM) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown node type."; - setLastErrorString(ss.str()); - return false; - } - - PropStream stream; - if (!loader.getProps(itemNode, stream)) { - setLastErrorString("Invalid item node."); - return false; - } - - Item* item = Item::CreateItem(stream); - if (!item) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; - setLastErrorString(ss.str()); - return false; - } - - if (!item->unserializeItemNode(loader, itemNode, stream)) { - std::ostringstream ss; - ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to load item " << item->getID() << '.'; - setLastErrorString(ss.str()); - delete item; - return false; - } - - if (isHouseTile && item->isMoveable()) { - std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl; - delete item; - } else { - if (item->getItemCount() == 0) { - item->setItemCount(1); } - if (tile) { - tile->internalAddThing(item); - item->startDecaying(); - item->setLoadedFromMap(true); - } else if (item->isGroundTile()) { - delete ground_item; - ground_item = item; - } else { - tile = createTile(ground_item, item, x, y, z); - tile->internalAddThing(item); - item->startDecaying(); - item->setLoadedFromMap(true); + if (!f.getProps(nodeTown, propStream)) { + setLastErrorString("Could not read town data."); + return false; } + + uint32_t townId; + if (!propStream.read(townId)) { + setLastErrorString("Could not read town id."); + return false; + } + + Town* town = map->towns.getTown(townId); + if (!town) { + town = new Town(townId); + map->towns.addTown(townId, town); + } + + std::string townName; + if (!propStream.readString(townName)) { + setLastErrorString("Could not read town name."); + return false; + } + + town->setName(townName); + + OTBM_Destination_coords town_coords; + if (!propStream.read(town_coords)) { + setLastErrorString("Could not read town coordinates."); + return false; + } + + town->setTemplePos(Position(town_coords.x, town_coords.y, town_coords.z)); + + nodeTown = f.getNextNode(nodeTown, type); } + } else if (type == OTBM_WAYPOINTS) { + NODE nodeWaypoint = f.getChildNode(nodeMapData, type); + while (nodeWaypoint != NO_NODE) { + if (type != OTBM_WAYPOINT) { + setLastErrorString("Unknown waypoint node."); + return false; + } + + if (!f.getProps(nodeWaypoint, propStream)) { + setLastErrorString("Could not read waypoint data."); + return false; + } + + std::string name; + if (!propStream.readString(name)) { + setLastErrorString("Could not read waypoint name."); + return false; + } + + OTBM_Destination_coords waypoint_coords; + if (!propStream.read(waypoint_coords)) { + setLastErrorString("Could not read waypoint coordinates."); + return false; + } + + map->waypoints[name] = Position(waypoint_coords.x, waypoint_coords.y, waypoint_coords.z); + + nodeWaypoint = f.getNextNode(nodeWaypoint, type); + } + } else { + setLastErrorString("Unknown map node."); + return false; } - if (!tile) { - tile = createTile(ground_item, nullptr, x, y, z); - } - - tile->setFlag(static_cast(tileflags)); - - map.setTile(x, y, z, tile); + nodeMapData = f.getNextNode(nodeMapData, type); } + + std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; return true; } - -bool IOMap::parseTowns(OTB::Loader& loader, const OTB::Node& townsNode, Map& map) -{ - for (auto& townNode : townsNode.children) { - PropStream propStream; - if (townNode.type != OTBM_TOWN) { - setLastErrorString("Unknown town node."); - return false; - } - - if (!loader.getProps(townNode, propStream)) { - setLastErrorString("Could not read town data."); - return false; - } - - uint32_t townId; - if (!propStream.read(townId)) { - setLastErrorString("Could not read town id."); - return false; - } - - Town* town = map.towns.getTown(townId); - if (!town) { - town = new Town(townId); - map.towns.addTown(townId, town); - } - - std::string townName; - if (!propStream.readString(townName)) { - setLastErrorString("Could not read town name."); - return false; - } - - town->setName(townName); - - OTBM_Destination_coords town_coords; - if (!propStream.read(town_coords)) { - setLastErrorString("Could not read town coordinates."); - return false; - } - - town->setTemplePos(Position(town_coords.x, town_coords.y, town_coords.z)); - } - return true; -} - - -bool IOMap::parseWaypoints(OTB::Loader& loader, const OTB::Node& waypointsNode, Map& map) -{ - PropStream propStream; - for (auto& node : waypointsNode.children) { - if (node.type != OTBM_WAYPOINT) { - setLastErrorString("Unknown waypoint node."); - return false; - } - - if (!loader.getProps(node, propStream)) { - setLastErrorString("Could not read waypoint data."); - return false; - } - - std::string name; - if (!propStream.readString(name)) { - setLastErrorString("Could not read waypoint name."); - return false; - } - - OTBM_Destination_coords waypoint_coords; - if (!propStream.read(waypoint_coords)) { - setLastErrorString("Could not read waypoint coordinates."); - return false; - } - - map.waypoints[name] = Position(waypoint_coords.x, waypoint_coords.y, waypoint_coords.z); - } - return true; -} - diff --git a/src/iomap.h b/src/iomap.h index a00d8a4..5062e98 100644 --- a/src/iomap.h +++ b/src/iomap.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,7 +33,7 @@ enum OTBM_AttrTypes_t { OTBM_ATTR_EXT_FILE = 2, OTBM_ATTR_TILE_FLAGS = 3, OTBM_ATTR_ACTION_ID = 4, - OTBM_ATTR_UNIQUE_ID = 5, + OTBM_ATTR_MOVEMENT_ID = 5, OTBM_ATTR_TEXT = 6, OTBM_ATTR_DESC = 7, OTBM_ATTR_TELE_DEST = 8, @@ -51,6 +51,12 @@ enum OTBM_AttrTypes_t { OTBM_ATTR_SLEEPERGUID = 20, OTBM_ATTR_SLEEPSTART = 21, OTBM_ATTR_CHARGES = 22, + OTBM_ATTR_KEYNUMBER = 23, + OTBM_ATTR_KEYHOLENUMBER = 24, + OTBM_ATTR_DOORQUESTNUMBER = 25, + OTBM_ATTR_DOORQUESTVALUE = 26, + OTBM_ATTR_DOORLEVEL = 27, + OTBM_ATTR_CHESTQUESTNUMBER = 28, }; enum OTBM_NodeTypes_t { @@ -76,7 +82,8 @@ enum OTBM_TileFlag_t : uint32_t { OTBM_TILEFLAG_PROTECTIONZONE = 1 << 0, OTBM_TILEFLAG_NOPVPZONE = 1 << 2, OTBM_TILEFLAG_NOLOGOUT = 1 << 3, - OTBM_TILEFLAG_PVPZONE = 1 << 4 + OTBM_TILEFLAG_PVPZONE = 1 << 4, + OTBM_TILEFLAG_REFRESH = 1 << 5, }; #pragma pack(1) @@ -107,7 +114,7 @@ class IOMap static Tile* createTile(Item*& ground, Item* item, uint16_t x, uint16_t y, uint8_t z); public: - bool loadMap(Map* map, const std::string& fileName); + bool loadMap(Map* map, const std::string& identifier); /* Load the spawns * \param map pointer to the Map class @@ -147,11 +154,7 @@ class IOMap errorString = error; } - private: - bool parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName); - bool parseWaypoints(OTB::Loader& loader, const OTB::Node& waypointsNode, Map& map); - bool parseTowns(OTB::Loader& loader, const OTB::Node& townsNode, Map& map); - bool parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map); + protected: std::string errorString; }; diff --git a/src/iomapserialize.cpp b/src/iomapserialize.cpp index af606c2..a6a5b9d 100644 --- a/src/iomapserialize.cpp +++ b/src/iomapserialize.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,7 +29,7 @@ void IOMapSerialize::loadHouseItems(Map* map) { int64_t start = OTSYS_TIME(); - DBResult_ptr result = Database::getInstance().storeQuery("SELECT `data` FROM `tile_store`"); + DBResult_ptr result = Database::getInstance()->storeQuery("SELECT `data` FROM `tile_store`"); if (!result) { return; } @@ -67,7 +67,7 @@ void IOMapSerialize::loadHouseItems(Map* map) bool IOMapSerialize::saveHouseItems() { int64_t start = OTSYS_TIME(); - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); std::ostringstream query; //Start the transaction @@ -77,7 +77,7 @@ bool IOMapSerialize::saveHouseItems() } //clear old tile data - if (!db.executeQuery("DELETE FROM `tile_store`")) { + if (!db->executeQuery("DELETE FROM `tile_store`")) { return false; } @@ -93,7 +93,7 @@ bool IOMapSerialize::saveHouseItems() size_t attributesSize; const char* attributes = stream.getStream(attributesSize); if (attributesSize > 0) { - query << house->getId() << ',' << db.escapeBlob(attributes, attributesSize); + query << house->getId() << ',' << db->escapeBlob(attributes, attributesSize); if (!stmt.addRow(query)) { return false; } @@ -144,7 +144,7 @@ bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent) } const ItemType& iType = Item::items[id]; - if (iType.moveable || iType.forceSerialize || !tile) { + if (iType.moveable || !tile) { //create a new item Item* item = Item::CreateItem(id); if (item) { @@ -247,7 +247,7 @@ void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile) const ItemType& it = Item::items[item->getID()]; // Note that these are NEGATED, ie. these are the items that will be saved. - if (!(it.moveable || it.forceSerialize || item->getDoor() || (item->getContainer() && !item->getContainer()->empty()) || it.canWriteText || item->getBed())) { + if (!(it.moveable || item->getDoor() || (item->getContainer() && !item->getContainer()->empty()) || it.canWriteText || item->getBed())) { continue; } @@ -270,9 +270,9 @@ void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile) bool IOMapSerialize::loadHouseInfo() { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); - DBResult_ptr result = db.storeQuery("SELECT `id`, `owner`, `paid`, `warnings` FROM `houses`"); + DBResult_ptr result = db->storeQuery("SELECT `id`, `owner`, `paid`, `warnings` FROM `houses`"); if (!result) { return false; } @@ -286,7 +286,7 @@ bool IOMapSerialize::loadHouseInfo() } } while (result->next()); - result = db.storeQuery("SELECT `house_id`, `listid`, `list` FROM `house_lists`"); + result = db->storeQuery("SELECT `house_id`, `listid`, `list` FROM `house_lists`"); if (result) { do { House* house = g_game.map.houses.getHouse(result->getNumber("house_id")); @@ -300,14 +300,14 @@ bool IOMapSerialize::loadHouseInfo() bool IOMapSerialize::saveHouseInfo() { - Database& db = Database::getInstance(); + Database* db = Database::getInstance(); DBTransaction transaction; if (!transaction.begin()) { return false; } - if (!db.executeQuery("DELETE FROM `house_lists`")) { + if (!db->executeQuery("DELETE FROM `house_lists`")) { return false; } @@ -315,16 +315,16 @@ bool IOMapSerialize::saveHouseInfo() for (const auto& it : g_game.map.houses.getHouses()) { House* house = it.second; query << "SELECT `id` FROM `houses` WHERE `id` = " << house->getId(); - DBResult_ptr result = db.storeQuery(query.str()); + DBResult_ptr result = db->storeQuery(query.str()); if (result) { query.str(std::string()); - query << "UPDATE `houses` SET `owner` = " << house->getOwner() << ", `paid` = " << house->getPaidUntil() << ", `warnings` = " << house->getPayRentWarnings() << ", `name` = " << db.escapeString(house->getName()) << ", `town_id` = " << house->getTownId() << ", `rent` = " << house->getRent() << ", `size` = " << house->getTiles().size() << ", `beds` = " << house->getBedCount() << " WHERE `id` = " << house->getId(); + query << "UPDATE `houses` SET `owner` = " << house->getOwner() << ", `paid` = " << house->getPaidUntil() << ", `warnings` = " << house->getPayRentWarnings() << ", `name` = " << db->escapeString(house->getName()) << ", `town_id` = " << house->getTownId() << ", `rent` = " << house->getRent() << ", `size` = " << house->getTiles().size() << ", `beds` = " << house->getBedCount() << " WHERE `id` = " << house->getId(); } else { query.str(std::string()); - query << "INSERT INTO `houses` (`id`, `owner`, `paid`, `warnings`, `name`, `town_id`, `rent`, `size`, `beds`) VALUES (" << house->getId() << ',' << house->getOwner() << ',' << house->getPaidUntil() << ',' << house->getPayRentWarnings() << ',' << db.escapeString(house->getName()) << ',' << house->getTownId() << ',' << house->getRent() << ',' << house->getTiles().size() << ',' << house->getBedCount() << ')'; + query << "INSERT INTO `houses` (`id`, `owner`, `paid`, `warnings`, `name`, `town_id`, `rent`, `size`, `beds`) VALUES (" << house->getId() << ',' << house->getOwner() << ',' << house->getPaidUntil() << ',' << house->getPayRentWarnings() << ',' << db->escapeString(house->getName()) << ',' << house->getTownId() << ',' << house->getRent() << ',' << house->getTiles().size() << ',' << house->getBedCount() << ')'; } - db.executeQuery(query.str()); + db->executeQuery(query.str()); query.str(std::string()); } @@ -335,7 +335,7 @@ bool IOMapSerialize::saveHouseInfo() std::string listText; if (house->getAccessList(GUEST_LIST, listText) && !listText.empty()) { - query << house->getId() << ',' << GUEST_LIST << ',' << db.escapeString(listText); + query << house->getId() << ',' << GUEST_LIST << ',' << db->escapeString(listText); if (!stmt.addRow(query)) { return false; } @@ -344,7 +344,7 @@ bool IOMapSerialize::saveHouseInfo() } if (house->getAccessList(SUBOWNER_LIST, listText) && !listText.empty()) { - query << house->getId() << ',' << SUBOWNER_LIST << ',' << db.escapeString(listText); + query << house->getId() << ',' << SUBOWNER_LIST << ',' << db->escapeString(listText); if (!stmt.addRow(query)) { return false; } @@ -354,7 +354,7 @@ bool IOMapSerialize::saveHouseInfo() for (Door* door : house->getDoors()) { if (door->getAccessList(listText) && !listText.empty()) { - query << house->getId() << ',' << door->getDoorId() << ',' << db.escapeString(listText); + query << house->getId() << ',' << door->getDoorId() << ',' << db->escapeString(listText); if (!stmt.addRow(query)) { return false; } diff --git a/src/iomapserialize.h b/src/iomapserialize.h index 9840f24..1a87c28 100644 --- a/src/iomapserialize.h +++ b/src/iomapserialize.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ class IOMapSerialize static bool loadHouseInfo(); static bool saveHouseInfo(); - private: + protected: static void saveItem(PropWriteStream& stream, const Item* item); static void saveTile(PropWriteStream& stream, const Tile* tile); diff --git a/src/iomarket.cpp b/src/iomarket.cpp deleted file mode 100644 index 7a651df..0000000 --- a/src/iomarket.cpp +++ /dev/null @@ -1,347 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "iomarket.h" - -#include "configmanager.h" -#include "databasetasks.h" -#include "iologindata.h" -#include "game.h" -#include "scheduler.h" - -extern ConfigManager g_config; -extern Game g_game; - -MarketOfferList IOMarket::getActiveOffers(MarketAction_t action, uint16_t itemId) -{ - MarketOfferList offerList; - - std::ostringstream query; - query << "SELECT `id`, `amount`, `price`, `created`, `anonymous`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `player_name` FROM `market_offers` WHERE `sale` = " << action << " AND `itemtype` = " << itemId; - - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); - if (!result) { - return offerList; - } - - const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); - - do { - MarketOffer offer; - offer.amount = result->getNumber("amount"); - offer.price = result->getNumber("price"); - offer.timestamp = result->getNumber("created") + marketOfferDuration; - offer.counter = result->getNumber("id") & 0xFFFF; - if (result->getNumber("anonymous") == 0) { - offer.playerName = result->getString("player_name"); - } else { - offer.playerName = "Anonymous"; - } - offerList.push_back(offer); - } while (result->next()); - return offerList; -} - -MarketOfferList IOMarket::getOwnOffers(MarketAction_t action, uint32_t playerId) -{ - MarketOfferList offerList; - - const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); - - std::ostringstream query; - query << "SELECT `id`, `amount`, `price`, `created`, `itemtype` FROM `market_offers` WHERE `player_id` = " << playerId << " AND `sale` = " << action; - - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); - if (!result) { - return offerList; - } - - do { - MarketOffer offer; - offer.amount = result->getNumber("amount"); - offer.price = result->getNumber("price"); - offer.timestamp = result->getNumber("created") + marketOfferDuration; - offer.counter = result->getNumber("id") & 0xFFFF; - offer.itemId = result->getNumber("itemtype"); - offerList.push_back(offer); - } while (result->next()); - return offerList; -} - -HistoryMarketOfferList IOMarket::getOwnHistory(MarketAction_t action, uint32_t playerId) -{ - HistoryMarketOfferList offerList; - - std::ostringstream query; - query << "SELECT `itemtype`, `amount`, `price`, `expires_at`, `state` FROM `market_history` WHERE `player_id` = " << playerId << " AND `sale` = " << action; - - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); - if (!result) { - return offerList; - } - - do { - HistoryMarketOffer offer; - offer.itemId = result->getNumber("itemtype"); - offer.amount = result->getNumber("amount"); - offer.price = result->getNumber("price"); - offer.timestamp = result->getNumber("expires_at"); - - MarketOfferState_t offerState = static_cast(result->getNumber("state")); - if (offerState == OFFERSTATE_ACCEPTEDEX) { - offerState = OFFERSTATE_ACCEPTED; - } - - offer.state = offerState; - - offerList.push_back(offer); - } while (result->next()); - return offerList; -} - -void IOMarket::processExpiredOffers(DBResult_ptr result, bool) -{ - if (!result) { - return; - } - - do { - if (!IOMarket::moveOfferToHistory(result->getNumber("id"), OFFERSTATE_EXPIRED)) { - continue; - } - - const uint32_t playerId = result->getNumber("player_id"); - const uint16_t amount = result->getNumber("amount"); - if (result->getNumber("sale") == 1) { - const ItemType& itemType = Item::items[result->getNumber("itemtype")]; - if (itemType.id == 0) { - continue; - } - - Player* player = g_game.getPlayerByGUID(playerId); - if (!player) { - player = new Player(nullptr); - if (!IOLoginData::loadPlayerById(player, playerId)) { - delete player; - continue; - } - } - - if (itemType.stackable) { - uint16_t tmpAmount = amount; - while (tmpAmount > 0) { - uint16_t stackCount = std::min(100, tmpAmount); - Item* item = Item::CreateItem(itemType.id, stackCount); - if (g_game.internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - - tmpAmount -= stackCount; - } - } else { - int32_t subType; - if (itemType.charges != 0) { - subType = itemType.charges; - } else { - subType = -1; - } - - for (uint16_t i = 0; i < amount; ++i) { - Item* item = Item::CreateItem(itemType.id, subType); - if (g_game.internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) { - delete item; - break; - } - } - } - - if (player->isOffline()) { - IOLoginData::savePlayer(player); - delete player; - } - } else { - uint64_t totalPrice = result->getNumber("price") * amount; - - Player* player = g_game.getPlayerByGUID(playerId); - if (player) { - player->setBankBalance(player->getBankBalance() + totalPrice); - } else { - IOLoginData::increaseBankBalance(playerId, totalPrice); - } - } - } while (result->next()); -} - -void IOMarket::checkExpiredOffers() -{ - const time_t lastExpireDate = time(nullptr) - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); - - std::ostringstream query; - query << "SELECT `id`, `amount`, `price`, `itemtype`, `player_id`, `sale` FROM `market_offers` WHERE `created` <= " << lastExpireDate; - g_databaseTasks.addTask(query.str(), IOMarket::processExpiredOffers, true); - - int32_t checkExpiredMarketOffersEachMinutes = g_config.getNumber(ConfigManager::CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES); - if (checkExpiredMarketOffersEachMinutes <= 0) { - return; - } - - g_scheduler.addEvent(createSchedulerTask(checkExpiredMarketOffersEachMinutes * 60 * 1000, IOMarket::checkExpiredOffers)); -} - -uint32_t IOMarket::getPlayerOfferCount(uint32_t playerId) -{ - std::ostringstream query; - query << "SELECT COUNT(*) AS `count` FROM `market_offers` WHERE `player_id` = " << playerId; - - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); - if (!result) { - return 0; - } - return result->getNumber("count"); -} - -MarketOfferEx IOMarket::getOfferByCounter(uint32_t timestamp, uint16_t counter) -{ - MarketOfferEx offer; - - const int32_t created = timestamp - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); - - std::ostringstream query; - query << "SELECT `id`, `sale`, `itemtype`, `amount`, `created`, `price`, `player_id`, `anonymous`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `player_name` FROM `market_offers` WHERE `created` = " << created << " AND (`id` & 65535) = " << counter << " LIMIT 1"; - - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); - if (!result) { - offer.id = 0; - return offer; - } - - offer.id = result->getNumber("id"); - offer.type = static_cast(result->getNumber("sale")); - offer.amount = result->getNumber("amount"); - offer.counter = result->getNumber("id") & 0xFFFF; - offer.timestamp = result->getNumber("created"); - offer.price = result->getNumber("price"); - offer.itemId = result->getNumber("itemtype"); - offer.playerId = result->getNumber("player_id"); - if (result->getNumber("anonymous") == 0) { - offer.playerName = result->getString("player_name"); - } else { - offer.playerName = "Anonymous"; - } - return offer; -} - -void IOMarket::createOffer(uint32_t playerId, MarketAction_t action, uint32_t itemId, uint16_t amount, uint32_t price, bool anonymous) -{ - std::ostringstream query; - query << "INSERT INTO `market_offers` (`player_id`, `sale`, `itemtype`, `amount`, `price`, `created`, `anonymous`) VALUES (" << playerId << ',' << action << ',' << itemId << ',' << amount << ',' << price << ',' << time(nullptr) << ',' << anonymous << ')'; - Database::getInstance().executeQuery(query.str()); -} - -void IOMarket::acceptOffer(uint32_t offerId, uint16_t amount) -{ - std::ostringstream query; - query << "UPDATE `market_offers` SET `amount` = `amount` - " << amount << " WHERE `id` = " << offerId; - Database::getInstance().executeQuery(query.str()); -} - -void IOMarket::deleteOffer(uint32_t offerId) -{ - std::ostringstream query; - query << "DELETE FROM `market_offers` WHERE `id` = " << offerId; - Database::getInstance().executeQuery(query.str()); -} - -void IOMarket::appendHistory(uint32_t playerId, MarketAction_t type, uint16_t itemId, uint16_t amount, uint32_t price, time_t timestamp, MarketOfferState_t state) -{ - std::ostringstream query; - query << "INSERT INTO `market_history` (`player_id`, `sale`, `itemtype`, `amount`, `price`, `expires_at`, `inserted`, `state`) VALUES (" - << playerId << ',' << type << ',' << itemId << ',' << amount << ',' << price << ',' - << timestamp << ',' << time(nullptr) << ',' << state << ')'; - g_databaseTasks.addTask(query.str()); -} - -bool IOMarket::moveOfferToHistory(uint32_t offerId, MarketOfferState_t state) -{ - const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION); - - Database& db = Database::getInstance(); - - std::ostringstream query; - query << "SELECT `player_id`, `sale`, `itemtype`, `amount`, `price`, `created` FROM `market_offers` WHERE `id` = " << offerId; - - DBResult_ptr result = db.storeQuery(query.str()); - if (!result) { - return false; - } - - query.str(std::string()); - query << "DELETE FROM `market_offers` WHERE `id` = " << offerId; - if (!db.executeQuery(query.str())) { - return false; - } - - appendHistory(result->getNumber("player_id"), static_cast(result->getNumber("sale")), result->getNumber("itemtype"), result->getNumber("amount"), result->getNumber("price"), result->getNumber("created") + marketOfferDuration, state); - return true; -} - -void IOMarket::updateStatistics() -{ - std::ostringstream query; - query << "SELECT `sale` AS `sale`, `itemtype` AS `itemtype`, COUNT(`price`) AS `num`, MIN(`price`) AS `min`, MAX(`price`) AS `max`, SUM(`price`) AS `sum` FROM `market_history` WHERE `state` = " << OFFERSTATE_ACCEPTED << " GROUP BY `itemtype`, `sale`"; - DBResult_ptr result = Database::getInstance().storeQuery(query.str()); - if (!result) { - return; - } - - do { - MarketStatistics* statistics; - if (result->getNumber("sale") == MARKETACTION_BUY) { - statistics = &purchaseStatistics[result->getNumber("itemtype")]; - } else { - statistics = &saleStatistics[result->getNumber("itemtype")]; - } - - statistics->numTransactions = result->getNumber("num"); - statistics->lowestPrice = result->getNumber("min"); - statistics->totalPrice = result->getNumber("sum"); - statistics->highestPrice = result->getNumber("max"); - } while (result->next()); -} - -MarketStatistics* IOMarket::getPurchaseStatistics(uint16_t itemId) -{ - auto it = purchaseStatistics.find(itemId); - if (it == purchaseStatistics.end()) { - return nullptr; - } - return &it->second; -} - -MarketStatistics* IOMarket::getSaleStatistics(uint16_t itemId) -{ - auto it = saleStatistics.find(itemId); - if (it == saleStatistics.end()) { - return nullptr; - } - return &it->second; -} diff --git a/src/iomarket.h b/src/iomarket.h deleted file mode 100644 index 3491e6f..0000000 --- a/src/iomarket.h +++ /dev/null @@ -1,63 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_IOMARKET_H_B981E52C218C42D3B9EF726EBF0E92C9 -#define FS_IOMARKET_H_B981E52C218C42D3B9EF726EBF0E92C9 - -#include "enums.h" -#include "database.h" - -class IOMarket -{ - public: - static IOMarket& getInstance() { - static IOMarket instance; - return instance; - } - - static MarketOfferList getActiveOffers(MarketAction_t action, uint16_t itemId); - static MarketOfferList getOwnOffers(MarketAction_t action, uint32_t playerId); - static HistoryMarketOfferList getOwnHistory(MarketAction_t action, uint32_t playerId); - - static void processExpiredOffers(DBResult_ptr result, bool); - static void checkExpiredOffers(); - - static uint32_t getPlayerOfferCount(uint32_t playerId); - static MarketOfferEx getOfferByCounter(uint32_t timestamp, uint16_t counter); - - static void createOffer(uint32_t playerId, MarketAction_t action, uint32_t itemId, uint16_t amount, uint32_t price, bool anonymous); - static void acceptOffer(uint32_t offerId, uint16_t amount); - static void deleteOffer(uint32_t offerId); - - static void appendHistory(uint32_t playerId, MarketAction_t type, uint16_t itemId, uint16_t amount, uint32_t price, time_t timestamp, MarketOfferState_t state); - static bool moveOfferToHistory(uint32_t offerId, MarketOfferState_t state); - - void updateStatistics(); - - MarketStatistics* getPurchaseStatistics(uint16_t itemId); - MarketStatistics* getSaleStatistics(uint16_t itemId); - - private: - IOMarket() = default; - - std::map purchaseStatistics; - std::map saleStatistics; -}; - -#endif diff --git a/src/item.cpp b/src/item.cpp index 69fa85c..acf2f1b 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ #include "item.h" #include "container.h" #include "teleport.h" -#include "trashholder.h" #include "mailbox.h" #include "house.h" #include "game.h" @@ -53,7 +52,7 @@ Item* Item::CreateItem(const uint16_t type, uint16_t count /*= 0*/) if (it.id != 0) { if (it.isDepot()) { newItem = new DepotLocker(type); - } else if (it.isContainer()) { + } else if (it.isContainer() || it.isChest()) { newItem = new Container(type); } else if (it.isTeleport()) { newItem = new Teleport(type); @@ -61,24 +60,10 @@ Item* Item::CreateItem(const uint16_t type, uint16_t count /*= 0*/) newItem = new MagicField(type); } else if (it.isDoor()) { newItem = new Door(type); - } else if (it.isTrashHolder()) { - newItem = new TrashHolder(type); } else if (it.isMailbox()) { newItem = new Mailbox(type); } else if (it.isBed()) { newItem = new BedItem(type); - } else if (it.id >= 2210 && it.id <= 2212) { - newItem = new Item(type - 3, count); - } else if (it.id == 2215 || it.id == 2216) { - newItem = new Item(type - 2, count); - } else if (it.id >= 2202 && it.id <= 2206) { - newItem = new Item(type - 37, count); - } else if (it.id == 2640) { - newItem = new Item(6132, count); - } else if (it.id == 6301) { - newItem = new Item(6300, count); - } else if (it.id == 18528) { - newItem = new Item(18408, count); } else { newItem = new Item(type, count); } @@ -163,6 +148,8 @@ Item::Item(const uint16_t type, uint16_t count /*= 0*/) : } else { setCharges(it.charges); } + } else if (it.isKey()) { + setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, count); } setDefaultDuration(); @@ -181,11 +168,6 @@ Item* Item::clone() const Item* item = Item::CreateItem(id, count); if (attributes) { item->attributes.reset(new ItemAttributes(*attributes)); - if (item->getDuration() > 0) { - item->incrementReferenceCounter(); - item->setDecaying(DECAYING_TRUE); - g_game.toDecayItems.push_front(item); - } } return item; } @@ -243,10 +225,6 @@ void Item::setDefaultSubtype() void Item::onRemoved() { ScriptEnvironment::removeTempItem(this); - - if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { - g_game.removeUniqueItem(getUniqueId()); - } } void Item::setID(uint16_t newid) @@ -392,13 +370,13 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } - case ATTR_UNIQUE_ID: { - uint16_t uniqueId; - if (!propStream.read(uniqueId)) { + case ATTR_MOVEMENT_ID: { + uint16_t movementId; + if (!propStream.read(movementId)) { return ATTR_READ_ERROR; } - setUniqueId(uniqueId); + setMovementID(movementId); break; } @@ -534,16 +512,6 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } - case ATTR_EXTRADEFENSE: { - int32_t extraDefense; - if (!propStream.read(extraDefense)) { - return ATTR_READ_ERROR; - } - - setIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE, extraDefense); - break; - } - case ATTR_ARMOR: { int32_t armor; if (!propStream.read(armor)) { @@ -554,16 +522,6 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } - case ATTR_HITCHANCE: { - int8_t hitChance; - if (!propStream.read(hitChance)) { - return ATTR_READ_ERROR; - } - - setIntAttr(ITEM_ATTRIBUTE_HITCHANCE, hitChance); - break; - } - case ATTR_SHOOTRANGE: { uint8_t shootRange; if (!propStream.read(shootRange)) { @@ -574,13 +532,68 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } - case ATTR_DECAYTO: { - int32_t decayTo; - if (!propStream.read(decayTo)) { + case ATTR_KEYNUMBER: { + uint16_t keyNumber; + if (!propStream.read(keyNumber)) { return ATTR_READ_ERROR; } - setIntAttr(ITEM_ATTRIBUTE_DECAYTO, decayTo); + setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, keyNumber); + break; + } + + case ATTR_KEYHOLENUMBER: + { + uint16_t keyHoleNumber; + if (!propStream.read(keyHoleNumber)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER, keyHoleNumber); + break; + } + + case ATTR_DOORLEVEL: + { + uint16_t doorLevel; + if (!propStream.read(doorLevel)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_DOORLEVEL, doorLevel); + break; + } + + case ATTR_DOORQUESTNUMBER: + { + uint16_t doorQuestNumber; + if (!propStream.read(doorQuestNumber)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER, doorQuestNumber); + break; + } + + case ATTR_DOORQUESTVALUE: + { + uint16_t doorQuestValue; + if (!propStream.read(doorQuestValue)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE, doorQuestValue); + break; + } + + case ATTR_CHESTQUESTNUMBER: + { + uint16_t chestQuestNumber; + if (!propStream.read(chestQuestNumber)) { + return ATTR_READ_ERROR; + } + + setIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER, chestQuestNumber); break; } @@ -632,30 +645,6 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) return ATTR_READ_ERROR; } - case ATTR_CUSTOM_ATTRIBUTES: { - uint64_t size; - if (!propStream.read(size)) { - return ATTR_READ_ERROR; - } - - for (uint64_t i = 0; i < size; i++) { - // Unserialize key type and value - std::string key; - if (!propStream.readString(key)) { - return ATTR_READ_ERROR; - }; - - // Unserialize value type and value - ItemAttributes::CustomAttribute val; - if (!val.unserialize(propStream)) { - return ATTR_READ_ERROR; - } - - setCustomAttribute(key, val); - } - break; - } - default: return ATTR_READ_ERROR; } @@ -677,7 +666,7 @@ bool Item::unserializeAttr(PropStream& propStream) return true; } -bool Item::unserializeItemNode(OTB::Loader&, const OTB::Node&, PropStream& propStream) +bool Item::unserializeItemNode(FileLoader&, NODE, PropStream& propStream) { return unserializeAttr(propStream); } @@ -769,42 +758,44 @@ void Item::serializeAttr(PropWriteStream& propWriteStream) const propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_DEFENSE)); } - if (hasAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE)) { - propWriteStream.write(ATTR_EXTRADEFENSE); - propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE)); - } - if (hasAttribute(ITEM_ATTRIBUTE_ARMOR)) { propWriteStream.write(ATTR_ARMOR); propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_ARMOR)); } - if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) { - propWriteStream.write(ATTR_HITCHANCE); - propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_HITCHANCE)); - } - if (hasAttribute(ITEM_ATTRIBUTE_SHOOTRANGE)) { propWriteStream.write(ATTR_SHOOTRANGE); propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_SHOOTRANGE)); } - if (hasAttribute(ITEM_ATTRIBUTE_DECAYTO)) { - propWriteStream.write(ATTR_DECAYTO); - propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_DECAYTO)); + if (hasAttribute(ITEM_ATTRIBUTE_KEYNUMBER)) { + propWriteStream.write(ATTR_KEYNUMBER); + propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER)); } - if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) { - const ItemAttributes::CustomAttributeMap* customAttrMap = attributes->getCustomAttributeMap(); - propWriteStream.write(ATTR_CUSTOM_ATTRIBUTES); - propWriteStream.write(static_cast(customAttrMap->size())); - for (const auto &entry : *customAttrMap) { - // Serializing key type and value - propWriteStream.writeString(entry.first); + if (hasAttribute(ITEM_ATTRIBUTE_KEYHOLENUMBER)) { + propWriteStream.write(ATTR_KEYHOLENUMBER); + propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER)); + } - // Serializing value type and value - entry.second.serialize(propWriteStream); - } + if (hasAttribute(ITEM_ATTRIBUTE_DOORLEVEL)) { + propWriteStream.write(ATTR_DOORLEVEL); + propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_DOORLEVEL)); + } + + if (hasAttribute(ITEM_ATTRIBUTE_DOORQUESTNUMBER)) { + propWriteStream.write(ATTR_DOORQUESTNUMBER); + propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER)); + } + + if (hasAttribute(ITEM_ATTRIBUTE_DOORQUESTVALUE)) { + propWriteStream.write(ATTR_DOORQUESTVALUE); + propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE)); + } + + if (hasAttribute(ITEM_ATTRIBUTE_CHESTQUESTNUMBER)) { + propWriteStream.write(ATTR_CHESTQUESTNUMBER); + propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER)); } } @@ -813,17 +804,18 @@ bool Item::hasProperty(ITEMPROPERTY prop) const const ItemType& it = items[id]; switch (prop) { case CONST_PROP_BLOCKSOLID: return it.blockSolid; - case CONST_PROP_MOVEABLE: return it.moveable && !hasAttribute(ITEM_ATTRIBUTE_UNIQUEID); + case CONST_PROP_MOVEABLE: return it.moveable; case CONST_PROP_HASHEIGHT: return it.hasHeight; case CONST_PROP_BLOCKPROJECTILE: return it.blockProjectile; case CONST_PROP_BLOCKPATH: return it.blockPathFind; case CONST_PROP_ISVERTICAL: return it.isVertical; case CONST_PROP_ISHORIZONTAL: return it.isHorizontal; - case CONST_PROP_IMMOVABLEBLOCKSOLID: return it.blockSolid && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)); - case CONST_PROP_IMMOVABLEBLOCKPATH: return it.blockPathFind && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)); - case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind && (!it.moveable || hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)); + case CONST_PROP_IMMOVABLEBLOCKSOLID: return it.blockSolid && !it.moveable; + case CONST_PROP_IMMOVABLEBLOCKPATH: return it.blockPathFind && !it.moveable; + case CONST_PROP_IMMOVABLENOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind && !it.moveable; case CONST_PROP_NOFIELDBLOCKPATH: return !it.isMagicField() && it.blockPathFind; case CONST_PROP_SUPPORTHANGABLE: return it.isHorizontal || it.isVertical; + case CONST_PROP_UNLAY: return !it.allowPickupable; default: return false; } } @@ -838,10 +830,8 @@ uint32_t Item::getWeight() const } std::string Item::getDescription(const ItemType& it, int32_t lookDistance, - const Item* item /*= nullptr*/, int32_t subType /*= -1*/, bool addArticle /*= true*/) + const Item* item /*= nullptr*/, int32_t subType /*= -1*/, bool addArticle /*= true*/) { - const std::string* text = nullptr; - std::ostringstream s; s << getNameDescription(it, item, subType, addArticle); @@ -850,567 +840,141 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, } if (it.isRune()) { - if (it.runeLevel > 0 || it.runeMagLevel > 0) { - if (RuneSpell* rune = g_spells->getRuneSpell(it.id)) { - int32_t tmpSubType = subType; - if (item) { - tmpSubType = item->getSubType(); - } - s << ". " << (it.stackable && tmpSubType > 1 ? "They" : "It") << " can only be used by "; + uint32_t charges = std::max(static_cast(1), static_cast(item == nullptr ? it.charges : item->getCharges())); - const VocSpellMap& vocMap = rune->getVocMap(); - std::vector showVocMap; - - // vocations are usually listed with the unpromoted and promoted version, the latter being - // hidden from description, so `total / 2` is most likely the amount of vocations to be shown. - showVocMap.reserve(vocMap.size() / 2); - for (const auto& voc : vocMap) { - if (voc.second) { - showVocMap.push_back(g_vocations.getVocation(voc.first)); - } - } - - if (!showVocMap.empty()) { - auto vocIt = showVocMap.begin(), vocLast = (showVocMap.end() - 1); - while (vocIt != vocLast) { - s << asLowerCaseString((*vocIt)->getVocName()) << "s"; - if (++vocIt == vocLast) { - s << " and "; - } else { - s << ", "; - } - } - s << asLowerCaseString((*vocLast)->getVocName()) << "s"; - } else { - s << "players"; - } - - s << " with"; - - if (it.runeLevel > 0) { - s << " level " << it.runeLevel; - } - - if (it.runeMagLevel > 0) { - if (it.runeLevel > 0) { - s << " and"; - } - - s << " magic level " << it.runeMagLevel; - } - - s << " or higher"; - } + if (it.runeLevel > 0) { + s << " for level " << it.runeLevel; } + + if (it.runeLevel > 0) { + s << " and"; + } + + s << " for magic level " << it.runeMagLevel; + s << ". It's an \"" << it.runeSpellName << "\"-spell (" << charges << "x). "; + } else if (it.isDoor() && item) { + if (item->hasAttribute(ITEM_ATTRIBUTE_DOORLEVEL)) { + s << " for level " << item->getIntAttr(ITEM_ATTRIBUTE_DOORLEVEL); + } + s << "."; } else if (it.weaponType != WEAPON_NONE) { - bool begin = true; if (it.weaponType == WEAPON_DISTANCE && it.ammoType != AMMO_NONE) { - s << " (Range:" << static_cast(item ? item->getShootRange() : it.shootRange); + if (it.attack != 0) { + s << ", Atk" << std::showpos << it.attack << std::noshowpos; + } + } else if (it.weaponType != WEAPON_AMMO && it.weaponType != WEAPON_WAND && (it.attack != 0 || it.defense != 0)) { + s << " ("; + if (it.attack != 0) { + s << "Atk:" << static_cast(it.attack); + } - int32_t attack; - int8_t hitChance; - if (item) { - attack = item->getAttack(); - hitChance = item->getHitChance(); + if (it.defense != 0) { + if (it.attack != 0) + s << " "; + + s << "Def:" << static_cast(it.defense); + } + + s << ")"; + } + s << "."; + } else if (it.armor != 0) { + if (it.charges > 0) { + if (subType > 1) { + s << " that has " << static_cast(subType) << " charges left"; } else { - attack = it.attack; - hitChance = it.hitChance; - } - - if (attack != 0) { - s << ", Atk" << std::showpos << attack << std::noshowpos; - } - - if (hitChance != 0) { - s << ", Hit%" << std::showpos << static_cast(hitChance) << std::noshowpos; - } - - begin = false; - } else if (it.weaponType != WEAPON_AMMO) { - - int32_t attack, defense, extraDefense; - if (item) { - attack = item->getAttack(); - defense = item->getDefense(); - extraDefense = item->getExtraDefense(); - } else { - attack = it.attack; - defense = it.defense; - extraDefense = it.extraDefense; - } - - if (attack != 0) { - begin = false; - s << " (Atk:" << attack; - - if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) { - s << " physical + " << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType); - } - } - - if (defense != 0 || extraDefense != 0) { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "Def:" << defense; - if (extraDefense != 0) { - s << ' ' << std::showpos << extraDefense << std::noshowpos; - } + s << " that has " << it.charges << " charge left"; } } - if (it.abilities) { - for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) { - if (!it.abilities->skills[i]) { - continue; - } + s << " (Arm:" << it.armor << ")."; + } else if (it.isFluidContainer()) { + if (item && item->getFluidType() != 0) { + s << " of " << items[item->getFluidType()].name << "."; + } else { + s << ". It is empty."; + } + } else if (it.isSplash()) { + s << " of "; + if (item && item->getFluidType() != 0) { + s << items[item->getFluidType()].name; + } else { + s << items[1].name; + } + s << "."; + } else if (it.isContainer() && !it.isChest()) { + s << " (Vol:" << static_cast(it.maxItems) << ")."; + } else if (it.isKey()) { + if (item) { + s << " (Key:" << static_cast(item->getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER)) << ")."; + } else { + s << " (Key:0)."; + } + } else if (it.allowDistRead) { + s << "."; + s << std::endl; - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos; - } - - for (uint8_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; i++) { - if (!it.abilities->specialSkills[i]) { - continue; - } - - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << getSpecialSkillName(i) << ' ' << std::showpos << it.abilities->specialSkills[i] << '%' << std::noshowpos; - } - - if (it.abilities->stats[STAT_MAGICPOINTS]) { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos; - } - - int16_t show = it.abilities->absorbPercent[0]; - if (show != 0) { - for (size_t i = 1; i < COMBAT_COUNT; ++i) { - if (it.abilities->absorbPercent[i] != show) { - show = 0; - break; + if (item && item->getText() != "") { + if (lookDistance <= 4) { + const std::string& writer = item->getWriter(); + if (!writer.empty()) { + s << writer << " wrote"; + time_t date = item->getDate(); + if (date != 0) { + s << " on " << formatDateShort(date); } + s << ": "; + } else { + s << "You read: "; } - } - - if (show == 0) { - bool tmp = true; - - for (size_t i = 0; i < COMBAT_COUNT; ++i) { - if (it.abilities->absorbPercent[i] == 0) { - continue; - } - - if (tmp) { - tmp = false; - - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection "; - } else { - s << ", "; - } - - s << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%'; - } + s << item->getText(); } else { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection all " << std::showpos << show << std::noshowpos << '%'; - } - - show = it.abilities->fieldAbsorbPercent[0]; - if (show != 0) { - for (size_t i = 1; i < COMBAT_COUNT; ++i) { - if (it.abilities->absorbPercent[i] != show) { - show = 0; - break; - } - } - } - - if (show == 0) { - bool tmp = true; - - for (size_t i = 0; i < COMBAT_COUNT; ++i) { - if (it.abilities->fieldAbsorbPercent[i] == 0) { - continue; - } - - if (tmp) { - tmp = false; - - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection "; - } else { - s << ", "; - } - - s << getCombatName(indexToCombatType(i)) << " field " << std::showpos << it.abilities->fieldAbsorbPercent[i] << std::noshowpos << '%'; - } - } else { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection all fields " << std::showpos << show << std::noshowpos << '%'; - } - - if (it.abilities->speed) { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos; - } - } - - if (!begin) { - s << ')'; - } - } else if (it.armor != 0 || (item && item->getArmor() != 0) || it.showAttributes) { - bool begin = true; - - int32_t armor = (item ? item->getArmor() : it.armor); - if (armor != 0) { - s << " (Arm:" << armor; - begin = false; - } - - if (it.abilities) { - for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) { - if (!it.abilities->skills[i]) { - continue; - } - - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos; - } - - if (it.abilities->stats[STAT_MAGICPOINTS]) { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos; - } - - int16_t show = it.abilities->absorbPercent[0]; - if (show != 0) { - for (size_t i = 1; i < COMBAT_COUNT; ++i) { - if (it.abilities->absorbPercent[i] != show) { - show = 0; - break; - } - } - } - - if (!show) { - bool protectionBegin = true; - for (size_t i = 0; i < COMBAT_COUNT; ++i) { - if (it.abilities->absorbPercent[i] == 0) { - continue; - } - - if (protectionBegin) { - protectionBegin = false; - - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection "; - } else { - s << ", "; - } - - s << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%'; - } - } else { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection all " << std::showpos << show << std::noshowpos << '%'; - } - - show = it.abilities->fieldAbsorbPercent[0]; - if (show != 0) { - for (size_t i = 1; i < COMBAT_COUNT; ++i) { - if (it.abilities->absorbPercent[i] != show) { - show = 0; - break; - } - } - } - - if (!show) { - bool tmp = true; - - for (size_t i = 0; i < COMBAT_COUNT; ++i) { - if (it.abilities->fieldAbsorbPercent[i] == 0) { - continue; - } - - if (tmp) { - tmp = false; - - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection "; - } else { - s << ", "; - } - - s << getCombatName(indexToCombatType(i)) << " field " << std::showpos << it.abilities->fieldAbsorbPercent[i] << std::noshowpos << '%'; - } - } else { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "protection all fields " << std::showpos << show << std::noshowpos << '%'; - } - - if (it.abilities->speed) { - if (begin) { - begin = false; - s << " ("; - } else { - s << ", "; - } - - s << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos; - } - } - - if (!begin) { - s << ')'; - } - } else if (it.isContainer() || (item && item->getContainer())) { - uint32_t volume = 0; - if (!item || !item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { - if (it.isContainer()) { - volume = it.maxItems; - } else { - volume = item->getContainer()->capacity(); - } - } - - if (volume != 0) { - s << " (Vol:" << volume << ')'; - } - } else { - bool found = true; - - if (it.abilities) { - if (it.abilities->speed > 0) { - s << " (speed " << std::showpos << (it.abilities->speed / 2) << std::noshowpos << ')'; - } else if (hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions)) { - s << " (hard drinking)"; - } else if (it.abilities->invisible) { - s << " (invisibility)"; - } else if (it.abilities->regeneration) { - s << " (faster regeneration)"; - } else if (it.abilities->manaShield) { - s << " (mana shield)"; - } else { - found = false; + s << "You are too far away to read it."; } } else { - found = false; + s << "Nothing is written on it."; } - - if (!found) { - if (it.isKey()) { - s << " (Key:" << std::setfill('0') << std::setw(4) << (item ? item->getActionId() : 0) << ')'; - } else if (it.isFluidContainer()) { - if (subType > 0) { - const std::string& itemName = items[subType].name; - s << " of " << (!itemName.empty() ? itemName : "unknown"); - } else { - s << ". It is empty"; - } - } else if (it.isSplash()) { - s << " of "; - - if (subType > 0 && !items[subType].name.empty()) { - s << items[subType].name; - } else { - s << "unknown"; - } - } else if (it.allowDistRead && (it.id < 7369 || it.id > 7371)) { - s << ".\n"; - - if (lookDistance <= 4) { - if (item) { - text = &item->getText(); - if (!text->empty()) { - const std::string& writer = item->getWriter(); - if (!writer.empty()) { - s << writer << " wrote"; - time_t date = item->getDate(); - if (date != 0) { - s << " on " << formatDateShort(date); - } - s << ": "; - } else { - s << "You read: "; - } - s << *text; - } else { - s << "Nothing is written on it"; - } - } else { - s << "Nothing is written on it"; - } - } else { - s << "You are too far away to read it"; - } - } else if (it.levelDoor != 0 && item) { - uint16_t actionId = item->getActionId(); - if (actionId >= it.levelDoor) { - s << " for level " << (actionId - it.levelDoor); - } - } + } else if (it.charges > 0) { + uint32_t charges = (item == nullptr ? it.charges : item->getCharges()); + if (charges > 1) { + s << " that has " << static_cast(charges) << " charges left."; + } else { + s << " that has 1 charge left."; } - } - - if (it.showCharges) { - s << " that has " << subType << " charge" << (subType != 1 ? "s" : "") << " left"; - } - - if (it.showDuration) { + } else if (it.showDuration) { if (item && item->hasAttribute(ITEM_ATTRIBUTE_DURATION)) { - uint32_t duration = item->getDuration() / 1000; - s << " that will expire in "; + int32_t duration = item->getDuration() / 1000; + s << " that has energy for "; - if (duration >= 86400) { - uint16_t days = duration / 86400; - uint16_t hours = (duration % 86400) / 3600; - s << days << " day" << (days != 1 ? "s" : ""); - - if (hours > 0) { - s << " and " << hours << " hour" << (hours != 1 ? "s" : ""); - } - } else if (duration >= 3600) { - uint16_t hours = duration / 3600; - uint16_t minutes = (duration % 3600) / 60; - s << hours << " hour" << (hours != 1 ? "s" : ""); - - if (minutes > 0) { - s << " and " << minutes << " minute" << (minutes != 1 ? "s" : ""); - } - } else if (duration >= 60) { - uint16_t minutes = duration / 60; - s << minutes << " minute" << (minutes != 1 ? "s" : ""); - uint16_t seconds = duration % 60; - - if (seconds > 0) { - s << " and " << seconds << " second" << (seconds != 1 ? "s" : ""); - } + if (duration >= 120) { + s << duration / 60 << " minutes left."; + } else if (duration > 60) { + s << "1 minute left."; } else { - s << duration << " second" << (duration != 1 ? "s" : ""); + s << "less than a minute left."; } } else { - s << " that is brand-new"; + s << " that is brand-new."; } - } - - if (!it.allowDistRead || (it.id >= 7369 && it.id <= 7371)) { - s << '.'; } else { - if (!text && item) { - text = &item->getText(); - } - - if (!text || text->empty()) { - s << '.'; - } + s << "."; } if (it.wieldInfo != 0) { - s << "\nIt can only be wielded properly by "; + s << std::endl << "It can only be wielded properly by "; if (it.wieldInfo & WIELDINFO_PREMIUM) { s << "premium "; } - if (!it.vocationString.empty()) { + if (it.wieldInfo & WIELDINFO_VOCREQ) { s << it.vocationString; } else { s << "players"; } if (it.wieldInfo & WIELDINFO_LEVEL) { - s << " of level " << it.minReqLevel << " or higher"; + s << " of level " << static_cast(it.minReqLevel) << " or higher"; } if (it.wieldInfo & WIELDINFO_MAGLV) { @@ -1420,43 +984,25 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, s << " of"; } - s << " magic level " << it.minReqMagicLevel << " or higher"; + s << " magic level " << static_cast(it.minReqMagicLevel) << " or higher"; } - s << '.'; + s << "."; } - if (lookDistance <= 1) { - if (item) { - const uint32_t weight = item->getWeight(); - if (weight != 0 && it.pickupable) { - s << '\n' << getWeightDescription(it, weight, item->getItemCount()); - } - } else if (it.weight != 0 && it.pickupable) { - s << '\n' << getWeightDescription(it, it.weight); + if (lookDistance <= 1 && !it.isChest() && it.pickupable) { + double weight = (item == nullptr ? it.weight : item->getWeight()); + if (weight > 0) { + s << std::endl << getWeightDescription(it, weight); } } - if (item) { - const std::string& specialDescription = item->getSpecialDescription(); - if (!specialDescription.empty()) { - s << '\n' << specialDescription; - } else if (lookDistance <= 1 && !it.description.empty()) { - s << '\n' << it.description; - } - } else if (lookDistance <= 1 && !it.description.empty()) { - s << '\n' << it.description; + if (item && item->getSpecialDescription() != "") { + s << std::endl << item->getSpecialDescription().c_str(); + } else if (it.description.length() && lookDistance <= 1) { + s << std::endl << it.description << "."; } - if (it.allowDistRead && it.id >= 7369 && it.id <= 7371) { - if (!text && item) { - text = &item->getText(); - } - - if (text && !text->empty()) { - s << '\n' << *text; - } - } return s.str(); } @@ -1542,17 +1088,6 @@ std::string Item::getWeightDescription() const return getWeightDescription(weight); } -void Item::setUniqueId(uint16_t n) -{ - if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { - return; - } - - if (g_game.addUniqueItem(n, this)) { - getAttributes()->setUniqueId(n); - } -} - bool Item::canDecay() const { if (isRemoved()) { @@ -1560,11 +1095,7 @@ bool Item::canDecay() const } const ItemType& it = Item::items[id]; - if (getDecayTo() < 0 || it.decayTime == 0) { - return false; - } - - if (hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { + if (it.decayTo < 0 || it.decayTime == 0) { return false; } @@ -1588,16 +1119,14 @@ uint32_t Item::getWorth() const } } -LightInfo Item::getLightInfo() const +void Item::getLight(LightInfo& lightInfo) const { const ItemType& it = items[id]; - return {it.lightLevel, it.lightColor}; + lightInfo.color = it.lightColor; + lightInfo.level = it.lightLevel; } std::string ItemAttributes::emptyString; -int64_t ItemAttributes::emptyInt; -double ItemAttributes::emptyDouble; -bool ItemAttributes::emptyBool; const std::string& ItemAttributes::getStrAttr(itemAttrTypes type) const { @@ -1711,63 +1240,3 @@ void Item::startDecaying() { g_game.startDecay(this); } - -bool Item::hasMarketAttributes() const -{ - if (attributes == nullptr) { - return true; - } - - for (const auto& attr : attributes->getList()) { - if (attr.type == ITEM_ATTRIBUTE_CHARGES) { - uint16_t charges = static_cast(attr.value.integer); - if (charges != items[id].charges) { - return false; - } - } else if (attr.type == ITEM_ATTRIBUTE_DURATION) { - uint32_t duration = static_cast(attr.value.integer); - if (duration != getDefaultDuration()) { - return false; - } - } else { - return false; - } - } - return true; -} - -template<> -const std::string& ItemAttributes::CustomAttribute::get() { - if (value.type() == typeid(std::string)) { - return boost::get(value); - } - - return emptyString; -} - -template<> -const int64_t& ItemAttributes::CustomAttribute::get() { - if (value.type() == typeid(int64_t)) { - return boost::get(value); - } - - return emptyInt; -} - -template<> -const double& ItemAttributes::CustomAttribute::get() { - if (value.type() == typeid(double)) { - return boost::get(value); - } - - return emptyDouble; -} - -template<> -const bool& ItemAttributes::CustomAttribute::get() { - if (value.type() == typeid(bool)) { - return boost::get(value); - } - - return emptyBool; -} diff --git a/src/item.h b/src/item.h index edbda91..87b2a04 100644 --- a/src/item.h +++ b/src/item.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +23,7 @@ #include "cylinder.h" #include "thing.h" #include "items.h" -#include "luascript.h" -#include "tools.h" -#include -#include -#include #include class Creature; @@ -36,8 +31,8 @@ class Player; class Container; class Depot; class Teleport; -class TrashHolder; class Mailbox; +class DepotLocker; class Door; class MagicField; class BedItem; @@ -55,6 +50,7 @@ enum ITEMPROPERTY { CONST_PROP_IMMOVABLENOFIELDBLOCKPATH, CONST_PROP_NOFIELDBLOCKPATH, CONST_PROP_SUPPORTHANGABLE, + CONST_PROP_UNLAY, }; enum TradeEvents_t { @@ -73,7 +69,7 @@ enum AttrTypes_t { //ATTR_EXT_FILE = 2, ATTR_TILE_FLAGS = 3, ATTR_ACTION_ID = 4, - ATTR_UNIQUE_ID = 5, + ATTR_MOVEMENT_ID = 5, ATTR_TEXT = 6, ATTR_DESC = 7, ATTR_TELE_DEST = 8, @@ -91,19 +87,22 @@ enum AttrTypes_t { ATTR_SLEEPERGUID = 20, ATTR_SLEEPSTART = 21, ATTR_CHARGES = 22, - ATTR_CONTAINER_ITEMS = 23, - ATTR_NAME = 24, - ATTR_ARTICLE = 25, - ATTR_PLURALNAME = 26, - ATTR_WEIGHT = 27, - ATTR_ATTACK = 28, - ATTR_DEFENSE = 29, - ATTR_EXTRADEFENSE = 30, - ATTR_ARMOR = 31, - ATTR_HITCHANCE = 32, - ATTR_SHOOTRANGE = 33, - ATTR_CUSTOM_ATTRIBUTES = 34, - ATTR_DECAYTO = 35 + ATTR_KEYNUMBER = 23, + ATTR_KEYHOLENUMBER = 24, + ATTR_DOORQUESTNUMBER = 25, + ATTR_DOORQUESTVALUE = 26, + ATTR_DOORLEVEL = 27, + ATTR_CHESTQUESTNUMBER = 28, + // add non-OTBM attributes after here + ATTR_CONTAINER_ITEMS = 29, + ATTR_NAME = 30, + ATTR_ARTICLE = 31, + ATTR_PLURALNAME = 32, + ATTR_WEIGHT = 33, + ATTR_ATTACK = 34, + ATTR_DEFENSE = 35, + ATTR_ARMOR = 36, + ATTR_SHOOTRANGE = 37, }; enum Attr_ReadValue { @@ -161,11 +160,53 @@ class ItemAttributes return static_cast(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); } - void setUniqueId(uint16_t n) { - setIntAttr(ITEM_ATTRIBUTE_UNIQUEID, n); + void setMovementID(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n); } - uint16_t getUniqueId() const { - return static_cast(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID)); + uint16_t getMovementId() const { + return static_cast(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID)); + } + + void setKeyNumber(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, n); + } + uint16_t getKeyNumber() const { + return static_cast(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER)); + } + + void setKeyHoleNumber(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER, n); + } + uint16_t getKeyHoleNumber() const { + return static_cast(getIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER)); + } + + void setDoorQuestNumber(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER, n); + } + uint16_t getDoorQuestNumber() const { + return static_cast(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER)); + } + + void setDoorQuestValue(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE, n); + } + uint16_t getDoorQuestValue() const { + return static_cast(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE)); + } + + void setDoorLevel(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_DOORLEVEL, n); + } + uint16_t getDoorLevel() const { + return static_cast(getIntAttr(ITEM_ATTRIBUTE_DOORLEVEL)); + } + + void setChestQuestNumber(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER, n); + } + uint16_t getChestQuestNumber() const { + return static_cast(getIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER)); } void setCharges(uint16_t n) { @@ -189,13 +230,6 @@ class ItemAttributes return getIntAttr(ITEM_ATTRIBUTE_OWNER); } - void setCorpseOwner(uint32_t corpseOwner) { - setIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER, corpseOwner); - } - uint32_t getCorpseOwner() const { - return getIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER); - } - void setDuration(int32_t time) { setIntAttr(ITEM_ATTRIBUTE_DURATION, time); } @@ -213,149 +247,19 @@ class ItemAttributes return static_cast(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); } - struct CustomAttribute - { - typedef boost::variant VariantAttribute; - VariantAttribute value; - - CustomAttribute() : value(boost::blank()) {} - - template - explicit CustomAttribute(const T& v) : value(v) {} - - template - void set(const T& v) { - value = v; - } - - template - const T& get(); - - struct PushLuaVisitor : public boost::static_visitor<> { - lua_State* L; - - explicit PushLuaVisitor(lua_State* L) : boost::static_visitor<>(), L(L) {} - - void operator()(const boost::blank&) const { - lua_pushnil(L); - } - - void operator()(const std::string& v) const { - LuaScriptInterface::pushString(L, v); - } - - void operator()(bool v) const { - LuaScriptInterface::pushBoolean(L, v); - } - - void operator()(const int64_t& v) const { - lua_pushnumber(L, v); - } - - void operator()(const double& v) const { - lua_pushnumber(L, v); - } - }; - - void pushToLua(lua_State* L) const { - boost::apply_visitor(PushLuaVisitor(L), value); - } - - struct SerializeVisitor : public boost::static_visitor<> { - PropWriteStream& propWriteStream; - - explicit SerializeVisitor(PropWriteStream& propWriteStream) : boost::static_visitor<>(), propWriteStream(propWriteStream) {} - - void operator()(const boost::blank&) const { - } - - void operator()(const std::string& v) const { - propWriteStream.writeString(v); - } - - template - void operator()(const T& v) const { - propWriteStream.write(v); - } - }; - - void serialize(PropWriteStream& propWriteStream) const { - propWriteStream.write(static_cast(value.which())); - boost::apply_visitor(SerializeVisitor(propWriteStream), value); - } - - bool unserialize(PropStream& propStream) { - // This is hard coded so it's not general, depends on the position of the variants. - uint8_t pos; - if (!propStream.read(pos)) { - return false; - } - - switch (pos) { - case 1: { // std::string - std::string tmp; - if (!propStream.readString(tmp)) { - return false; - } - value = tmp; - break; - } - - case 2: { // int64_t - int64_t tmp; - if (!propStream.read(tmp)) { - return false; - } - value = tmp; - break; - } - - case 3: { // double - double tmp; - if (!propStream.read(tmp)) { - return false; - } - value = tmp; - break; - } - - case 4: { // bool - bool tmp; - if (!propStream.read(tmp)) { - return false; - } - value = tmp; - break; - } - - default: { - value = boost::blank(); - return false; - } - } - return true; - } - }; - - private: - bool hasAttribute(itemAttrTypes type) const { + protected: + inline bool hasAttribute(itemAttrTypes type) const { return (type & attributeBits) != 0; } void removeAttribute(itemAttrTypes type); static std::string emptyString; - static int64_t emptyInt; - static double emptyDouble; - static bool emptyBool; - - typedef std::unordered_map CustomAttributeMap; struct Attribute { union { int64_t integer; std::string* string; - CustomAttributeMap* custom; } value; itemAttrTypes type; @@ -368,8 +272,6 @@ class ItemAttributes value.integer = i.value.integer; } else if (ItemAttributes::isStrAttrType(type)) { value.string = new std::string(*i.value.string); - } else if (ItemAttributes::isCustomAttrType(type)) { - value.custom = new CustomAttributeMap(*i.value.custom); } else { memset(&value, 0, sizeof(value)); } @@ -381,8 +283,6 @@ class ItemAttributes ~Attribute() { if (ItemAttributes::isStrAttrType(type)) { delete value.string; - } else if (ItemAttributes::isCustomAttrType(type)) { - delete value.custom; } } Attribute& operator=(Attribute other) { @@ -393,8 +293,6 @@ class ItemAttributes if (this != &other) { if (ItemAttributes::isStrAttrType(type)) { delete value.string; - } else if (ItemAttributes::isCustomAttrType(type)) { - delete value.custom; } value = other.value; @@ -425,94 +323,12 @@ class ItemAttributes const Attribute* getExistingAttr(itemAttrTypes type) const; Attribute& getAttr(itemAttrTypes type); - CustomAttributeMap* getCustomAttributeMap() { - if (!hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) { - return nullptr; - } - - return getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom; - } - - template - void setCustomAttribute(int64_t key, R value) { - std::string tmp = boost::lexical_cast(key); - setCustomAttribute(tmp, value); - } - - void setCustomAttribute(int64_t key, CustomAttribute& value) { - std::string tmp = boost::lexical_cast(key); - setCustomAttribute(tmp, value); - } - - template - void setCustomAttribute(std::string& key, R value) { - toLowerCaseString(key); - if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) { - removeCustomAttribute(key); - } else { - getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap(); - } - getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom->emplace(key, value); - } - - void setCustomAttribute(std::string& key, CustomAttribute& value) { - toLowerCaseString(key); - if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) { - removeCustomAttribute(key); - } else { - getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap(); - } - getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom->insert(std::make_pair(std::move(key), std::move(value))); - } - - const CustomAttribute* getCustomAttribute(int64_t key) { - std::string tmp = boost::lexical_cast(key); - return getCustomAttribute(tmp); - } - - const CustomAttribute* getCustomAttribute(const std::string& key) { - if (const CustomAttributeMap* customAttrMap = getCustomAttributeMap()) { - auto it = customAttrMap->find(asLowerCaseString(key)); - if (it != customAttrMap->end()) { - return &(it->second); - } - } - return nullptr; - } - - bool removeCustomAttribute(int64_t key) { - std::string tmp = boost::lexical_cast(key); - return removeCustomAttribute(tmp); - } - - bool removeCustomAttribute(const std::string& key) { - if (CustomAttributeMap* customAttrMap = getCustomAttributeMap()) { - auto it = customAttrMap->find(asLowerCaseString(key)); - if (it != customAttrMap->end()) { - customAttrMap->erase(it); - return true; - } - } - return false; - } - - const static uint32_t intAttributeTypes = ITEM_ATTRIBUTE_ACTIONID | ITEM_ATTRIBUTE_UNIQUEID | ITEM_ATTRIBUTE_DATE - | ITEM_ATTRIBUTE_WEIGHT | ITEM_ATTRIBUTE_ATTACK | ITEM_ATTRIBUTE_DEFENSE | ITEM_ATTRIBUTE_EXTRADEFENSE - | ITEM_ATTRIBUTE_ARMOR | ITEM_ATTRIBUTE_HITCHANCE | ITEM_ATTRIBUTE_SHOOTRANGE | ITEM_ATTRIBUTE_OWNER - | ITEM_ATTRIBUTE_DURATION | ITEM_ATTRIBUTE_DECAYSTATE | ITEM_ATTRIBUTE_CORPSEOWNER | ITEM_ATTRIBUTE_CHARGES - | ITEM_ATTRIBUTE_FLUIDTYPE | ITEM_ATTRIBUTE_DOORID | ITEM_ATTRIBUTE_DECAYTO; - const static uint32_t stringAttributeTypes = ITEM_ATTRIBUTE_DESCRIPTION | ITEM_ATTRIBUTE_TEXT | ITEM_ATTRIBUTE_WRITER - | ITEM_ATTRIBUTE_NAME | ITEM_ATTRIBUTE_ARTICLE | ITEM_ATTRIBUTE_PLURALNAME; - public: - static bool isIntAttrType(itemAttrTypes type) { - return (type & intAttributeTypes) == type; + inline static bool isIntAttrType(itemAttrTypes type) { + return (type & 0xFFFFE13) != 0; } - static bool isStrAttrType(itemAttrTypes type) { - return (type & stringAttributeTypes) == type; - } - inline static bool isCustomAttrType(itemAttrTypes type) { - return (type & ITEM_ATTRIBUTE_CUSTOM) == type; + inline static bool isStrAttrType(itemAttrTypes type) { + return (type & 0x1EC) != 0; } const std::forward_list& getList() const { @@ -543,10 +359,10 @@ class Item : virtual public Thing bool equals(const Item* otherItem) const; - Item* getItem() override final { + Item* getItem() final { return this; } - const Item* getItem() const override final { + const Item* getItem() const final { return this; } virtual Teleport* getTeleport() { @@ -555,18 +371,18 @@ class Item : virtual public Thing virtual const Teleport* getTeleport() const { return nullptr; } - virtual TrashHolder* getTrashHolder() { - return nullptr; - } - virtual const TrashHolder* getTrashHolder() const { - return nullptr; - } virtual Mailbox* getMailbox() { return nullptr; } virtual const Mailbox* getMailbox() const { return nullptr; } + virtual DepotLocker* getDepotLocker() { + return nullptr; + } + virtual const DepotLocker* getDepotLocker() const { + return nullptr; + } virtual Door* getDoor() { return nullptr; } @@ -621,31 +437,6 @@ class Item : virtual public Thing return attributes->hasAttribute(type); } - template - void setCustomAttribute(std::string& key, R value) { - getAttributes()->setCustomAttribute(key, value); - } - - void setCustomAttribute(std::string& key, ItemAttributes::CustomAttribute& value) { - getAttributes()->setCustomAttribute(key, value); - } - - const ItemAttributes::CustomAttribute* getCustomAttribute(int64_t key) { - return getAttributes()->getCustomAttribute(key); - } - - const ItemAttributes::CustomAttribute* getCustomAttribute(const std::string& key) { - return getAttributes()->getCustomAttribute(key); - } - - bool removeCustomAttribute(int64_t key) { - return getAttributes()->removeCustomAttribute(key); - } - - bool removeCustomAttribute(const std::string& key) { - return getAttributes()->removeCustomAttribute(key); - } - void setSpecialDescription(const std::string& desc) { setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc); } @@ -684,10 +475,6 @@ class Item : virtual public Thing } void setActionId(uint16_t n) { - if (n < 100) { - n = 100; - } - setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n); } uint16_t getActionId() const { @@ -697,11 +484,14 @@ class Item : virtual public Thing return static_cast(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); } - uint16_t getUniqueId() const { + void setMovementID(uint16_t n) { + setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n); + } + uint16_t getMovementId() const { if (!attributes) { return 0; } - return static_cast(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID)); + return static_cast(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID)); } void setCharges(uint16_t n) { @@ -767,49 +557,42 @@ class Item : virtual public Thing return static_cast(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); } - void setDecayTo(int32_t decayTo) { - setIntAttr(ITEM_ATTRIBUTE_DECAYTO, decayTo); - } - int32_t getDecayTo() const { - if (hasAttribute(ITEM_ATTRIBUTE_DECAYTO)) { - return getIntAttr(ITEM_ATTRIBUTE_DECAYTO); - } - return items[id].decayTo; - } - static std::string getDescription(const ItemType& it, int32_t lookDistance, const Item* item = nullptr, int32_t subType = -1, bool addArticle = true); static std::string getNameDescription(const ItemType& it, const Item* item = nullptr, int32_t subType = -1, bool addArticle = true); static std::string getWeightDescription(const ItemType& it, uint32_t weight, uint32_t count = 1); - std::string getDescription(int32_t lookDistance) const override final; + std::string getDescription(int32_t lookDistance) const final; std::string getNameDescription() const; std::string getWeightDescription() const; //serialization virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream); bool unserializeAttr(PropStream& propStream); - virtual bool unserializeItemNode(OTB::Loader&, const OTB::Node&, PropStream& propStream); + virtual bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream); virtual void serializeAttr(PropWriteStream& propWriteStream) const; - bool isPushable() const override final { + bool isPushable() const final { return isMoveable(); } - int32_t getThrowRange() const override final { + int32_t getThrowRange() const final { return (isPickupable() ? 15 : 2); } uint16_t getID() const { return id; } - uint16_t getClientID() const { - return items[id].clientId; - } void setID(uint16_t newid); // Returns the player that is holding this item in his inventory Player* getHoldingPlayer() const; + CombatType_t getDamageType() const { + return items[id].damageType; + } + CombatType_t getCombatType() const { + return items[id].combatType; + } WeaponType_t getWeaponType() const { return items[id].weaponType; } @@ -822,7 +605,28 @@ class Item : virtual public Thing } return items[id].shootRange; } + uint8_t getMissileType() const { + return items[id].shootType; + } + uint8_t getFragility() const { + return items[id].fragility; + } + int32_t getAttackStrength() const { + return items[id].attackStrength; + } + int32_t getAttackVariation() const { + return items[id].attackVariation; + } + int32_t getManaConsumption() const { + return items[id].manaConsumption; + } + uint32_t getMinimumLevel() const { + return items[id].minReqLevel; + } + int32_t getWeaponSpecialEffect() const { + return items[id].weaponSpecialEffect; + } virtual uint32_t getWeight() const; uint32_t getBaseWeight() const { if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) { @@ -848,24 +652,15 @@ class Item : virtual public Thing } return items[id].defense; } - int32_t getExtraDefense() const { - if (hasAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE)) { - return getIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE); - } - return items[id].extraDefense; - } int32_t getSlotPosition() const { return items[id].slotPosition; } - int8_t getHitChance() const { - if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) { - return getIntAttr(ITEM_ATTRIBUTE_HITCHANCE); - } - return items[id].hitChance; + uint16_t getDisguiseId() const { + return items[id].disguiseId; } uint32_t getWorth() const; - LightInfo getLightInfo() const; + void getLight(LightInfo& lightInfo) const; bool hasProperty(ITEMPROPERTY prop) const; bool isBlocking() const { @@ -883,15 +678,15 @@ class Item : virtual public Thing bool isMagicField() const { return items[id].isMagicField(); } + bool isSplash() const { + return items[id].isSplash(); + } bool isMoveable() const { return items[id].moveable; } bool isPickupable() const { return items[id].pickupable; } - bool isUseable() const { - return items[id].useable; - } bool isHangable() const { return items[id].isHangable; } @@ -899,10 +694,33 @@ class Item : virtual public Thing const ItemType& it = items[id]; return it.rotatable && it.rotateTo; } - bool hasWalkStack() const { - return items[id].walkStack; + bool isDisguised() const { + return items[id].disguise; + } + bool isChangeUse() const { + return items[id].changeUse; + } + bool isChestQuest() const { + return items[id].isChest(); + } + bool hasCollisionEvent() const { + return items[id].collisionEvent; + } + bool hasSeparationEvent() const { + return items[id].separationEvent; + } + bool hasUseEvent() const { + return items[id].useEvent; + } + bool hasMultiUseEvent() const { + return items[id].multiUseEvent; + } + bool canDistUse() const { + return items[id].distUse; + } + bool isRune() const { + return items[id].isRune(); } - const std::string& getName() const { if (hasAttribute(ITEM_ATTRIBUTE_NAME)) { return getStrAttr(ITEM_ATTRIBUTE_NAME); @@ -930,20 +748,12 @@ class Item : virtual public Thing count = n; } - static uint32_t countByType(const Item* i, int32_t subType) { - if (subType == -1 || subType == i->getSubType()) { - return i->getItemCount(); - } - - return 0; - } + static uint32_t countByType(const Item* i, int32_t subType); void setDefaultSubtype(); uint16_t getSubType() const; void setSubType(uint16_t n); - void setUniqueId(uint16_t n); - void setDefaultDuration() { uint32_t duration = getDefaultDuration(); if (duration != 0) { @@ -966,18 +776,13 @@ class Item : virtual public Thing virtual void startDecaying(); - bool isLoadedFromMap() const { - return loadedFromMap; - } void setLoadedFromMap(bool value) { loadedFromMap = value; } bool isCleanable() const { - return !loadedFromMap && canRemove() && isPickupable() && !hasAttribute(ITEM_ATTRIBUTE_UNIQUEID) && !hasAttribute(ITEM_ATTRIBUTE_ACTIONID); + return !loadedFromMap && canRemove() && isPickupable() && !hasAttribute(ITEM_ATTRIBUTE_ACTIONID); } - bool hasMarketAttributes() const; - std::unique_ptr& getAttributes() { if (!attributes) { attributes.reset(new ItemAttributes()); @@ -994,32 +799,29 @@ class Item : virtual public Thing } } - Cylinder* getParent() const override { + Cylinder* getParent() const { return parent; } - void setParent(Cylinder* cylinder) override { + void setParent(Cylinder* cylinder) { parent = cylinder; } Cylinder* getTopParent(); const Cylinder* getTopParent() const; - Tile* getTile() override; - const Tile* getTile() const override; - bool isRemoved() const override { + Tile* getTile(); + const Tile* getTile() const; + bool isRemoved() const { return !parent || parent->isRemoved(); } protected: - Cylinder* parent = nullptr; - - uint16_t id; // the same id as in ItemType - - private: std::string getWeightDescription(uint32_t weight) const; + Cylinder* parent = nullptr; std::unique_ptr attributes; uint32_t referenceCounter = 0; + uint16_t id; // the same id as in ItemType uint8_t count = 1; // number of stacked items bool loadedFromMap = false; @@ -1027,7 +829,16 @@ class Item : virtual public Thing //Don't add variables here, use the ItemAttribute class. }; -using ItemList = std::list; -using ItemDeque = std::deque; +typedef std::list ItemList; +typedef std::deque ItemDeque; + +inline uint32_t Item::countByType(const Item* i, int32_t subType) +{ + if (subType == -1 || subType == i->getSubType()) { + return i->getItemCount(); + } + + return 0; +} #endif diff --git a/src/itemloader.h b/src/itemloader.h deleted file mode 100644 index 159ee2a..0000000 --- a/src/itemloader.h +++ /dev/null @@ -1,198 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_ITEMLOADER_H_107F1D3EECC94CD0A0F528843010D5D4 -#define FS_ITEMLOADER_H_107F1D3EECC94CD0A0F528843010D5D4 - -#include "fileloader.h" - -enum itemgroup_t { - ITEM_GROUP_NONE, - - ITEM_GROUP_GROUND, - ITEM_GROUP_CONTAINER, - ITEM_GROUP_WEAPON, //deprecated - ITEM_GROUP_AMMUNITION, //deprecated - ITEM_GROUP_ARMOR, //deprecated - ITEM_GROUP_CHARGES, - ITEM_GROUP_TELEPORT, //deprecated - ITEM_GROUP_MAGICFIELD, //deprecated - ITEM_GROUP_WRITEABLE, //deprecated - ITEM_GROUP_KEY, //deprecated - ITEM_GROUP_SPLASH, - ITEM_GROUP_FLUID, - ITEM_GROUP_DOOR, //deprecated - ITEM_GROUP_DEPRECATED, - - ITEM_GROUP_LAST -}; - -/////////OTB specific////////////// -enum clientVersion_t { - CLIENT_VERSION_750 = 1, - CLIENT_VERSION_755 = 2, - CLIENT_VERSION_760 = 3, - CLIENT_VERSION_770 = 3, - CLIENT_VERSION_780 = 4, - CLIENT_VERSION_790 = 5, - CLIENT_VERSION_792 = 6, - CLIENT_VERSION_800 = 7, - CLIENT_VERSION_810 = 8, - CLIENT_VERSION_811 = 9, - CLIENT_VERSION_820 = 10, - CLIENT_VERSION_830 = 11, - CLIENT_VERSION_840 = 12, - CLIENT_VERSION_841 = 13, - CLIENT_VERSION_842 = 14, - CLIENT_VERSION_850 = 15, - CLIENT_VERSION_854_BAD = 16, - CLIENT_VERSION_854 = 17, - CLIENT_VERSION_855 = 18, - CLIENT_VERSION_860_OLD = 19, - CLIENT_VERSION_860 = 20, - CLIENT_VERSION_861 = 21, - CLIENT_VERSION_862 = 22, - CLIENT_VERSION_870 = 23, - CLIENT_VERSION_871 = 24, - CLIENT_VERSION_872 = 25, - CLIENT_VERSION_873 = 26, - CLIENT_VERSION_900 = 27, - CLIENT_VERSION_910 = 28, - CLIENT_VERSION_920 = 29, - CLIENT_VERSION_940 = 30, - CLIENT_VERSION_944_V1 = 31, - CLIENT_VERSION_944_V2 = 32, - CLIENT_VERSION_944_V3 = 33, - CLIENT_VERSION_944_V4 = 34, - CLIENT_VERSION_946 = 35, - CLIENT_VERSION_950 = 36, - CLIENT_VERSION_952 = 37, - CLIENT_VERSION_953 = 38, - CLIENT_VERSION_954 = 39, - CLIENT_VERSION_960 = 40, - CLIENT_VERSION_961 = 41, - CLIENT_VERSION_963 = 42, - CLIENT_VERSION_970 = 43, - CLIENT_VERSION_980 = 44, - CLIENT_VERSION_981 = 45, - CLIENT_VERSION_982 = 46, - CLIENT_VERSION_983 = 47, - CLIENT_VERSION_985 = 48, - CLIENT_VERSION_986 = 49, - CLIENT_VERSION_1010 = 50, - CLIENT_VERSION_1020 = 51, - CLIENT_VERSION_1021 = 52, - CLIENT_VERSION_1030 = 53, - CLIENT_VERSION_1031 = 54, - CLIENT_VERSION_1035 = 55, - CLIENT_VERSION_1076 = 56, - CLIENT_VERSION_1098 = 57, -}; - -enum rootattrib_ { - ROOT_ATTR_VERSION = 0x01, -}; - -enum itemattrib_t { - ITEM_ATTR_FIRST = 0x10, - ITEM_ATTR_SERVERID = ITEM_ATTR_FIRST, - ITEM_ATTR_CLIENTID, - ITEM_ATTR_NAME, - ITEM_ATTR_DESCR, - ITEM_ATTR_SPEED, - ITEM_ATTR_SLOT, - ITEM_ATTR_MAXITEMS, - ITEM_ATTR_WEIGHT, - ITEM_ATTR_WEAPON, - ITEM_ATTR_AMU, - ITEM_ATTR_ARMOR, - ITEM_ATTR_MAGLEVEL, - ITEM_ATTR_MAGFIELDTYPE, - ITEM_ATTR_WRITEABLE, - ITEM_ATTR_ROTATETO, - ITEM_ATTR_DECAY, - ITEM_ATTR_SPRITEHASH, - ITEM_ATTR_MINIMAPCOLOR, - ITEM_ATTR_07, - ITEM_ATTR_08, - ITEM_ATTR_LIGHT, - - //1-byte aligned - ITEM_ATTR_DECAY2, //deprecated - ITEM_ATTR_WEAPON2, //deprecated - ITEM_ATTR_AMU2, //deprecated - ITEM_ATTR_ARMOR2, //deprecated - ITEM_ATTR_WRITEABLE2, //deprecated - ITEM_ATTR_LIGHT2, - ITEM_ATTR_TOPORDER, - ITEM_ATTR_WRITEABLE3, //deprecated - - ITEM_ATTR_WAREID, - - ITEM_ATTR_LAST -}; - -enum itemflags_t { - FLAG_BLOCK_SOLID = 1 << 0, - FLAG_BLOCK_PROJECTILE = 1 << 1, - FLAG_BLOCK_PATHFIND = 1 << 2, - FLAG_HAS_HEIGHT = 1 << 3, - FLAG_USEABLE = 1 << 4, - FLAG_PICKUPABLE = 1 << 5, - FLAG_MOVEABLE = 1 << 6, - FLAG_STACKABLE = 1 << 7, - FLAG_FLOORCHANGEDOWN = 1 << 8, // unused - FLAG_FLOORCHANGENORTH = 1 << 9, // unused - FLAG_FLOORCHANGEEAST = 1 << 10, // unused - FLAG_FLOORCHANGESOUTH = 1 << 11, // unused - FLAG_FLOORCHANGEWEST = 1 << 12, // unused - FLAG_ALWAYSONTOP = 1 << 13, - FLAG_READABLE = 1 << 14, - FLAG_ROTATABLE = 1 << 15, - FLAG_HANGABLE = 1 << 16, - FLAG_VERTICAL = 1 << 17, - FLAG_HORIZONTAL = 1 << 18, - FLAG_CANNOTDECAY = 1 << 19, // unused - FLAG_ALLOWDISTREAD = 1 << 20, - FLAG_UNUSED = 1 << 21, // unused - FLAG_CLIENTCHARGES = 1 << 22, /* deprecated */ - FLAG_LOOKTHROUGH = 1 << 23, - FLAG_ANIMATION = 1 << 24, - FLAG_FULLTILE = 1 << 25, // unused - FLAG_FORCEUSE = 1 << 26, -}; - -//1-byte aligned structs -#pragma pack(1) - -struct VERSIONINFO { - uint32_t dwMajorVersion; - uint32_t dwMinorVersion; - uint32_t dwBuildNumber; - uint8_t CSDVersion[128]; -}; - -struct lightBlock2 { - uint16_t lightLevel; - uint16_t lightColor; -}; - -#pragma pack() -/////////OTB specific////////////// -#endif diff --git a/src/items.cpp b/src/items.cpp index fb5cc3c..8259ff0 100644 --- a/src/items.cpp +++ b/src/items.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,1323 +22,562 @@ #include "items.h" #include "spells.h" #include "movement.h" -#include "weapons.h" +#include "script.h" #include "pugicast.h" extern MoveEvents* g_moveEvents; -extern Weapons* g_weapons; - -const std::unordered_map ItemParseAttributesMap = { - {"type", ITEM_PARSE_TYPE}, - {"description", ITEM_PARSE_DESCRIPTION}, - {"runespellname", ITEM_PARSE_RUNESPELLNAME}, - {"weight", ITEM_PARSE_WEIGHT}, - {"showcount", ITEM_PARSE_SHOWCOUNT}, - {"armor", ITEM_PARSE_ARMOR}, - {"defense", ITEM_PARSE_DEFENSE}, - {"extradef", ITEM_PARSE_EXTRADEF}, - {"attack", ITEM_PARSE_ATTACK}, - {"rotateto", ITEM_PARSE_ROTATETO}, - {"moveable", ITEM_PARSE_MOVEABLE}, - {"movable", ITEM_PARSE_MOVEABLE}, - {"blockprojectile", ITEM_PARSE_BLOCKPROJECTILE}, - {"allowpickupable", ITEM_PARSE_PICKUPABLE}, - {"pickupable", ITEM_PARSE_PICKUPABLE}, - {"forceserialize", ITEM_PARSE_FORCESERIALIZE}, - {"forcesave", ITEM_PARSE_FORCESERIALIZE}, - {"floorchange", ITEM_PARSE_FLOORCHANGE}, - {"corpsetype", ITEM_PARSE_CORPSETYPE}, - {"containersize", ITEM_PARSE_CONTAINERSIZE}, - {"fluidsource", ITEM_PARSE_FLUIDSOURCE}, - {"readable", ITEM_PARSE_READABLE}, - {"writeable", ITEM_PARSE_WRITEABLE}, - {"maxtextlen", ITEM_PARSE_MAXTEXTLEN}, - {"writeonceitemid", ITEM_PARSE_WRITEONCEITEMID}, - {"weapontype", ITEM_PARSE_WEAPONTYPE}, - {"slottype", ITEM_PARSE_SLOTTYPE}, - {"ammotype", ITEM_PARSE_AMMOTYPE}, - {"shoottype", ITEM_PARSE_SHOOTTYPE}, - {"effect", ITEM_PARSE_EFFECT}, - {"range", ITEM_PARSE_RANGE}, - {"stopduration", ITEM_PARSE_STOPDURATION}, - {"decayto", ITEM_PARSE_DECAYTO}, - {"transformequipto", ITEM_PARSE_TRANSFORMEQUIPTO}, - {"transformdeequipto", ITEM_PARSE_TRANSFORMDEEQUIPTO}, - {"duration", ITEM_PARSE_DURATION}, - {"showduration", ITEM_PARSE_SHOWDURATION}, - {"charges", ITEM_PARSE_CHARGES}, - {"showcharges", ITEM_PARSE_SHOWCHARGES}, - {"showattributes", ITEM_PARSE_SHOWATTRIBUTES}, - {"hitchance", ITEM_PARSE_HITCHANCE}, - {"maxhitchance", ITEM_PARSE_MAXHITCHANCE}, - {"invisible", ITEM_PARSE_INVISIBLE}, - {"speed", ITEM_PARSE_SPEED}, - {"healthgain", ITEM_PARSE_HEALTHGAIN}, - {"healthticks", ITEM_PARSE_HEALTHTICKS}, - {"managain", ITEM_PARSE_MANAGAIN}, - {"manaticks", ITEM_PARSE_MANATICKS}, - {"manashield", ITEM_PARSE_MANASHIELD}, - {"skillsword", ITEM_PARSE_SKILLSWORD}, - {"skillaxe", ITEM_PARSE_SKILLAXE}, - {"skillclub", ITEM_PARSE_SKILLCLUB}, - {"skilldist", ITEM_PARSE_SKILLDIST}, - {"skillfish", ITEM_PARSE_SKILLFISH}, - {"skillshield", ITEM_PARSE_SKILLSHIELD}, - {"skillfist", ITEM_PARSE_SKILLFIST}, - {"maxhitpoints", ITEM_PARSE_MAXHITPOINTS}, - {"maxhitpointspercent", ITEM_PARSE_MAXHITPOINTSPERCENT}, - {"maxmanapoints", ITEM_PARSE_MAXMANAPOINTS}, - {"maxmanapointspercent", ITEM_PARSE_MAXMANAPOINTSPERCENT}, - {"magicpoints", ITEM_PARSE_MAGICPOINTS}, - {"magiclevelpoints", ITEM_PARSE_MAGICPOINTS}, - {"magicpointspercent", ITEM_PARSE_MAGICPOINTSPERCENT}, - {"criticalhitchance", ITEM_PARSE_CRITICALHITCHANCE}, - {"criticalhitamount", ITEM_PARSE_CRITICALHITAMOUNT}, - {"lifeleechchance", ITEM_PARSE_LIFELEECHCHANCE}, - {"lifeleechamount", ITEM_PARSE_LIFELEECHAMOUNT}, - {"manaleechchance", ITEM_PARSE_MANALEECHCHANCE}, - {"manaleechamount", ITEM_PARSE_MANALEECHAMOUNT}, - {"fieldabsorbpercentenergy", ITEM_PARSE_FIELDABSORBPERCENTENERGY}, - {"fieldabsorbpercentfire", ITEM_PARSE_FIELDABSORBPERCENTFIRE}, - {"fieldabsorbpercentpoison", ITEM_PARSE_FIELDABSORBPERCENTPOISON}, - {"fieldabsorbpercentearth", ITEM_PARSE_FIELDABSORBPERCENTPOISON}, - {"absorbpercentall", ITEM_PARSE_ABSORBPERCENTALL}, - {"absorbpercentallelements", ITEM_PARSE_ABSORBPERCENTALL}, - {"absorbpercentelements", ITEM_PARSE_ABSORBPERCENTELEMENTS}, - {"absorbpercentmagic", ITEM_PARSE_ABSORBPERCENTMAGIC}, - {"absorbpercentenergy", ITEM_PARSE_ABSORBPERCENTENERGY}, - {"absorbpercentfire", ITEM_PARSE_ABSORBPERCENTFIRE}, - {"absorbpercentpoison", ITEM_PARSE_ABSORBPERCENTPOISON}, - {"absorbpercentearth", ITEM_PARSE_ABSORBPERCENTPOISON}, - {"absorbpercentice", ITEM_PARSE_ABSORBPERCENTICE}, - {"absorbpercentholy", ITEM_PARSE_ABSORBPERCENTHOLY}, - {"absorbpercentdeath", ITEM_PARSE_ABSORBPERCENTDEATH}, - {"absorbpercentlifedrain", ITEM_PARSE_ABSORBPERCENTLIFEDRAIN}, - {"absorbpercentmanadrain", ITEM_PARSE_ABSORBPERCENTMANADRAIN}, - {"absorbpercentdrown", ITEM_PARSE_ABSORBPERCENTDROWN}, - {"absorbpercentphysical", ITEM_PARSE_ABSORBPERCENTPHYSICAL}, - {"absorbpercenthealing", ITEM_PARSE_ABSORBPERCENTHEALING}, - {"absorbpercentundefined", ITEM_PARSE_ABSORBPERCENTUNDEFINED}, - {"suppressdrunk", ITEM_PARSE_SUPPRESSDRUNK}, - {"suppressenergy", ITEM_PARSE_SUPPRESSENERGY}, - {"suppressfire", ITEM_PARSE_SUPPRESSFIRE}, - {"suppresspoison", ITEM_PARSE_SUPPRESSPOISON}, - {"suppressdrown", ITEM_PARSE_SUPPRESSDROWN}, - {"suppressphysical", ITEM_PARSE_SUPPRESSPHYSICAL}, - {"suppressfreeze", ITEM_PARSE_SUPPRESSFREEZE}, - {"suppressdazzle", ITEM_PARSE_SUPPRESSDAZZLE}, - {"suppresscurse", ITEM_PARSE_SUPPRESSCURSE}, - {"field", ITEM_PARSE_FIELD}, - {"replaceable", ITEM_PARSE_REPLACEABLE}, - {"partnerdirection", ITEM_PARSE_PARTNERDIRECTION}, - {"leveldoor", ITEM_PARSE_LEVELDOOR}, - {"maletransformto", ITEM_PARSE_MALETRANSFORMTO}, - {"malesleeper", ITEM_PARSE_MALETRANSFORMTO}, - {"femaletransformto", ITEM_PARSE_FEMALETRANSFORMTO}, - {"femalesleeper", ITEM_PARSE_FEMALETRANSFORMTO}, - {"transformto", ITEM_PARSE_TRANSFORMTO}, - {"destroyto", ITEM_PARSE_DESTROYTO}, - {"elementice", ITEM_PARSE_ELEMENTICE}, - {"elementearth", ITEM_PARSE_ELEMENTEARTH}, - {"elementfire", ITEM_PARSE_ELEMENTFIRE}, - {"elementenergy", ITEM_PARSE_ELEMENTENERGY}, - {"walkstack", ITEM_PARSE_WALKSTACK}, - {"blocking", ITEM_PARSE_BLOCKING}, - {"allowdistread", ITEM_PARSE_ALLOWDISTREAD}, -}; - -const std::unordered_map ItemTypesMap = { - {"key", ITEM_TYPE_KEY}, - {"magicfield", ITEM_TYPE_MAGICFIELD}, - {"container", ITEM_TYPE_CONTAINER}, - {"depot", ITEM_TYPE_DEPOT}, - {"mailbox", ITEM_TYPE_MAILBOX}, - {"trashholder", ITEM_TYPE_TRASHHOLDER}, - {"teleport", ITEM_TYPE_TELEPORT}, - {"door", ITEM_TYPE_DOOR}, - {"bed", ITEM_TYPE_BED}, - {"rune", ITEM_TYPE_RUNE}, -}; - -const std::unordered_map TileStatesMap = { - {"down", TILESTATE_FLOORCHANGE_DOWN}, - {"north", TILESTATE_FLOORCHANGE_NORTH}, - {"south", TILESTATE_FLOORCHANGE_SOUTH}, - {"southalt", TILESTATE_FLOORCHANGE_SOUTH_ALT}, - {"west", TILESTATE_FLOORCHANGE_WEST}, - {"east", TILESTATE_FLOORCHANGE_EAST}, - {"eastalt", TILESTATE_FLOORCHANGE_EAST_ALT}, -}; - -const std::unordered_map RaceTypesMap = { - {"venom", RACE_VENOM}, - {"blood", RACE_BLOOD}, - {"undead", RACE_UNDEAD}, - {"fire", RACE_FIRE}, - {"energy", RACE_ENERGY}, -}; - -const std::unordered_map WeaponTypesMap = { - {"sword", WEAPON_SWORD}, - {"club", WEAPON_CLUB}, - {"axe", WEAPON_AXE}, - {"shield", WEAPON_SHIELD}, - {"distance", WEAPON_DISTANCE}, - {"wand", WEAPON_WAND}, - {"ammunition", WEAPON_AMMO}, -}; - -const std::unordered_map FluidTypesMap = { - {"water", FLUID_WATER}, - {"blood", FLUID_BLOOD}, - {"beer", FLUID_BEER}, - {"slime", FLUID_SLIME}, - {"lemonade", FLUID_LEMONADE}, - {"milk", FLUID_MILK}, - {"mana", FLUID_MANA}, - {"life", FLUID_LIFE}, - {"oil", FLUID_OIL}, - {"urine", FLUID_URINE}, - {"coconut", FLUID_COCONUTMILK}, - {"wine", FLUID_WINE}, - {"mud", FLUID_MUD}, - {"fruitjuice", FLUID_FRUITJUICE}, - {"lava", FLUID_LAVA}, - {"rum", FLUID_RUM}, - {"swamp", FLUID_SWAMP}, - {"tea", FLUID_TEA}, - {"mead", FLUID_MEAD}, -}; - Items::Items() { - items.reserve(30000); - nameToItems.reserve(30000); + items.reserve(6000); + nameToItems.reserve(6000); } void Items::clear() { items.clear(); - reverseItemMap.clear(); nameToItems.clear(); } bool Items::reload() { clear(); - loadFromOtb("data/items/items.otb"); - - if (!loadFromXml()) { + if (!loadItems()) { return false; } g_moveEvents->reload(); - g_weapons->reload(); - g_weapons->loadDefaults(); return true; } -constexpr auto OTBI = OTB::Identifier{{'O','T', 'B', 'I'}}; - -bool Items::loadFromOtb(const std::string& file) +bool Items::loadItems() { - OTB::Loader loader{file, OTBI}; - - auto& root = loader.parseTree(); - - PropStream props; - if (loader.getProps(root, props)) { - //4 byte flags - //attributes - //0x01 = version data - uint32_t flags; - if (!props.read(flags)) { - return false; - } - - uint8_t attr; - if (!props.read(attr)) { - return false; - } - - if (attr == ROOT_ATTR_VERSION) { - uint16_t datalen; - if (!props.read(datalen)) { - return false; - } - - if (datalen != sizeof(VERSIONINFO)) { - return false; - } - - VERSIONINFO vi; - if (!props.read(vi)) { - return false; - } - - majorVersion = vi.dwMajorVersion; //items otb format file version - minorVersion = vi.dwMinorVersion; //client version - buildNumber = vi.dwBuildNumber; //revision - } - } - - if (majorVersion == 0xFFFFFFFF) { - std::cout << "[Warning - Items::loadFromOtb] items.otb using generic client version." << std::endl; - } else if (majorVersion != 3) { - std::cout << "Old version detected, a newer version of items.otb is required." << std::endl; - return false; - } else if (minorVersion < CLIENT_VERSION_1098) { - std::cout << "A newer version of items.otb is required." << std::endl; + ScriptReader script; + if (!script.open("data/items/items.srv")) { return false; } - for (auto& itemNode : root.children) { - PropStream stream; - if (!loader.getProps(itemNode, stream)) { + std::string identifier; + uint16_t id = 0; + while (true) { + script.nextToken(); + if (script.Token == ENDOFFILE) { + break; + } + + if (script.Token != IDENTIFIER) { + script.error("Identifier expected"); return false; } - uint32_t flags; - if (!stream.read(flags)) { - return false; - } + identifier = script.getIdentifier(); + script.readSymbol('='); - uint16_t serverId = 0; - uint16_t clientId = 0; - uint16_t speed = 0; - uint16_t wareId = 0; - uint8_t lightLevel = 0; - uint8_t lightColor = 0; - uint8_t alwaysOnTopOrder = 0; + if (identifier == "typeid") { + id = script.readNumber(); + if (id >= items.size()) { + items.resize(id + 1); + } - uint8_t attrib; - while (stream.read(attrib)) { - uint16_t datalen; - if (!stream.read(datalen)) { + if (items[id].id) { + script.error("item type already defined"); return false; } - switch (attrib) { - case ITEM_ATTR_SERVERID: { - if (datalen != sizeof(uint16_t)) { - return false; + items[id].id = id; + } else if (identifier == "name") { + items[id].name = script.readString(); + } else if (identifier == "description") { + items[id].description = script.readString(); + } else if (identifier == "flags") { + script.readSymbol('{'); + while (true) { + while (true) { + script.nextToken(); + if (script.Token == SPECIAL) { + break; } - if (!stream.read(serverId)) { + identifier = script.getIdentifier(); + + if (identifier == "bank") { + items[id].group = ITEM_GROUP_GROUND; + } else if (identifier == "clip") { + items[id].alwaysOnTop = true; + items[id].alwaysOnTopOrder = 1; + } else if (identifier == "bottom") { + items[id].alwaysOnTop = true; + items[id].alwaysOnTopOrder = 2; + } else if (identifier == "top") { + items[id].alwaysOnTop = true; + items[id].alwaysOnTopOrder = 3; + } else if (identifier == "container") { + items[id].type = ITEM_TYPE_CONTAINER; + } else if (identifier == "chest") { + items[id].type = ITEM_TYPE_CHEST; + } else if (identifier == "cumulative") { + items[id].stackable = true; + } else if (identifier == "changeuse") { + items[id].changeUse = true; + } else if (identifier == "forceuse") { + items[id].forceUse = true; + } else if (identifier == "key") { + items[id].type = ITEM_TYPE_KEY; + items[id].group = ITEM_GROUP_KEY; + } else if (identifier == "door") { + items[id].type = ITEM_TYPE_DOOR; + } else if (identifier == "bed") { + items[id].type = ITEM_TYPE_BED; + } else if (identifier == "rune") { + items[id].type = ITEM_TYPE_RUNE; + } else if (identifier == "depot") { + items[id].type = ITEM_TYPE_DEPOT; + } else if (identifier == "mailbox") { + items[id].type = ITEM_TYPE_MAILBOX; + } else if (identifier == "allowdistread") { + items[id].allowDistRead = true; + } else if (identifier == "text") { + items[id].canReadText = true; + } else if (identifier == "write") { + items[id].canWriteText = true; + } else if (identifier == "writeonce") { + items[id].canWriteText = true; + items[id].writeOnceItemId = id; + } else if (identifier == "fluidcontainer") { + items[id].group = ITEM_GROUP_FLUID; + } else if (identifier == "splash") { + items[id].group = ITEM_GROUP_SPLASH; + } else if (identifier == "unpass") { + items[id].blockSolid = true; + } else if (identifier == "unmove") { + items[id].moveable = false; + } else if (identifier == "unthrow") { + items[id].blockProjectile = true; + } else if (identifier == "unlay") { + items[id].allowPickupable = false; + } else if (identifier == "avoid") { + items[id].blockPathFind = true; + } else if (identifier == "magicfield") { + items[id].type = ITEM_TYPE_MAGICFIELD; + items[id].group = ITEM_GROUP_MAGICFIELD; + } else if (identifier == "take") { + items[id].pickupable = true; + } else if (identifier == "hang") { + items[id].isHangable = true; + } else if (identifier == "hooksouth") { + items[id].isHorizontal = true; + } else if (identifier == "hookeast") { + items[id].isVertical = true; + } else if (identifier == "rotate") { + items[id].rotatable = true; + } else if (identifier == "destroy") { + items[id].destroy = true; + } else if (identifier == "corpse") { + items[id].corpse = true; + } else if (identifier == "expire") { + items[id].stopTime = false; + } else if (identifier == "expirestop") { + items[id].stopTime = true; + } else if (identifier == "weapon") { + items[id].group = ITEM_GROUP_WEAPON; + } else if (identifier == "shield") { + items[id].weaponType = WEAPON_SHIELD; + } else if (identifier == "distance") { + items[id].weaponType = WEAPON_DISTANCE; + } else if (identifier == "wand") { + items[id].weaponType = WEAPON_WAND; + } else if (identifier == "ammo") { + items[id].weaponType = WEAPON_AMMO; + } else if (identifier == "armor") { + items[id].group = ITEM_GROUP_ARMOR; + } else if (identifier == "height") { + items[id].hasHeight = true; + } else if (identifier == "disguise") { + items[id].disguise = true; + } else if (identifier == "showdetail") { + items[id].showDuration = true; + } else if (identifier == "noreplace") { + items[id].replaceable = false; + } else if (identifier == "collisionevent") { + items[id].collisionEvent = true; + } else if (identifier == "separationevent") { + items[id].separationEvent = true; + } else if (identifier == "useevent") { + items[id].useEvent = true; + } else if (identifier == "distuse") { + items[id].distUse = true; + } else if (identifier == "multiuse") { + items[id].multiUseEvent = true; + } else { + script.error("Unknown flag"); return false; } + } - if (serverId > 30000 && serverId < 30100) { - serverId -= 30000; - } + if (script.getSpecial() == '}') { break; } - case ITEM_ATTR_CLIENTID: { - if (datalen != sizeof(uint16_t)) { - return false; - } - - if (!stream.read(clientId)) { - return false; - } - break; - } - - case ITEM_ATTR_SPEED: { - if (datalen != sizeof(uint16_t)) { - return false; - } - - if (!stream.read(speed)) { - return false; - } - break; - } - - case ITEM_ATTR_LIGHT2: { - if (datalen != sizeof(lightBlock2)) { - return false; - } - - lightBlock2 lb2; - if (!stream.read(lb2)) { - return false; - } - - lightLevel = static_cast(lb2.lightLevel); - lightColor = static_cast(lb2.lightColor); - break; - } - - case ITEM_ATTR_TOPORDER: { - if (datalen != sizeof(uint8_t)) { - return false; - } - - if (!stream.read(alwaysOnTopOrder)) { - return false; - } - break; - } - - case ITEM_ATTR_WAREID: { - if (datalen != sizeof(uint16_t)) { - return false; - } - - if (!stream.read(wareId)) { - return false; - } - break; - } - - default: { - //skip unknown attributes - if (!stream.skip(datalen)) { - return false; - } - break; + if (script.Token != SPECIAL || script.getSpecial() != ',') { + continue; } } - } + } else if (identifier == "attributes") { + script.readSymbol('{'); + while (true) { + while (true) { + script.nextToken(); + if (script.Token == SPECIAL) { + break; + } - reverseItemMap.emplace(clientId, serverId); + identifier = script.getIdentifier(); + script.readSymbol('='); - // store the found item - if (serverId >= items.size()) { - items.resize(serverId + 1); - } - ItemType& iType = items[serverId]; + if (identifier == "waypoints") { + items[id].speed = script.readNumber(); + } else if (identifier == "capacity") { + items[id].maxItems = script.readNumber(); + } else if (identifier == "changetarget") { + items[id].transformToOnUse = script.readNumber(); + } else if (identifier == "nutrition") { + items[id].nutrition = script.readNumber(); + } else if (identifier == "maxlength") { + items[id].maxTextLen = script.readNumber(); + } else if (identifier == "fluidsource") { + items[id].fluidSource = getFluidType(script.readIdentifier()); + } else if (identifier == "avoiddamagetypes") { + items[id].combatType = getCombatType(script.readIdentifier()); + } else if (identifier == "damagetype") { + items[id].damageType = getCombatType(script.readIdentifier()); + } else if (identifier == "attackstrength") { + items[id].attackStrength = script.readNumber(); + } else if (identifier == "attackvariation") { + items[id].attackVariation = script.readNumber(); + } else if (identifier == "manaconsumption") { + items[id].manaConsumption = script.readNumber(); + } else if (identifier == "minimumlevel") { + items[id].minReqLevel = script.readNumber(); + items[id].wieldInfo |= WIELDINFO_LEVEL; + } else if (identifier == "vocations") { + int32_t vocations = script.readNumber(); + items[id].vocations = vocations; - iType.group = static_cast(itemNode.type); - switch (itemNode.type) { - case ITEM_GROUP_CONTAINER: - iType.type = ITEM_TYPE_CONTAINER; - break; - case ITEM_GROUP_DOOR: - //not used - iType.type = ITEM_TYPE_DOOR; - break; - case ITEM_GROUP_MAGICFIELD: - //not used - iType.type = ITEM_TYPE_MAGICFIELD; - break; - case ITEM_GROUP_TELEPORT: - //not used - iType.type = ITEM_TYPE_TELEPORT; - break; - case ITEM_GROUP_NONE: - case ITEM_GROUP_GROUND: - case ITEM_GROUP_SPLASH: - case ITEM_GROUP_FLUID: - case ITEM_GROUP_CHARGES: - case ITEM_GROUP_DEPRECATED: - break; - default: - return false; - } + std::list vocationStringList; - iType.blockSolid = hasBitSet(FLAG_BLOCK_SOLID, flags); - iType.blockProjectile = hasBitSet(FLAG_BLOCK_PROJECTILE, flags); - iType.blockPathFind = hasBitSet(FLAG_BLOCK_PATHFIND, flags); - iType.hasHeight = hasBitSet(FLAG_HAS_HEIGHT, flags); - iType.useable = hasBitSet(FLAG_USEABLE, flags); - iType.pickupable = hasBitSet(FLAG_PICKUPABLE, flags); - iType.moveable = hasBitSet(FLAG_MOVEABLE, flags); - iType.stackable = hasBitSet(FLAG_STACKABLE, flags); - - iType.alwaysOnTop = hasBitSet(FLAG_ALWAYSONTOP, flags); - iType.isVertical = hasBitSet(FLAG_VERTICAL, flags); - iType.isHorizontal = hasBitSet(FLAG_HORIZONTAL, flags); - iType.isHangable = hasBitSet(FLAG_HANGABLE, flags); - iType.allowDistRead = hasBitSet(FLAG_ALLOWDISTREAD, flags); - iType.rotatable = hasBitSet(FLAG_ROTATABLE, flags); - iType.canReadText = hasBitSet(FLAG_READABLE, flags); - iType.lookThrough = hasBitSet(FLAG_LOOKTHROUGH, flags); - iType.isAnimation = hasBitSet(FLAG_ANIMATION, flags); - // iType.walkStack = !hasBitSet(FLAG_FULLTILE, flags); - iType.forceUse = hasBitSet(FLAG_FORCEUSE, flags); - - iType.id = serverId; - iType.clientId = clientId; - iType.speed = speed; - iType.lightLevel = lightLevel; - iType.lightColor = lightColor; - iType.wareId = wareId; - iType.alwaysOnTopOrder = alwaysOnTopOrder; - } - - items.shrink_to_fit(); - return true; -} - -bool Items::loadFromXml() -{ - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file("data/items/items.xml"); - if (!result) { - printXMLError("Error - Items::loadFromXml", "data/items/items.xml", result); - return false; - } - - for (auto itemNode : doc.child("items").children()) { - pugi::xml_attribute idAttribute = itemNode.attribute("id"); - if (idAttribute) { - parseItemNode(itemNode, pugi::cast(idAttribute.value())); - continue; - } - - pugi::xml_attribute fromIdAttribute = itemNode.attribute("fromid"); - if (!fromIdAttribute) { - std::cout << "[Warning - Items::loadFromXml] No item id found" << std::endl; - continue; - } - - pugi::xml_attribute toIdAttribute = itemNode.attribute("toid"); - if (!toIdAttribute) { - std::cout << "[Warning - Items::loadFromXml] fromid (" << fromIdAttribute.value() << ") without toid" << std::endl; - continue; - } - - uint16_t id = pugi::cast(fromIdAttribute.value()); - uint16_t toId = pugi::cast(toIdAttribute.value()); - while (id <= toId) { - parseItemNode(itemNode, id++); - } - } - - buildInventoryList(); - return true; -} - -void Items::buildInventoryList() -{ - inventory.reserve(items.size()); - for (const auto& type: items) { - if (type.weaponType != WEAPON_NONE || type.ammoType != AMMO_NONE || - type.attack != 0 || type.defense != 0 || - type.extraDefense != 0 || type.armor != 0 || - type.slotPosition & SLOTP_NECKLACE || - type.slotPosition & SLOTP_RING || - type.slotPosition & SLOTP_AMMO || - type.slotPosition & SLOTP_FEET || - type.slotPosition & SLOTP_HEAD || - type.slotPosition & SLOTP_ARMOR || - type.slotPosition & SLOTP_LEGS) - { - inventory.push_back(type.clientId); - } - } - inventory.shrink_to_fit(); - std::sort(inventory.begin(), inventory.end()); -} - -void Items::parseItemNode(const pugi::xml_node& itemNode, uint16_t id) -{ - if (id > 30000 && id < 30100) { - id -= 30000; - - if (id >= items.size()) { - items.resize(id + 1); - } - ItemType& iType = items[id]; - iType.id = id; - } - - ItemType& it = getItemType(id); - if (it.id == 0) { - return; - } - - it.name = itemNode.attribute("name").as_string(); - - nameToItems.insert({ asLowerCaseString(it.name), id }); - - pugi::xml_attribute articleAttribute = itemNode.attribute("article"); - if (articleAttribute) { - it.article = articleAttribute.as_string(); - } - - pugi::xml_attribute pluralAttribute = itemNode.attribute("plural"); - if (pluralAttribute) { - it.pluralName = pluralAttribute.as_string(); - } - - Abilities& abilities = it.getAbilities(); - - for (auto attributeNode : itemNode.children()) { - pugi::xml_attribute keyAttribute = attributeNode.attribute("key"); - if (!keyAttribute) { - continue; - } - - pugi::xml_attribute valueAttribute = attributeNode.attribute("value"); - if (!valueAttribute) { - continue; - } - - std::string tmpStrValue = asLowerCaseString(keyAttribute.as_string()); - auto parseAttribute = ItemParseAttributesMap.find(tmpStrValue); - if (parseAttribute != ItemParseAttributesMap.end()) { - ItemParseAttributes_t parseType = parseAttribute->second; - switch (parseType) { - case ITEM_PARSE_TYPE: { - tmpStrValue = asLowerCaseString(valueAttribute.as_string()); - auto it2 = ItemTypesMap.find(tmpStrValue); - if (it2 != ItemTypesMap.end()) { - it.type = it2->second; - if (it.type == ITEM_TYPE_CONTAINER) { - it.group = ITEM_GROUP_CONTAINER; + if (hasBitSet(VOCATION_SORCERER, vocations)) { + vocationStringList.push_back("sorcerer"); } - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown type: " << valueAttribute.as_string() << std::endl; - } - break; - } - case ITEM_PARSE_DESCRIPTION: { - it.description = valueAttribute.as_string(); - break; - } + if (hasBitSet(VOCATION_DRUID, vocations)) { + vocationStringList.push_back("druid"); + } - case ITEM_PARSE_RUNESPELLNAME: { - it.runeSpellName = valueAttribute.as_string(); - break; - } + if (hasBitSet(VOCATION_PALADIN, vocations)) { + vocationStringList.push_back("paladin"); + } - case ITEM_PARSE_WEIGHT: { - it.weight = pugi::cast(valueAttribute.value()); - break; - } + if (hasBitSet(VOCATION_KNIGHT, vocations)) { + vocationStringList.push_back("knight"); + } - case ITEM_PARSE_SHOWCOUNT: { - it.showCount = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_ARMOR: { - it.armor = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_DEFENSE: { - it.defense = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_EXTRADEF: { - it.extraDefense = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ATTACK: { - it.attack = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ROTATETO: { - it.rotateTo = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MOVEABLE: { - it.moveable = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_BLOCKPROJECTILE: { - it.blockProjectile = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_PICKUPABLE: { - it.allowPickupable = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_FORCESERIALIZE: { - it.forceSerialize = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_FLOORCHANGE: { - tmpStrValue = asLowerCaseString(valueAttribute.as_string()); - auto it2 = TileStatesMap.find(tmpStrValue); - if (it2 != TileStatesMap.end()) { - it.floorChange |= it2->second; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown floorChange: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_CORPSETYPE: { - tmpStrValue = asLowerCaseString(valueAttribute.as_string()); - auto it2 = RaceTypesMap.find(tmpStrValue); - if (it2 != RaceTypesMap.end()) { - it.corpseType = it2->second; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown corpseType: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_CONTAINERSIZE: { - it.maxItems = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_FLUIDSOURCE: { - tmpStrValue = asLowerCaseString(valueAttribute.as_string()); - auto it2 = FluidTypesMap.find(tmpStrValue); - if (it2 != FluidTypesMap.end()) { - it.fluidSource = it2->second; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown fluidSource: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_READABLE: { - it.canReadText = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_WRITEABLE: { - it.canWriteText = valueAttribute.as_bool(); - it.canReadText = it.canWriteText; - break; - } - - case ITEM_PARSE_MAXTEXTLEN: { - it.maxTextLen = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_WRITEONCEITEMID: { - it.writeOnceItemId = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_WEAPONTYPE: { - tmpStrValue = asLowerCaseString(valueAttribute.as_string()); - auto it2 = WeaponTypesMap.find(tmpStrValue); - if (it2 != WeaponTypesMap.end()) { - it.weaponType = it2->second; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown weaponType: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_SLOTTYPE: { - tmpStrValue = asLowerCaseString(valueAttribute.as_string()); - if (tmpStrValue == "head") { - it.slotPosition |= SLOTP_HEAD; - } else if (tmpStrValue == "body") { - it.slotPosition |= SLOTP_ARMOR; - } else if (tmpStrValue == "legs") { - it.slotPosition |= SLOTP_LEGS; - } else if (tmpStrValue == "feet") { - it.slotPosition |= SLOTP_FEET; - } else if (tmpStrValue == "backpack") { - it.slotPosition |= SLOTP_BACKPACK; - } else if (tmpStrValue == "two-handed") { - it.slotPosition |= SLOTP_TWO_HAND; - } else if (tmpStrValue == "right-hand") { - it.slotPosition &= ~SLOTP_LEFT; - } else if (tmpStrValue == "left-hand") { - it.slotPosition &= ~SLOTP_RIGHT; - } else if (tmpStrValue == "necklace") { - it.slotPosition |= SLOTP_NECKLACE; - } else if (tmpStrValue == "ring") { - it.slotPosition |= SLOTP_RING; - } else if (tmpStrValue == "ammo") { - it.slotPosition |= SLOTP_AMMO; - } else if (tmpStrValue == "hand") { - it.slotPosition |= SLOTP_HAND; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown slotType: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_AMMOTYPE: { - it.ammoType = getAmmoType(asLowerCaseString(valueAttribute.as_string())); - if (it.ammoType == AMMO_NONE) { - std::cout << "[Warning - Items::parseItemNode] Unknown ammoType: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_SHOOTTYPE: { - ShootType_t shoot = getShootType(asLowerCaseString(valueAttribute.as_string())); - if (shoot != CONST_ANI_NONE) { - it.shootType = shoot; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown shootType: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_EFFECT: { - MagicEffectClasses effect = getMagicEffect(asLowerCaseString(valueAttribute.as_string())); - if (effect != CONST_ME_NONE) { - it.magicEffect = effect; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown effect: " << valueAttribute.as_string() << std::endl; - } - break; - } - - case ITEM_PARSE_RANGE: { - it.shootRange = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_STOPDURATION: { - it.stopTime = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_DECAYTO: { - it.decayTo = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_TRANSFORMEQUIPTO: { - it.transformEquipTo = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_TRANSFORMDEEQUIPTO: { - it.transformDeEquipTo = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_DURATION: { - it.decayTime = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SHOWDURATION: { - it.showDuration = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_CHARGES: { - it.charges = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SHOWCHARGES: { - it.showCharges = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_SHOWATTRIBUTES: { - it.showAttributes = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_HITCHANCE: { - it.hitChance = std::min(100, std::max(-100, pugi::cast(valueAttribute.value()))); - break; - } - - case ITEM_PARSE_MAXHITCHANCE: { - it.maxHitChance = std::min(100, pugi::cast(valueAttribute.value())); - break; - } - - case ITEM_PARSE_INVISIBLE: { - abilities.invisible = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_SPEED: { - abilities.speed = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_HEALTHGAIN: { - abilities.regeneration = true; - abilities.healthGain = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_HEALTHTICKS: { - abilities.regeneration = true; - abilities.healthTicks = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MANAGAIN: { - abilities.regeneration = true; - abilities.manaGain = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MANATICKS: { - abilities.regeneration = true; - abilities.manaTicks = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MANASHIELD: { - abilities.manaShield = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_SKILLSWORD: { - abilities.skills[SKILL_SWORD] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SKILLAXE: { - abilities.skills[SKILL_AXE] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SKILLCLUB: { - abilities.skills[SKILL_CLUB] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SKILLDIST: { - abilities.skills[SKILL_DISTANCE] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SKILLFISH: { - abilities.skills[SKILL_FISHING] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SKILLSHIELD: { - abilities.skills[SKILL_SHIELD] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SKILLFIST: { - abilities.skills[SKILL_FIST] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_CRITICALHITAMOUNT: { - abilities.specialSkills[SPECIALSKILL_CRITICALHITAMOUNT] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_CRITICALHITCHANCE: { - abilities.specialSkills[SPECIALSKILL_CRITICALHITCHANCE] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MANALEECHAMOUNT: { - abilities.specialSkills[SPECIALSKILL_MANALEECHAMOUNT] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MANALEECHCHANCE: { - abilities.specialSkills[SPECIALSKILL_MANALEECHCHANCE] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_LIFELEECHAMOUNT: { - abilities.specialSkills[SPECIALSKILL_LIFELEECHAMOUNT] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_LIFELEECHCHANCE: { - abilities.specialSkills[SPECIALSKILL_LIFELEECHCHANCE] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MAXHITPOINTS: { - abilities.stats[STAT_MAXHITPOINTS] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MAXHITPOINTSPERCENT: { - abilities.statsPercent[STAT_MAXHITPOINTS] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MAXMANAPOINTS: { - abilities.stats[STAT_MAXMANAPOINTS] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MAXMANAPOINTSPERCENT: { - abilities.statsPercent[STAT_MAXMANAPOINTS] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MAGICPOINTS: { - abilities.stats[STAT_MAGICPOINTS] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MAGICPOINTSPERCENT: { - abilities.statsPercent[STAT_MAGICPOINTS] = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_FIELDABSORBPERCENTENERGY: { - abilities.fieldAbsorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_FIELDABSORBPERCENTFIRE: { - abilities.fieldAbsorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_FIELDABSORBPERCENTPOISON: { - abilities.fieldAbsorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTALL: { - int16_t value = pugi::cast(valueAttribute.value()); - for (auto& i : abilities.absorbPercent) { - i += value; - } - break; - } - - case ITEM_PARSE_ABSORBPERCENTELEMENTS: { - int16_t value = pugi::cast(valueAttribute.value()); - abilities.absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += value; - break; - } - - case ITEM_PARSE_ABSORBPERCENTMAGIC: { - int16_t value = pugi::cast(valueAttribute.value()); - abilities.absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_HOLYDAMAGE)] += value; - abilities.absorbPercent[combatTypeToIndex(COMBAT_DEATHDAMAGE)] += value; - break; - } - - case ITEM_PARSE_ABSORBPERCENTENERGY: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTFIRE: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTPOISON: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTICE: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_ICEDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTHOLY: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_HOLYDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTDEATH: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_DEATHDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTLIFEDRAIN: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_LIFEDRAIN)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTMANADRAIN: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_MANADRAIN)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTDROWN: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_DROWNDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTPHYSICAL: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_PHYSICALDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTHEALING: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_HEALING)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ABSORBPERCENTUNDEFINED: { - abilities.absorbPercent[combatTypeToIndex(COMBAT_UNDEFINEDDAMAGE)] += pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_SUPPRESSDRUNK: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_DRUNK; - } - break; - } - - case ITEM_PARSE_SUPPRESSENERGY: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_ENERGY; - } - break; - } - - case ITEM_PARSE_SUPPRESSFIRE: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_FIRE; - } - break; - } - - case ITEM_PARSE_SUPPRESSPOISON: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_POISON; - } - break; - } - - case ITEM_PARSE_SUPPRESSDROWN: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_DROWN; - } - break; - } - - case ITEM_PARSE_SUPPRESSPHYSICAL: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_BLEEDING; - } - break; - } - - case ITEM_PARSE_SUPPRESSFREEZE: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_FREEZING; - } - break; - } - - case ITEM_PARSE_SUPPRESSDAZZLE: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_DAZZLED; - } - break; - } - - case ITEM_PARSE_SUPPRESSCURSE: { - if (valueAttribute.as_bool()) { - abilities.conditionSuppressions |= CONDITION_CURSED; - } - break; - } - - case ITEM_PARSE_FIELD: { - it.group = ITEM_GROUP_MAGICFIELD; - it.type = ITEM_TYPE_MAGICFIELD; - - CombatType_t combatType = COMBAT_NONE; - ConditionDamage* conditionDamage = nullptr; - - tmpStrValue = asLowerCaseString(valueAttribute.as_string()); - if (tmpStrValue == "fire") { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FIRE); - combatType = COMBAT_FIREDAMAGE; - } else if (tmpStrValue == "energy") { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_ENERGY); - combatType = COMBAT_ENERGYDAMAGE; - } else if (tmpStrValue == "poison") { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_POISON); - combatType = COMBAT_EARTHDAMAGE; - } else if (tmpStrValue == "drown") { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_DROWN); - combatType = COMBAT_DROWNDAMAGE; - } else if (tmpStrValue == "physical") { - conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_BLEEDING); - combatType = COMBAT_PHYSICALDAMAGE; - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown field value: " << valueAttribute.as_string() << std::endl; - } - - if (combatType != COMBAT_NONE) { - it.combatType = combatType; - it.conditionDamage.reset(conditionDamage); - - uint32_t ticks = 0; - int32_t start = 0; - int32_t count = 1; - for (auto subAttributeNode : attributeNode.children()) { - pugi::xml_attribute subKeyAttribute = subAttributeNode.attribute("key"); - if (!subKeyAttribute) { - continue; - } - - pugi::xml_attribute subValueAttribute = subAttributeNode.attribute("value"); - if (!subValueAttribute) { - continue; - } - - tmpStrValue = asLowerCaseString(subKeyAttribute.as_string()); - if (tmpStrValue == "ticks") { - ticks = pugi::cast(subValueAttribute.value()); - } else if (tmpStrValue == "count") { - count = std::max(1, pugi::cast(subValueAttribute.value())); - } else if (tmpStrValue == "start") { - start = std::max(0, pugi::cast(subValueAttribute.value())); - } else if (tmpStrValue == "damage") { - int32_t damage = -pugi::cast(subValueAttribute.value()); - if (start > 0) { - std::list damageList; - ConditionDamage::generateDamageList(damage, start, damageList); - for (int32_t damageValue : damageList) { - conditionDamage->addDamage(1, ticks, -damageValue); - } - - start = 0; + std::string vocationString; + for (const std::string& str : vocationStringList) { + if (!vocationString.empty()) { + if (str != vocationStringList.back()) { + vocationString.push_back(','); + vocationString.push_back(' '); } else { - conditionDamage->addDamage(count, ticks, damage); + vocationString += " and "; } } + + vocationString += str; + vocationString.push_back('s'); } - conditionDamage->setParam(CONDITION_PARAM_FIELD, 1); - - if (conditionDamage->getTotalDamage() > 0) { - conditionDamage->setParam(CONDITION_PARAM_FORCEUPDATE, 1); + items[id].wieldInfo |= WIELDINFO_VOCREQ; + items[id].vocationString = vocationString; + } else if (identifier == "weaponspecialeffect") { + items[id].weaponSpecialEffect = script.readNumber(); + } else if (identifier == "beddirection") { + items[id].bedPartnerDir = getDirection(script.readIdentifier()); + } else if (identifier == "bedtarget") { + items[id].transformToOnUse = script.readNumber(); + } else if (identifier == "bedfree") { + items[id].transformToFree = script.readNumber(); + } else if (identifier == "weight") { + items[id].weight = script.readNumber(); + } else if (identifier == "rotatetarget") { + items[id].rotateTo = script.readNumber(); + } else if (identifier == "destroytarget") { + items[id].destroyTarget = script.readNumber(); + } else if (identifier == "slottype") { + identifier = asLowerCaseString(script.readIdentifier()); + if (identifier == "head") { + items[id].slotPosition |= SLOTP_HEAD; + } else if (identifier == "body") { + items[id].slotPosition |= SLOTP_ARMOR; + } else if (identifier == "legs") { + items[id].slotPosition |= SLOTP_LEGS; + } else if (identifier == "feet") { + items[id].slotPosition |= SLOTP_FEET; + } else if (identifier == "backpack") { + items[id].slotPosition |= SLOTP_BACKPACK; + } else if (identifier == "twohanded") { + items[id].slotPosition |= SLOTP_TWO_HAND; + } else if (identifier == "righthand") { + items[id].slotPosition &= ~SLOTP_LEFT; + } else if (identifier == "lefthand") { + items[id].slotPosition &= ~SLOTP_RIGHT; + } else if (identifier == "necklace") { + items[id].slotPosition |= SLOTP_NECKLACE; + } else if (identifier == "ring") { + items[id].slotPosition |= SLOTP_RING; + } else if (identifier == "ammo") { + items[id].slotPosition |= SLOTP_AMMO; + } else if (identifier == "hand") { + items[id].slotPosition |= SLOTP_HAND; + } else { + script.error("Unknown slot position"); + return false; } + } else if (identifier == "speedboost") { + items[id].getAbilities().speed = script.readNumber(); + } else if (identifier == "fistboost") { + items[id].getAbilities().skills[SKILL_FIST] = script.readNumber(); + } else if (identifier == "swordboost") { + items[id].getAbilities().skills[SKILL_SWORD] = script.readNumber(); + } else if (identifier == "clubboost") { + items[id].getAbilities().skills[SKILL_CLUB] = script.readNumber(); + } else if (identifier == "axeboost") { + items[id].getAbilities().skills[SKILL_AXE] = script.readNumber(); + } else if (identifier == "shieldboost") { + items[id].getAbilities().skills[SKILL_SHIELD] = script.readNumber(); + } else if (identifier == "distanceboost") { + items[id].getAbilities().skills[SKILL_DISTANCE] = script.readNumber(); + } else if (identifier == "magicboost") { + items[id].getAbilities().stats[STAT_MAGICPOINTS] = script.readNumber(); + } else if (identifier == "percenthp") { + items[id].getAbilities().statsPercent[STAT_MAXHITPOINTS] = script.readNumber(); + } else if (identifier == "percentmp") { + items[id].getAbilities().statsPercent[STAT_MAXMANAPOINTS] = script.readNumber(); + } else if (identifier == "suppressdrunk") { + if (script.readNumber()) { + items[id].getAbilities().conditionSuppressions |= CONDITION_DRUNK; + } + } else if (identifier == "invisible") { + if (script.readNumber()) { + items[id].getAbilities().invisible = true; + } + } else if (identifier == "manashield") { + if (script.readNumber()) { + items[id].getAbilities().manaShield = true; + } + } else if (identifier == "healthticks") { + Abilities& abilities = items[id].getAbilities(); + abilities.regeneration = true; + abilities.healthTicks = script.readNumber(); + } else if (identifier == "healthgain") { + Abilities& abilities = items[id].getAbilities(); + abilities.regeneration = true; + abilities.healthGain = script.readNumber(); + } else if (identifier == "manaticks") { + Abilities& abilities = items[id].getAbilities(); + abilities.regeneration = true; + abilities.manaTicks = script.readNumber(); + } else if (identifier == "managain") { + Abilities& abilities = items[id].getAbilities(); + abilities.regeneration = true; + abilities.manaGain = script.readNumber(); + } else if (identifier == "absorbmagic") { + int32_t percent = script.readNumber(); + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += percent; + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += percent; + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += percent; + } else if (identifier == "absorbenergy") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_ENERGYDAMAGE)] += script.readNumber(); + } else if (identifier == "absorbfire") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += script.readNumber(); + } else if (identifier == "absorbpoison") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_EARTHDAMAGE)] += script.readNumber(); + } else if (identifier == "absorbdrown") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_DROWNDAMAGE)] += script.readNumber(); + } else if (identifier == "absorblifedrain") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_LIFEDRAIN)] += script.readNumber(); + } else if (identifier == "absorbmanadrain") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_MANADRAIN)] += script.readNumber(); + } else if (identifier == "absorbphysical") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_PHYSICALDAMAGE)] += script.readNumber(); + } else if (identifier == "absorbhealing") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_HEALING)] += script.readNumber(); + } else if (identifier == "absorbundefined") { + items[id].getAbilities().absorbPercent[combatTypeToIndex(COMBAT_UNDEFINEDDAMAGE)] += script.readNumber(); + } else if (identifier == "absorbfirefield") { + items[id].getAbilities().fieldAbsorbPercent[combatTypeToIndex(COMBAT_FIREDAMAGE)] += static_cast(script.readNumber()); + } else if (identifier == "brightness") { + items[id].lightLevel = script.readNumber(); + } else if (identifier == "lightcolor") { + items[id].lightColor = script.readNumber(); + } else if (identifier == "totalexpiretime") { + items[id].decayTime = script.readNumber(); + } else if (identifier == "expiretarget") { + items[id].decayTo = script.readNumber(); + } else if (identifier == "totaluses") { + items[id].charges = script.readNumber(); + } else if (identifier == "weapontype") { + identifier = script.readIdentifier(); + if (identifier == "sword") { + items[id].weaponType = WEAPON_SWORD; + } else if (identifier == "club") { + items[id].weaponType = WEAPON_CLUB; + } else if (identifier == "axe") { + items[id].weaponType = WEAPON_AXE; + } else if (identifier == "shield") { + items[id].weaponType = WEAPON_SHIELD; + } else if (identifier == "distance") { + items[id].weaponType = WEAPON_DISTANCE; + } else if (identifier == "wand") { + items[id].weaponType = WEAPON_WAND; + } else if (identifier == "ammunition") { + items[id].weaponType = WEAPON_AMMO; + } else { + script.error("Unknown weapon type"); + return false; + } + } else if (identifier == "attack") { + items[id].attack = script.readNumber(); + } else if (identifier == "defense") { + items[id].defense = script.readNumber(); + } else if (identifier == "range") { + items[id].shootRange = static_cast(script.readNumber()); + } else if (identifier == "ammotype") { + items[id].ammoType = getAmmoType(script.readIdentifier()); + if (items[id].ammoType == AMMO_NONE) { + script.error("Unknown ammo type"); + return false; + } + } else if (identifier == "missileeffect") { + items[id].shootType = static_cast(script.readNumber()); + } else if (identifier == "fragility") { + items[id].fragility = script.readNumber(); + } else if (identifier == "armorvalue") { + items[id].armor = script.readNumber(); + } else if (identifier == "disguisetarget") { + items[id].disguiseId = script.readNumber(); + } else if (identifier == "equiptarget") { + items[id].transformEquipTo = script.readNumber(); + } else if (identifier == "deequiptarget") { + items[id].transformDeEquipTo = script.readNumber(); + } else { + script.error("Unknown attribute"); + return false; } + } + + if (script.getSpecial() == '}') { break; } - case ITEM_PARSE_REPLACEABLE: { - it.replaceable = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_PARTNERDIRECTION: { - it.bedPartnerDir = getDirection(valueAttribute.as_string()); - break; - } - - case ITEM_PARSE_LEVELDOOR: { - it.levelDoor = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_MALETRANSFORMTO: { - uint16_t value = pugi::cast(valueAttribute.value()); - it.transformToOnUse[PLAYERSEX_MALE] = value; - ItemType& other = getItemType(value); - if (other.transformToFree == 0) { - other.transformToFree = it.id; - } - - if (it.transformToOnUse[PLAYERSEX_FEMALE] == 0) { - it.transformToOnUse[PLAYERSEX_FEMALE] = value; - } - break; - } - - case ITEM_PARSE_FEMALETRANSFORMTO: { - uint16_t value = pugi::cast(valueAttribute.value()); - it.transformToOnUse[PLAYERSEX_FEMALE] = value; - - ItemType& other = getItemType(value); - if (other.transformToFree == 0) { - other.transformToFree = it.id; - } - - if (it.transformToOnUse[PLAYERSEX_MALE] == 0) { - it.transformToOnUse[PLAYERSEX_MALE] = value; - } - break; - } - - case ITEM_PARSE_TRANSFORMTO: { - it.transformToFree = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_DESTROYTO: { - it.destroyTo = pugi::cast(valueAttribute.value()); - break; - } - - case ITEM_PARSE_ELEMENTICE: { - abilities.elementDamage = pugi::cast(valueAttribute.value()); - abilities.elementType = COMBAT_ICEDAMAGE; - break; - } - - case ITEM_PARSE_ELEMENTEARTH: { - abilities.elementDamage = pugi::cast(valueAttribute.value()); - abilities.elementType = COMBAT_EARTHDAMAGE; - break; - } - - case ITEM_PARSE_ELEMENTFIRE: { - abilities.elementDamage = pugi::cast(valueAttribute.value()); - abilities.elementType = COMBAT_FIREDAMAGE; - break; - } - - case ITEM_PARSE_ELEMENTENERGY: { - abilities.elementDamage = pugi::cast(valueAttribute.value()); - abilities.elementType = COMBAT_ENERGYDAMAGE; - break; - } - - case ITEM_PARSE_WALKSTACK: { - it.walkStack = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_BLOCKING: { - it.blockSolid = valueAttribute.as_bool(); - break; - } - - case ITEM_PARSE_ALLOWDISTREAD: { - it.allowDistRead = booleanString(valueAttribute.as_string()); - break; - } - - default: { - // It should not ever get to here, only if you add a new key to the map and don't configure a case for it. - std::cout << "[Warning - Items::parseItemNode] Not configured key value: " << keyAttribute.as_string() << std::endl; - break; + if (script.Token != SPECIAL || script.getSpecial() != ',') { + continue; } } - } else { - std::cout << "[Warning - Items::parseItemNode] Unknown key value: " << keyAttribute.as_string() << std::endl; + } else if (identifier == "magicfield") { + script.readSymbol('{'); + + CombatType_t combatType = COMBAT_NONE; + ConditionDamage* conditionDamage = nullptr; + + int32_t cycles = 0; + int32_t hit_damage = 0; + + while (true) { + while (true) { + script.nextToken(); + if (script.Token == SPECIAL) { + break; + } + + identifier = script.getIdentifier(); + script.readSymbol('='); + + if (identifier == "type") { + identifier = script.readIdentifier(); + if (identifier == "fire") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_FIRE); + combatType = COMBAT_FIREDAMAGE; + items[id].combatType = combatType; + items[id].conditionDamage.reset(conditionDamage); + } else if (identifier == "energy") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_ENERGY); + combatType = COMBAT_ENERGYDAMAGE; + items[id].combatType = combatType; + items[id].conditionDamage.reset(conditionDamage); + } else if (identifier == "poison") { + conditionDamage = new ConditionDamage(CONDITIONID_COMBAT, CONDITION_POISON); + combatType = COMBAT_EARTHDAMAGE; + items[id].combatType = combatType; + items[id].conditionDamage.reset(conditionDamage); + } else { + script.error("unknown magicfield type"); + return false; + } + } else if (identifier == "count") { + cycles = script.readNumber(); + } else if (identifier == "damage") { + hit_damage = script.readNumber(); + } else { + script.error("unknown identifier"); + return false; + } + } + + if (script.getSpecial() == '}') { + break; + } + + if (script.Token != SPECIAL || script.getSpecial() != ',') { + continue; + } + } + + int32_t count = 3; + + if (combatType == COMBAT_FIREDAMAGE) { + cycles /= 10; + count = 8; + } else if (combatType == COMBAT_ENERGYDAMAGE) { + cycles /= 20; + count = 10; + } + + conditionDamage->setParam(CONDITION_PARAM_CYCLE, cycles); + conditionDamage->setParam(CONDITION_PARAM_COUNT, count); + conditionDamage->setParam(CONDITION_PARAM_MAX_COUNT, count); + conditionDamage->setParam(CONDITION_PARAM_HIT_DAMAGE, hit_damage); + + conditionDamage->setParam(CONDITION_PARAM_FIELD, 1); } } - //check bed items - if ((it.transformToFree != 0 || it.transformToOnUse[PLAYERSEX_FEMALE] != 0 || it.transformToOnUse[PLAYERSEX_MALE] != 0) && it.type != ITEM_TYPE_BED) { - std::cout << "[Warning - Items::parseItemNode] Item " << it.id << " is not set as a bed-type" << std::endl; + script.close(); + items.shrink_to_fit(); + + for (ItemType& type : items) { + std::string& name = type.name; + extractArticleAndName(name, type.article, type.name); + nameToItems.insert({ asLowerCaseString(type.name), type.id }); + if (!name.empty()) { + if (type.stackable) { + type.showCount = true; + type.pluralName = pluralizeString(name); + } + } } + + return true; } ItemType& Items::getItemType(size_t id) @@ -1357,15 +596,6 @@ const ItemType& Items::getItemType(size_t id) const return items.front(); } -const ItemType& Items::getItemIdByClientId(uint16_t spriteId) const -{ - auto it = reverseItemMap.find(spriteId); - if (it != reverseItemMap.end()) { - return getItemType(it->second); - } - return items.front(); -} - uint16_t Items::getItemIdByName(const std::string& name) { auto result = nameToItems.find(asLowerCaseString(name)); diff --git a/src/items.h b/src/items.h index 0bc3d13..6411e99 100644 --- a/src/items.h +++ b/src/items.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,8 +22,8 @@ #include "const.h" #include "enums.h" -#include "itemloader.h" #include "position.h" +#include "fileloader.h" enum SlotPositionBits : uint32_t { SLOTP_WHEREEVER = 0xFFFFFFFF, @@ -46,7 +46,6 @@ enum ItemTypes_t { ITEM_TYPE_NONE, ITEM_TYPE_DEPOT, ITEM_TYPE_MAILBOX, - ITEM_TYPE_TRASHHOLDER, ITEM_TYPE_CONTAINER, ITEM_TYPE_DOOR, ITEM_TYPE_MAGICFIELD, @@ -54,117 +53,28 @@ enum ItemTypes_t { ITEM_TYPE_BED, ITEM_TYPE_KEY, ITEM_TYPE_RUNE, + ITEM_TYPE_CHEST, ITEM_TYPE_LAST }; -enum ItemParseAttributes_t { - ITEM_PARSE_TYPE, - ITEM_PARSE_DESCRIPTION, - ITEM_PARSE_RUNESPELLNAME, - ITEM_PARSE_WEIGHT, - ITEM_PARSE_SHOWCOUNT, - ITEM_PARSE_ARMOR, - ITEM_PARSE_DEFENSE, - ITEM_PARSE_EXTRADEF, - ITEM_PARSE_ATTACK, - ITEM_PARSE_ROTATETO, - ITEM_PARSE_MOVEABLE, - ITEM_PARSE_BLOCKPROJECTILE, - ITEM_PARSE_PICKUPABLE, - ITEM_PARSE_FORCESERIALIZE, - ITEM_PARSE_FLOORCHANGE, - ITEM_PARSE_CORPSETYPE, - ITEM_PARSE_CONTAINERSIZE, - ITEM_PARSE_FLUIDSOURCE, - ITEM_PARSE_READABLE, - ITEM_PARSE_WRITEABLE, - ITEM_PARSE_MAXTEXTLEN, - ITEM_PARSE_WRITEONCEITEMID, - ITEM_PARSE_WEAPONTYPE, - ITEM_PARSE_SLOTTYPE, - ITEM_PARSE_AMMOTYPE, - ITEM_PARSE_SHOOTTYPE, - ITEM_PARSE_EFFECT, - ITEM_PARSE_RANGE, - ITEM_PARSE_STOPDURATION, - ITEM_PARSE_DECAYTO, - ITEM_PARSE_TRANSFORMEQUIPTO, - ITEM_PARSE_TRANSFORMDEEQUIPTO, - ITEM_PARSE_DURATION, - ITEM_PARSE_SHOWDURATION, - ITEM_PARSE_CHARGES, - ITEM_PARSE_SHOWCHARGES, - ITEM_PARSE_SHOWATTRIBUTES, - ITEM_PARSE_HITCHANCE, - ITEM_PARSE_MAXHITCHANCE, - ITEM_PARSE_INVISIBLE, - ITEM_PARSE_SPEED, - ITEM_PARSE_HEALTHGAIN, - ITEM_PARSE_HEALTHTICKS, - ITEM_PARSE_MANAGAIN, - ITEM_PARSE_MANATICKS, - ITEM_PARSE_MANASHIELD, - ITEM_PARSE_SKILLSWORD, - ITEM_PARSE_SKILLAXE, - ITEM_PARSE_SKILLCLUB, - ITEM_PARSE_SKILLDIST, - ITEM_PARSE_SKILLFISH, - ITEM_PARSE_SKILLSHIELD, - ITEM_PARSE_SKILLFIST, - ITEM_PARSE_MAXHITPOINTS, - ITEM_PARSE_MAXHITPOINTSPERCENT, - ITEM_PARSE_MAXMANAPOINTS, - ITEM_PARSE_MAXMANAPOINTSPERCENT, - ITEM_PARSE_MAGICPOINTS, - ITEM_PARSE_MAGICPOINTSPERCENT, - ITEM_PARSE_CRITICALHITCHANCE, - ITEM_PARSE_CRITICALHITAMOUNT, - ITEM_PARSE_LIFELEECHCHANCE, - ITEM_PARSE_LIFELEECHAMOUNT, - ITEM_PARSE_MANALEECHCHANCE, - ITEM_PARSE_MANALEECHAMOUNT, - ITEM_PARSE_FIELDABSORBPERCENTENERGY, - ITEM_PARSE_FIELDABSORBPERCENTFIRE, - ITEM_PARSE_FIELDABSORBPERCENTPOISON, - ITEM_PARSE_ABSORBPERCENTALL, - ITEM_PARSE_ABSORBPERCENTELEMENTS, - ITEM_PARSE_ABSORBPERCENTMAGIC, - ITEM_PARSE_ABSORBPERCENTENERGY, - ITEM_PARSE_ABSORBPERCENTFIRE, - ITEM_PARSE_ABSORBPERCENTPOISON, - ITEM_PARSE_ABSORBPERCENTICE, - ITEM_PARSE_ABSORBPERCENTHOLY, - ITEM_PARSE_ABSORBPERCENTDEATH, - ITEM_PARSE_ABSORBPERCENTLIFEDRAIN, - ITEM_PARSE_ABSORBPERCENTMANADRAIN, - ITEM_PARSE_ABSORBPERCENTDROWN, - ITEM_PARSE_ABSORBPERCENTPHYSICAL, - ITEM_PARSE_ABSORBPERCENTHEALING, - ITEM_PARSE_ABSORBPERCENTUNDEFINED, - ITEM_PARSE_SUPPRESSDRUNK, - ITEM_PARSE_SUPPRESSENERGY, - ITEM_PARSE_SUPPRESSFIRE, - ITEM_PARSE_SUPPRESSPOISON, - ITEM_PARSE_SUPPRESSDROWN, - ITEM_PARSE_SUPPRESSPHYSICAL, - ITEM_PARSE_SUPPRESSFREEZE, - ITEM_PARSE_SUPPRESSDAZZLE, - ITEM_PARSE_SUPPRESSCURSE, - ITEM_PARSE_FIELD, - ITEM_PARSE_REPLACEABLE, - ITEM_PARSE_PARTNERDIRECTION, - ITEM_PARSE_LEVELDOOR, - ITEM_PARSE_MALETRANSFORMTO, - ITEM_PARSE_FEMALETRANSFORMTO, - ITEM_PARSE_TRANSFORMTO, - ITEM_PARSE_DESTROYTO, - ITEM_PARSE_ELEMENTICE, - ITEM_PARSE_ELEMENTEARTH, - ITEM_PARSE_ELEMENTFIRE, - ITEM_PARSE_ELEMENTENERGY, - ITEM_PARSE_WALKSTACK, - ITEM_PARSE_BLOCKING, - ITEM_PARSE_ALLOWDISTREAD, +enum itemgroup_t { + ITEM_GROUP_NONE, + + ITEM_GROUP_GROUND, + ITEM_GROUP_WEAPON, + ITEM_GROUP_AMMUNITION, + ITEM_GROUP_ARMOR, + ITEM_GROUP_CHARGES, + ITEM_GROUP_TELEPORT, + ITEM_GROUP_MAGICFIELD, + ITEM_GROUP_WRITEABLE, + ITEM_GROUP_KEY, + ITEM_GROUP_SPLASH, + ITEM_GROUP_FLUID, + ITEM_GROUP_DOOR, + ITEM_GROUP_DEPRECATED, + + ITEM_GROUP_LAST }; struct Abilities { @@ -182,7 +92,6 @@ struct Abilities { //extra skill modifiers int32_t skills[SKILL_LAST + 1] = { 0 }; - int32_t specialSkills[SPECIALSKILL_LAST + 1] = { 0 }; int32_t speed = 0; @@ -192,10 +101,6 @@ struct Abilities { //damage abilities modifiers int16_t absorbPercent[COMBAT_COUNT] = { 0 }; - //elemental damage - uint16_t elementDamage = 0; - CombatType_t elementType = COMBAT_NONE; - bool manaShield = false; bool invisible = false; bool regeneration = false; @@ -219,7 +124,10 @@ class ItemType return group == ITEM_GROUP_GROUND; } bool isContainer() const { - return group == ITEM_GROUP_CONTAINER; + return type == ITEM_TYPE_CONTAINER; + } + bool isChest() const { + return type == ITEM_TYPE_CHEST; } bool isSplash() const { return group == ITEM_GROUP_SPLASH; @@ -246,20 +154,11 @@ class ItemType bool isMailbox() const { return (type == ITEM_TYPE_MAILBOX); } - bool isTrashHolder() const { - return (type == ITEM_TYPE_TRASHHOLDER); - } bool isBed() const { return (type == ITEM_TYPE_BED); } bool isRune() const { - return (type == ITEM_TYPE_RUNE); - } - bool isPickupable() const { - return (allowPickupable || pickupable); - } - bool isUseable() const { - return (useable); + return type == ITEM_TYPE_RUNE; } bool hasSubType() const { return (isFluidContainer() || isSplash() || stackable || charges != 0); @@ -291,9 +190,7 @@ class ItemType itemgroup_t group = ITEM_GROUP_NONE; ItemTypes_t type = ITEM_TYPE_NONE; uint16_t id = 0; - uint16_t clientId = 0; bool stackable = false; - bool isAnimation = false; std::string name; std::string article; @@ -306,35 +203,40 @@ class ItemType std::unique_ptr conditionDamage; uint32_t weight = 0; - uint32_t levelDoor = 0; uint32_t decayTime = 0; uint32_t wieldInfo = 0; uint32_t minReqLevel = 0; uint32_t minReqMagicLevel = 0; uint32_t charges = 0; - int32_t maxHitChance = -1; + int32_t attackStrength = 0; + int32_t attackVariation = 0; + int32_t manaConsumption = 0; + int32_t vocations = 0; int32_t decayTo = -1; int32_t attack = 0; int32_t defense = 0; int32_t extraDefense = 0; int32_t armor = 0; - uint16_t rotateTo = 0; + int32_t rotateTo = 0; int32_t runeMagLevel = 0; int32_t runeLevel = 0; + int32_t nutrition = 0; + int32_t destroyTarget = 0; CombatType_t combatType = COMBAT_NONE; + CombatType_t damageType = COMBAT_NONE; - uint16_t transformToOnUse[2] = {0, 0}; + uint16_t transformToOnUse = 0; uint16_t transformToFree = 0; + uint16_t disguiseId = 0; uint16_t destroyTo = 0; uint16_t maxTextLen = 0; uint16_t writeOnceItemId = 0; uint16_t transformEquipTo = 0; uint16_t transformDeEquipTo = 0; uint16_t maxItems = 8; - uint16_t slotPosition = SLOTP_HAND; + uint16_t slotPosition = SLOTP_RIGHT | SLOTP_LEFT | SLOTP_AMMO; uint16_t speed = 0; - uint16_t wareId = 0; MagicEffectClasses magicEffect = CONST_ME_NONE; Direction bedPartnerDir = DIRECTION_NONE; @@ -344,22 +246,30 @@ class ItemType RaceType_t corpseType = RACE_NONE; FluidTypes_t fluidSource = FLUID_NONE; - uint8_t floorChange = 0; + uint8_t fragility = 0; uint8_t alwaysOnTopOrder = 0; uint8_t lightLevel = 0; uint8_t lightColor = 0; uint8_t shootRange = 1; - int8_t hitChance = 0; + uint8_t weaponSpecialEffect = 0; + bool collisionEvent = false; + bool separationEvent = false; + bool useEvent = false; + bool multiUseEvent = false; + bool distUse = false; + bool disguise = false; bool forceUse = false; - bool forceSerialize = false; + bool changeUse = false; + bool destroy = false; + bool corpse = false; bool hasHeight = false; bool walkStack = true; bool blockSolid = false; bool blockPickupable = false; bool blockProjectile = false; bool blockPathFind = false; - bool allowPickupable = false; + bool allowPickupable = true; bool showDuration = false; bool showCharges = false; bool showAttributes = false; @@ -367,7 +277,7 @@ class ItemType bool pickupable = false; bool rotatable = false; bool useable = false; - bool moveable = false; + bool moveable = true; bool alwaysOnTop = false; bool canReadText = false; bool canWriteText = false; @@ -383,8 +293,7 @@ class ItemType class Items { public: - using NameMap = std::unordered_multimap; - using InventoryVector = std::vector; + using nameMap = std::unordered_multimap; Items(); @@ -395,38 +304,23 @@ class Items bool reload(); void clear(); - bool loadFromOtb(const std::string& file); - const ItemType& operator[](size_t id) const { return getItemType(id); } const ItemType& getItemType(size_t id) const; ItemType& getItemType(size_t id); - const ItemType& getItemIdByClientId(uint16_t spriteId) const; uint16_t getItemIdByName(const std::string& name); - uint32_t majorVersion = 0; - uint32_t minorVersion = 0; - uint32_t buildNumber = 0; + bool loadItems(); - bool loadFromXml(); - void parseItemNode(const pugi::xml_node& itemNode, uint16_t id); - - void buildInventoryList(); - const InventoryVector& getInventory() const { - return inventory; - } - - size_t size() const { + inline size_t size() const { return items.size(); } - NameMap nameToItems; + nameMap nameToItems; - private: - std::map reverseItemMap; + protected: std::vector items; - InventoryVector inventory; }; #endif diff --git a/src/lockfree.h b/src/lockfree.h index b555139..7c8cc7b 100644 --- a/src/lockfree.h +++ b/src/lockfree.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,52 +26,37 @@ #include -/* - * we use this to avoid instantiating multiple free lists for objects of the - * same size and it can be replaced by a variable template in C++14 - * - * template - * boost::lockfree::stack lockfreeFreeList; - */ -template -struct LockfreeFreeList -{ - using FreeList = boost::lockfree::stack>; - static FreeList& get() - { - static FreeList freeList; - return freeList; - } -}; - template class LockfreePoolingAllocator : public std::allocator { public: - LockfreePoolingAllocator() = default; - - template ::value>::type> + template explicit constexpr LockfreePoolingAllocator(const U&) {} - using value_type = T; + typedef T value_type; T* allocate(size_t) const { - auto& inst = LockfreeFreeList::get(); - void* p; // NOTE: p doesn't have to be initialized - if (!inst.pop(p)) { + T* p; // NOTE: p doesn't have to be initialized + if (!getFreeList().pop(p)) { //Acquire memory without calling the constructor of T - p = operator new (sizeof(T)); + p = static_cast(operator new (sizeof(T))); } - return static_cast(p); + return p; } void deallocate(T* p, size_t) const { - auto& inst = LockfreeFreeList::get(); - if (!inst.bounded_push(p)) { + if (!getFreeList().bounded_push(p)) { //Release memory without calling the destructor of T //(it has already been called at this point) operator delete(p); } } + + private: + typedef boost::lockfree::stack> FreeList; + static FreeList& getFreeList() { + static FreeList freeList; + return freeList; + } }; #endif diff --git a/src/luascript.cpp b/src/luascript.cpp index c1f05b5..c46e5d7 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,10 +35,6 @@ #include "monster.h" #include "scheduler.h" #include "databasetasks.h" -#include "movement.h" -#include "globalevent.h" -#include "script.h" -#include "weapons.h" extern Chat* g_chat; extern Game g_game; @@ -46,13 +42,6 @@ extern Monsters g_monsters; extern ConfigManager g_config; extern Vocations g_vocations; extern Spells* g_spells; -extern Actions* g_actions; -extern TalkActions* g_talkActions; -extern CreatureEvents* g_creatureEvents; -extern MoveEvents* g_moveEvents; -extern GlobalEvents* g_globalEvents; -extern Scripts* g_scripts; -extern Weapons* g_weapons; ScriptEnvironment::DBResultMap ScriptEnvironment::tempResults; uint32_t ScriptEnvironment::lastResultId = 0; @@ -126,10 +115,6 @@ uint32_t ScriptEnvironment::addThing(Thing* thing) } Item* item = thing->getItem(); - if (item && item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { - return item->getUniqueId(); - } - for (const auto& it : localMap) { if (it.second == item) { return it.first; @@ -154,14 +139,6 @@ Thing* ScriptEnvironment::getThingByUID(uint32_t uid) return g_game.getCreatureByID(uid); } - if (uid <= std::numeric_limits::max()) { - Item* item = g_game.getUniqueItem(uid); - if (item && !item->isRemoved()) { - return item; - } - return nullptr; - } - auto it = localMap.find(uid); if (it != localMap.end()) { Item* item = it->second; @@ -192,11 +169,6 @@ Container* ScriptEnvironment::getContainerByUID(uint32_t uid) void ScriptEnvironment::removeItemByUID(uint32_t uid) { - if (uid <= std::numeric_limits::max()) { - g_game.removeUniqueItem(uid); - return; - } - auto it = localMap.find(uid); if (it != localMap.end()) { localMap.erase(it); @@ -365,29 +337,6 @@ int32_t LuaScriptInterface::getEvent(const std::string& eventName) return runningEventId++; } -int32_t LuaScriptInterface::getEvent() -{ - //check if function is on the stack - if (!isFunction(luaState, -1)) { - return -1; - } - - //get our events table - lua_rawgeti(luaState, LUA_REGISTRYINDEX, eventTableRef); - if (!isTable(luaState, -1)) { - lua_pop(luaState, 1); - return -1; - } - - //save in our events table - lua_pushvalue(luaState, -2); - lua_rawseti(luaState, -2, runningEventId); - lua_pop(luaState, 2); - - cacheFiles[runningEventId] = loadingFile + ":callback"; - return runningEventId++; -} - int32_t LuaScriptInterface::getMetaEvent(const std::string& globalName, const std::string& eventName) { //get our events table @@ -727,6 +676,18 @@ void LuaScriptInterface::setCreatureMetatable(lua_State* L, int32_t index, const } // Get +CombatDamage LuaScriptInterface::getCombatDamage(lua_State* L) +{ + CombatDamage damage; + damage.value = getNumber(L, -4); + damage.type = getNumber(L, -3); + damage.min = getNumber(L, -2); + damage.max = getNumber(L, -1); + + lua_pop(L, 4); + return damage; +} + std::string LuaScriptInterface::getString(lua_State* L, int32_t arg) { size_t len; @@ -769,7 +730,6 @@ Position LuaScriptInterface::getPosition(lua_State* L, int32_t arg) Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg) { Outfit_t outfit; - outfit.lookMount = getField(L, arg, "lookMount"); outfit.lookAddons = getField(L, arg, "lookAddons"); outfit.lookFeet = getField(L, arg, "lookFeet"); @@ -777,10 +737,10 @@ Outfit_t LuaScriptInterface::getOutfit(lua_State* L, int32_t arg) outfit.lookBody = getField(L, arg, "lookBody"); outfit.lookHead = getField(L, arg, "lookHead"); - outfit.lookTypeEx = getField(L, arg, "lookTypeEx"); - outfit.lookType = getField(L, arg, "lookType"); + outfit.lookTypeEx = getField(L, arg, "lookTypeEx"); + outfit.lookType = getField(L, arg, "lookType"); - lua_pop(L, 8); + lua_pop(L, 6); return outfit; } @@ -817,13 +777,6 @@ LuaVariant LuaScriptInterface::getVariant(lua_State* L, int32_t arg) return var; } -InstantSpell* LuaScriptInterface::getInstantSpell(lua_State* L, int32_t arg) -{ - InstantSpell* spell = g_spells->getInstantSpellByName(getFieldString(L, arg, "name")); - lua_pop(L, 1); - return spell; -} - Thing* LuaScriptInterface::getThing(lua_State* L, int32_t arg) { Thing* thing; @@ -902,26 +855,13 @@ void LuaScriptInterface::pushBoolean(lua_State* L, bool value) void LuaScriptInterface::pushCombatDamage(lua_State* L, const CombatDamage& damage) { - lua_pushnumber(L, damage.primary.value); - lua_pushnumber(L, damage.primary.type); - lua_pushnumber(L, damage.secondary.value); - lua_pushnumber(L, damage.secondary.type); + lua_pushnumber(L, damage.value); + lua_pushnumber(L, damage.type); + lua_pushnumber(L, damage.min); + lua_pushnumber(L, damage.max); lua_pushnumber(L, damage.origin); } -void LuaScriptInterface::pushInstantSpell(lua_State* L, const InstantSpell& spell) -{ - lua_createtable(L, 0, 6); - - setField(L, "name", spell.getName()); - setField(L, "words", spell.getWords()); - setField(L, "level", spell.getLevel()); - setField(L, "mlevel", spell.getMagicLevel()); - setField(L, "mana", spell.getMana()); - setField(L, "manapercent", spell.getManaPercent()); - - setMetatable(L, -1, "Spell"); -} void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, int32_t stackpos/* = 0*/) { @@ -937,7 +877,7 @@ void LuaScriptInterface::pushPosition(lua_State* L, const Position& position, in void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit) { - lua_createtable(L, 0, 8); + lua_createtable(L, 0, 6); setField(L, "lookType", outfit.lookType); setField(L, "lookTypeEx", outfit.lookTypeEx); setField(L, "lookHead", outfit.lookHead); @@ -945,29 +885,6 @@ void LuaScriptInterface::pushOutfit(lua_State* L, const Outfit_t& outfit) setField(L, "lookLegs", outfit.lookLegs); setField(L, "lookFeet", outfit.lookFeet); setField(L, "lookAddons", outfit.lookAddons); - setField(L, "lookMount", outfit.lookMount); -} - -void LuaScriptInterface::pushLoot(lua_State* L, const std::vector& lootList) -{ - lua_createtable(L, lootList.size(), 0); - - int index = 0; - for (const auto& lootBlock : lootList) { - lua_createtable(L, 0, 7); - - setField(L, "itemId", lootBlock.id); - setField(L, "chance", lootBlock.chance); - setField(L, "subType", lootBlock.subType); - setField(L, "maxCount", lootBlock.countmax); - setField(L, "actionId", lootBlock.actionId); - setField(L, "text", lootBlock.text); - - pushLoot(L, lootBlock.childLoot); - lua_setfield(L, -2, "childLoot"); - - lua_rawseti(L, -2, ++index); - } } #define registerEnum(value) { std::string enumName = #value; registerGlobalVariable(enumName.substr(enumName.find_last_of(':') + 1), value); } @@ -975,14 +892,39 @@ void LuaScriptInterface::pushLoot(lua_State* L, const std::vector& lo void LuaScriptInterface::registerFunctions() { + //getPlayerFlagValue(cid, flag) + lua_register(luaState, "getPlayerFlagValue", LuaScriptInterface::luaGetPlayerFlagValue); + + //getPlayerInstantSpellCount(cid) + lua_register(luaState, "getPlayerInstantSpellCount", LuaScriptInterface::luaGetPlayerInstantSpellCount); + + //getPlayerInstantSpellInfo(cid, index) + lua_register(luaState, "getPlayerInstantSpellInfo", LuaScriptInterface::luaGetPlayerInstantSpellInfo); + //doPlayerAddItem(uid, itemid, count/subtype) //doPlayerAddItem(cid, itemid, count, canDropOnMap, subtype) //Returns uid of the created item lua_register(luaState, "doPlayerAddItem", LuaScriptInterface::luaDoPlayerAddItem); + //doCreateItem(itemid, type/count, pos) + //Returns uid of the created item, only works on tiles. + lua_register(luaState, "doCreateItem", LuaScriptInterface::luaDoCreateItem); + + //doCreateItemEx(itemid, count/subtype) + lua_register(luaState, "doCreateItemEx", LuaScriptInterface::luaDoCreateItemEx); + + //doTileAddItemEx(pos, uid) + lua_register(luaState, "doTileAddItemEx", LuaScriptInterface::luaDoTileAddItemEx); + + //doMoveCreature(cid, direction) + lua_register(luaState, "doMoveCreature", LuaScriptInterface::luaDoMoveCreature); + //doSetCreatureLight(cid, lightLevel, lightColor, time) lua_register(luaState, "doSetCreatureLight", LuaScriptInterface::luaDoSetCreatureLight); + //getCreatureCondition(cid, condition[, subId]) + lua_register(luaState, "getCreatureCondition", LuaScriptInterface::luaGetCreatureCondition); + //isValidUID(uid) lua_register(luaState, "isValidUID", LuaScriptInterface::luaIsValidUID); @@ -1037,6 +979,18 @@ void LuaScriptInterface::registerFunctions() //doChallengeCreature(cid, target) lua_register(luaState, "doChallengeCreature", LuaScriptInterface::luaDoChallengeCreature); + //doSetMonsterOutfit(cid, name, time) + lua_register(luaState, "doSetMonsterOutfit", LuaScriptInterface::luaSetMonsterOutfit); + + //doSetItemOutfit(cid, item, time) + lua_register(luaState, "doSetItemOutfit", LuaScriptInterface::luaSetItemOutfit); + + //doSetCreatureOutfit(cid, outfit, time) + lua_register(luaState, "doSetCreatureOutfit", LuaScriptInterface::luaSetCreatureOutfit); + + //isInArray(array, value) + lua_register(luaState, "isInArray", LuaScriptInterface::luaIsInArray); + //addEvent(callback, delay, ...) lua_register(luaState, "addEvent", LuaScriptInterface::luaAddEvent); @@ -1058,12 +1012,6 @@ void LuaScriptInterface::registerFunctions() //getWaypointPosition(name) lua_register(luaState, "getWaypointPositionByName", LuaScriptInterface::luaGetWaypointPositionByName); - //sendChannelMessage(channelId, type, message) - lua_register(luaState, "sendChannelMessage", LuaScriptInterface::luaSendChannelMessage); - - //sendGuildChannelMessage(guildId, type, message) - lua_register(luaState, "sendGuildChannelMessage", LuaScriptInterface::luaSendGuildChannelMessage); - #ifndef LUAJIT_VERSION //bit operations for Lua, based on bitlib project release 24 //bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshift, bit.rshift @@ -1097,11 +1045,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(ACCOUNT_TYPE_GAMEMASTER) registerEnum(ACCOUNT_TYPE_GOD) - registerEnum(BUG_CATEGORY_MAP) - registerEnum(BUG_CATEGORY_TYPO) - registerEnum(BUG_CATEGORY_TECHNICAL) - registerEnum(BUG_CATEGORY_OTHER) - registerEnum(CALLBACK_PARAM_LEVELMAGICVALUE) registerEnum(CALLBACK_PARAM_SKILLVALUE) registerEnum(CALLBACK_PARAM_TARGETTILE) @@ -1126,14 +1069,11 @@ void LuaScriptInterface::registerFunctions() registerEnum(COMBAT_ENERGYDAMAGE) registerEnum(COMBAT_EARTHDAMAGE) registerEnum(COMBAT_FIREDAMAGE) + registerEnum(COMBAT_DROWNDAMAGE) registerEnum(COMBAT_UNDEFINEDDAMAGE) registerEnum(COMBAT_LIFEDRAIN) registerEnum(COMBAT_MANADRAIN) registerEnum(COMBAT_HEALING) - registerEnum(COMBAT_DROWNDAMAGE) - registerEnum(COMBAT_ICEDAMAGE) - registerEnum(COMBAT_HOLYDAMAGE) - registerEnum(COMBAT_DEATHDAMAGE) registerEnum(COMBAT_PARAM_TYPE) registerEnum(COMBAT_PARAM_EFFECT) @@ -1145,12 +1085,14 @@ void LuaScriptInterface::registerFunctions() registerEnum(COMBAT_PARAM_AGGRESSIVE) registerEnum(COMBAT_PARAM_DISPEL) registerEnum(COMBAT_PARAM_USECHARGES) + registerEnum(COMBAT_PARAM_DECREASEDAMAGE) + registerEnum(COMBAT_PARAM_MAXIMUMDECREASEDDAMAGE) registerEnum(CONDITION_NONE) registerEnum(CONDITION_POISON) registerEnum(CONDITION_FIRE) registerEnum(CONDITION_ENERGY) - registerEnum(CONDITION_BLEEDING) + registerEnum(CONDITION_DROWN) registerEnum(CONDITION_HASTE) registerEnum(CONDITION_PARALYZE) registerEnum(CONDITION_OUTFIT) @@ -1159,22 +1101,14 @@ void LuaScriptInterface::registerFunctions() registerEnum(CONDITION_MANASHIELD) registerEnum(CONDITION_INFIGHT) registerEnum(CONDITION_DRUNK) - registerEnum(CONDITION_EXHAUST_WEAPON) registerEnum(CONDITION_REGENERATION) registerEnum(CONDITION_SOUL) - registerEnum(CONDITION_DROWN) registerEnum(CONDITION_MUTED) registerEnum(CONDITION_CHANNELMUTEDTICKS) registerEnum(CONDITION_YELLTICKS) registerEnum(CONDITION_ATTRIBUTES) - registerEnum(CONDITION_FREEZING) - registerEnum(CONDITION_DAZZLED) - registerEnum(CONDITION_CURSED) - registerEnum(CONDITION_EXHAUST_COMBAT) - registerEnum(CONDITION_EXHAUST_HEAL) + registerEnum(CONDITION_EXHAUST) registerEnum(CONDITION_PACIFIED) - registerEnum(CONDITION_SPELLCOOLDOWN) - registerEnum(CONDITION_SPELLGROUPCOOLDOWN) registerEnum(CONDITIONID_DEFAULT) registerEnum(CONDITIONID_COMBAT) @@ -1205,7 +1139,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(CONDITION_PARAM_MAXVALUE) registerEnum(CONDITION_PARAM_STARTVALUE) registerEnum(CONDITION_PARAM_TICKINTERVAL) - registerEnum(CONDITION_PARAM_FORCEUPDATE) registerEnum(CONDITION_PARAM_SKILL_MELEE) registerEnum(CONDITION_PARAM_SKILL_FIST) registerEnum(CONDITION_PARAM_SKILL_CLUB) @@ -1221,6 +1154,7 @@ void LuaScriptInterface::registerFunctions() registerEnum(CONDITION_PARAM_STAT_MAXMANAPOINTSPERCENT) registerEnum(CONDITION_PARAM_STAT_MAGICPOINTSPERCENT) registerEnum(CONDITION_PARAM_PERIODICDAMAGE) + registerEnum(CONDITION_PARAM_HIT_DAMAGE) registerEnum(CONDITION_PARAM_SKILL_MELEEPERCENT) registerEnum(CONDITION_PARAM_SKILL_FISTPERCENT) registerEnum(CONDITION_PARAM_SKILL_CLUBPERCENT) @@ -1229,16 +1163,8 @@ void LuaScriptInterface::registerFunctions() registerEnum(CONDITION_PARAM_SKILL_DISTANCEPERCENT) registerEnum(CONDITION_PARAM_SKILL_SHIELDPERCENT) registerEnum(CONDITION_PARAM_SKILL_FISHINGPERCENT) - registerEnum(CONDITION_PARAM_BUFF_SPELL) registerEnum(CONDITION_PARAM_SUBID) registerEnum(CONDITION_PARAM_FIELD) - registerEnum(CONDITION_PARAM_DISABLE_DEFENSE) - registerEnum(CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE) - registerEnum(CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT) - registerEnum(CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE) - registerEnum(CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT) - registerEnum(CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE) - registerEnum(CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT) registerEnum(CONST_ME_NONE) registerEnum(CONST_ME_DRAWBLOOD) @@ -1268,65 +1194,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(CONST_ME_SOUND_WHITE) registerEnum(CONST_ME_BUBBLES) registerEnum(CONST_ME_CRAPS) - registerEnum(CONST_ME_GIFT_WRAPS) - registerEnum(CONST_ME_FIREWORK_YELLOW) - registerEnum(CONST_ME_FIREWORK_RED) - registerEnum(CONST_ME_FIREWORK_BLUE) - registerEnum(CONST_ME_STUN) - registerEnum(CONST_ME_SLEEP) - registerEnum(CONST_ME_WATERCREATURE) - registerEnum(CONST_ME_GROUNDSHAKER) - registerEnum(CONST_ME_HEARTS) - registerEnum(CONST_ME_FIREATTACK) - registerEnum(CONST_ME_ENERGYAREA) - registerEnum(CONST_ME_SMALLCLOUDS) - registerEnum(CONST_ME_HOLYDAMAGE) - registerEnum(CONST_ME_BIGCLOUDS) - registerEnum(CONST_ME_ICEAREA) - registerEnum(CONST_ME_ICETORNADO) - registerEnum(CONST_ME_ICEATTACK) - registerEnum(CONST_ME_STONES) - registerEnum(CONST_ME_SMALLPLANTS) - registerEnum(CONST_ME_CARNIPHILA) - registerEnum(CONST_ME_PURPLEENERGY) - registerEnum(CONST_ME_YELLOWENERGY) - registerEnum(CONST_ME_HOLYAREA) - registerEnum(CONST_ME_BIGPLANTS) - registerEnum(CONST_ME_CAKE) - registerEnum(CONST_ME_GIANTICE) - registerEnum(CONST_ME_WATERSPLASH) - registerEnum(CONST_ME_PLANTATTACK) - registerEnum(CONST_ME_TUTORIALARROW) - registerEnum(CONST_ME_TUTORIALSQUARE) - registerEnum(CONST_ME_MIRRORHORIZONTAL) - registerEnum(CONST_ME_MIRRORVERTICAL) - registerEnum(CONST_ME_SKULLHORIZONTAL) - registerEnum(CONST_ME_SKULLVERTICAL) - registerEnum(CONST_ME_ASSASSIN) - registerEnum(CONST_ME_STEPSHORIZONTAL) - registerEnum(CONST_ME_BLOODYSTEPS) - registerEnum(CONST_ME_STEPSVERTICAL) - registerEnum(CONST_ME_YALAHARIGHOST) - registerEnum(CONST_ME_BATS) - registerEnum(CONST_ME_SMOKE) - registerEnum(CONST_ME_INSECTS) - registerEnum(CONST_ME_DRAGONHEAD) - registerEnum(CONST_ME_ORCSHAMAN) - registerEnum(CONST_ME_ORCSHAMAN_FIRE) - registerEnum(CONST_ME_THUNDER) - registerEnum(CONST_ME_FERUMBRAS) - registerEnum(CONST_ME_CONFETTI_HORIZONTAL) - registerEnum(CONST_ME_CONFETTI_VERTICAL) - registerEnum(CONST_ME_BLACKSMOKE) - registerEnum(CONST_ME_REDSMOKE) - registerEnum(CONST_ME_YELLOWSMOKE) - registerEnum(CONST_ME_GREENSMOKE) - registerEnum(CONST_ME_PURPLESMOKE) - registerEnum(CONST_ME_EARLY_THUNDER) - registerEnum(CONST_ME_RAGIAZ_BONECAPSULE) - registerEnum(CONST_ME_CRITICAL_DAMAGE) - registerEnum(CONST_ME_PLUNGING_FISH) - registerEnum(CONST_ANI_NONE) registerEnum(CONST_ANI_SPEAR) registerEnum(CONST_ANI_BOLT) @@ -1343,42 +1210,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(CONST_ANI_SNOWBALL) registerEnum(CONST_ANI_POWERBOLT) registerEnum(CONST_ANI_POISON) - registerEnum(CONST_ANI_INFERNALBOLT) - registerEnum(CONST_ANI_HUNTINGSPEAR) - registerEnum(CONST_ANI_ENCHANTEDSPEAR) - registerEnum(CONST_ANI_REDSTAR) - registerEnum(CONST_ANI_GREENSTAR) - registerEnum(CONST_ANI_ROYALSPEAR) - registerEnum(CONST_ANI_SNIPERARROW) - registerEnum(CONST_ANI_ONYXARROW) - registerEnum(CONST_ANI_PIERCINGBOLT) - registerEnum(CONST_ANI_WHIRLWINDSWORD) - registerEnum(CONST_ANI_WHIRLWINDAXE) - registerEnum(CONST_ANI_WHIRLWINDCLUB) - registerEnum(CONST_ANI_ETHEREALSPEAR) - registerEnum(CONST_ANI_ICE) - registerEnum(CONST_ANI_EARTH) - registerEnum(CONST_ANI_HOLY) - registerEnum(CONST_ANI_SUDDENDEATH) - registerEnum(CONST_ANI_FLASHARROW) - registerEnum(CONST_ANI_FLAMMINGARROW) - registerEnum(CONST_ANI_SHIVERARROW) - registerEnum(CONST_ANI_ENERGYBALL) - registerEnum(CONST_ANI_SMALLICE) - registerEnum(CONST_ANI_SMALLHOLY) - registerEnum(CONST_ANI_SMALLEARTH) - registerEnum(CONST_ANI_EARTHARROW) - registerEnum(CONST_ANI_EXPLOSION) - registerEnum(CONST_ANI_CAKE) - registerEnum(CONST_ANI_TARSALARROW) - registerEnum(CONST_ANI_VORTEXBOLT) - registerEnum(CONST_ANI_PRISMATICBOLT) - registerEnum(CONST_ANI_CRYSTALLINEARROW) - registerEnum(CONST_ANI_DRILLBOLT) - registerEnum(CONST_ANI_ENVENOMEDARROW) - registerEnum(CONST_ANI_GLOOTHSPEAR) - registerEnum(CONST_ANI_SIMPLEARROW) - registerEnum(CONST_ANI_WEAPONTYPE) registerEnum(CONST_PROP_BLOCKSOLID) registerEnum(CONST_PROP_HASHEIGHT) @@ -1412,10 +1243,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(CREATURE_EVENT_DEATH) registerEnum(CREATURE_EVENT_KILL) registerEnum(CREATURE_EVENT_ADVANCE) - registerEnum(CREATURE_EVENT_MODALWINDOW) - registerEnum(CREATURE_EVENT_TEXTEDIT) - registerEnum(CREATURE_EVENT_HEALTHCHANGE) - registerEnum(CREATURE_EVENT_MANACHANGE) registerEnum(CREATURE_EVENT_EXTENDED_OPCODE) registerEnum(GAME_STATE_STARTUP) @@ -1433,26 +1260,8 @@ void LuaScriptInterface::registerFunctions() registerEnum(MESSAGE_EVENT_ADVANCE) registerEnum(MESSAGE_STATUS_SMALL) registerEnum(MESSAGE_INFO_DESCR) - registerEnum(MESSAGE_DAMAGE_DEALT) - registerEnum(MESSAGE_DAMAGE_RECEIVED) - registerEnum(MESSAGE_HEALED) - registerEnum(MESSAGE_EXPERIENCE) - registerEnum(MESSAGE_DAMAGE_OTHERS) - registerEnum(MESSAGE_HEALED_OTHERS) - registerEnum(MESSAGE_EXPERIENCE_OTHERS) registerEnum(MESSAGE_EVENT_DEFAULT) - registerEnum(MESSAGE_GUILD) - registerEnum(MESSAGE_PARTY_MANAGEMENT) - registerEnum(MESSAGE_PARTY) - registerEnum(MESSAGE_EVENT_ORANGE) registerEnum(MESSAGE_STATUS_CONSOLE_ORANGE) - registerEnum(MESSAGE_LOOT) - - registerEnum(CREATURETYPE_PLAYER) - registerEnum(CREATURETYPE_MONSTER) - registerEnum(CREATURETYPE_NPC) - registerEnum(CREATURETYPE_SUMMON_OWN) - registerEnum(CREATURETYPE_SUMMON_OTHERS) registerEnum(CLIENTOS_LINUX) registerEnum(CLIENTOS_WINDOWS) @@ -1461,13 +1270,9 @@ void LuaScriptInterface::registerFunctions() registerEnum(CLIENTOS_OTCLIENT_WINDOWS) registerEnum(CLIENTOS_OTCLIENT_MAC) - registerEnum(FIGHTMODE_ATTACK) - registerEnum(FIGHTMODE_BALANCED) - registerEnum(FIGHTMODE_DEFENSE) - registerEnum(ITEM_ATTRIBUTE_NONE) registerEnum(ITEM_ATTRIBUTE_ACTIONID) - registerEnum(ITEM_ATTRIBUTE_UNIQUEID) + registerEnum(ITEM_ATTRIBUTE_MOVEMENTID) registerEnum(ITEM_ATTRIBUTE_DESCRIPTION) registerEnum(ITEM_ATTRIBUTE_TEXT) registerEnum(ITEM_ATTRIBUTE_DATE) @@ -1478,9 +1283,7 @@ void LuaScriptInterface::registerFunctions() registerEnum(ITEM_ATTRIBUTE_WEIGHT) registerEnum(ITEM_ATTRIBUTE_ATTACK) registerEnum(ITEM_ATTRIBUTE_DEFENSE) - registerEnum(ITEM_ATTRIBUTE_EXTRADEFENSE) registerEnum(ITEM_ATTRIBUTE_ARMOR) - registerEnum(ITEM_ATTRIBUTE_HITCHANCE) registerEnum(ITEM_ATTRIBUTE_SHOOTRANGE) registerEnum(ITEM_ATTRIBUTE_OWNER) registerEnum(ITEM_ATTRIBUTE_DURATION) @@ -1489,10 +1292,15 @@ void LuaScriptInterface::registerFunctions() registerEnum(ITEM_ATTRIBUTE_CHARGES) registerEnum(ITEM_ATTRIBUTE_FLUIDTYPE) registerEnum(ITEM_ATTRIBUTE_DOORID) + registerEnum(ITEM_ATTRIBUTE_KEYNUMBER) + registerEnum(ITEM_ATTRIBUTE_KEYHOLENUMBER) + registerEnum(ITEM_ATTRIBUTE_DOORQUESTNUMBER) + registerEnum(ITEM_ATTRIBUTE_DOORQUESTVALUE) + registerEnum(ITEM_ATTRIBUTE_DOORLEVEL) + registerEnum(ITEM_ATTRIBUTE_CHESTQUESTNUMBER) registerEnum(ITEM_TYPE_DEPOT) registerEnum(ITEM_TYPE_MAILBOX) - registerEnum(ITEM_TYPE_TRASHHOLDER) registerEnum(ITEM_TYPE_CONTAINER) registerEnum(ITEM_TYPE_DOOR) registerEnum(ITEM_TYPE_MAGICFIELD) @@ -1500,9 +1308,8 @@ void LuaScriptInterface::registerFunctions() registerEnum(ITEM_TYPE_BED) registerEnum(ITEM_TYPE_KEY) registerEnum(ITEM_TYPE_RUNE) + registerEnum(ITEM_TYPE_CHEST) - registerEnum(ITEM_BAG) - registerEnum(ITEM_SHOPPING_BAG) registerEnum(ITEM_GOLD_COIN) registerEnum(ITEM_PLATINUM_COIN) registerEnum(ITEM_CRYSTAL_COIN) @@ -1524,10 +1331,8 @@ void LuaScriptInterface::registerFunctions() registerEnum(ITEM_ENERGYFIELD_NOPVP) registerEnum(ITEM_MAGICWALL) registerEnum(ITEM_MAGICWALL_PERSISTENT) - registerEnum(ITEM_MAGICWALL_SAFE) registerEnum(ITEM_WILDGROWTH) registerEnum(ITEM_WILDGROWTH_PERSISTENT) - registerEnum(ITEM_WILDGROWTH_SAFE) registerEnum(PlayerFlag_CannotUseCombat) registerEnum(PlayerFlag_CannotAttackPlayer) @@ -1567,38 +1372,17 @@ void LuaScriptInterface::registerFunctions() registerEnum(PlayerFlag_IgnoreWeaponCheck) registerEnum(PlayerFlag_CannotBeMuted) registerEnum(PlayerFlag_IsAlwaysPremium) + registerEnum(PlayerFlag_SpecialMoveUse) registerEnum(PLAYERSEX_FEMALE) registerEnum(PLAYERSEX_MALE) - registerEnum(REPORT_REASON_NAMEINAPPROPRIATE) - registerEnum(REPORT_REASON_NAMEPOORFORMATTED) - registerEnum(REPORT_REASON_NAMEADVERTISING) - registerEnum(REPORT_REASON_NAMEUNFITTING) - registerEnum(REPORT_REASON_NAMERULEVIOLATION) - registerEnum(REPORT_REASON_INSULTINGSTATEMENT) - registerEnum(REPORT_REASON_SPAMMING) - registerEnum(REPORT_REASON_ADVERTISINGSTATEMENT) - registerEnum(REPORT_REASON_UNFITTINGSTATEMENT) - registerEnum(REPORT_REASON_LANGUAGESTATEMENT) - registerEnum(REPORT_REASON_DISCLOSURE) - registerEnum(REPORT_REASON_RULEVIOLATION) - registerEnum(REPORT_REASON_STATEMENT_BUGABUSE) - registerEnum(REPORT_REASON_UNOFFICIALSOFTWARE) - registerEnum(REPORT_REASON_PRETENDING) - registerEnum(REPORT_REASON_HARASSINGOWNERS) - registerEnum(REPORT_REASON_FALSEINFO) - registerEnum(REPORT_REASON_ACCOUNTSHARING) - registerEnum(REPORT_REASON_STEALINGDATA) - registerEnum(REPORT_REASON_SERVICEATTACKING) - registerEnum(REPORT_REASON_SERVICEAGREEMENT) - - registerEnum(REPORT_TYPE_NAME) - registerEnum(REPORT_TYPE_STATEMENT) - registerEnum(REPORT_TYPE_BOT) - registerEnum(VOCATION_NONE) + registerEnum(FIGHTMODE_ATTACK) + registerEnum(FIGHTMODE_BALANCED) + registerEnum(FIGHTMODE_DEFENSE) + registerEnum(SKILL_FIST) registerEnum(SKILL_CLUB) registerEnum(SKILL_SWORD) @@ -1609,34 +1393,36 @@ void LuaScriptInterface::registerFunctions() registerEnum(SKILL_MAGLEVEL) registerEnum(SKILL_LEVEL) - registerEnum(SPECIALSKILL_CRITICALHITCHANCE) - registerEnum(SPECIALSKILL_CRITICALHITAMOUNT) - registerEnum(SPECIALSKILL_LIFELEECHCHANCE) - registerEnum(SPECIALSKILL_LIFELEECHAMOUNT) - registerEnum(SPECIALSKILL_MANALEECHCHANCE) - registerEnum(SPECIALSKILL_MANALEECHAMOUNT) - registerEnum(SKULL_NONE) registerEnum(SKULL_YELLOW) registerEnum(SKULL_GREEN) registerEnum(SKULL_WHITE) registerEnum(SKULL_RED) - registerEnum(SKULL_BLACK) - registerEnum(SKULL_ORANGE) + + registerEnum(FLUID_NONE) + registerEnum(FLUID_WATER) + registerEnum(FLUID_WINE) + registerEnum(FLUID_BEER) + registerEnum(FLUID_MUD) + registerEnum(FLUID_BLOOD) + registerEnum(FLUID_SLIME) + registerEnum(FLUID_OIL) + registerEnum(FLUID_URINE) + registerEnum(FLUID_MILK) + registerEnum(FLUID_MANAFLUID) + registerEnum(FLUID_LIFEFLUID) + registerEnum(FLUID_LEMONADE) + registerEnum(FLUID_RUM) + registerEnum(FLUID_COCONUTMILK) + registerEnum(FLUID_FRUITJUICE) registerEnum(TALKTYPE_SAY) registerEnum(TALKTYPE_WHISPER) registerEnum(TALKTYPE_YELL) - registerEnum(TALKTYPE_PRIVATE_FROM) - registerEnum(TALKTYPE_PRIVATE_TO) registerEnum(TALKTYPE_CHANNEL_Y) registerEnum(TALKTYPE_CHANNEL_O) - registerEnum(TALKTYPE_PRIVATE_NP) - registerEnum(TALKTYPE_PRIVATE_PN) registerEnum(TALKTYPE_BROADCAST) registerEnum(TALKTYPE_CHANNEL_R1) - registerEnum(TALKTYPE_PRIVATE_RED_FROM) - registerEnum(TALKTYPE_PRIVATE_RED_TO) registerEnum(TALKTYPE_MONSTER_SAY) registerEnum(TALKTYPE_MONSTER_YELL) registerEnum(TALKTYPE_CHANNEL_R2) @@ -1649,9 +1435,7 @@ void LuaScriptInterface::registerFunctions() registerEnum(TEXTCOLOR_LIGHTGREY) registerEnum(TEXTCOLOR_SKYBLUE) registerEnum(TEXTCOLOR_PURPLE) - registerEnum(TEXTCOLOR_ELECTRICPURPLE) registerEnum(TEXTCOLOR_RED) - registerEnum(TEXTCOLOR_PASTELRED) registerEnum(TEXTCOLOR_ORANGE) registerEnum(TEXTCOLOR_YELLOW) registerEnum(TEXTCOLOR_WHITE_EXP) @@ -1662,16 +1446,10 @@ void LuaScriptInterface::registerFunctions() registerEnum(TILESTATE_NOPVPZONE) registerEnum(TILESTATE_NOLOGOUT) registerEnum(TILESTATE_PVPZONE) - registerEnum(TILESTATE_FLOORCHANGE) - registerEnum(TILESTATE_FLOORCHANGE_DOWN) - registerEnum(TILESTATE_FLOORCHANGE_NORTH) - registerEnum(TILESTATE_FLOORCHANGE_SOUTH) - registerEnum(TILESTATE_FLOORCHANGE_EAST) - registerEnum(TILESTATE_FLOORCHANGE_WEST) + registerEnum(TILESTATE_REFRESH) registerEnum(TILESTATE_TELEPORT) registerEnum(TILESTATE_MAGICFIELD) registerEnum(TILESTATE_MAILBOX) - registerEnum(TILESTATE_TRASHHOLDER) registerEnum(TILESTATE_BED) registerEnum(TILESTATE_DEPOT) registerEnum(TILESTATE_BLOCKSOLID) @@ -1680,8 +1458,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(TILESTATE_IMMOVABLEBLOCKPATH) registerEnum(TILESTATE_IMMOVABLENOFIELDBLOCKPATH) registerEnum(TILESTATE_NOFIELDBLOCKPATH) - registerEnum(TILESTATE_FLOORCHANGE_SOUTH_ALT) - registerEnum(TILESTATE_FLOORCHANGE_EAST_ALT) registerEnum(TILESTATE_SUPPORTS_HANGABLE) registerEnum(WEAPON_NONE) @@ -1733,35 +1509,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(GUEST_LIST) registerEnum(SUBOWNER_LIST) - // Use with npc:setSpeechBubble - registerEnum(SPEECHBUBBLE_NONE) - registerEnum(SPEECHBUBBLE_NORMAL) - registerEnum(SPEECHBUBBLE_TRADE) - registerEnum(SPEECHBUBBLE_QUEST) - registerEnum(SPEECHBUBBLE_QUESTTRADER) - - // Use with player:addMapMark - registerEnum(MAPMARK_TICK) - registerEnum(MAPMARK_QUESTION) - registerEnum(MAPMARK_EXCLAMATION) - registerEnum(MAPMARK_STAR) - registerEnum(MAPMARK_CROSS) - registerEnum(MAPMARK_TEMPLE) - registerEnum(MAPMARK_KISS) - registerEnum(MAPMARK_SHOVEL) - registerEnum(MAPMARK_SWORD) - registerEnum(MAPMARK_FLAG) - registerEnum(MAPMARK_LOCK) - registerEnum(MAPMARK_BAG) - registerEnum(MAPMARK_SKULL) - registerEnum(MAPMARK_DOLLAR) - registerEnum(MAPMARK_REDNORTH) - registerEnum(MAPMARK_REDSOUTH) - registerEnum(MAPMARK_REDEAST) - registerEnum(MAPMARK_REDWEST) - registerEnum(MAPMARK_GREENNORTH) - registerEnum(MAPMARK_GREENSOUTH) - // Use with Game.getReturnMessage registerEnum(RETURNVALUE_NOERROR) registerEnum(RETURNVALUE_NOTPOSSIBLE) @@ -1822,7 +1569,7 @@ void LuaScriptInterface::registerFunctions() registerEnum(RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL) registerEnum(RETURNVALUE_CANNOTCONJUREITEMHERE) registerEnum(RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS) - registerEnum(RETURNVALUE_NAMEISTOOAMBIGUOUS) + registerEnum(RETURNVALUE_NAMEISTOOAMBIGIOUS) registerEnum(RETURNVALUE_CANONLYUSEONESHIELD) registerEnum(RETURNVALUE_NOPARTYMEMBERSINRANGE) registerEnum(RETURNVALUE_YOUARENOTTHEOWNER) @@ -1831,10 +1578,11 @@ void LuaScriptInterface::registerFunctions() registerEnum(RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE) registerEnum(RETURNVALUE_TRADEPLAYERHIGHESTBIDDER) registerEnum(RETURNVALUE_YOUCANNOTTRADETHISHOUSE) - + registerEnum(RELOAD_TYPE_ALL) registerEnum(RELOAD_TYPE_ACTIONS) registerEnum(RELOAD_TYPE_CHAT) + registerEnum(RELOAD_TYPE_COMMANDS) registerEnum(RELOAD_TYPE_CONFIG) registerEnum(RELOAD_TYPE_CREATURESCRIPTS) registerEnum(RELOAD_TYPE_EVENTS) @@ -1847,28 +1595,10 @@ void LuaScriptInterface::registerFunctions() registerEnum(RELOAD_TYPE_NPCS) registerEnum(RELOAD_TYPE_QUESTS) registerEnum(RELOAD_TYPE_RAIDS) - registerEnum(RELOAD_TYPE_SCRIPTS) registerEnum(RELOAD_TYPE_SPELLS) registerEnum(RELOAD_TYPE_TALKACTIONS) registerEnum(RELOAD_TYPE_WEAPONS) - registerEnum(ZONE_PROTECTION) - registerEnum(ZONE_NOPVP) - registerEnum(ZONE_PVP) - registerEnum(ZONE_NOLOGOUT) - registerEnum(ZONE_NORMAL) - - registerEnum(MAX_LOOTCHANCE) - - registerEnum(SPELL_INSTANT) - registerEnum(SPELL_RUNE) - - registerEnum(MONSTERS_EVENT_THINK) - registerEnum(MONSTERS_EVENT_APPEAR) - registerEnum(MONSTERS_EVENT_DISAPPEAR) - registerEnum(MONSTERS_EVENT_MOVE) - registerEnum(MONSTERS_EVENT_SAY) - // _G registerGlobalVariable("INDEX_WHEREEVER", INDEX_WHEREEVER); registerGlobalBoolean("VIRTUAL_PARENT", true); @@ -1881,7 +1611,6 @@ void LuaScriptInterface::registerFunctions() registerEnumIn("configKeys", ConfigManager::ALLOW_CHANGEOUTFIT) registerEnumIn("configKeys", ConfigManager::ONE_PLAYER_ON_ACCOUNT) - registerEnumIn("configKeys", ConfigManager::AIMBOT_HOTKEY_ENABLED) registerEnumIn("configKeys", ConfigManager::REMOVE_RUNE_CHARGES) registerEnumIn("configKeys", ConfigManager::EXPERIENCE_FROM_PLAYERS) registerEnumIn("configKeys", ConfigManager::FREE_PREMIUM) @@ -1889,19 +1618,10 @@ void LuaScriptInterface::registerFunctions() registerEnumIn("configKeys", ConfigManager::ALLOW_CLONES) registerEnumIn("configKeys", ConfigManager::BIND_ONLY_GLOBAL_ADDRESS) registerEnumIn("configKeys", ConfigManager::OPTIMIZE_DATABASE) - registerEnumIn("configKeys", ConfigManager::MARKET_PREMIUM) - registerEnumIn("configKeys", ConfigManager::EMOTE_SPELLS) registerEnumIn("configKeys", ConfigManager::STAMINA_SYSTEM) registerEnumIn("configKeys", ConfigManager::WARN_UNSAFE_SCRIPTS) registerEnumIn("configKeys", ConfigManager::CONVERT_UNSAFE_SCRIPTS) - registerEnumIn("configKeys", ConfigManager::CLASSIC_EQUIPMENT_SLOTS) - registerEnumIn("configKeys", ConfigManager::CLASSIC_ATTACK_SPEED) - registerEnumIn("configKeys", ConfigManager::SERVER_SAVE_NOTIFY_MESSAGE) - registerEnumIn("configKeys", ConfigManager::SERVER_SAVE_NOTIFY_DURATION) - registerEnumIn("configKeys", ConfigManager::SERVER_SAVE_CLEAN_MAP) - registerEnumIn("configKeys", ConfigManager::SERVER_SAVE_CLOSE) - registerEnumIn("configKeys", ConfigManager::SERVER_SAVE_SHUTDOWN) - registerEnumIn("configKeys", ConfigManager::ONLINE_OFFLINE_CHARLIST) + registerEnumIn("configKeys", ConfigManager::TELEPORT_NEWBIES) registerEnumIn("configKeys", ConfigManager::MAP_NAME) registerEnumIn("configKeys", ConfigManager::HOUSE_RENT_PERIOD) @@ -1931,9 +1651,6 @@ void LuaScriptInterface::registerFunctions() registerEnumIn("configKeys", ConfigManager::RATE_LOOT) registerEnumIn("configKeys", ConfigManager::RATE_MAGIC) registerEnumIn("configKeys", ConfigManager::RATE_SPAWN) - registerEnumIn("configKeys", ConfigManager::HOUSE_PRICE) - registerEnumIn("configKeys", ConfigManager::KILLS_TO_RED) - registerEnumIn("configKeys", ConfigManager::KILLS_TO_BLACK) registerEnumIn("configKeys", ConfigManager::MAX_MESSAGEBUFFER) registerEnumIn("configKeys", ConfigManager::ACTIONS_DELAY_INTERVAL) registerEnumIn("configKeys", ConfigManager::EX_ACTIONS_DELAY_INTERVAL) @@ -1941,17 +1658,24 @@ void LuaScriptInterface::registerFunctions() registerEnumIn("configKeys", ConfigManager::PROTECTION_LEVEL) registerEnumIn("configKeys", ConfigManager::DEATH_LOSE_PERCENT) registerEnumIn("configKeys", ConfigManager::STATUSQUERY_TIMEOUT) - registerEnumIn("configKeys", ConfigManager::FRAG_TIME) registerEnumIn("configKeys", ConfigManager::WHITE_SKULL_TIME) + registerEnumIn("configKeys", ConfigManager::RED_SKULL_TIME) + registerEnumIn("configKeys", ConfigManager::KILLS_DAY_RED_SKULL) + registerEnumIn("configKeys", ConfigManager::KILLS_WEEK_RED_SKULL) + registerEnumIn("configKeys", ConfigManager::KILLS_MONTH_RED_SKULL) + registerEnumIn("configKeys", ConfigManager::KILLS_DAY_BANISHMENT) + registerEnumIn("configKeys", ConfigManager::KILLS_WEEK_BANISHMENT) + registerEnumIn("configKeys", ConfigManager::KILLS_MONTH_BANISHMENT) registerEnumIn("configKeys", ConfigManager::GAME_PORT) registerEnumIn("configKeys", ConfigManager::LOGIN_PORT) registerEnumIn("configKeys", ConfigManager::STATUS_PORT) registerEnumIn("configKeys", ConfigManager::STAIRHOP_DELAY) - registerEnumIn("configKeys", ConfigManager::MARKET_OFFER_DURATION) - registerEnumIn("configKeys", ConfigManager::CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES) - registerEnumIn("configKeys", ConfigManager::MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER) registerEnumIn("configKeys", ConfigManager::EXP_FROM_PLAYERS_LEVEL_RANGE) registerEnumIn("configKeys", ConfigManager::MAX_PACKETS_PER_SECOND) + registerEnumIn("configKeys", ConfigManager::NEWBIE_TOWN) + registerEnumIn("configKeys", ConfigManager::NEWBIE_LEVEL_THRESHOLD) + registerEnumIn("configKeys", ConfigManager::BLOCK_HEIGHT) + registerEnumIn("configKeys", ConfigManager::DROP_ITEMS) // os registerMethod("os", "mtime", LuaScriptInterface::luaSystemTime); @@ -1987,12 +1711,9 @@ void LuaScriptInterface::registerFunctions() registerMethod("Game", "createMonster", LuaScriptInterface::luaGameCreateMonster); registerMethod("Game", "createNpc", LuaScriptInterface::luaGameCreateNpc); registerMethod("Game", "createTile", LuaScriptInterface::luaGameCreateTile); - registerMethod("Game", "createMonsterType", LuaScriptInterface::luaGameCreateMonsterType); registerMethod("Game", "startRaid", LuaScriptInterface::luaGameStartRaid); - registerMethod("Game", "getClientVersion", LuaScriptInterface::luaGameGetClientVersion); - registerMethod("Game", "reload", LuaScriptInterface::luaGameReload); // Variant @@ -2013,6 +1734,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Position", "sendMagicEffect", LuaScriptInterface::luaPositionSendMagicEffect); registerMethod("Position", "sendDistanceEffect", LuaScriptInterface::luaPositionSendDistanceEffect); + registerMethod("Position", "sendMonsterSay", LuaScriptInterface::luaPositionSendMonsterSay); // Tile registerClass("Tile", "", LuaScriptInterface::luaTileCreate); @@ -2052,8 +1774,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Tile", "hasFlag", LuaScriptInterface::luaTileHasFlag); registerMethod("Tile", "queryAdd", LuaScriptInterface::luaTileQueryAdd); - registerMethod("Tile", "addItem", LuaScriptInterface::luaTileAddItem); - registerMethod("Tile", "addItemEx", LuaScriptInterface::luaTileAddItemEx); registerMethod("Tile", "getHouse", LuaScriptInterface::luaTileGetHouse); @@ -2084,36 +1804,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("NetworkMessage", "skipBytes", LuaScriptInterface::luaNetworkMessageSkipBytes); registerMethod("NetworkMessage", "sendToPlayer", LuaScriptInterface::luaNetworkMessageSendToPlayer); - // ModalWindow - registerClass("ModalWindow", "", LuaScriptInterface::luaModalWindowCreate); - registerMetaMethod("ModalWindow", "__eq", LuaScriptInterface::luaUserdataCompare); - registerMetaMethod("ModalWindow", "__gc", LuaScriptInterface::luaModalWindowDelete); - registerMethod("ModalWindow", "delete", LuaScriptInterface::luaModalWindowDelete); - - registerMethod("ModalWindow", "getId", LuaScriptInterface::luaModalWindowGetId); - registerMethod("ModalWindow", "getTitle", LuaScriptInterface::luaModalWindowGetTitle); - registerMethod("ModalWindow", "getMessage", LuaScriptInterface::luaModalWindowGetMessage); - - registerMethod("ModalWindow", "setTitle", LuaScriptInterface::luaModalWindowSetTitle); - registerMethod("ModalWindow", "setMessage", LuaScriptInterface::luaModalWindowSetMessage); - - registerMethod("ModalWindow", "getButtonCount", LuaScriptInterface::luaModalWindowGetButtonCount); - registerMethod("ModalWindow", "getChoiceCount", LuaScriptInterface::luaModalWindowGetChoiceCount); - - registerMethod("ModalWindow", "addButton", LuaScriptInterface::luaModalWindowAddButton); - registerMethod("ModalWindow", "addChoice", LuaScriptInterface::luaModalWindowAddChoice); - - registerMethod("ModalWindow", "getDefaultEnterButton", LuaScriptInterface::luaModalWindowGetDefaultEnterButton); - registerMethod("ModalWindow", "setDefaultEnterButton", LuaScriptInterface::luaModalWindowSetDefaultEnterButton); - - registerMethod("ModalWindow", "getDefaultEscapeButton", LuaScriptInterface::luaModalWindowGetDefaultEscapeButton); - registerMethod("ModalWindow", "setDefaultEscapeButton", LuaScriptInterface::luaModalWindowSetDefaultEscapeButton); - - registerMethod("ModalWindow", "hasPriority", LuaScriptInterface::luaModalWindowHasPriority); - registerMethod("ModalWindow", "setPriority", LuaScriptInterface::luaModalWindowSetPriority); - - registerMethod("ModalWindow", "sendToPlayer", LuaScriptInterface::luaModalWindowSendToPlayer); - // Item registerClass("Item", "", LuaScriptInterface::luaItemCreate); registerMetaMethod("Item", "__eq", LuaScriptInterface::luaUserdataCompare); @@ -2129,9 +1819,11 @@ void LuaScriptInterface::registerFunctions() registerMethod("Item", "split", LuaScriptInterface::luaItemSplit); registerMethod("Item", "remove", LuaScriptInterface::luaItemRemove); - registerMethod("Item", "getUniqueId", LuaScriptInterface::luaItemGetUniqueId); + registerMethod("Item", "getMovementId", LuaScriptInterface::luaItemGetMovementId); + registerMethod("Item", "setMovementId", LuaScriptInterface::luaItemSetMovementId); registerMethod("Item", "getActionId", LuaScriptInterface::luaItemGetActionId); registerMethod("Item", "setActionId", LuaScriptInterface::luaItemSetActionId); + registerMethod("Item", "getUniqueId", LuaScriptInterface::luaItemGetUniqueId); registerMethod("Item", "getCount", LuaScriptInterface::luaItemGetCount); registerMethod("Item", "getCharges", LuaScriptInterface::luaItemGetCharges); @@ -2151,9 +1843,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Item", "getAttribute", LuaScriptInterface::luaItemGetAttribute); registerMethod("Item", "setAttribute", LuaScriptInterface::luaItemSetAttribute); registerMethod("Item", "removeAttribute", LuaScriptInterface::luaItemRemoveAttribute); - registerMethod("Item", "getCustomAttribute", LuaScriptInterface::luaItemGetCustomAttribute); - registerMethod("Item", "setCustomAttribute", LuaScriptInterface::luaItemSetCustomAttribute); - registerMethod("Item", "removeCustomAttribute", LuaScriptInterface::luaItemRemoveCustomAttribute); registerMethod("Item", "moveTo", LuaScriptInterface::luaItemMoveTo); registerMethod("Item", "transform", LuaScriptInterface::luaItemTransform); @@ -2162,7 +1851,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Item", "getDescription", LuaScriptInterface::luaItemGetDescription); registerMethod("Item", "hasProperty", LuaScriptInterface::luaItemHasProperty); - registerMethod("Item", "isLoadedFromMap", LuaScriptInterface::luaItemIsLoadedFromMap); // Container registerClass("Container", "Item", LuaScriptInterface::luaContainerCreate); @@ -2171,7 +1859,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Container", "getSize", LuaScriptInterface::luaContainerGetSize); registerMethod("Container", "getCapacity", LuaScriptInterface::luaContainerGetCapacity); registerMethod("Container", "getEmptySlots", LuaScriptInterface::luaContainerGetEmptySlots); - registerMethod("Container", "getContentDescription", LuaScriptInterface::luaContainerGetContentDescription); + registerMethod("Container", "getItemHoldingCount", LuaScriptInterface::luaContainerGetItemHoldingCount); registerMethod("Container", "getItemCountById", LuaScriptInterface::luaContainerGetItemCountById); @@ -2179,7 +1867,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Container", "hasItem", LuaScriptInterface::luaContainerHasItem); registerMethod("Container", "addItem", LuaScriptInterface::luaContainerAddItem); registerMethod("Container", "addItemEx", LuaScriptInterface::luaContainerAddItemEx); - registerMethod("Container", "getCorpseOwner", LuaScriptInterface::luaContainerGetCorpseOwner); // Teleport registerClass("Teleport", "Item", LuaScriptInterface::luaTeleportCreate); @@ -2199,8 +1886,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Creature", "isRemoved", LuaScriptInterface::luaCreatureIsRemoved); registerMethod("Creature", "isCreature", LuaScriptInterface::luaCreatureIsCreature); registerMethod("Creature", "isInGhostMode", LuaScriptInterface::luaCreatureIsInGhostMode); - registerMethod("Creature", "isHealthHidden", LuaScriptInterface::luaCreatureIsHealthHidden); - registerMethod("Creature", "isImmune", LuaScriptInterface::luaCreatureIsImmune); registerMethod("Creature", "canSee", LuaScriptInterface::luaCreatureCanSee); registerMethod("Creature", "canSeeCreature", LuaScriptInterface::luaCreatureCanSeeCreature); @@ -2227,7 +1912,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Creature", "changeSpeed", LuaScriptInterface::luaCreatureChangeSpeed); registerMethod("Creature", "setDropLoot", LuaScriptInterface::luaCreatureSetDropLoot); - registerMethod("Creature", "setSkillLoss", LuaScriptInterface::luaCreatureSetSkillLoss); registerMethod("Creature", "getPosition", LuaScriptInterface::luaCreatureGetPosition); registerMethod("Creature", "getTile", LuaScriptInterface::luaCreatureGetTile); @@ -2235,7 +1919,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Creature", "setDirection", LuaScriptInterface::luaCreatureSetDirection); registerMethod("Creature", "getHealth", LuaScriptInterface::luaCreatureGetHealth); - registerMethod("Creature", "setHealth", LuaScriptInterface::luaCreatureSetHealth); registerMethod("Creature", "addHealth", LuaScriptInterface::luaCreatureAddHealth); registerMethod("Creature", "getMaxHealth", LuaScriptInterface::luaCreatureGetMaxHealth); registerMethod("Creature", "setMaxHealth", LuaScriptInterface::luaCreatureSetMaxHealth); @@ -2250,7 +1933,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Creature", "getCondition", LuaScriptInterface::luaCreatureGetCondition); registerMethod("Creature", "addCondition", LuaScriptInterface::luaCreatureAddCondition); registerMethod("Creature", "removeCondition", LuaScriptInterface::luaCreatureRemoveCondition); - registerMethod("Creature", "hasCondition", LuaScriptInterface::luaCreatureHasCondition); registerMethod("Creature", "remove", LuaScriptInterface::luaCreatureRemove); registerMethod("Creature", "teleportTo", LuaScriptInterface::luaCreatureTeleportTo); @@ -2263,9 +1945,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Creature", "getDescription", LuaScriptInterface::luaCreatureGetDescription); registerMethod("Creature", "getPathTo", LuaScriptInterface::luaCreatureGetPathTo); - registerMethod("Creature", "move", LuaScriptInterface::luaCreatureMove); - - registerMethod("Creature", "getZone", LuaScriptInterface::luaCreatureGetZone); // Player registerClass("Player", "Creature", LuaScriptInterface::luaPlayerCreate); @@ -2278,6 +1957,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "getAccountId", LuaScriptInterface::luaPlayerGetAccountId); registerMethod("Player", "getLastLoginSaved", LuaScriptInterface::luaPlayerGetLastLoginSaved); registerMethod("Player", "getLastLogout", LuaScriptInterface::luaPlayerGetLastLogout); + registerMethod("Player", "hasFlag", LuaScriptInterface::luaPlayerHasFlag); registerMethod("Player", "getAccountType", LuaScriptInterface::luaPlayerGetAccountType); registerMethod("Player", "setAccountType", LuaScriptInterface::luaPlayerSetAccountType); @@ -2288,10 +1968,10 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "getFreeCapacity", LuaScriptInterface::luaPlayerGetFreeCapacity); registerMethod("Player", "getDepotChest", LuaScriptInterface::luaPlayerGetDepotChest); - registerMethod("Player", "getInbox", LuaScriptInterface::luaPlayerGetInbox); - registerMethod("Player", "getSkullTime", LuaScriptInterface::luaPlayerGetSkullTime); - registerMethod("Player", "setSkullTime", LuaScriptInterface::luaPlayerSetSkullTime); + registerMethod("Player", "getMurderTimestamps", LuaScriptInterface::luaPlayerGetMurderTimestamps); + registerMethod("Player", "getPlayerKillerEnd", LuaScriptInterface::luaPlayerGetPlayerKillerEnd); + registerMethod("Player", "setPlayerKillerEnd", LuaScriptInterface::luaPlayerSetPlayerKillerEnd); registerMethod("Player", "getDeathPenalty", LuaScriptInterface::luaPlayerGetDeathPenalty); registerMethod("Player", "getExperience", LuaScriptInterface::luaPlayerGetExperience); @@ -2316,17 +1996,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "getSkillPercent", LuaScriptInterface::luaPlayerGetSkillPercent); registerMethod("Player", "getSkillTries", LuaScriptInterface::luaPlayerGetSkillTries); registerMethod("Player", "addSkillTries", LuaScriptInterface::luaPlayerAddSkillTries); - registerMethod("Player", "getSpecialSkill", LuaScriptInterface::luaPlayerGetSpecialSkill); - registerMethod("Player", "addSpecialSkill", LuaScriptInterface::luaPlayerAddSpecialSkill); - - registerMethod("Player", "addOfflineTrainingTime", LuaScriptInterface::luaPlayerAddOfflineTrainingTime); - registerMethod("Player", "getOfflineTrainingTime", LuaScriptInterface::luaPlayerGetOfflineTrainingTime); - registerMethod("Player", "removeOfflineTrainingTime", LuaScriptInterface::luaPlayerRemoveOfflineTrainingTime); - - registerMethod("Player", "addOfflineTrainingTries", LuaScriptInterface::luaPlayerAddOfflineTrainingTries); - - registerMethod("Player", "getOfflineTrainingSkill", LuaScriptInterface::luaPlayerGetOfflineTrainingSkill); - registerMethod("Player", "setOfflineTrainingSkill", LuaScriptInterface::luaPlayerSetOfflineTrainingSkill); registerMethod("Player", "getItemCount", LuaScriptInterface::luaPlayerGetItemCount); registerMethod("Player", "getItemById", LuaScriptInterface::luaPlayerGetItemById); @@ -2376,7 +2045,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "showTextDialog", LuaScriptInterface::luaPlayerShowTextDialog); registerMethod("Player", "sendTextMessage", LuaScriptInterface::luaPlayerSendTextMessage); - registerMethod("Player", "sendChannelMessage", LuaScriptInterface::luaPlayerSendChannelMessage); registerMethod("Player", "sendPrivateMessage", LuaScriptInterface::luaPlayerSendPrivateMessage); registerMethod("Player", "channelSay", LuaScriptInterface::luaPlayerChannelSay); registerMethod("Player", "openChannel", LuaScriptInterface::luaPlayerOpenChannel); @@ -2392,10 +2060,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "hasOutfit", LuaScriptInterface::luaPlayerHasOutfit); registerMethod("Player", "sendOutfitWindow", LuaScriptInterface::luaPlayerSendOutfitWindow); - registerMethod("Player", "addMount", LuaScriptInterface::luaPlayerAddMount); - registerMethod("Player", "removeMount", LuaScriptInterface::luaPlayerRemoveMount); - registerMethod("Player", "hasMount", LuaScriptInterface::luaPlayerHasMount); - registerMethod("Player", "getPremiumDays", LuaScriptInterface::luaPlayerGetPremiumDays); registerMethod("Player", "addPremiumDays", LuaScriptInterface::luaPlayerAddPremiumDays); registerMethod("Player", "removePremiumDays", LuaScriptInterface::luaPlayerRemovePremiumDays); @@ -2409,19 +2073,12 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "forgetSpell", LuaScriptInterface::luaPlayerForgetSpell); registerMethod("Player", "hasLearnedSpell", LuaScriptInterface::luaPlayerHasLearnedSpell); - registerMethod("Player", "sendTutorial", LuaScriptInterface::luaPlayerSendTutorial); - registerMethod("Player", "addMapMark", LuaScriptInterface::luaPlayerAddMapMark); - registerMethod("Player", "save", LuaScriptInterface::luaPlayerSave); - registerMethod("Player", "popupFYI", LuaScriptInterface::luaPlayerPopupFYI); registerMethod("Player", "isPzLocked", LuaScriptInterface::luaPlayerIsPzLocked); registerMethod("Player", "getClient", LuaScriptInterface::luaPlayerGetClient); - registerMethod("Player", "getHouse", LuaScriptInterface::luaPlayerGetHouse); - registerMethod("Player", "sendHouseWindow", LuaScriptInterface::luaPlayerSendHouseWindow); - registerMethod("Player", "setEditHouse", LuaScriptInterface::luaPlayerSetEditHouse); registerMethod("Player", "setGhostMode", LuaScriptInterface::luaPlayerSetGhostMode); @@ -2429,12 +2086,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Player", "getContainerById", LuaScriptInterface::luaPlayerGetContainerById); registerMethod("Player", "getContainerIndex", LuaScriptInterface::luaPlayerGetContainerIndex); - registerMethod("Player", "getInstantSpells", LuaScriptInterface::luaPlayerGetInstantSpells); - registerMethod("Player", "canCast", LuaScriptInterface::luaPlayerCanCast); - - registerMethod("Player", "hasChaseMode", LuaScriptInterface::luaPlayerHasChaseMode); - registerMethod("Player", "hasSecureMode", LuaScriptInterface::luaPlayerHasSecureMode); - registerMethod("Player", "getFightMode", LuaScriptInterface::luaPlayerGetFightMode); + registerMethod("Player", "getTotalDamage", LuaScriptInterface::luaPlayerGetTotalDamage); // Monster registerClass("Monster", "Creature", LuaScriptInterface::luaMonsterCreate); @@ -2475,9 +2127,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Npc", "setMasterPos", LuaScriptInterface::luaNpcSetMasterPos); - registerMethod("Npc", "getSpeechBubble", LuaScriptInterface::luaNpcGetSpeechBubble); - registerMethod("Npc", "setSpeechBubble", LuaScriptInterface::luaNpcSetSpeechBubble); - // Guild registerClass("Guild", "", LuaScriptInterface::luaGuildCreate); registerMetaMethod("Guild", "__eq", LuaScriptInterface::luaUserdataCompare); @@ -2490,9 +2139,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Guild", "getRankById", LuaScriptInterface::luaGuildGetRankById); registerMethod("Guild", "getRankByLevel", LuaScriptInterface::luaGuildGetRankByLevel); - registerMethod("Guild", "getMotd", LuaScriptInterface::luaGuildGetMotd); - registerMethod("Guild", "setMotd", LuaScriptInterface::luaGuildSetMotd); - // Group registerClass("Group", "", LuaScriptInterface::luaGroupCreate); registerMetaMethod("Group", "__eq", LuaScriptInterface::luaUserdataCompare); @@ -2503,14 +2149,12 @@ void LuaScriptInterface::registerFunctions() registerMethod("Group", "getAccess", LuaScriptInterface::luaGroupGetAccess); registerMethod("Group", "getMaxDepotItems", LuaScriptInterface::luaGroupGetMaxDepotItems); registerMethod("Group", "getMaxVipEntries", LuaScriptInterface::luaGroupGetMaxVipEntries); - registerMethod("Group", "hasFlag", LuaScriptInterface::luaGroupHasFlag); // Vocation registerClass("Vocation", "", LuaScriptInterface::luaVocationCreate); registerMetaMethod("Vocation", "__eq", LuaScriptInterface::luaUserdataCompare); registerMethod("Vocation", "getId", LuaScriptInterface::luaVocationGetId); - registerMethod("Vocation", "getClientId", LuaScriptInterface::luaVocationGetClientId); registerMethod("Vocation", "getName", LuaScriptInterface::luaVocationGetName); registerMethod("Vocation", "getDescription", LuaScriptInterface::luaVocationGetDescription); @@ -2563,18 +2207,13 @@ void LuaScriptInterface::registerFunctions() registerMethod("House", "getDoors", LuaScriptInterface::luaHouseGetDoors); registerMethod("House", "getDoorCount", LuaScriptInterface::luaHouseGetDoorCount); - registerMethod("House", "getDoorIdByPosition", LuaScriptInterface::luaHouseGetDoorIdByPosition); registerMethod("House", "getTiles", LuaScriptInterface::luaHouseGetTiles); - registerMethod("House", "getItems", LuaScriptInterface::luaHouseGetItems); registerMethod("House", "getTileCount", LuaScriptInterface::luaHouseGetTileCount); - registerMethod("House", "canEditAccessList", LuaScriptInterface::luaHouseCanEditAccessList); registerMethod("House", "getAccessList", LuaScriptInterface::luaHouseGetAccessList); registerMethod("House", "setAccessList", LuaScriptInterface::luaHouseSetAccessList); - registerMethod("House", "kickPlayer", LuaScriptInterface::luaHouseKickPlayer); - // ItemType registerClass("ItemType", "", LuaScriptInterface::luaItemTypeCreate); registerMetaMethod("ItemType", "__eq", LuaScriptInterface::luaUserdataCompare); @@ -2582,51 +2221,47 @@ void LuaScriptInterface::registerFunctions() registerMethod("ItemType", "isCorpse", LuaScriptInterface::luaItemTypeIsCorpse); registerMethod("ItemType", "isDoor", LuaScriptInterface::luaItemTypeIsDoor); registerMethod("ItemType", "isContainer", LuaScriptInterface::luaItemTypeIsContainer); + registerMethod("ItemType", "isChest", LuaScriptInterface::luaItemTypeIsChest); registerMethod("ItemType", "isFluidContainer", LuaScriptInterface::luaItemTypeIsFluidContainer); registerMethod("ItemType", "isMovable", LuaScriptInterface::luaItemTypeIsMovable); registerMethod("ItemType", "isRune", LuaScriptInterface::luaItemTypeIsRune); registerMethod("ItemType", "isStackable", LuaScriptInterface::luaItemTypeIsStackable); registerMethod("ItemType", "isReadable", LuaScriptInterface::luaItemTypeIsReadable); registerMethod("ItemType", "isWritable", LuaScriptInterface::luaItemTypeIsWritable); - registerMethod("ItemType", "isBlocking", LuaScriptInterface::luaItemTypeIsBlocking); - registerMethod("ItemType", "isGroundTile", LuaScriptInterface::luaItemTypeIsGroundTile); registerMethod("ItemType", "isMagicField", LuaScriptInterface::luaItemTypeIsMagicField); - registerMethod("ItemType", "isUseable", LuaScriptInterface::luaItemTypeIsUseable); - registerMethod("ItemType", "isPickupable", LuaScriptInterface::luaItemTypeIsPickupable); + registerMethod("ItemType", "isSplash", LuaScriptInterface::luaItemTypeIsSplash); + registerMethod("ItemType", "isKey", LuaScriptInterface::luaItemTypeIsKey); + registerMethod("ItemType", "isDisguised", LuaScriptInterface::luaItemTypeIsDisguised); + registerMethod("ItemType", "isDestroyable", LuaScriptInterface::luaItemTypeIsDestroyable); + registerMethod("ItemType", "isGroundTile", LuaScriptInterface::luaItemTypeIsGroundTile); registerMethod("ItemType", "getType", LuaScriptInterface::luaItemTypeGetType); registerMethod("ItemType", "getId", LuaScriptInterface::luaItemTypeGetId); - registerMethod("ItemType", "getClientId", LuaScriptInterface::luaItemTypeGetClientId); + registerMethod("ItemType", "getDisguiseId", LuaScriptInterface::luaItemTypeGetDisguiseId); registerMethod("ItemType", "getName", LuaScriptInterface::luaItemTypeGetName); registerMethod("ItemType", "getPluralName", LuaScriptInterface::luaItemTypeGetPluralName); registerMethod("ItemType", "getArticle", LuaScriptInterface::luaItemTypeGetArticle); registerMethod("ItemType", "getDescription", LuaScriptInterface::luaItemTypeGetDescription); registerMethod("ItemType", "getSlotPosition", LuaScriptInterface::luaItemTypeGetSlotPosition); + registerMethod("ItemType", "getDestroyTarget", LuaScriptInterface::luaItemTypeGetDestroyTarget); registerMethod("ItemType", "getCharges", LuaScriptInterface::luaItemTypeGetCharges); registerMethod("ItemType", "getFluidSource", LuaScriptInterface::luaItemTypeGetFluidSource); registerMethod("ItemType", "getCapacity", LuaScriptInterface::luaItemTypeGetCapacity); registerMethod("ItemType", "getWeight", LuaScriptInterface::luaItemTypeGetWeight); - registerMethod("ItemType", "getHitChance", LuaScriptInterface::luaItemTypeGetHitChance); registerMethod("ItemType", "getShootRange", LuaScriptInterface::luaItemTypeGetShootRange); registerMethod("ItemType", "getAttack", LuaScriptInterface::luaItemTypeGetAttack); registerMethod("ItemType", "getDefense", LuaScriptInterface::luaItemTypeGetDefense); - registerMethod("ItemType", "getExtraDefense", LuaScriptInterface::luaItemTypeGetExtraDefense); registerMethod("ItemType", "getArmor", LuaScriptInterface::luaItemTypeGetArmor); registerMethod("ItemType", "getWeaponType", LuaScriptInterface::luaItemTypeGetWeaponType); - registerMethod("ItemType", "getElementType", LuaScriptInterface::luaItemTypeGetElementType); - registerMethod("ItemType", "getElementDamage", LuaScriptInterface::luaItemTypeGetElementDamage); - registerMethod("ItemType", "getTransformEquipId", LuaScriptInterface::luaItemTypeGetTransformEquipId); registerMethod("ItemType", "getTransformDeEquipId", LuaScriptInterface::luaItemTypeGetTransformDeEquipId); - registerMethod("ItemType", "getDestroyId", LuaScriptInterface::luaItemTypeGetDestroyId); registerMethod("ItemType", "getDecayId", LuaScriptInterface::luaItemTypeGetDecayId); + registerMethod("ItemType", "getNutrition", LuaScriptInterface::luaItemTypeGetNutrition); registerMethod("ItemType", "getRequiredLevel", LuaScriptInterface::luaItemTypeGetRequiredLevel); - registerMethod("ItemType", "getAmmoType", LuaScriptInterface::luaItemTypeGetAmmoType); - registerMethod("ItemType", "getCorpseType", LuaScriptInterface::luaItemTypeGetCorpseType); registerMethod("ItemType", "hasSubType", LuaScriptInterface::luaItemTypeHasSubType); @@ -2638,8 +2273,7 @@ void LuaScriptInterface::registerFunctions() registerMethod("Combat", "setFormula", LuaScriptInterface::luaCombatSetFormula); registerMethod("Combat", "setArea", LuaScriptInterface::luaCombatSetArea); - registerMethod("Combat", "addCondition", LuaScriptInterface::luaCombatAddCondition); - registerMethod("Combat", "clearConditions", LuaScriptInterface::luaCombatClearConditions); + registerMethod("Combat", "setCondition", LuaScriptInterface::luaCombatSetCondition); registerMethod("Combat", "setCallback", LuaScriptInterface::luaCombatSetCallback); registerMethod("Combat", "setOrigin", LuaScriptInterface::luaCombatSetOrigin); @@ -2663,10 +2297,10 @@ void LuaScriptInterface::registerFunctions() registerMethod("Condition", "setTicks", LuaScriptInterface::luaConditionSetTicks); registerMethod("Condition", "setParameter", LuaScriptInterface::luaConditionSetParameter); - registerMethod("Condition", "setFormula", LuaScriptInterface::luaConditionSetFormula); + registerMethod("Condition", "setSpeedDelta", LuaScriptInterface::luaConditionSetSpeedDelta); registerMethod("Condition", "setOutfit", LuaScriptInterface::luaConditionSetOutfit); - registerMethod("Condition", "addDamage", LuaScriptInterface::luaConditionAddDamage); + registerMethod("Condition", "setTiming", LuaScriptInterface::luaConditionSetTiming); // MonsterType registerClass("MonsterType", "", LuaScriptInterface::luaMonsterTypeCreate); @@ -2678,109 +2312,48 @@ void LuaScriptInterface::registerFunctions() registerMethod("MonsterType", "isIllusionable", LuaScriptInterface::luaMonsterTypeIsIllusionable); registerMethod("MonsterType", "isHostile", LuaScriptInterface::luaMonsterTypeIsHostile); registerMethod("MonsterType", "isPushable", LuaScriptInterface::luaMonsterTypeIsPushable); - registerMethod("MonsterType", "isHealthHidden", LuaScriptInterface::luaMonsterTypeIsHealthHidden); + registerMethod("MonsterType", "isHealthShown", LuaScriptInterface::luaMonsterTypeIsHealthShown); registerMethod("MonsterType", "canPushItems", LuaScriptInterface::luaMonsterTypeCanPushItems); registerMethod("MonsterType", "canPushCreatures", LuaScriptInterface::luaMonsterTypeCanPushCreatures); - registerMethod("MonsterType", "name", LuaScriptInterface::luaMonsterTypeName); + registerMethod("MonsterType", "getName", LuaScriptInterface::luaMonsterTypeGetName); + registerMethod("MonsterType", "getNameDescription", LuaScriptInterface::luaMonsterTypeGetNameDescription); - registerMethod("MonsterType", "nameDescription", LuaScriptInterface::luaMonsterTypeNameDescription); + registerMethod("MonsterType", "getHealth", LuaScriptInterface::luaMonsterTypeGetHealth); + registerMethod("MonsterType", "getMaxHealth", LuaScriptInterface::luaMonsterTypeGetMaxHealth); + registerMethod("MonsterType", "getRunHealth", LuaScriptInterface::luaMonsterTypeGetRunHealth); + registerMethod("MonsterType", "getExperience", LuaScriptInterface::luaMonsterTypeGetExperience); - registerMethod("MonsterType", "health", LuaScriptInterface::luaMonsterTypeHealth); - registerMethod("MonsterType", "maxHealth", LuaScriptInterface::luaMonsterTypeMaxHealth); - registerMethod("MonsterType", "runHealth", LuaScriptInterface::luaMonsterTypeRunHealth); - registerMethod("MonsterType", "experience", LuaScriptInterface::luaMonsterTypeExperience); - - registerMethod("MonsterType", "combatImmunities", LuaScriptInterface::luaMonsterTypeCombatImmunities); - registerMethod("MonsterType", "conditionImmunities", LuaScriptInterface::luaMonsterTypeConditionImmunities); + registerMethod("MonsterType", "getCombatImmunities", LuaScriptInterface::luaMonsterTypeGetCombatImmunities); + registerMethod("MonsterType", "getConditionImmunities", LuaScriptInterface::luaMonsterTypeGetConditionImmunities); registerMethod("MonsterType", "getAttackList", LuaScriptInterface::luaMonsterTypeGetAttackList); - registerMethod("MonsterType", "addAttack", LuaScriptInterface::luaMonsterTypeAddAttack); - registerMethod("MonsterType", "getDefenseList", LuaScriptInterface::luaMonsterTypeGetDefenseList); - registerMethod("MonsterType", "addDefense", LuaScriptInterface::luaMonsterTypeAddDefense); - registerMethod("MonsterType", "getElementList", LuaScriptInterface::luaMonsterTypeGetElementList); - registerMethod("MonsterType", "addElement", LuaScriptInterface::luaMonsterTypeAddElement); registerMethod("MonsterType", "getVoices", LuaScriptInterface::luaMonsterTypeGetVoices); - registerMethod("MonsterType", "addVoice", LuaScriptInterface::luaMonsterTypeAddVoice); - registerMethod("MonsterType", "getLoot", LuaScriptInterface::luaMonsterTypeGetLoot); - registerMethod("MonsterType", "addLoot", LuaScriptInterface::luaMonsterTypeAddLoot); - registerMethod("MonsterType", "getCreatureEvents", LuaScriptInterface::luaMonsterTypeGetCreatureEvents); - registerMethod("MonsterType", "registerEvent", LuaScriptInterface::luaMonsterTypeRegisterEvent); - - registerMethod("MonsterType", "eventType", LuaScriptInterface::luaMonsterTypeEventType); - registerMethod("MonsterType", "onThink", LuaScriptInterface::luaMonsterTypeEventOnCallback); - registerMethod("MonsterType", "onAppear", LuaScriptInterface::luaMonsterTypeEventOnCallback); - registerMethod("MonsterType", "onDisappear", LuaScriptInterface::luaMonsterTypeEventOnCallback); - registerMethod("MonsterType", "onMove", LuaScriptInterface::luaMonsterTypeEventOnCallback); - registerMethod("MonsterType", "onSay", LuaScriptInterface::luaMonsterTypeEventOnCallback); registerMethod("MonsterType", "getSummonList", LuaScriptInterface::luaMonsterTypeGetSummonList); - registerMethod("MonsterType", "addSummon", LuaScriptInterface::luaMonsterTypeAddSummon); + registerMethod("MonsterType", "getMaxSummons", LuaScriptInterface::luaMonsterTypeGetMaxSummons); - registerMethod("MonsterType", "maxSummons", LuaScriptInterface::luaMonsterTypeMaxSummons); + registerMethod("MonsterType", "getArmor", LuaScriptInterface::luaMonsterTypeGetArmor); + registerMethod("MonsterType", "getDefense", LuaScriptInterface::luaMonsterTypeGetDefense); + registerMethod("MonsterType", "getOutfit", LuaScriptInterface::luaMonsterTypeGetOutfit); + registerMethod("MonsterType", "getRace", LuaScriptInterface::luaMonsterTypeGetRace); + registerMethod("MonsterType", "getCorpseId", LuaScriptInterface::luaMonsterTypeGetCorpseId); + registerMethod("MonsterType", "getManaCost", LuaScriptInterface::luaMonsterTypeGetManaCost); + registerMethod("MonsterType", "getBaseSpeed", LuaScriptInterface::luaMonsterTypeGetBaseSpeed); + registerMethod("MonsterType", "getLight", LuaScriptInterface::luaMonsterTypeGetLight); - registerMethod("MonsterType", "armor", LuaScriptInterface::luaMonsterTypeArmor); - registerMethod("MonsterType", "defense", LuaScriptInterface::luaMonsterTypeDefense); - registerMethod("MonsterType", "outfit", LuaScriptInterface::luaMonsterTypeOutfit); - registerMethod("MonsterType", "race", LuaScriptInterface::luaMonsterTypeRace); - registerMethod("MonsterType", "corpseId", LuaScriptInterface::luaMonsterTypeCorpseId); - registerMethod("MonsterType", "manaCost", LuaScriptInterface::luaMonsterTypeManaCost); - registerMethod("MonsterType", "baseSpeed", LuaScriptInterface::luaMonsterTypeBaseSpeed); - registerMethod("MonsterType", "light", LuaScriptInterface::luaMonsterTypeLight); - - registerMethod("MonsterType", "staticAttackChance", LuaScriptInterface::luaMonsterTypeStaticAttackChance); - registerMethod("MonsterType", "targetDistance", LuaScriptInterface::luaMonsterTypeTargetDistance); - registerMethod("MonsterType", "yellChance", LuaScriptInterface::luaMonsterTypeYellChance); - registerMethod("MonsterType", "yellSpeedTicks", LuaScriptInterface::luaMonsterTypeYellSpeedTicks); - registerMethod("MonsterType", "changeTargetChance", LuaScriptInterface::luaMonsterTypeChangeTargetChance); - registerMethod("MonsterType", "changeTargetSpeed", LuaScriptInterface::luaMonsterTypeChangeTargetSpeed); - - // Loot - registerClass("Loot", "", LuaScriptInterface::luaCreateLoot); - registerMetaMethod("Loot", "__gc", LuaScriptInterface::luaDeleteLoot); - registerMethod("Loot", "delete", LuaScriptInterface::luaDeleteLoot); - - registerMethod("Loot", "setId", LuaScriptInterface::luaLootSetId); - registerMethod("Loot", "setMaxCount", LuaScriptInterface::luaLootSetMaxCount); - registerMethod("Loot", "setSubType", LuaScriptInterface::luaLootSetSubType); - registerMethod("Loot", "setChance", LuaScriptInterface::luaLootSetChance); - registerMethod("Loot", "setActionId", LuaScriptInterface::luaLootSetActionId); - registerMethod("Loot", "setDescription", LuaScriptInterface::luaLootSetDescription); - registerMethod("Loot", "addChildLoot", LuaScriptInterface::luaLootAddChildLoot); - - // MonsterSpell - registerClass("MonsterSpell", "", LuaScriptInterface::luaCreateMonsterSpell); - registerMetaMethod("MonsterSpell", "__gc", LuaScriptInterface::luaDeleteMonsterSpell); - registerMethod("MonsterSpell", "delete", LuaScriptInterface::luaDeleteMonsterSpell); - - registerMethod("MonsterSpell", "setType", LuaScriptInterface::luaMonsterSpellSetType); - registerMethod("MonsterSpell", "setScriptName", LuaScriptInterface::luaMonsterSpellSetScriptName); - registerMethod("MonsterSpell", "setChance", LuaScriptInterface::luaMonsterSpellSetChance); - registerMethod("MonsterSpell", "setInterval", LuaScriptInterface::luaMonsterSpellSetInterval); - registerMethod("MonsterSpell", "setRange", LuaScriptInterface::luaMonsterSpellSetRange); - registerMethod("MonsterSpell", "setCombatValue", LuaScriptInterface::luaMonsterSpellSetCombatValue); - registerMethod("MonsterSpell", "setCombatType", LuaScriptInterface::luaMonsterSpellSetCombatType); - registerMethod("MonsterSpell", "setAttackValue", LuaScriptInterface::luaMonsterSpellSetAttackValue); - registerMethod("MonsterSpell", "setNeedTarget", LuaScriptInterface::luaMonsterSpellSetNeedTarget); - registerMethod("MonsterSpell", "setCombatLength", LuaScriptInterface::luaMonsterSpellSetCombatLength); - registerMethod("MonsterSpell", "setCombatSpread", LuaScriptInterface::luaMonsterSpellSetCombatSpread); - registerMethod("MonsterSpell", "setCombatRadius", LuaScriptInterface::luaMonsterSpellSetCombatRadius); - registerMethod("MonsterSpell", "setConditionType", LuaScriptInterface::luaMonsterSpellSetConditionType); - registerMethod("MonsterSpell", "setConditionDamage", LuaScriptInterface::luaMonsterSpellSetConditionDamage); - registerMethod("MonsterSpell", "setConditionSpeedChange", LuaScriptInterface::luaMonsterSpellSetConditionSpeedChange); - registerMethod("MonsterSpell", "setConditionDuration", LuaScriptInterface::luaMonsterSpellSetConditionDuration); - registerMethod("MonsterSpell", "setConditionTickInterval", LuaScriptInterface::luaMonsterSpellSetConditionTickInterval); - registerMethod("MonsterSpell", "setCombatShootEffect", LuaScriptInterface::luaMonsterSpellSetCombatShootEffect); - registerMethod("MonsterSpell", "setCombatEffect", LuaScriptInterface::luaMonsterSpellSetCombatEffect); + registerMethod("MonsterType", "getTargetDistance", LuaScriptInterface::luaMonsterTypeGetTargetDistance); + registerMethod("MonsterType", "getChangeTargetChance", LuaScriptInterface::luaMonsterTypeGetChangeTargetChance); + registerMethod("MonsterType", "getChangeTargetSpeed", LuaScriptInterface::luaMonsterTypeGetChangeTargetSpeed); // Party - registerClass("Party", "", LuaScriptInterface::luaPartyCreate); + registerClass("Party", "", nullptr); registerMetaMethod("Party", "__eq", LuaScriptInterface::luaUserdataCompare); registerMethod("Party", "disband", LuaScriptInterface::luaPartyDisband); @@ -2804,154 +2377,6 @@ void LuaScriptInterface::registerFunctions() registerMethod("Party", "isSharedExperienceEnabled", LuaScriptInterface::luaPartyIsSharedExperienceEnabled); registerMethod("Party", "shareExperience", LuaScriptInterface::luaPartyShareExperience); registerMethod("Party", "setSharedExperience", LuaScriptInterface::luaPartySetSharedExperience); - - // Spells - registerClass("Spell", "", LuaScriptInterface::luaSpellCreate); - registerMetaMethod("Spell", "__eq", LuaScriptInterface::luaUserdataCompare); - - registerMethod("Spell", "onCastSpell", LuaScriptInterface::luaSpellOnCastSpell); - registerMethod("Spell", "register", LuaScriptInterface::luaSpellRegister); - registerMethod("Spell", "name", LuaScriptInterface::luaSpellName); - registerMethod("Spell", "id", LuaScriptInterface::luaSpellId); - registerMethod("Spell", "group", LuaScriptInterface::luaSpellGroup); - registerMethod("Spell", "cooldown", LuaScriptInterface::luaSpellCooldown); - registerMethod("Spell", "groupCooldown", LuaScriptInterface::luaSpellGroupCooldown); - registerMethod("Spell", "level", LuaScriptInterface::luaSpellLevel); - registerMethod("Spell", "magicLevel", LuaScriptInterface::luaSpellMagicLevel); - registerMethod("Spell", "mana", LuaScriptInterface::luaSpellMana); - registerMethod("Spell", "manaPercent", LuaScriptInterface::luaSpellManaPercent); - registerMethod("Spell", "soul", LuaScriptInterface::luaSpellSoul); - registerMethod("Spell", "range", LuaScriptInterface::luaSpellRange); - registerMethod("Spell", "isPremium", LuaScriptInterface::luaSpellPremium); - registerMethod("Spell", "isEnabled", LuaScriptInterface::luaSpellEnabled); - registerMethod("Spell", "needTarget", LuaScriptInterface::luaSpellNeedTarget); - registerMethod("Spell", "needWeapon", LuaScriptInterface::luaSpellNeedWeapon); - registerMethod("Spell", "needLearn", LuaScriptInterface::luaSpellNeedLearn); - registerMethod("Spell", "isSelfTarget", LuaScriptInterface::luaSpellSelfTarget); - registerMethod("Spell", "isBlocking", LuaScriptInterface::luaSpellBlocking); - registerMethod("Spell", "isAggressive", LuaScriptInterface::luaSpellAggressive); - registerMethod("Spell", "vocation", LuaScriptInterface::luaSpellVocation); - - // only for InstantSpell - registerMethod("Spell", "words", LuaScriptInterface::luaSpellWords); - registerMethod("Spell", "needDirection", LuaScriptInterface::luaSpellNeedDirection); - registerMethod("Spell", "hasParams", LuaScriptInterface::luaSpellHasParams); - registerMethod("Spell", "hasPlayerNameParam", LuaScriptInterface::luaSpellHasPlayerNameParam); - registerMethod("Spell", "needCasterTargetOrDirection", LuaScriptInterface::luaSpellNeedCasterTargetOrDirection); - registerMethod("Spell", "isBlockingWalls", LuaScriptInterface::luaSpellIsBlockingWalls); - - // only for RuneSpells - registerMethod("Spell", "runeId", LuaScriptInterface::luaSpellRuneId); - registerMethod("Spell", "charges", LuaScriptInterface::luaSpellCharges); - registerMethod("Spell", "allowFarUse", LuaScriptInterface::luaSpellAllowFarUse); - registerMethod("Spell", "blockWalls", LuaScriptInterface::luaSpellBlockWalls); - registerMethod("Spell", "checkFloor", LuaScriptInterface::luaSpellCheckFloor); - - // Action - registerClass("Action", "", LuaScriptInterface::luaCreateAction); - registerMethod("Action", "onUse", LuaScriptInterface::luaActionOnUse); - registerMethod("Action", "register", LuaScriptInterface::luaActionRegister); - registerMethod("Action", "id", LuaScriptInterface::luaActionItemId); - registerMethod("Action", "aid", LuaScriptInterface::luaActionActionId); - registerMethod("Action", "uid", LuaScriptInterface::luaActionUniqueId); - registerMethod("Action", "allowFarUse", LuaScriptInterface::luaActionAllowFarUse); - registerMethod("Action", "blockWalls", LuaScriptInterface::luaActionBlockWalls); - registerMethod("Action", "checkFloor", LuaScriptInterface::luaActionCheckFloor); - - // TalkAction - registerClass("TalkAction", "", LuaScriptInterface::luaCreateTalkaction); - registerMethod("TalkAction", "onSay", LuaScriptInterface::luaTalkactionOnSay); - registerMethod("TalkAction", "register", LuaScriptInterface::luaTalkactionRegister); - registerMethod("TalkAction", "separator", LuaScriptInterface::luaTalkactionSeparator); - - // CreatureEvent - registerClass("CreatureEvent", "", LuaScriptInterface::luaCreateCreatureEvent); - registerMethod("CreatureEvent", "type", LuaScriptInterface::luaCreatureEventType); - registerMethod("CreatureEvent", "register", LuaScriptInterface::luaCreatureEventRegister); - registerMethod("CreatureEvent", "onLogin", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onLogout", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onThink", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onPrepareDeath", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onDeath", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onKill", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onAdvance", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onModalWindow", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onTextEdit", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onHealthChange", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onManaChange", LuaScriptInterface::luaCreatureEventOnCallback); - registerMethod("CreatureEvent", "onExtendedOpcode", LuaScriptInterface::luaCreatureEventOnCallback); - - // MoveEvent - registerClass("MoveEvent", "", LuaScriptInterface::luaCreateMoveEvent); - registerMethod("MoveEvent", "type", LuaScriptInterface::luaMoveEventType); - registerMethod("MoveEvent", "register", LuaScriptInterface::luaMoveEventRegister); - registerMethod("MoveEvent", "level", LuaScriptInterface::luaMoveEventLevel); - registerMethod("MoveEvent", "magicLevel", LuaScriptInterface::luaMoveEventMagLevel); - registerMethod("MoveEvent", "slot", LuaScriptInterface::luaMoveEventSlot); - registerMethod("MoveEvent", "id", LuaScriptInterface::luaMoveEventItemId); - registerMethod("MoveEvent", "aid", LuaScriptInterface::luaMoveEventActionId); - registerMethod("MoveEvent", "uid", LuaScriptInterface::luaMoveEventUniqueId); - registerMethod("MoveEvent", "position", LuaScriptInterface::luaMoveEventPosition); - registerMethod("MoveEvent", "premium", LuaScriptInterface::luaMoveEventPremium); - registerMethod("MoveEvent", "vocation", LuaScriptInterface::luaMoveEventVocation); - registerMethod("MoveEvent", "onEquip", LuaScriptInterface::luaMoveEventOnCallback); - registerMethod("MoveEvent", "onDeEquip", LuaScriptInterface::luaMoveEventOnCallback); - registerMethod("MoveEvent", "onStepIn", LuaScriptInterface::luaMoveEventOnCallback); - registerMethod("MoveEvent", "onStepOut", LuaScriptInterface::luaMoveEventOnCallback); - registerMethod("MoveEvent", "onAddItem", LuaScriptInterface::luaMoveEventOnCallback); - registerMethod("MoveEvent", "onRemoveItem", LuaScriptInterface::luaMoveEventOnCallback); - - // GlobalEvent - registerClass("GlobalEvent", "", LuaScriptInterface::luaCreateGlobalEvent); - registerMethod("GlobalEvent", "type", LuaScriptInterface::luaGlobalEventType); - registerMethod("GlobalEvent", "register", LuaScriptInterface::luaGlobalEventRegister); - registerMethod("GlobalEvent", "time", LuaScriptInterface::luaGlobalEventTime); - registerMethod("GlobalEvent", "interval", LuaScriptInterface::luaGlobalEventInterval); - registerMethod("GlobalEvent", "onThink", LuaScriptInterface::luaGlobalEventOnCallback); - registerMethod("GlobalEvent", "onTime", LuaScriptInterface::luaGlobalEventOnCallback); - registerMethod("GlobalEvent", "onStartup", LuaScriptInterface::luaGlobalEventOnCallback); - registerMethod("GlobalEvent", "onShutdown", LuaScriptInterface::luaGlobalEventOnCallback); - registerMethod("GlobalEvent", "onRecord", LuaScriptInterface::luaGlobalEventOnCallback); - - // Weapon - registerClass("Weapon", "", LuaScriptInterface::luaCreateWeapon); - registerMethod("Weapon", "action", LuaScriptInterface::luaWeaponAction); - registerMethod("Weapon", "register", LuaScriptInterface::luaWeaponRegister); - registerMethod("Weapon", "id", LuaScriptInterface::luaWeaponId); - registerMethod("Weapon", "level", LuaScriptInterface::luaWeaponLevel); - registerMethod("Weapon", "magicLevel", LuaScriptInterface::luaWeaponMagicLevel); - registerMethod("Weapon", "mana", LuaScriptInterface::luaWeaponMana); - registerMethod("Weapon", "manaPercent", LuaScriptInterface::luaWeaponManaPercent); - registerMethod("Weapon", "health", LuaScriptInterface::luaWeaponHealth); - registerMethod("Weapon", "healthPercent", LuaScriptInterface::luaWeaponHealthPercent); - registerMethod("Weapon", "soul", LuaScriptInterface::luaWeaponSoul); - registerMethod("Weapon", "breakChance", LuaScriptInterface::luaWeaponBreakChance); - registerMethod("Weapon", "premium", LuaScriptInterface::luaWeaponPremium); - registerMethod("Weapon", "wieldUnproperly", LuaScriptInterface::luaWeaponUnproperly); - registerMethod("Weapon", "vocation", LuaScriptInterface::luaWeaponVocation); - registerMethod("Weapon", "onUseWeapon", LuaScriptInterface::luaWeaponOnUseWeapon); - registerMethod("Weapon", "element", LuaScriptInterface::luaWeaponElement); - registerMethod("Weapon", "attack", LuaScriptInterface::luaWeaponAttack); - registerMethod("Weapon", "defense", LuaScriptInterface::luaWeaponDefense); - registerMethod("Weapon", "range", LuaScriptInterface::luaWeaponRange); - registerMethod("Weapon", "charges", LuaScriptInterface::luaWeaponCharges); - registerMethod("Weapon", "duration", LuaScriptInterface::luaWeaponDuration); - registerMethod("Weapon", "decayTo", LuaScriptInterface::luaWeaponDecayTo); - registerMethod("Weapon", "transformEquipTo", LuaScriptInterface::luaWeaponTransformEquipTo); - registerMethod("Weapon", "transformDeEquipTo", LuaScriptInterface::luaWeaponTransformDeEquipTo); - registerMethod("Weapon", "slotType", LuaScriptInterface::luaWeaponSlotType); - registerMethod("Weapon", "hitChance", LuaScriptInterface::luaWeaponHitChance); - registerMethod("Weapon", "extraElement", LuaScriptInterface::luaWeaponExtraElement); - - // exclusively for distance weapons - registerMethod("Weapon", "ammoType", LuaScriptInterface::luaWeaponAmmoType); - registerMethod("Weapon", "maxHitChance", LuaScriptInterface::luaWeaponMaxHitChance); - - // exclusively for wands - registerMethod("Weapon", "damage", LuaScriptInterface::luaWeaponWandDamage); - - // exclusively for wands & distance weapons - registerMethod("Weapon", "shootType", LuaScriptInterface::luaWeaponShootType); } #undef registerEnum @@ -3091,6 +2516,61 @@ void LuaScriptInterface::registerGlobalBoolean(const std::string& name, bool val lua_setglobal(luaState, name.c_str()); } +int LuaScriptInterface::luaGetPlayerFlagValue(lua_State* L) +{ + //getPlayerFlagValue(cid, flag) + Player* player = getPlayer(L, 1); + if (player) { + PlayerFlags flag = getNumber(L, 2); + pushBoolean(L, player->hasFlag(flag)); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int LuaScriptInterface::luaGetPlayerInstantSpellCount(lua_State* L) +{ + //getPlayerInstantSpellCount(cid) + Player* player = getPlayer(L, 1); + if (player) { + lua_pushnumber(L, g_spells->getInstantSpellCount(player)); + } else { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + pushBoolean(L, false); + } + return 1; +} + +int LuaScriptInterface::luaGetPlayerInstantSpellInfo(lua_State* L) +{ + //getPlayerInstantSpellInfo(cid, index) + Player* player = getPlayer(L, 1); + if (!player) { + reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + uint32_t index = getNumber(L, 2); + InstantSpell* spell = g_spells->getInstantSpellByIndex(player, index); + if (!spell) { + reportErrorFunc(getErrorDesc(LUA_ERROR_SPELL_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + lua_createtable(L, 0, 6); + setField(L, "name", spell->getName()); + setField(L, "words", spell->getWords()); + setField(L, "level", spell->getLevel()); + setField(L, "mlevel", spell->getMagicLevel()); + setField(L, "mana", spell->getManaCost(player)); + setField(L, "manapercent", spell->getManaPercent()); + return 1; +} + int LuaScriptInterface::luaDoPlayerAddItem(lua_State* L) { //doPlayerAddItem(cid, itemid, count/subtype, canDropOnMap) @@ -3166,6 +2646,138 @@ int LuaScriptInterface::luaDoPlayerAddItem(lua_State* L) return 1; } +int LuaScriptInterface::luaDoTileAddItemEx(lua_State* L) +{ + //doTileAddItemEx(pos, uid) + const Position& pos = getPosition(L, 1); + + Tile* tile = g_game.map.getTile(pos); + if (!tile) { + std::ostringstream ss; + ss << pos << ' ' << getErrorDesc(LUA_ERROR_TILE_NOT_FOUND); + reportErrorFunc(ss.str()); + pushBoolean(L, false); + return 1; + } + + uint32_t uid = getNumber(L, 2); + Item* item = getScriptEnv()->getItemByUID(uid); + if (!item) { + reportErrorFunc(getErrorDesc(LUA_ERROR_ITEM_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + if (item->getParent() != VirtualCylinder::virtualCylinder) { + reportErrorFunc("Item already has a parent"); + pushBoolean(L, false); + return 1; + } + + lua_pushnumber(L, g_game.internalAddItem(tile, item)); + return 1; +} + +int LuaScriptInterface::luaDoCreateItem(lua_State* L) +{ + //doCreateItem(itemid, type/count, pos) + //Returns uid of the created item, only works on tiles. + const Position& pos = getPosition(L, 3); + Tile* tile = g_game.map.getTile(pos); + if (!tile) { + std::ostringstream ss; + ss << pos << ' ' << getErrorDesc(LUA_ERROR_TILE_NOT_FOUND); + reportErrorFunc(ss.str()); + pushBoolean(L, false); + return 1; + } + + ScriptEnvironment* env = getScriptEnv(); + + int32_t itemCount = 1; + int32_t subType = 1; + + uint16_t itemId = getNumber(L, 1); + uint32_t count = getNumber(L, 2, 1); + + const ItemType& it = Item::items[itemId]; + if (it.hasSubType()) { + if (it.stackable) { + itemCount = static_cast(std::ceil(static_cast(count) / 100)); + } + + subType = count; + } else { + itemCount = std::max(1, count); + } + + while (itemCount > 0) { + int32_t stackCount = std::min(100, subType); + Item* newItem = Item::CreateItem(itemId, stackCount); + if (!newItem) { + reportErrorFunc(getErrorDesc(LUA_ERROR_ITEM_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + if (it.stackable) { + subType -= stackCount; + } + + ReturnValue ret = g_game.internalAddItem(tile, newItem, INDEX_WHEREEVER, FLAG_NOLIMIT); + if (ret != RETURNVALUE_NOERROR) { + delete newItem; + pushBoolean(L, false); + return 1; + } + + if (--itemCount == 0) { + if (newItem->getParent()) { + uint32_t uid = env->addThing(newItem); + lua_pushnumber(L, uid); + return 1; + } else { + //stackable item stacked with existing object, newItem will be released + pushBoolean(L, false); + return 1; + } + } + } + + pushBoolean(L, false); + return 1; +} + +int LuaScriptInterface::luaDoCreateItemEx(lua_State* L) +{ + //doCreateItemEx(itemid, count/subtype) + //Returns uid of the created item + uint16_t itemId = getNumber(L, 1); + uint32_t count = getNumber(L, 2, 1); + + const ItemType& it = Item::items[itemId]; + if (it.stackable && count > 100) { + reportErrorFunc("Stack count cannot be higher than 100."); + count = 100; + } + + Item* newItem = Item::CreateItem(itemId, count); + if (!newItem) { + reportErrorFunc(getErrorDesc(LUA_ERROR_ITEM_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + newItem->setParent(VirtualCylinder::virtualCylinder); + + ScriptEnvironment* env = getScriptEnv(); + env->addTempItem(newItem); + + uint32_t uid = env->addThing(newItem); + lua_pushnumber(L, uid); + return 1; +} + int LuaScriptInterface::luaDebugPrint(lua_State* L) { //debugPrint(text) @@ -3184,7 +2796,8 @@ int LuaScriptInterface::luaGetWorldTime(lua_State* L) int LuaScriptInterface::luaGetWorldLight(lua_State* L) { //getWorldLight() - LightInfo lightInfo = g_game.getWorldLightInfo(); + LightInfo lightInfo; + g_game.getWorldLightInfo(lightInfo); lua_pushnumber(L, lightInfo.level); lua_pushnumber(L, lightInfo.color); return 2; @@ -3281,8 +2894,8 @@ int LuaScriptInterface::luaDoAreaCombatHealth(lua_State* L) CombatDamage damage; damage.origin = getNumber(L, 8, ORIGIN_SPELL); - damage.primary.type = combatType; - damage.primary.value = normal_random(getNumber(L, 6), getNumber(L, 5)); + damage.type = combatType; + damage.value = normal_random(getNumber(L, 6), getNumber(L, 5)); Combat::doCombatHealth(creature, getPosition(L, 3), area, damage, params); pushBoolean(L, true); @@ -3318,8 +2931,8 @@ int LuaScriptInterface::luaDoTargetCombatHealth(lua_State* L) CombatDamage damage; damage.origin = getNumber(L, 7, ORIGIN_SPELL); - damage.primary.type = combatType; - damage.primary.value = normal_random(getNumber(L, 4), getNumber(L, 5)); + damage.type = combatType; + damage.value = normal_random(getNumber(L, 4), getNumber(L, 5)); Combat::doCombatHealth(creature, target, damage, params); pushBoolean(L, true); @@ -3344,8 +2957,8 @@ int LuaScriptInterface::luaDoAreaCombatMana(lua_State* L) CombatDamage damage; damage.origin = getNumber(L, 7, ORIGIN_SPELL); - damage.primary.type = COMBAT_MANADRAIN; - damage.primary.value = normal_random(getNumber(L, 4), getNumber(L, 5)); + damage.type = COMBAT_MANADRAIN; + damage.value = normal_random(getNumber(L, 4), getNumber(L, 5)); Position pos = getPosition(L, 2); Combat::doCombatMana(creature, pos, area, damage, params); @@ -3379,8 +2992,8 @@ int LuaScriptInterface::luaDoTargetCombatMana(lua_State* L) CombatDamage damage; damage.origin = getNumber(L, 6, ORIGIN_SPELL); - damage.primary.type = COMBAT_MANADRAIN; - damage.primary.value = normal_random(getNumber(L, 3), getNumber(L, 4)); + damage.type = COMBAT_MANADRAIN; + damage.value = normal_random(getNumber(L, 3), getNumber(L, 4)); Combat::doCombatMana(creature, target, damage, params); pushBoolean(L, true); @@ -3409,7 +3022,7 @@ int LuaScriptInterface::luaDoAreaCombatCondition(lua_State* L) if (area || areaId == 0) { CombatParams params; params.impactEffect = getNumber(L, 5); - params.conditionList.emplace_front(condition->clone()); + params.conditionList.emplace_front(condition); Combat::doCombatCondition(creature, getPosition(L, 2), area, params); pushBoolean(L, true); } else { @@ -3445,7 +3058,7 @@ int LuaScriptInterface::luaDoTargetCombatCondition(lua_State* L) CombatParams params; params.impactEffect = getNumber(L, 4); - params.conditionList.emplace_front(condition->clone()); + params.conditionList.emplace_front(condition); Combat::doCombatCondition(creature, target, params); pushBoolean(L, true); return 1; @@ -3524,6 +3137,76 @@ int LuaScriptInterface::luaDoChallengeCreature(lua_State* L) return 1; } +int LuaScriptInterface::luaSetCreatureOutfit(lua_State* L) +{ + //doSetCreatureOutfit(cid, outfit, time) + Creature* creature = getCreature(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + Outfit_t outfit = getOutfit(L, 2); + int32_t time = getNumber(L, 3); + pushBoolean(L, Spell::CreateIllusion(creature, outfit, time) == RETURNVALUE_NOERROR); + return 1; +} + +int LuaScriptInterface::luaSetMonsterOutfit(lua_State* L) +{ + //doSetMonsterOutfit(cid, name, time) + Creature* creature = getCreature(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + std::string name = getString(L, 2); + int32_t time = getNumber(L, 3); + pushBoolean(L, Spell::CreateIllusion(creature, name, time) == RETURNVALUE_NOERROR); + return 1; +} + +int LuaScriptInterface::luaSetItemOutfit(lua_State* L) +{ + //doSetItemOutfit(cid, item, time) + Creature* creature = getCreature(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + uint32_t item = getNumber(L, 2); + int32_t time = getNumber(L, 3); + pushBoolean(L, Spell::CreateIllusion(creature, item, time) == RETURNVALUE_NOERROR); + return 1; +} + +int LuaScriptInterface::luaDoMoveCreature(lua_State* L) +{ + //doMoveCreature(cid, direction) + Creature* creature = getCreature(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + Direction direction = getNumber(L, 2); + if (direction > DIRECTION_LAST) { + reportErrorFunc("No valid direction"); + pushBoolean(L, false); + return 1; + } + + ReturnValue ret = g_game.internalMoveCreature(creature, direction, FLAG_NOLIMIT); + lua_pushnumber(L, ret); + return 1; +} + int LuaScriptInterface::luaIsValidUID(lua_State* L) { //isValidUID(uid) @@ -3636,6 +3319,27 @@ int LuaScriptInterface::luaGetDepotId(lua_State* L) return 1; } +int LuaScriptInterface::luaIsInArray(lua_State* L) +{ + //isInArray(array, value) + if (!isTable(L, 1)) { + pushBoolean(L, false); + return 1; + } + + lua_pushnil(L); + while (lua_next(L, 1)) { + if (lua_equal(L, 2, -1) != 0) { + pushBoolean(L, true); + return 1; + } + lua_pop(L, 1); + } + + pushBoolean(L, false); + return 1; +} + int LuaScriptInterface::luaDoSetCreatureLight(lua_State* L) { //doSetCreatureLight(cid, lightLevel, lightColor, time) @@ -3726,7 +3430,7 @@ int LuaScriptInterface::luaAddEvent(lua_State* L) case LuaData_Container: case LuaData_Teleport: { lua_getglobal(globalState, "Item"); - lua_getfield(globalState, -1, "getUniqueId"); + lua_getfield(globalState, -1, "getMovementId"); break; } case LuaData_Player: @@ -3802,6 +3506,21 @@ int LuaScriptInterface::luaStopEvent(lua_State* L) return 1; } +int LuaScriptInterface::luaGetCreatureCondition(lua_State* L) +{ + Creature* creature = getCreature(L, 1); + if (!creature) { + reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); + pushBoolean(L, false); + return 1; + } + + ConditionType_t condition = getNumber(L, 2); + uint32_t subId = getNumber(L, 3, 0); + pushBoolean(L, creature->hasCondition(condition, subId)); + return 1; +} + int LuaScriptInterface::luaSaveServer(lua_State* L) { g_game.saveGameState(); @@ -3850,40 +3569,6 @@ int LuaScriptInterface::luaGetWaypointPositionByName(lua_State* L) return 1; } -int LuaScriptInterface::luaSendChannelMessage(lua_State* L) -{ - //sendChannelMessage(channelId, type, message) - uint32_t channelId = getNumber(L, 1); - ChatChannel* channel = g_chat->getChannelById(channelId); - if (!channel) { - pushBoolean(L, false); - return 1; - } - - SpeakClasses type = getNumber(L, 2); - std::string message = getString(L, 3); - channel->sendToAll(message, type); - pushBoolean(L, true); - return 1; -} - -int LuaScriptInterface::luaSendGuildChannelMessage(lua_State* L) -{ - //sendGuildChannelMessage(guildId, type, message) - uint32_t guildId = getNumber(L, 1); - ChatChannel* channel = g_chat->getGuildChannelById(guildId); - if (!channel) { - pushBoolean(L, false); - return 1; - } - - SpeakClasses type = getNumber(L, 2); - std::string message = getString(L, 3); - channel->sendToAll(message, type); - pushBoolean(L, true); - return 1; -} - std::string LuaScriptInterface::escapeString(const std::string& string) { std::string s = string; @@ -3983,7 +3668,7 @@ const luaL_Reg LuaScriptInterface::luaDatabaseTable[] = { int LuaScriptInterface::luaDatabaseExecute(lua_State* L) { - pushBoolean(L, Database::getInstance().executeQuery(getString(L, -1))); + pushBoolean(L, Database::getInstance()->executeQuery(getString(L, -1))); return 1; } @@ -4019,7 +3704,7 @@ int LuaScriptInterface::luaDatabaseAsyncExecute(lua_State* L) int LuaScriptInterface::luaDatabaseStoreQuery(lua_State* L) { - if (DBResult_ptr res = Database::getInstance().storeQuery(getString(L, -1))) { + if (DBResult_ptr res = Database::getInstance()->storeQuery(getString(L, -1))) { lua_pushnumber(L, ScriptEnvironment::addResult(res)); } else { pushBoolean(L, false); @@ -4063,20 +3748,20 @@ int LuaScriptInterface::luaDatabaseAsyncStoreQuery(lua_State* L) int LuaScriptInterface::luaDatabaseEscapeString(lua_State* L) { - pushString(L, Database::getInstance().escapeString(getString(L, -1))); + pushString(L, Database::getInstance()->escapeString(getString(L, -1))); return 1; } int LuaScriptInterface::luaDatabaseEscapeBlob(lua_State* L) { uint32_t length = getNumber(L, 2); - pushString(L, Database::getInstance().escapeBlob(getString(L, 1).c_str(), length)); + pushString(L, Database::getInstance()->escapeBlob(getString(L, 1).c_str(), length)); return 1; } int LuaScriptInterface::luaDatabaseLastInsertId(lua_State* L) { - lua_pushnumber(L, Database::getInstance().getLastInsertId()); + lua_pushnumber(L, Database::getInstance()->getLastInsertId()); return 1; } @@ -4256,15 +3941,7 @@ int LuaScriptInterface::luaGameLoadMap(lua_State* L) { // Game.loadMap(path) const std::string& path = getString(L, 1); - g_dispatcher.addTask(createTask([path]() { - try { - g_game.loadMap(path); - } catch (const std::exception& e) { - // FIXME: Should only catch some exceptions - std::cout << "[Error - LuaScriptInterface::luaGameLoadMap] Failed to load map: " - << e.what() << std::endl; - } - })); + g_dispatcher.addTask(createTask(std::bind(&Game::loadMap, &g_game, path))); return 0; } @@ -4530,37 +4207,6 @@ int LuaScriptInterface::luaGameCreateTile(lua_State* L) return 1; } -int LuaScriptInterface::luaGameCreateMonsterType(lua_State* L) -{ - // Game.createMonsterType(name) - if (getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc("MonsterTypes can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - MonsterType* monsterType = g_monsters.getMonsterType(getString(L, 1)); - if (monsterType) { - monsterType->info.lootItems.clear(); - monsterType->info.attackSpells.clear(); - monsterType->info.defenseSpells.clear(); - pushUserdata(L, monsterType); - setMetatable(L, -1, "MonsterType"); - } else if (isString(L, 1)) { - monsterType = new MonsterType(); - std::string name = getString(L, 1); - g_monsters.addMonsterType(name, monsterType); - monsterType = g_monsters.getMonsterType(getString(L, 1)); - monsterType->name = name; - monsterType->nameDescription = "a " + name; - pushUserdata(L, monsterType); - setMetatable(L, -1, "MonsterType"); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaGameStartRaid(lua_State* L) { // Game.startRaid(raidName) @@ -4568,12 +4214,12 @@ int LuaScriptInterface::luaGameStartRaid(lua_State* L) Raid* raid = g_game.raids.getRaidByName(raidName); if (!raid || !raid->isLoaded()) { - lua_pushnumber(L, RETURNVALUE_NOSUCHRAIDEXISTS); + lua_pushnil(L); return 1; } if (g_game.raids.getRunning()) { - lua_pushnumber(L, RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING); + lua_pushnil(L); return 1; } @@ -4583,24 +4229,19 @@ int LuaScriptInterface::luaGameStartRaid(lua_State* L) return 1; } -int LuaScriptInterface::luaGameGetClientVersion(lua_State* L) -{ - // Game.getClientVersion() - lua_createtable(L, 0, 3); - setField(L, "min", CLIENT_VERSION_MIN); - setField(L, "max", CLIENT_VERSION_MAX); - setField(L, "string", CLIENT_VERSION_STR); - return 1; -} - int LuaScriptInterface::luaGameReload(lua_State* L) { // Game.reload(reloadType) ReloadTypes_t reloadType = getNumber(L, 1); + if (!reloadType) { + lua_pushnil(L); + return 1; + } + if (reloadType == RELOAD_TYPE_GLOBAL) { pushBoolean(L, g_luaEnvironment.loadFile("data/global.lua") == 0); - pushBoolean(L, g_scripts->loadScripts("scripts/lib", true, true)); - } else { + } + else { pushBoolean(L, g_game.reload(reloadType)); } lua_gc(g_luaEnvironment.getLuaState(), LUA_GCCOLLECT, 0); @@ -4763,18 +4404,18 @@ int LuaScriptInterface::luaPositionIsSightClear(lua_State* L) int LuaScriptInterface::luaPositionSendMagicEffect(lua_State* L) { // position:sendMagicEffect(magicEffect[, player = nullptr]) - SpectatorVec spectators; + SpectatorVec list; if (lua_gettop(L) >= 3) { Player* player = getPlayer(L, 3); if (player) { - spectators.emplace_back(player); + list.insert(player); } } MagicEffectClasses magicEffect = getNumber(L, 2); const Position& position = getPosition(L, 1); - if (!spectators.empty()) { - Game::addMagicEffect(spectators, position, magicEffect); + if (!list.empty()) { + Game::addMagicEffect(list, position, magicEffect); } else { g_game.addMagicEffect(position, magicEffect); } @@ -4786,19 +4427,19 @@ int LuaScriptInterface::luaPositionSendMagicEffect(lua_State* L) int LuaScriptInterface::luaPositionSendDistanceEffect(lua_State* L) { // position:sendDistanceEffect(positionEx, distanceEffect[, player = nullptr]) - SpectatorVec spectators; + SpectatorVec list; if (lua_gettop(L) >= 4) { Player* player = getPlayer(L, 4); if (player) { - spectators.emplace_back(player); + list.insert(player); } } ShootType_t distanceEffect = getNumber(L, 3); const Position& positionEx = getPosition(L, 2); const Position& position = getPosition(L, 1); - if (!spectators.empty()) { - Game::addDistanceEffect(spectators, position, positionEx, distanceEffect); + if (!list.empty()) { + Game::addDistanceEffect(list, position, positionEx, distanceEffect); } else { g_game.addDistanceEffect(position, positionEx, distanceEffect); } @@ -4807,6 +4448,16 @@ int LuaScriptInterface::luaPositionSendDistanceEffect(lua_State* L) return 1; } +int LuaScriptInterface::luaPositionSendMonsterSay(lua_State * L) +{ + // position:sendMonsterSay(text) + const std::string& text = getString(L, 2); + const Position& position = getPosition(L, 1); + g_game.addMonsterSayText(position, text); + pushBoolean(L, true); + return 1; +} + // Tile int LuaScriptInterface::luaTileCreate(lua_State* L) { @@ -5034,9 +4685,6 @@ int LuaScriptInterface::luaTileGetItemByType(lua_State* L) case ITEM_TYPE_MAILBOX: found = tile->hasFlag(TILESTATE_MAILBOX); break; - case ITEM_TYPE_TRASHHOLDER: - found = tile->hasFlag(TILESTATE_TRASHHOLDER); - break; case ITEM_TYPE_BED: found = tile->hasFlag(TILESTATE_BED); break; @@ -5393,77 +5041,6 @@ int LuaScriptInterface::luaTileQueryAdd(lua_State* L) return 1; } -int LuaScriptInterface::luaTileAddItem(lua_State* L) -{ - // tile:addItem(itemId[, count/subType = 1[, flags = 0]]) - Tile* tile = getUserdata(L, 1); - if (!tile) { - lua_pushnil(L); - return 1; - } - - uint16_t itemId; - if (isNumber(L, 2)) { - itemId = getNumber(L, 2); - } else { - itemId = Item::items.getItemIdByName(getString(L, 2)); - if (itemId == 0) { - lua_pushnil(L); - return 1; - } - } - - uint32_t subType = getNumber(L, 3, 1); - - Item* item = Item::CreateItem(itemId, std::min(subType, 100)); - if (!item) { - lua_pushnil(L); - return 1; - } - - uint32_t flags = getNumber(L, 4, 0); - - ReturnValue ret = g_game.internalAddItem(tile, item, INDEX_WHEREEVER, flags); - if (ret == RETURNVALUE_NOERROR) { - pushUserdata(L, item); - setItemMetatable(L, -1, item); - } else { - delete item; - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaTileAddItemEx(lua_State* L) -{ - // tile:addItemEx(item[, flags = 0]) - Item* item = getUserdata(L, 2); - if (!item) { - lua_pushnil(L); - return 1; - } - - Tile* tile = getUserdata(L, 1); - if (!tile) { - lua_pushnil(L); - return 1; - } - - if (item->getParent() != VirtualCylinder::virtualCylinder) { - reportErrorFunc("Item already has a parent"); - lua_pushnil(L); - return 1; - } - - uint32_t flags = getNumber(L, 3, 0); - ReturnValue ret = g_game.internalAddItem(tile, item, INDEX_WHEREEVER, flags); - if (ret == RETURNVALUE_NOERROR) { - ScriptEnvironment::removeTempItem(item); - } - lua_pushnumber(L, ret); - return 1; -} - int LuaScriptInterface::luaTileGetHouse(lua_State* L) { // tile:getHouse() @@ -5763,243 +5340,6 @@ int LuaScriptInterface::luaNetworkMessageSendToPlayer(lua_State* L) return 1; } -// ModalWindow -int LuaScriptInterface::luaModalWindowCreate(lua_State* L) -{ - // ModalWindow(id, title, message) - const std::string& message = getString(L, 4); - const std::string& title = getString(L, 3); - uint32_t id = getNumber(L, 2); - - pushUserdata(L, new ModalWindow(id, title, message)); - setMetatable(L, -1, "ModalWindow"); - return 1; -} - -int LuaScriptInterface::luaModalWindowDelete(lua_State* L) -{ - ModalWindow** windowPtr = getRawUserdata(L, 1); - if (windowPtr && *windowPtr) { - delete *windowPtr; - *windowPtr = nullptr; - } - return 0; -} - -int LuaScriptInterface::luaModalWindowGetId(lua_State* L) -{ - // modalWindow:getId() - ModalWindow* window = getUserdata(L, 1); - if (window) { - lua_pushnumber(L, window->id); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowGetTitle(lua_State* L) -{ - // modalWindow:getTitle() - ModalWindow* window = getUserdata(L, 1); - if (window) { - pushString(L, window->title); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowGetMessage(lua_State* L) -{ - // modalWindow:getMessage() - ModalWindow* window = getUserdata(L, 1); - if (window) { - pushString(L, window->message); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowSetTitle(lua_State* L) -{ - // modalWindow:setTitle(text) - const std::string& text = getString(L, 2); - ModalWindow* window = getUserdata(L, 1); - if (window) { - window->title = text; - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowSetMessage(lua_State* L) -{ - // modalWindow:setMessage(text) - const std::string& text = getString(L, 2); - ModalWindow* window = getUserdata(L, 1); - if (window) { - window->message = text; - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowGetButtonCount(lua_State* L) -{ - // modalWindow:getButtonCount() - ModalWindow* window = getUserdata(L, 1); - if (window) { - lua_pushnumber(L, window->buttons.size()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowGetChoiceCount(lua_State* L) -{ - // modalWindow:getChoiceCount() - ModalWindow* window = getUserdata(L, 1); - if (window) { - lua_pushnumber(L, window->choices.size()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowAddButton(lua_State* L) -{ - // modalWindow:addButton(id, text) - const std::string& text = getString(L, 3); - uint8_t id = getNumber(L, 2); - ModalWindow* window = getUserdata(L, 1); - if (window) { - window->buttons.emplace_back(text, id); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowAddChoice(lua_State* L) -{ - // modalWindow:addChoice(id, text) - const std::string& text = getString(L, 3); - uint8_t id = getNumber(L, 2); - ModalWindow* window = getUserdata(L, 1); - if (window) { - window->choices.emplace_back(text, id); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowGetDefaultEnterButton(lua_State* L) -{ - // modalWindow:getDefaultEnterButton() - ModalWindow* window = getUserdata(L, 1); - if (window) { - lua_pushnumber(L, window->defaultEnterButton); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowSetDefaultEnterButton(lua_State* L) -{ - // modalWindow:setDefaultEnterButton(buttonId) - ModalWindow* window = getUserdata(L, 1); - if (window) { - window->defaultEnterButton = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowGetDefaultEscapeButton(lua_State* L) -{ - // modalWindow:getDefaultEscapeButton() - ModalWindow* window = getUserdata(L, 1); - if (window) { - lua_pushnumber(L, window->defaultEscapeButton); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowSetDefaultEscapeButton(lua_State* L) -{ - // modalWindow:setDefaultEscapeButton(buttonId) - ModalWindow* window = getUserdata(L, 1); - if (window) { - window->defaultEscapeButton = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowHasPriority(lua_State* L) -{ - // modalWindow:hasPriority() - ModalWindow* window = getUserdata(L, 1); - if (window) { - pushBoolean(L, window->priority); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowSetPriority(lua_State* L) -{ - // modalWindow:setPriority(priority) - ModalWindow* window = getUserdata(L, 1); - if (window) { - window->priority = getBoolean(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaModalWindowSendToPlayer(lua_State* L) -{ - // modalWindow:sendToPlayer(player) - Player* player = getPlayer(L, 2); - if (!player) { - lua_pushnil(L); - return 1; - } - - ModalWindow* window = getUserdata(L, 1); - if (window) { - if (!player->hasModalWindowOpen(window->id)) { - player->sendModalWindow(*window); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - // Item int LuaScriptInterface::luaItemCreate(lua_State* L) { @@ -6120,8 +5460,6 @@ int LuaScriptInterface::luaItemSplit(lua_State* L) return 1; } - splitItem->setItemCount(count); - ScriptEnvironment* env = getScriptEnv(); uint32_t uid = env->addThing(item); @@ -6157,16 +5495,12 @@ int LuaScriptInterface::luaItemRemove(lua_State* L) return 1; } -int LuaScriptInterface::luaItemGetUniqueId(lua_State* L) +int LuaScriptInterface::luaItemGetMovementId(lua_State* L) { - // item:getUniqueId() + // item:getMovementId() Item* item = getUserdata(L, 1); if (item) { - uint32_t uniqueId = item->getUniqueId(); - if (uniqueId == 0) { - uniqueId = getScriptEnv()->addThing(item); - } - lua_pushnumber(L, uniqueId); + lua_pushnumber(L, item->getMovementId()); } else { lua_pushnil(L); } @@ -6199,6 +5533,32 @@ int LuaScriptInterface::luaItemSetActionId(lua_State* L) return 1; } +int LuaScriptInterface::luaItemSetMovementId(lua_State* L) +{ + // item:setMovementId(movementId) + uint16_t movementId = getNumber(L, 2); + Item* item = getUserdata(L, 1); + if (item) { + item->setMovementID(movementId); + pushBoolean(L, true); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaItemGetUniqueId(lua_State * L) +{ + // item:getUniqueId() + Item* item = getUserdata(L, 1); + if (item) { + lua_pushnumber(L, getScriptEnv()->addThing(item)); + } else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaItemGetCount(lua_State* L) { // item:getCount() @@ -6395,12 +5755,6 @@ int LuaScriptInterface::luaItemSetAttribute(lua_State* L) } if (ItemAttributes::isIntAttrType(attribute)) { - if (attribute == ITEM_ATTRIBUTE_UNIQUEID) { - reportErrorFunc("Attempt to set protected key \"uid\""); - pushBoolean(L, false); - return 1; - } - item->setIntAttr(attribute, getNumber(L, 3)); pushBoolean(L, true); } else if (ItemAttributes::isStrAttrType(attribute)) { @@ -6430,103 +5784,14 @@ int LuaScriptInterface::luaItemRemoveAttribute(lua_State* L) attribute = ITEM_ATTRIBUTE_NONE; } - bool ret = attribute != ITEM_ATTRIBUTE_UNIQUEID; - if (ret) { - item->removeAttribute(attribute); - } else { - reportErrorFunc("Attempt to erase protected key \"uid\""); - } - pushBoolean(L, ret); - return 1; -} - -int LuaScriptInterface::luaItemGetCustomAttribute(lua_State* L) { - // item:getCustomAttribute(key) - Item* item = getUserdata(L, 1); - if (!item) { - lua_pushnil(L); - return 1; - } - - const ItemAttributes::CustomAttribute* attr; - if (isNumber(L, 2)) { - attr = item->getCustomAttribute(getNumber(L, 2)); - } else if (isString(L, 2)) { - attr = item->getCustomAttribute(getString(L, 2)); - } else { - lua_pushnil(L); - return 1; - } - - if (attr) { - attr->pushToLua(L); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaItemSetCustomAttribute(lua_State* L) { - // item:setCustomAttribute(key, value) - Item* item = getUserdata(L, 1); - if (!item) { - lua_pushnil(L); - return 1; - } - - std::string key; - if (isNumber(L, 2)) { - key = boost::lexical_cast(getNumber(L, 2)); - } else if (isString(L, 2)) { - key = getString(L, 2); - } else { - lua_pushnil(L); - return 1; - } - - ItemAttributes::CustomAttribute val; - if (isNumber(L, 3)) { - double tmp = getNumber(L, 3); - if (std::floor(tmp) < tmp) { - val.set(tmp); - } else { - val.set(tmp); - } - } else if (isString(L, 3)) { - val.set(getString(L, 3)); - } else if (isBoolean(L, 3)) { - val.set(getBoolean(L, 3)); - } else { - lua_pushnil(L); - return 1; - } - - item->setCustomAttribute(key, val); + item->removeAttribute(attribute); pushBoolean(L, true); return 1; } -int LuaScriptInterface::luaItemRemoveCustomAttribute(lua_State* L) { - // item:removeCustomAttribute(key) - Item* item = getUserdata(L, 1); - if (!item) { - lua_pushnil(L); - return 1; - } - - if (isNumber(L, 2)) { - pushBoolean(L, item->removeCustomAttribute(getNumber(L, 2))); - } else if (isString(L, 2)) { - pushBoolean(L, item->removeCustomAttribute(getString(L, 2))); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaItemMoveTo(lua_State* L) { - // item:moveTo(position or cylinder[, flags]) + // item:moveTo(position or cylinder) Item** itemPtr = getRawUserdata(L, 1); if (!itemPtr) { lua_pushnil(L); @@ -6570,13 +5835,11 @@ int LuaScriptInterface::luaItemMoveTo(lua_State* L) return 1; } - uint32_t flags = getNumber(L, 3, FLAG_NOLIMIT | FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE | FLAG_IGNORENOTMOVEABLE); - if (item->getParent() == VirtualCylinder::virtualCylinder) { - pushBoolean(L, g_game.internalAddItem(toCylinder, item, INDEX_WHEREEVER, flags) == RETURNVALUE_NOERROR); + pushBoolean(L, g_game.internalAddItem(toCylinder, item) == RETURNVALUE_NOERROR); } else { Item* moveItem = nullptr; - ReturnValue ret = g_game.internalMoveItem(item->getParent(), toCylinder, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem, flags); + ReturnValue ret = g_game.internalMoveItem(item->getParent(), toCylinder, INDEX_WHEREEVER, item, item->getItemCount(), &moveItem, FLAG_NOLIMIT | FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE | FLAG_IGNORENOTMOVEABLE); if (moveItem) { *itemPtr = moveItem; } @@ -6641,13 +5904,9 @@ int LuaScriptInterface::luaItemTransform(lua_State* L) int LuaScriptInterface::luaItemDecay(lua_State* L) { - // item:decay(decayId) + // item:decay() Item* item = getUserdata(L, 1); if (item) { - if (isNumber(L, 2)) { - item->setDecayTo(getNumber(L, 2)); - } - g_game.startDecay(item); pushBoolean(L, true); } else { @@ -6682,18 +5941,6 @@ int LuaScriptInterface::luaItemHasProperty(lua_State* L) return 1; } -int LuaScriptInterface::luaItemIsLoadedFromMap(lua_State* L) -{ - // item:isLoadedFromMap() - Item* item = getUserdata(L, 1); - if (item) { - pushBoolean(L, item->isLoadedFromMap()); - } else { - lua_pushnil(L); - } - return 1; -} - // Container int LuaScriptInterface::luaContainerCreate(lua_State* L) { @@ -6821,13 +6068,9 @@ int LuaScriptInterface::luaContainerAddItem(lua_State* L) } } - uint32_t count = getNumber(L, 3, 1); - const ItemType& it = Item::items[itemId]; - if (it.stackable) { - count = std::min(count, 100); - } + uint32_t subType = getNumber(L, 3, 1); - Item* item = Item::CreateItem(itemId, count); + Item* item = Item::CreateItem(itemId, std::min(subType, 100)); if (!item) { lua_pushnil(L); return 1; @@ -6878,18 +6121,6 @@ int LuaScriptInterface::luaContainerAddItemEx(lua_State* L) return 1; } -int LuaScriptInterface::luaContainerGetCorpseOwner(lua_State* L) -{ - // container:getCorpseOwner() - Container* container = getUserdata(L, 1); - if (container) { - lua_pushnumber(L, container->getCorpseOwner()); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaContainerGetItemCountById(lua_State* L) { // container:getItemCountById(itemId[, subType = -1]) @@ -6915,18 +6146,6 @@ int LuaScriptInterface::luaContainerGetItemCountById(lua_State* L) return 1; } -int LuaScriptInterface::luaContainerGetContentDescription(lua_State* L) -{ - // container:getContentDescription() - Container* container = getUserdata(L, 1); - if (container) { - pushString(L, container->getContentDescription()); - } else { - lua_pushnil(L); - } - return 1; -} - // Teleport int LuaScriptInterface::luaTeleportCreate(lua_State* L) { @@ -7075,18 +6294,6 @@ int LuaScriptInterface::luaCreatureIsInGhostMode(lua_State* L) return 1; } -int LuaScriptInterface::luaCreatureIsHealthHidden(lua_State* L) -{ - // creature:isHealthHidden() - const Creature* creature = getUserdata(L, 1); - if (creature) { - pushBoolean(L, creature->isHealthHidden()); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaCreatureCanSee(lua_State* L) { // creature:canSee(position) @@ -7249,8 +6456,19 @@ int LuaScriptInterface::luaCreatureSetMaster(lua_State* L) return 1; } - pushBoolean(L, creature->setMaster(getCreature(L, 2))); - g_game.updateCreatureType(creature); + Creature* master = getCreature(L, 2); + if (master) { + pushBoolean(L, creature->convinceCreature(master)); + } else { + master = creature->getMaster(); + if (master) { + master->removeSummon(creature); + creature->incrementReferenceCounter(); + creature->setDropLoot(true); + } + pushBoolean(L, true); + } + return 1; } @@ -7263,9 +6481,10 @@ int LuaScriptInterface::luaCreatureGetLight(lua_State* L) return 1; } - LightInfo lightInfo = creature->getCreatureLight(); - lua_pushnumber(L, lightInfo.level); - lua_pushnumber(L, lightInfo.color); + LightInfo light; + creature->getCreatureLight(light); + lua_pushnumber(L, light.level); + lua_pushnumber(L, light.color); return 2; } @@ -7340,19 +6559,6 @@ int LuaScriptInterface::luaCreatureSetDropLoot(lua_State* L) return 1; } -int LuaScriptInterface::luaCreatureSetSkillLoss(lua_State* L) -{ - // creature:setSkillLoss(skillLoss) - Creature* creature = getUserdata(L, 1); - if (creature) { - creature->setSkillLoss(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaCreatureGetPosition(lua_State* L) { // creature:getPosition() @@ -7420,26 +6626,6 @@ int LuaScriptInterface::luaCreatureGetHealth(lua_State* L) return 1; } -int LuaScriptInterface::luaCreatureSetHealth(lua_State* L) -{ - // creature:setHealth(health) - Creature* creature = getUserdata(L, 1); - if (!creature) { - lua_pushnil(L); - return 1; - } - - creature->health = std::min(getNumber(L, 2), creature->healthMax); - g_game.addCreatureHealth(creature); - - Player* player = creature->getPlayer(); - if (player) { - player->sendStats(); - } - pushBoolean(L, true); - return 1; -} - int LuaScriptInterface::luaCreatureAddHealth(lua_State* L) { // creature:addHealth(healthChange) @@ -7450,11 +6636,11 @@ int LuaScriptInterface::luaCreatureAddHealth(lua_State* L) } CombatDamage damage; - damage.primary.value = getNumber(L, 2); - if (damage.primary.value >= 0) { - damage.primary.type = COMBAT_HEALING; + damage.value = getNumber(L, 2); + if (damage.value >= 0) { + damage.type = COMBAT_HEALING; } else { - damage.primary.type = COMBAT_UNDEFINEDDAMAGE; + damage.type = COMBAT_UNDEFINEDDAMAGE; } pushBoolean(L, g_game.combatChangeHealth(nullptr, creature, damage)); return 1; @@ -7501,7 +6687,8 @@ int LuaScriptInterface::luaCreatureSetHiddenHealth(lua_State* L) creature->setHiddenHealth(getBoolean(L, 2)); g_game.addCreatureHealth(creature); pushBoolean(L, true); - } else { + } + else { lua_pushnil(L); } return 1; @@ -7609,7 +6796,7 @@ int LuaScriptInterface::luaCreatureRemoveCondition(lua_State* L) uint32_t subId = getNumber(L, 4, 0); Condition* condition = creature->getCondition(conditionType, conditionId, subId); if (condition) { - bool force = getBoolean(L, 5, false); + bool force = getBoolean(L, 5, true); creature->removeCondition(condition, force); pushBoolean(L, true); } else { @@ -7618,40 +6805,6 @@ int LuaScriptInterface::luaCreatureRemoveCondition(lua_State* L) return 1; } -int LuaScriptInterface::luaCreatureHasCondition(lua_State* L) -{ - // creature:hasCondition(conditionType[, subId = 0]) - Creature* creature = getUserdata(L, 1); - if (!creature) { - lua_pushnil(L); - return 1; - } - - ConditionType_t conditionType = getNumber(L, 2); - uint32_t subId = getNumber(L, 3, 0); - pushBoolean(L, creature->hasCondition(conditionType, subId)); - return 1; -} - -int LuaScriptInterface::luaCreatureIsImmune(lua_State* L) -{ - // creature:isImmune(condition or conditionType) - Creature* creature = getUserdata(L, 1); - if (!creature) { - lua_pushnil(L); - return 1; - } - - if (isNumber(L, 2)) { - pushBoolean(L, creature->isImmune(getNumber(L, 2))); - } else if (Condition* condition = getUserdata(L, 2)) { - pushBoolean(L, creature->isImmune(condition->getType())); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaCreatureRemove(lua_State* L) { // creature:remove() @@ -7697,7 +6850,7 @@ int LuaScriptInterface::luaCreatureTeleportTo(lua_State* L) return 1; } - if (pushMovement) { + if (!pushMovement) { if (oldPosition.x == position.x) { if (oldPosition.y < position.y) { g_game.internalCreatureTurn(creature, DIRECTION_SOUTH); @@ -7716,7 +6869,7 @@ int LuaScriptInterface::luaCreatureTeleportTo(lua_State* L) int LuaScriptInterface::luaCreatureSay(lua_State* L) { - // creature:say(text[, type = TALKTYPE_MONSTER_SAY[, ghost = false[, target = nullptr[, position]]]]) + // creature:say(text, type[, ghost = false[, target = nullptr[, position]]]) int parameters = lua_gettop(L); Position position; @@ -7736,7 +6889,7 @@ int LuaScriptInterface::luaCreatureSay(lua_State* L) bool ghost = getBoolean(L, 4, false); - SpeakClasses type = getNumber(L, 3, TALKTYPE_MONSTER_SAY); + SpeakClasses type = getNumber(L, 3); const std::string& text = getString(L, 2); Creature* creature = getUserdata(L, 1); if (!creature) { @@ -7744,15 +6897,15 @@ int LuaScriptInterface::luaCreatureSay(lua_State* L) return 1; } - SpectatorVec spectators; + SpectatorVec list; if (target) { - spectators.emplace_back(target); + list.insert(target); } if (position.x != 0) { - pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &spectators, &position)); + pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &list, &position)); } else { - pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &spectators)); + pushBoolean(L, g_game.internalCreatureSay(creature, type, text, ghost, &list)); } return 1; } @@ -7842,58 +6995,13 @@ int LuaScriptInterface::luaCreatureGetPathTo(lua_State* L) return 1; } -int LuaScriptInterface::luaCreatureMove(lua_State* L) -{ - // creature:move(direction) - // creature:move(tile[, flags = 0]) - Creature* creature = getUserdata(L, 1); - if (!creature) { - lua_pushnil(L); - return 1; - } - - if (isNumber(L, 2)) { - Direction direction = getNumber(L, 2); - if (direction > DIRECTION_LAST) { - lua_pushnil(L); - return 1; - } - lua_pushnumber(L, g_game.internalMoveCreature(creature, direction, FLAG_NOLIMIT)); - } else { - Tile* tile = getUserdata(L, 2); - if (!tile) { - lua_pushnil(L); - return 1; - } - lua_pushnumber(L, g_game.internalMoveCreature(*creature, *tile, getNumber(L, 3))); - } - return 1; -} - -int LuaScriptInterface::luaCreatureGetZone(lua_State* L) -{ - // creature:getZone() - Creature* creature = getUserdata(L, 1); - if (creature) { - lua_pushnumber(L, creature->getZone()); - } else { - lua_pushnil(L); - } - return 1; -} - // Player int LuaScriptInterface::luaPlayerCreate(lua_State* L) { - // Player(id or guid or name or userdata) + // Player(id or name or userdata) Player* player; if (isNumber(L, 2)) { - uint32_t id = getNumber(L, 2); - if (id >= 0x10000000 && id <= Player::playerAutoID) { - player = g_game.getPlayerByID(id); - } else { - player = g_game.getPlayerByGUID(id); - } + player = g_game.getPlayerByID(getNumber(L, 2)); } else if (isString(L, 2)) { ReturnValue ret = g_game.getPlayerByNameWildcard(getString(L, 2), player); if (ret != RETURNVALUE_NOERROR) { @@ -7987,6 +7095,19 @@ int LuaScriptInterface::luaPlayerGetLastLogout(lua_State* L) return 1; } +int LuaScriptInterface::luaPlayerHasFlag(lua_State * L) +{ + // player:hasFlag(flag) + Player* player = getUserdata(L, 1); + if (player) { + PlayerFlags flag = getNumber(L, 2); + pushBoolean(L, player->hasFlag(flag)); + } else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaPlayerGetAccountType(lua_State* L) { // player:getAccountType() @@ -8062,54 +7183,56 @@ int LuaScriptInterface::luaPlayerGetDepotChest(lua_State* L) uint32_t depotId = getNumber(L, 2); bool autoCreate = getBoolean(L, 3, false); - DepotChest* depotChest = player->getDepotChest(depotId, autoCreate); - if (depotChest) { - player->setLastDepotId(depotId); // FIXME: workaround for #2251 - pushUserdata(L, depotChest); - setItemMetatable(L, -1, depotChest); + DepotLocker* depotLocker = player->getDepotLocker(depotId, autoCreate); + if (depotLocker) { + if (!depotLocker->getParent() && player->getTile()) { + depotLocker->setParent(player->getTile()); + } + + pushUserdata(L, depotLocker); + setItemMetatable(L, -1, depotLocker); } else { pushBoolean(L, false); } return 1; } -int LuaScriptInterface::luaPlayerGetInbox(lua_State* L) +int LuaScriptInterface::luaPlayerGetMurderTimestamps(lua_State * L) { - // player:getInbox() - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - Inbox* inbox = player->getInbox(); - if (inbox) { - pushUserdata(L, inbox); - setItemMetatable(L, -1, inbox); - } else { - pushBoolean(L, false); - } - return 1; -} - -int LuaScriptInterface::luaPlayerGetSkullTime(lua_State* L) -{ - // player:getSkullTime() + // player:getMurderTimestamps() Player* player = getUserdata(L, 1); if (player) { - lua_pushnumber(L, player->getSkullTicks()); + lua_createtable(L, player->murderTimeStamps.size(), 0); + + uint32_t i = 1; + for (time_t currentMurderTimestamp : player->murderTimeStamps) { + lua_pushnumber(L, static_cast(currentMurderTimestamp)); + lua_rawseti(L, -2, ++i); + } } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaPlayerSetSkullTime(lua_State* L) +int LuaScriptInterface::luaPlayerGetPlayerKillerEnd(lua_State* L) { - // player:setSkullTime(skullTime) + // player:getPlayerKillerEnd() Player* player = getUserdata(L, 1); if (player) { - player->setSkullTicks(getNumber(L, 2)); + lua_pushnumber(L, player->getPlayerKillerEnd()); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaPlayerSetPlayerKillerEnd(lua_State* L) +{ + // player:setPlayerKillerEnd(skullTime) + Player* player = getUserdata(L, 1); + if (player) { + player->setPlayerKillerEnd(getNumber(L, 2)); pushBoolean(L, true); } else { lua_pushnil(L); @@ -8150,7 +7273,8 @@ int LuaScriptInterface::luaPlayerAddExperience(lua_State* L) bool sendText = getBoolean(L, 3, false); player->addExperience(nullptr, experience, sendText); pushBoolean(L, true); - } else { + } + else { lua_pushnil(L); } return 1; @@ -8158,12 +7282,11 @@ int LuaScriptInterface::luaPlayerAddExperience(lua_State* L) int LuaScriptInterface::luaPlayerRemoveExperience(lua_State* L) { - // player:removeExperience(experience[, sendText = false]) + // player:removeExperience(experience) Player* player = getUserdata(L, 1); if (player) { int64_t experience = getNumber(L, 2); - bool sendText = getBoolean(L, 3, false); - player->removeExperience(experience, sendText); + player->removeExperience(experience); pushBoolean(L, true); } else { lua_pushnil(L); @@ -8213,7 +7336,8 @@ int LuaScriptInterface::luaPlayerGetMana(lua_State* L) const Player* player = getUserdata(L, 1); if (player) { lua_pushnumber(L, player->getMana()); - } else { + } + else { lua_pushnil(L); } return 1; @@ -8232,9 +7356,10 @@ int LuaScriptInterface::luaPlayerAddMana(lua_State* L) bool animationOnLoss = getBoolean(L, 3, false); if (!animationOnLoss && manaChange < 0) { player->changeMana(manaChange); - } else { + } + else { CombatDamage damage; - damage.primary.value = manaChange; + damage.value = manaChange; damage.origin = ORIGIN_NONE; g_game.combatChangeMana(nullptr, player, damage); } @@ -8248,7 +7373,8 @@ int LuaScriptInterface::luaPlayerGetMaxMana(lua_State* L) const Player* player = getUserdata(L, 1); if (player) { lua_pushnumber(L, player->getMaxMana()); - } else { + } + else { lua_pushnil(L); } return 1; @@ -8385,123 +7511,6 @@ int LuaScriptInterface::luaPlayerAddSkillTries(lua_State* L) return 1; } -int LuaScriptInterface::luaPlayerGetSpecialSkill(lua_State* L) -{ - // player:getSpecialSkill(specialSkillType) - SpecialSkills_t specialSkillType = getNumber(L, 2); - Player* player = getUserdata(L, 1); - if (player && specialSkillType <= SPECIALSKILL_LAST) { - lua_pushnumber(L, player->getSpecialSkill(specialSkillType)); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerAddSpecialSkill(lua_State* L) -{ - // player:addSpecialSkill(specialSkillType, value) - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - SpecialSkills_t specialSkillType = getNumber(L, 2); - if (specialSkillType > SPECIALSKILL_LAST) { - lua_pushnil(L); - return 1; - } - - player->setVarSpecialSkill(specialSkillType, getNumber(L, 3)); - player->sendSkills(); - pushBoolean(L, true); - return 1; -} - -int LuaScriptInterface::luaPlayerAddOfflineTrainingTime(lua_State* L) -{ - // player:addOfflineTrainingTime(time) - Player* player = getUserdata(L, 1); - if (player) { - int32_t time = getNumber(L, 2); - player->addOfflineTrainingTime(time); - player->sendStats(); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - - -int LuaScriptInterface::luaPlayerGetOfflineTrainingTime(lua_State* L) -{ - // player:getOfflineTrainingTime() - Player* player = getUserdata(L, 1); - if (player) { - lua_pushnumber(L, player->getOfflineTrainingTime()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerRemoveOfflineTrainingTime(lua_State* L) -{ - // player:removeOfflineTrainingTime(time) - Player* player = getUserdata(L, 1); - if (player) { - int32_t time = getNumber(L, 2); - player->removeOfflineTrainingTime(time); - player->sendStats(); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerAddOfflineTrainingTries(lua_State* L) -{ - // player:addOfflineTrainingTries(skillType, tries) - Player* player = getUserdata(L, 1); - if (player) { - skills_t skillType = getNumber(L, 2); - uint64_t tries = getNumber(L, 3); - pushBoolean(L, player->addOfflineTrainingTries(skillType, tries)); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerGetOfflineTrainingSkill(lua_State* L) -{ - // player:getOfflineTrainingSkill() - Player* player = getUserdata(L, 1); - if (player) { - lua_pushnumber(L, player->getOfflineTrainingSkill()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerSetOfflineTrainingSkill(lua_State* L) -{ - // player:setOfflineTrainingSkill(skillId) - Player* player = getUserdata(L, 1); - if (player) { - uint32_t skillId = getNumber(L, 2); - player->setOfflineTrainingSkill(skillId); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaPlayerGetItemCount(lua_State* L) { // player:getItemCount(itemId[, subType = -1]) @@ -8791,7 +7800,8 @@ int LuaScriptInterface::luaPlayerGetStamina(lua_State* L) Player* player = getUserdata(L, 1); if (player) { lua_pushnumber(L, player->getStaminaMinutes()); - } else { + } + else { lua_pushnil(L); } return 1; @@ -8803,10 +7813,11 @@ int LuaScriptInterface::luaPlayerSetStamina(lua_State* L) uint16_t stamina = getNumber(L, 2); Player* player = getUserdata(L, 1); if (player) { - player->staminaMinutes = std::min(2520, stamina); + player->staminaMinutes = std::min(3360, stamina); player->sendStats(); pushBoolean(L, true); - } else { + } + else { lua_pushnil(L); } return 1; @@ -8897,7 +7908,7 @@ int LuaScriptInterface::luaPlayerGetStorageValue(lua_State* L) if (player->getStorageValue(key, value)) { lua_pushnumber(L, value); } else { - lua_pushnumber(L, -1); + lua_pushnumber(L, 0); } return 1; } @@ -8908,14 +7919,6 @@ int LuaScriptInterface::luaPlayerSetStorageValue(lua_State* L) int32_t value = getNumber(L, 3); uint32_t key = getNumber(L, 2); Player* player = getUserdata(L, 1); - if (IS_IN_KEYRANGE(key, RESERVED_RANGE)) { - std::ostringstream ss; - ss << "Accessing reserved range: " << key; - reportErrorFunc(ss.str()); - pushBoolean(L, false); - return 1; - } - if (player) { player->addStorageValue(key, value); pushBoolean(L, true); @@ -9120,7 +8123,7 @@ int LuaScriptInterface::luaPlayerRemoveMoney(lua_State* L) int LuaScriptInterface::luaPlayerShowTextDialog(lua_State* L) { - // player:showTextDialog(id or name or userdata[, text[, canWrite[, length]]]) + // player:showTextDialog(itemId[, text[, canWrite[, length]]]) Player* player = getUserdata(L, 1); if (!player) { lua_pushnil(L); @@ -9136,22 +8139,18 @@ int LuaScriptInterface::luaPlayerShowTextDialog(lua_State* L) text = getString(L, 3); } - Item* item; + uint16_t itemId; if (isNumber(L, 2)) { - item = Item::CreateItem(getNumber(L, 2)); - } else if (isString(L, 2)) { - item = Item::CreateItem(Item::items.getItemIdByName(getString(L, 2))); - } else if (isUserdata(L, 2)) { - if (getUserdataType(L, 2) != LuaData_Item) { - pushBoolean(L, false); + itemId = getNumber(L, 2); + } else { + itemId = Item::items.getItemIdByName(getString(L, 2)); + if (itemId == 0) { + lua_pushnil(L); return 1; } - - item = getUserdata(L, 2); - } else { - item = nullptr; } + Item* item = Item::CreateItem(itemId); if (!item) { reportErrorFunc(getErrorDesc(LUA_ERROR_ITEM_NOT_FOUND)); pushBoolean(L, false); @@ -9176,55 +8175,11 @@ int LuaScriptInterface::luaPlayerShowTextDialog(lua_State* L) int LuaScriptInterface::luaPlayerSendTextMessage(lua_State* L) { - // player:sendTextMessage(type, text[, position, primaryValue = 0, primaryColor = TEXTCOLOR_NONE[, secondaryValue = 0, secondaryColor = TEXTCOLOR_NONE]]) - // player:sendTextMessage(type, text, channelId) - - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - int parameters = lua_gettop(L); - + // player:sendTextMessage(type, text) TextMessage message(getNumber(L, 2), getString(L, 3)); - if (parameters == 4) { - uint16_t channelId = getNumber(L, 4); - ChatChannel* channel = g_chat->getChannel(*player, channelId); - if (!channel || !channel->hasUser(*player)) { - pushBoolean(L, false); - return 1; - } - message.channelId = channelId; - } else { - if (parameters >= 6) { - message.position = getPosition(L, 4); - message.primary.value = getNumber(L, 5); - message.primary.color = getNumber(L, 6); - } - - if (parameters >= 8) { - message.secondary.value = getNumber(L, 7); - message.secondary.color = getNumber(L, 8); - } - } - - player->sendTextMessage(message); - pushBoolean(L, true); - - return 1; -} - -int LuaScriptInterface::luaPlayerSendChannelMessage(lua_State* L) -{ - // player:sendChannelMessage(author, text, type, channelId) - uint16_t channelId = getNumber(L, 5); - SpeakClasses type = getNumber(L, 4); - const std::string& text = getString(L, 3); - const std::string& author = getString(L, 2); Player* player = getUserdata(L, 1); if (player) { - player->sendChannelMessage(author, text, type, channelId); + player->sendTextMessage(message); pushBoolean(L, true); } else { lua_pushnil(L); @@ -9243,7 +8198,7 @@ int LuaScriptInterface::luaPlayerSendPrivateMessage(lua_State* L) const Player* speaker = getUserdata(L, 2); const std::string& text = getString(L, 3); - SpeakClasses type = getNumber(L, 4, TALKTYPE_PRIVATE_FROM); + SpeakClasses type = getNumber(L, 4, TALKTYPE_PRIVATE); player->sendPrivateMessage(speaker, type, text); pushBoolean(L, true); return 1; @@ -9333,7 +8288,8 @@ int LuaScriptInterface::luaPlayerAddOutfit(lua_State* L) if (player) { player->addOutfit(getNumber(L, 2), 0); pushBoolean(L, true); - } else { + } + else { lua_pushnil(L); } return 1; @@ -9348,7 +8304,8 @@ int LuaScriptInterface::luaPlayerAddOutfitAddon(lua_State* L) uint8_t addon = getNumber(L, 3); player->addOutfit(lookType, addon); pushBoolean(L, true); - } else { + } + else { lua_pushnil(L); } return 1; @@ -9361,7 +8318,8 @@ int LuaScriptInterface::luaPlayerRemoveOutfit(lua_State* L) if (player) { uint16_t lookType = getNumber(L, 2); pushBoolean(L, player->removeOutfit(lookType)); - } else { + } + else { lua_pushnil(L); } return 1; @@ -9375,7 +8333,8 @@ int LuaScriptInterface::luaPlayerRemoveOutfitAddon(lua_State* L) uint16_t lookType = getNumber(L, 2); uint8_t addon = getNumber(L, 3); pushBoolean(L, player->removeOutfitAddon(lookType, addon)); - } else { + } + else { lua_pushnil(L); } return 1; @@ -9389,7 +8348,8 @@ int LuaScriptInterface::luaPlayerHasOutfit(lua_State* L) uint16_t lookType = getNumber(L, 2); uint8_t addon = getNumber(L, 3, 0); pushBoolean(L, player->canWear(lookType, addon)); - } else { + } + else { lua_pushnil(L); } return 1; @@ -9408,75 +8368,6 @@ int LuaScriptInterface::luaPlayerSendOutfitWindow(lua_State* L) return 1; } -int LuaScriptInterface::luaPlayerAddMount(lua_State* L) { - // player:addMount(mountId or mountName) - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - uint8_t mountId; - if (isNumber(L, 2)) { - mountId = getNumber(L, 2); - } else { - Mount* mount = g_game.mounts.getMountByName(getString(L, 2)); - if (!mount) { - lua_pushnil(L); - return 1; - } - mountId = mount->id; - } - pushBoolean(L, player->tameMount(mountId)); - return 1; -} - -int LuaScriptInterface::luaPlayerRemoveMount(lua_State* L) { - // player:removeMount(mountId or mountName) - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - uint8_t mountId; - if (isNumber(L, 2)) { - mountId = getNumber(L, 2); - } else { - Mount* mount = g_game.mounts.getMountByName(getString(L, 2)); - if (!mount) { - lua_pushnil(L); - return 1; - } - mountId = mount->id; - } - pushBoolean(L, player->untameMount(mountId)); - return 1; -} - -int LuaScriptInterface::luaPlayerHasMount(lua_State* L) { - // player:hasMount(mountId or mountName) - const Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - Mount* mount = nullptr; - if (isNumber(L, 2)) { - mount = g_game.mounts.getMountByID(getNumber(L, 2)); - } else { - mount = g_game.mounts.getMountByName(getString(L, 2)); - } - - if (mount) { - pushBoolean(L, player->hasMount(mount)); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaPlayerGetPremiumDays(lua_State* L) { // player:getPremiumDays() @@ -9660,36 +8551,6 @@ int LuaScriptInterface::luaPlayerHasLearnedSpell(lua_State* L) return 1; } -int LuaScriptInterface::luaPlayerSendTutorial(lua_State* L) -{ - // player:sendTutorial(tutorialId) - Player* player = getUserdata(L, 1); - if (player) { - uint8_t tutorialId = getNumber(L, 2); - player->sendTutorial(tutorialId); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerAddMapMark(lua_State* L) -{ - // player:addMapMark(position, type, description) - Player* player = getUserdata(L, 1); - if (player) { - const Position& position = getPosition(L, 2); - uint8_t type = getNumber(L, 3); - const std::string& description = getString(L, 4); - player->sendAddMarker(position, type, description); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaPlayerSave(lua_State* L) { // player:save() @@ -9703,20 +8564,6 @@ int LuaScriptInterface::luaPlayerSave(lua_State* L) return 1; } -int LuaScriptInterface::luaPlayerPopupFYI(lua_State* L) -{ - // player:popupFYI(message) - Player* player = getUserdata(L, 1); - if (player) { - const std::string& message = getString(L, 2); - player->sendFYIBox(message); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaPlayerIsPzLocked(lua_State* L) { // player:isPzLocked() @@ -9762,51 +8609,9 @@ int LuaScriptInterface::luaPlayerGetHouse(lua_State* L) return 1; } -int LuaScriptInterface::luaPlayerSendHouseWindow(lua_State* L) -{ - // player:sendHouseWindow(house, listId) - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - House* house = getUserdata(L, 2); - if (!house) { - lua_pushnil(L); - return 1; - } - - uint32_t listId = getNumber(L, 3); - player->sendHouseWindow(house, listId); - pushBoolean(L, true); - return 1; -} - -int LuaScriptInterface::luaPlayerSetEditHouse(lua_State* L) -{ - // player:setEditHouse(house, listId) - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - House* house = getUserdata(L, 2); - if (!house) { - lua_pushnil(L); - return 1; - } - - uint32_t listId = getNumber(L, 3); - player->setEditHouse(house, listId); - pushBoolean(L, true); - return 1; -} - int LuaScriptInterface::luaPlayerSetGhostMode(lua_State* L) { - // player:setGhostMode(enabled[, showEffect=true]) + // player:setGhostMode(enabled) Player* player = getUserdata(L, 1); if (!player) { lua_pushnil(L); @@ -9819,22 +8624,20 @@ int LuaScriptInterface::luaPlayerSetGhostMode(lua_State* L) return 1; } - bool showEffect = getBoolean(L, 3, true); - player->switchGhostMode(); Tile* tile = player->getTile(); const Position& position = player->getPosition(); - SpectatorVec spectators; - g_game.map.getSpectators(spectators, position, true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + g_game.map.getSpectators(list, position, true, true); + for (Creature* spectator : list) { Player* tmpPlayer = spectator->getPlayer(); if (tmpPlayer != player && !tmpPlayer->isAccessPlayer()) { if (enabled) { tmpPlayer->sendRemoveTileThing(position, tile->getStackposOfCreature(tmpPlayer, player)); } else { - tmpPlayer->sendCreatureAppear(player, position, showEffect); + tmpPlayer->sendCreatureAppear(player, position, true); } } else { tmpPlayer->sendCreatureChangeVisible(player, !enabled); @@ -9909,75 +8712,16 @@ int LuaScriptInterface::luaPlayerGetContainerIndex(lua_State* L) return 1; } -int LuaScriptInterface::luaPlayerGetInstantSpells(lua_State* L) +int LuaScriptInterface::luaPlayerGetTotalDamage(lua_State * L) { - // player:getInstantSpells() - Player* player = getUserdata(L, 1); - if (!player) { - lua_pushnil(L); - return 1; - } - - std::vector spells; - for (auto& spell : g_spells->getInstantSpells()) { - if (spell.second.canCast(player)) { - spells.push_back(&spell.second); - } - } - - lua_createtable(L, spells.size(), 0); - - int index = 0; - for (auto spell : spells) { - pushInstantSpell(L, *spell); - lua_rawseti(L, -2, ++index); - } - return 1; -} - -int LuaScriptInterface::luaPlayerCanCast(lua_State* L) -{ - // player:canCast(spell) - Player* player = getUserdata(L, 1); - InstantSpell* spell = getUserdata(L, 2); - if (player && spell) { - pushBoolean(L, spell->canCast(player)); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerHasChaseMode(lua_State* L) -{ - // player:hasChaseMode() + // player:getTotalDamage(attackSkill, attackValue, fightMode) Player* player = getUserdata(L, 1); if (player) { - pushBoolean(L, player->chaseMode); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerHasSecureMode(lua_State* L) -{ - // player:hasSecureMode() - Player* player = getUserdata(L, 1); - if (player) { - pushBoolean(L, player->secureMode); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaPlayerGetFightMode(lua_State* L) -{ - // player:getFightMode() - Player* player = getUserdata(L, 1); - if (player) { - lua_pushnumber(L, player->fightMode); + uint32_t attackSkill = getNumber(L, 2); + uint32_t attackValue = getNumber(L, 3); + fightMode_t fightMode = static_cast(getNumber(L, 4, FIGHTMODE_BALANCED)); + + lua_pushnumber(L, Combat::getTotalDamage(attackSkill, attackValue, fightMode)); } else { lua_pushnil(L); } @@ -10258,10 +9002,10 @@ int LuaScriptInterface::luaMonsterSelectTarget(lua_State* L) int LuaScriptInterface::luaMonsterSearchTarget(lua_State* L) { - // monster:searchTarget([searchType = TARGETSEARCH_DEFAULT]) + // monster:searchTarget([searchType = TARGETSEARCH_ANY]) Monster* monster = getUserdata(L, 1); if (monster) { - TargetSearchType_t searchType = getNumber(L, 2, TARGETSEARCH_DEFAULT); + TargetSearchType_t searchType = getNumber(L, 2, TARGETSEARCH_ANY); pushBoolean(L, monster->searchTarget(searchType)); } else { lua_pushnil(L); @@ -10324,28 +9068,6 @@ int LuaScriptInterface::luaNpcSetMasterPos(lua_State* L) return 1; } -int LuaScriptInterface::luaNpcGetSpeechBubble(lua_State* L) -{ - // npc:getSpeechBubble() - Npc* npc = getUserdata(L, 1); - if (npc) { - lua_pushnumber(L, npc->getSpeechBubble()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaNpcSetSpeechBubble(lua_State* L) -{ - // npc:setSpeechBubble(speechBubble) - Npc* npc = getUserdata(L, 1); - if (npc) { - npc->setSpeechBubble(getNumber(L, 2)); - } - return 0; -} - // Guild int LuaScriptInterface::luaGuildCreate(lua_State* L) { @@ -10467,32 +9189,6 @@ int LuaScriptInterface::luaGuildGetRankByLevel(lua_State* L) return 1; } -int LuaScriptInterface::luaGuildGetMotd(lua_State* L) -{ - // guild:getMotd() - Guild* guild = getUserdata(L, 1); - if (guild) { - pushString(L, guild->getMotd()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaGuildSetMotd(lua_State* L) -{ - // guild:setMotd(motd) - const std::string& motd = getString(L, 2); - Guild* guild = getUserdata(L, 1); - if (guild) { - guild->setMotd(motd); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - // Group int LuaScriptInterface::luaGroupCreate(lua_State* L) { @@ -10581,19 +9277,6 @@ int LuaScriptInterface::luaGroupGetMaxVipEntries(lua_State* L) return 1; } -int LuaScriptInterface::luaGroupHasFlag(lua_State* L) -{ - // group:hasFlag(flag) - Group* group = getUserdata(L, 1); - if (group) { - PlayerFlags flag = getNumber(L, 2); - pushBoolean(L, (group->flags & flag) != 0); - } else { - lua_pushnil(L); - } - return 1; -} - // Vocation int LuaScriptInterface::luaVocationCreate(lua_State* L) { @@ -10627,18 +9310,6 @@ int LuaScriptInterface::luaVocationGetId(lua_State* L) return 1; } -int LuaScriptInterface::luaVocationGetClientId(lua_State* L) -{ - // vocation:getClientId() - Vocation* vocation = getUserdata(L, 1); - if (vocation) { - lua_pushnumber(L, vocation->getClientId()); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaVocationGetName(lua_State* L) { // vocation:getName() @@ -11151,24 +9822,6 @@ int LuaScriptInterface::luaHouseGetDoorCount(lua_State* L) return 1; } -int LuaScriptInterface::luaHouseGetDoorIdByPosition(lua_State* L) -{ - // house:getDoorIdByPosition(position) - House* house = getUserdata(L, 1); - if (!house) { - lua_pushnil(L); - return 1; - } - - Door* door = house->getDoorByPosition(getPosition(L, 2)); - if (door) { - lua_pushnumber(L, door->getDoorId()); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaHouseGetTiles(lua_State* L) { // house:getTiles() @@ -11190,32 +9843,6 @@ int LuaScriptInterface::luaHouseGetTiles(lua_State* L) return 1; } -int LuaScriptInterface::luaHouseGetItems(lua_State* L) -{ - // house:getItems() - House* house = getUserdata(L, 1); - if (!house) { - lua_pushnil(L); - return 1; - } - - const auto& tiles = house->getTiles(); - lua_newtable(L); - - int index = 0; - for (Tile* tile : tiles) { - TileItemVector* itemVector = tile->getItemList(); - if(itemVector) { - for(Item* item : *itemVector) { - pushUserdata(L, item); - setItemMetatable(L, -1, item); - lua_rawseti(L, -2, ++index); - } - } - } - return 1; -} - int LuaScriptInterface::luaHouseGetTileCount(lua_State* L) { // house:getTileCount() @@ -11228,22 +9855,6 @@ int LuaScriptInterface::luaHouseGetTileCount(lua_State* L) return 1; } -int LuaScriptInterface::luaHouseCanEditAccessList(lua_State* L) -{ - // house:canEditAccessList(listId, player) - House* house = getUserdata(L, 1); - if (!house) { - lua_pushnil(L); - return 1; - } - - uint32_t listId = getNumber(L, 2); - Player* player = getPlayer(L, 3); - - pushBoolean(L, house->canEditAccessList(listId, player)); - return 1; -} - int LuaScriptInterface::luaHouseGetAccessList(lua_State* L) { // house:getAccessList(listId) @@ -11279,19 +9890,6 @@ int LuaScriptInterface::luaHouseSetAccessList(lua_State* L) return 1; } -int LuaScriptInterface::luaHouseKickPlayer(lua_State* L) -{ - // house:kickPlayer(player, targetPlayer) - House* house = getUserdata(L, 1); - if (!house) { - lua_pushnil(L); - return 1; - } - - pushBoolean(L, house->kickPlayer(getPlayer(L, 2), getPlayer(L, 3))); - return 1; -} - // ItemType int LuaScriptInterface::luaItemTypeCreate(lua_State* L) { @@ -11314,7 +9912,7 @@ int LuaScriptInterface::luaItemTypeIsCorpse(lua_State* L) // itemType:isCorpse() const ItemType* itemType = getUserdata(L, 1); if (itemType) { - pushBoolean(L, itemType->corpseType != RACE_NONE); + pushBoolean(L, itemType->corpse); } else { lua_pushnil(L); } @@ -11345,6 +9943,18 @@ int LuaScriptInterface::luaItemTypeIsContainer(lua_State* L) return 1; } +int LuaScriptInterface::luaItemTypeIsChest(lua_State * L) +{ + // itemType:isChest() + const ItemType* itemType = getUserdata(L, 1); + if (itemType) { + pushBoolean(L, itemType->isChest()); + } else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaItemTypeIsFluidContainer(lua_State* L) { // itemType:isFluidContainer() @@ -11417,31 +10027,7 @@ int LuaScriptInterface::luaItemTypeIsWritable(lua_State* L) return 1; } -int LuaScriptInterface::luaItemTypeIsBlocking(lua_State* L) -{ - // itemType:isBlocking() - const ItemType* itemType = getUserdata(L, 1); - if (itemType) { - pushBoolean(L, itemType->blockProjectile || itemType->blockSolid); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaItemTypeIsGroundTile(lua_State* L) -{ - // itemType:isGroundTile() - const ItemType* itemType = getUserdata(L, 1); - if (itemType) { - pushBoolean(L, itemType->isGroundTile()); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaItemTypeIsMagicField(lua_State* L) +int LuaScriptInterface::luaItemTypeIsMagicField(lua_State* L) { // itemType:isMagicField() const ItemType* itemType = getUserdata(L, 1); @@ -11453,24 +10039,61 @@ int LuaScriptInterface::luaItemTypeIsMagicField(lua_State* L) return 1; } -int LuaScriptInterface::luaItemTypeIsUseable(lua_State* L) +int LuaScriptInterface::luaItemTypeIsSplash(lua_State* L) { - // itemType:isUseable() + // itemType:isSplash() const ItemType* itemType = getUserdata(L, 1); if (itemType) { - pushBoolean(L, itemType->isUseable()); + pushBoolean(L, itemType->isSplash()); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaItemTypeIsPickupable(lua_State* L) +int LuaScriptInterface::luaItemTypeIsKey(lua_State* L) { - // itemType:isPickupable() + // itemType:isKey() const ItemType* itemType = getUserdata(L, 1); if (itemType) { - pushBoolean(L, itemType->isPickupable()); + pushBoolean(L, itemType->isKey()); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaItemTypeIsDisguised(lua_State* L) +{ + // itemType:isDisguised() + const ItemType* itemType = getUserdata(L, 1); + if (itemType) { + pushBoolean(L, itemType->disguise); + } + else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaItemTypeIsDestroyable(lua_State * L) +{ + // itemType:isDestroyable() + const ItemType* itemType = getUserdata(L, 1); + if (itemType) { + pushBoolean(L, itemType->destroy); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaItemTypeIsGroundTile(lua_State * L) +{ + // itemType:isGroundTile() + const ItemType* itemType = getUserdata(L, 1); + if (itemType) { + pushBoolean(L, itemType->group == ITEM_GROUP_GROUND); } else { lua_pushnil(L); } @@ -11501,13 +10124,14 @@ int LuaScriptInterface::luaItemTypeGetId(lua_State* L) return 1; } -int LuaScriptInterface::luaItemTypeGetClientId(lua_State* L) +int LuaScriptInterface::luaItemTypeGetDisguiseId(lua_State * L) { - // itemType:getClientId() + // itemType:getDisguiseId() const ItemType* itemType = getUserdata(L, 1); if (itemType) { - lua_pushnumber(L, itemType->clientId); - } else { + lua_pushnumber(L, itemType->disguiseId); + } + else { lua_pushnil(L); } return 1; @@ -11573,6 +10197,18 @@ int LuaScriptInterface::luaItemTypeGetSlotPosition(lua_State *L) return 1; } +int LuaScriptInterface::luaItemTypeGetDestroyTarget(lua_State * L) +{ + // itemType:getDestroyTarget() + const ItemType* itemType = getUserdata(L, 1); + if (itemType) { + lua_pushnumber(L, itemType->destroyTarget); + } else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaItemTypeGetCharges(lua_State* L) { // itemType:getCharges() @@ -11625,18 +10261,6 @@ int LuaScriptInterface::luaItemTypeGetWeight(lua_State* L) return 1; } -int LuaScriptInterface::luaItemTypeGetHitChance(lua_State* L) -{ - // itemType:getHitChance() - const ItemType* itemType = getUserdata(L, 1); - if (itemType) { - lua_pushnumber(L, itemType->hitChance); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaItemTypeGetShootRange(lua_State* L) { // itemType:getShootRange() @@ -11673,18 +10297,6 @@ int LuaScriptInterface::luaItemTypeGetDefense(lua_State* L) return 1; } -int LuaScriptInterface::luaItemTypeGetExtraDefense(lua_State* L) -{ - // itemType:getExtraDefense() - const ItemType* itemType = getUserdata(L, 1); - if (itemType) { - lua_pushnumber(L, itemType->extraDefense); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaItemTypeGetArmor(lua_State* L) { // itemType:getArmor() @@ -11709,66 +10321,6 @@ int LuaScriptInterface::luaItemTypeGetWeaponType(lua_State* L) return 1; } -int LuaScriptInterface::luaItemTypeGetAmmoType(lua_State* L) -{ - // itemType:getAmmoType() - const ItemType* itemType = getUserdata(L, 1); - if (itemType) { - lua_pushnumber(L, itemType->ammoType); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaItemTypeGetCorpseType(lua_State* L) -{ - // itemType:getCorpseType() - const ItemType* itemType = getUserdata(L, 1); - if (itemType) { - lua_pushnumber(L, itemType->corpseType); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaItemTypeGetElementType(lua_State* L) -{ - // itemType:getElementType() - const ItemType* itemType = getUserdata(L, 1); - if (!itemType) { - lua_pushnil(L); - return 1; - } - - auto& abilities = itemType->abilities; - if (abilities) { - lua_pushnumber(L, abilities->elementType); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaItemTypeGetElementDamage(lua_State* L) -{ - // itemType:getElementDamage() - const ItemType* itemType = getUserdata(L, 1); - if (!itemType) { - lua_pushnil(L); - return 1; - } - - auto& abilities = itemType->abilities; - if (abilities) { - lua_pushnumber(L, abilities->elementDamage); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaItemTypeGetTransformEquipId(lua_State* L) { // itemType:getTransformEquipId() @@ -11793,18 +10345,6 @@ int LuaScriptInterface::luaItemTypeGetTransformDeEquipId(lua_State* L) return 1; } -int LuaScriptInterface::luaItemTypeGetDestroyId(lua_State* L) -{ - // itemType:getDestroyId() - const ItemType* itemType = getUserdata(L, 1); - if (itemType) { - lua_pushnumber(L, itemType->destroyTo); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaItemTypeGetDecayId(lua_State* L) { // itemType:getDecayId() @@ -11817,6 +10357,18 @@ int LuaScriptInterface::luaItemTypeGetDecayId(lua_State* L) return 1; } +int LuaScriptInterface::luaItemTypeGetNutrition(lua_State* L) +{ + // itemType:getNutrition() + const ItemType* itemType = getUserdata(L, 1); + if (itemType) { + lua_pushnumber(L, itemType->nutrition); + } else { + lua_pushnil(L); + } + return 1; +} + int LuaScriptInterface::luaItemTypeGetRequiredLevel(lua_State* L) { // itemType:getRequiredLevel() @@ -11916,26 +10468,13 @@ int LuaScriptInterface::luaCombatSetArea(lua_State* L) return 1; } -int LuaScriptInterface::luaCombatAddCondition(lua_State* L) +int LuaScriptInterface::luaCombatSetCondition(lua_State* L) { - // combat:addCondition(condition) + // combat:setCondition(condition) Condition* condition = getUserdata(L, 2); Combat* combat = getUserdata(L, 1); if (combat && condition) { - combat->addCondition(condition->clone()); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCombatClearConditions(lua_State* L) -{ - // combat:clearConditions() - Combat* combat = getUserdata(L, 1); - if (combat) { - combat->clearConditions(); + combat->setCondition(condition->clone()); pushBoolean(L, true); } else { lua_pushnil(L); @@ -11976,7 +10515,8 @@ int LuaScriptInterface::luaCombatSetOrigin(lua_State* L) if (combat) { combat->setOrigin(getNumber(L, 2)); pushBoolean(L, true); - } else { + } + else { lua_pushnil(L); } return 1; @@ -11991,14 +10531,6 @@ int LuaScriptInterface::luaCombatExecute(lua_State* L) return 1; } - if (isUserdata(L, 2)) { - LuaDataType type = getUserdataType(L, 2); - if (type != LuaData_Player && type != LuaData_Monster && type != LuaData_Npc) { - pushBoolean(L, false); - return 1; - } - } - Creature* creature = getCreature(L, 2); const LuaVariant& variant = getVariant(L, 3); @@ -12207,16 +10739,13 @@ int LuaScriptInterface::luaConditionSetParameter(lua_State* L) return 1; } -int LuaScriptInterface::luaConditionSetFormula(lua_State* L) +int LuaScriptInterface::luaConditionSetSpeedDelta(lua_State* L) { - // condition:setFormula(mina, minb, maxa, maxb) - double maxb = getNumber(L, 5); - double maxa = getNumber(L, 4); - double minb = getNumber(L, 3); - double mina = getNumber(L, 2); + // condition:setSpeedDelta(speedDelta) + int32_t speedDelta = getNumber(L, 2); ConditionSpeed* condition = dynamic_cast(getUserdata(L, 1)); if (condition) { - condition->setFormulaVars(mina, minb, maxa, maxb); + condition->setSpeedDelta(speedDelta); pushBoolean(L, true); } else { lua_pushnil(L); @@ -12227,19 +10756,18 @@ int LuaScriptInterface::luaConditionSetFormula(lua_State* L) int LuaScriptInterface::luaConditionSetOutfit(lua_State* L) { // condition:setOutfit(outfit) - // condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet[, lookAddons[, lookMount]]) + // condition:setOutfit(lookTypeEx, lookType, lookHead, lookBody, lookLegs, lookFeet[, lookAddons]) Outfit_t outfit; if (isTable(L, 2)) { outfit = getOutfit(L, 2); } else { - outfit.lookMount = getNumber(L, 9, outfit.lookMount); outfit.lookAddons = getNumber(L, 8, outfit.lookAddons); outfit.lookFeet = getNumber(L, 7); outfit.lookLegs = getNumber(L, 6); outfit.lookBody = getNumber(L, 5); outfit.lookHead = getNumber(L, 4); - outfit.lookType = getNumber(L, 3); - outfit.lookTypeEx = getNumber(L, 2); + outfit.lookType = getNumber(L, 3); + outfit.lookTypeEx = getNumber(L, 2); } ConditionOutfit* condition = dynamic_cast(getUserdata(L, 1)); @@ -12252,21 +10780,37 @@ int LuaScriptInterface::luaConditionSetOutfit(lua_State* L) return 1; } -int LuaScriptInterface::luaConditionAddDamage(lua_State* L) +int LuaScriptInterface::luaConditionSetTiming(lua_State* L) { - // condition:addDamage(rounds, time, value) - int32_t value = getNumber(L, 4); - int32_t time = getNumber(L, 3); - int32_t rounds = getNumber(L, 2); + // condition:setTiming(count) + int32_t count = getNumber(L, 2); ConditionDamage* condition = dynamic_cast(getUserdata(L, 1)); if (condition) { - pushBoolean(L, condition->addDamage(rounds, time, value)); + if (condition->getType() == CONDITION_POISON) { + condition->setParam(CONDITION_PARAM_COUNT, 3); + condition->setParam(CONDITION_PARAM_MAX_COUNT, 3); + } else if (condition->getType() == CONDITION_FIRE) { + condition->setParam(CONDITION_PARAM_COUNT, 8); + condition->setParam(CONDITION_PARAM_MAX_COUNT, 8); + + count /= 10; + } else if (condition->getType() == CONDITION_ENERGY) { + condition->setParam(CONDITION_PARAM_COUNT, 10); + condition->setParam(CONDITION_PARAM_MAX_COUNT, 10); + + count /= 20; + } + + condition->setParam(CONDITION_PARAM_CYCLE, count); + + pushBoolean(L, true); } else { lua_pushnil(L); } return 1; } + // MonsterType int LuaScriptInterface::luaMonsterTypeCreate(lua_State* L) { @@ -12283,15 +10827,10 @@ int LuaScriptInterface::luaMonsterTypeCreate(lua_State* L) int LuaScriptInterface::luaMonsterTypeIsAttackable(lua_State* L) { - // get: monsterType:isAttackable() set: monsterType:isAttackable(bool) + // monsterType:isAttackable() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.isAttackable); - } else { - monsterType->info.isAttackable = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.isAttackable); } else { lua_pushnil(L); } @@ -12300,15 +10839,10 @@ int LuaScriptInterface::luaMonsterTypeIsAttackable(lua_State* L) int LuaScriptInterface::luaMonsterTypeIsConvinceable(lua_State* L) { - // get: monsterType:isConvinceable() set: monsterType:isConvinceable(bool) + // monsterType:isConvinceable() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.isConvinceable); - } else { - monsterType->info.isConvinceable = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.isConvinceable); } else { lua_pushnil(L); } @@ -12317,15 +10851,10 @@ int LuaScriptInterface::luaMonsterTypeIsConvinceable(lua_State* L) int LuaScriptInterface::luaMonsterTypeIsSummonable(lua_State* L) { - // get: monsterType:isSummonable() set: monsterType:isSummonable(bool) + // monsterType:isSummonable() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.isSummonable); - } else { - monsterType->info.isSummonable = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.isSummonable); } else { lua_pushnil(L); } @@ -12334,15 +10863,10 @@ int LuaScriptInterface::luaMonsterTypeIsSummonable(lua_State* L) int LuaScriptInterface::luaMonsterTypeIsIllusionable(lua_State* L) { - // get: monsterType:isIllusionable() set: monsterType:isIllusionable(bool) + // monsterType:isIllusionable() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.isIllusionable); - } else { - monsterType->info.isIllusionable = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.isIllusionable); } else { lua_pushnil(L); } @@ -12351,15 +10875,10 @@ int LuaScriptInterface::luaMonsterTypeIsIllusionable(lua_State* L) int LuaScriptInterface::luaMonsterTypeIsHostile(lua_State* L) { - // get: monsterType:isHostile() set: monsterType:isHostile(bool) + // monsterType:isHostile() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.isHostile); - } else { - monsterType->info.isHostile = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.isHostile); } else { lua_pushnil(L); } @@ -12368,33 +10887,24 @@ int LuaScriptInterface::luaMonsterTypeIsHostile(lua_State* L) int LuaScriptInterface::luaMonsterTypeIsPushable(lua_State* L) { - // get: monsterType:isPushable() set: monsterType:isPushable(bool) + // monsterType:isPushable() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.pushable); - } else { - monsterType->info.pushable = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.pushable); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeIsHealthHidden(lua_State* L) +int LuaScriptInterface::luaMonsterTypeIsHealthShown(lua_State* L) { - // get: monsterType:isHealthHidden() set: monsterType:isHealthHidden(bool) + // monsterType:isHealthShown() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.hiddenHealth); - } else { - monsterType->info.hiddenHealth = getBoolean(L, 2); - pushBoolean(L, true); - } - } else { + pushBoolean(L, !monsterType->info.hiddenHealth); + } + else { lua_pushnil(L); } return 1; @@ -12402,15 +10912,10 @@ int LuaScriptInterface::luaMonsterTypeIsHealthHidden(lua_State* L) int LuaScriptInterface::luaMonsterTypeCanPushItems(lua_State* L) { - // get: monsterType:canPushItems() set: monsterType:canPushItems(bool) + // monsterType:canPushItems() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.canPushItems); - } else { - monsterType->info.canPushItems = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.canPushItems); } else { lua_pushnil(L); } @@ -12419,226 +10924,106 @@ int LuaScriptInterface::luaMonsterTypeCanPushItems(lua_State* L) int LuaScriptInterface::luaMonsterTypeCanPushCreatures(lua_State* L) { - // get: monsterType:canPushCreatures() set: monsterType:canPushCreatures(bool) + // monsterType:canPushCreatures() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushBoolean(L, monsterType->info.canPushCreatures); - } else { - monsterType->info.canPushCreatures = getBoolean(L, 2); - pushBoolean(L, true); - } + pushBoolean(L, monsterType->info.canPushCreatures); } else { lua_pushnil(L); } return 1; } -int32_t LuaScriptInterface::luaMonsterTypeName(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetName(lua_State* L) { - // get: monsterType:name() set: monsterType:name(name) + // monsterType:getName() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushString(L, monsterType->name); - } else { - monsterType->name = getString(L, 2); - pushBoolean(L, true); - } + pushString(L, monsterType->name); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeNameDescription(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetNameDescription(lua_State* L) { - // get: monsterType:nameDescription() set: monsterType:nameDescription(desc) + // monsterType:getNameDescription() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushString(L, monsterType->nameDescription); - } else { - monsterType->nameDescription = getString(L, 2); - pushBoolean(L, true); - } + pushString(L, monsterType->nameDescription); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeHealth(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetHealth(lua_State* L) { - // get: monsterType:health() set: monsterType:health(health) + // monsterType:getHealth() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.health); - } else { - monsterType->info.health = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.health); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeMaxHealth(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetMaxHealth(lua_State* L) { - // get: monsterType:maxHealth() set: monsterType:maxHealth(health) + // monsterType:getMaxHealth() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.healthMax); - } else { - monsterType->info.healthMax = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.healthMax); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeRunHealth(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetRunHealth(lua_State* L) { - // get: monsterType:runHealth() set: monsterType:runHealth(health) + // monsterType:getRunHealth() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.runAwayHealth); - } else { - monsterType->info.runAwayHealth = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.runAwayHealth); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeExperience(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetExperience(lua_State* L) { - // get: monsterType:experience() set: monsterType:experience(exp) + // monsterType:getExperience() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.experience); - } else { - monsterType->info.experience = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.experience); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeCombatImmunities(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetCombatImmunities(lua_State* L) { - // get: monsterType:combatImmunities() set: monsterType:combatImmunities(immunity) + // monsterType:getCombatImmunities() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.damageImmunities); - } else { - std::string immunity = getString(L, 2); - if (immunity == "physical") { - monsterType->info.damageImmunities |= COMBAT_PHYSICALDAMAGE; - pushBoolean(L, true); - } else if (immunity == "energy") { - monsterType->info.damageImmunities |= COMBAT_ENERGYDAMAGE; - pushBoolean(L, true); - } else if (immunity == "fire") { - monsterType->info.damageImmunities |= COMBAT_FIREDAMAGE; - pushBoolean(L, true); - } else if (immunity == "poison" || immunity == "earth") { - monsterType->info.damageImmunities |= COMBAT_EARTHDAMAGE; - pushBoolean(L, true); - } else if (immunity == "drown") { - monsterType->info.damageImmunities |= COMBAT_DROWNDAMAGE; - pushBoolean(L, true); - } else if (immunity == "ice") { - monsterType->info.damageImmunities |= COMBAT_ICEDAMAGE; - pushBoolean(L, true); - } else if (immunity == "holy") { - monsterType->info.damageImmunities |= COMBAT_HOLYDAMAGE; - pushBoolean(L, true); - } else if (immunity == "death") { - monsterType->info.damageImmunities |= COMBAT_DEATHDAMAGE; - pushBoolean(L, true); - } else if (immunity == "lifedrain") { - monsterType->info.damageImmunities |= COMBAT_LIFEDRAIN; - pushBoolean(L, true); - } else if (immunity == "manadrain") { - monsterType->info.damageImmunities |= COMBAT_MANADRAIN; - pushBoolean(L, true); - } else { - std::cout << "[Warning - Monsters::loadMonster] Unknown immunity name " << immunity << " for monster: " << monsterType->name << std::endl; - lua_pushnil(L); - } - } + lua_pushnumber(L, monsterType->info.damageImmunities); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeConditionImmunities(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetConditionImmunities(lua_State* L) { - // get: monsterType:conditionImmunities() set: monsterType:conditionImmunities(immunity) + // monsterType:getConditionImmunities() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.conditionImmunities); - } else { - std::string immunity = getString(L, 2); - if (immunity == "physical") { - monsterType->info.conditionImmunities |= CONDITION_BLEEDING; - pushBoolean(L, true); - } else if (immunity == "energy") { - monsterType->info.conditionImmunities |= CONDITION_ENERGY; - pushBoolean(L, true); - } else if (immunity == "fire") { - monsterType->info.conditionImmunities |= CONDITION_FIRE; - pushBoolean(L, true); - } else if (immunity == "poison" || immunity == "earth") { - monsterType->info.conditionImmunities |= CONDITION_POISON; - pushBoolean(L, true); - } else if (immunity == "drown") { - monsterType->info.conditionImmunities |= CONDITION_DROWN; - pushBoolean(L, true); - } else if (immunity == "ice") { - monsterType->info.conditionImmunities |= CONDITION_FREEZING; - pushBoolean(L, true); - } else if (immunity == "holy") { - monsterType->info.conditionImmunities |= CONDITION_DAZZLED; - pushBoolean(L, true); - } else if (immunity == "death") { - monsterType->info.conditionImmunities |= CONDITION_CURSED; - pushBoolean(L, true); - } else if (immunity == "paralyze") { - monsterType->info.conditionImmunities |= CONDITION_PARALYZE; - pushBoolean(L, true); - } else if (immunity == "outfit") { - monsterType->info.conditionImmunities |= CONDITION_OUTFIT; - pushBoolean(L, true); - } else if (immunity == "drunk") { - monsterType->info.conditionImmunities |= CONDITION_DRUNK; - pushBoolean(L, true); - } else if (immunity == "invisible" || immunity == "invisibility") { - monsterType->info.conditionImmunities |= CONDITION_INVISIBLE; - pushBoolean(L, true); - } else if (immunity == "bleed") { - monsterType->info.conditionImmunities |= CONDITION_BLEEDING; - pushBoolean(L, true); - } else { - std::cout << "[Warning - Monsters::loadMonster] Unknown immunity name " << immunity << " for monster: " << monsterType->name << std::endl; - lua_pushnil(L); - } - } + lua_pushnumber(L, monsterType->info.conditionImmunities); } else { lua_pushnil(L); } @@ -12662,11 +11047,9 @@ int LuaScriptInterface::luaMonsterTypeGetAttackList(lua_State* L) setField(L, "chance", spellBlock.chance); setField(L, "isCombatSpell", spellBlock.combatSpell ? 1 : 0); - setField(L, "isMelee", spellBlock.isMelee ? 1 : 0); setField(L, "minCombatValue", spellBlock.minCombatValue); setField(L, "maxCombatValue", spellBlock.maxCombatValue); setField(L, "range", spellBlock.range); - setField(L, "speed", spellBlock.speed); pushUserdata(L, static_cast(spellBlock.spell)); lua_setfield(L, -2, "spell"); @@ -12675,29 +11058,6 @@ int LuaScriptInterface::luaMonsterTypeGetAttackList(lua_State* L) return 1; } -int LuaScriptInterface::luaMonsterTypeAddAttack(lua_State* L) -{ - // monsterType:addAttack(monsterspell) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - MonsterSpell* spell = getUserdata(L, 2); - if (spell) { - spellBlock_t sb; - if (g_monsters.deserializeSpell(spell, sb, monsterType->name)) { - monsterType->info.attackSpells.push_back(std::move(sb)); - } else { - std::cout << monsterType->name << std::endl; - std::cout << "[Warning - Monsters::loadMonster] Cant load spell. " << spell->name << std::endl; - } - } else { - lua_pushnil(L); - } - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaMonsterTypeGetDefenseList(lua_State* L) { // monsterType:getDefenseList() @@ -12716,11 +11076,9 @@ int LuaScriptInterface::luaMonsterTypeGetDefenseList(lua_State* L) setField(L, "chance", spellBlock.chance); setField(L, "isCombatSpell", spellBlock.combatSpell ? 1 : 0); - setField(L, "isMelee", spellBlock.isMelee ? 1 : 0); setField(L, "minCombatValue", spellBlock.minCombatValue); setField(L, "maxCombatValue", spellBlock.maxCombatValue); setField(L, "range", spellBlock.range); - setField(L, "speed", spellBlock.speed); pushUserdata(L, static_cast(spellBlock.spell)); lua_setfield(L, -2, "spell"); @@ -12729,29 +11087,6 @@ int LuaScriptInterface::luaMonsterTypeGetDefenseList(lua_State* L) return 1; } -int LuaScriptInterface::luaMonsterTypeAddDefense(lua_State* L) -{ - // monsterType:addDefense(monsterspell) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - MonsterSpell* spell = getUserdata(L, 2); - if (spell) { - spellBlock_t sb; - if (g_monsters.deserializeSpell(spell, sb, monsterType->name)) { - monsterType->info.defenseSpells.push_back(std::move(sb)); - } else { - std::cout << monsterType->name << std::endl; - std::cout << "[Warning - Monsters::loadMonster] Cant load spell. " << spell->name << std::endl; - } - } else { - lua_pushnil(L); - } - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaMonsterTypeGetElementList(lua_State* L) { // monsterType:getElementList() @@ -12769,20 +11104,6 @@ int LuaScriptInterface::luaMonsterTypeGetElementList(lua_State* L) return 1; } -int LuaScriptInterface::luaMonsterTypeAddElement(lua_State* L) -{ - // monsterType:addElement(type, percent) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - CombatType_t element = getNumber(L, 2); - monsterType->info.elementMap[element] = getNumber(L, 3); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaMonsterTypeGetVoices(lua_State* L) { // monsterType:getVoices() @@ -12803,24 +11124,6 @@ int LuaScriptInterface::luaMonsterTypeGetVoices(lua_State* L) return 1; } -int LuaScriptInterface::luaMonsterTypeAddVoice(lua_State* L) -{ - // monsterType:addVoice(sentence, interval, chance, yell) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - voiceBlock_t voice; - voice.text = getString(L, 2); - monsterType->info.yellSpeedTicks = getNumber(L, 3); - monsterType->info.yellChance = getNumber(L, 4); - voice.yellText = getBoolean(L, 5); - monsterType->info.voiceVector.push_back(voice); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaMonsterTypeGetLoot(lua_State* L) { // monsterType:getLoot() @@ -12830,25 +11133,27 @@ int LuaScriptInterface::luaMonsterTypeGetLoot(lua_State* L) return 1; } - pushLoot(L, monsterType->info.lootItems); - return 1; -} + static const std::function&)> parseLoot = [&](const std::vector& lootList) { + lua_createtable(L, lootList.size(), 0); -int LuaScriptInterface::luaMonsterTypeAddLoot(lua_State* L) -{ - // monsterType:addLoot(loot) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - Loot* loot = getUserdata(L, 2); - if (loot) { - monsterType->loadLoot(monsterType, loot->lootBlock); - pushBoolean(L, true); - } else { - lua_pushnil(L); + int index = 0; + for (const auto& lootBlock : lootList) { + lua_createtable(L, 0, 7); + + setField(L, "itemId", lootBlock.id); + setField(L, "chance", lootBlock.chance); + setField(L, "subType", lootBlock.subType); + setField(L, "maxCount", lootBlock.countmax); + setField(L, "actionId", lootBlock.actionId); + setField(L, "text", lootBlock.text); + + parseLoot(lootBlock.childLoot); + lua_setfield(L, -2, "childLoot"); + + lua_rawseti(L, -2, ++index); } - } else { - lua_pushnil(L); - } + }; + parseLoot(monsterType->info.lootItems); return 1; } @@ -12870,52 +11175,6 @@ int LuaScriptInterface::luaMonsterTypeGetCreatureEvents(lua_State* L) return 1; } -int LuaScriptInterface::luaMonsterTypeRegisterEvent(lua_State* L) -{ - // monsterType:registerEvent(name) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - monsterType->info.scripts.push_back(getString(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterTypeEventOnCallback(lua_State* L) -{ - // monsterType:onThink(callback) - // monsterType:onAppear(callback) - // monsterType:onDisappear(callback) - // monsterType:onMove(callback) - // monsterType:onSay(callback) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - if (monsterType->loadCallback(&g_scripts->getScriptInterface())) { - pushBoolean(L, true); - return 1; - } - pushBoolean(L, false); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterTypeEventType(lua_State* L) -{ - // monstertype:eventType(event) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - monsterType->info.eventType = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaMonsterTypeGetSummonList(lua_State* L) { // monsterType:getSummonList() @@ -12930,694 +11189,152 @@ int LuaScriptInterface::luaMonsterTypeGetSummonList(lua_State* L) for (const auto& summonBlock : monsterType->info.summons) { lua_createtable(L, 0, 3); setField(L, "name", summonBlock.name); - setField(L, "speed", summonBlock.speed); setField(L, "chance", summonBlock.chance); lua_rawseti(L, -2, ++index); } return 1; } -int LuaScriptInterface::luaMonsterTypeAddSummon(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetMaxSummons(lua_State* L) { - // monsterType:addSummon(name, interval, chance) + // monsterType:getMaxSummons() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - summonBlock_t summon; - summon.name = getString(L, 2); - summon.chance = getNumber(L, 3); - summon.speed = getNumber(L, 4); - monsterType->info.summons.push_back(summon); - pushBoolean(L, true); + lua_pushnumber(L, monsterType->info.maxSummons); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeMaxSummons(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetArmor(lua_State* L) { - // get: monsterType:maxSummons() set: monsterType:maxSummons(ammount) + // monsterType:getArmor() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.maxSummons); - } else { - monsterType->info.maxSummons = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.armor); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeArmor(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetDefense(lua_State* L) { - // get: monsterType:armor() set: monsterType:armor(armor) + // monsterType:getDefense() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.armor); - } else { - monsterType->info.armor = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.defense); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeDefense(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetOutfit(lua_State* L) { - // get: monsterType:defense() set: monsterType:defense(defense) + // monsterType:getOutfit() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.defense); - } else { - monsterType->info.defense = getNumber(L, 2); - pushBoolean(L, true); - } + pushOutfit(L, monsterType->info.outfit); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeOutfit(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetRace(lua_State* L) { - // get: monsterType:outfit() set: monsterType:outfit(outfit) + // monsterType:getRace() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - pushOutfit(L, monsterType->info.outfit); - } else { - monsterType->info.outfit = getOutfit(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.race); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeRace(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetCorpseId(lua_State* L) { - // get: monsterType:race() set: monsterType:race(race) + // monsterType:getCorpseId() MonsterType* monsterType = getUserdata(L, 1); - std::string race = getString(L, 2); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.race); - } else { - if (race == "venom") { - monsterType->info.race = RACE_VENOM; - } else if (race == "blood") { - monsterType->info.race = RACE_BLOOD; - } else if (race == "undead") { - monsterType->info.race = RACE_UNDEAD; - } else if (race == "fire") { - monsterType->info.race = RACE_FIRE; - } else if (race == "energy") { - monsterType->info.race = RACE_ENERGY; - } else { - std::cout << "[Warning - Monsters::loadMonster] Unknown race type " << race << "." << std::endl; - lua_pushnil(L); - return 1; - } - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.lookcorpse); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeCorpseId(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetManaCost(lua_State* L) { - // get: monsterType:corpseId() set: monsterType:corpseId(id) + // monsterType:getManaCost() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.lookcorpse); - } else { - monsterType->info.lookcorpse = getNumber(L, 2); - lua_pushboolean(L, true); - } + lua_pushnumber(L, monsterType->info.manaCost); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeManaCost(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetBaseSpeed(lua_State* L) { - // get: monsterType:manaCost() set: monsterType:manaCost(mana) + // monsterType:getBaseSpeed() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.manaCost); - } else { - monsterType->info.manaCost = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.baseSpeed); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeBaseSpeed(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetLight(lua_State* L) { - // get: monsterType:baseSpeed() set: monsterType:baseSpeed(speed) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.baseSpeed); - } else { - monsterType->info.baseSpeed = getNumber(L, 2); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterTypeLight(lua_State* L) -{ - // get: monsterType:light() set: monsterType:light(color, level) + // monsterType:getLight() MonsterType* monsterType = getUserdata(L, 1); if (!monsterType) { lua_pushnil(L); return 1; } - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.light.level); - lua_pushnumber(L, monsterType->info.light.color); - return 2; - } else { - monsterType->info.light.color = getNumber(L, 2); - monsterType->info.light.level = getNumber(L, 3); - pushBoolean(L, true); - } - return 1; + + lua_pushnumber(L, monsterType->info.light.level); + lua_pushnumber(L, monsterType->info.light.color); + return 2; } -int LuaScriptInterface::luaMonsterTypeStaticAttackChance(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetTargetDistance(lua_State* L) { - // get: monsterType:staticAttackChance() set: monsterType:staticAttackChance(chance) + // monsterType:getTargetDistance() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.staticAttackChance); - } else { - monsterType->info.staticAttackChance = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.targetDistance); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeTargetDistance(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetChangeTargetChance(lua_State* L) { - // get: monsterType:targetDistance() set: monsterType:targetDistance(distance) + // monsterType:getChangeTargetChance() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.targetDistance); - } else { - monsterType->info.targetDistance = getNumber(L, 2); - pushBoolean(L, true); - } + lua_pushnumber(L, monsterType->info.changeTargetChance); } else { lua_pushnil(L); } return 1; } -int LuaScriptInterface::luaMonsterTypeYellChance(lua_State* L) +int LuaScriptInterface::luaMonsterTypeGetChangeTargetSpeed(lua_State* L) { - // get: monsterType:yellChance() set: monsterType:yellChance(chance) + // monsterType:getChangeTargetSpeed() MonsterType* monsterType = getUserdata(L, 1); if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.yellChance); - } else { - monsterType->info.yellChance = getNumber(L, 2); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterTypeYellSpeedTicks(lua_State* L) -{ - // get: monsterType:yellSpeedTicks() set: monsterType:yellSpeedTicks(rate) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.yellSpeedTicks); - } else { - monsterType->info.yellSpeedTicks = getNumber(L, 2); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterTypeChangeTargetChance(lua_State* L) -{ - // get: monsterType:changeTargetChance() set: monsterType:changeTargetChance(chance) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.changeTargetChance); - } else { - monsterType->info.changeTargetChance = getNumber(L, 2); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterTypeChangeTargetSpeed(lua_State* L) -{ - // get: monsterType:changeTargetSpeed() set: monsterType:changeTargetSpeed(speed) - MonsterType* monsterType = getUserdata(L, 1); - if (monsterType) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, monsterType->info.changeTargetSpeed); - } else { - monsterType->info.changeTargetSpeed = getNumber(L, 2); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// Loot -int LuaScriptInterface::luaCreateLoot(lua_State* L) -{ - // Loot() will create a new loot item - Loot* loot = new Loot(); - if (loot) { - pushUserdata(L, loot); - setMetatable(L, -1, "Loot"); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaDeleteLoot(lua_State* L) -{ - // loot:delete() loot:__gc() - Loot** lootPtr = getRawUserdata(L, 1); - if (lootPtr && *lootPtr) { - delete *lootPtr; - *lootPtr = nullptr; - } - return 0; -} - -int LuaScriptInterface::luaLootSetId(lua_State* L) -{ - // loot:setId(id or name) - Loot* loot = getUserdata(L, 1); - uint16_t item; - if (loot) { - if (isNumber(L, 2)) { - loot->lootBlock.id = getNumber(L, 2); - } else { - item = Item::items.getItemIdByName(getString(L, 2)); - loot->lootBlock.id = item; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaLootSetSubType(lua_State* L) -{ - // loot:setSubType(type) - Loot* loot = getUserdata(L, 1); - if (loot) { - loot->lootBlock.subType = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaLootSetChance(lua_State* L) -{ - // loot:setChance(chance) - Loot* loot = getUserdata(L, 1); - if (loot) { - loot->lootBlock.chance = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaLootSetMaxCount(lua_State* L) -{ - // loot:setMaxCount(max) - Loot* loot = getUserdata(L, 1); - if (loot) { - loot->lootBlock.countmax = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaLootSetActionId(lua_State* L) -{ - // loot:setActionId(actionid) - Loot* loot = getUserdata(L, 1); - if (loot) { - loot->lootBlock.actionId = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaLootSetDescription(lua_State* L) -{ - // loot:setDescription(desc) - Loot* loot = getUserdata(L, 1); - if (loot) { - loot->lootBlock.text = getString(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaLootAddChildLoot(lua_State* L) -{ - // loot:addChildLoot(loot) - Loot* loot = getUserdata(L, 1); - if (loot) { - loot->lootBlock.childLoot.push_back(getUserdata(L, 2)->lootBlock); - } else { - lua_pushnil(L); - } - return 1; -} - -// MonsterSpell -int LuaScriptInterface::luaCreateMonsterSpell(lua_State* L) -{ - // MonsterSpell() will create a new Monster Spell - MonsterSpell* spell = new MonsterSpell(); - if (spell) { - pushUserdata(L, spell); - setMetatable(L, -1, "MonsterSpell"); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaDeleteMonsterSpell(lua_State* L) -{ - // monsterSpell:delete() monsterSpell:__gc() - MonsterSpell** monsterSpellPtr = getRawUserdata(L, 1); - if (monsterSpellPtr && *monsterSpellPtr) { - delete *monsterSpellPtr; - *monsterSpellPtr = nullptr; - } - return 0; -} - -int LuaScriptInterface::luaMonsterSpellSetType(lua_State* L) -{ - // monsterSpell:setType(type) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->name = getString(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetScriptName(lua_State* L) -{ - // monsterSpell:setScriptName(name) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->scriptName = getString(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetChance(lua_State* L) -{ - // monsterSpell:setChance(chance) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->chance = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetInterval(lua_State* L) -{ - // monsterSpell:setInterval(interval) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->interval = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetRange(lua_State* L) -{ - // monsterSpell:setRange(range) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->range = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetCombatValue(lua_State* L) -{ - // monsterSpell:setCombatValue(min, max) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->minCombatValue = getNumber(L, 2); - spell->maxCombatValue = getNumber(L, 3); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetCombatType(lua_State* L) -{ - // monsterSpell:setCombatType(combatType_t) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->combatType = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetAttackValue(lua_State* L) -{ - // monsterSpell:setAttackValue(attack, skill) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->attack = getNumber(L, 2); - spell->skill = getNumber(L, 3); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetNeedTarget(lua_State* L) -{ - // monsterSpell:setNeedTarget(bool) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->needTarget = getBoolean(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetCombatLength(lua_State* L) -{ - // monsterSpell:setCombatLength(length) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->length = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetCombatSpread(lua_State* L) -{ - // monsterSpell:setCombatSpread(spread) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->spread = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetCombatRadius(lua_State* L) -{ - // monsterSpell:setCombatRadius(radius) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->radius = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetConditionType(lua_State* L) -{ - // monsterSpell:setConditionType(type) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->conditionType = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetConditionDamage(lua_State* L) -{ - // monsterSpell:setConditionDamage(min, max, start) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->conditionMinDamage = getNumber(L, 2); - spell->conditionMaxDamage = getNumber(L, 3); - spell->conditionStartDamage = getNumber(L, 4); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetConditionSpeedChange(lua_State* L) -{ - // monsterSpell:setConditionSpeedChange(speed) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->speedChange = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetConditionDuration(lua_State* L) -{ - // monsterSpell:setConditionDuration(duration) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->duration = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetConditionTickInterval(lua_State* L) -{ - // monsterSpell:setConditionTickInterval(interval) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->tickInterval = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetCombatShootEffect(lua_State* L) -{ - // monsterSpell:setCombatShootEffect(effect) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->shoot = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMonsterSpellSetCombatEffect(lua_State* L) -{ - // monsterSpell:setCombatEffect(effect) - MonsterSpell* spell = getUserdata(L, 1); - if (spell) { - spell->effect = getNumber(L, 2); - pushBoolean(L, true); + lua_pushnumber(L, monsterType->info.changeTargetSpeed); } else { lua_pushnil(L); } @@ -13625,28 +11342,6 @@ int LuaScriptInterface::luaMonsterSpellSetCombatEffect(lua_State* L) } // Party -int32_t LuaScriptInterface::luaPartyCreate(lua_State* L) -{ - // Party(userdata) - Player* player = getUserdata(L, 2); - if (!player) { - lua_pushnil(L); - return 1; - } - - Party* party = player->getParty(); - if (!party) { - party = new Party(player); - g_game.updatePlayerShield(player); - player->sendCreatureSkull(player); - pushUserdata(L, party); - setMetatable(L, -1, "Party"); - } else { - lua_pushnil(L); - } - return 1; -} - int LuaScriptInterface::luaPartyDisband(lua_State* L) { // party:disband() @@ -13838,7 +11533,7 @@ int LuaScriptInterface::luaPartyShareExperience(lua_State* L) uint64_t experience = getNumber(L, 2); Party* party = getUserdata(L, 1); if (party) { - party->shareExperience(experience); + party->shareExperience(experience, nullptr); pushBoolean(L, true); } else { lua_pushnil(L); @@ -13859,2222 +11554,6 @@ int LuaScriptInterface::luaPartySetSharedExperience(lua_State* L) return 1; } -// Spells -int LuaScriptInterface::luaSpellCreate(lua_State* L) -{ - // Spell(words, name or id) to get an existing spell - // Spell(type) ex: Spell(SPELL_INSTANT) or Spell(SPELL_RUNE) to create a new spell - if (lua_gettop(L) == 1) { - std::cout << "[Error - Spell::luaSpellCreate] There is no parameter set!" << std::endl; - lua_pushnil(L); - return 1; - } - - SpellType_t type = getNumber(L, 2); - - if (isString(L, 2)) { - std::string tmp = asLowerCaseString(getString(L, 2)); - if (tmp == "instant") { - type = SPELL_INSTANT; - } else if (tmp == "rune") { - type = SPELL_RUNE; - } - } - - if (type == SPELL_INSTANT) { - InstantSpell* spell = new InstantSpell(getScriptEnv()->getScriptInterface()); - spell->fromLua = true; - pushUserdata(L, spell); - setMetatable(L, -1, "Spell"); - spell->spellType = SPELL_INSTANT; - return 1; - } else if (type == SPELL_RUNE) { - RuneSpell* spell = new RuneSpell(getScriptEnv()->getScriptInterface()); - spell->fromLua = true; - pushUserdata(L, spell); - setMetatable(L, -1, "Spell"); - spell->spellType = SPELL_RUNE; - return 1; - } - - // isNumber(L, 2) doesn't work here for some reason, maybe a bug? - if (getNumber(L, 2)) { - InstantSpell* instant = g_spells->getInstantSpellById(getNumber(L, 2)); - if (instant) { - pushUserdata(L, instant); - setMetatable(L, -1, "Spell"); - return 1; - } - RuneSpell* rune = g_spells->getRuneSpell(getNumber(L, 2)); - if (rune) { - pushUserdata(L, rune); - setMetatable(L, -1, "Spell"); - return 1; - } - } else if (isString(L, 2)) { - std::string arg = getString(L, 2); - InstantSpell* instant = g_spells->getInstantSpellByName(arg); - if (instant) { - pushUserdata(L, instant); - setMetatable(L, -1, "Spell"); - return 1; - } - instant = g_spells->getInstantSpell(arg); - if (instant) { - pushUserdata(L, instant); - setMetatable(L, -1, "Spell"); - return 1; - } - RuneSpell* rune = g_spells->getRuneSpellByName(arg); - if (rune) { - pushUserdata(L, rune); - setMetatable(L, -1, "Spell"); - return 1; - } - } - lua_pushnil(L); - return 1; -} - -int LuaScriptInterface::luaSpellOnCastSpell(lua_State* L) -{ - // spell:onCastSpell(callback) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (spell->spellType == SPELL_INSTANT) { - InstantSpell* instant = dynamic_cast(getUserdata(L, 1)); - if (!instant->loadCallback()) { - pushBoolean(L, false); - return 1; - } - instant->scripted = true; - pushBoolean(L, true); - } else if (spell->spellType == SPELL_RUNE) { - RuneSpell* rune = dynamic_cast(getUserdata(L, 1)); - if (!rune->loadCallback()) { - pushBoolean(L, false); - return 1; - } - rune->scripted = true; - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellRegister(lua_State* L) -{ - // spell:register() - Spell* spell = getUserdata(L, 1); - if (spell) { - if (spell->spellType == SPELL_INSTANT) { - InstantSpell* instant = dynamic_cast(getUserdata(L, 1)); - if (!instant->isScripted()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_spells->registerInstantLuaEvent(instant)); - } else if (spell->spellType == SPELL_RUNE) { - RuneSpell* rune = dynamic_cast(getUserdata(L, 1)); - if (rune->getMagicLevel() != 0 || rune->getLevel() != 0) { - //Change information in the ItemType to get accurate description - ItemType& iType = Item::items.getItemType(rune->getRuneItemId()); - iType.name = rune->getName(); - iType.runeMagLevel = rune->getMagicLevel(); - iType.runeLevel = rune->getLevel(); - iType.charges = rune->getCharges(); - } - if (!rune->isScripted()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_spells->registerRuneLuaEvent(rune)); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellName(lua_State* L) -{ - // spell:name(name) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushString(L, spell->getName()); - } else { - spell->setName(getString(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellId(lua_State* L) -{ - // spell:id(id) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getId()); - } else { - spell->setId(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellGroup(lua_State* L) -{ - // spell:group(primaryGroup[, secondaryGroup]) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getGroup()); - lua_pushnumber(L, spell->getSecondaryGroup()); - return 2; - } else if (lua_gettop(L) == 2) { - SpellGroup_t group = getNumber(L, 2); - if (group) { - spell->setGroup(group); - pushBoolean(L, true); - } else if (isString(L, 2)) { - group = stringToSpellGroup(getString(L, 2)); - if (group != SPELLGROUP_NONE) { - spell->setGroup(group); - } else { - std::cout << "[Warning - Spell::group] Unknown group: " << getString(L, 2) << std::endl; - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - std::cout << "[Warning - Spell::group] Unknown group: " << getString(L, 2) << std::endl; - pushBoolean(L, false); - return 1; - } - } else { - SpellGroup_t primaryGroup = getNumber(L, 2); - SpellGroup_t secondaryGroup = getNumber(L, 2); - if (primaryGroup && secondaryGroup) { - spell->setGroup(primaryGroup); - spell->setSecondaryGroup(secondaryGroup); - pushBoolean(L, true); - } else if (isString(L, 2) && isString(L, 3)) { - primaryGroup = stringToSpellGroup(getString(L, 2)); - if (primaryGroup != SPELLGROUP_NONE) { - spell->setGroup(primaryGroup); - } else { - std::cout << "[Warning - Spell::group] Unknown primaryGroup: " << getString(L, 2) << std::endl; - pushBoolean(L, false); - return 1; - } - secondaryGroup = stringToSpellGroup(getString(L, 3)); - if (secondaryGroup != SPELLGROUP_NONE) { - spell->setSecondaryGroup(secondaryGroup); - } else { - std::cout << "[Warning - Spell::group] Unknown secondaryGroup: " << getString(L, 3) << std::endl; - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - std::cout << "[Warning - Spell::group] Unknown primaryGroup: " << getString(L, 2) << " or secondaryGroup: " << getString(L, 3) << std::endl; - pushBoolean(L, false); - return 1; - } - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellCooldown(lua_State* L) -{ - // spell:cooldown(cooldown) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getCooldown()); - } else { - spell->setCooldown(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellGroupCooldown(lua_State* L) -{ - // spell:groupCooldown(primaryGroupCd[, secondaryGroupCd]) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getGroupCooldown()); - lua_pushnumber(L, spell->getSecondaryCooldown()); - return 2; - } else if (lua_gettop(L) == 2) { - spell->setGroupCooldown(getNumber(L, 2)); - pushBoolean(L, true); - } else { - spell->setGroupCooldown(getNumber(L, 2)); - spell->setSecondaryCooldown(getNumber(L, 3)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellLevel(lua_State* L) -{ - // spell:level(lvl) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getLevel()); - } else { - spell->setLevel(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellMagicLevel(lua_State* L) -{ - // spell:magicLevel(lvl) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getMagicLevel()); - } else { - spell->setMagicLevel(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellMana(lua_State* L) -{ - // spell:mana(mana) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getMana()); - } else { - spell->setMana(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellManaPercent(lua_State* L) -{ - // spell:manaPercent(percent) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getManaPercent()); - } else { - spell->setManaPercent(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellSoul(lua_State* L) -{ - // spell:soul(soul) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getSoulCost()); - } else { - spell->setSoulCost(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellRange(lua_State* L) -{ - // spell:range(range) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getRange()); - } else { - spell->setRange(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellPremium(lua_State* L) -{ - // spell:isPremium(bool) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->isPremium()); - } else { - spell->setPremium(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellEnabled(lua_State* L) -{ - // spell:isEnabled(bool) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->isEnabled()); - } else { - spell->setEnabled(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellNeedTarget(lua_State* L) -{ - // spell:needTarget(bool) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedTarget()); - } else { - spell->setNeedTarget(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellNeedWeapon(lua_State* L) -{ - // spell:needWeapon(bool) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedWeapon()); - } else { - spell->setNeedWeapon(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellNeedLearn(lua_State* L) -{ - // spell:needLearn(bool) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedLearn()); - } else { - spell->setNeedLearn(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellSelfTarget(lua_State* L) -{ - // spell:isSelfTarget(bool) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getSelfTarget()); - } else { - spell->setSelfTarget(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellBlocking(lua_State* L) -{ - // spell:isBlocking(blockingSolid, blockingCreature) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getBlockingSolid()); - pushBoolean(L, spell->getBlockingCreature()); - return 2; - } else { - spell->setBlockingSolid(getBoolean(L, 2)); - spell->setBlockingCreature(getBoolean(L, 3)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellAggressive(lua_State* L) -{ - // spell:isAggressive(bool) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getAggressive()); - } else { - spell->setAggressive(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaSpellVocation(lua_State* L) -{ - // spell:vocation(vocation) - Spell* spell = getUserdata(L, 1); - if (spell) { - if (lua_gettop(L) == 1) { - lua_createtable(L, 0, 0); - auto it = 0; - for (auto voc : spell->getVocMap()) { - ++it; - std::string s = std::to_string(it); - char const *pchar = s.c_str(); - std::string name = g_vocations.getVocation(voc.first)->getVocName(); - setField(L, pchar, name); - } - setMetatable(L, -1, "Spell"); - } else { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - for (int i = 0; i < parameters; ++i) { - if (getString(L, 2 + i).find(";") != std::string::npos) { - std::vector vocList = explodeString(getString(L, 2 + i), ";"); - int32_t vocationId = g_vocations.getVocationId(vocList[0]); - if (vocList.size() > 0) { - if (vocList[1] == "true") { - spell->addVocMap(vocationId, true); - } else { - spell->addVocMap(vocationId, false); - } - } - } else { - int32_t vocationId = g_vocations.getVocationId(getString(L, 2 + i)); - spell->addVocMap(vocationId, false); - } - } - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int LuaScriptInterface::luaSpellWords(lua_State* L) -{ - // spell:words(words[, separator = ""]) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushString(L, spell->getWords()); - pushString(L, spell->getSeparator()); - return 2; - } else { - std::string sep = ""; - if (lua_gettop(L) == 3) { - sep = getString(L, 3); - } - spell->setWords(getString(L, 2)); - spell->setSeparator(sep); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int LuaScriptInterface::luaSpellNeedDirection(lua_State* L) -{ - // spell:needDirection(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedDirection()); - } else { - spell->setNeedDirection(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int LuaScriptInterface::luaSpellHasParams(lua_State* L) -{ - // spell:hasParams(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getHasParam()); - } else { - spell->setHasParam(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int LuaScriptInterface::luaSpellHasPlayerNameParam(lua_State* L) -{ - // spell:hasPlayerNameParam(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getHasPlayerNameParam()); - } else { - spell->setHasPlayerNameParam(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int LuaScriptInterface::luaSpellNeedCasterTargetOrDirection(lua_State* L) -{ - // spell:needCasterTargetOrDirection(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getNeedCasterTargetOrDirection()); - } else { - spell->setNeedCasterTargetOrDirection(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for InstantSpells -int LuaScriptInterface::luaSpellIsBlockingWalls(lua_State* L) -{ - // spell:blockWalls(bool) - InstantSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_INSTANT, it means that this actually is no InstantSpell, so we return nil - if (spell->spellType != SPELL_INSTANT) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getBlockWalls()); - } else { - spell->setBlockWalls(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int LuaScriptInterface::luaSpellRuneId(lua_State* L) -{ - // spell:runeId(id) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getRuneItemId()); - } else { - spell->setRuneItemId(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int LuaScriptInterface::luaSpellCharges(lua_State* L) -{ - // spell:charges(charges) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - lua_pushnumber(L, spell->getCharges()); - } else { - spell->setCharges(getNumber(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int LuaScriptInterface::luaSpellAllowFarUse(lua_State* L) -{ - // spell:allowFarUse(bool) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getAllowFarUse()); - } else { - spell->setAllowFarUse(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int LuaScriptInterface::luaSpellBlockWalls(lua_State* L) -{ - // spell:blockWalls(bool) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getCheckLineOfSight()); - } else { - spell->setCheckLineOfSight(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -// only for RuneSpells -int LuaScriptInterface::luaSpellCheckFloor(lua_State* L) -{ - // spell:checkFloor(bool) - RuneSpell* spell = dynamic_cast(getUserdata(L, 1)); - if (spell) { - // if spell != SPELL_RUNE, it means that this actually is no RuneSpell, so we return nil - if (spell->spellType != SPELL_RUNE) { - lua_pushnil(L); - return 1; - } - - if (lua_gettop(L) == 1) { - pushBoolean(L, spell->getCheckFloor()); - } else { - spell->setCheckFloor(getBoolean(L, 2)); - pushBoolean(L, true); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreateAction(lua_State* L) -{ - // Action() - if (getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc("Actions can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - Action* action = new Action(getScriptEnv()->getScriptInterface()); - if (action) { - action->fromLua = true; - pushUserdata(L, action); - setMetatable(L, -1, "Action"); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionOnUse(lua_State* L) -{ - // action:onUse(callback) - Action* action = getUserdata(L, 1); - if (action) { - if (!action->loadCallback()) { - pushBoolean(L, false); - return 1; - } - action->scripted = true; - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionRegister(lua_State* L) -{ - // action:register() - Action* action = getUserdata(L, 1); - if (action) { - if (!action->isScripted()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_actions->registerLuaEvent(action)); - action->getActionIdRange().clear(); - action->getItemIdRange().clear(); - action->getUniqueIdRange().clear(); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionItemId(lua_State* L) -{ - // action:id(ids) - Action* action = getUserdata(L, 1); - if (action) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - action->addItemId(getNumber(L, 2 + i)); - } - } else { - action->addItemId(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionActionId(lua_State* L) -{ - // action:aid(aids) - Action* action = getUserdata(L, 1); - if (action) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - action->addActionId(getNumber(L, 2 + i)); - } - } else { - action->addActionId(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionUniqueId(lua_State* L) -{ - // action:uid(uids) - Action* action = getUserdata(L, 1); - if (action) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - action->addUniqueId(getNumber(L, 2 + i)); - } - } else { - action->addUniqueId(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionAllowFarUse(lua_State* L) -{ - // action:allowFarUse(bool) - Action* action = getUserdata(L, 1); - if (action) { - action->setAllowFarUse(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionBlockWalls(lua_State* L) -{ - // action:blockWalls(bool) - Action* action = getUserdata(L, 1); - if (action) { - action->setCheckLineOfSight(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaActionCheckFloor(lua_State* L) -{ - // action:checkFloor(bool) - Action* action = getUserdata(L, 1); - if (action) { - action->setCheckFloor(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreateTalkaction(lua_State* L) -{ - // TalkAction(words) - if (getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc("TalkActions can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - TalkAction* talk = new TalkAction(getScriptEnv()->getScriptInterface()); - if (talk) { - talk->setWords(getString(L, 2)); - talk->fromLua = true; - pushUserdata(L, talk); - setMetatable(L, -1, "TalkAction"); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaTalkactionOnSay(lua_State* L) -{ - // talkAction:onSay(callback) - TalkAction* talk = getUserdata(L, 1); - if (talk) { - if (!talk->loadCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaTalkactionRegister(lua_State* L) -{ - // talkAction:register() - TalkAction* talk = getUserdata(L, 1); - if (talk) { - if (!talk->isScripted()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_talkActions->registerLuaEvent(talk)); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaTalkactionSeparator(lua_State* L) -{ - // talkAction:separator(sep) - TalkAction* talk = getUserdata(L, 1); - if (talk) { - talk->setSeparator(getString(L, 2).c_str()); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreateCreatureEvent(lua_State* L) -{ - // CreatureEvent(eventName) - if (getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc("CreatureEvents can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - CreatureEvent* creature = new CreatureEvent(getScriptEnv()->getScriptInterface()); - if (creature) { - creature->setName(getString(L, 2)); - creature->fromLua = true; - pushUserdata(L, creature); - setMetatable(L, -1, "CreatureEvent"); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreatureEventType(lua_State* L) -{ - // creatureevent:type(callback) - CreatureEvent* creature = getUserdata(L, 1); - if (creature) { - std::string typeName = getString(L, 2); - std::string tmpStr = asLowerCaseString(typeName); - if (tmpStr == "login") { - creature->setEventType(CREATURE_EVENT_LOGIN); - } else if (tmpStr == "logout") { - creature->setEventType(CREATURE_EVENT_LOGOUT); - } else if (tmpStr == "think") { - creature->setEventType(CREATURE_EVENT_THINK); - } else if (tmpStr == "preparedeath") { - creature->setEventType(CREATURE_EVENT_PREPAREDEATH); - } else if (tmpStr == "death") { - creature->setEventType(CREATURE_EVENT_DEATH); - } else if (tmpStr == "kill") { - creature->setEventType(CREATURE_EVENT_KILL); - } else if (tmpStr == "advance") { - creature->setEventType(CREATURE_EVENT_ADVANCE); - } else if (tmpStr == "modalwindow") { - creature->setEventType(CREATURE_EVENT_MODALWINDOW); - } else if (tmpStr == "textedit") { - creature->setEventType(CREATURE_EVENT_TEXTEDIT); - } else if (tmpStr == "healthchange") { - creature->setEventType(CREATURE_EVENT_HEALTHCHANGE); - } else if (tmpStr == "manachange") { - creature->setEventType(CREATURE_EVENT_MANACHANGE); - } else if (tmpStr == "extendedopcode") { - creature->setEventType(CREATURE_EVENT_EXTENDED_OPCODE); - } else { - std::cout << "[Error - CreatureEvent::configureLuaEvent] Invalid type for creature event: " << typeName << std::endl; - pushBoolean(L, false); - } - creature->setLoaded(true); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreatureEventRegister(lua_State* L) -{ - // creatureevent:register() - CreatureEvent* creature = getUserdata(L, 1); - if (creature) { - if (!creature->isScripted()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_creatureEvents->registerLuaEvent(creature)); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreatureEventOnCallback(lua_State* L) -{ - // creatureevent:onLogin / logout / etc. (callback) - CreatureEvent* creature = getUserdata(L, 1); - if (creature) { - if (!creature->loadCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreateMoveEvent(lua_State* L) -{ - // MoveEvent() - if (getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc("MoveEvents can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - MoveEvent* moveevent = new MoveEvent(getScriptEnv()->getScriptInterface()); - if (moveevent) { - moveevent->fromLua = true; - pushUserdata(L, moveevent); - setMetatable(L, -1, "MoveEvent"); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventType(lua_State* L) -{ - // moveevent:type(callback) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - std::string typeName = getString(L, 2); - std::string tmpStr = asLowerCaseString(typeName); - if (tmpStr == "stepin") { - moveevent->setEventType(MOVE_EVENT_STEP_IN); - moveevent->stepFunction = moveevent->StepInField; - } else if (tmpStr == "stepout") { - moveevent->setEventType(MOVE_EVENT_STEP_OUT); - moveevent->stepFunction = moveevent->StepOutField; - } else if (tmpStr == "equip") { - moveevent->setEventType(MOVE_EVENT_EQUIP); - moveevent->equipFunction = moveevent->EquipItem; - } else if (tmpStr == "deequip") { - moveevent->setEventType(MOVE_EVENT_DEEQUIP); - moveevent->equipFunction = moveevent->DeEquipItem; - } else if (tmpStr == "additem") { - moveevent->setEventType(MOVE_EVENT_ADD_ITEM_ITEMTILE); - moveevent->moveFunction = moveevent->AddItemField; - } else if (tmpStr == "removeitem") { - moveevent->setEventType(MOVE_EVENT_REMOVE_ITEM_ITEMTILE); - moveevent->moveFunction = moveevent->RemoveItemField; - } else { - std::cout << "Error: [MoveEvent::configureMoveEvent] No valid event name " << typeName << std::endl; - pushBoolean(L, false); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventRegister(lua_State* L) -{ - // moveevent:register() - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - if (!moveevent->isScripted()) { - pushBoolean(L, g_moveEvents->registerLuaFunction(moveevent)); - return 1; - } - pushBoolean(L, g_moveEvents->registerLuaEvent(moveevent)); - moveevent->getItemIdRange().clear(); - moveevent->getActionIdRange().clear(); - moveevent->getUniqueIdRange().clear(); - moveevent->getPosList().clear(); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventOnCallback(lua_State* L) -{ - // moveevent:onEquip / deEquip / etc. (callback) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - if (!moveevent->loadCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventSlot(lua_State* L) -{ - // moveevent:slot(slot) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - if (moveevent->getEventType() == MOVE_EVENT_EQUIP || moveevent->getEventType() == MOVE_EVENT_DEEQUIP) { - if (!moveevent->getSlotName().empty()) { - std::string slotName = getString(L, 2); - std::string tmpStr = asLowerCaseString(slotName); - tmpStr = asLowerCaseString(moveevent->getSlotName()); - if (tmpStr == "head") { - moveevent->setSlot(SLOTP_HEAD); - } else if (tmpStr == "necklace") { - moveevent->setSlot(SLOTP_NECKLACE); - } else if (tmpStr == "backpack") { - moveevent->setSlot(SLOTP_BACKPACK); - } else if (tmpStr == "armor" || tmpStr == "body") { - moveevent->setSlot(SLOTP_ARMOR); - } else if (tmpStr == "right-hand") { - moveevent->setSlot(SLOTP_RIGHT); - } else if (tmpStr == "left-hand") { - moveevent->setSlot(SLOTP_LEFT); - } else if (tmpStr == "hand" || tmpStr == "shield") { - moveevent->setSlot(SLOTP_RIGHT | SLOTP_LEFT); - } else if (tmpStr == "legs") { - moveevent->setSlot(SLOTP_LEGS); - } else if (tmpStr == "feet") { - moveevent->setSlot(SLOTP_FEET); - } else if (tmpStr == "ring") { - moveevent->setSlot(SLOTP_RING); - } else if (tmpStr == "ammo") { - moveevent->setSlot(SLOTP_AMMO); - } else { - std::cout << "[Warning - MoveEvent::configureMoveEvent] Unknown slot type: " << moveevent->getSlotName() << std::endl; - } - } - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventLevel(lua_State* L) -{ - // moveevent:level(lvl) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - moveevent->setRequiredLevel(getNumber(L, 2)); - moveevent->setWieldInfo(WIELDINFO_LEVEL); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventMagLevel(lua_State* L) -{ - // moveevent:magicLevel(lvl) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - moveevent->setRequiredMagLevel(getNumber(L, 2)); - moveevent->setWieldInfo(WIELDINFO_MAGLV); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventPremium(lua_State* L) -{ - // moveevent:premium(bool) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - moveevent->setNeedPremium(getBoolean(L, 2)); - moveevent->setWieldInfo(WIELDINFO_PREMIUM); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventVocation(lua_State* L) -{ - // moveevent:vocation(vocName[, showInDescription = false, lastVoc = false]) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - moveevent->addVocEquipMap(getString(L, 2)); - moveevent->setWieldInfo(WIELDINFO_VOCREQ); - std::string tmp; - bool showInDescription = false; - bool lastVoc = false; - if (getBoolean(L, 3)) { - showInDescription = getBoolean(L, 3); - } - if (getBoolean(L, 4)) { - lastVoc = getBoolean(L, 4); - } - if (showInDescription) { - if (moveevent->getVocationString().empty()) { - tmp = asLowerCaseString(getString(L, 2)); - tmp += "s"; - moveevent->setVocationString(tmp); - } else { - tmp = moveevent->getVocationString(); - if (lastVoc) { - tmp += " and "; - } else { - tmp += ", "; - } - tmp += asLowerCaseString(getString(L, 2)); - tmp += "s"; - moveevent->setVocationString(tmp); - } - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventItemId(lua_State* L) -{ - // moveevent:id(ids) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - moveevent->addItemId(getNumber(L, 2 + i)); - } - } else { - moveevent->addItemId(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventActionId(lua_State* L) -{ - // moveevent:aid(ids) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - moveevent->addActionId(getNumber(L, 2 + i)); - } - } else { - moveevent->addActionId(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventUniqueId(lua_State* L) -{ - // moveevent:uid(ids) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - moveevent->addUniqueId(getNumber(L, 2 + i)); - } - } else { - moveevent->addUniqueId(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaMoveEventPosition(lua_State* L) -{ - // moveevent:position(positions) - MoveEvent* moveevent = getUserdata(L, 1); - if (moveevent) { - int parameters = lua_gettop(L) - 1; // - 1 because self is a parameter aswell, which we want to skip ofc - if (parameters > 1) { - for (int i = 0; i < parameters; ++i) { - moveevent->addPosList(getPosition(L, 2 + i)); - } - } else { - moveevent->addPosList(getPosition(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaCreateGlobalEvent(lua_State* L) -{ - // GlobalEvent(eventName) - if (getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc("GlobalEvents can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - GlobalEvent* global = new GlobalEvent(getScriptEnv()->getScriptInterface()); - if (global) { - global->setName(getString(L, 2)); - global->setEventType(GLOBALEVENT_NONE); - global->fromLua = true; - pushUserdata(L, global); - setMetatable(L, -1, "GlobalEvent"); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaGlobalEventType(lua_State* L) -{ - // globalevent:type(callback) - GlobalEvent* global = getUserdata(L, 1); - if (global) { - std::string typeName = getString(L, 2); - std::string tmpStr = asLowerCaseString(typeName); - if (tmpStr == "startup") { - global->setEventType(GLOBALEVENT_STARTUP); - } else if (tmpStr == "shutdown") { - global->setEventType(GLOBALEVENT_SHUTDOWN); - } else if (tmpStr == "record") { - global->setEventType(GLOBALEVENT_RECORD); - } else { - std::cout << "[Error - CreatureEvent::configureLuaEvent] Invalid type for global event: " << typeName << std::endl; - pushBoolean(L, false); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaGlobalEventRegister(lua_State* L) -{ - // globalevent:register() - GlobalEvent* globalevent = getUserdata(L, 1); - if (globalevent) { - if (!globalevent->isScripted()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, g_globalEvents->registerLuaEvent(globalevent)); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaGlobalEventOnCallback(lua_State* L) -{ - // globalevent:onThink / record / etc. (callback) - GlobalEvent* globalevent = getUserdata(L, 1); - if (globalevent) { - if (!globalevent->loadCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaGlobalEventTime(lua_State* L) -{ - // globalevent:time(time) - GlobalEvent* globalevent = getUserdata(L, 1); - if (globalevent) { - std::string timer = getString(L, 2); - std::vector params = vectorAtoi(explodeString(timer, ":")); - - int32_t hour = params.front(); - if (hour < 0 || hour > 23) { - std::cout << "[Error - GlobalEvent::configureEvent] Invalid hour \"" << timer << "\" for globalevent with name: " << globalevent->getName() << std::endl; - pushBoolean(L, false); - return 1; - } - - globalevent->setInterval(hour << 16); - - int32_t min = 0; - int32_t sec = 0; - if (params.size() > 1) { - min = params[1]; - if (min < 0 || min > 59) { - std::cout << "[Error - GlobalEvent::configureEvent] Invalid minute \"" << timer << "\" for globalevent with name: " << globalevent->getName() << std::endl; - pushBoolean(L, false); - return 1; - } - - if (params.size() > 2) { - sec = params[2]; - if (sec < 0 || sec > 59) { - std::cout << "[Error - GlobalEvent::configureEvent] Invalid second \"" << timer << "\" for globalevent with name: " << globalevent->getName() << std::endl; - pushBoolean(L, false); - return 1; - } - } - } - - time_t current_time = time(nullptr); - tm* timeinfo = localtime(¤t_time); - timeinfo->tm_hour = hour; - timeinfo->tm_min = min; - timeinfo->tm_sec = sec; - - time_t difference = static_cast(difftime(mktime(timeinfo), current_time)); - if (difference < 0) { - difference += 86400; - } - - globalevent->setNextExecution(current_time + difference); - globalevent->setEventType(GLOBALEVENT_TIMER); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaGlobalEventInterval(lua_State* L) -{ - // globalevent:interval(interval) - GlobalEvent* globalevent = getUserdata(L, 1); - if (globalevent) { - globalevent->setInterval(getNumber(L, 2)); - globalevent->setNextExecution(OTSYS_TIME() + getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -// Weapon -int LuaScriptInterface::luaCreateWeapon(lua_State* L) -{ - // Weapon(type) - if (getScriptEnv()->getScriptInterface() != &g_scripts->getScriptInterface()) { - reportErrorFunc("Weapons can only be registered in the Scripts interface."); - lua_pushnil(L); - return 1; - } - - WeaponType_t type = getNumber(L, 2); - switch (type) { - case WEAPON_SWORD: - case WEAPON_AXE: - case WEAPON_CLUB: { - WeaponMelee* weapon = new WeaponMelee(getScriptEnv()->getScriptInterface()); - if (weapon) { - pushUserdata(L, weapon); - setMetatable(L, -1, "Weapon"); - weapon->weaponType = type; - } else { - lua_pushnil(L); - } - break; - } - case WEAPON_DISTANCE: - case WEAPON_AMMO: { - WeaponDistance* weapon = new WeaponDistance(getScriptEnv()->getScriptInterface()); - if (weapon) { - pushUserdata(L, weapon); - setMetatable(L, -1, "Weapon"); - weapon->weaponType = type; - } else { - lua_pushnil(L); - } - break; - } - case WEAPON_WAND: { - WeaponWand* weapon = new WeaponWand(getScriptEnv()->getScriptInterface()); - if (weapon) { - pushUserdata(L, weapon); - setMetatable(L, -1, "Weapon"); - weapon->weaponType = type; - } else { - lua_pushnil(L); - } - break; - } - default: { - lua_pushnil(L); - break; - } - } - return 1; -} - -int LuaScriptInterface::luaWeaponAction(lua_State* L) -{ - // weapon:action(callback) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - std::string typeName = getString(L, 2); - std::string tmpStr = asLowerCaseString(typeName); - if (tmpStr == "removecount") { - weapon->action = WEAPONACTION_REMOVECOUNT; - } else if (tmpStr == "removecharge") { - weapon->action = WEAPONACTION_REMOVECHARGE; - } else if (tmpStr == "move") { - weapon->action = WEAPONACTION_MOVE; - } else { - std::cout << "Error: [Weapon::action] No valid action " << typeName << std::endl; - pushBoolean(L, false); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponRegister(lua_State* L) -{ - // weapon:register() - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - if (weapon->weaponType == WEAPON_DISTANCE || weapon->weaponType == WEAPON_AMMO) { - weapon = getUserdata(L, 1); - } else if (weapon->weaponType == WEAPON_WAND) { - weapon = getUserdata(L, 1); - } else { - weapon = getUserdata(L, 1); - } - - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.weaponType = weapon->weaponType; - - if (weapon->getWieldInfo() != 0) { - it.wieldInfo = weapon->getWieldInfo(); - it.vocationString = weapon->getVocationString(); - it.minReqLevel = weapon->getReqLevel(); - it.minReqMagicLevel = weapon->getReqMagLv(); - } - - weapon->configureWeapon(it); - pushBoolean(L, g_weapons->registerLuaEvent(weapon)); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponOnUseWeapon(lua_State* L) -{ - // weapon:onUseWeapon(callback) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - if (!weapon->loadCallback()) { - pushBoolean(L, false); - return 1; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponUnproperly(lua_State* L) -{ - // weapon:wieldedUnproperly(bool) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setWieldUnproperly(getBoolean(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponLevel(lua_State* L) -{ - // weapon:level(lvl) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setRequiredLevel(getNumber(L, 2)); - weapon->setWieldInfo(WIELDINFO_LEVEL); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponMagicLevel(lua_State* L) -{ - // weapon:magicLevel(lvl) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setRequiredMagLevel(getNumber(L, 2)); - weapon->setWieldInfo(WIELDINFO_MAGLV); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponMana(lua_State* L) -{ - // weapon:mana(mana) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setMana(getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponManaPercent(lua_State* L) -{ - // weapon:manaPercent(percent) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setManaPercent(getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponHealth(lua_State* L) -{ - // weapon:health(health) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setHealth(getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponHealthPercent(lua_State* L) -{ - // weapon:healthPercent(percent) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setHealthPercent(getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponSoul(lua_State* L) -{ - // weapon:soul(soul) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setSoul(getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponBreakChance(lua_State* L) -{ - // weapon:breakChance(percent) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setBreakChance(getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponWandDamage(lua_State* L) -{ - // weapon:damage(damage[min, max]) only use this if the weapon is a wand! - WeaponWand* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setMinChange(getNumber(L, 2)); - if (lua_gettop(L) > 2) { - weapon->setMaxChange(getNumber(L, 3)); - } else { - weapon->setMaxChange(getNumber(L, 2)); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponElement(lua_State* L) -{ - // weapon:element(combatType) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - if (!getNumber(L, 2)) { - std::string element = getString(L, 2); - std::string tmpStrValue = asLowerCaseString(element); - if (tmpStrValue == "earth") { - weapon->params.combatType = COMBAT_EARTHDAMAGE; - } else if (tmpStrValue == "ice") { - weapon->params.combatType = COMBAT_ICEDAMAGE; - } else if (tmpStrValue == "energy") { - weapon->params.combatType = COMBAT_ENERGYDAMAGE; - } else if (tmpStrValue == "fire") { - weapon->params.combatType = COMBAT_FIREDAMAGE; - } else if (tmpStrValue == "death") { - weapon->params.combatType = COMBAT_DEATHDAMAGE; - } else if (tmpStrValue == "holy") { - weapon->params.combatType = COMBAT_HOLYDAMAGE; - } else { - std::cout << "[Warning - weapon:element] Type \"" << element << "\" does not exist." << std::endl; - } - } else { - weapon->params.combatType = getNumber(L, 2); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponPremium(lua_State* L) -{ - // weapon:premium(bool) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setNeedPremium(getBoolean(L, 2)); - weapon->setWieldInfo(WIELDINFO_PREMIUM); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponVocation(lua_State* L) -{ - // weapon:vocation(vocName[, showInDescription = false, lastVoc = false]) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->addVocWeaponMap(getString(L, 2)); - weapon->setWieldInfo(WIELDINFO_VOCREQ); - std::string tmp; - bool showInDescription = getBoolean(L, 3, false); - bool lastVoc = getBoolean(L, 4, false); - - if (showInDescription) { - if (weapon->getVocationString().empty()) { - tmp = asLowerCaseString(getString(L, 2)); - tmp += "s"; - weapon->setVocationString(tmp); - } else { - tmp = weapon->getVocationString(); - if (lastVoc) { - tmp += " and "; - } else { - tmp += ", "; - } - tmp += asLowerCaseString(getString(L, 2)); - tmp += "s"; - weapon->setVocationString(tmp); - } - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponId(lua_State* L) -{ - // weapon:id(id) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - weapon->setID(getNumber(L, 2)); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponAttack(lua_State* L) -{ - // weapon:attack(atk) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.attack = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponDefense(lua_State* L) -{ - // weapon:defense(defense[, extraDefense]) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.defense = getNumber(L, 2); - if (lua_gettop(L) > 2) { - it.extraDefense = getNumber(L, 3); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponRange(lua_State* L) -{ - // weapon:range(range) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.shootRange = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponCharges(lua_State* L) -{ - // weapon:charges(charges[, showCharges = true]) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - bool showCharges = getBoolean(L, 3, true); - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - - it.charges = getNumber(L, 2); - it.showCharges = showCharges; - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponDuration(lua_State* L) -{ - // weapon:duration(duration[, showDuration = true]) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - bool showDuration = getBoolean(L, 3, true); - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - - it.decayTime = getNumber(L, 2); - it.showDuration = showDuration; - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponDecayTo(lua_State* L) -{ - // weapon:decayTo([itemid = 0] - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t itemid = getNumber(L, 2, 0); - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - - it.decayTo = itemid; - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponTransformEquipTo(lua_State* L) -{ - // weapon:transformEquipTo(itemid) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.transformEquipTo = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponTransformDeEquipTo(lua_State* L) -{ - // weapon:transformDeEquipTo(itemid) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.transformDeEquipTo = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponShootType(lua_State* L) -{ - // weapon:shootType(type) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.shootType = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponSlotType(lua_State* L) -{ - // weapon:slotType(slot) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - std::string slot = getString(L, 2); - - if (slot == "two-handed") { - it.slotPosition |= SLOTP_TWO_HAND; - } else { - it.slotPosition |= SLOTP_HAND; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponAmmoType(lua_State* L) -{ - // weapon:ammoType(type) - WeaponDistance* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - std::string type = getString(L, 2); - - if (type == "arrow") { - it.ammoType = AMMO_ARROW; - } else if (type == "bolt"){ - it.ammoType = AMMO_BOLT; - } else { - std::cout << "[Warning - weapon:ammoType] Type \"" << type << "\" does not exist." << std::endl; - lua_pushnil(L); - return 1; - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponHitChance(lua_State* L) -{ - // weapon:hitChance(chance) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.hitChance = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponMaxHitChance(lua_State* L) -{ - // weapon:maxHitChance(max) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.maxHitChance = getNumber(L, 2); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int LuaScriptInterface::luaWeaponExtraElement(lua_State* L) -{ - // weapon:extraElement(atk, combatType) - Weapon* weapon = getUserdata(L, 1); - if (weapon) { - uint16_t id = weapon->getID(); - ItemType& it = Item::items.getItemType(id); - it.abilities.get()->elementDamage = getNumber(L, 2); - - if (!getNumber(L, 3)) { - std::string element = getString(L, 3); - std::string tmpStrValue = asLowerCaseString(element); - if (tmpStrValue == "earth") { - it.abilities.get()->elementType = COMBAT_EARTHDAMAGE; - } else if (tmpStrValue == "ice") { - it.abilities.get()->elementType = COMBAT_ICEDAMAGE; - } else if (tmpStrValue == "energy") { - it.abilities.get()->elementType = COMBAT_ENERGYDAMAGE; - } else if (tmpStrValue == "fire") { - it.abilities.get()->elementType = COMBAT_FIREDAMAGE; - } else if (tmpStrValue == "death") { - it.abilities.get()->elementType = COMBAT_DEATHDAMAGE; - } else if (tmpStrValue == "holy") { - it.abilities.get()->elementType = COMBAT_HOLYDAMAGE; - } else { - std::cout << "[Warning - weapon:extraElement] Type \"" << element << "\" does not exist." << std::endl; - } - } else { - it.abilities.get()->elementType = getNumber(L, 3); - } - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - // LuaEnvironment::LuaEnvironment() : LuaScriptInterface("Main Interface") {} diff --git a/src/luascript.h b/src/luascript.h index b2644cb..8fd8a1e 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,11 +20,7 @@ #ifndef FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8 #define FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8 -#if __has_include("luajit/lua.hpp") #include -#else -#include -#endif #if LUA_VERSION_NUM >= 502 #ifndef LUA_COMPAT_ALL @@ -39,7 +35,6 @@ #include "database.h" #include "enums.h" #include "position.h" -#include class Thing; class Creature; @@ -51,7 +46,6 @@ class Combat; class Condition; class Npc; class Monster; -class InstantSpell; enum { EVENT_ID_LOADING = 1, @@ -99,8 +93,7 @@ struct LuaTimerEventDesc { class LuaScriptInterface; class Cylinder; class Game; - -struct LootBlock; +class Npc; class ScriptEnvironment { @@ -155,9 +148,9 @@ class ScriptEnvironment void removeItemByUID(uint32_t uid); private: - using VariantVector = std::vector; - using StorageMap = std::map; - using DBResultMap = std::map; + typedef std::vector VariantVector; + typedef std::map StorageMap; + typedef std::map DBResultMap; LuaScriptInterface* interface; @@ -216,7 +209,6 @@ class LuaScriptInterface const std::string& getFileById(int32_t scriptId); int32_t getEvent(const std::string& eventName); - int32_t getEvent(); int32_t getMetaEvent(const std::string& globalName, const std::string& eventName); static ScriptEnvironment* getScriptEnv() { @@ -279,13 +271,13 @@ class LuaScriptInterface // Get template - static typename std::enable_if::value, T>::type + inline static typename std::enable_if::value, T>::type getNumber(lua_State* L, int32_t arg) { return static_cast(static_cast(lua_tonumber(L, arg))); } template - static typename std::enable_if::value || std::is_floating_point::value, T>::type + inline static typename std::enable_if::value || std::is_floating_point::value, T>::type getNumber(lua_State* L, int32_t arg) { return static_cast(lua_tonumber(L, arg)); @@ -309,16 +301,16 @@ class LuaScriptInterface return *userdata; } template - static T** getRawUserdata(lua_State* L, int32_t arg) + inline static T** getRawUserdata(lua_State* L, int32_t arg) { return static_cast(lua_touserdata(L, arg)); } - static bool getBoolean(lua_State* L, int32_t arg) + inline static bool getBoolean(lua_State* L, int32_t arg) { return lua_toboolean(L, arg) != 0; } - static bool getBoolean(lua_State* L, int32_t arg, bool defaultValue) + inline static bool getBoolean(lua_State* L, int32_t arg, bool defaultValue) { const auto parameters = lua_gettop(L); if (parameters == 0 || arg > parameters) { @@ -328,11 +320,11 @@ class LuaScriptInterface } static std::string getString(lua_State* L, int32_t arg); + static CombatDamage getCombatDamage(lua_State* L); static Position getPosition(lua_State* L, int32_t arg, int32_t& stackpos); static Position getPosition(lua_State* L, int32_t arg); static Outfit_t getOutfit(lua_State* L, int32_t arg); static LuaVariant getVariant(lua_State* L, int32_t arg); - static InstantSpell* getInstantSpell(lua_State* L, int32_t arg); static Thing* getThing(lua_State* L, int32_t arg); static Creature* getCreature(lua_State* L, int32_t arg); @@ -350,27 +342,27 @@ class LuaScriptInterface static LuaDataType getUserdataType(lua_State* L, int32_t arg); // Is - static bool isNumber(lua_State* L, int32_t arg) + inline static bool isNumber(lua_State* L, int32_t arg) { return lua_type(L, arg) == LUA_TNUMBER; } - static bool isString(lua_State* L, int32_t arg) + inline static bool isString(lua_State* L, int32_t arg) { return lua_isstring(L, arg) != 0; } - static bool isBoolean(lua_State* L, int32_t arg) + inline static bool isBoolean(lua_State* L, int32_t arg) { return lua_isboolean(L, arg); } - static bool isTable(lua_State* L, int32_t arg) + inline static bool isTable(lua_State* L, int32_t arg) { return lua_istable(L, arg); } - static bool isFunction(lua_State* L, int32_t arg) + inline static bool isFunction(lua_State* L, int32_t arg) { return lua_isfunction(L, arg); } - static bool isUserdata(lua_State* L, int32_t arg) + inline static bool isUserdata(lua_State* L, int32_t arg) { return lua_isuserdata(L, arg) != 0; } @@ -378,19 +370,17 @@ class LuaScriptInterface // Push static void pushBoolean(lua_State* L, bool value); static void pushCombatDamage(lua_State* L, const CombatDamage& damage); - static void pushInstantSpell(lua_State* L, const InstantSpell& spell); static void pushPosition(lua_State* L, const Position& position, int32_t stackpos = 0); static void pushOutfit(lua_State* L, const Outfit_t& outfit); - static void pushLoot(lua_State* L, const std::vector& lootList); // - static void setField(lua_State* L, const char* index, lua_Number value) + inline static void setField(lua_State* L, const char* index, lua_Number value) { lua_pushnumber(L, value); lua_setfield(L, -2, index); } - static void setField(lua_State* L, const char* index, const std::string& value) + inline static void setField(lua_State* L, const char* index, const std::string& value) { pushString(L, value); lua_setfield(L, -2, index); @@ -412,21 +402,9 @@ class LuaScriptInterface void registerFunctions(); - void registerMethod(const std::string& globalName, const std::string& methodName, lua_CFunction func); - - static std::string getErrorDesc(ErrorCode_t code); - - lua_State* luaState = nullptr; - - int32_t eventTableRef = -1; - int32_t runningEventId = EVENT_ID_USER; - - //script file cache - std::map cacheFiles; - - private: void registerClass(const std::string& className, const std::string& baseClass, lua_CFunction newFunction = nullptr); void registerTable(const std::string& tableName); + void registerMethod(const std::string& className, const std::string& methodName, lua_CFunction func); void registerMetaMethod(const std::string& className, const std::string& methodName, lua_CFunction func); void registerGlobalMethod(const std::string& functionName, lua_CFunction func); void registerVariable(const std::string& tableName, const std::string& name, lua_Number value); @@ -435,16 +413,28 @@ class LuaScriptInterface std::string getStackTrace(const std::string& error_desc); + static std::string getErrorDesc(ErrorCode_t code); static bool getArea(lua_State* L, std::list& list, uint32_t& rows); //lua functions + static int luaDoCreateItem(lua_State* L); + static int luaDoCreateItemEx(lua_State* L); + static int luaDoMoveCreature(lua_State* L); + static int luaDoPlayerAddItem(lua_State* L); + static int luaDoTileAddItemEx(lua_State* L); static int luaDoSetCreatureLight(lua_State* L); //get item info static int luaGetDepotId(lua_State* L); - //get world info + //get creature info functions + static int luaGetPlayerFlagValue(lua_State* L); + static int luaGetCreatureCondition(lua_State* L); + + static int luaGetPlayerInstantSpellInfo(lua_State* L); + static int luaGetPlayerInstantSpellCount(lua_State* L); + static int luaGetWorldTime(lua_State* L); static int luaGetWorldLight(lua_State* L); static int luaGetWorldUpTime(lua_State* L); @@ -475,7 +465,12 @@ class LuaScriptInterface static int luaDoChallengeCreature(lua_State* L); + static int luaSetCreatureOutfit(lua_State* L); + static int luaSetMonsterOutfit(lua_State* L); + static int luaSetItemOutfit(lua_State* L); + static int luaDebugPrint(lua_State* L); + static int luaIsInArray(lua_State* L); static int luaAddEvent(lua_State* L); static int luaStopEvent(lua_State* L); @@ -486,9 +481,6 @@ class LuaScriptInterface static int luaGetWaypointPositionByName(lua_State* L); - static int luaSendChannelMessage(lua_State* L); - static int luaSendGuildChannelMessage(lua_State* L); - #ifndef LUAJIT_VERSION static int luaBitNot(lua_State* L); static int luaBitAnd(lua_State* L); @@ -556,12 +548,9 @@ class LuaScriptInterface static int luaGameCreateMonster(lua_State* L); static int luaGameCreateNpc(lua_State* L); static int luaGameCreateTile(lua_State* L); - static int luaGameCreateMonsterType(lua_State* L); static int luaGameStartRaid(lua_State* L); - static int luaGameGetClientVersion(lua_State* L); - static int luaGameReload(lua_State* L); // Variant @@ -582,6 +571,7 @@ class LuaScriptInterface static int luaPositionSendMagicEffect(lua_State* L); static int luaPositionSendDistanceEffect(lua_State* L); + static int luaPositionSendMonsterSay(lua_State* L); // Tile static int luaTileCreate(lua_State* L); @@ -620,8 +610,6 @@ class LuaScriptInterface static int luaTileGetThingIndex(lua_State* L); static int luaTileQueryAdd(lua_State* L); - static int luaTileAddItem(lua_State* L); - static int luaTileAddItemEx(lua_State* L); static int luaTileGetHouse(lua_State* L); @@ -650,34 +638,6 @@ class LuaScriptInterface static int luaNetworkMessageSkipBytes(lua_State* L); static int luaNetworkMessageSendToPlayer(lua_State* L); - // ModalWindow - static int luaModalWindowCreate(lua_State* L); - static int luaModalWindowDelete(lua_State* L); - - static int luaModalWindowGetId(lua_State* L); - static int luaModalWindowGetTitle(lua_State* L); - static int luaModalWindowGetMessage(lua_State* L); - - static int luaModalWindowSetTitle(lua_State* L); - static int luaModalWindowSetMessage(lua_State* L); - - static int luaModalWindowGetButtonCount(lua_State* L); - static int luaModalWindowGetChoiceCount(lua_State* L); - - static int luaModalWindowAddButton(lua_State* L); - static int luaModalWindowAddChoice(lua_State* L); - - static int luaModalWindowGetDefaultEnterButton(lua_State* L); - static int luaModalWindowSetDefaultEnterButton(lua_State* L); - - static int luaModalWindowGetDefaultEscapeButton(lua_State* L); - static int luaModalWindowSetDefaultEscapeButton(lua_State* L); - - static int luaModalWindowHasPriority(lua_State* L); - static int luaModalWindowSetPriority(lua_State* L); - - static int luaModalWindowSendToPlayer(lua_State* L); - // Item static int luaItemCreate(lua_State* L); @@ -692,9 +652,11 @@ class LuaScriptInterface static int luaItemSplit(lua_State* L); static int luaItemRemove(lua_State* L); - static int luaItemGetUniqueId(lua_State* L); + static int luaItemGetMovementId(lua_State* L); + static int luaItemSetMovementId(lua_State* L); static int luaItemGetActionId(lua_State* L); static int luaItemSetActionId(lua_State* L); + static int luaItemGetUniqueId(lua_State* L); static int luaItemGetCount(lua_State* L); static int luaItemGetCharges(lua_State* L); @@ -714,9 +676,6 @@ class LuaScriptInterface static int luaItemGetAttribute(lua_State* L); static int luaItemSetAttribute(lua_State* L); static int luaItemRemoveAttribute(lua_State* L); - static int luaItemGetCustomAttribute(lua_State* L); - static int luaItemSetCustomAttribute(lua_State* L); - static int luaItemRemoveCustomAttribute(lua_State* L); static int luaItemMoveTo(lua_State* L); static int luaItemTransform(lua_State* L); @@ -725,7 +684,6 @@ class LuaScriptInterface static int luaItemGetDescription(lua_State* L); static int luaItemHasProperty(lua_State* L); - static int luaItemIsLoadedFromMap(lua_State* L); // Container static int luaContainerCreate(lua_State* L); @@ -733,7 +691,7 @@ class LuaScriptInterface static int luaContainerGetSize(lua_State* L); static int luaContainerGetCapacity(lua_State* L); static int luaContainerGetEmptySlots(lua_State* L); - static int luaContainerGetContentDescription(lua_State* L); + static int luaContainerGetItemHoldingCount(lua_State* L); static int luaContainerGetItemCountById(lua_State* L); @@ -741,8 +699,7 @@ class LuaScriptInterface static int luaContainerHasItem(lua_State* L); static int luaContainerAddItem(lua_State* L); static int luaContainerAddItemEx(lua_State* L); - static int luaContainerGetCorpseOwner(lua_State* L); - + // Teleport static int luaTeleportCreate(lua_State* L); @@ -759,8 +716,6 @@ class LuaScriptInterface static int luaCreatureIsRemoved(lua_State* L); static int luaCreatureIsCreature(lua_State* L); static int luaCreatureIsInGhostMode(lua_State* L); - static int luaCreatureIsHealthHidden(lua_State* L); - static int luaCreatureIsImmune(lua_State* L); static int luaCreatureCanSee(lua_State* L); static int luaCreatureCanSeeCreature(lua_State* L); @@ -787,7 +742,6 @@ class LuaScriptInterface static int luaCreatureChangeSpeed(lua_State* L); static int luaCreatureSetDropLoot(lua_State* L); - static int luaCreatureSetSkillLoss(lua_State* L); static int luaCreatureGetPosition(lua_State* L); static int luaCreatureGetTile(lua_State* L); @@ -795,7 +749,6 @@ class LuaScriptInterface static int luaCreatureSetDirection(lua_State* L); static int luaCreatureGetHealth(lua_State* L); - static int luaCreatureSetHealth(lua_State* L); static int luaCreatureAddHealth(lua_State* L); static int luaCreatureGetMaxHealth(lua_State* L); static int luaCreatureSetMaxHealth(lua_State* L); @@ -810,7 +763,6 @@ class LuaScriptInterface static int luaCreatureGetCondition(lua_State* L); static int luaCreatureAddCondition(lua_State* L); static int luaCreatureRemoveCondition(lua_State* L); - static int luaCreatureHasCondition(lua_State* L); static int luaCreatureRemove(lua_State* L); static int luaCreatureTeleportTo(lua_State* L); @@ -823,9 +775,6 @@ class LuaScriptInterface static int luaCreatureGetDescription(lua_State* L); static int luaCreatureGetPathTo(lua_State* L); - static int luaCreatureMove(lua_State* L); - - static int luaCreatureGetZone(lua_State* L); // Player static int luaPlayerCreate(lua_State* L); @@ -837,6 +786,7 @@ class LuaScriptInterface static int luaPlayerGetAccountId(lua_State* L); static int luaPlayerGetLastLoginSaved(lua_State* L); static int luaPlayerGetLastLogout(lua_State* L); + static int luaPlayerHasFlag(lua_State* L); static int luaPlayerGetAccountType(lua_State* L); static int luaPlayerSetAccountType(lua_State* L); @@ -847,10 +797,10 @@ class LuaScriptInterface static int luaPlayerGetFreeCapacity(lua_State* L); static int luaPlayerGetDepotChest(lua_State* L); - static int luaPlayerGetInbox(lua_State* L); - static int luaPlayerGetSkullTime(lua_State* L); - static int luaPlayerSetSkullTime(lua_State* L); + static int luaPlayerGetMurderTimestamps(lua_State* L); + static int luaPlayerGetPlayerKillerEnd(lua_State* L); + static int luaPlayerSetPlayerKillerEnd(lua_State* L); static int luaPlayerGetDeathPenalty(lua_State* L); static int luaPlayerGetExperience(lua_State* L); @@ -875,17 +825,6 @@ class LuaScriptInterface static int luaPlayerGetSkillPercent(lua_State* L); static int luaPlayerGetSkillTries(lua_State* L); static int luaPlayerAddSkillTries(lua_State* L); - static int luaPlayerGetSpecialSkill(lua_State* L); - static int luaPlayerAddSpecialSkill(lua_State* L); - - static int luaPlayerAddOfflineTrainingTime(lua_State* L); - static int luaPlayerGetOfflineTrainingTime(lua_State* L); - static int luaPlayerRemoveOfflineTrainingTime(lua_State* L); - - static int luaPlayerAddOfflineTrainingTries(lua_State* L); - - static int luaPlayerGetOfflineTrainingSkill(lua_State* L); - static int luaPlayerSetOfflineTrainingSkill(lua_State* L); static int luaPlayerGetItemCount(lua_State* L); static int luaPlayerGetItemById(lua_State* L); @@ -935,7 +874,6 @@ class LuaScriptInterface static int luaPlayerShowTextDialog(lua_State* L); static int luaPlayerSendTextMessage(lua_State* L); - static int luaPlayerSendChannelMessage(lua_State* L); static int luaPlayerSendPrivateMessage(lua_State* L); static int luaPlayerChannelSay(lua_State* L); @@ -952,10 +890,6 @@ class LuaScriptInterface static int luaPlayerHasOutfit(lua_State* L); static int luaPlayerSendOutfitWindow(lua_State* L); - static int luaPlayerAddMount(lua_State* L); - static int luaPlayerRemoveMount(lua_State* L); - static int luaPlayerHasMount(lua_State* L); - static int luaPlayerGetPremiumDays(lua_State* L); static int luaPlayerAddPremiumDays(lua_State* L); static int luaPlayerRemovePremiumDays(lua_State* L); @@ -969,19 +903,12 @@ class LuaScriptInterface static int luaPlayerForgetSpell(lua_State* L); static int luaPlayerHasLearnedSpell(lua_State* L); - static int luaPlayerSendTutorial(lua_State* L); - static int luaPlayerAddMapMark(lua_State* L); - static int luaPlayerSave(lua_State* L); - static int luaPlayerPopupFYI(lua_State* L); static int luaPlayerIsPzLocked(lua_State* L); static int luaPlayerGetClient(lua_State* L); - static int luaPlayerGetHouse(lua_State* L); - static int luaPlayerSendHouseWindow(lua_State* L); - static int luaPlayerSetEditHouse(lua_State* L); static int luaPlayerSetGhostMode(lua_State* L); @@ -989,12 +916,7 @@ class LuaScriptInterface static int luaPlayerGetContainerById(lua_State* L); static int luaPlayerGetContainerIndex(lua_State* L); - static int luaPlayerGetInstantSpells(lua_State* L); - static int luaPlayerCanCast(lua_State* L); - - static int luaPlayerHasChaseMode(lua_State* L); - static int luaPlayerHasSecureMode(lua_State* L); - static int luaPlayerGetFightMode(lua_State* L); + static int luaPlayerGetTotalDamage(lua_State* L); // Monster static int luaMonsterCreate(lua_State* L); @@ -1033,9 +955,6 @@ class LuaScriptInterface static int luaNpcSetMasterPos(lua_State* L); - static int luaNpcGetSpeechBubble(lua_State* L); - static int luaNpcSetSpeechBubble(lua_State* L); - // Guild static int luaGuildCreate(lua_State* L); @@ -1047,9 +966,6 @@ class LuaScriptInterface static int luaGuildGetRankById(lua_State* L); static int luaGuildGetRankByLevel(lua_State* L); - static int luaGuildGetMotd(lua_State* L); - static int luaGuildSetMotd(lua_State* L); - // Group static int luaGroupCreate(lua_State* L); @@ -1059,13 +975,11 @@ class LuaScriptInterface static int luaGroupGetAccess(lua_State* L); static int luaGroupGetMaxDepotItems(lua_State* L); static int luaGroupGetMaxVipEntries(lua_State* L); - static int luaGroupHasFlag(lua_State* L); // Vocation static int luaVocationCreate(lua_State* L); static int luaVocationGetId(lua_State* L); - static int luaVocationGetClientId(lua_State* L); static int luaVocationGetName(lua_State* L); static int luaVocationGetDescription(lua_State* L); @@ -1116,68 +1030,59 @@ class LuaScriptInterface static int luaHouseGetDoors(lua_State* L); static int luaHouseGetDoorCount(lua_State* L); - static int luaHouseGetDoorIdByPosition(lua_State* L); static int luaHouseGetTiles(lua_State* L); - static int luaHouseGetItems(lua_State* L); static int luaHouseGetTileCount(lua_State* L); - static int luaHouseCanEditAccessList(lua_State* L); static int luaHouseGetAccessList(lua_State* L); static int luaHouseSetAccessList(lua_State* L); - static int luaHouseKickPlayer(lua_State* L); - // ItemType static int luaItemTypeCreate(lua_State* L); static int luaItemTypeIsCorpse(lua_State* L); static int luaItemTypeIsDoor(lua_State* L); static int luaItemTypeIsContainer(lua_State* L); + static int luaItemTypeIsChest(lua_State* L); static int luaItemTypeIsFluidContainer(lua_State* L); static int luaItemTypeIsMovable(lua_State* L); static int luaItemTypeIsRune(lua_State* L); static int luaItemTypeIsStackable(lua_State* L); static int luaItemTypeIsReadable(lua_State* L); static int luaItemTypeIsWritable(lua_State* L); - static int luaItemTypeIsBlocking(lua_State* L); - static int luaItemTypeIsGroundTile(lua_State* L); static int luaItemTypeIsMagicField(lua_State* L); - static int luaItemTypeIsUseable(lua_State* L); - static int luaItemTypeIsPickupable(lua_State* L); + static int luaItemTypeIsSplash(lua_State* L); + static int luaItemTypeIsKey(lua_State* L); + static int luaItemTypeIsDisguised(lua_State* L); + static int luaItemTypeIsDestroyable(lua_State* L); + static int luaItemTypeIsGroundTile(lua_State* L); static int luaItemTypeGetType(lua_State* L); static int luaItemTypeGetId(lua_State* L); - static int luaItemTypeGetClientId(lua_State* L); + static int luaItemTypeGetDisguiseId(lua_State* L); static int luaItemTypeGetName(lua_State* L); static int luaItemTypeGetPluralName(lua_State* L); static int luaItemTypeGetArticle(lua_State* L); static int luaItemTypeGetDescription(lua_State* L); static int luaItemTypeGetSlotPosition(lua_State *L); + static int luaItemTypeGetDestroyTarget(lua_State* L); static int luaItemTypeGetCharges(lua_State* L); static int luaItemTypeGetFluidSource(lua_State* L); static int luaItemTypeGetCapacity(lua_State* L); static int luaItemTypeGetWeight(lua_State* L); - static int luaItemTypeGetHitChance(lua_State* L); static int luaItemTypeGetShootRange(lua_State* L); static int luaItemTypeGetAttack(lua_State* L); static int luaItemTypeGetDefense(lua_State* L); - static int luaItemTypeGetExtraDefense(lua_State* L); static int luaItemTypeGetArmor(lua_State* L); static int luaItemTypeGetWeaponType(lua_State* L); - static int luaItemTypeGetElementType(lua_State* L); - static int luaItemTypeGetElementDamage(lua_State* L); - static int luaItemTypeGetTransformEquipId(lua_State* L); static int luaItemTypeGetTransformDeEquipId(lua_State* L); - static int luaItemTypeGetDestroyId(lua_State* L); static int luaItemTypeGetDecayId(lua_State* L); + static int luaItemTypeGetNutrition(lua_State* L); static int luaItemTypeGetRequiredLevel(lua_State* L); - static int luaItemTypeGetAmmoType(lua_State* L); - static int luaItemTypeGetCorpseType(lua_State* L); static int luaItemTypeHasSubType(lua_State* L); @@ -1188,8 +1093,7 @@ class LuaScriptInterface static int luaCombatSetFormula(lua_State* L); static int luaCombatSetArea(lua_State* L); - static int luaCombatAddCondition(lua_State* L); - static int luaCombatClearConditions(lua_State* L); + static int luaCombatSetCondition(lua_State* L); static int luaCombatSetCallback(lua_State* L); static int luaCombatSetOrigin(lua_State* L); @@ -1211,10 +1115,10 @@ class LuaScriptInterface static int luaConditionSetTicks(lua_State* L); static int luaConditionSetParameter(lua_State* L); - static int luaConditionSetFormula(lua_State* L); + static int luaConditionSetSpeedDelta(lua_State* L); static int luaConditionSetOutfit(lua_State* L); - static int luaConditionAddDamage(lua_State* L); + static int luaConditionSetTiming(lua_State* L); // MonsterType static int luaMonsterTypeCreate(lua_State* L); @@ -1225,100 +1129,47 @@ class LuaScriptInterface static int luaMonsterTypeIsIllusionable(lua_State* L); static int luaMonsterTypeIsHostile(lua_State* L); static int luaMonsterTypeIsPushable(lua_State* L); - static int luaMonsterTypeIsHealthHidden(lua_State* L); + static int luaMonsterTypeIsHealthShown(lua_State* L); static int luaMonsterTypeCanPushItems(lua_State* L); static int luaMonsterTypeCanPushCreatures(lua_State* L); - static int luaMonsterTypeName(lua_State* L); - static int luaMonsterTypeNameDescription(lua_State* L); + static int luaMonsterTypeGetName(lua_State* L); + static int luaMonsterTypeGetNameDescription(lua_State* L); - static int luaMonsterTypeHealth(lua_State* L); - static int luaMonsterTypeMaxHealth(lua_State* L); - static int luaMonsterTypeRunHealth(lua_State* L); - static int luaMonsterTypeExperience(lua_State* L); + static int luaMonsterTypeGetHealth(lua_State* L); + static int luaMonsterTypeGetMaxHealth(lua_State* L); + static int luaMonsterTypeGetRunHealth(lua_State* L); + static int luaMonsterTypeGetExperience(lua_State* L); - static int luaMonsterTypeCombatImmunities(lua_State* L); - static int luaMonsterTypeConditionImmunities(lua_State* L); + static int luaMonsterTypeGetCombatImmunities(lua_State* L); + static int luaMonsterTypeGetConditionImmunities(lua_State* L); static int luaMonsterTypeGetAttackList(lua_State* L); - static int luaMonsterTypeAddAttack(lua_State* L); - static int luaMonsterTypeGetDefenseList(lua_State* L); - static int luaMonsterTypeAddDefense(lua_State* L); - static int luaMonsterTypeGetElementList(lua_State* L); - static int luaMonsterTypeAddElement(lua_State* L); static int luaMonsterTypeGetVoices(lua_State* L); - static int luaMonsterTypeAddVoice(lua_State* L); - static int luaMonsterTypeGetLoot(lua_State* L); - static int luaMonsterTypeAddLoot(lua_State* L); - static int luaMonsterTypeGetCreatureEvents(lua_State* L); - static int luaMonsterTypeRegisterEvent(lua_State* L); - - static int luaMonsterTypeEventOnCallback(lua_State* L); - static int luaMonsterTypeEventType(lua_State* L); static int luaMonsterTypeGetSummonList(lua_State* L); - static int luaMonsterTypeAddSummon(lua_State* L); + static int luaMonsterTypeGetMaxSummons(lua_State* L); - static int luaMonsterTypeMaxSummons(lua_State* L); + static int luaMonsterTypeGetArmor(lua_State* L); + static int luaMonsterTypeGetDefense(lua_State* L); + static int luaMonsterTypeGetOutfit(lua_State* L); + static int luaMonsterTypeGetRace(lua_State* L); + static int luaMonsterTypeGetCorpseId(lua_State* L); + static int luaMonsterTypeGetManaCost(lua_State* L); + static int luaMonsterTypeGetBaseSpeed(lua_State* L); + static int luaMonsterTypeGetLight(lua_State* L); - static int luaMonsterTypeArmor(lua_State* L); - static int luaMonsterTypeDefense(lua_State* L); - static int luaMonsterTypeOutfit(lua_State* L); - static int luaMonsterTypeRace(lua_State* L); - static int luaMonsterTypeCorpseId(lua_State* L); - static int luaMonsterTypeManaCost(lua_State* L); - static int luaMonsterTypeBaseSpeed(lua_State* L); - static int luaMonsterTypeLight(lua_State* L); - - static int luaMonsterTypeStaticAttackChance(lua_State* L); - static int luaMonsterTypeTargetDistance(lua_State* L); - static int luaMonsterTypeYellChance(lua_State* L); - static int luaMonsterTypeYellSpeedTicks(lua_State* L); - static int luaMonsterTypeChangeTargetChance(lua_State* L); - static int luaMonsterTypeChangeTargetSpeed(lua_State* L); - - // Loot - static int luaCreateLoot(lua_State* L); - static int luaDeleteLoot(lua_State* L); - static int luaLootSetId(lua_State* L); - static int luaLootSetMaxCount(lua_State* L); - static int luaLootSetSubType(lua_State* L); - static int luaLootSetChance(lua_State* L); - static int luaLootSetActionId(lua_State* L); - static int luaLootSetDescription(lua_State* L); - static int luaLootAddChildLoot(lua_State* L); - - // MonsterSpell - static int luaCreateMonsterSpell(lua_State* L); - static int luaDeleteMonsterSpell(lua_State* L); - static int luaMonsterSpellSetType(lua_State* L); - static int luaMonsterSpellSetScriptName(lua_State* L); - static int luaMonsterSpellSetChance(lua_State* L); - static int luaMonsterSpellSetInterval(lua_State* L); - static int luaMonsterSpellSetRange(lua_State* L); - static int luaMonsterSpellSetCombatValue(lua_State* L); - static int luaMonsterSpellSetCombatType(lua_State* L); - static int luaMonsterSpellSetAttackValue(lua_State* L); - static int luaMonsterSpellSetNeedTarget(lua_State* L); - static int luaMonsterSpellSetCombatLength(lua_State* L); - static int luaMonsterSpellSetCombatSpread(lua_State* L); - static int luaMonsterSpellSetCombatRadius(lua_State* L); - static int luaMonsterSpellSetConditionType(lua_State* L); - static int luaMonsterSpellSetConditionDamage(lua_State* L); - static int luaMonsterSpellSetConditionSpeedChange(lua_State* L); - static int luaMonsterSpellSetConditionDuration(lua_State* L); - static int luaMonsterSpellSetConditionTickInterval(lua_State* L); - static int luaMonsterSpellSetCombatShootEffect(lua_State* L); - static int luaMonsterSpellSetCombatEffect(lua_State* L); + static int luaMonsterTypeGetTargetDistance(lua_State* L); + static int luaMonsterTypeGetChangeTargetChance(lua_State* L); + static int luaMonsterTypeGetChangeTargetSpeed(lua_State* L); // Party - static int luaPartyCreate(lua_State* L); static int luaPartyDisband(lua_State* L); static int luaPartyGetLeader(lua_State* L); @@ -1341,142 +1192,21 @@ class LuaScriptInterface static int luaPartyShareExperience(lua_State* L); static int luaPartySetSharedExperience(lua_State* L); - // Spells - static int luaSpellCreate(lua_State* L); - - static int luaSpellOnCastSpell(lua_State* L); - static int luaSpellRegister(lua_State* L); - static int luaSpellName(lua_State* L); - static int luaSpellId(lua_State* L); - static int luaSpellGroup(lua_State* L); - static int luaSpellCooldown(lua_State* L); - static int luaSpellGroupCooldown(lua_State* L); - static int luaSpellLevel(lua_State* L); - static int luaSpellMagicLevel(lua_State* L); - static int luaSpellMana(lua_State* L); - static int luaSpellManaPercent(lua_State* L); - static int luaSpellSoul(lua_State* L); - static int luaSpellRange(lua_State* L); - static int luaSpellPremium(lua_State* L); - static int luaSpellEnabled(lua_State* L); - static int luaSpellNeedTarget(lua_State* L); - static int luaSpellNeedWeapon(lua_State* L); - static int luaSpellNeedLearn(lua_State* L); - static int luaSpellSelfTarget(lua_State* L); - static int luaSpellBlocking(lua_State* L); - static int luaSpellAggressive(lua_State* L); - static int luaSpellVocation(lua_State* L); - - // only for InstantSpells - static int luaSpellWords(lua_State* L); - static int luaSpellNeedDirection(lua_State* L); - static int luaSpellHasParams(lua_State* L); - static int luaSpellHasPlayerNameParam(lua_State* L); - static int luaSpellNeedCasterTargetOrDirection(lua_State* L); - static int luaSpellIsBlockingWalls(lua_State* L); - - // only for RuneSpells - static int luaSpellRuneId(lua_State* L); - static int luaSpellCharges(lua_State* L); - static int luaSpellAllowFarUse(lua_State* L); - static int luaSpellBlockWalls(lua_State* L); - static int luaSpellCheckFloor(lua_State* L); - - // Actions - static int luaCreateAction(lua_State* L); - static int luaActionOnUse(lua_State* L); - static int luaActionRegister(lua_State* L); - static int luaActionItemId(lua_State* L); - static int luaActionActionId(lua_State* L); - static int luaActionUniqueId(lua_State* L); - static int luaActionAllowFarUse(lua_State* L); - static int luaActionBlockWalls(lua_State* L); - static int luaActionCheckFloor(lua_State* L); - - // Talkactions - static int luaCreateTalkaction(lua_State* L); - static int luaTalkactionOnSay(lua_State* L); - static int luaTalkactionRegister(lua_State* L); - static int luaTalkactionSeparator(lua_State* L); - - // CreatureEvents - static int luaCreateCreatureEvent(lua_State* L); - static int luaCreatureEventType(lua_State* L); - static int luaCreatureEventRegister(lua_State* L); - static int luaCreatureEventOnCallback(lua_State* L); - - // MoveEvents - static int luaCreateMoveEvent(lua_State* L); - static int luaMoveEventType(lua_State* L); - static int luaMoveEventRegister(lua_State* L); - static int luaMoveEventOnCallback(lua_State* L); - static int luaMoveEventLevel(lua_State* L); - static int luaMoveEventSlot(lua_State* L); - static int luaMoveEventMagLevel(lua_State* L); - static int luaMoveEventPremium(lua_State* L); - static int luaMoveEventVocation(lua_State* L); - static int luaMoveEventItemId(lua_State* L); - static int luaMoveEventActionId(lua_State* L); - static int luaMoveEventUniqueId(lua_State* L); - static int luaMoveEventPosition(lua_State* L); - - // GlobalEvents - static int luaCreateGlobalEvent(lua_State* L); - static int luaGlobalEventType(lua_State* L); - static int luaGlobalEventRegister(lua_State* L); - static int luaGlobalEventOnCallback(lua_State* L); - static int luaGlobalEventTime(lua_State* L); - static int luaGlobalEventInterval(lua_State* L); - - // Weapon - static int luaCreateWeapon(lua_State* L); - static int luaWeaponId(lua_State* L); - static int luaWeaponLevel(lua_State* L); - static int luaWeaponMagicLevel(lua_State* L); - static int luaWeaponMana(lua_State* L); - static int luaWeaponManaPercent(lua_State* L); - static int luaWeaponHealth(lua_State* L); - static int luaWeaponHealthPercent(lua_State* L); - static int luaWeaponSoul(lua_State* L); - static int luaWeaponPremium(lua_State* L); - static int luaWeaponBreakChance(lua_State* L); - static int luaWeaponAction(lua_State* L); - static int luaWeaponUnproperly(lua_State* L); - static int luaWeaponVocation(lua_State* L); - static int luaWeaponOnUseWeapon(lua_State* L); - static int luaWeaponRegister(lua_State* L); - static int luaWeaponElement(lua_State* L); - static int luaWeaponAttack(lua_State* L); - static int luaWeaponDefense(lua_State* L); - static int luaWeaponRange(lua_State* L); - static int luaWeaponCharges(lua_State* L); - static int luaWeaponDuration(lua_State* L); - static int luaWeaponDecayTo(lua_State* L); - static int luaWeaponTransformEquipTo(lua_State* L); - static int luaWeaponTransformDeEquipTo(lua_State* L); - static int luaWeaponSlotType(lua_State* L); - static int luaWeaponHitChance(lua_State* L); - static int luaWeaponExtraElement(lua_State* L); - - // exclusively for distance weapons - static int luaWeaponMaxHitChance(lua_State* L); - static int luaWeaponAmmoType(lua_State* L); - - // exclusively for wands - static int luaWeaponWandDamage(lua_State* L); - - // exclusively for wands & distance weapons - static int luaWeaponShootType(lua_State* L); - // + lua_State* luaState = nullptr; std::string lastLuaError; std::string interfaceName; + int32_t eventTableRef = -1; static ScriptEnvironment scriptEnv[16]; static int32_t scriptEnvIndex; + int32_t runningEventId = EVENT_ID_USER; std::string loadingFile; + + //script file cache + std::map cacheFiles; }; class LuaEnvironment : public LuaScriptInterface @@ -1489,9 +1219,9 @@ class LuaEnvironment : public LuaScriptInterface LuaEnvironment(const LuaEnvironment&) = delete; LuaEnvironment& operator=(const LuaEnvironment&) = delete; - bool initState() override; + bool initState(); bool reInitState(); - bool closeState() override; + bool closeState(); LuaScriptInterface* getTestInterface(); diff --git a/src/mailbox.cpp b/src/mailbox.cpp index 98423da..31cd67f 100644 --- a/src/mailbox.cpp +++ b/src/mailbox.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,7 +21,9 @@ #include "mailbox.h" #include "game.h" +#include "player.h" #include "iologindata.h" +#include "town.h" extern Game g_game; @@ -91,22 +93,30 @@ void Mailbox::postRemoveNotification(Thing* thing, const Cylinder* newParent, in bool Mailbox::sendItem(Item* item) const { std::string receiver; - if (!getReceiver(item, receiver)) { + std::string townName; + if (!getDestination(item, receiver, townName)) { return false; } - /**No need to continue if its still empty**/ - if (receiver.empty()) { + if (receiver.empty() || townName.empty()) { + return false; + } + + Town* town = g_game.map.towns.getTown(townName); + if (!town) { return false; } Player* player = g_game.getPlayerByName(receiver); if (player) { - if (g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, - item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { - g_game.transformItem(item, item->getID() + 1); - player->onReceiveMail(); - return true; + DepotLocker* depotLocker = player->getDepotLocker(town->getID(), true); + if (depotLocker) { + if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER, + item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { + g_game.transformItem(item, item->getID() + 1); + player->onReceiveMail(town->getID()); + return true; + } } } else { Player tmpPlayer(nullptr); @@ -114,25 +124,30 @@ bool Mailbox::sendItem(Item* item) const return false; } - if (g_game.internalMoveItem(item->getParent(), tmpPlayer.getInbox(), INDEX_WHEREEVER, - item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { - g_game.transformItem(item, item->getID() + 1); - IOLoginData::savePlayer(&tmpPlayer); - return true; + DepotLocker* depotLocker = tmpPlayer.getDepotLocker(town->getID(), true); + if (depotLocker) { + if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER, + item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { + g_game.transformItem(item, item->getID() + 1); + IOLoginData::savePlayer(&tmpPlayer); + return true; + } } } + return false; } -bool Mailbox::getReceiver(Item* item, std::string& name) const +bool Mailbox::getDestination(Item* item, std::string& name, std::string& town) const { const Container* container = item->getContainer(); if (container) { for (Item* containerItem : container->getItemList()) { - if (containerItem->getID() == ITEM_LABEL && getReceiver(containerItem, name)) { + if (containerItem->getID() == ITEM_LABEL && getDestination(containerItem, name, town)) { return true; } } + return false; } @@ -141,8 +156,24 @@ bool Mailbox::getReceiver(Item* item, std::string& name) const return false; } - name = getFirstLine(text); + std::istringstream iss(text, std::istringstream::in); + std::string temp; + uint32_t currentLine = 1; + + while (getline(iss, temp, '\n')) { + if (currentLine == 1) { + name = temp; + } else if (currentLine == 2) { + town = temp; + } else { + break; + } + + ++currentLine; + } + trimString(name); + trimString(town); return true; } diff --git a/src/mailbox.h b/src/mailbox.h index 400b6c6..084dc26 100644 --- a/src/mailbox.h +++ b/src/mailbox.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,35 +29,35 @@ class Mailbox final : public Item, public Cylinder public: explicit Mailbox(uint16_t itemId) : Item(itemId) {} - Mailbox* getMailbox() override { + Mailbox* getMailbox() final { return this; } - const Mailbox* getMailbox() const override { + const Mailbox* getMailbox() const final { return this; } //cylinder implementations ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor = nullptr) const override; + uint32_t flags, Creature* actor = nullptr) const final; ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, - uint32_t& maxQueryCount, uint32_t flags) const override; - ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override; + uint32_t& maxQueryCount, uint32_t flags) const final; + ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, - uint32_t& flags) override; + uint32_t& flags) final; - void addThing(Thing* thing) override; - void addThing(int32_t index, Thing* thing) override; + void addThing(Thing* thing) final; + void addThing(int32_t index, Thing* thing) final; - void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override; - void replaceThing(uint32_t index, Thing* thing) override; + void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; + void replaceThing(uint32_t index, Thing* thing) final; - void removeThing(Thing* thing, uint32_t count) override; + void removeThing(Thing* thing, uint32_t count) final; - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; + void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; + void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; private: - bool getReceiver(Item* item, std::string& name) const; + bool getDestination(Item* item, std::string& name, std::string& town) const; bool sendItem(Item* item) const; static bool canSend(const Item* item); diff --git a/src/map.cpp b/src/map.cpp index 539e937..94bdeef 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,6 +23,7 @@ #include "iomapserialize.h" #include "combat.h" #include "creature.h" +#include "monster.h" #include "game.h" extern Game g_game; @@ -35,6 +36,7 @@ bool Map::loadMap(const std::string& identifier, bool loadHouses) return false; } + Npcs::loadNpcs(); if (!IOMap::loadSpawns(this)) { std::cout << "[Warning - Map::loadMap] Failed to load spawn data." << std::endl; } @@ -161,7 +163,14 @@ bool Map::placeCreature(const Position& centerPos, Creature* creature, bool exte Tile* tile = getTile(centerPos.x, centerPos.y, centerPos.z); if (tile) { placeInPZ = tile->hasFlag(TILESTATE_PROTECTIONZONE); - ReturnValue ret = tile->queryAdd(0, *creature, 1, FLAG_IGNOREBLOCKITEM); + + ReturnValue ret; + if (creature->getPlayer()) { + ret = tile->queryAdd(0, *creature, 1, 0); + } else { + ret = tile->queryAdd(0, *creature, 1, (creature->getMonster() ? FLAG_PLACECHECK : FLAG_IGNOREBLOCKITEM)); + } + foundTile = forceLogin || ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_PLAYERISNOTINVITED; } else { placeInPZ = false; @@ -200,7 +209,7 @@ bool Map::placeCreature(const Position& centerPos, Creature* creature, bool exte continue; } - if (tile->queryAdd(0, *creature, 1, 0) == RETURNVALUE_NOERROR) { + if (tile->queryAdd(0, *creature, 1, (creature->getMonster() ? FLAG_PLACECHECK : 0)) == RETURNVALUE_NOERROR) { if (!extendedPos || isSightClear(centerPos, tryPos, false)) { foundTile = true; break; @@ -234,13 +243,12 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* = bool teleport = forceTeleport || !newTile.getGround() || !Position::areInRange<1, 1, 0>(oldPos, newPos); - SpectatorVec spectators, newPosSpectators; - getSpectators(spectators, oldPos, true); - getSpectators(newPosSpectators, newPos, true); - spectators.addSpectators(newPosSpectators); + SpectatorVec list; + getSpectators(list, oldPos, true); + getSpectators(list, newPos, true); std::vector oldStackPosVector; - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { if (tmpPlayer->canSeeCreature(&creature)) { oldStackPosVector.push_back(oldTile.getClientIndexOfCreature(tmpPlayer, &creature)); @@ -281,7 +289,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* = //send to client size_t i = 0; - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { //Use the correct stackpos int32_t stackpos = oldStackPosVector[i++]; @@ -292,7 +300,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* = } //event method - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onCreatureMove(&creature, &newTile, newPos, &oldTile, oldPos, teleport); } @@ -300,7 +308,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* = newTile.postAddNotification(&creature, &oldTile, 0); } -void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& centerPos, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const +void Map::getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const { int_fast16_t min_y = centerPos.y + minRangeY; int_fast16_t min_x = centerPos.x + minRangeX; @@ -340,7 +348,7 @@ void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& center continue; } - spectators.emplace_back(creature); + list.insert(creature); } leafE = leafE->leafE; } else { @@ -356,7 +364,7 @@ void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& center } } -void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, bool multifloor /*= false*/, bool onlyPlayers /*= false*/, int32_t minRangeX /*= 0*/, int32_t maxRangeX /*= 0*/, int32_t minRangeY /*= 0*/, int32_t maxRangeY /*= 0*/) +void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool multifloor /*= false*/, bool onlyPlayers /*= false*/, int32_t minRangeX /*= 0*/, int32_t maxRangeX /*= 0*/, int32_t minRangeY /*= 0*/, int32_t maxRangeY /*= 0*/) { if (centerPos.z >= MAP_MAX_LAYERS) { return; @@ -374,11 +382,11 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo if (onlyPlayers) { auto it = playersSpectatorCache.find(centerPos); if (it != playersSpectatorCache.end()) { - if (!spectators.empty()) { - const SpectatorVec& cachedSpectators = it->second; - spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end()); + if (!list.empty()) { + const SpectatorVec& cachedList = it->second; + list.insert(cachedList.begin(), cachedList.end()); } else { - spectators = it->second; + list = it->second; } foundCache = true; @@ -389,17 +397,17 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo auto it = spectatorCache.find(centerPos); if (it != spectatorCache.end()) { if (!onlyPlayers) { - if (!spectators.empty()) { - const SpectatorVec& cachedSpectators = it->second; - spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end()); + if (!list.empty()) { + const SpectatorVec& cachedList = it->second; + list.insert(cachedList.begin(), cachedList.end()); } else { - spectators = it->second; + list = it->second; } } else { - const SpectatorVec& cachedSpectators = it->second; - for (Creature* spectator : cachedSpectators) { + const SpectatorVec& cachedList = it->second; + for (Creature* spectator : cachedList) { if (spectator->getPlayer()) { - spectators.emplace_back(spectator); + list.insert(spectator); } } } @@ -437,13 +445,13 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo maxRangeZ = centerPos.z; } - getSpectatorsInternal(spectators, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers); + getSpectatorsInternal(list, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers); if (cacheResult) { if (onlyPlayers) { - playersSpectatorCache[centerPos] = spectators; + playersSpectatorCache[centerPos] = list; } else { - spectatorCache[centerPos] = spectators; + spectatorCache[centerPos] = list; } } } @@ -452,10 +460,6 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo void Map::clearSpectatorCache() { spectatorCache.clear(); -} - -void Map::clearPlayersSpectatorCache() -{ playersSpectatorCache.clear(); } @@ -559,7 +563,7 @@ const Tile* Map::canWalkTo(const Creature& creature, const Position& pos) const //used for non-cached tiles Tile* tile = getTile(pos.x, pos.y, pos.z); if (creature.getTile() != tile) { - if (!tile || tile->queryAdd(0, creature, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) != RETURNVALUE_NOERROR) { + if (!tile || tile->queryAdd(0, creature, 1, FLAG_PATHFINDING) != RETURNVALUE_NOERROR) { return nullptr; } } @@ -842,16 +846,25 @@ int_fast32_t AStarNodes::getTileWalkCost(const Creature& creature, const Tile* t { int_fast32_t cost = 0; if (tile->getTopVisibleCreature(&creature) != nullptr) { + if (const Monster* monster = creature.getMonster()) { + if (monster->canPushCreatures()) { + return cost; + } + } + //destroy creature cost cost += MAP_NORMALWALKCOST * 3; } if (const MagicField* field = tile->getFieldItem()) { CombatType_t combatType = field->getCombatType(); - if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) { - cost += MAP_NORMALWALKCOST * 18; + if (combatType != COMBAT_NONE) { + if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) { + cost += MAP_NORMALWALKCOST * 18; + } } } + return cost; } diff --git a/src/map.h b/src/map.h index 390eb6d..3e8df8d 100644 --- a/src/map.h +++ b/src/map.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,7 +73,7 @@ class AStarNodes int_fast32_t closedNodes; }; -using SpectatorCache = std::map; +typedef std::map SpectatorCache; static constexpr int32_t FLOOR_BITS = 3; static constexpr int32_t FLOOR_SIZE = (1 << FLOOR_BITS); @@ -110,7 +110,7 @@ class QTreeNode QTreeLeafNode* getLeaf(uint32_t x, uint32_t y); template - static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y) + inline static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y) { do { node = node->child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; @@ -127,11 +127,10 @@ class QTreeNode QTreeLeafNode* createLeaf(uint32_t x, uint32_t y, uint32_t level); protected: - bool leaf = false; - - private: QTreeNode* child[4] = {}; + bool leaf = false; + friend class Map; }; @@ -153,7 +152,7 @@ class QTreeLeafNode final : public QTreeNode void addCreature(Creature* c); void removeCreature(Creature* c); - private: + protected: static bool newLeaf; QTreeLeafNode* leafS = nullptr; QTreeLeafNode* leafE = nullptr; @@ -197,7 +196,7 @@ class Map * \returns A pointer to that tile. */ Tile* getTile(uint16_t x, uint16_t y, uint8_t z) const; - Tile* getTile(const Position& pos) const { + inline Tile* getTile(const Position& pos) const { return getTile(pos.x, pos.y, pos.z); } @@ -220,12 +219,11 @@ class Map void moveCreature(Creature& creature, Tile& newTile, bool forceTeleport = false); - void getSpectators(SpectatorVec& spectators, const Position& centerPos, bool multifloor = false, bool onlyPlayers = false, + void getSpectators(SpectatorVec& list, const Position& centerPos, bool multifloor = false, bool onlyPlayers = false, int32_t minRangeX = 0, int32_t maxRangeX = 0, int32_t minRangeY = 0, int32_t maxRangeY = 0); void clearSpectatorCache(); - void clearPlayersSpectatorCache(); /** * Checks if you can throw an object to that position @@ -264,8 +262,7 @@ class Map Spawns spawns; Towns towns; Houses houses; - - private: + protected: SpectatorCache spectatorCache; SpectatorCache playersSpectatorCache; @@ -278,7 +275,7 @@ class Map uint32_t height = 0; // Actually scans the map for spectators - void getSpectatorsInternal(SpectatorVec& spectators, const Position& centerPos, + void getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const; diff --git a/src/monster.cpp b/src/monster.cpp index 58883a7..69718e4 100644 --- a/src/monster.cpp +++ b/src/monster.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,11 +22,11 @@ #include "monster.h" #include "game.h" #include "spells.h" -#include "events.h" +#include "configmanager.h" +extern ConfigManager g_config; extern Game g_game; extern Monsters g_monsters; -extern Events* g_events; int32_t Monster::despawnRange; int32_t Monster::despawnRadius; @@ -42,10 +42,10 @@ Monster* Monster::createMonster(const std::string& name) return new Monster(mType); } -Monster::Monster(MonsterType* mType) : +Monster::Monster(MonsterType* mtype) : Creature(), - strDescription(mType->nameDescription), - mType(mType) + strDescription(asLowerCaseString(mtype->nameDescription)), + mType(mtype) { defaultOutfit = mType->info.outfit; currentOutfit = mType->info.outfit; @@ -80,31 +80,45 @@ void Monster::removeList() g_game.removeMonster(this); } +int32_t Monster::getDefense() +{ + int32_t totalDefense = mType->info.defense + 1; + int32_t defenseSkill = mType->info.skill; + + fightMode_t attackMode = FIGHTMODE_BALANCED; + + if ((followCreature || !attackedCreature) && earliestAttackTime <= OTSYS_TIME()) { + attackMode = FIGHTMODE_DEFENSE; + } + + if (attackMode == FIGHTMODE_ATTACK) { + totalDefense -= 4 * totalDefense / 10; + } else if (attackMode == FIGHTMODE_DEFENSE) { + totalDefense += 8 * totalDefense / 10; + } + + if (totalDefense) { + int32_t formula = (5 * (defenseSkill) + 50) * totalDefense; + int32_t randresult = rand() % 100; + + totalDefense = formula * ((rand() % 100 + randresult) / 2) / 10000.; + } + + return totalDefense; +} + bool Monster::canSee(const Position& pos) const { return Creature::canSee(getPosition(), pos, 9, 9); } -bool Monster::canWalkOnFieldType(CombatType_t combatType) const +void Monster::onAttackedCreature(Creature* creature) { - switch (combatType) { - case COMBAT_ENERGYDAMAGE: - return mType->info.canWalkOnEnergy; - case COMBAT_FIREDAMAGE: - return mType->info.canWalkOnFire; - case COMBAT_EARTHDAMAGE: - return mType->info.canWalkOnPoison; - default: - return true; + if (isSummon() && getMaster()) { + master->onAttackedCreature(creature); } } -void Monster::onAttackedCreatureDisappear(bool) -{ - attackTicks = 0; - extraMeleeAttack = true; -} - void Monster::onCreatureAppear(Creature* creature, bool isLogin) { Creature::onCreatureAppear(creature, isLogin); @@ -238,7 +252,7 @@ void Monster::onCreatureMove(Creature* creature, const Tile* newTile, const Posi } if (canSeeNewPos && isSummon() && getMaster() == creature) { - isMasterInRange = true; //Follow master again + isMasterInRange = true; //Follow master again } updateIdleStatus(); @@ -250,7 +264,7 @@ void Monster::onCreatureMove(Creature* creature, const Tile* newTile, const Posi int32_t offset_x = Position::getDistanceX(followPosition, position); int32_t offset_y = Position::getDistanceY(followPosition, position); - if ((offset_x > 1 || offset_y > 1) && mType->info.changeTargetChance > 0) { + if ((offset_x > 1 || offset_y > 1) && mType->info.changeTargetChance > 0 && targetChangeCooldown <= 0) { Direction dir = getDirectionTo(position, followPosition); const Position& checkPosition = getNextPosition(dir, position); @@ -365,10 +379,10 @@ void Monster::updateTargetList() } } - SpectatorVec spectators; - g_game.map.getSpectators(spectators, position, true); - spectators.erase(this); - for (Creature* spectator : spectators) { + SpectatorVec list; + g_game.map.getSpectators(list, position, true); + list.erase(this); + for (Creature* spectator : list) { if (canSee(spectator->getPosition())) { onCreatureFound(spectator); } @@ -481,14 +495,14 @@ void Monster::onCreatureLeave(Creature* creature) } } -bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAULT*/) +bool Monster::searchTarget(TargetSearchType_t searchType) { std::list resultList; const Position& myPos = getPosition(); for (Creature* creature : targetList) { if (followCreature != creature && isTarget(creature)) { - if (searchType == TARGETSEARCH_RANDOM || canUseAttack(myPos, creature)) { + if (searchType == TARGETSEARCH_ANY || canUseAttack(myPos, creature)) { resultList.push_back(creature); } } @@ -497,32 +511,31 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL switch (searchType) { case TARGETSEARCH_NEAREST: { Creature* target = nullptr; + + int32_t minRange = 0; + if (attackedCreature) { + const Position& targetPosition = attackedCreature->getPosition(); + minRange = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition); + } + if (!resultList.empty()) { - auto it = resultList.begin(); - target = *it; + for (Creature* creature : resultList) { + const Position& targetPosition = creature->getPosition(); - if (++it != resultList.end()) { - const Position& targetPosition = target->getPosition(); - int32_t minRange = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition); - do { - const Position& pos = (*it)->getPosition(); - - int32_t distance = Position::getDistanceX(myPos, pos) + Position::getDistanceY(myPos, pos); - if (distance < minRange) { - target = *it; - minRange = distance; - } - } while (++it != resultList.end()); + int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition); + if (distance < minRange) { + target = creature; + minRange = distance; + } } } else { - int32_t minRange = std::numeric_limits::max(); for (Creature* creature : targetList) { if (!isTarget(creature)) { continue; } - const Position& pos = creature->getPosition(); - int32_t distance = Position::getDistanceX(myPos, pos) + Position::getDistanceY(myPos, pos); + const Position& targetPosition = creature->getPosition(); + int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition); if (distance < minRange) { target = creature; minRange = distance; @@ -535,10 +548,79 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL } break; } + case TARGETSEARCH_WEAKEST: { + Creature* target = nullptr; - case TARGETSEARCH_DEFAULT: - case TARGETSEARCH_ATTACKRANGE: - case TARGETSEARCH_RANDOM: + int32_t health = 0; + if (attackedCreature) { + health = attackedCreature->getMaxHealth(); + } + + if (!resultList.empty()) { + for (Creature* creature : resultList) { + if (creature->getMaxHealth() < health) { + target = creature; + health = creature->getMaxHealth(); + } + } + } else { + for (Creature* creature : targetList) { + if (creature->getMaxHealth() < health) { + target = creature; + health = creature->getMaxHealth(); + } + } + } + + if (target && selectTarget(target)) { + return true; + } + break; + } + case TARGETSEARCH_MOSTDAMAGE: { + Creature* target = nullptr; + + int32_t maxDamage = 0; + + if (!resultList.empty()) { + for (Creature* creature : resultList) { + auto it = damageMap.find(creature->getID()); + if (it == damageMap.end()) { + continue; + } + + int32_t damage = it->second.total; + + if (OTSYS_TIME() - it->second.ticks <= g_config.getNumber(ConfigManager::PZ_LOCKED)) { + if (damage > maxDamage) { + target = creature; + maxDamage = damage; + } + } + } + } else { + for (Creature* creature : targetList) { + auto it = damageMap.find(creature->getID()); + if (it == damageMap.end()) { + continue; + } + + int32_t damage = it->second.total; + + if (OTSYS_TIME() - it->second.ticks <= g_config.getNumber(ConfigManager::PZ_LOCKED)) { + if (damage > maxDamage) { + target = creature; + maxDamage = damage; + } + } + } + } + + if (target && selectTarget(target)) { + return true; + } + break; + } default: { if (!resultList.empty()) { auto it = resultList.begin(); @@ -546,20 +628,19 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL return selectTarget(*it); } - if (searchType == TARGETSEARCH_ATTACKRANGE) { - return false; - } - break; } } - //lets just pick the first target in the list - for (Creature* target : targetList) { - if (followCreature != target && selectTarget(target)) { - return true; + //lets just pick the first target in the list if we do not have a target + if (!attackedCreature) { + for (Creature* target : targetList) { + if (followCreature != target && selectTarget(target)) { + return true; + } } } + return false; } @@ -637,6 +718,10 @@ bool Monster::selectTarget(Creature* creature) g_dispatcher.addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game, getID()))); } } + + // without this task, monster would randomly start dancing until next game round + g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game, getID()))); + targetChangeCooldown += 3000; return setFollowCreature(creature); } @@ -673,7 +758,7 @@ void Monster::updateIdleStatus() void Monster::onAddCondition(ConditionType_t type) { - if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) { + if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON || type == CONDITION_AGGRESSIVE) { updateMapCache(); } @@ -682,8 +767,7 @@ void Monster::onAddCondition(ConditionType_t type) void Monster::onEndCondition(ConditionType_t type) { - if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) { - ignoreFieldDamage = false; + if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON || type == CONDITION_AGGRESSIVE) { updateMapCache(); } @@ -692,6 +776,10 @@ void Monster::onEndCondition(ConditionType_t type) void Monster::onThink(uint32_t interval) { + if (OTSYS_TIME() < earliestWakeUpTime) { + return; + } + Creature::onThink(interval); if (mType->info.thinkEvent != -1) { @@ -718,9 +806,10 @@ void Monster::onThink(uint32_t interval) } } - if (!isInSpawnRange(position)) { - g_game.internalTeleport(this, masterPos); - setIdle(true); + if (!isInSpawnRange(position) || (lifetime > 0 && (OTSYS_TIME() >= lifetime))) { + // Despawn creatures if they are out of their spawn zone + g_game.removeCreature(this); + g_game.addMagicEffect(getPosition(), CONST_ME_POFF); } else { updateIdleStatus(); @@ -730,7 +819,6 @@ void Monster::onThink(uint32_t interval) if (isSummon()) { if (!attackedCreature) { if (getMaster() && getMaster()->getAttackedCreature()) { - //This happens if the monster is summoned during combat selectTarget(getMaster()->getAttackedCreature()); } else if (getMaster() != followCreature) { //Our master has not ordered us to attack anything, lets follow him around instead. @@ -739,15 +827,23 @@ void Monster::onThink(uint32_t interval) } else if (attackedCreature == this) { setFollowCreature(nullptr); } else if (followCreature != attackedCreature) { - //This happens just after a master orders an attack, so lets follow it aswell. setFollowCreature(attackedCreature); } + + if (master) { + if (Monster* monster = master->getMonster()) { + if (monster->mType->info.targetDistance <= 1 && !monster->hasFollowPath) { + setFollowCreature(master); + setAttackedCreature(nullptr); + } + } + } } else if (!targetList.empty()) { if (!followCreature || !hasFollowPath) { - searchTarget(); + searchTarget(TARGETSEARCH_ANY); } else if (isFleeing()) { if (attackedCreature && !canUseAttack(getPosition(), attackedCreature)) { - searchTarget(TARGETSEARCH_ATTACKRANGE); + searchTarget(TARGETSEARCH_NEAREST); } } } @@ -759,56 +855,43 @@ void Monster::onThink(uint32_t interval) } } -void Monster::doAttacking(uint32_t interval) +void Monster::doAttacking(uint32_t) { if (!attackedCreature || (isSummon() && attackedCreature == this)) { return; } - bool updateLook = true; - bool resetTicks = interval != 0; - attackTicks += interval; - const Position& myPos = getPosition(); const Position& targetPos = attackedCreature->getPosition(); - for (const spellBlock_t& spellBlock : mType->info.attackSpells) { - bool inRange = false; - - if (attackedCreature == nullptr) { - break; + bool updateLook = false; + + if (OTSYS_TIME() >= earliestAttackTime && !isFleeing()) { + updateLook = true; + if (Combat::closeAttack(this, attackedCreature, FIGHTMODE_BALANCED)) { + egibleToDance = true; + earliestAttackTime = OTSYS_TIME() + 2000; + removeCondition(CONDITION_AGGRESSIVE, true); } - - if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) { - if (spellBlock.chance >= static_cast(uniform_random(1, 100))) { - if (updateLook) { - updateLookDirection(); - updateLook = false; - } + } + + for (spellBlock_t& spellBlock : mType->info.attackSpells) { + if (spellBlock.range != 0 && std::max(Position::getDistanceX(myPos, targetPos), Position::getDistanceY(myPos, targetPos)) <= spellBlock.range) { + if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) { + updateLookDirection(); minCombatValue = spellBlock.minCombatValue; maxCombatValue = spellBlock.maxCombatValue; + spellBlock.spell->castSpell(this, attackedCreature); - - if (spellBlock.isMelee) { - extraMeleeAttack = false; - } + egibleToDance = true; } } - - if (!inRange && spellBlock.isMelee) { - //melee swing out of reach - extraMeleeAttack = true; - } } if (updateLook) { updateLookDirection(); } - - if (resetTicks) { - attackTicks = 0; - } } bool Monster::canUseAttack(const Position& pos, const Creature* target) const @@ -826,40 +909,6 @@ bool Monster::canUseAttack(const Position& pos, const Creature* target) const return true; } -bool Monster::canUseSpell(const Position& pos, const Position& targetPos, - const spellBlock_t& sb, uint32_t interval, bool& inRange, bool& resetTicks) -{ - inRange = true; - - if (sb.isMelee && isFleeing()) { - return false; - } - - if (extraMeleeAttack) { - lastMeleeAttack = OTSYS_TIME(); - } else if (sb.isMelee && (OTSYS_TIME() - lastMeleeAttack) < 1500) { - return false; - } - - if (!sb.isMelee || !extraMeleeAttack) { - if (sb.speed > attackTicks) { - resetTicks = false; - return false; - } - - if (attackTicks % sb.speed >= interval) { - //already used this spell for this round - return false; - } - } - - if (sb.range != 0 && std::max(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)) > sb.range) { - inRange = false; - return false; - } - return true; -} - void Monster::onThinkTarget(uint32_t interval) { if (!isSummon()) { @@ -882,13 +931,42 @@ void Monster::onThinkTarget(uint32_t interval) if (targetChangeTicks >= mType->info.changeTargetSpeed) { targetChangeTicks = 0; - targetChangeCooldown = mType->info.changeTargetSpeed; - if (mType->info.changeTargetChance >= uniform_random(1, 100)) { - if (mType->info.targetDistance <= 1) { - searchTarget(TARGETSEARCH_RANDOM); - } else { - searchTarget(TARGETSEARCH_NEAREST); + if (mType->info.changeTargetChance > uniform_random(0, 99)) { + // search target strategies, if no strategy succeeds, target is not switched + int32_t random = uniform_random(0, 99); + int32_t current_strategy = 0; + + TargetSearchType_t searchType = TARGETSEARCH_ANY; + + do + { + int32_t strategy = 0; + + if (current_strategy == 0) { + strategy = mType->info.strategyNearestEnemy; + searchType = TARGETSEARCH_NEAREST; + } else if (current_strategy == 1) { + strategy = mType->info.strategyWeakestEnemy; + searchType = TARGETSEARCH_WEAKEST; + } else if (current_strategy == 2) { + strategy = mType->info.strategyMostDamageEnemy; + searchType = TARGETSEARCH_MOSTDAMAGE; + } else if (current_strategy == 3) { + strategy = mType->info.strategyRandomEnemy; + searchType = TARGETSEARCH_RANDOM; + } + + if (random < strategy) { + break; + } + + current_strategy++; + random -= strategy; + } while (current_strategy <= 3); + + if (searchType != TARGETSEARCH_ANY) { + searchTarget(searchType); } } } @@ -897,23 +975,10 @@ void Monster::onThinkTarget(uint32_t interval) } } -void Monster::onThinkDefense(uint32_t interval) +void Monster::onThinkDefense(uint32_t) { - bool resetTicks = true; - defenseTicks += interval; - for (const spellBlock_t& spellBlock : mType->info.defenseSpells) { - if (spellBlock.speed > defenseTicks) { - resetTicks = false; - continue; - } - - if (defenseTicks % spellBlock.speed >= interval) { - //already used this spell for this round - continue; - } - - if ((spellBlock.chance >= static_cast(uniform_random(1, 100)))) { + if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) { minCombatValue = spellBlock.minCombatValue; maxCombatValue = spellBlock.maxCombatValue; spellBlock.spell->castSpell(this, this); @@ -922,20 +987,10 @@ void Monster::onThinkDefense(uint32_t interval) if (!isSummon() && summons.size() < mType->info.maxSummons && hasFollowPath) { for (const summonBlock_t& summonBlock : mType->info.summons) { - if (summonBlock.speed > defenseTicks) { - resetTicks = false; - continue; - } - if (summons.size() >= mType->info.maxSummons) { continue; } - if (defenseTicks % summonBlock.speed >= interval) { - //already used this spell for this round - continue; - } - uint32_t summonCount = 0; for (Creature* summon : summons) { if (summon->getName() == summonBlock.name) { @@ -947,49 +1002,41 @@ void Monster::onThinkDefense(uint32_t interval) continue; } - if (summonBlock.chance < static_cast(uniform_random(1, 100))) { - continue; - } + if (normal_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) { + Monster* summon = Monster::createMonster(summonBlock.name); + if (summon) { + const Position& summonPos = getPosition(); - Monster* summon = Monster::createMonster(summonBlock.name); - if (summon) { - if (g_game.placeCreature(summon, getPosition(), false, summonBlock.force)) { - summon->setDropLoot(false); - summon->setSkillLoss(false); - summon->setMaster(this); - g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); - g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); - } else { - delete summon; + addSummon(summon); + + if (!g_game.placeCreature(summon, summonPos, false, summonBlock.force)) { + removeSummon(summon); + } else { + g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); + g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); + } } } } } - - if (resetTicks) { - defenseTicks = 0; - } } -void Monster::onThinkYell(uint32_t interval) +void Monster::onThinkYell(uint32_t) { - if (mType->info.yellSpeedTicks == 0) { + if (mType->info.voiceVector.empty()) { return; } - yellTicks += interval; - if (yellTicks >= mType->info.yellSpeedTicks) { - yellTicks = 0; + int32_t randomResult = rand(); + if (rand() == 50 * (randomResult / 50)) { + int32_t totalVoices = mType->info.voiceVector.size(); + const voiceBlock_t& voice = mType->info.voiceVector[rand() % totalVoices + 1]; - if (!mType->info.voiceVector.empty() && (mType->info.yellChance >= static_cast(uniform_random(1, 100)))) { - uint32_t index = uniform_random(0, mType->info.voiceVector.size() - 1); - const voiceBlock_t& vb = mType->info.voiceVector[index]; - - if (vb.yellText) { - g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, vb.text, false); - } else { - g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, vb.text, false); - } + if (voice.yellText) { + g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, voice.text, false); + } + else { + g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, voice.text, false); } } } @@ -1112,27 +1159,117 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags) bool result = false; if ((!followCreature || !hasFollowPath) && (!isSummon() || !isMasterInRange)) { - if (getTimeSinceLastMove() >= 1000) { - randomStepping = true; + if (OTSYS_TIME() >= nextDanceStepRound) { + updateLookDirection(); + nextDanceStepRound = OTSYS_TIME() + getStepDuration(); + //choose a random direction result = getRandomStep(getPosition(), direction); } } else if ((isSummon() && isMasterInRange) || followCreature) { - randomStepping = false; result = Creature::getNextStep(direction, flags); if (result) { flags |= FLAG_PATHFINDING; } else { - if (ignoreFieldDamage) { - ignoreFieldDamage = false; - updateMapCache(); - } //target dancing if (attackedCreature && attackedCreature == followCreature) { if (isFleeing()) { result = getDanceStep(getPosition(), direction, false, false); - } else if (mType->info.staticAttackChance < static_cast(uniform_random(1, 100))) { - result = getDanceStep(getPosition(), direction); + } else if (egibleToDance && OTSYS_TIME() >= earliestDanceTime) { + if (mType->info.targetDistance >= 4) { + const Position& myPos = getPosition(); + const Position targetPos = attackedCreature->getPosition(); + + if (Position::getDistanceX(myPos, targetPos) == 4 || Position::getDistanceY(myPos, targetPos) == 4) { + int32_t currentX = myPos.x; + int32_t currentY = myPos.y; + int32_t danceRandom = rand(); + int32_t danceRandomResult = danceRandom % 5; + + if (danceRandom % 5 == 1) { + direction = DIRECTION_EAST; + currentX++; + } else if (danceRandomResult <= 1) { + if (danceRandom == 5 * (danceRandom / 5)) { + direction = DIRECTION_WEST; + currentX--; + } + } else if (danceRandomResult == 2) { + direction = DIRECTION_NORTH; + currentY--; + } else if (danceRandomResult == 3) { + direction = DIRECTION_SOUTH; + currentY++; + } + + if (danceRandomResult <= 3 && canWalkTo(myPos, direction)) { + int32_t xTest = targetPos.x - currentX; + if (currentX - targetPos.x > -1) { + xTest = currentX - targetPos.x; + } + + int32_t yTest = targetPos.y - currentY; + if (currentY - targetPos.y > -1) { + yTest = currentY - targetPos.y; + } + + int32_t realTest = yTest; + + if (xTest >= yTest) { + realTest = xTest; + } + + if (realTest == 4) { + result = true; + egibleToDance = false; + earliestWakeUpTime = OTSYS_TIME() + 1000; + earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration(); + earliestAttackTime += 200; + } + } + } + } else { + const Position& myPos = getPosition(); + const Position targetPos = attackedCreature->getPosition(); + + if (Position::areInRange<1, 1>(myPos, targetPos)) { + int32_t danceRandom = rand(); + int32_t danceRandomResult = danceRandom % 5; + + int32_t currentX = myPos.x; + int32_t currentY = myPos.y; + + if (danceRandom % 5 == 1) { + direction = DIRECTION_EAST; + currentX++; + } else if (danceRandomResult <= 1) { + if (danceRandom == 5 * (danceRandom / 5)) { + direction = DIRECTION_WEST; + currentX--; + } + } else if (danceRandomResult == 2) { + direction = DIRECTION_NORTH; + currentY--; + } else if (danceRandomResult == 3) { + direction = DIRECTION_SOUTH; + currentY++; + } + + Position position = myPos; + position.x = currentX; + position.y = currentY; + + if (danceRandomResult <= 3 && + canWalkTo(myPos, direction) && + Position::areInRange<1, 1>(position, targetPos)) { + result = true; + egibleToDance = false; + earliestWakeUpTime = OTSYS_TIME() + 1000; + earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration(); + earliestAttackTime += 200; + } + } + } } } } @@ -1343,7 +1480,7 @@ bool Monster::getDistanceStep(const Position& targetPos, Direction& direction, b //escape to NW , W or N [and some extra] bool w = canWalkTo(creaturePos, DIRECTION_WEST); bool n = canWalkTo(creaturePos, DIRECTION_NORTH); - + if (w && n) { direction = boolean_random() ? DIRECTION_WEST : DIRECTION_NORTH; return true; @@ -1776,7 +1913,8 @@ void Monster::death(Creature*) for (Creature* summon : summons) { summon->changeHealth(-summon->getHealth()); - summon->removeMaster(); + summon->setMaster(nullptr); + summon->decrementReferenceCounter(); } summons.clear(); @@ -1855,61 +1993,86 @@ void Monster::updateLookDirection() //look EAST/WEST if (offsetx < 0) { newDir = DIRECTION_WEST; - } else { + } + else { newDir = DIRECTION_EAST; } - } else if (dx < dy) { + } + else if (dx < dy) { //look NORTH/SOUTH if (offsety < 0) { newDir = DIRECTION_NORTH; - } else { + } + else { newDir = DIRECTION_SOUTH; } - } else { + } + else { Direction dir = getDirection(); if (offsetx < 0 && offsety < 0) { + if (offsetx == -1 && offsety == -1) { + if (dir == DIRECTION_NORTH) { + newDir = DIRECTION_WEST; + } + } if (dir == DIRECTION_SOUTH) { newDir = DIRECTION_WEST; - } else if (dir == DIRECTION_NORTH) { - newDir = DIRECTION_WEST; - } else if (dir == DIRECTION_EAST) { + } + else if (dir == DIRECTION_EAST) { newDir = DIRECTION_NORTH; } - } else if (offsetx < 0 && offsety > 0) { + } + else if (offsetx < 0 && offsety > 0) { + if (offsetx == -1 && offsety == 1) { + if (dir == DIRECTION_SOUTH) { + newDir = DIRECTION_WEST; + } + } if (dir == DIRECTION_NORTH) { newDir = DIRECTION_WEST; - } else if (dir == DIRECTION_SOUTH) { - newDir = DIRECTION_WEST; - } else if (dir == DIRECTION_EAST) { + } + else if (dir == DIRECTION_EAST) { newDir = DIRECTION_SOUTH; } - } else if (offsetx > 0 && offsety < 0) { + } + else if (offsetx > 0 && offsety < 0) { + if (offsetx == 1 && offsety == -1) { + if (dir == DIRECTION_NORTH) { + newDir = DIRECTION_EAST; + } + } if (dir == DIRECTION_SOUTH) { newDir = DIRECTION_EAST; - } else if (dir == DIRECTION_NORTH) { - newDir = DIRECTION_EAST; - } else if (dir == DIRECTION_WEST) { + } + else if (dir == DIRECTION_WEST) { newDir = DIRECTION_NORTH; } - } else { + } + else { + if (offsetx == 1 && offsety == 1) { + if (dir == DIRECTION_SOUTH) { + newDir = DIRECTION_EAST; + } + } if (dir == DIRECTION_NORTH) { newDir = DIRECTION_EAST; - } else if (dir == DIRECTION_SOUTH) { - newDir = DIRECTION_EAST; - } else if (dir == DIRECTION_WEST) { + } + else if (dir == DIRECTION_WEST) { newDir = DIRECTION_SOUTH; } } } } - g_game.internalCreatureTurn(this, newDir); + if (direction != newDir) { + g_game.internalCreatureTurn(this, newDir); + } } void Monster::dropLoot(Container* corpse, Creature*) { if (corpse && lootDrop) { - g_events->eventMonsterOnDropLoot(this, corpse); + mType->createLoot(corpse); } } @@ -1921,12 +2084,6 @@ void Monster::setNormalCreatureLight() void Monster::drainHealth(Creature* attacker, int32_t damage) { Creature::drainHealth(attacker, damage); - - if (damage > 0 && randomStepping) { - ignoreFieldDamage = true; - updateMapCache(); - } - if (isInvisible()) { removeCondition(CONDITION_INVISIBLE); } @@ -1947,12 +2104,70 @@ bool Monster::challengeCreature(Creature* creature) bool result = selectTarget(creature); if (result) { - targetChangeCooldown = 8000; + targetChangeCooldown = 1000; targetChangeTicks = 0; } return result; } +bool Monster::convinceCreature(Creature* creature) +{ + Player* player = creature->getPlayer(); + if (player && !player->hasFlag(PlayerFlag_CanConvinceAll)) { + if (!mType->info.isConvinceable) { + return false; + } + } + + if (isSummon()) { + if (getMaster() == creature) { + return false; + } + + Creature* oldMaster = getMaster(); + oldMaster->removeSummon(this); + } + + creature->addSummon(this); + + setFollowCreature(nullptr); + setAttackedCreature(nullptr); + + //destroy summons + for (Creature* summon : summons) { + summon->changeHealth(-summon->getHealth()); + summon->setMaster(nullptr); + summon->decrementReferenceCounter(); + } + summons.clear(); + + isMasterInRange = true; + updateTargetList(); + updateIdleStatus(); + + //Notify surrounding about the change + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), true); + g_game.map.getSpectators(list, creature->getPosition(), true); + for (Creature* spectator : list) { + spectator->onCreatureConvinced(creature, this); + } + + if (spawn) { + spawn->removeMonster(this); + spawn = nullptr; + } + return true; +} + +void Monster::onCreatureConvinced(const Creature* convincer, const Creature* creature) +{ + if (convincer != this && (isFriend(creature) || isOpponent(creature))) { + updateTargetList(); + updateIdleStatus(); + } +} + void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const { Creature::getPathSearchParams(creature, fpp); diff --git a/src/monster.h b/src/monster.h index 6d9f94f..8447df7 100644 --- a/src/monster.h +++ b/src/monster.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,15 +26,17 @@ class Creature; class Game; class Spawn; +class Combat; -using CreatureHashSet = std::unordered_set; -using CreatureList = std::list; +typedef std::unordered_set CreatureHashSet; +typedef std::list CreatureList; enum TargetSearchType_t { - TARGETSEARCH_DEFAULT, + TARGETSEARCH_ANY, TARGETSEARCH_RANDOM, - TARGETSEARCH_ATTACKRANGE, TARGETSEARCH_NEAREST, + TARGETSEARCH_WEAKEST, + TARGETSEARCH_MOSTDAMAGE, }; class Monster final : public Creature @@ -44,43 +46,39 @@ class Monster final : public Creature static int32_t despawnRange; static int32_t despawnRadius; - explicit Monster(MonsterType* mType); + explicit Monster(MonsterType* mtype); ~Monster(); // non-copyable Monster(const Monster&) = delete; Monster& operator=(const Monster&) = delete; - Monster* getMonster() override { + Monster* getMonster() final { return this; } - const Monster* getMonster() const override { + const Monster* getMonster() const final { return this; } - void setID() override { + void setID() final { if (id == 0) { id = monsterAutoID++; } } - void removeList() override; - void addList() override; + void removeList() final; + void addList() final; - const std::string& getName() const override { + const std::string& getName() const final { return mType->name; } - const std::string& getNameDescription() const override { + const std::string& getNameDescription() const final { return mType->nameDescription; } - std::string getDescription(int32_t) const override { + std::string getDescription(int32_t) const final { return strDescription + '.'; } - CreatureType_t getType() const override { - return CREATURETYPE_MONSTER; - } - const Position& getMasterPos() const { return masterPos; } @@ -88,19 +86,22 @@ class Monster final : public Creature masterPos = pos; } - RaceType_t getRace() const override { + RaceType_t getRace() const final { return mType->info.race; } - int32_t getArmor() const override { - return mType->info.armor; + int32_t getArmor() const final { + int32_t armor = mType->info.armor; + if (armor > 1) { + return rand() % (mType->info.armor >> 1) + (mType->info.armor >> 1); + } + + return armor; } - int32_t getDefense() const override { - return mType->info.defense; - } - bool isPushable() const override { + int32_t getDefense() final; + bool isPushable() const final { return mType->info.pushable && baseSpeed != 0; } - bool isAttackable() const override { + bool isAttackable() const final { return mType->info.isAttackable; } @@ -113,8 +114,8 @@ class Monster final : public Creature bool isHostile() const { return mType->info.isHostile; } - bool canSee(const Position& pos) const override; - bool canSeeInvisibility() const override { + bool canSee(const Position& pos) const final; + bool canSeeInvisibility() const final { return isImmune(CONDITION_INVISIBLE); } uint32_t getManaCost() const { @@ -123,35 +124,31 @@ class Monster final : public Creature void setSpawn(Spawn* spawn) { this->spawn = spawn; } - bool canWalkOnFieldType(CombatType_t combatType) const; + void onAttackedCreature(Creature* creature) final; - void onAttackedCreatureDisappear(bool isLogout) override; + void onCreatureAppear(Creature* creature, bool isLogin) final; + void onRemoveCreature(Creature* creature, bool isLogout) final; + void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) final; + void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) final; - void onCreatureAppear(Creature* creature, bool isLogin) override; - void onRemoveCreature(Creature* creature, bool isLogout) override; - void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) override; - void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override; + void drainHealth(Creature* attacker, int32_t damage) final; + void changeHealth(int32_t healthChange, bool sendHealthChange = true) final; + void onWalk() final; + bool getNextStep(Direction& direction, uint32_t& flags) final; + void onFollowCreatureComplete(const Creature* creature) final; - void drainHealth(Creature* attacker, int32_t damage) override; - void changeHealth(int32_t healthChange, bool sendHealthChange = true) override; - void onWalk() override; - bool getNextStep(Direction& direction, uint32_t& flags) override; - void onFollowCreatureComplete(const Creature* creature) override; + void onThink(uint32_t interval) final; - void onThink(uint32_t interval) override; + bool challengeCreature(Creature* creature) final; + bool convinceCreature(Creature* creature) final; - bool challengeCreature(Creature* creature) override; + void setNormalCreatureLight() final; + bool getCombatValues(int32_t& min, int32_t& max) final; - void setNormalCreatureLight() override; - bool getCombatValues(int32_t& min, int32_t& max) override; + void doAttacking(uint32_t interval) final; - void doAttacking(uint32_t interval) override; - bool hasExtraSwing() override { - return extraMeleeAttack; - } - - bool searchTarget(TargetSearchType_t searchType = TARGETSEARCH_DEFAULT); + bool searchTarget(TargetSearchType_t searchType); bool selectTarget(Creature* creature); const CreatureList& getTargetList() const { @@ -170,12 +167,9 @@ class Monster final : public Creature bool isTargetNearby() const { return stepDuration >= 1; } - bool isIgnoringFieldDamage() const { - return ignoreFieldDamage; - } BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense = false, bool checkArmor = false, bool field = false) override; + bool checkDefense = false, bool checkArmor = false, bool field = false); static uint32_t monsterAutoID; @@ -188,13 +182,13 @@ class Monster final : public Creature MonsterType* mType; Spawn* spawn = nullptr; - int64_t lastMeleeAttack = 0; + int64_t lifetime = 0; + int64_t nextDanceStepRound = 0; + int64_t earliestAttackTime = 0; + int64_t earliestWakeUpTime = 0; + int64_t earliestDanceTime = 0; - uint32_t attackTicks = 0; - uint32_t targetTicks = 0; uint32_t targetChangeTicks = 0; - uint32_t defenseTicks = 0; - uint32_t yellTicks = 0; int32_t minCombatValue = 0; int32_t maxCombatValue = 0; int32_t targetChangeCooldown = 0; @@ -203,10 +197,8 @@ class Monster final : public Creature Position masterPos; bool isIdle = true; - bool extraMeleeAttack = false; bool isMasterInRange = false; - bool randomStepping = false; - bool ignoreFieldDamage = false; + bool egibleToDance = true; void onCreatureEnter(Creature* creature); void onCreatureLeave(Creature* creature); @@ -223,8 +215,8 @@ class Monster final : public Creature void clearTargetList(); void clearFriendList(); - void death(Creature* lastHitCreature) override; - Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) override; + void death(Creature* lastHitCreature) final; + Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) final; void setIdle(bool idle); void updateIdleStatus(); @@ -232,12 +224,11 @@ class Monster final : public Creature return isIdle; } - void onAddCondition(ConditionType_t type) override; - void onEndCondition(ConditionType_t type) override; + void onAddCondition(ConditionType_t type) final; + void onEndCondition(ConditionType_t type) final; + void onCreatureConvinced(const Creature* convincer, const Creature* creature) final; bool canUseAttack(const Position& pos, const Creature* target) const; - bool canUseSpell(const Position& pos, const Position& targetPos, - const spellBlock_t& sb, uint32_t interval, bool& inRange, bool& resetTicks); bool getRandomStep(const Position& creaturePos, Direction& direction) const; bool getDanceStep(const Position& creaturePos, Direction& direction, bool keepAttack = true, bool keepDistance = true); @@ -256,25 +247,28 @@ class Monster final : public Creature bool isFriend(const Creature* creature) const; bool isOpponent(const Creature* creature) const; - uint64_t getLostExperience() const override { + uint64_t getLostExperience() const final { return skillLoss ? mType->info.experience : 0; } - uint16_t getLookCorpse() const override { + uint16_t getLookCorpse() const final { return mType->info.lookcorpse; } - void dropLoot(Container* corpse, Creature* lastHitCreature) override; - uint32_t getDamageImmunities() const override { + void dropLoot(Container* corpse, Creature* lastHitCreature) final; + uint32_t getDamageImmunities() const final { return mType->info.damageImmunities; } - uint32_t getConditionImmunities() const override { + uint32_t getConditionImmunities() const final { return mType->info.conditionImmunities; } - void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const override; - bool useCacheMap() const override { - return !randomStepping; + void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const final; + bool useCacheMap() const final { + return true; } friend class LuaScriptInterface; + friend class AreaSpawnEvent; + friend class Combat; + friend class Creature; }; #endif diff --git a/src/monsters.cpp b/src/monsters.cpp index c135872..ca294e2 100644 --- a/src/monsters.cpp +++ b/src/monsters.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +23,6 @@ #include "monster.h" #include "spells.h" #include "combat.h" -#include "weapons.h" #include "configmanager.h" #include "game.h" @@ -41,24 +40,158 @@ spellBlock_t::~spellBlock_t() } } -void MonsterType::loadLoot(MonsterType* monsterType, LootBlock lootBlock) +uint32_t Monsters::getLootRandom() { - if (lootBlock.childLoot.empty()) { - bool isContainer = Item::items[lootBlock.id].isContainer(); - if (isContainer) { - for (LootBlock child : lootBlock.childLoot) { - lootBlock.childLoot.push_back(child); + return uniform_random(0, MAX_LOOTCHANCE); +} + +void MonsterType::createLoot(Container* corpse) +{ + if (g_config.getNumber(ConfigManager::RATE_LOOT) == 0) { + corpse->startDecaying(); + return; + } + + Item* bagItem = Item::CreateItem(2853, 1); + if (!bagItem) { + return; + } + + Container* bagContainer = bagItem->getContainer(); + if (!bagContainer) { + return; + } + + if (g_game.internalAddItem(corpse, bagItem) != RETURNVALUE_NOERROR) { + corpse->internalAddThing(bagItem); + } + + bool includeBagLoot = false; + for (auto it = info.lootItems.rbegin(), end = info.lootItems.rend(); it != end; ++it) { + std::vector itemList = createLootItem(*it); + if (itemList.empty()) { + continue; + } + + for (Item* item : itemList) { + //check containers + if (Container* container = item->getContainer()) { + if (!createLootContainer(container, *it)) { + delete container; + continue; + } + } + + const ItemType& itemType = Item::items[item->getID()]; + if (itemType.weaponType != WEAPON_NONE || + itemType.stopTime || + itemType.decayTime) { + includeBagLoot = true; + if (g_game.internalAddItem(bagContainer, item) != RETURNVALUE_NOERROR) { + corpse->internalAddThing(item); + } + } else { + if (g_game.internalAddItem(corpse, item) != RETURNVALUE_NOERROR) { + corpse->internalAddThing(item); + } } } - monsterType->info.lootItems.push_back(lootBlock); - } else { - monsterType->info.lootItems.push_back(lootBlock); } + + if (!includeBagLoot) { + g_game.internalRemoveItem(bagItem); + } + + if (g_config.getBoolean(ConfigManager::SHOW_MONSTER_LOOT)) { + Player* owner = g_game.getPlayerByID(corpse->getCorpseOwner()); + if (owner) { + std::ostringstream ss; + ss << "Loot of " << nameDescription << ": " << corpse->getContentDescription(); + + if (owner->getParty()) { + owner->getParty()->broadcastPartyLoot(ss.str()); + } else { + owner->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); + } + } + } + + corpse->startDecaying(); +} + +std::vector MonsterType::createLootItem(const LootBlock& lootBlock) +{ + int32_t itemCount = 0; + + uint32_t randvalue = Monsters::getLootRandom(); + uint32_t extraMoney = g_config.getNumber(ConfigManager::MONEY_RATE); + uint32_t countMax = lootBlock.countmax + 1; + + if (randvalue < g_config.getNumber(ConfigManager::RATE_LOOT) * lootBlock.chance) { + if (Item::items[lootBlock.id].stackable) { + if (lootBlock.id == 3031) { + countMax *= extraMoney; + } + + itemCount = randvalue % countMax; + } else { + itemCount = 1; + } + } + + std::vector itemList; + while (itemCount > 0) { + uint16_t n = static_cast(std::min(itemCount, 100)); + Item* tmpItem = Item::CreateItem(lootBlock.id, n); + if (!tmpItem) { + break; + } + + itemCount -= n; + + if (lootBlock.subType != -1) { + tmpItem->setSubType(lootBlock.subType); + } + + if (lootBlock.actionId != -1) { + tmpItem->setActionId(lootBlock.actionId); + } + + if (!lootBlock.text.empty()) { + tmpItem->setText(lootBlock.text); + } + + itemList.push_back(tmpItem); + } + return itemList; +} + +bool MonsterType::createLootContainer(Container* parent, const LootBlock& lootblock) +{ + auto it = lootblock.childLoot.begin(), end = lootblock.childLoot.end(); + if (it == end) { + return true; + } + + for (; it != end && parent->size() < parent->capacity(); ++it) { + auto itemList = createLootItem(*it); + for (Item* tmpItem : itemList) { + if (Container* container = tmpItem->getContainer()) { + if (!createLootContainer(container, *it)) { + delete container; + } else { + parent->internalAddThing(container); + } + } else { + parent->internalAddThing(tmpItem); + } + } + } + return !parent->empty(); } bool Monsters::loadFromXml(bool reloading /*= false*/) { - unloadedMonsters = {}; pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file("data/monster/monsters.xml"); if (!result) { @@ -68,13 +201,30 @@ bool Monsters::loadFromXml(bool reloading /*= false*/) loaded = true; + std::list> monsterScriptList; for (auto monsterNode : doc.child("monsters").children()) { - std::string name = asLowerCaseString(monsterNode.attribute("name").as_string()); - std::string file = "data/monster/" + std::string(monsterNode.attribute("file").as_string()); - if (reloading && monsters.find(name) != monsters.end()) { - loadMonster(file, name, true); - } else { - unloadedMonsters.emplace(name, file); + loadMonster("data/monster/" + std::string(monsterNode.attribute("file").as_string()), monsterNode.attribute("name").as_string(), monsterScriptList, reloading); + } + + if (!monsterScriptList.empty()) { + if (!scriptInterface) { + scriptInterface.reset(new LuaScriptInterface("Monster Interface")); + scriptInterface->initState(); + } + + for (const auto& scriptEntry : monsterScriptList) { + MonsterType* mType = scriptEntry.first; + if (scriptInterface->loadFile("data/monster/scripts/" + scriptEntry.second) == 0) { + mType->info.scriptInterface = scriptInterface.get(); + mType->info.creatureAppearEvent = scriptInterface->getEvent("onCreatureAppear"); + mType->info.creatureDisappearEvent = scriptInterface->getEvent("onCreatureDisappear"); + mType->info.creatureMoveEvent = scriptInterface->getEvent("onCreatureMove"); + mType->info.creatureSayEvent = scriptInterface->getEvent("onCreatureSay"); + mType->info.thinkEvent = scriptInterface->getEvent("onThink"); + } else { + std::cout << "[Warning - Monsters::loadMonster] Can not load script: " << scriptEntry.second << std::endl; + std::cout << scriptInterface->getLastLuaError() << std::endl; + } } } return true; @@ -89,15 +239,13 @@ bool Monsters::reload() return loadFromXml(true); } -ConditionDamage* Monsters::getDamageCondition(ConditionType_t conditionType, - int32_t maxDamage, int32_t minDamage, int32_t startDamage, uint32_t tickInterval) +ConditionDamage* Monsters::getDamageCondition(ConditionType_t conditionType, int32_t cycle, int32_t count, int32_t max_count) { ConditionDamage* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, conditionType, 0, 0)); - condition->setParam(CONDITION_PARAM_TICKINTERVAL, tickInterval); - condition->setParam(CONDITION_PARAM_MINVALUE, minDamage); - condition->setParam(CONDITION_PARAM_MAXVALUE, maxDamage); - condition->setParam(CONDITION_PARAM_STARTVALUE, startDamage); - condition->setParam(CONDITION_PARAM_DELAYED, 1); + + condition->setParam(CONDITION_PARAM_CYCLE, cycle); + condition->setParam(CONDITION_PARAM_COUNT, count); + condition->setParam(CONDITION_PARAM_MAX_COUNT, max_count); return condition; } @@ -118,15 +266,16 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co return false; } - if ((attr = node.attribute("speed")) || (attr = node.attribute("interval"))) { - sb.speed = std::max(1, pugi::cast(attr.value())); - } - if ((attr = node.attribute("chance"))) { uint32_t chance = pugi::cast(attr.value()); if (chance > 100) { chance = 100; } + + if (chance == 0) { + std::cout << "[Error - Monsters::deserializeSpell] - " << description << " - Spell chance is zero: " << name << std::endl; + } + sb.chance = chance; } @@ -136,6 +285,8 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co range = Map::maxViewportX * 2; } sb.range = range; + } else { + sb.range = Map::maxClientViewportX; } if ((attr = node.attribute("min"))) { @@ -217,88 +368,22 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co std::string tmpName = asLowerCaseString(name); - if (tmpName == "melee") { - sb.isMelee = true; - - pugi::xml_attribute attackAttribute, skillAttribute; - if ((attackAttribute = node.attribute("attack")) && (skillAttribute = node.attribute("skill"))) { - sb.minCombatValue = 0; - sb.maxCombatValue = -Weapons::getMaxMeleeDamage(pugi::cast(skillAttribute.value()), pugi::cast(attackAttribute.value())); - } - - ConditionType_t conditionType = CONDITION_NONE; - int32_t minDamage = 0; - int32_t maxDamage = 0; - uint32_t tickInterval = 2000; - - if ((attr = node.attribute("fire"))) { - conditionType = CONDITION_FIRE; - - minDamage = pugi::cast(attr.value()); - maxDamage = minDamage; - tickInterval = 9000; - } else if ((attr = node.attribute("poison"))) { - conditionType = CONDITION_POISON; - - minDamage = pugi::cast(attr.value()); - maxDamage = minDamage; - tickInterval = 4000; - } else if ((attr = node.attribute("energy"))) { - conditionType = CONDITION_ENERGY; - - minDamage = pugi::cast(attr.value()); - maxDamage = minDamage; - tickInterval = 10000; - } else if ((attr = node.attribute("drown"))) { - conditionType = CONDITION_DROWN; - - minDamage = pugi::cast(attr.value()); - maxDamage = minDamage; - tickInterval = 5000; - } else if ((attr = node.attribute("freeze"))) { - conditionType = CONDITION_FREEZING; - - minDamage = pugi::cast(attr.value()); - maxDamage = minDamage; - tickInterval = 8000; - } else if ((attr = node.attribute("dazzle"))) { - conditionType = CONDITION_DAZZLED; - - minDamage = pugi::cast(attr.value()); - maxDamage = minDamage; - tickInterval = 10000; - } else if ((attr = node.attribute("curse"))) { - conditionType = CONDITION_CURSED; - - minDamage = pugi::cast(attr.value()); - maxDamage = minDamage; - tickInterval = 4000; - } else if ((attr = node.attribute("bleed")) || (attr = node.attribute("physical"))) { - conditionType = CONDITION_BLEEDING; - tickInterval = 4000; - } - - if ((attr = node.attribute("tick"))) { - int32_t value = pugi::cast(attr.value()); - if (value > 0) { - tickInterval = value; - } - } - - if (conditionType != CONDITION_NONE) { - Condition* condition = getDamageCondition(conditionType, maxDamage, minDamage, 0, tickInterval); - combat->addCondition(condition); - } - - sb.range = 1; + if (tmpName == "physical") { combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE); combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1); combat->setParam(COMBAT_PARAM_BLOCKSHIELD, 1); - combat->setOrigin(ORIGIN_MELEE); - } else if (tmpName == "physical") { - combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE); - combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1); - combat->setOrigin(ORIGIN_RANGED); + uint32_t tD = this->getMonsterType(description)->info.targetDistance; + if (tD == 1) { + if (sb.range > 1) { + combat->setOrigin(ORIGIN_RANGED); + } + else { + combat->setOrigin(ORIGIN_MELEE); + } + } + else if (tD > 1 && sb.range > 1) { + combat->setOrigin(ORIGIN_RANGED); + } } else if (tmpName == "bleed") { combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE); } else if (tmpName == "poison" || tmpName == "earth") { @@ -309,12 +394,6 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co combat->setParam(COMBAT_PARAM_TYPE, COMBAT_ENERGYDAMAGE); } else if (tmpName == "drown") { combat->setParam(COMBAT_PARAM_TYPE, COMBAT_DROWNDAMAGE); - } else if (tmpName == "ice") { - combat->setParam(COMBAT_PARAM_TYPE, COMBAT_ICEDAMAGE); - } else if (tmpName == "holy") { - combat->setParam(COMBAT_PARAM_TYPE, COMBAT_HOLYDAMAGE); - } else if (tmpName == "death") { - combat->setParam(COMBAT_PARAM_TYPE, COMBAT_DEATHDAMAGE); } else if (tmpName == "lifedrain") { combat->setParam(COMBAT_PARAM_TYPE, COMBAT_LIFEDRAIN); } else if (tmpName == "manadrain") { @@ -324,6 +403,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); } else if (tmpName == "speed") { int32_t speedChange = 0; + int32_t variation = 0; int32_t duration = 10000; if ((attr = node.attribute("duration"))) { @@ -332,10 +412,10 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co if ((attr = node.attribute("speedchange"))) { speedChange = pugi::cast(attr.value()); - if (speedChange < -1000) { - //cant be slower than 100% - speedChange = -1000; - } + } + + if ((attr = node.attribute("variation"))) { + variation = pugi::cast(attr.value()); } ConditionType_t conditionType; @@ -347,8 +427,9 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co } ConditionSpeed* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, conditionType, duration, 0)); - condition->setFormulaVars(speedChange / 1000.0, 0, speedChange / 1000.0, 0); - combat->addCondition(condition); + condition->setVariation(variation); + condition->setSpeedDelta(speedChange); + combat->setCondition(condition); } else if (tmpName == "outfit") { int32_t duration = 10000; @@ -362,7 +443,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co ConditionOutfit* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0)); condition->setOutfit(mType->info.outfit); combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); - combat->addCondition(condition); + combat->setCondition(condition); } } else if ((attr = node.attribute("item"))) { Outfit_t outfit; @@ -371,7 +452,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co ConditionOutfit* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0)); condition->setOutfit(outfit); combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); - combat->addCondition(condition); + combat->setCondition(condition); } } else if (tmpName == "invisible") { int32_t duration = 10000; @@ -382,7 +463,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_INVISIBLE, duration, 0); combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); - combat->addCondition(condition); + combat->setCondition(condition); } else if (tmpName == "drunk") { int32_t duration = 10000; @@ -391,7 +472,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co } Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_DRUNK, duration, 0); - combat->addCondition(condition); + combat->setCondition(condition); } else if (tmpName == "firefield") { combat->setParam(COMBAT_PARAM_CREATEITEM, ITEM_FIREFIELD_PVP_FULL); } else if (tmpName == "poisonfield") { @@ -401,59 +482,42 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co } else if (tmpName == "firecondition" || tmpName == "energycondition" || tmpName == "earthcondition" || tmpName == "poisoncondition" || tmpName == "icecondition" || tmpName == "freezecondition" || - tmpName == "deathcondition" || tmpName == "cursecondition" || - tmpName == "holycondition" || tmpName == "dazzlecondition" || - tmpName == "drowncondition" || tmpName == "bleedcondition" || - tmpName == "physicalcondition") { + tmpName == "physicalcondition" || tmpName == "drowncondition") { ConditionType_t conditionType = CONDITION_NONE; - uint32_t tickInterval = 2000; if (tmpName == "firecondition") { conditionType = CONDITION_FIRE; - tickInterval = 10000; } else if (tmpName == "poisoncondition" || tmpName == "earthcondition") { conditionType = CONDITION_POISON; - tickInterval = 4000; } else if (tmpName == "energycondition") { conditionType = CONDITION_ENERGY; - tickInterval = 10000; } else if (tmpName == "drowncondition") { conditionType = CONDITION_DROWN; - tickInterval = 5000; - } else if (tmpName == "freezecondition" || tmpName == "icecondition") { - conditionType = CONDITION_FREEZING; - tickInterval = 10000; - } else if (tmpName == "cursecondition" || tmpName == "deathcondition") { - conditionType = CONDITION_CURSED; - tickInterval = 4000; - } else if (tmpName == "dazzlecondition" || tmpName == "holycondition") { - conditionType = CONDITION_DAZZLED; - tickInterval = 10000; - } else if (tmpName == "physicalcondition" || tmpName == "bleedcondition") { - conditionType = CONDITION_BLEEDING; - tickInterval = 4000; } - if ((attr = node.attribute("tick"))) { - int32_t value = pugi::cast(attr.value()); - if (value > 0) { - tickInterval = value; - } + int32_t cycle = 0; + if ((attr = node.attribute("count"))) { + cycle = std::abs(pugi::cast(attr.value())); + } else { + std::cout << "[Error - Monsters::deserializeSpell] - " << description << " - missing count attribute" << std::endl; + delete combat; + return false; } - int32_t minDamage = std::abs(sb.minCombatValue); - int32_t maxDamage = std::abs(sb.maxCombatValue); - int32_t startDamage = 0; + int32_t count = 0; - if ((attr = node.attribute("start"))) { - int32_t value = std::abs(pugi::cast(attr.value())); - if (value <= minDamage) { - startDamage = value; - } + if (conditionType == CONDITION_POISON) { + count = 3; + } else if (conditionType == CONDITION_FIRE) { + count = 8; + cycle /= 10; + } else if (conditionType == CONDITION_ENERGY) { + count = 10; + cycle /= 20; } - Condition* condition = getDamageCondition(conditionType, maxDamage, minDamage, startDamage, tickInterval); - combat->addCondition(condition); + Condition* condition = getDamageCondition(conditionType, cycle, count, count); + combat->setCondition(condition); } else if (tmpName == "strength") { // } else if (tmpName == "effect") { @@ -465,6 +529,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co } combat->setPlayerCombatValues(COMBAT_FORMULA_DAMAGE, sb.minCombatValue, 0, sb.maxCombatValue, 0); + combatSpell = new CombatSpell(combat, needTarget, needDirection); for (auto attributeNode : node.children()) { @@ -472,7 +537,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co const char* value = attr.value(); if (strcasecmp(value, "shooteffect") == 0) { if ((attr = attributeNode.attribute("value"))) { - ShootType_t shoot = getShootType(asLowerCaseString(attr.as_string())); + ShootType_t shoot = getShootType(attr.as_string()); if (shoot != CONST_ANI_NONE) { combat->setParam(COMBAT_PARAM_DISTANCEEFFECT, shoot); } else { @@ -481,7 +546,7 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co } } else if (strcasecmp(value, "areaeffect") == 0) { if ((attr = attributeNode.attribute("value"))) { - MagicEffectClasses effect = getMagicEffect(asLowerCaseString(attr.as_string())); + MagicEffectClasses effect = getMagicEffect(attr.as_string()); if (effect != CONST_ME_NONE) { combat->setParam(COMBAT_PARAM_EFFECT, effect); } else { @@ -502,269 +567,39 @@ bool Monsters::deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, co return true; } -bool Monsters::deserializeSpell(MonsterSpell* spell, spellBlock_t& sb, const std::string& description) -{ - if (!spell->scriptName.empty()) { - spell->isScripted = true; - } else if (!spell->name.empty()) { - spell->isScripted = false; - } else { - return false; - } - - sb.speed = spell->interval; - - if (spell->chance > 100) { - sb.chance = 100; - } else { - sb.chance = spell->chance; - } - - if (spell->range > (Map::maxViewportX * 2)) { - spell->range = Map::maxViewportX * 2; - } - sb.range = spell->range; - - sb.minCombatValue = spell->minCombatValue; - sb.maxCombatValue = spell->maxCombatValue; - if (std::abs(sb.minCombatValue) > std::abs(sb.maxCombatValue)) { - int32_t value = sb.maxCombatValue; - sb.maxCombatValue = sb.minCombatValue; - sb.minCombatValue = value; - } - - sb.spell = g_spells->getSpellByName(spell->name); - if (sb.spell) { - return true; - } - - CombatSpell* combatSpell = nullptr; - - if (spell->isScripted) { - std::unique_ptr combatSpellPtr(new CombatSpell(nullptr, spell->needTarget, spell->needDirection)); - if (!combatSpellPtr->loadScript("data/" + g_spells->getScriptBaseName() + "/scripts/" + spell->scriptName)) { - std::cout << "cannot find file" << std::endl; - return false; - } - - if (!combatSpellPtr->loadScriptCombat()) { - return false; - } - - combatSpell = combatSpellPtr.release(); - combatSpell->getCombat()->setPlayerCombatValues(COMBAT_FORMULA_DAMAGE, sb.minCombatValue, 0, sb.maxCombatValue, 0); - } else { - std::unique_ptr combat{ new Combat }; - sb.combatSpell = true; - - if (spell->length > 0) { - spell->spread = std::max(0, spell->spread); - - AreaCombat* area = new AreaCombat(); - area->setupArea(spell->length, spell->spread); - combat->setArea(area); - - spell->needDirection = true; - } - - if (spell->radius > 0) { - AreaCombat* area = new AreaCombat(); - area->setupArea(spell->radius); - combat->setArea(area); - } - - std::string tmpName = asLowerCaseString(spell->name); - - if (tmpName == "melee") { - sb.isMelee = true; - - if (spell->attack > 0 && spell->skill > 0) { - sb.minCombatValue = 0; - sb.maxCombatValue = -Weapons::getMaxMeleeDamage(spell->skill, spell->attack); - } - - ConditionType_t conditionType = CONDITION_NONE; - int32_t minDamage = 0; - int32_t maxDamage = 0; - uint32_t tickInterval = 2000; - - if (spell->conditionType != CONDITION_NONE) { - conditionType = spell->conditionType; - - minDamage = spell->conditionMinDamage; - maxDamage = minDamage; - if (spell->tickInterval != 0) { - tickInterval = spell->tickInterval; - } - - Condition* condition = getDamageCondition(conditionType, maxDamage, minDamage, spell->conditionStartDamage, tickInterval); - combat->addCondition(condition); - } - - sb.range = 1; - combat->setParam(COMBAT_PARAM_TYPE, COMBAT_PHYSICALDAMAGE); - combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1); - combat->setParam(COMBAT_PARAM_BLOCKSHIELD, 1); - combat->setOrigin(ORIGIN_MELEE); - } else if (tmpName == "combat") { - if (spell->combatType == COMBAT_PHYSICALDAMAGE) { - combat->setParam(COMBAT_PARAM_BLOCKARMOR, 1); - combat->setOrigin(ORIGIN_RANGED); - } else if (spell->combatType == COMBAT_HEALING) { - combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); - } - combat->setParam(COMBAT_PARAM_TYPE, spell->combatType); - } else if (tmpName == "speed") { - int32_t speedChange = 0; - int32_t duration = 10000; - - if (spell->duration != 0) { - duration = spell->duration; - } - - if (spell->speedChange != 0) { - speedChange = spell->speedChange; - if (speedChange < -1000) { - //cant be slower than 100% - speedChange = -1000; - } - } - - ConditionType_t conditionType; - if (speedChange > 0) { - conditionType = CONDITION_HASTE; - combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); - } else { - conditionType = CONDITION_PARALYZE; - } - - ConditionSpeed* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, conditionType, duration, 0)); - condition->setFormulaVars(speedChange / 1000.0, 0, speedChange / 1000.0, 0); - combat->addCondition(condition); - } else if (tmpName == "outfit") { - int32_t duration = 10000; - - if (spell->duration != 0) { - duration = spell->duration; - } - - ConditionOutfit* condition = static_cast(Condition::createCondition(CONDITIONID_COMBAT, CONDITION_OUTFIT, duration, 0)); - condition->setOutfit(spell->outfit); - combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); - combat->addCondition(condition); - } else if (tmpName == "invisible") { - int32_t duration = 10000; - - if (spell->duration != 0) { - duration = spell->duration; - } - - Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_INVISIBLE, duration, 0); - combat->setParam(COMBAT_PARAM_AGGRESSIVE, 0); - combat->addCondition(condition); - } else if (tmpName == "drunk") { - int32_t duration = 10000; - - if (spell->duration != 0) { - duration = spell->duration; - } - - Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_DRUNK, duration, 0); - combat->addCondition(condition); - } else if (tmpName == "firefield") { - combat->setParam(COMBAT_PARAM_CREATEITEM, ITEM_FIREFIELD_PVP_FULL); - } else if (tmpName == "poisonfield") { - combat->setParam(COMBAT_PARAM_CREATEITEM, ITEM_POISONFIELD_PVP); - } else if (tmpName == "energyfield") { - combat->setParam(COMBAT_PARAM_CREATEITEM, ITEM_ENERGYFIELD_PVP); - } else if (tmpName == "condition") { - uint32_t tickInterval = 2000; - - if (spell->conditionType == CONDITION_NONE) { - std::cout << "[Error - Monsters::deserializeSpell] - " << description << " - Condition is not set for: " << spell->name << std::endl; - } - - if (spell->tickInterval != 0) { - int32_t value = spell->tickInterval; - if (value > 0) { - tickInterval = value; - } - } - - int32_t minDamage = std::abs(spell->conditionMinDamage); - int32_t maxDamage = std::abs(spell->conditionMaxDamage); - int32_t startDamage = 0; - - if (spell->conditionStartDamage != 0) { - int32_t value = std::abs(spell->conditionStartDamage); - if (value <= minDamage) { - startDamage = value; - } - } - - Condition* condition = getDamageCondition(spell->conditionType, maxDamage, minDamage, startDamage, tickInterval); - combat->addCondition(condition); - } else if (tmpName == "strength") { - // - } else if (tmpName == "effect") { - // - } else { - std::cout << "[Error - Monsters::deserializeSpell] - " << description << " - Unknown spell name: " << spell->name << std::endl; - } - - if (spell->needTarget) { - if (spell->shoot != CONST_ANI_NONE) { - combat->setParam(COMBAT_PARAM_DISTANCEEFFECT, spell->shoot); - } - } - - if (spell->effect != CONST_ME_NONE) { - combat->setParam(COMBAT_PARAM_EFFECT, spell->effect); - } - - combat->setPlayerCombatValues(COMBAT_FORMULA_DAMAGE, sb.minCombatValue, 0, sb.maxCombatValue, 0); - combatSpell = new CombatSpell(combat.release(), spell->needTarget, spell->needDirection); - } - - sb.spell = combatSpell; - if (combatSpell) { - sb.combatSpell = true; - } - return true; -} - -MonsterType* Monsters::loadMonster(const std::string& file, const std::string& monsterName, bool reloading /*= false*/) +bool Monsters::loadMonster(const std::string& file, const std::string& monsterName, std::list>& monsterScriptList, bool reloading /*= false*/) { MonsterType* mType = nullptr; + bool new_mType = true; pugi::xml_document doc; pugi::xml_parse_result result = doc.load_file(file.c_str()); if (!result) { printXMLError("Error - Monsters::loadMonster", file, result); - return nullptr; + return false; } pugi::xml_node monsterNode = doc.child("monster"); if (!monsterNode) { std::cout << "[Error - Monsters::loadMonster] Missing monster node in: " << file << std::endl; - return nullptr; + return false; } pugi::xml_attribute attr; if (!(attr = monsterNode.attribute("name"))) { std::cout << "[Error - Monsters::loadMonster] Missing name in: " << file << std::endl; - return nullptr; + return false; } if (reloading) { - auto it = monsters.find(asLowerCaseString(monsterName)); - if (it != monsters.end()) { - mType = &it->second; + mType = getMonsterType(monsterName); + if (mType != nullptr) { + new_mType = false; mType->info = {}; } } - if (!mType) { + if (new_mType) { mType = &monsters[asLowerCaseString(monsterName)]; } @@ -787,8 +622,6 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m mType->info.race = RACE_UNDEAD; } else if (tmpStrValue == "fire" || tmpInt == 4) { mType->info.race = RACE_FIRE; - } else if (tmpStrValue == "energy" || tmpInt == 5) { - mType->info.race = RACE_ENERGY; } else { std::cout << "[Warning - Monsters::loadMonster] Unknown race type " << attr.as_string() << ". " << file << std::endl; } @@ -807,27 +640,11 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m } if ((attr = monsterNode.attribute("skull"))) { - mType->info.skull = getSkullType(asLowerCaseString(attr.as_string())); + mType->info.skull = getSkullType(attr.as_string()); } if ((attr = monsterNode.attribute("script"))) { - if (!scriptInterface) { - scriptInterface.reset(new LuaScriptInterface("Monster Interface")); - scriptInterface->initState(); - } - - std::string script = attr.as_string(); - if (scriptInterface->loadFile("data/monster/scripts/" + script) == 0) { - mType->info.scriptInterface = scriptInterface.get(); - mType->info.creatureAppearEvent = scriptInterface->getEvent("onCreatureAppear"); - mType->info.creatureDisappearEvent = scriptInterface->getEvent("onCreatureDisappear"); - mType->info.creatureMoveEvent = scriptInterface->getEvent("onCreatureMove"); - mType->info.creatureSayEvent = scriptInterface->getEvent("onCreatureSay"); - mType->info.thinkEvent = scriptInterface->getEvent("onThink"); - } else { - std::cout << "[Warning - Monsters::loadMonster] Can not load script: " << script << std::endl; - std::cout << scriptInterface->getLastLuaError() << std::endl; - } + monsterScriptList.emplace_back(mType, attr.as_string()); } pugi::xml_node node; @@ -865,14 +682,6 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m mType->info.canPushItems = attr.as_bool(); } else if (strcasecmp(attrName, "canpushcreatures") == 0) { mType->info.canPushCreatures = attr.as_bool(); - } else if (strcasecmp(attrName, "staticattack") == 0) { - uint32_t staticAttack = pugi::cast(attr.value()); - if (staticAttack > 100) { - std::cout << "[Warning - Monsters::loadMonster] staticattack greater than 100. " << file << std::endl; - staticAttack = 100; - } - - mType->info.staticAttackChance = staticAttack; } else if (strcasecmp(attrName, "lightlevel") == 0) { mType->info.light.level = pugi::cast(attr.value()); } else if (strcasecmp(attrName, "lightcolor") == 0) { @@ -883,12 +692,6 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m mType->info.runAwayHealth = pugi::cast(attr.value()); } else if (strcasecmp(attrName, "hidehealth") == 0) { mType->info.hiddenHealth = attr.as_bool(); - } else if (strcasecmp(attrName, "canwalkonenergy") == 0) { - mType->info.canWalkOnEnergy = attr.as_bool(); - } else if (strcasecmp(attrName, "canwalkonfire") == 0) { - mType->info.canWalkOnFire = attr.as_bool(); - } else if (strcasecmp(attrName, "canwalkonpoison") == 0) { - mType->info.canWalkOnPoison = attr.as_bool(); } else { std::cout << "[Warning - Monsters::loadMonster] Unknown flag attribute: " << attrName << ". " << file << std::endl; } @@ -915,6 +718,34 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m } } + if ((node = monsterNode.child("targetstrategy"))) { + if ((attr = node.attribute("nearest"))) { + mType->info.strategyNearestEnemy = pugi::cast(attr.value()); + } else { + std::cout << "[Warning - Monsters::loadMonster] Missing nearest enemy chance. " << file << std::endl; + } + + if ((attr = node.attribute("weakest"))) { + mType->info.strategyWeakestEnemy = pugi::cast(attr.value()); + } else { + std::cout << "[Warning - Monsters::loadMonster] Missing weakest enemy chance. " << file << std::endl; + } + + if ((attr = node.attribute("mostdamage"))) { + mType->info.strategyMostDamageEnemy = pugi::cast(attr.value()); + } else { + std::cout << "[Warning - Monsters::loadMonster] Missing most damage enemy chance. " << file << std::endl; + } + + if ((attr = node.attribute("random"))) { + mType->info.strategyRandomEnemy = pugi::cast(attr.value()); + } else { + std::cout << "[Warning - Monsters::loadMonster] Missing random enemy chance. " << file << std::endl; + } + } else { + std::cout << "[Warning - Monsters::loadMonster] Missing target change strategies. " << file << std::endl; + } + if ((node = monsterNode.child("look"))) { if ((attr = node.attribute("type"))) { mType->info.outfit.lookType = pugi::cast(attr.value()); @@ -944,16 +775,24 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m std::cout << "[Warning - Monsters::loadMonster] Missing look type/typeex. " << file << std::endl; } - if ((attr = node.attribute("mount"))) { - mType->info.outfit.lookMount = pugi::cast(attr.value()); - } - if ((attr = node.attribute("corpse"))) { mType->info.lookcorpse = pugi::cast(attr.value()); } } if ((node = monsterNode.child("attacks"))) { + if ((attr = node.attribute("attack"))) { + mType->info.attack = pugi::cast(attr.value()); + } + + if ((attr = node.attribute("skill"))) { + mType->info.skill = pugi::cast(attr.value()); + } + + if ((attr = node.attribute("poison"))) { + mType->info.poison = pugi::cast(attr.value()); + } + for (auto attackNode : node.children()) { spellBlock_t sb; if (deserializeSpell(attackNode, sb, monsterName)) { @@ -989,29 +828,19 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m std::string tmpStrValue = asLowerCaseString(attr.as_string()); if (tmpStrValue == "physical") { mType->info.damageImmunities |= COMBAT_PHYSICALDAMAGE; - mType->info.conditionImmunities |= CONDITION_BLEEDING; } else if (tmpStrValue == "energy") { mType->info.damageImmunities |= COMBAT_ENERGYDAMAGE; mType->info.conditionImmunities |= CONDITION_ENERGY; } else if (tmpStrValue == "fire") { mType->info.damageImmunities |= COMBAT_FIREDAMAGE; mType->info.conditionImmunities |= CONDITION_FIRE; + } else if (tmpStrValue == "drown") { + mType->info.damageImmunities |= COMBAT_DROWNDAMAGE; + mType->info.conditionImmunities |= CONDITION_DROWN; } else if (tmpStrValue == "poison" || tmpStrValue == "earth") { mType->info.damageImmunities |= COMBAT_EARTHDAMAGE; mType->info.conditionImmunities |= CONDITION_POISON; - } else if (tmpStrValue == "drown") { - mType->info.damageImmunities |= COMBAT_DROWNDAMAGE; - mType->info.conditionImmunities |= CONDITION_DROWN; - } else if (tmpStrValue == "ice") { - mType->info.damageImmunities |= COMBAT_ICEDAMAGE; - mType->info.conditionImmunities |= CONDITION_FREEZING; - } else if (tmpStrValue == "holy") { - mType->info.damageImmunities |= COMBAT_HOLYDAMAGE; - mType->info.conditionImmunities |= CONDITION_DAZZLED; - } else if (tmpStrValue == "death") { - mType->info.damageImmunities |= COMBAT_DEATHDAMAGE; - mType->info.conditionImmunities |= CONDITION_CURSED; } else if (tmpStrValue == "lifedrain") { mType->info.damageImmunities |= COMBAT_LIFEDRAIN; } else if (tmpStrValue == "manadrain") { @@ -1024,15 +853,12 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m mType->info.conditionImmunities |= CONDITION_DRUNK; } else if (tmpStrValue == "invisible" || tmpStrValue == "invisibility") { mType->info.conditionImmunities |= CONDITION_INVISIBLE; - } else if (tmpStrValue == "bleed") { - mType->info.conditionImmunities |= CONDITION_BLEEDING; } else { std::cout << "[Warning - Monsters::loadMonster] Unknown immunity name " << attr.as_string() << ". " << file << std::endl; } } else if ((attr = immunityNode.attribute("physical"))) { if (attr.as_bool()) { mType->info.damageImmunities |= COMBAT_PHYSICALDAMAGE; - mType->info.conditionImmunities |= CONDITION_BLEEDING; } } else if ((attr = immunityNode.attribute("energy"))) { if (attr.as_bool()) { @@ -1044,30 +870,15 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m mType->info.damageImmunities |= COMBAT_FIREDAMAGE; mType->info.conditionImmunities |= CONDITION_FIRE; } - } else if ((attr = immunityNode.attribute("poison")) || (attr = immunityNode.attribute("earth"))) { - if (attr.as_bool()) { - mType->info.damageImmunities |= COMBAT_EARTHDAMAGE; - mType->info.conditionImmunities |= CONDITION_POISON; - } } else if ((attr = immunityNode.attribute("drown"))) { if (attr.as_bool()) { mType->info.damageImmunities |= COMBAT_DROWNDAMAGE; mType->info.conditionImmunities |= CONDITION_DROWN; } - } else if ((attr = immunityNode.attribute("ice"))) { + } else if ((attr = immunityNode.attribute("poison")) || (attr = immunityNode.attribute("earth"))) { if (attr.as_bool()) { - mType->info.damageImmunities |= COMBAT_ICEDAMAGE; - mType->info.conditionImmunities |= CONDITION_FREEZING; - } - } else if ((attr = immunityNode.attribute("holy"))) { - if (attr.as_bool()) { - mType->info.damageImmunities |= COMBAT_HOLYDAMAGE; - mType->info.conditionImmunities |= CONDITION_DAZZLED; - } - } else if ((attr = immunityNode.attribute("death"))) { - if (attr.as_bool()) { - mType->info.damageImmunities |= COMBAT_DEATHDAMAGE; - mType->info.conditionImmunities |= CONDITION_CURSED; + mType->info.damageImmunities |= COMBAT_EARTHDAMAGE; + mType->info.conditionImmunities |= CONDITION_POISON; } } else if ((attr = immunityNode.attribute("lifedrain"))) { if (attr.as_bool()) { @@ -1085,10 +896,6 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m if (attr.as_bool()) { mType->info.conditionImmunities |= CONDITION_OUTFIT; } - } else if ((attr = immunityNode.attribute("bleed"))) { - if (attr.as_bool()) { - mType->info.conditionImmunities |= CONDITION_BLEEDING; - } } else if ((attr = immunityNode.attribute("drunk"))) { if (attr.as_bool()) { mType->info.conditionImmunities |= CONDITION_DRUNK; @@ -1104,18 +911,6 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m } if ((node = monsterNode.child("voices"))) { - if ((attr = node.attribute("speed")) || (attr = node.attribute("interval"))) { - mType->info.yellSpeedTicks = pugi::cast(attr.value()); - } else { - std::cout << "[Warning - Monsters::loadMonster] Missing voices speed. " << file << std::endl; - } - - if ((attr = node.attribute("chance"))) { - mType->info.yellChance = pugi::cast(attr.value()); - } else { - std::cout << "[Warning - Monsters::loadMonster] Missing voices chance. " << file << std::endl; - } - for (auto voiceNode : node.children()) { voiceBlock_t vb; if ((attr = voiceNode.attribute("sentence"))) { @@ -1148,20 +943,12 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m for (auto elementNode : node.children()) { if ((attr = elementNode.attribute("physicalPercent"))) { mType->info.elementMap[COMBAT_PHYSICALDAMAGE] = pugi::cast(attr.value()); - } else if ((attr = elementNode.attribute("icePercent"))) { - mType->info.elementMap[COMBAT_ICEDAMAGE] = pugi::cast(attr.value()); } else if ((attr = elementNode.attribute("poisonPercent")) || (attr = elementNode.attribute("earthPercent"))) { mType->info.elementMap[COMBAT_EARTHDAMAGE] = pugi::cast(attr.value()); } else if ((attr = elementNode.attribute("firePercent"))) { mType->info.elementMap[COMBAT_FIREDAMAGE] = pugi::cast(attr.value()); } else if ((attr = elementNode.attribute("energyPercent"))) { - mType->info.elementMap[COMBAT_ENERGYDAMAGE] = pugi::cast(attr.value()); - } else if ((attr = elementNode.attribute("holyPercent"))) { - mType->info.elementMap[COMBAT_HOLYDAMAGE] = pugi::cast(attr.value()); - } else if ((attr = elementNode.attribute("deathPercent"))) { - mType->info.elementMap[COMBAT_DEATHDAMAGE] = pugi::cast(attr.value()); - } else if ((attr = elementNode.attribute("drownPercent"))) { - mType->info.elementMap[COMBAT_DROWNDAMAGE] = pugi::cast(attr.value()); + mType->info.elementMap[COMBAT_ENERGYDAMAGE] = pugi::cast(attr.value()); } else if ((attr = elementNode.attribute("lifedrainPercent"))) { mType->info.elementMap[COMBAT_LIFEDRAIN] = pugi::cast(attr.value()); } else if ((attr = elementNode.attribute("manadrainPercent"))) { @@ -1181,14 +968,9 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m for (auto summonNode : node.children()) { int32_t chance = 100; - int32_t speed = 1000; int32_t max = mType->info.maxSummons; bool force = false; - if ((attr = summonNode.attribute("speed")) || (attr = summonNode.attribute("interval"))) { - speed = std::max(1, pugi::cast(attr.value())); - } - if ((attr = summonNode.attribute("chance"))) { chance = pugi::cast(attr.value()); } @@ -1204,7 +986,6 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m if ((attr = summonNode.attribute("name"))) { summonBlock_t sb; sb.name = attr.as_string(); - sb.speed = speed; sb.chance = chance; sb.max = max; sb.force = force; @@ -1231,29 +1012,6 @@ MonsterType* Monsters::loadMonster(const std::string& file, const std::string& m mType->info.defenseSpells.shrink_to_fit(); mType->info.voiceVector.shrink_to_fit(); mType->info.scripts.shrink_to_fit(); - return mType; -} - -bool MonsterType::loadCallback(LuaScriptInterface* scriptInterface) -{ - int32_t id = scriptInterface->getEvent(); - if (id == -1) { - std::cout << "[Warning - MonsterType::loadCallback] Event not found. " << std::endl; - return false; - } - - info.scriptInterface = scriptInterface; - if (info.eventType == MONSTERS_EVENT_THINK) { - info.thinkEvent = id; - } else if (info.eventType == MONSTERS_EVENT_APPEAR) { - info.creatureAppearEvent = id; - } else if (info.eventType == MONSTERS_EVENT_DISAPPEAR) { - info.creatureDisappearEvent = id; - } else if (info.eventType == MONSTERS_EVENT_MOVE) { - info.creatureMoveEvent = id; - } else if (info.eventType == MONSTERS_EVENT_SAY) { - info.creatureSayEvent = id; - } return true; } @@ -1333,22 +1091,10 @@ void Monsters::loadLootContainer(const pugi::xml_node& node, LootBlock& lBlock) MonsterType* Monsters::getMonsterType(const std::string& name) { - std::string lowerCaseName = asLowerCaseString(name); + auto it = monsters.find(asLowerCaseString(name)); - auto it = monsters.find(lowerCaseName); if (it == monsters.end()) { - auto it2 = unloadedMonsters.find(lowerCaseName); - if (it2 == unloadedMonsters.end()) { - return nullptr; - } - - return loadMonster(it2->second, name); + return nullptr; } return &it->second; } - -void Monsters::addMonsterType(const std::string& name, MonsterType* mType) -{ - mType = &monsters[asLowerCaseString(name)]; -} - diff --git a/src/monsters.h b/src/monsters.h index 992373e..23906ab 100644 --- a/src/monsters.h +++ b/src/monsters.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,9 +22,8 @@ #include "creature.h" - -const uint32_t MAX_LOOTCHANCE = 100000; -const uint32_t MAX_STATICWALK = 100; +#define MAX_LOOTCHANCE 1000 +#define MAX_STATICWALK 100 struct LootBlock { uint16_t id; @@ -39,7 +38,7 @@ struct LootBlock { std::vector childLoot; LootBlock() { id = 0; - countmax = 1; + countmax = 0; chance = 0; subType = -1; @@ -47,21 +46,9 @@ struct LootBlock { } }; -class Loot { - public: - Loot() = default; - - // non-copyable - Loot(const Loot&) = delete; - Loot& operator=(const Loot&) = delete; - - LootBlock lootBlock; -}; - struct summonBlock_t { std::string name; uint32_t chance; - uint32_t speed; uint32_t max; bool force = false; }; @@ -75,23 +62,23 @@ struct spellBlock_t { spellBlock_t(spellBlock_t&& other) : spell(other.spell), chance(other.chance), - speed(other.speed), range(other.range), minCombatValue(other.minCombatValue), maxCombatValue(other.maxCombatValue), - combatSpell(other.combatSpell), - isMelee(other.isMelee) { + attack(other.attack), + skill(other.skill), + combatSpell(other.combatSpell) { other.spell = nullptr; } BaseSpell* spell = nullptr; uint32_t chance = 100; - uint32_t speed = 2000; uint32_t range = 0; int32_t minCombatValue = 0; int32_t maxCombatValue = 0; + int32_t attack = 0; + int32_t skill = 0; bool combatSpell = false; - bool isMelee = false; }; struct voiceBlock_t { @@ -124,14 +111,11 @@ class MonsterType uint64_t experience = 0; uint32_t manaCost = 0; - uint32_t yellChance = 0; - uint32_t yellSpeedTicks = 0; - uint32_t staticAttackChance = 95; uint32_t maxSummons = 0; uint32_t changeTargetSpeed = 0; uint32_t conditionImmunities = 0; uint32_t damageImmunities = 0; - uint32_t baseSpeed = 200; + uint32_t baseSpeed = 70; int32_t creatureAppearEvent = -1; int32_t creatureDisappearEvent = -1; @@ -143,8 +127,15 @@ class MonsterType int32_t health = 100; int32_t healthMax = 100; int32_t changeTargetChance = 0; - int32_t defense = 0; + int32_t strategyNearestEnemy = 0; + int32_t strategyWeakestEnemy = 0; + int32_t strategyMostDamageEnemy = 0; + int32_t strategyRandomEnemy = 0; int32_t armor = 0; + int32_t defense = 0; + int32_t attack = 0; + int32_t skill = 0; + int32_t poison = 0; bool canPushItems = false; bool canPushCreatures = false; @@ -155,11 +146,6 @@ class MonsterType bool isAttackable = true; bool isHostile = true; bool hiddenHealth = false; - bool canWalkOnEnergy = true; - bool canWalkOnFire = true; - bool canWalkOnPoison = true; - - MonstersEvent_t eventType = MONSTERS_EVENT_NONE; }; public: @@ -169,57 +155,14 @@ class MonsterType MonsterType(const MonsterType&) = delete; MonsterType& operator=(const MonsterType&) = delete; - bool loadCallback(LuaScriptInterface* scriptInterface); - std::string name; std::string nameDescription; MonsterInfo info; - void loadLoot(MonsterType* monsterType, LootBlock lootblock); -}; - -class MonsterSpell -{ - public: - MonsterSpell() = default; - - MonsterSpell(const MonsterSpell&) = delete; - MonsterSpell& operator=(const MonsterSpell&) = delete; - - std::string name = ""; - std::string scriptName = ""; - - uint8_t chance = 100; - uint8_t range = 0; - - uint16_t interval = 2000; - - int32_t minCombatValue = 0; - int32_t maxCombatValue = 0; - int32_t attack = 0; - int32_t skill = 0; - int32_t length = 0; - int32_t spread = 0; - int32_t radius = 0; - int32_t conditionMinDamage = 0; - int32_t conditionMaxDamage = 0; - int32_t conditionStartDamage = 0; - int32_t tickInterval = 0; - int32_t speedChange = 0; - int32_t duration = 0; - - bool isScripted = false; - bool needTarget = false; - bool needDirection = false; - bool combatSpell = false; - bool isMelee = false; - - Outfit_t outfit = {}; - ShootType_t shoot = CONST_ANI_NONE; - MagicEffectClasses effect = CONST_ME_NONE; - ConditionType_t conditionType = CONDITION_NONE; - CombatType_t combatType = COMBAT_UNDEFINEDDAMAGE; + void createLoot(Container* corpse); + bool createLootContainer(Container* parent, const LootBlock& lootblock); + std::vector createLootItem(const LootBlock& lootBlock); }; class Monsters @@ -237,23 +180,20 @@ class Monsters bool reload(); MonsterType* getMonsterType(const std::string& name); - void addMonsterType(const std::string& name, MonsterType* mType); - bool deserializeSpell(MonsterSpell* spell, spellBlock_t& sb, const std::string& description = ""); - std::unique_ptr scriptInterface; + static uint32_t getLootRandom(); private: - ConditionDamage* getDamageCondition(ConditionType_t conditionType, - int32_t maxDamage, int32_t minDamage, int32_t startDamage, uint32_t tickInterval); + ConditionDamage* getDamageCondition(ConditionType_t conditionType, int32_t cycles, int32_t count, int32_t max_count); bool deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, const std::string& description = ""); - MonsterType* loadMonster(const std::string& file, const std::string& monsterName, bool reloading = false); + bool loadMonster(const std::string& file, const std::string& monsterName, std::list>& monsterScriptList, bool reloading = false); void loadLootContainer(const pugi::xml_node& node, LootBlock&); bool loadLootItem(const pugi::xml_node& node, LootBlock&); std::map monsters; - std::map unloadedMonsters; + std::unique_ptr scriptInterface; bool loaded = false; }; diff --git a/src/mounts.cpp b/src/mounts.cpp deleted file mode 100644 index ce10d97..0000000 --- a/src/mounts.cpp +++ /dev/null @@ -1,82 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "mounts.h" - -#include "pugicast.h" -#include "tools.h" - -bool Mounts::reload() -{ - mounts.clear(); - return loadFromXml(); -} - -bool Mounts::loadFromXml() -{ - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file("data/XML/mounts.xml"); - if (!result) { - printXMLError("Error - Mounts::loadFromXml", "data/XML/mounts.xml", result); - return false; - } - - for (auto mountNode : doc.child("mounts").children()) { - mounts.emplace_back( - static_cast(pugi::cast(mountNode.attribute("id").value())), - pugi::cast(mountNode.attribute("clientid").value()), - mountNode.attribute("name").as_string(), - pugi::cast(mountNode.attribute("speed").value()), - mountNode.attribute("premium").as_bool() - ); - } - mounts.shrink_to_fit(); - return true; -} - -Mount* Mounts::getMountByID(uint8_t id) -{ - auto it = std::find_if(mounts.begin(), mounts.end(), [id](const Mount& mount) { - return mount.id == id; - }); - - return it != mounts.end() ? &*it : nullptr; -} - -Mount* Mounts::getMountByName(const std::string& name) { - auto mountName = name.c_str(); - for (auto& it : mounts) { - if (strcasecmp(mountName, it.name.c_str()) == 0) { - return ⁢ - } - } - - return nullptr; -} - -Mount* Mounts::getMountByClientID(uint16_t clientId) -{ - auto it = std::find_if(mounts.begin(), mounts.end(), [clientId](const Mount& mount) { - return mount.clientId == clientId; - }); - - return it != mounts.end() ? &*it : nullptr; -} diff --git a/src/mounts.h b/src/mounts.h deleted file mode 100644 index c63d425..0000000 --- a/src/mounts.h +++ /dev/null @@ -1,52 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6 -#define FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6 - -struct Mount -{ - Mount(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) : - name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {} - - std::string name; - int32_t speed; - uint16_t clientId; - uint8_t id; - bool premium; -}; - -class Mounts -{ - public: - bool reload(); - bool loadFromXml(); - Mount* getMountByID(uint8_t id); - Mount* getMountByName(const std::string& name); - Mount* getMountByClientID(uint16_t clientId); - - const std::vector& getMounts() const { - return mounts; - } - - private: - std::vector mounts; -}; - -#endif diff --git a/src/movement.cpp b/src/movement.cpp index c59c772..0dc15bf 100644 --- a/src/movement.cpp +++ b/src/movement.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,49 +36,43 @@ MoveEvents::MoveEvents() : MoveEvents::~MoveEvents() { - clear(false); + clear(); } -void MoveEvents::clearMap(MoveListMap& map, bool fromLua) +void MoveEvents::clearMap(MoveListMap& map) { - for (auto it = map.begin(); it != map.end(); ++it) { - for (int eventType = MOVE_EVENT_STEP_IN; eventType < MOVE_EVENT_LAST; ++eventType) { - auto& moveEvents = it->second.moveEvent[eventType]; - for (auto find = moveEvents.begin(); find != moveEvents.end(); ) { - if (fromLua == find->fromLua) { - find = moveEvents.erase(find); - } else { - ++find; - } + std::unordered_set set; + for (const auto& it : map) { + const MoveEventList& moveEventList = it.second; + for (const auto& i : moveEventList.moveEvent) { + for (MoveEvent* moveEvent : i) { + set.insert(moveEvent); } } } -} + map.clear(); -void MoveEvents::clearPosMap(MovePosListMap& map, bool fromLua) -{ - for (auto it = map.begin(); it != map.end(); ++it) { - for (int eventType = MOVE_EVENT_STEP_IN; eventType < MOVE_EVENT_LAST; ++eventType) { - auto& moveEvents = it->second.moveEvent[eventType]; - for (auto find = moveEvents.begin(); find != moveEvents.end(); ) { - if (fromLua == find->fromLua) { - find = moveEvents.erase(find); - } else { - ++find; - } - } - } + for (MoveEvent* moveEvent : set) { + delete moveEvent; } } -void MoveEvents::clear(bool fromLua) +void MoveEvents::clear() { - clearMap(itemIdMap, fromLua); - clearMap(actionIdMap, fromLua); - clearMap(uniqueIdMap, fromLua); - clearPosMap(positionMap, fromLua); + clearMap(itemIdMap); + clearMap(movementIdMap); - reInitState(fromLua); + for (const auto& it : positionMap) { + const MoveEventList& moveEventList = it.second; + for (const auto& i : moveEventList.moveEvent) { + for (MoveEvent* moveEvent : i) { + delete moveEvent; + } + } + } + positionMap.clear(); + + scriptInterface.reInitState(); } LuaScriptInterface& MoveEvents::getScriptInterface() @@ -91,17 +85,17 @@ std::string MoveEvents::getScriptBaseName() const return "movements"; } -Event_ptr MoveEvents::getEvent(const std::string& nodeName) +Event* MoveEvents::getEvent(const std::string& nodeName) { if (strcasecmp(nodeName.c_str(), "movevent") != 0) { return nullptr; } - return Event_ptr(new MoveEvent(&scriptInterface)); + return new MoveEvent(&scriptInterface); } -bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node) +bool MoveEvents::registerEvent(Event* event, const pugi::xml_node& node) { - MoveEvent_ptr moveEvent{static_cast(event.release())}; //event is guaranteed to be a MoveEvent + MoveEvent* moveEvent = static_cast(event); //event is guaranteed to be a MoveEvent const MoveEvent_t eventType = moveEvent->getEventType(); if (eventType == MOVE_EVENT_ADD_ITEM || eventType == MOVE_EVENT_REMOVE_ITEM) { @@ -123,6 +117,7 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node) pugi::xml_attribute attr; if ((attr = node.attribute("itemid"))) { int32_t id = pugi::cast(attr.value()); + addEvent(moveEvent, id, itemIdMap); if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { ItemType& it = Item::items.getItemType(id); it.wieldInfo = moveEvent->getWieldInfo(); @@ -130,12 +125,11 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node) it.minReqMagicLevel = moveEvent->getReqMagLv(); it.vocationString = moveEvent->getVocationString(); } - addEvent(std::move(*moveEvent), id, itemIdMap); } else if ((attr = node.attribute("fromid"))) { uint32_t id = pugi::cast(attr.value()); uint32_t endId = pugi::cast(node.attribute("toid").value()); - addEvent(*moveEvent, id, itemIdMap); + addEvent(moveEvent, id, itemIdMap); if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { ItemType& it = Item::items.getItemType(id); @@ -145,7 +139,7 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node) it.vocationString = moveEvent->getVocationString(); while (++id <= endId) { - addEvent(*moveEvent, id, itemIdMap); + addEvent(moveEvent, id, itemIdMap); ItemType& tit = Item::items.getItemType(id); tit.wieldInfo = moveEvent->getWieldInfo(); @@ -155,26 +149,17 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node) } } else { while (++id <= endId) { - addEvent(*moveEvent, id, itemIdMap); + addEvent(moveEvent, id, itemIdMap); } } - } else if ((attr = node.attribute("uniqueid"))) { - addEvent(std::move(*moveEvent), pugi::cast(attr.value()), uniqueIdMap); - } else if ((attr = node.attribute("fromuid"))) { + } else if ((attr = node.attribute("movementid"))) { + addEvent(moveEvent, pugi::cast(attr.value()), movementIdMap); + } else if ((attr = node.attribute("frommovementid"))) { uint32_t id = pugi::cast(attr.value()); - uint32_t endId = pugi::cast(node.attribute("touid").value()); - addEvent(*moveEvent, id, uniqueIdMap); + uint32_t endId = pugi::cast(node.attribute("tomovementid").value()); + addEvent(moveEvent, id, movementIdMap); while (++id <= endId) { - addEvent(*moveEvent, id, uniqueIdMap); - } - } else if ((attr = node.attribute("actionid"))) { - addEvent(std::move(*moveEvent), pugi::cast(attr.value()), actionIdMap); - } else if ((attr = node.attribute("fromaid"))) { - uint32_t id = pugi::cast(attr.value()); - uint32_t endId = pugi::cast(node.attribute("toaid").value()); - addEvent(*moveEvent, id, actionIdMap); - while (++id <= endId) { - addEvent(*moveEvent, id, actionIdMap); + addEvent(moveEvent, id, movementIdMap); } } else if ((attr = node.attribute("pos"))) { std::vector posList = vectorAtoi(explodeString(attr.as_string(), ";")); @@ -183,125 +168,28 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node) } Position pos(posList[0], posList[1], posList[2]); - addEvent(std::move(*moveEvent), pos, positionMap); + addEvent(moveEvent, pos, positionMap); } else { return false; } return true; } -bool MoveEvents::registerLuaFunction(MoveEvent* event) -{ - MoveEvent_ptr moveEvent{ event }; - if (moveEvent->getItemIdRange().size() > 0) { - if (moveEvent->getItemIdRange().size() == 1) { - uint32_t id = moveEvent->getItemIdRange().at(0); - addEvent(*moveEvent, id, itemIdMap); - if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { - ItemType& it = Item::items.getItemType(id); - it.wieldInfo = moveEvent->getWieldInfo(); - it.minReqLevel = moveEvent->getReqLevel(); - it.minReqMagicLevel = moveEvent->getReqMagLv(); - it.vocationString = moveEvent->getVocationString(); - } - } else { - uint32_t iterId = 0; - while (++iterId < moveEvent->getItemIdRange().size()) { - if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { - ItemType& it = Item::items.getItemType(moveEvent->getItemIdRange().at(iterId)); - it.wieldInfo = moveEvent->getWieldInfo(); - it.minReqLevel = moveEvent->getReqLevel(); - it.minReqMagicLevel = moveEvent->getReqMagLv(); - it.vocationString = moveEvent->getVocationString(); - } - addEvent(*moveEvent, moveEvent->getItemIdRange().at(iterId), itemIdMap); - } - } - } else { - return false; - } - return true; -} - -bool MoveEvents::registerLuaEvent(MoveEvent* event) -{ - MoveEvent_ptr moveEvent{ event }; - if (moveEvent->getItemIdRange().size() > 0) { - if (moveEvent->getItemIdRange().size() == 1) { - uint32_t id = moveEvent->getItemIdRange().at(0); - addEvent(*moveEvent, id, itemIdMap); - if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { - ItemType& it = Item::items.getItemType(id); - it.wieldInfo = moveEvent->getWieldInfo(); - it.minReqLevel = moveEvent->getReqLevel(); - it.minReqMagicLevel = moveEvent->getReqMagLv(); - it.vocationString = moveEvent->getVocationString(); - } - } else { - auto v = moveEvent->getItemIdRange(); - for (auto i = v.begin(); i != v.end(); i++) { - if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { - ItemType& it = Item::items.getItemType(*i); - it.wieldInfo = moveEvent->getWieldInfo(); - it.minReqLevel = moveEvent->getReqLevel(); - it.minReqMagicLevel = moveEvent->getReqMagLv(); - it.vocationString = moveEvent->getVocationString(); - } - addEvent(*moveEvent, *i, itemIdMap); - } - } - } else if (moveEvent->getActionIdRange().size() > 0) { - if (moveEvent->getActionIdRange().size() == 1) { - int32_t id = moveEvent->getActionIdRange().at(0); - addEvent(*moveEvent, id, actionIdMap); - } else { - auto v = moveEvent->getActionIdRange(); - for (auto i = v.begin(); i != v.end(); i++) { - addEvent(*moveEvent, *i, actionIdMap); - } - } - } else if (moveEvent->getUniqueIdRange().size() > 0) { - if (moveEvent->getUniqueIdRange().size() == 1) { - int32_t id = moveEvent->getUniqueIdRange().at(0); - addEvent(*moveEvent, id, uniqueIdMap); - } else { - auto v = moveEvent->getUniqueIdRange(); - for (auto i = v.begin(); i != v.end(); i++) { - addEvent(*moveEvent, *i, uniqueIdMap); - } - } - } else if (moveEvent->getPosList().size() > 0) { - if (moveEvent->getPosList().size() == 1) { - Position pos = moveEvent->getPosList().at(0); - addEvent(*moveEvent, pos, positionMap); - } else { - auto v = moveEvent->getPosList(); - for (auto i = v.begin(); i != v.end(); i++) { - addEvent(*moveEvent, *i, positionMap); - } - } - } else { - return false; - } - - return true; -} - -void MoveEvents::addEvent(MoveEvent moveEvent, int32_t id, MoveListMap& map) +void MoveEvents::addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map) { auto it = map.find(id); if (it == map.end()) { MoveEventList moveEventList; - moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent)); + moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent); map[id] = moveEventList; } else { - std::list& moveEventList = it->second.moveEvent[moveEvent.getEventType()]; - for (MoveEvent& existingMoveEvent : moveEventList) { - if (existingMoveEvent.getSlot() == moveEvent.getSlot()) { + std::list& moveEventList = it->second.moveEvent[moveEvent->getEventType()]; + for (MoveEvent* existingMoveEvent : moveEventList) { + if (existingMoveEvent->getSlot() == moveEvent->getSlot()) { std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << id << std::endl; } } - moveEventList.push_back(std::move(moveEvent)); + moveEventList.push_back(moveEvent); } } @@ -324,64 +212,59 @@ MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType, slots_t slot) auto it = itemIdMap.find(item->getID()); if (it != itemIdMap.end()) { - std::list& moveEventList = it->second.moveEvent[eventType]; - for (MoveEvent& moveEvent : moveEventList) { - if ((moveEvent.getSlot() & slotp) != 0) { - return &moveEvent; + std::list& moveEventList = it->second.moveEvent[eventType]; + for (MoveEvent* moveEvent : moveEventList) { + if ((moveEvent->getSlot() & slotp) != 0) { + return moveEvent; } } } return nullptr; } -MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType) +MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType) { MoveListMap::iterator it; - if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { - it = uniqueIdMap.find(item->getUniqueId()); - if (it != uniqueIdMap.end()) { - std::list& moveEventList = it->second.moveEvent[eventType]; + if (item->hasAttribute(ITEM_ATTRIBUTE_MOVEMENTID)) { + it = movementIdMap.find(item->getMovementId()); + if (it != movementIdMap.end()) { + std::list& moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { - return &(*moveEventList.begin()); + return *moveEventList.begin(); } } } - if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) { - it = actionIdMap.find(item->getActionId()); - if (it != actionIdMap.end()) { - std::list& moveEventList = it->second.moveEvent[eventType]; - if (!moveEventList.empty()) { - return &(*moveEventList.begin()); - } - } + if (!item->hasCollisionEvent() && !item->hasSeparationEvent()) { + return nullptr; } it = itemIdMap.find(item->getID()); if (it != itemIdMap.end()) { - std::list& moveEventList = it->second.moveEvent[eventType]; + std::list& moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { - return &(*moveEventList.begin()); + return *moveEventList.begin(); } } + return nullptr; } -void MoveEvents::addEvent(MoveEvent moveEvent, const Position& pos, MovePosListMap& map) +void MoveEvents::addEvent(MoveEvent* moveEvent, const Position& pos, MovePosListMap& map) { auto it = map.find(pos); if (it == map.end()) { MoveEventList moveEventList; - moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent)); + moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent); map[pos] = moveEventList; } else { - std::list& moveEventList = it->second.moveEvent[moveEvent.getEventType()]; + std::list& moveEventList = it->second.moveEvent[moveEvent->getEventType()]; if (!moveEventList.empty()) { std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << pos << std::endl; } - moveEventList.push_back(std::move(moveEvent)); + moveEventList.push_back(moveEvent); } } @@ -389,9 +272,9 @@ MoveEvent* MoveEvents::getEvent(const Tile* tile, MoveEvent_t eventType) { auto it = positionMap.find(tile->getPosition()); if (it != positionMap.end()) { - std::list& moveEventList = it->second.moveEvent[eventType]; + std::list& moveEventList = it->second.moveEvent[eventType]; if (!moveEventList.empty()) { - return &(*moveEventList.begin()); + return *moveEventList.begin(); } } return nullptr; @@ -442,7 +325,7 @@ uint32_t MoveEvents::onPlayerDeEquip(Player* player, Item* item, slots_t slot) if (!moveEvent) { return 1; } - return moveEvent->fireEquip(player, item, slot, false); + return moveEvent->fireEquip(player, item, slot, true); } uint32_t MoveEvents::onItemMove(Item* item, Tile* tile, bool isAdd) @@ -488,6 +371,20 @@ uint32_t MoveEvents::onItemMove(Item* item, Tile* tile, bool isAdd) MoveEvent::MoveEvent(LuaScriptInterface* interface) : Event(interface) {} +MoveEvent::MoveEvent(const MoveEvent* copy) : + Event(copy), + eventType(copy->eventType), + stepFunction(copy->stepFunction), + moveFunction(copy->moveFunction), + equipFunction(copy->equipFunction), + slot(copy->slot), + reqLevel(copy->reqLevel), + reqMagLevel(copy->reqMagLevel), + premium(copy->premium), + vocationString(copy->vocationString), + wieldInfo(copy->wieldInfo), + vocEquipMap(copy->vocEquipMap) {} + std::string MoveEvent::getScriptEventName() const { switch (eventType) { @@ -624,6 +521,40 @@ bool MoveEvent::configureEvent(const pugi::xml_node& node) return true; } +bool MoveEvent::loadFunction(const pugi::xml_attribute& attr) +{ + const char* functionName = attr.as_string(); + if (strcasecmp(functionName, "onstepinfield") == 0) { + stepFunction = StepInField; + } else if (strcasecmp(functionName, "onstepoutfield") == 0) { + stepFunction = StepOutField; + } else if (strcasecmp(functionName, "onaddfield") == 0) { + moveFunction = AddItemField; + } else if (strcasecmp(functionName, "onremovefield") == 0) { + moveFunction = RemoveItemField; + } else if (strcasecmp(functionName, "onequipitem") == 0) { + equipFunction = EquipItem; + } else if (strcasecmp(functionName, "ondeequipitem") == 0) { + equipFunction = DeEquipItem; + } else { + std::cout << "[Warning - MoveEvent::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + return false; + } + + scripted = false; + return true; +} + +MoveEvent_t MoveEvent::getEventType() const +{ + return eventType; +} + +void MoveEvent::setEventType(MoveEvent_t type) +{ + eventType = type; +} + uint32_t MoveEvent::StepInField(Creature* creature, Item* item, const Position&) { MagicField* field = item->getMagicField(); @@ -715,6 +646,10 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, player->sendIcons(); } + if (it.abilities->absorbPercent[combatTypeToIndex(COMBAT_DROWNDAMAGE)] == 100) { + player->removeCondition(CONDITION_DROWN); + } + if (it.abilities->regeneration) { Condition* condition = Condition::createCondition(static_cast(slot), CONDITION_REGENERATION, -1, 0); @@ -747,13 +682,6 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, } } - for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { - if (it.abilities->specialSkills[i]) { - needUpdateSkills = true; - player->setVarSpecialSkill(static_cast(i), it.abilities->specialSkills[i]); - } - } - if (needUpdateSkills) { player->sendSkills(); } @@ -829,13 +757,6 @@ uint32_t MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots_t } } - for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { - if (it.abilities->specialSkills[i] != 0) { - needUpdateSkills = true; - player->setVarSpecialSkill(static_cast(i), -it.abilities->specialSkills[i]); - } - } - if (needUpdateSkills) { player->sendSkills(); } @@ -862,44 +783,6 @@ uint32_t MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots_t return 1; } -bool MoveEvent::loadFunction(const pugi::xml_attribute& attr, bool isScripted) -{ - const char* functionName = attr.as_string(); - if (strcasecmp(functionName, "onstepinfield") == 0) { - stepFunction = StepInField; - } else if (strcasecmp(functionName, "onstepoutfield") == 0) { - stepFunction = StepOutField; - } else if (strcasecmp(functionName, "onaddfield") == 0) { - moveFunction = AddItemField; - } else if (strcasecmp(functionName, "onremovefield") == 0) { - moveFunction = RemoveItemField; - } else if (strcasecmp(functionName, "onequipitem") == 0) { - equipFunction = EquipItem; - } else if (strcasecmp(functionName, "ondeequipitem") == 0) { - equipFunction = DeEquipItem; - } else { - if (!isScripted) { - std::cout << "[Warning - MoveEvent::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; - return false; - } - } - - if (!isScripted) { - scripted = false; - } - return true; -} - -MoveEvent_t MoveEvent::getEventType() const -{ - return eventType; -} - -void MoveEvent::setEventType(MoveEvent_t type) -{ - eventType = type; -} - uint32_t MoveEvent::fireStepEvent(Creature* creature, Item* item, const Position& pos) { if (scripted) { @@ -933,24 +816,19 @@ bool MoveEvent::executeStep(Creature* creature, Item* item, const Position& pos) return scriptInterface->callFunction(4); } -uint32_t MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool isCheck) +uint32_t MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool boolean) { if (scripted) { - if (!equipFunction || equipFunction(this, player, item, slot, isCheck) == 1) { - if (executeEquip(player, item, slot, isCheck)) { - return 1; - } - } - return 0; + return executeEquip(player, item, slot); } else { - return equipFunction(this, player, item, slot, isCheck); + return equipFunction(this, player, item, slot, boolean); } } -bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool isCheck) +bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot) { - //onEquip(player, item, slot, isCheck) - //onDeEquip(player, item, slot, isCheck) + //onEquip(player, item, slot) + //onDeEquip(player, item, slot) if (!scriptInterface->reserveScriptEnv()) { std::cout << "[Error - MoveEvent::executeEquip] Call stack overflow" << std::endl; return false; @@ -966,9 +844,8 @@ bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool isCh LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::pushThing(L, item); lua_pushnumber(L, slot); - LuaScriptInterface::pushBoolean(L, isCheck); - return scriptInterface->callFunction(4); + return scriptInterface->callFunction(3); } uint32_t MoveEvent::fireAddRemItem(Item* item, Item* tileItem, const Position& pos) diff --git a/src/movement.h b/src/movement.h index 27ed82f..d8a67e4 100644 --- a/src/movement.h +++ b/src/movement.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +23,6 @@ #include "baseevents.h" #include "item.h" #include "luascript.h" -#include "vocation.h" - -extern Vocations g_vocations; enum MoveEvent_t { MOVE_EVENT_STEP_IN, @@ -42,13 +39,12 @@ enum MoveEvent_t { }; class MoveEvent; -using MoveEvent_ptr = std::unique_ptr; struct MoveEventList { - std::list moveEvent[MOVE_EVENT_LAST]; + std::list moveEvent[MOVE_EVENT_LAST]; }; -using VocEquipMap = std::map; +typedef std::map VocEquipMap; class MoveEvents final : public BaseEvents { @@ -67,54 +63,51 @@ class MoveEvents final : public BaseEvents MoveEvent* getEvent(Item* item, MoveEvent_t eventType); - bool registerLuaEvent(MoveEvent* event); - bool registerLuaFunction(MoveEvent* event); - void clear(bool fromLua) override final; + protected: + typedef std::map MoveListMap; + void clearMap(MoveListMap& map); - private: - using MoveListMap = std::map; - using MovePosListMap = std::map; - void clearMap(MoveListMap& map, bool fromLua); - void clearPosMap(MovePosListMap& map, bool fromLua); + typedef std::map MovePosListMap; + void clear() final; + LuaScriptInterface& getScriptInterface() final; + std::string getScriptBaseName() const final; + Event* getEvent(const std::string& nodeName) final; + bool registerEvent(Event* event, const pugi::xml_node& node) final; - LuaScriptInterface& getScriptInterface() override; - std::string getScriptBaseName() const override; - Event_ptr getEvent(const std::string& nodeName) override; - bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; + void addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map); - void addEvent(MoveEvent moveEvent, int32_t id, MoveListMap& map); - - void addEvent(MoveEvent moveEvent, const Position& pos, MovePosListMap& map); + void addEvent(MoveEvent* moveEvent, const Position& pos, MovePosListMap& map); MoveEvent* getEvent(const Tile* tile, MoveEvent_t eventType); MoveEvent* getEvent(Item* item, MoveEvent_t eventType, slots_t slot); - MoveListMap uniqueIdMap; - MoveListMap actionIdMap; + + MoveListMap movementIdMap; MoveListMap itemIdMap; MovePosListMap positionMap; LuaScriptInterface scriptInterface; }; -using StepFunction = std::function; -using MoveFunction = std::function; -using EquipFunction = std::function; +typedef uint32_t (StepFunction)(Creature* creature, Item* item, const Position& pos); +typedef uint32_t (MoveFunction)(Item* item, Item* tileItem, const Position& pos); +typedef uint32_t (EquipFunction)(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); class MoveEvent final : public Event { public: explicit MoveEvent(LuaScriptInterface* interface); + explicit MoveEvent(const MoveEvent* copy); MoveEvent_t getEventType() const; void setEventType(MoveEvent_t type); - bool configureEvent(const pugi::xml_node& node) override; - bool loadFunction(const pugi::xml_attribute& attr, bool isScripted) override; + bool configureEvent(const pugi::xml_node& node) final; + bool loadFunction(const pugi::xml_attribute& attr) final; uint32_t fireStepEvent(Creature* creature, Item* item, const Position& pos); uint32_t fireAddRemItem(Item* item, Item* tileItem, const Position& pos); - uint32_t fireEquip(Player* player, Item* item, slots_t slot, bool isCheck); + uint32_t fireEquip(Player* player, Item* item, slots_t slot, bool boolean); uint32_t getSlot() const { return slot; @@ -122,7 +115,7 @@ class MoveEvent final : public Event //scripting bool executeStep(Creature* creature, Item* item, const Position& pos); - bool executeEquip(Player* player, Item* item, slots_t slot, bool isCheck); + bool executeEquip(Player* player, Item* item, slots_t slot); bool executeAddRemItem(Item* item, Item* tileItem, const Position& pos); // @@ -139,104 +132,29 @@ class MoveEvent final : public Event const std::string& getVocationString() const { return vocationString; } - void setVocationString(const std::string& str) { - vocationString = str; - } uint32_t getWieldInfo() const { return wieldInfo; } const VocEquipMap& getVocEquipMap() const { return vocEquipMap; } - void addVocEquipMap(std::string vocName) { - int32_t vocationId = g_vocations.getVocationId(vocName); - if (vocationId != -1) { - vocEquipMap[vocationId] = true; - } - } - bool getTileItem() const { - return tileItem; - } - void setTileItem(bool b) { - tileItem = b; - } - std::vector getItemIdRange() { - return itemIdRange; - } - void addItemId(uint32_t id) { - itemIdRange.emplace_back(id); - } - std::vector getActionIdRange() { - return actionIdRange; - } - void addActionId(uint32_t id) { - actionIdRange.emplace_back(id); - } - std::vector getUniqueIdRange() { - return uniqueIdRange; - } - void addUniqueId(uint32_t id) { - uniqueIdRange.emplace_back(id); - } - std::vector getPosList() { - return posList; - } - void addPosList(Position pos) { - posList.emplace_back(pos); - } - std::string getSlotName() { - return slotName; - } - void setSlotName(std::string name) { - slotName = name; - } - void setSlot(uint32_t s) { - slot = s; - } - uint32_t getRequiredLevel() { - return reqLevel; - } - void setRequiredLevel(uint32_t level) { - reqLevel = level; - } - uint32_t getRequiredMagLevel() { - return reqMagLevel; - } - void setRequiredMagLevel(uint32_t level) { - reqMagLevel = level; - } - bool needPremium() { - return premium; - } - void setNeedPremium(bool b) { - premium = b; - } - uint32_t getWieldInfo() { - return wieldInfo; - } - void setWieldInfo(WieldInfo_t info) { - wieldInfo |= info; - } - static uint32_t StepInField(Creature* creature, Item* item, const Position& pos); - static uint32_t StepOutField(Creature* creature, Item* item, const Position& pos); + protected: + std::string getScriptEventName() const final; - static uint32_t AddItemField(Item* item, Item* tileItem, const Position& pos); - static uint32_t RemoveItemField(Item* item, Item* tileItem, const Position& pos); + static StepFunction StepInField; + static StepFunction StepOutField; - static uint32_t EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); - static uint32_t DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); + static MoveFunction AddItemField; + static MoveFunction RemoveItemField; + static EquipFunction EquipItem; + static EquipFunction DeEquipItem; MoveEvent_t eventType = MOVE_EVENT_NONE; - StepFunction stepFunction; - MoveFunction moveFunction; - EquipFunction equipFunction; - - private: - std::string getScriptEventName() const override; - + StepFunction* stepFunction = nullptr; + MoveFunction* moveFunction = nullptr; + EquipFunction* equipFunction = nullptr; uint32_t slot = SLOTP_WHEREEVER; - std::string slotName; //onEquip information uint32_t reqLevel = 0; @@ -245,12 +163,6 @@ class MoveEvent final : public Event std::string vocationString; uint32_t wieldInfo = 0; VocEquipMap vocEquipMap; - bool tileItem = false; - - std::vector itemIdRange; - std::vector actionIdRange; - std::vector uniqueIdRange; - std::vector posList; }; #endif diff --git a/src/networkmessage.cpp b/src/networkmessage.cpp index b9be9f0..7f02803 100644 --- a/src/networkmessage.cpp +++ b/src/networkmessage.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -99,18 +99,16 @@ void NetworkMessage::addItem(uint16_t id, uint8_t count) { const ItemType& it = Item::items[id]; - add(it.clientId); - - addByte(0xFF); // MARK_UNMARKED + if (it.disguise) { + add(it.disguiseId); + } else { + add(it.id); + } if (it.stackable) { addByte(count); } else if (it.isSplash() || it.isFluidContainer()) { - addByte(fluidMap[count & 7]); - } - - if (it.isAnimation) { - addByte(0xFE); // random phase (0xFF for async) + addByte(getLiquidColor(count)); } } @@ -118,21 +116,20 @@ void NetworkMessage::addItem(const Item* item) { const ItemType& it = Item::items[item->getID()]; - add(it.clientId); - addByte(0xFF); // MARK_UNMARKED + if (it.disguise) { + add(it.disguiseId); + } else { + add(it.id); + } if (it.stackable) { addByte(std::min(0xFF, item->getItemCount())); } else if (it.isSplash() || it.isFluidContainer()) { - addByte(fluidMap[item->getFluidType() & 7]); - } - - if (it.isAnimation) { - addByte(0xFE); // random phase (0xFF for async) + addByte(getLiquidColor(item->getFluidType())); } } void NetworkMessage::addItemId(uint16_t itemId) { - add(Item::items[itemId].clientId); + add(itemId); } diff --git a/src/networkmessage.h b/src/networkmessage.h index 7118f44..7d11153 100644 --- a/src/networkmessage.h +++ b/src/networkmessage.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,16 +31,14 @@ class RSA; class NetworkMessage { public: - using MsgSize_t = uint16_t; + typedef uint16_t MsgSize_t; // Headers: // 2 bytes for unencrypted message size - // 4 bytes for checksum // 2 bytes for encrypted message size - static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 8; + static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 4; enum { HEADER_LENGTH = 2 }; - enum { CHECKSUM_LENGTH = 4 }; enum { XTEA_MULTIPLE = 8 }; - enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - CHECKSUM_LENGTH - XTEA_MULTIPLE }; + enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - XTEA_MULTIPLE }; enum { MAX_PROTOCOL_BODY_LENGTH = MAX_BODY_LENGTH - 10 }; NetworkMessage() = default; @@ -150,6 +148,18 @@ class NetworkMessage } protected: + inline bool canAdd(size_t size) const { + return (size + info.position) < MAX_BODY_LENGTH; + } + + inline bool canRead(int32_t size) { + if ((info.position + size) > (info.length + 8) || size >= (NETWORKMESSAGE_MAXSIZE - info.position)) { + info.overrun = true; + return false; + } + return true; + } + struct NetworkMessageInfo { MsgSize_t length = 0; MsgSize_t position = INITIAL_BUFFER_POSITION; @@ -158,19 +168,6 @@ class NetworkMessage NetworkMessageInfo info; uint8_t buffer[NETWORKMESSAGE_MAXSIZE]; - - private: - bool canAdd(size_t size) const { - return (size + info.position) < MAX_BODY_LENGTH; - } - - bool canRead(int32_t size) { - if ((info.position + size) > (info.length + 8) || size >= (NETWORKMESSAGE_MAXSIZE - info.position)) { - info.overrun = true; - return false; - } - return true; - } }; #endif // #ifndef __NETWORK_MESSAGE_H__ diff --git a/src/npc.cpp b/src/npc.cpp index f090ff9..4d811e7 100644 --- a/src/npc.cpp +++ b/src/npc.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,23 +21,43 @@ #include "npc.h" #include "game.h" -#include "pugicast.h" +#include "tools.h" +#include "position.h" +#include "player.h" +#include "spawn.h" +#include "script.h" +#include "behaviourdatabase.h" extern Game g_game; -extern LuaEnvironment g_luaEnvironment; uint32_t Npc::npcAutoID = 0x80000000; -NpcScriptInterface* Npc::scriptInterface = nullptr; + +void Npcs::loadNpcs() +{ + std::cout << ">> Loading npcs..." << std::endl; + + std::vector files; + getFilesInDirectory("data/npc/", ".npc", files); + for (auto file : files) + { + std::string npcName = file.filename().string(); + int32_t end = npcName.find_first_of('/'); + npcName = npcName.substr(end + 1, npcName.length() - end); + end = npcName.find_first_of('.'); + npcName = npcName.substr(0, end); + + Npc* npc = Npc::createNpc(npcName); + if (!npc) { + return; + } + + g_game.placeCreature(npc, npc->getMasterPos(), false, true); + } +} void Npcs::reload() { const std::map& npcs = g_game.getNpcs(); - for (const auto& it : npcs) { - it.second->closeAllShopWindows(); - } - - delete Npc::scriptInterface; - Npc::scriptInterface = nullptr; for (const auto& it : npcs) { it.second->reload(); @@ -47,19 +67,23 @@ void Npcs::reload() Npc* Npc::createNpc(const std::string& name) { std::unique_ptr npc(new Npc(name)); + npc->filename = "data/npc/" + name + ".npc"; if (!npc->load()) { return nullptr; } + return npc.release(); } Npc::Npc(const std::string& name) : Creature(), - filename("data/npc/" + name + ".xml"), - npcEventHandler(nullptr), - masterRadius(-1), - loaded(false) + filename("data/npc/" + name + ".npc"), + masterRadius(0), + staticMovementTime(0), + loaded(false), + behaviourDatabase(nullptr) { + baseSpeed = 5; reset(); } @@ -86,147 +110,89 @@ bool Npc::load() reset(); - if (!scriptInterface) { - scriptInterface = new NpcScriptInterface(); - scriptInterface->loadNpcLib("data/npc/lib/npc.lua"); + ScriptReader script; + if (!script.open(filename)) { + return false; } - loaded = loadFromXml(); - return loaded; + while (true) { + script.nextToken(); + + if (script.Token == ENDOFFILE) { + break; + } + + if (script.Token != IDENTIFIER) { + script.error("identifier expected"); + return false; + } + + std::string ident = script.getIdentifier(); + script.readSymbol('='); + + if (ident == "name") { + name = script.readString(); + } else if (ident == "outfit") { + script.readSymbol('('); + uint8_t* c; + currentOutfit.lookType = script.readNumber(); + script.readSymbol(','); + if (currentOutfit.lookType > 0) { + c = script.readBytesequence(); + currentOutfit.lookHead = c[0]; + currentOutfit.lookBody = c[1]; + currentOutfit.lookLegs = c[2]; + currentOutfit.lookFeet = c[3]; + currentOutfit.lookAddons = c[4]; + } else { + currentOutfit.lookTypeEx = script.readNumber(); + } + script.readSymbol(')'); + } else if (ident == "home") { + script.readCoordinate(masterPos.x, masterPos.y, masterPos.z); + } else if (ident == "radius") { + masterRadius = script.readNumber(); + } else if (ident == "behaviour") { + if (behaviourDatabase) { + script.error("behaviour database already defined"); + return false; + } + + behaviourDatabase = new BehaviourDatabase(this); + if (!behaviourDatabase->loadDatabase(script)) { + return false; + } + } + } + + script.close(); + return true; } void Npc::reset() { loaded = false; - walkTicks = 1500; - pushable = true; - floorChange = false; - attackable = false; - ignoreHeight = true; focusCreature = 0; - speechBubble = SPEECHBUBBLE_NONE; + conversationEndTime = 0; - delete npcEventHandler; - npcEventHandler = nullptr; - - parameters.clear(); - shopPlayerSet.clear(); + if (behaviourDatabase) { + delete behaviourDatabase; + behaviourDatabase = nullptr; + } } void Npc::reload() { + loaded = false; + reset(); load(); - // Simulate that the creature is placed on the map again. - if (npcEventHandler) { - npcEventHandler->onCreatureAppear(this); - } - - if (walkTicks > 0) { + if (baseSpeed > 0) { addEventWalk(); } } -bool Npc::loadFromXml() -{ - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file(filename.c_str()); - if (!result) { - printXMLError("Error - Npc::loadFromXml", filename, result); - return false; - } - - pugi::xml_node npcNode = doc.child("npc"); - if (!npcNode) { - std::cout << "[Error - Npc::loadFromXml] Missing npc tag in " << filename << std::endl; - return false; - } - - name = npcNode.attribute("name").as_string(); - attackable = npcNode.attribute("attackable").as_bool(); - floorChange = npcNode.attribute("floorchange").as_bool(); - - pugi::xml_attribute attr; - if ((attr = npcNode.attribute("speed"))) { - baseSpeed = pugi::cast(attr.value()); - } else { - baseSpeed = 100; - } - - if ((attr = npcNode.attribute("pushable"))) { - pushable = attr.as_bool(); - } - - if ((attr = npcNode.attribute("walkinterval"))) { - walkTicks = pugi::cast(attr.value()); - } - - if ((attr = npcNode.attribute("walkradius"))) { - masterRadius = pugi::cast(attr.value()); - } - - if ((attr = npcNode.attribute("ignoreheight"))) { - ignoreHeight = attr.as_bool(); - } - - if ((attr = npcNode.attribute("speechbubble"))) { - speechBubble = pugi::cast(attr.value()); - } - - if ((attr = npcNode.attribute("skull"))) { - setSkull(getSkullType(asLowerCaseString(attr.as_string()))); - } - - pugi::xml_node healthNode = npcNode.child("health"); - if (healthNode) { - if ((attr = healthNode.attribute("now"))) { - health = pugi::cast(attr.value()); - } else { - health = 100; - } - - if ((attr = healthNode.attribute("max"))) { - healthMax = pugi::cast(attr.value()); - } else { - healthMax = 100; - } - } - - pugi::xml_node lookNode = npcNode.child("look"); - if (lookNode) { - pugi::xml_attribute lookTypeAttribute = lookNode.attribute("type"); - if (lookTypeAttribute) { - defaultOutfit.lookType = pugi::cast(lookTypeAttribute.value()); - defaultOutfit.lookHead = pugi::cast(lookNode.attribute("head").value()); - defaultOutfit.lookBody = pugi::cast(lookNode.attribute("body").value()); - defaultOutfit.lookLegs = pugi::cast(lookNode.attribute("legs").value()); - defaultOutfit.lookFeet = pugi::cast(lookNode.attribute("feet").value()); - defaultOutfit.lookAddons = pugi::cast(lookNode.attribute("addons").value()); - } else if ((attr = lookNode.attribute("typeex"))) { - defaultOutfit.lookTypeEx = pugi::cast(attr.value()); - } - defaultOutfit.lookMount = pugi::cast(lookNode.attribute("mount").value()); - - currentOutfit = defaultOutfit; - } - - for (auto parameterNode : npcNode.child("parameters").children()) { - parameters[parameterNode.attribute("key").as_string()] = parameterNode.attribute("value").as_string(); - } - - pugi::xml_attribute scriptFile = npcNode.attribute("script"); - if (scriptFile) { - npcEventHandler = new NpcEventsHandler(scriptFile.as_string(), this); - if (!npcEventHandler->isLoaded()) { - delete npcEventHandler; - npcEventHandler = nullptr; - return false; - } - } - return true; -} - bool Npc::canSee(const Position& pos) const { if (pos.z != getPosition().z) { @@ -249,18 +215,10 @@ void Npc::onCreatureAppear(Creature* creature, bool isLogin) Creature::onCreatureAppear(creature, isLogin); if (creature == this) { - if (walkTicks > 0) { + if (baseSpeed > 0) { addEventWalk(); } - - if (npcEventHandler) { - npcEventHandler->onCreatureAppear(creature); - } } else if (Player* player = creature->getPlayer()) { - if (npcEventHandler) { - npcEventHandler->onCreatureAppear(creature); - } - spectators.insert(player); updateIdleStatus(); } @@ -270,14 +228,14 @@ void Npc::onRemoveCreature(Creature* creature, bool isLogout) { Creature::onRemoveCreature(creature, isLogout); - if (creature == this) { - closeAllShopWindows(); - if (npcEventHandler) { - npcEventHandler->onCreatureDisappear(creature); - } - } else if (Player* player = creature->getPlayer()) { - if (npcEventHandler) { - npcEventHandler->onCreatureDisappear(creature); + if (!behaviourDatabase) { + return; + } + + Player* player = creature->getPlayer(); + if (player) { + if (player->getID() == focusCreature) { + behaviourDatabase->react(SITUATION_VANISH, player, ""); } spectators.erase(player); @@ -290,15 +248,19 @@ void Npc::onCreatureMove(Creature* creature, const Tile* newTile, const Position { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); - if (creature == this || creature->getPlayer()) { - if (npcEventHandler) { - npcEventHandler->onCreatureMove(creature, oldPos, newPos); + if (!behaviourDatabase) { + return; + } + + Player* player = creature->getPlayer(); + if (player && player->getID() == focusCreature) { + if (!Position::areInRange<3, 3, 0>(creature->getPosition(), getPosition())) { + behaviourDatabase->react(SITUATION_VANISH, player, ""); } + } - if (creature != this) { - Player* player = creature->getPlayer(); - - // if player is now in range, add to spectators list, otherwise erase + if (creature != this) { + if (player) { if (player->canSee(position)) { spectators.insert(player); } else { @@ -312,23 +274,25 @@ void Npc::onCreatureMove(Creature* creature, const Tile* newTile, const Position void Npc::onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) { - if (creature->getID() == id) { + if (creature->getID() == id || type != TALKTYPE_SAY || !behaviourDatabase) { return; } - //only players for script events Player* player = creature->getPlayer(); if (player) { - if (npcEventHandler) { - npcEventHandler->onCreatureSay(player, type, text); + if (!Position::areInRange<3, 3>(creature->getPosition(), getPosition())) { + return; } - } -} -void Npc::onPlayerCloseChannel(Player* player) -{ - if (npcEventHandler) { - npcEventHandler->onPlayerCloseChannel(player); + lastTalkCreature = creature->getID(); + + if (focusCreature == 0) { + behaviourDatabase->react(SITUATION_ADDRESS, player, text); + } else if (focusCreature != player->getID()) { + behaviourDatabase->react(SITUATION_BUSY, player, text); + } else if (focusCreature == player->getID()) { + behaviourDatabase->react(SITUATION_NONE, player, text); + } } } @@ -336,63 +300,44 @@ void Npc::onThink(uint32_t interval) { Creature::onThink(interval); - if (npcEventHandler) { - npcEventHandler->onThink(); + if (!isIdle && focusCreature == 0 && baseSpeed > 0 && getTimeSinceLastMove() >= 100 + getStepDuration()) { + addEventWalk(); } - if (!isIdle && getTimeSinceLastMove() >= walkTicks) { - addEventWalk(); + if (!behaviourDatabase) { + return; + } + + if (focusCreature) { + Player* player = g_game.getPlayerByID(focusCreature); + if (player) { + turnToCreature(player); + + if (conversationEndTime != 0 && OTSYS_TIME() > conversationEndTime) { + if (player) { + behaviourDatabase->react(SITUATION_VANISH, player, ""); + } + } + } } } void Npc::doSay(const std::string& text) { + if (lastTalkCreature == focusCreature) { + conversationEndTime = OTSYS_TIME() + 60000; + } + g_game.internalCreatureSay(this, TALKTYPE_SAY, text, false); } -void Npc::doSayToPlayer(Player* player, const std::string& text) -{ - if (player) { - player->sendCreatureSay(this, TALKTYPE_PRIVATE_NP, text); - player->onCreatureSay(this, TALKTYPE_PRIVATE_NP, text); - } -} - -void Npc::onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, - uint8_t amount, bool ignore/* = false*/, bool inBackpacks/* = false*/) -{ - if (npcEventHandler) { - npcEventHandler->onPlayerTrade(player, callback, itemId, count, amount, ignore, inBackpacks); - } - player->sendSaleItemList(); -} - -void Npc::onPlayerEndTrade(Player* player, int32_t buyCallback, int32_t sellCallback) -{ - lua_State* L = getScriptInterface()->getLuaState(); - - if (buyCallback != -1) { - luaL_unref(L, LUA_REGISTRYINDEX, buyCallback); - } - - if (sellCallback != -1) { - luaL_unref(L, LUA_REGISTRYINDEX, sellCallback); - } - - removeShopPlayer(player); - - if (npcEventHandler) { - npcEventHandler->onPlayerEndTrade(player); - } -} - bool Npc::getNextStep(Direction& dir, uint32_t& flags) { if (Creature::getNextStep(dir, flags)) { return true; } - if (walkTicks == 0) { + if (baseSpeed <= 0) { return false; } @@ -400,7 +345,11 @@ bool Npc::getNextStep(Direction& dir, uint32_t& flags) return false; } - if (getTimeSinceLastMove() < walkTicks) { + if (OTSYS_TIME() < staticMovementTime) { + return false; + } + + if (getTimeSinceLastMove() < 100 + getStepDuration() + getStepSpeed()) { return false; } @@ -444,11 +393,11 @@ bool Npc::canWalkTo(const Position& fromPos, Direction dir) const return false; } - if (!floorChange && (tile->hasFlag(TILESTATE_FLOORCHANGE) || tile->getTeleportItem())) { + if (tile->hasFlag(TILESTATE_BLOCKPATH)) { return false; } - if (!ignoreHeight && tile->hasHeight(1)) { + if (tile->hasHeight(1)) { return false; } @@ -484,10 +433,10 @@ bool Npc::getRandomStep(Direction& dir) const return true; } -void Npc::doMoveTo(const Position& pos) +void Npc::doMoveTo(const Position& target) { std::forward_list listDir; - if (getPathTo(pos, listDir, 1, 1, true, true)) { + if (getPathTo(target, listDir, 1, 1, true, true)) { startAutoWalk(listDir); } } @@ -531,771 +480,4 @@ void Npc::setCreatureFocus(Creature* creature) } else { focusCreature = 0; } -} - -void Npc::addShopPlayer(Player* player) -{ - shopPlayerSet.insert(player); -} - -void Npc::removeShopPlayer(Player* player) -{ - shopPlayerSet.erase(player); -} - -void Npc::closeAllShopWindows() -{ - while (!shopPlayerSet.empty()) { - Player* player = *shopPlayerSet.begin(); - if (!player->closeShopWindow()) { - removeShopPlayer(player); - } - } -} - -NpcScriptInterface* Npc::getScriptInterface() -{ - return scriptInterface; -} - -NpcScriptInterface::NpcScriptInterface() : - LuaScriptInterface("Npc interface") -{ - libLoaded = false; - initState(); -} - -bool NpcScriptInterface::initState() -{ - luaState = g_luaEnvironment.getLuaState(); - if (!luaState) { - return false; - } - - registerFunctions(); - - lua_newtable(luaState); - eventTableRef = luaL_ref(luaState, LUA_REGISTRYINDEX); - runningEventId = EVENT_ID_USER; - return true; -} - -bool NpcScriptInterface::closeState() -{ - libLoaded = false; - LuaScriptInterface::closeState(); - return true; -} - -bool NpcScriptInterface::loadNpcLib(const std::string& file) -{ - if (libLoaded) { - return true; - } - - if (loadFile(file) == -1) { - std::cout << "[Warning - NpcScriptInterface::loadNpcLib] Can not load " << file << std::endl; - return false; - } - - libLoaded = true; - return true; -} - -void NpcScriptInterface::registerFunctions() -{ - //npc exclusive functions - lua_register(luaState, "selfSay", NpcScriptInterface::luaActionSay); - lua_register(luaState, "selfMove", NpcScriptInterface::luaActionMove); - lua_register(luaState, "selfMoveTo", NpcScriptInterface::luaActionMoveTo); - lua_register(luaState, "selfTurn", NpcScriptInterface::luaActionTurn); - lua_register(luaState, "selfFollow", NpcScriptInterface::luaActionFollow); - lua_register(luaState, "getDistanceTo", NpcScriptInterface::luagetDistanceTo); - lua_register(luaState, "doNpcSetCreatureFocus", NpcScriptInterface::luaSetNpcFocus); - lua_register(luaState, "getNpcCid", NpcScriptInterface::luaGetNpcCid); - lua_register(luaState, "getNpcParameter", NpcScriptInterface::luaGetNpcParameter); - lua_register(luaState, "openShopWindow", NpcScriptInterface::luaOpenShopWindow); - lua_register(luaState, "closeShopWindow", NpcScriptInterface::luaCloseShopWindow); - lua_register(luaState, "doSellItem", NpcScriptInterface::luaDoSellItem); - - // metatable - registerMethod("Npc", "getParameter", NpcScriptInterface::luaNpcGetParameter); - registerMethod("Npc", "setFocus", NpcScriptInterface::luaNpcSetFocus); - - registerMethod("Npc", "openShopWindow", NpcScriptInterface::luaNpcOpenShopWindow); - registerMethod("Npc", "closeShopWindow", NpcScriptInterface::luaNpcCloseShopWindow); -} - -int NpcScriptInterface::luaActionSay(lua_State* L) -{ - //selfSay(words[, target]) - Npc* npc = getScriptEnv()->getNpc(); - if (!npc) { - return 0; - } - - const std::string& text = getString(L, 1); - if (lua_gettop(L) >= 2) { - Player* target = getPlayer(L, 2); - if (target) { - npc->doSayToPlayer(target, text); - return 0; - } - } - - npc->doSay(text); - return 0; -} - -int NpcScriptInterface::luaActionMove(lua_State* L) -{ - //selfMove(direction) - Npc* npc = getScriptEnv()->getNpc(); - if (npc) { - g_game.internalMoveCreature(npc, getNumber(L, 1)); - } - return 0; -} - -int NpcScriptInterface::luaActionMoveTo(lua_State* L) -{ - //selfMoveTo(x,y,z) - Npc* npc = getScriptEnv()->getNpc(); - if (!npc) { - return 0; - } - - npc->doMoveTo(Position( - getNumber(L, 1), - getNumber(L, 2), - getNumber(L, 3) - )); - return 0; -} - -int NpcScriptInterface::luaActionTurn(lua_State* L) -{ - //selfTurn(direction) - Npc* npc = getScriptEnv()->getNpc(); - if (npc) { - g_game.internalCreatureTurn(npc, getNumber(L, 1)); - } - return 0; -} - -int NpcScriptInterface::luaActionFollow(lua_State* L) -{ - //selfFollow(player) - Npc* npc = getScriptEnv()->getNpc(); - if (!npc) { - pushBoolean(L, false); - return 1; - } - - pushBoolean(L, npc->setFollowCreature(getPlayer(L, 1))); - return 1; -} - -int NpcScriptInterface::luagetDistanceTo(lua_State* L) -{ - //getDistanceTo(uid) - ScriptEnvironment* env = getScriptEnv(); - - Npc* npc = env->getNpc(); - if (!npc) { - reportErrorFunc(getErrorDesc(LUA_ERROR_THING_NOT_FOUND)); - lua_pushnil(L); - return 1; - } - - uint32_t uid = getNumber(L, -1); - - Thing* thing = env->getThingByUID(uid); - if (!thing) { - reportErrorFunc(getErrorDesc(LUA_ERROR_THING_NOT_FOUND)); - lua_pushnil(L); - return 1; - } - - const Position& thingPos = thing->getPosition(); - const Position& npcPos = npc->getPosition(); - if (npcPos.z != thingPos.z) { - lua_pushnumber(L, -1); - } else { - int32_t dist = std::max(Position::getDistanceX(npcPos, thingPos), Position::getDistanceY(npcPos, thingPos)); - lua_pushnumber(L, dist); - } - return 1; -} - -int NpcScriptInterface::luaSetNpcFocus(lua_State* L) -{ - //doNpcSetCreatureFocus(cid) - Npc* npc = getScriptEnv()->getNpc(); - if (npc) { - npc->setCreatureFocus(getCreature(L, -1)); - } - return 0; -} - -int NpcScriptInterface::luaGetNpcCid(lua_State* L) -{ - //getNpcCid() - Npc* npc = getScriptEnv()->getNpc(); - if (npc) { - lua_pushnumber(L, npc->getID()); - } else { - lua_pushnil(L); - } - return 1; -} - -int NpcScriptInterface::luaGetNpcParameter(lua_State* L) -{ - //getNpcParameter(paramKey) - Npc* npc = getScriptEnv()->getNpc(); - if (!npc) { - lua_pushnil(L); - return 1; - } - - std::string paramKey = getString(L, -1); - - auto it = npc->parameters.find(paramKey); - if (it != npc->parameters.end()) { - LuaScriptInterface::pushString(L, it->second); - } else { - lua_pushnil(L); - } - return 1; -} - -int NpcScriptInterface::luaOpenShopWindow(lua_State* L) -{ - //openShopWindow(cid, items, onBuy callback, onSell callback) - int32_t sellCallback; - if (lua_isfunction(L, -1) == 0) { - sellCallback = -1; - lua_pop(L, 1); // skip it - use default value - } else { - sellCallback = popCallback(L); - } - - int32_t buyCallback; - if (lua_isfunction(L, -1) == 0) { - buyCallback = -1; - lua_pop(L, 1); // skip it - use default value - } else { - buyCallback = popCallback(L); - } - - if (lua_istable(L, -1) == 0) { - reportError(__FUNCTION__, "item list is not a table."); - pushBoolean(L, false); - return 1; - } - - std::list items; - lua_pushnil(L); - while (lua_next(L, -2) != 0) { - const auto tableIndex = lua_gettop(L); - ShopInfo item; - - item.itemId = getField(L, tableIndex, "id"); - item.subType = getField(L, tableIndex, "subType"); - if (item.subType == 0) { - item.subType = getField(L, tableIndex, "subtype"); - lua_pop(L, 1); - } - - item.buyPrice = getField(L, tableIndex, "buy"); - item.sellPrice = getField(L, tableIndex, "sell"); - item.realName = getFieldString(L, tableIndex, "name"); - - items.push_back(item); - lua_pop(L, 6); - } - lua_pop(L, 1); - - Player* player = getPlayer(L, -1); - if (!player) { - reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - //Close any eventual other shop window currently open. - player->closeShopWindow(false); - - Npc* npc = getScriptEnv()->getNpc(); - if (!npc) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - npc->addShopPlayer(player); - player->setShopOwner(npc, buyCallback, sellCallback); - player->openShopWindow(npc, items); - - pushBoolean(L, true); - return 1; -} - -int NpcScriptInterface::luaCloseShopWindow(lua_State* L) -{ - //closeShopWindow(cid) - Npc* npc = getScriptEnv()->getNpc(); - if (!npc) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - Player* player = getPlayer(L, 1); - if (!player) { - reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - int32_t buyCallback; - int32_t sellCallback; - - Npc* merchant = player->getShopOwner(buyCallback, sellCallback); - - //Check if we actually have a shop window with this player. - if (merchant == npc) { - player->sendCloseShop(); - - if (buyCallback != -1) { - luaL_unref(L, LUA_REGISTRYINDEX, buyCallback); - } - - if (sellCallback != -1) { - luaL_unref(L, LUA_REGISTRYINDEX, sellCallback); - } - - player->setShopOwner(nullptr, -1, -1); - npc->removeShopPlayer(player); - } - - pushBoolean(L, true); - return 1; -} - -int NpcScriptInterface::luaDoSellItem(lua_State* L) -{ - //doSellItem(cid, itemid, amount, subtype, actionid, canDropOnMap) - Player* player = getPlayer(L, 1); - if (!player) { - reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - uint32_t sellCount = 0; - - uint32_t itemId = getNumber(L, 2); - uint32_t amount = getNumber(L, 3); - uint32_t subType; - - int32_t n = getNumber(L, 4, -1); - if (n != -1) { - subType = n; - } else { - subType = 1; - } - - uint32_t actionId = getNumber(L, 5, 0); - bool canDropOnMap = getBoolean(L, 6, true); - - const ItemType& it = Item::items[itemId]; - if (it.stackable) { - while (amount > 0) { - int32_t stackCount = std::min(100, amount); - Item* item = Item::CreateItem(it.id, stackCount); - if (item && actionId != 0) { - item->setActionId(actionId); - } - - if (g_game.internalPlayerAddItem(player, item, canDropOnMap) != RETURNVALUE_NOERROR) { - delete item; - lua_pushnumber(L, sellCount); - return 1; - } - - amount -= stackCount; - sellCount += stackCount; - } - } else { - for (uint32_t i = 0; i < amount; ++i) { - Item* item = Item::CreateItem(it.id, subType); - if (item && actionId != 0) { - item->setActionId(actionId); - } - - if (g_game.internalPlayerAddItem(player, item, canDropOnMap) != RETURNVALUE_NOERROR) { - delete item; - lua_pushnumber(L, sellCount); - return 1; - } - - ++sellCount; - } - } - - lua_pushnumber(L, sellCount); - return 1; -} - -int NpcScriptInterface::luaNpcGetParameter(lua_State* L) -{ - // npc:getParameter(key) - const std::string& key = getString(L, 2); - Npc* npc = getUserdata(L, 1); - if (npc) { - auto it = npc->parameters.find(key); - if (it != npc->parameters.end()) { - pushString(L, it->second); - } else { - lua_pushnil(L); - } - } else { - lua_pushnil(L); - } - return 1; -} - -int NpcScriptInterface::luaNpcSetFocus(lua_State* L) -{ - // npc:setFocus(creature) - Creature* creature = getCreature(L, 2); - Npc* npc = getUserdata(L, 1); - if (npc) { - npc->setCreatureFocus(creature); - pushBoolean(L, true); - } else { - lua_pushnil(L); - } - return 1; -} - -int NpcScriptInterface::luaNpcOpenShopWindow(lua_State* L) -{ - // npc:openShopWindow(cid, items, buyCallback, sellCallback) - if (!isTable(L, 3)) { - reportErrorFunc("item list is not a table."); - pushBoolean(L, false); - return 1; - } - - Player* player = getPlayer(L, 2); - if (!player) { - reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - Npc* npc = getUserdata(L, 1); - if (!npc) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - int32_t sellCallback = -1; - if (LuaScriptInterface::isFunction(L, 5)) { - sellCallback = luaL_ref(L, LUA_REGISTRYINDEX); - } - - int32_t buyCallback = -1; - if (LuaScriptInterface::isFunction(L, 4)) { - buyCallback = luaL_ref(L, LUA_REGISTRYINDEX); - } - - std::list items; - - lua_pushnil(L); - while (lua_next(L, 3) != 0) { - const auto tableIndex = lua_gettop(L); - ShopInfo item; - - item.itemId = getField(L, tableIndex, "id"); - item.subType = getField(L, tableIndex, "subType"); - if (item.subType == 0) { - item.subType = getField(L, tableIndex, "subtype"); - lua_pop(L, 1); - } - - item.buyPrice = getField(L, tableIndex, "buy"); - item.sellPrice = getField(L, tableIndex, "sell"); - item.realName = getFieldString(L, tableIndex, "name"); - - items.push_back(item); - lua_pop(L, 6); - } - lua_pop(L, 1); - - player->closeShopWindow(false); - npc->addShopPlayer(player); - - player->setShopOwner(npc, buyCallback, sellCallback); - player->openShopWindow(npc, items); - - pushBoolean(L, true); - return 1; -} - -int NpcScriptInterface::luaNpcCloseShopWindow(lua_State* L) -{ - // npc:closeShopWindow(player) - Player* player = getPlayer(L, 2); - if (!player) { - reportErrorFunc(getErrorDesc(LUA_ERROR_PLAYER_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - Npc* npc = getUserdata(L, 1); - if (!npc) { - reportErrorFunc(getErrorDesc(LUA_ERROR_CREATURE_NOT_FOUND)); - pushBoolean(L, false); - return 1; - } - - int32_t buyCallback; - int32_t sellCallback; - - Npc* merchant = player->getShopOwner(buyCallback, sellCallback); - if (merchant == npc) { - player->sendCloseShop(); - if (buyCallback != -1) { - luaL_unref(L, LUA_REGISTRYINDEX, buyCallback); - } - - if (sellCallback != -1) { - luaL_unref(L, LUA_REGISTRYINDEX, sellCallback); - } - - player->setShopOwner(nullptr, -1, -1); - npc->removeShopPlayer(player); - } - - pushBoolean(L, true); - return 1; -} - -NpcEventsHandler::NpcEventsHandler(const std::string& file, Npc* npc) : - npc(npc), scriptInterface(npc->getScriptInterface()) -{ - loaded = scriptInterface->loadFile("data/npc/scripts/" + file, npc) == 0; - if (!loaded) { - std::cout << "[Warning - NpcScript::NpcScript] Can not load script: " << file << std::endl; - std::cout << scriptInterface->getLastLuaError() << std::endl; - } else { - creatureSayEvent = scriptInterface->getEvent("onCreatureSay"); - creatureDisappearEvent = scriptInterface->getEvent("onCreatureDisappear"); - creatureAppearEvent = scriptInterface->getEvent("onCreatureAppear"); - creatureMoveEvent = scriptInterface->getEvent("onCreatureMove"); - playerCloseChannelEvent = scriptInterface->getEvent("onPlayerCloseChannel"); - playerEndTradeEvent = scriptInterface->getEvent("onPlayerEndTrade"); - thinkEvent = scriptInterface->getEvent("onThink"); - } -} - -bool NpcEventsHandler::isLoaded() const -{ - return loaded; -} - -void NpcEventsHandler::onCreatureAppear(Creature* creature) -{ - if (creatureAppearEvent == -1) { - return; - } - - //onCreatureAppear(creature) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onCreatureAppear] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(creatureAppearEvent, scriptInterface); - env->setNpc(npc); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(creatureAppearEvent); - LuaScriptInterface::pushUserdata(L, creature); - LuaScriptInterface::setCreatureMetatable(L, -1, creature); - scriptInterface->callFunction(1); -} - -void NpcEventsHandler::onCreatureDisappear(Creature* creature) -{ - if (creatureDisappearEvent == -1) { - return; - } - - //onCreatureDisappear(creature) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onCreatureDisappear] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(creatureDisappearEvent, scriptInterface); - env->setNpc(npc); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(creatureDisappearEvent); - LuaScriptInterface::pushUserdata(L, creature); - LuaScriptInterface::setCreatureMetatable(L, -1, creature); - scriptInterface->callFunction(1); -} - -void NpcEventsHandler::onCreatureMove(Creature* creature, const Position& oldPos, const Position& newPos) -{ - if (creatureMoveEvent == -1) { - return; - } - - //onCreatureMove(creature, oldPos, newPos) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onCreatureMove] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(creatureMoveEvent, scriptInterface); - env->setNpc(npc); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(creatureMoveEvent); - LuaScriptInterface::pushUserdata(L, creature); - LuaScriptInterface::setCreatureMetatable(L, -1, creature); - LuaScriptInterface::pushPosition(L, oldPos); - LuaScriptInterface::pushPosition(L, newPos); - scriptInterface->callFunction(3); -} - -void NpcEventsHandler::onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) -{ - if (creatureSayEvent == -1) { - return; - } - - //onCreatureSay(creature, type, msg) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onCreatureSay] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(creatureSayEvent, scriptInterface); - env->setNpc(npc); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(creatureSayEvent); - LuaScriptInterface::pushUserdata(L, creature); - LuaScriptInterface::setCreatureMetatable(L, -1, creature); - lua_pushnumber(L, type); - LuaScriptInterface::pushString(L, text); - scriptInterface->callFunction(3); -} - -void NpcEventsHandler::onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, - uint8_t count, uint8_t amount, bool ignore, bool inBackpacks) -{ - if (callback == -1) { - return; - } - - //onBuy(player, itemid, count, amount, ignore, inbackpacks) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onPlayerTrade] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(-1, scriptInterface); - env->setNpc(npc); - - lua_State* L = scriptInterface->getLuaState(); - LuaScriptInterface::pushCallback(L, callback); - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - lua_pushnumber(L, itemId); - lua_pushnumber(L, count); - lua_pushnumber(L, amount); - LuaScriptInterface::pushBoolean(L, ignore); - LuaScriptInterface::pushBoolean(L, inBackpacks); - scriptInterface->callFunction(6); -} - -void NpcEventsHandler::onPlayerCloseChannel(Player* player) -{ - if (playerCloseChannelEvent == -1) { - return; - } - - //onPlayerCloseChannel(player) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onPlayerCloseChannel] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(playerCloseChannelEvent, scriptInterface); - env->setNpc(npc); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(playerCloseChannelEvent); - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - scriptInterface->callFunction(1); -} - -void NpcEventsHandler::onPlayerEndTrade(Player* player) -{ - if (playerEndTradeEvent == -1) { - return; - } - - //onPlayerEndTrade(player) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onPlayerEndTrade] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(playerEndTradeEvent, scriptInterface); - env->setNpc(npc); - - lua_State* L = scriptInterface->getLuaState(); - scriptInterface->pushFunction(playerEndTradeEvent); - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - scriptInterface->callFunction(1); -} - -void NpcEventsHandler::onThink() -{ - if (thinkEvent == -1) { - return; - } - - //onThink() - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - NpcScript::onThink] Call stack overflow" << std::endl; - return; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(thinkEvent, scriptInterface); - env->setNpc(npc); - - scriptInterface->pushFunction(thinkEvent); - scriptInterface->callFunction(0); -} +} \ No newline at end of file diff --git a/src/npc.h b/src/npc.h index cd463bb..acf95ec 100644 --- a/src/npc.h +++ b/src/npc.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,86 +21,20 @@ #define FS_NPC_H_B090D0CB549D4435AFA03647195D156F #include "creature.h" -#include "luascript.h" #include class Npc; class Player; +class BehaviourDatabase; class Npcs { public: + static void loadNpcs(); static void reload(); }; -class NpcScriptInterface final : public LuaScriptInterface -{ - public: - NpcScriptInterface(); - - bool loadNpcLib(const std::string& file); - - private: - void registerFunctions(); - - static int luaActionSay(lua_State* L); - static int luaActionMove(lua_State* L); - static int luaActionMoveTo(lua_State* L); - static int luaActionTurn(lua_State* L); - static int luaActionFollow(lua_State* L); - static int luagetDistanceTo(lua_State* L); - static int luaSetNpcFocus(lua_State* L); - static int luaGetNpcCid(lua_State* L); - static int luaGetNpcParameter(lua_State* L); - static int luaOpenShopWindow(lua_State* L); - static int luaCloseShopWindow(lua_State* L); - static int luaDoSellItem(lua_State* L); - - // metatable - static int luaNpcGetParameter(lua_State* L); - static int luaNpcSetFocus(lua_State* L); - - static int luaNpcOpenShopWindow(lua_State* L); - static int luaNpcCloseShopWindow(lua_State* L); - - private: - bool initState() override; - bool closeState() override; - - bool libLoaded; -}; - -class NpcEventsHandler -{ - public: - NpcEventsHandler(const std::string& file, Npc* npc); - - void onCreatureAppear(Creature* creature); - void onCreatureDisappear(Creature* creature); - void onCreatureMove(Creature* creature, const Position& oldPos, const Position& newPos); - void onCreatureSay(Creature* creature, SpeakClasses, const std::string& text); - void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, bool ignore = false, bool inBackpacks = false); - void onPlayerCloseChannel(Player* player); - void onPlayerEndTrade(Player* player); - void onThink(); - - bool isLoaded() const; - - private: - Npc* npc; - NpcScriptInterface* scriptInterface; - - int32_t creatureAppearEvent = -1; - int32_t creatureDisappearEvent = -1; - int32_t creatureMoveEvent = -1; - int32_t creatureSayEvent = -1; - int32_t playerCloseChannelEvent = -1; - int32_t playerEndTradeEvent = -1; - int32_t thinkEvent = -1; - bool loaded = false; -}; - class Npc final : public Creature { public: @@ -110,53 +44,41 @@ class Npc final : public Creature Npc(const Npc&) = delete; Npc& operator=(const Npc&) = delete; - Npc* getNpc() override { + Npc* getNpc() final { return this; } - const Npc* getNpc() const override { + const Npc* getNpc() const final { return this; } - bool isPushable() const override { - return pushable && walkTicks != 0; + bool isPushable() const final { + return baseSpeed > 0; } - void setID() override { + void setID() final { if (id == 0) { id = npcAutoID++; } } - void removeList() override; - void addList() override; + void removeList() final; + void addList() final; static Npc* createNpc(const std::string& name); - bool canSee(const Position& pos) const override; + bool canSee(const Position& pos) const final; bool load(); void reload(); - const std::string& getName() const override { + const std::string& getName() const final { return name; } - const std::string& getNameDescription() const override { + const std::string& getNameDescription() const final { return name; } - CreatureType_t getType() const override { - return CREATURETYPE_NPC; - } - - uint8_t getSpeechBubble() const override { - return speechBubble; - } - void setSpeechBubble(const uint8_t bubble) { - speechBubble = bubble; - } - void doSay(const std::string& text); - void doSayToPlayer(Player* player, const std::string& text); void doMoveTo(const Position& pos); @@ -168,45 +90,38 @@ class Npc final : public Creature } void setMasterPos(Position pos, int32_t radius = 1) { masterPos = pos; - if (masterRadius == -1) { + if (masterRadius == 0) { masterRadius = radius; } } - void onPlayerCloseChannel(Player* player); - void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, - uint8_t amount, bool ignore = false, bool inBackpacks = false); - void onPlayerEndTrade(Player* player, int32_t buyCallback, int32_t sellCallback); - void turnToCreature(Creature* creature); void setCreatureFocus(Creature* creature); - NpcScriptInterface* getScriptInterface(); - static uint32_t npcAutoID; - private: + protected: explicit Npc(const std::string& name); - void onCreatureAppear(Creature* creature, bool isLogin) override; - void onRemoveCreature(Creature* creature, bool isLogout) override; + void onCreatureAppear(Creature* creature, bool isLogin) final; + void onRemoveCreature(Creature* creature, bool isLogout) final; void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, - const Tile* oldTile, const Position& oldPos, bool teleport) override; + const Tile* oldTile, const Position& oldPos, bool teleport) final; - void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override; - void onThink(uint32_t interval) override; - std::string getDescription(int32_t lookDistance) const override; + void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) final; + void onThink(uint32_t interval) final; + std::string getDescription(int32_t lookDistance) const final; - bool isImmune(CombatType_t) const override { - return !attackable; + bool isImmune(CombatType_t) const final { + return true; } - bool isImmune(ConditionType_t) const override { - return !attackable; + bool isImmune(ConditionType_t) const final { + return true; } - bool isAttackable() const override { - return attackable; + bool isAttackable() const final { + return false; } - bool getNextStep(Direction& dir, uint32_t& flags) override; + bool getNextStep(Direction& dir, uint32_t& flags) final; void setIdle(bool idle); void updateIdleStatus(); @@ -215,41 +130,29 @@ class Npc final : public Creature bool getRandomStep(Direction& dir) const; void reset(); - bool loadFromXml(); - void addShopPlayer(Player* player); - void removeShopPlayer(Player* player); - void closeAllShopWindows(); - - std::map parameters; - - std::set shopPlayerSet; std::set spectators; std::string name; std::string filename; - NpcEventsHandler* npcEventHandler; - Position masterPos; - uint32_t walkTicks; - int32_t focusCreature; - int32_t masterRadius; + uint32_t lastTalkCreature; + uint32_t focusCreature; + uint32_t masterRadius; - uint8_t speechBubble; + int64_t conversationStartTime; + int64_t conversationEndTime; + int64_t staticMovementTime; - bool floorChange; - bool attackable; - bool ignoreHeight; bool loaded; bool isIdle; - bool pushable; - static NpcScriptInterface* scriptInterface; + BehaviourDatabase* behaviourDatabase; friend class Npcs; - friend class NpcScriptInterface; + friend class BehaviourDatabase; }; #endif diff --git a/src/otpch.cpp b/src/otpch.cpp index c60a95d..5db3123 100644 --- a/src/otpch.cpp +++ b/src/otpch.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/otpch.h b/src/otpch.h index e154370..a2ff7c1 100644 --- a/src/otpch.h +++ b/src/otpch.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/otserv.cpp b/src/otserv.cpp index c47501c..ce6e996 100644 --- a/src/otserv.cpp +++ b/src/otserv.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,19 +23,18 @@ #include "game.h" -#include "iomarket.h" +#ifndef _WIN32 +#include // for sigemptyset() +#endif #include "configmanager.h" #include "scriptmanager.h" #include "rsa.h" -#include "protocolold.h" #include "protocollogin.h" #include "protocolstatus.h" #include "databasemanager.h" #include "scheduler.h" #include "databasetasks.h" -#include "script.h" -#include DatabaseTasks g_databaseTasks; Dispatcher g_dispatcher; @@ -45,7 +44,6 @@ Game g_game; ConfigManager g_config; Monsters g_monsters; Vocations g_vocations; -extern Scripts* g_scripts; RSA g_RSA; std::mutex g_loaderLock; @@ -58,9 +56,9 @@ void startupErrorMessage(const std::string& errorStr) g_loaderSignal.notify_all(); } -void mainLoader(int argc, char* argv[], ServiceManager* services); +void mainLoader(int argc, char* argv[], ServiceManager* servicer); -[[noreturn]] void badAllocationHandler() +void badAllocationHandler() { // Use functions that only use stack allocation puts("Allocation failed, server out of memory.\nDecrease the size of your map or compile in 64 bits mode.\n"); @@ -73,6 +71,15 @@ int main(int argc, char* argv[]) // Setup bad allocation handler std::set_new_handler(badAllocationHandler); +#ifndef _WIN32 + // ignore sigpipe... + struct sigaction sigh; + sigh.sa_handler = SIG_IGN; + sigh.sa_flags = 0; + sigemptyset(&sigh.sa_mask); + sigaction(SIGPIPE, &sigh, nullptr); +#endif + ServiceManager serviceManager; g_dispatcher.start(); @@ -84,6 +91,19 @@ int main(int argc, char* argv[]) if (serviceManager.is_running()) { std::cout << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " Server Online!" << std::endl << std::endl; +#ifdef _WIN32 + SetConsoleCtrlHandler([](DWORD) -> BOOL { + g_dispatcher.addTask(createTask([]() { + g_dispatcher.addTask(createTask( + std::bind(&Game::shutdown, &g_game) + )); + g_scheduler.stop(); + g_databaseTasks.stop(); + g_dispatcher.stop(); + })); + ExitThread(0); + }, 1); +#endif serviceManager.run(); } else { std::cout << ">> No services running. The server is NOT online." << std::endl; @@ -126,21 +146,6 @@ void mainLoader(int, char*[], ServiceManager* services) std::cout << "Visit our forum for updates, support, and resources: http://otland.net/." << std::endl; std::cout << std::endl; - // check if config.lua or config.lua.dist exist - std::ifstream c_test("./config.lua"); - if (!c_test.is_open()) { - std::ifstream config_lua_dist("./config.lua.dist"); - if (config_lua_dist.is_open()) { - std::cout << ">> copying config.lua.dist to config.lua" << std::endl; - std::ofstream config_lua("config.lua"); - config_lua << config_lua_dist.rdbuf(); - config_lua.close(); - config_lua_dist.close(); - } - } else { - c_test.close(); - } - // read global config std::cout << ">> Loading config" << std::endl; if (!g_config.load()) { @@ -160,14 +165,16 @@ void mainLoader(int, char*[], ServiceManager* services) //set RSA key try { g_RSA.loadPEM("key.pem"); - } catch(const std::exception& e) { + } + catch (const std::exception& e) { startupErrorMessage(e.what()); return; } std::cout << ">> Establishing database connection..." << std::flush; - if (!Database::getInstance().connect()) { + Database* db = Database::getInstance(); + if (!db->connect()) { startupErrorMessage("Failed to connect to database."); return; } @@ -183,8 +190,6 @@ void mainLoader(int, char*[], ServiceManager* services) } g_databaseTasks.start(); - DatabaseManager::updateDatabase(); - if (g_config.getBoolean(ConfigManager::OPTIMIZE_DATABASE) && !DatabaseManager::optimizeTables()) { std::cout << "> No tables were optimized." << std::endl; } @@ -198,42 +203,26 @@ void mainLoader(int, char*[], ServiceManager* services) // load item data std::cout << ">> Loading items" << std::endl; - if (!Item::items.loadFromOtb("data/items/items.otb")) { - startupErrorMessage("Unable to load items (OTB)!"); - return; - } - - if (!Item::items.loadFromXml()) { - startupErrorMessage("Unable to load items (XML)!"); + if (!Item::items.loadItems()) { + startupErrorMessage("Unable to load items (SRV)!"); return; } std::cout << ">> Loading script systems" << std::endl; - if (!ScriptingManager::getInstance().loadScriptSystems()) { + if (!ScriptingManager::getInstance()->loadScriptSystems()) { startupErrorMessage("Failed to load script systems"); return; } - std::cout << ">> Loading lua scripts" << std::endl; - if (!g_scripts->loadScripts("scripts", false, false)) { - startupErrorMessage("Failed to load lua scripts"); - return; - } - std::cout << ">> Loading monsters" << std::endl; if (!g_monsters.loadFromXml()) { startupErrorMessage("Unable to load monsters!"); return; } - std::cout << ">> Loading lua monsters" << std::endl; - if (!g_scripts->loadScripts("monster", false, false)) { - startupErrorMessage("Failed to load lua monsters"); - return; - } - std::cout << ">> Loading outfits" << std::endl; - if (!Outfits::getInstance().loadFromXml()) { + auto& outfits = Outfits::getInstance(); + if (!outfits.loadFromXml()) { startupErrorMessage("Unable to load outfits!"); return; } @@ -266,14 +255,11 @@ void mainLoader(int, char*[], ServiceManager* services) g_game.setGameState(GAME_STATE_INIT); // Game client protocols - services->add(static_cast(g_config.getNumber(ConfigManager::GAME_PORT))); - services->add(static_cast(g_config.getNumber(ConfigManager::LOGIN_PORT))); + services->add(g_config.getNumber(ConfigManager::GAME_PORT)); + services->add(g_config.getNumber(ConfigManager::LOGIN_PORT)); // OT protocols - services->add(static_cast(g_config.getNumber(ConfigManager::STATUS_PORT))); - - // Legacy login protocol - services->add(static_cast(g_config.getNumber(ConfigManager::LOGIN_PORT))); + services->add(g_config.getNumber(ConfigManager::STATUS_PORT)); RentPeriod_t rentPeriod; std::string strRentPeriod = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD)); @@ -292,9 +278,6 @@ void mainLoader(int, char*[], ServiceManager* services) g_game.map.houses.payHouses(rentPeriod); - IOMarket::checkExpiredOffers(); - IOMarket::getInstance().updateStatistics(); - std::cout << ">> Loaded all modules, server starting up..." << std::endl; #ifndef _WIN32 diff --git a/src/outputmessage.cpp b/src/outputmessage.cpp index b4c0f77..0d3d597 100644 --- a/src/outputmessage.cpp +++ b/src/outputmessage.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,6 +29,14 @@ extern Scheduler g_scheduler; const uint16_t OUTPUTMESSAGE_FREE_LIST_CAPACITY = 2048; const std::chrono::milliseconds OUTPUTMESSAGE_AUTOSEND_DELAY {10}; +class OutputMessageAllocator +{ + public: + typedef OutputMessage value_type; + template + struct rebind {typedef LockfreePoolingAllocator other;}; +}; + void OutputMessagePool::scheduleSendAll() { auto functor = std::bind(&OutputMessagePool::sendAll, this); @@ -71,7 +79,5 @@ void OutputMessagePool::removeProtocolFromAutosend(const Protocol_ptr& protocol) OutputMessage_ptr OutputMessagePool::getOutputMessage() { - // LockfreePoolingAllocator will leave (void* allocate) ill-formed because - // of sizeof(T), so this guaranatees that only one list will be initialized - return std::allocate_shared(LockfreePoolingAllocator()); + return std::allocate_shared(OutputMessageAllocator()); } diff --git a/src/outputmessage.h b/src/outputmessage.h index 1a78599..3dde508 100644 --- a/src/outputmessage.h +++ b/src/outputmessage.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -28,80 +28,76 @@ class Protocol; class OutputMessage : public NetworkMessage { - public: - OutputMessage() = default; +public: + OutputMessage() = default; - // non-copyable - OutputMessage(const OutputMessage&) = delete; - OutputMessage& operator=(const OutputMessage&) = delete; + // non-copyable + OutputMessage(const OutputMessage&) = delete; + OutputMessage& operator=(const OutputMessage&) = delete; - uint8_t* getOutputBuffer() { - return buffer + outputBufferStart; - } + uint8_t* getOutputBuffer() { + return buffer + outputBufferStart; + } - void writeMessageLength() { - add_header(info.length); - } + void writeMessageLength() { + add_header(info.length); + } - void addCryptoHeader(bool addChecksum) { - if (addChecksum) { - add_header(adlerChecksum(buffer + outputBufferStart, info.length)); - } + void addCryptoHeader() { + writeMessageLength(); + } - writeMessageLength(); - } + inline void append(const NetworkMessage& msg) { + auto msgLen = msg.getLength(); + memcpy(buffer + info.position, msg.getBuffer() + 4, msgLen); + info.length += msgLen; + info.position += msgLen; + } - void append(const NetworkMessage& msg) { - auto msgLen = msg.getLength(); - memcpy(buffer + info.position, msg.getBuffer() + 8, msgLen); - info.length += msgLen; - info.position += msgLen; - } + inline void append(const OutputMessage_ptr& msg) { + auto msgLen = msg->getLength(); + memcpy(buffer + info.position, msg->getBuffer() + 4, msgLen); + info.length += msgLen; + info.position += msgLen; + } - void append(const OutputMessage_ptr& msg) { - auto msgLen = msg->getLength(); - memcpy(buffer + info.position, msg->getBuffer() + 8, msgLen); - info.length += msgLen; - info.position += msgLen; - } +protected: + template + inline void add_header(T add) { + assert(outputBufferStart >= sizeof(T)); + outputBufferStart -= sizeof(T); + memcpy(buffer + outputBufferStart, &add, sizeof(T)); + //added header size to the message size + info.length += sizeof(T); + } - private: - template - void add_header(T add) { - assert(outputBufferStart >= sizeof(T)); - outputBufferStart -= sizeof(T); - memcpy(buffer + outputBufferStart, &add, sizeof(T)); - //added header size to the message size - info.length += sizeof(T); - } - - MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION; + MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION; }; class OutputMessagePool { - public: - // non-copyable - OutputMessagePool(const OutputMessagePool&) = delete; - OutputMessagePool& operator=(const OutputMessagePool&) = delete; +public: + // non-copyable + OutputMessagePool(const OutputMessagePool&) = delete; + OutputMessagePool& operator=(const OutputMessagePool&) = delete; - static OutputMessagePool& getInstance() { - static OutputMessagePool instance; - return instance; - } + static OutputMessagePool& getInstance() { + static OutputMessagePool instance; + return instance; + } - void sendAll(); - void scheduleSendAll(); + void sendAll(); + void scheduleSendAll(); - static OutputMessage_ptr getOutputMessage(); + static OutputMessage_ptr getOutputMessage(); - void addProtocolToAutosend(Protocol_ptr protocol); - void removeProtocolFromAutosend(const Protocol_ptr& protocol); - private: - OutputMessagePool() = default; - //NOTE: A vector is used here because this container is mostly read - //and relatively rarely modified (only when a client connects/disconnects) - std::vector bufferedProtocols; + void addProtocolToAutosend(Protocol_ptr protocol); + void removeProtocolFromAutosend(const Protocol_ptr& protocol); +private: + OutputMessagePool() = default; + //NOTE: A vector is used here because this container is mostly read + //and relatively rarely modified (only when a client connects/disconnects) + std::vector bufferedProtocols; }; diff --git a/src/party.cpp b/src/party.cpp index 1f1cd50..e0d80a4 100644 --- a/src/party.cpp +++ b/src/party.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,13 +39,13 @@ void Party::disband() return; } + Player* currentLeader = leader; leader = nullptr; currentLeader->setParty(nullptr); currentLeader->sendClosePrivate(CHANNEL_PARTY); g_game.updatePlayerShield(currentLeader); - g_game.updatePlayerHelpers(*currentLeader); currentLeader->sendCreatureSkull(currentLeader); currentLeader->sendTextMessage(MESSAGE_INFO_DESCR, "Your party has been disbanded."); @@ -70,8 +70,8 @@ void Party::disband() member->sendCreatureSkull(currentLeader); currentLeader->sendCreatureSkull(member); - g_game.updatePlayerHelpers(*member); } + memberList.clear(); delete this; } @@ -112,12 +112,10 @@ bool Party::leaveParty(Player* player) player->setParty(nullptr); player->sendClosePrivate(CHANNEL_PARTY); g_game.updatePlayerShield(player); - g_game.updatePlayerHelpers(*player); for (Player* member : memberList) { member->sendCreatureSkull(player); player->sendPlayerPartyIcons(member); - g_game.updatePlayerHelpers(*member); } leader->sendCreatureSkull(player); @@ -127,6 +125,7 @@ bool Party::leaveParty(Player* player) player->sendTextMessage(MESSAGE_INFO_DESCR, "You have left the party."); updateSharedExperience(); + updateVocationsList(); clearPlayerPoints(player); @@ -213,10 +212,9 @@ bool Party::joinParty(Player& player) memberList.push_back(&player); - g_game.updatePlayerHelpers(player); - player.removePartyInvitation(this); updateSharedExperience(); + updateVocationsList(); const std::string& leaderName = leader->getName(); ss.str(std::string()); @@ -244,12 +242,6 @@ bool Party::removeInvite(Player& player, bool removeFromPlayer/* = true*/) if (empty()) { disband(); - } else { - for (Player* member : memberList) { - g_game.updatePlayerHelpers(*member); - } - - g_game.updatePlayerHelpers(*leader); } return true; @@ -277,7 +269,7 @@ bool Party::invitePlayer(Player& player) std::ostringstream ss; ss << player.getName() << " has been invited."; - if (empty()) { + if (memberList.empty() && inviteList.empty()) { ss << " Open the party channel to communicate with your members."; g_game.updatePlayerShield(leader); leader->sendCreatureSkull(leader); @@ -287,11 +279,6 @@ bool Party::invitePlayer(Player& player) inviteList.push_back(&player); - for (Player* member : memberList) { - g_game.updatePlayerHelpers(*member); - } - g_game.updatePlayerHelpers(*leader); - leader->sendCreatureShield(&player); player.sendCreatureShield(leader); @@ -336,6 +323,15 @@ void Party::broadcastPartyMessage(MessageClasses msgClass, const std::string& ms } } +void Party::broadcastPartyLoot(const std::string& loot) +{ + leader->sendTextMessage(MESSAGE_INFO_DESCR, loot); + + for (Player* member : memberList) { + member->sendTextMessage(MESSAGE_INFO_DESCR, loot); + } +} + void Party::updateSharedExperience() { if (sharedExpActive) { @@ -347,6 +343,30 @@ void Party::updateSharedExperience() } } +void Party::updateVocationsList() +{ + std::set vocationIds; + + uint32_t vocationId = leader->getVocation()->getFromVocation(); + if (vocationId != VOCATION_NONE) { + vocationIds.insert(vocationId); + } + + for (const Player* member : memberList) { + vocationId = member->getVocation()->getFromVocation(); + if (vocationId != VOCATION_NONE) { + vocationIds.insert(vocationId); + } + } + + size_t size = vocationIds.size(); + if (size > 1) { + extraExpRate = static_cast(size * (10 + (size - 1) * 5)) / 100.f; + } else { + extraExpRate = 0.20f; + } +} + bool Party::setSharedExperience(Player* player, bool sharedExpActive) { if (!player || leader != player) { @@ -399,7 +419,7 @@ bool Party::canUseSharedExperience(const Player* player) const } } - uint32_t minLevel = static_cast(std::ceil((static_cast(highestLevel) * 2) / 3)); + uint32_t minLevel = static_cast(std::ceil((static_cast(highestLevel) * 2) / 3)); if (player->getLevel() < minLevel) { return false; } diff --git a/src/party.h b/src/party.h index 50c10c0..96fce94 100644 --- a/src/party.h +++ b/src/party.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,7 @@ class Player; class Party; -using PlayerVector = std::vector; +typedef std::vector PlayerVector; class Party { @@ -61,12 +61,13 @@ class Party bool isPlayerInvited(const Player* player) const; void updateAllPartyIcons(); void broadcastPartyMessage(MessageClasses msgClass, const std::string& msg, bool sendToInvitations = false); + void broadcastPartyLoot(const std::string& loot); bool empty() const { return memberList.empty() && inviteList.empty(); } bool canOpenCorpse(uint32_t ownerId) const; - void shareExperience(uint64_t experience, Creature* source = nullptr); + void shareExperience(uint64_t experience, Creature* source/* = nullptr*/); bool setSharedExperience(Player* player, bool sharedExpActive); bool isSharedExperienceActive() const { return sharedExpActive; @@ -77,10 +78,12 @@ class Party bool canUseSharedExperience(const Player* player) const; void updateSharedExperience(); + void updateVocationsList(); + void updatePlayerTicks(Player* player, uint32_t points); void clearPlayerPoints(Player* player); - private: + protected: bool canEnableSharedExperience(); std::map ticksMap; @@ -90,6 +93,8 @@ class Party Player* leader; + float extraExpRate = 0.20f; + bool sharedExpActive = false; bool sharedExpEnabled = false; }; diff --git a/src/player.cpp b/src/player.cpp index 41a72ff..a4936c0 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,14 +32,12 @@ #include "monster.h" #include "movement.h" #include "scheduler.h" -#include "weapons.h" extern ConfigManager g_config; extern Game g_game; extern Chat* g_chat; extern Vocations g_vocations; extern MoveEvents* g_moveEvents; -extern Weapons* g_weapons; extern CreatureEvents* g_creatureEvents; extern Events* g_events; @@ -48,9 +46,8 @@ MuteCountMap Player::muteCountMap; uint32_t Player::playerAutoID = 0x10000000; Player::Player(ProtocolGame_ptr p) : - Creature(), lastPing(OTSYS_TIME()), lastPong(lastPing), inbox(new Inbox(ITEM_INBOX)), client(std::move(p)) + Creature(), lastPing(OTSYS_TIME()), lastPong(lastPing), client(std::move(p)) { - inbox->incrementReferenceCounter(); } Player::~Player() @@ -63,12 +60,9 @@ Player::~Player() } for (const auto& it : depotLockerMap) { - it.second->removeInbox(inbox); it.second->decrementReferenceCounter(); } - inbox->decrementReferenceCounter(); - setWriteItem(nullptr); setEditHouse(nullptr); } @@ -135,53 +129,23 @@ std::string Player::getDescription(int32_t lookDistance) const } } - if (party) { + if (guild && guildRank) { if (lookDistance == -1) { - s << " Your party has "; + s << " You are "; } else if (sex == PLAYERSEX_FEMALE) { - s << " She is in a party with "; + s << " She is "; } else { - s << " He is in a party with "; + s << " He is "; } - size_t memberCount = party->getMemberCount() + 1; - if (memberCount == 1) { - s << "1 member and "; - } else { - s << memberCount << " members and "; + s << guildRank->name << " of the " << guild->getName(); + if (!guildNick.empty()) { + s << " (" << guildNick << ')'; } - size_t invitationCount = party->getInvitationCount(); - if (invitationCount == 1) { - s << "1 pending invitation."; - } else { - s << invitationCount << " pending invitations."; - } + s << "."; } - if (!guild || !guildRank) { - return s.str(); - } - - if (lookDistance == -1) { - s << " You are "; - } else if (sex == PLAYERSEX_FEMALE) { - s << " She is "; - } else { - s << " He is "; - } - - s << guildRank->name << " of the " << guild->getName(); - if (!guildNick.empty()) { - s << " (" << guildNick << ')'; - } - - size_t memberCount = guild->getMemberCount(); - if (memberCount == 1) { - s << ", which has 1 member, " << guild->getMembersOnline().size() << " of them online."; - } else { - s << ", which has " << memberCount << " members, " << guild->getMembersOnline().size() << " of them online."; - } return s.str(); } @@ -203,104 +167,43 @@ void Player::removeConditionSuppressions(uint32_t conditions) conditionSuppressions &= ~conditions; } -Item* Player::getWeapon(slots_t slot, bool ignoreAmmo) const +Item* Player::getWeapon() const { - Item* item = inventory[slot]; - if (!item) { - return nullptr; - } - - WeaponType_t weaponType = item->getWeaponType(); - if (weaponType == WEAPON_NONE || weaponType == WEAPON_SHIELD || weaponType == WEAPON_AMMO) { - return nullptr; - } - - if (!ignoreAmmo && weaponType == WEAPON_DISTANCE) { - const ItemType& it = Item::items[item->getID()]; - if (it.ammoType != AMMO_NONE) { - Item* ammoItem = inventory[CONST_SLOT_AMMO]; - if (!ammoItem || ammoItem->getAmmoType() != it.ammoType) { - return nullptr; - } - item = ammoItem; - } - } - return item; -} - -Item* Player::getWeapon(bool ignoreAmmo/* = false*/) const -{ - Item* item = getWeapon(CONST_SLOT_LEFT, ignoreAmmo); - if (item) { + Item* item = inventory[CONST_SLOT_LEFT]; + if (item && item->getWeaponType() != WEAPON_NONE && item->getWeaponType() != WEAPON_SHIELD && item->getWeaponType() != WEAPON_AMMO) { return item; } - item = getWeapon(CONST_SLOT_RIGHT, ignoreAmmo); - if (item) { + item = inventory[CONST_SLOT_RIGHT]; + if (item && item->getWeaponType() != WEAPON_NONE && item->getWeaponType() != WEAPON_SHIELD && item->getWeaponType() != WEAPON_AMMO) { return item; } + return nullptr; } -WeaponType_t Player::getWeaponType() const +Item* Player::getAmmunition() const { - Item* item = getWeapon(); - if (!item) { - return WEAPON_NONE; - } - return item->getWeaponType(); -} - -int32_t Player::getWeaponSkill(const Item* item) const -{ - if (!item) { - return getSkillLevel(SKILL_FIST); - } - - int32_t attackSkill; - - WeaponType_t weaponType = item->getWeaponType(); - switch (weaponType) { - case WEAPON_SWORD: { - attackSkill = getSkillLevel(SKILL_SWORD); - break; - } - - case WEAPON_CLUB: { - attackSkill = getSkillLevel(SKILL_CLUB); - break; - } - - case WEAPON_AXE: { - attackSkill = getSkillLevel(SKILL_AXE); - break; - } - - case WEAPON_DISTANCE: { - attackSkill = getSkillLevel(SKILL_DISTANCE); - break; - } - - default: { - attackSkill = 0; - break; - } - } - return attackSkill; + return inventory[CONST_SLOT_AMMO]; } int32_t Player::getArmor() const { - int32_t armor = 0; + int32_t armor = 0; // base armor - static const slots_t armorSlots[] = {CONST_SLOT_HEAD, CONST_SLOT_NECKLACE, CONST_SLOT_ARMOR, CONST_SLOT_LEGS, CONST_SLOT_FEET, CONST_SLOT_RING}; + static const slots_t armorSlots[] = { CONST_SLOT_HEAD, CONST_SLOT_NECKLACE, CONST_SLOT_ARMOR, CONST_SLOT_LEGS, CONST_SLOT_FEET, CONST_SLOT_RING }; for (slots_t slot : armorSlots) { Item* inventoryItem = inventory[slot]; if (inventoryItem) { armor += inventoryItem->getArmor(); } } - return static_cast(armor * vocation->armorMultiplier); + + if (armor > 1) { + armor = rand() % (armor >> 1) + (armor >> 1); + } + + return armor; } void Player::getShieldAndWeapon(const Item*& shield, const Item*& weapon) const @@ -316,7 +219,7 @@ void Player::getShieldAndWeapon(const Item*& shield, const Item*& weapon) const switch (item->getWeaponType()) { case WEAPON_NONE: - break; + break; case WEAPON_SHIELD: { if (!shield || item->getDefense() > shield->getDefense()) { @@ -333,56 +236,67 @@ void Player::getShieldAndWeapon(const Item*& shield, const Item*& weapon) const } } -int32_t Player::getDefense() const +int32_t Player::getDefense() { + int32_t totalDefense = 5; int32_t defenseSkill = getSkillLevel(SKILL_FIST); - int32_t defenseValue = 7; + const Item* weapon; const Item* shield; getShieldAndWeapon(shield, weapon); if (weapon) { - defenseValue = weapon->getDefense() + weapon->getExtraDefense(); - defenseSkill = getWeaponSkill(weapon); - } + totalDefense = weapon->getDefense() + 1; - if (shield) { - defenseValue = weapon != nullptr ? shield->getDefense() + weapon->getExtraDefense() : shield->getDefense(); - defenseSkill = getSkillLevel(SKILL_SHIELD); - } - - if (defenseSkill == 0) { - switch (fightMode) { - case FIGHTMODE_ATTACK: - case FIGHTMODE_BALANCED: - return 1; - - case FIGHTMODE_DEFENSE: - return 2; + switch (weapon->getWeaponType()) { + case WEAPON_AXE: + defenseSkill = SKILL_AXE; + break; + case WEAPON_SWORD: + defenseSkill = SKILL_SWORD; + break; + case WEAPON_CLUB: + defenseSkill = SKILL_CLUB; + break; + case WEAPON_DISTANCE: + case WEAPON_AMMO: + defenseSkill = SKILL_DISTANCE; + break; + default: + break; } } - return (defenseSkill / 4. + 2.23) * defenseValue * 0.15 * getDefenseFactor() * vocation->defenseMultiplier; + if (shield) { + totalDefense = shield->getDefense() + 1; + defenseSkill = getSkillLevel(SKILL_SHIELD); + } + + fightMode_t attackMode = getFightMode(); + + if ((followCreature || !attackedCreature) && earliestAttackTime <= OTSYS_TIME()) { + attackMode = FIGHTMODE_DEFENSE; + } + + if (attackMode == FIGHTMODE_ATTACK) { + totalDefense -= 4 * totalDefense / 10; + } else if (attackMode == FIGHTMODE_DEFENSE) { + totalDefense += 8 * totalDefense / 10; + } + + if (totalDefense) { + int32_t formula = (5 * (defenseSkill) + 50) * totalDefense; + int32_t randresult = rand() % 100; + + totalDefense = formula * ((rand() % 100 + randresult) / 2) / 10000.; + } + + return totalDefense; } -float Player::getAttackFactor() const +fightMode_t Player::getFightMode() const { - switch (fightMode) { - case FIGHTMODE_ATTACK: return 1.0f; - case FIGHTMODE_BALANCED: return 1.2f; - case FIGHTMODE_DEFENSE: return 2.0f; - default: return 1.0f; - } -} - -float Player::getDefenseFactor() const -{ - switch (fightMode) { - case FIGHTMODE_ATTACK: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.5f : 1.0f; - case FIGHTMODE_BALANCED: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.75f : 1.0f; - case FIGHTMODE_DEFENSE: return 1.0f; - default: return 1.0f; - } + return fightMode; } uint16_t Player::getClientIcons() const @@ -394,19 +308,6 @@ uint16_t Player::getClientIcons() const } } - if (pzLocked) { - icons |= ICON_REDSWORDS; - } - - if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) { - icons |= ICON_PIGEON; - - // Don't show ICON_SWORDS if player is in protection zone. - if (hasBitSet(ICON_SWORDS, icons)) { - icons &= ~ICON_SWORDS; - } - } - // Game client debugs with 10 or more icons // so let's prevent that from happening. std::bitset<20> icon_bitset(static_cast(icons)); @@ -532,18 +433,9 @@ void Player::addContainer(uint8_t cid, Container* container) return; } - if (container->getID() == ITEM_BROWSEFIELD) { - container->incrementReferenceCounter(); - } - auto it = openContainers.find(cid); if (it != openContainers.end()) { OpenContainer& openContainer = it->second; - Container* oldContainer = openContainer.container; - if (oldContainer->getID() == ITEM_BROWSEFIELD) { - oldContainer->decrementReferenceCounter(); - } - openContainer.container = container; openContainer.index = 0; } else { @@ -561,13 +453,7 @@ void Player::closeContainer(uint8_t cid) return; } - OpenContainer openContainer = it->second; - Container* container = openContainer.container; openContainers.erase(it); - - if (container && container->getID() == ITEM_BROWSEFIELD) { - container->decrementReferenceCounter(); - } } void Player::setContainerIndex(uint8_t cid, uint16_t index) @@ -621,7 +507,7 @@ uint16_t Player::getLookCorpse() const } } -void Player::addStorageValue(const uint32_t key, const int32_t value, const bool isLogin/* = false*/) +void Player::addStorageValue(const uint32_t key, const int32_t value) { if (IS_IN_KEYRANGE(key, RESERVED_RANGE)) { if (IS_IN_KEYRANGE(key, OUTFITS_RANGE)) { @@ -630,27 +516,15 @@ void Player::addStorageValue(const uint32_t key, const int32_t value, const bool value & 0xFF ); return; - } else if (IS_IN_KEYRANGE(key, MOUNTS_RANGE)) { - // do nothing - } else { + } + else { std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl; return; } } if (value != -1) { - int32_t oldValue; - getStorageValue(key, oldValue); - storageMap[key] = value; - - if (!isLogin) { - auto currentFrameTime = g_dispatcher.getDispatcherCycle(); - if (lastQuestlogUpdate != currentFrameTime && g_game.quests.isQuestStorage(key, value, oldValue)) { - lastQuestlogUpdate = currentFrameTime; - sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your questlog has been updated."); - } - } } else { storageMap.erase(key); } @@ -660,7 +534,7 @@ bool Player::getStorageValue(const uint32_t key, int32_t& value) const { auto it = storageMap.find(key); if (it == storageMap.end()) { - value = -1; + value = 0; return false; } @@ -689,68 +563,18 @@ bool Player::canSeeCreature(const Creature* creature) const if (!creature->getPlayer() && !canSeeInvisibility() && creature->isInvisible()) { return false; } + return true; } -bool Player::canWalkthrough(const Creature* creature) const +void Player::onReceiveMail(uint32_t townId) const { - if (group->access || creature->isInGhostMode()) { - return true; - } - - const Player* player = creature->getPlayer(); - if (!player) { - return false; - } - - const Tile* playerTile = player->getTile(); - if (!playerTile || (!playerTile->hasFlag(TILESTATE_PROTECTIONZONE) && player->getLevel() > static_cast(g_config.getNumber(ConfigManager::PROTECTION_LEVEL)))) { - return false; - } - - const Item* playerTileGround = playerTile->getGround(); - if (!playerTileGround || !playerTileGround->hasWalkStack()) { - return false; - } - - Player* thisPlayer = const_cast(this); - if ((OTSYS_TIME() - lastWalkthroughAttempt) > 2000) { - thisPlayer->setLastWalkthroughAttempt(OTSYS_TIME()); - return false; - } - - if (creature->getPosition() != lastWalkthroughPosition) { - thisPlayer->setLastWalkthroughPosition(creature->getPosition()); - return false; - } - - thisPlayer->setLastWalkthroughPosition(creature->getPosition()); - return true; -} - -bool Player::canWalkthroughEx(const Creature* creature) const -{ - if (group->access) { - return true; - } - - const Player* player = creature->getPlayer(); - if (!player) { - return false; - } - - const Tile* playerTile = player->getTile(); - return playerTile && (playerTile->hasFlag(TILESTATE_PROTECTIONZONE) || player->getLevel() <= static_cast(g_config.getNumber(ConfigManager::PROTECTION_LEVEL))); -} - -void Player::onReceiveMail() const -{ - if (isNearDepotBox()) { + if (isNearDepotBox(townId)) { sendTextMessage(MESSAGE_EVENT_ADVANCE, "New mail has arrived."); } } -bool Player::isNearDepotBox() const +bool Player::isNearDepotBox(uint32_t townId) const { const Position& pos = getPosition(); for (int32_t cx = -1; cx <= 1; ++cx) { @@ -760,47 +584,35 @@ bool Player::isNearDepotBox() const continue; } - if (tile->hasFlag(TILESTATE_DEPOT)) { - return true; + if (DepotLocker* depotLocker = tile->getDepotLocker()) { + if (depotLocker->getDepotId() == townId) { + return true; + } } } } return false; } -DepotChest* Player::getDepotChest(uint32_t depotId, bool autoCreate) -{ - auto it = depotChests.find(depotId); - if (it != depotChests.end()) { - return it->second; - } - - if (!autoCreate) { - return nullptr; - } - - DepotChest* depotChest = new DepotChest(ITEM_DEPOT); - depotChest->incrementReferenceCounter(); - depotChest->setMaxDepotItems(getMaxDepotItems()); - depotChests[depotId] = depotChest; - return depotChest; -} - -DepotLocker* Player::getDepotLocker(uint32_t depotId) +DepotLocker* Player::getDepotLocker(uint32_t depotId, bool autoCreate) { auto it = depotLockerMap.find(depotId); if (it != depotLockerMap.end()) { - inbox->setParent(it->second); return it->second; } - DepotLocker* depotLocker = new DepotLocker(ITEM_LOCKER1); - depotLocker->setDepotId(depotId); - depotLocker->internalAddThing(Item::CreateItem(ITEM_MARKET)); - depotLocker->internalAddThing(inbox); - depotLocker->internalAddThing(getDepotChest(depotId, true)); - depotLockerMap[depotId] = depotLocker; - return depotLocker; + if (autoCreate) { + DepotLocker* depotLocker = new DepotLocker(ITEM_LOCKER1); + depotLocker->setDepotId(depotId); + Item* depotItem = Item::CreateItem(ITEM_DEPOT); + if (depotItem) { + depotLocker->internalAddThing(depotItem); + } + depotLockerMap[depotId] = depotLocker; + return depotLocker; + } + + return nullptr; } void Player::sendCancelMessage(ReturnValue message) const @@ -812,7 +624,6 @@ void Player::sendStats() { if (client) { client->sendStats(); - lastStatsTrainingTime = getOfflineTrainingTime() / 60 / 1000; } } @@ -910,20 +721,10 @@ void Player::sendAddContainerItem(const Container* container, const Item* item) continue; } - uint16_t slot = openContainer.index; - if (container->getID() == ITEM_BROWSEFIELD) { - uint16_t containerSize = container->size() - 1; - uint16_t pageEnd = openContainer.index + container->capacity() - 1; - if (containerSize > pageEnd) { - slot = pageEnd; - item = container->getItemByIndex(pageEnd); - } else { - slot = containerSize; - } - } else if (openContainer.index >= container->capacity()) { + if (openContainer.index >= container->capacity()) { item = container->getItemByIndex(openContainer.index - 1); } - client->sendAddContainerItem(it.first, slot, item); + client->sendAddContainerItem(it.first, item); } } @@ -970,12 +771,12 @@ void Player::sendRemoveContainerItem(const Container* container, uint16_t slot) sendContainer(it.first, container, false, firstIndex); } - client->sendRemoveContainerItem(it.first, std::max(slot, firstIndex), container->getItemByIndex(container->capacity() + firstIndex)); + client->sendRemoveContainerItem(it.first, std::max(slot, firstIndex)); } } void Player::onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem, - const ItemType& oldType, const Item* newItem, const ItemType& newType) + const ItemType& oldType, const Item* newItem, const ItemType& newType) { Creature::onUpdateTileItem(tile, pos, oldItem, oldType, newItem, newType); @@ -991,7 +792,7 @@ void Player::onUpdateTileItem(const Tile* tile, const Position& pos, const Item* } void Player::onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType, - const Item* item) + const Item* item) { Creature::onRemoveTileItem(tile, pos, iType, item); @@ -1012,8 +813,6 @@ void Player::onCreatureAppear(Creature* creature, bool isLogin) Creature::onCreatureAppear(creature, isLogin); if (isLogin && creature == this) { - sendItems(); - for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { Item* item = inventory[slot]; if (item) { @@ -1031,9 +830,6 @@ void Player::onCreatureAppear(Creature* creature, bool isLogin) if (bed) { bed->wakeUp(this); } - - Account account = IOLoginData::loadAccount(accountNumber); - Game::updatePremium(account); std::cout << name << " has logged in." << std::endl; @@ -1086,20 +882,8 @@ void Player::onChangeZone(ZoneType_t zone) setAttackedCreature(nullptr); onAttackedCreatureDisappear(false); } - - if (!group->access && isMounted()) { - dismount(); - g_game.internalCreatureChangeOutfit(this, defaultOutfit); - wasMounted = true; - } - } else { - if (wasMounted) { - toggleMount(true); - wasMounted = false; - } } - g_game.updateCreatureWalkthrough(this); sendIcons(); } @@ -1147,8 +931,6 @@ void Player::onRemoveCreature(Creature* creature, bool isLogout) g_game.internalCloseTrade(this); } - closeShopWindow(); - clearPartyInvitations(); if (party) { @@ -1179,36 +961,6 @@ void Player::onRemoveCreature(Creature* creature, bool isLogout) } } -void Player::openShopWindow(Npc* npc, const std::list& shop) -{ - shopItemList = shop; - sendShop(npc); - sendSaleItemList(); -} - -bool Player::closeShopWindow(bool sendCloseShopWindow /*= true*/) -{ - //unreference callbacks - int32_t onBuy; - int32_t onSell; - - Npc* npc = getShopOwner(onBuy, onSell); - if (!npc) { - shopItemList.clear(); - return false; - } - - setShopOwner(nullptr, -1, -1); - npc->onPlayerEndTrade(this, onBuy, onSell); - - if (sendCloseShopWindow) { - sendCloseShop(); - } - - shopItemList.clear(); - return true; -} - void Player::onWalk(Direction& dir) { Creature::onWalk(dir); @@ -1217,7 +969,7 @@ void Player::onWalk(Direction& dir) } void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, - const Tile* oldTile, const Position& oldPos, bool teleport) + const Tile* oldTile, const Position& oldPos, bool teleport) { Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport); @@ -1241,23 +993,6 @@ void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Posit } } - // close modal windows - if (!modalWindows.empty()) { - // TODO: This shouldn't be hardcoded - for (uint32_t modalWindowId : modalWindows) { - if (modalWindowId == std::numeric_limits::max()) { - sendTextMessage(MESSAGE_EVENT_ADVANCE, "Offline training aborted."); - break; - } - } - modalWindows.clear(); - } - - // leave market - if (inMarket) { - inMarket = false; - } - if (party) { party->updateSharedExperience(); } @@ -1431,10 +1166,11 @@ void Player::onThink(uint32_t interval) addMessageBuffer(); } + lastWalkingTime += interval; if (!getTile()->hasFlag(TILESTATE_NOLOGOUT) && !isAccessPlayer()) { idleTime += interval; const int32_t kickAfterMinutes = g_config.getNumber(ConfigManager::KICK_AFTER_MINUTES); - if (idleTime > (kickAfterMinutes * 60000) + 60000) { + if ((!pzLocked && OTSYS_TIME() - lastPong >= 60000) || idleTime > (kickAfterMinutes * 60000) + 60000) { kickPlayer(true); } else if (client && idleTime == 60000 * kickAfterMinutes) { std::ostringstream ss; @@ -1444,12 +1180,7 @@ void Player::onThink(uint32_t interval) } if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { - checkSkullTicks(interval / 1000); - } - - addOfflineTrainingTime(interval); - if (lastStatsTrainingTime != getOfflineTrainingTime() / 60 / 1000) { - sendStats(); + checkSkullTicks(); } } @@ -1597,24 +1328,7 @@ void Player::addExperience(Creature* source, uint64_t exp, bool sendText/* = fal experience += exp; if (sendText) { - std::string expString = std::to_string(exp) + (exp != 1 ? " experience points." : " experience point."); - - TextMessage message(MESSAGE_EXPERIENCE, "You gained " + expString); - message.position = position; - message.primary.value = exp; - message.primary.color = TEXTCOLOR_WHITE_EXP; - sendTextMessage(message); - - SpectatorVec spectators; - g_game.map.getSpectators(spectators, position, false, true); - spectators.erase(this); - if (!spectators.empty()) { - message.type = MESSAGE_EXPERIENCE_OTHERS; - message.text = getName() + " gained " + expString; - for (Creature* spectator : spectators) { - spectator->getPlayer()->sendTextMessage(message); - } - } + g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(exp)); } uint32_t prevLevel = level; @@ -1635,9 +1349,6 @@ void Player::addExperience(Creature* source, uint64_t exp, bool sendText/* = fal } if (prevLevel != level) { - health = healthMax; - mana = manaMax; - updateBaseSpeed(); setBaseSpeed(getBaseSpeed()); @@ -1663,51 +1374,22 @@ void Player::addExperience(Creature* source, uint64_t exp, bool sendText/* = fal sendStats(); } -void Player::removeExperience(uint64_t exp, bool sendText/* = false*/) +void Player::removeExperience(uint64_t exp) { if (experience == 0 || exp == 0) { return; } - g_events->eventPlayerOnLoseExperience(this, exp); - if (exp == 0) { - return; - } - - uint64_t lostExp = experience; experience = std::max(0, experience - exp); - if (sendText) { - lostExp -= experience; - - std::string expString = std::to_string(lostExp) + (lostExp != 1 ? " experience points." : " experience point."); - - TextMessage message(MESSAGE_EXPERIENCE, "You lost " + expString); - message.position = position; - message.primary.value = lostExp; - message.primary.color = TEXTCOLOR_RED; - sendTextMessage(message); - - SpectatorVec spectators; - g_game.map.getSpectators(spectators, position, false, true); - spectators.erase(this); - if (!spectators.empty()) { - message.type = MESSAGE_EXPERIENCE_OTHERS; - message.text = getName() + " lost " + expString; - for (Creature* spectator : spectators) { - spectator->getPlayer()->sendTextMessage(message); - } - } - } - uint32_t oldLevel = level; uint64_t currLevelExp = Player::getExpForLevel(level); while (level > 1 && experience < currLevelExp) { --level; - healthMax = std::max(0, healthMax - vocation->getHPGain()); + healthMax = std::max(150, std::max(0, healthMax - vocation->getHPGain())); manaMax = std::max(0, manaMax - vocation->getManaGain()); - capacity = std::max(0, capacity - vocation->getCapGain()); + capacity = std::max(400, std::max(0, capacity - vocation->getCapGain())); currLevelExp = Player::getExpForLevel(level); } @@ -1752,6 +1434,11 @@ uint8_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount) return result; } +uint16_t Player::getDropLootPercent() +{ + return 10; +} + void Player::onBlockHit() { if (shieldBlockCount > 0) { @@ -1809,7 +1496,7 @@ bool Player::hasShield() const } BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense /* = false*/, bool checkArmor /* = false*/, bool field /* = false*/) + bool checkDefense /* = false*/, bool checkArmor /* = false*/, bool field /* = false*/) { BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, field); @@ -1821,58 +1508,55 @@ BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_ return blockType; } - if (damage <= 0) { - damage = 0; - return BLOCK_ARMOR; - } - - for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { - if (!isItemAbilityEnabled(static_cast(slot))) { - continue; - } - - Item* item = inventory[slot]; - if (!item) { - continue; - } - - const ItemType& it = Item::items[item->getID()]; - if (!it.abilities) { - if (damage <= 0) { - damage = 0; - return BLOCK_ARMOR; + if (damage > 0) { + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) { + if (!isItemAbilityEnabled(static_cast(slot))) { + continue; } - continue; - } - - const int16_t& absorbPercent = it.abilities->absorbPercent[combatTypeToIndex(combatType)]; - if (absorbPercent != 0) { - damage -= std::round(damage * (absorbPercent / 100.)); - - uint16_t charges = item->getCharges(); - if (charges != 0) { - g_game.transformItem(item, item->getID(), charges - 1); + Item* item = inventory[slot]; + if (!item) { + continue; } - } - if (field) { - const int16_t& fieldAbsorbPercent = it.abilities->fieldAbsorbPercent[combatTypeToIndex(combatType)]; - if (fieldAbsorbPercent != 0) { - damage -= std::round(damage * (fieldAbsorbPercent / 100.)); + const ItemType& it = Item::items[item->getID()]; + if (it.abilities) { + const int16_t& absorbPercent = it.abilities->absorbPercent[combatTypeToIndex(combatType)]; + if (absorbPercent != 0) { + damage -= std::round(damage * (absorbPercent / 100.)); - uint16_t charges = item->getCharges(); - if (charges != 0) { - g_game.transformItem(item, item->getID(), charges - 1); + uint16_t charges = item->getCharges() - 1; + if (charges != 0) { + g_game.transformItem(item, item->getID(), charges); + } else { + g_game.internalRemoveItem(item); + } + } + + if (field) { + const int16_t& fieldAbsorbPercent = it.abilities->fieldAbsorbPercent[combatTypeToIndex(combatType)]; + if (fieldAbsorbPercent != 0) { + damage -= std::round(damage * (fieldAbsorbPercent / 100.)); + + uint16_t charges = item->getCharges(); + if (charges != 0) { + if (charges - 1 == 0) { + g_game.internalRemoveItem(item); + } else { + g_game.transformItem(item, item->getID(), charges - 1); + } + } + } } } } + + if (damage <= 0) { + damage = 0; + blockType = BLOCK_ARMOR; + } } - if (damage <= 0) { - damage = 0; - blockType = BLOCK_ARMOR; - } return blockType; } @@ -1885,33 +1569,31 @@ uint32_t Player::getIP() const return 0; } +void Player::dropLoot(Container* corpse, Creature*) +{ + if (corpse && lootDrop) { + Skulls_t playerSkull = getSkull(); + if (inventory[CONST_SLOT_NECKLACE] && inventory[CONST_SLOT_NECKLACE]->getID() == ITEM_AMULETOFLOSS && playerSkull != SKULL_RED) { + g_game.internalRemoveItem(inventory[CONST_SLOT_NECKLACE], 1); + } else { + for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { + Item* item = inventory[i]; + if (item) { + if (playerSkull == SKULL_RED || item->getContainer() || uniform_random(1, 100) <= getDropLootPercent()) { + g_game.internalMoveItem(this, corpse, INDEX_WHEREEVER, item, item->getItemCount(), 0); + sendInventoryItem(static_cast(i), nullptr); + } + } + } + } + } +} + void Player::death(Creature* lastHitCreature) { loginPosition = town->getTemplePosition(); if (skillLoss) { - uint8_t unfairFightReduction = 100; - bool lastHitPlayer = Player::lastHitIsPlayer(lastHitCreature); - - if (lastHitPlayer) { - uint32_t sumLevels = 0; - uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED); - for (const auto& it : damageMap) { - CountBlock_t cb = it.second; - if ((OTSYS_TIME() - cb.ticks) <= inFightTicks) { - Player* damageDealer = g_game.getPlayerByID(it.first); - if (damageDealer) { - sumLevels += damageDealer->getLevel(); - } - } - } - - if (sumLevels > level) { - double reduce = level / static_cast(sumLevels); - unfairFightReduction = std::max(20, std::floor((reduce * 100) + 0.5)); - } - } - //Magic level loss uint64_t sumMana = 0; uint64_t lostMana = 0; @@ -1923,7 +1605,7 @@ void Player::death(Creature* lastHitCreature) sumMana += manaSpent; - double deathLossPercent = getLostPercent() * (unfairFightReduction / 100.); + double deathLossPercent = getLostPercent(); lostMana = static_cast(sumMana * deathLossPercent); @@ -1977,9 +1659,7 @@ void Player::death(Creature* lastHitCreature) if (expLoss != 0) { uint32_t oldLevel = level; - if (vocation->getId() == VOCATION_NONE || level > 7) { - experience -= expLoss; - } + experience -= expLoss; while (level > 1 && experience < Player::getExpForLevel(level)) { --level; @@ -2005,7 +1685,7 @@ void Player::death(Creature* lastHitCreature) std::bitset<6> bitset(blessings); if (bitset[5]) { - if (lastHitPlayer) { + if (Player::lastHitIsPlayer(lastHitCreature)) { bitset.reset(5); blessings = bitset.to_ulong(); } else { @@ -2017,15 +1697,9 @@ void Player::death(Creature* lastHitCreature) sendStats(); sendSkills(); - sendReLoginWindow(unfairFightReduction); - if (getSkull() == SKULL_BLACK) { - health = 40; - mana = 0; - } else { - health = healthMax; - mana = manaMax; - } + health = healthMax; + mana = manaMax; auto it = conditions.begin(), end = conditions.end(); while (it != end) { @@ -2040,8 +1714,61 @@ void Player::death(Creature* lastHitCreature) ++it; } } + + // Teleport newbies to newbie island + if (g_config.getBoolean(ConfigManager::TELEPORT_NEWBIES)) { + if (getVocationId() != VOCATION_NONE && level <= static_cast(g_config.getNumber(ConfigManager::NEWBIE_LEVEL_THRESHOLD))) { + Town* newbieTown = g_game.map.towns.getTown(g_config.getNumber(ConfigManager::NEWBIE_TOWN)); + if (newbieTown) { + // Restart stats + level = 1; + experience = 0; + levelPercent = 0; + capacity = 400; + health = 150; + healthMax = 150; + mana = 0; + manaMax = 0; + magLevel = 0; + magLevelPercent = 0; + manaSpent = 0; + staminaMinutes = 3360; + setVocation(0); + + // Restart skills + for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { //for each skill + skills[i].level = 10; + skills[i].tries = 0; + skills[i].percent = 0; + } + + // Restart town + setTown(newbieTown); + loginPosition = getTemplePosition(); + + // Restart first items + lastLoginSaved = 0; + lastLogout = 0; + + // Restart storages + storageMap.clear(); + outfits.clear(); + + // Restart items + for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; slot++) + { + Item* item = inventory[slot]; + if (item) { + g_game.internalRemoveItem(item, item->getItemCount()); + } + } + } else { + std::cout << "[Warning - Player:death] Newbie teletransportation is enabled, newbie town does not exist." << std::endl; + } + } + } } else { - setSkillLoss(true); + setLossSkill(true); auto it = conditions.begin(), end = conditions.end(); while (it != end) { @@ -2052,7 +1779,8 @@ void Player::death(Creature* lastHitCreature) condition->endCondition(this); onEndCondition(condition->getType()); delete condition; - } else { + } + else { ++it; } } @@ -2166,7 +1894,12 @@ bool Player::removeVIP(uint32_t vipGuid) bool Player::addVIP(uint32_t vipGuid, const std::string& vipName, VipStatus_t status) { - if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53 + if (guid == vipGuid) { + sendTextMessage(MESSAGE_STATUS_SMALL, "You cannot add yourself."); + return false; + } + + if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 100) { sendTextMessage(MESSAGE_STATUS_SMALL, "You cannot add more buddies."); return false; } @@ -2177,33 +1910,26 @@ bool Player::addVIP(uint32_t vipGuid, const std::string& vipName, VipStatus_t st return false; } - IOLoginData::addVIPEntry(accountNumber, vipGuid, "", 0, false); + IOLoginData::addVIPEntry(accountNumber, vipGuid); if (client) { - client->sendVIP(vipGuid, vipName, "", 0, false, status); + client->sendVIP(vipGuid, vipName, status); } return true; } bool Player::addVIPInternal(uint32_t vipGuid) { - if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53 + if (guid == vipGuid) { + return false; + } + + if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 100) { return false; } return VIPList.insert(vipGuid).second; } -bool Player::editVIP(uint32_t vipGuid, const std::string& description, uint32_t icon, bool notify) -{ - auto it = VIPList.find(vipGuid); - if (it == VIPList.end()) { - return false; // player is not in VIP - } - - IOLoginData::editVIPEntry(accountNumber, vipGuid, description, icon, notify); - return true; -} - //close container and its child containers void Player::autoCloseContainers(const Container* container) { @@ -2270,18 +1996,14 @@ ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, const int32_t& slotPosition = item->getSlotPosition(); if ((slotPosition & SLOTP_HEAD) || (slotPosition & SLOTP_NECKLACE) || - (slotPosition & SLOTP_BACKPACK) || (slotPosition & SLOTP_ARMOR) || - (slotPosition & SLOTP_LEGS) || (slotPosition & SLOTP_FEET) || - (slotPosition & SLOTP_RING)) { + (slotPosition & SLOTP_BACKPACK) || (slotPosition & SLOTP_ARMOR) || + (slotPosition & SLOTP_LEGS) || (slotPosition & SLOTP_FEET) || + (slotPosition & SLOTP_RING)) { ret = RETURNVALUE_CANNOTBEDRESSED; } else if (slotPosition & SLOTP_TWO_HAND) { ret = RETURNVALUE_PUTTHISOBJECTINBOTHHANDS; } else if ((slotPosition & SLOTP_RIGHT) || (slotPosition & SLOTP_LEFT)) { - if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) { - ret = RETURNVALUE_CANNOTBEDRESSED; - } else { - ret = RETURNVALUE_PUTTHISOBJECTINYOURHAND; - } + ret = RETURNVALUE_PUTTHISOBJECTINYOURHAND; } switch (index) { @@ -2315,22 +2037,7 @@ ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, case CONST_SLOT_RIGHT: { if (slotPosition & SLOTP_RIGHT) { - if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) { - if (item->getWeaponType() != WEAPON_SHIELD) { - ret = RETURNVALUE_CANNOTBEDRESSED; - } else { - const Item* leftItem = inventory[CONST_SLOT_LEFT]; - if (leftItem) { - if ((leftItem->getSlotPosition() | slotPosition) & SLOTP_TWO_HAND) { - ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE; - } else { - ret = RETURNVALUE_NOERROR; - } - } else { - ret = RETURNVALUE_NOERROR; - } - } - } else if (slotPosition & SLOTP_TWO_HAND) { + if (slotPosition & SLOTP_TWO_HAND) { if (inventory[CONST_SLOT_LEFT] && inventory[CONST_SLOT_LEFT] != item) { ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE; } else { @@ -2347,8 +2054,8 @@ ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, } else if (leftType == WEAPON_SHIELD && type == WEAPON_SHIELD) { ret = RETURNVALUE_CANONLYUSEONESHIELD; } else if (leftType == WEAPON_NONE || type == WEAPON_NONE || - leftType == WEAPON_SHIELD || leftType == WEAPON_AMMO - || type == WEAPON_SHIELD || type == WEAPON_AMMO) { + leftType == WEAPON_SHIELD || leftType == WEAPON_AMMO + || type == WEAPON_SHIELD || type == WEAPON_AMMO) { ret = RETURNVALUE_NOERROR; } else { ret = RETURNVALUE_CANONLYUSEONEWEAPON; @@ -2362,16 +2069,7 @@ ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, case CONST_SLOT_LEFT: { if (slotPosition & SLOTP_LEFT) { - if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) { - WeaponType_t type = item->getWeaponType(); - if (type == WEAPON_NONE || type == WEAPON_SHIELD) { - ret = RETURNVALUE_CANNOTBEDRESSED; - } else if (inventory[CONST_SLOT_RIGHT] && (slotPosition & SLOTP_TWO_HAND)) { - ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE; - } else { - ret = RETURNVALUE_NOERROR; - } - } else if (slotPosition & SLOTP_TWO_HAND) { + if (slotPosition & SLOTP_TWO_HAND) { if (inventory[CONST_SLOT_RIGHT] && inventory[CONST_SLOT_RIGHT] != item) { ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE; } else { @@ -2388,8 +2086,8 @@ ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, } else if (rightType == WEAPON_SHIELD && type == WEAPON_SHIELD) { ret = RETURNVALUE_CANONLYUSEONESHIELD; } else if (rightType == WEAPON_NONE || type == WEAPON_NONE || - rightType == WEAPON_SHIELD || rightType == WEAPON_AMMO - || type == WEAPON_SHIELD || type == WEAPON_AMMO) { + rightType == WEAPON_SHIELD || rightType == WEAPON_AMMO + || type == WEAPON_SHIELD || type == WEAPON_AMMO) { ret = RETURNVALUE_NOERROR; } else { ret = RETURNVALUE_CANONLYUSEONEWEAPON; @@ -2423,46 +2121,41 @@ ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, } case CONST_SLOT_AMMO: { - if ((slotPosition & SLOTP_AMMO) || g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) { - ret = RETURNVALUE_NOERROR; - } + ret = RETURNVALUE_NOERROR; break; } case CONST_SLOT_WHEREEVER: case -1: - ret = RETURNVALUE_NOTENOUGHROOM; - break; + ret = RETURNVALUE_NOTENOUGHROOM; + break; default: - ret = RETURNVALUE_NOTPOSSIBLE; - break; + ret = RETURNVALUE_NOTPOSSIBLE; + break; } + if (ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_NOTENOUGHROOM) { + //need an exchange with source? + const Item* inventoryItem = getInventoryItem(static_cast(index)); + if (inventoryItem && (!inventoryItem->isStackable() || inventoryItem->isRune() || inventoryItem->getID() != item->getID())) { + return RETURNVALUE_NEEDEXCHANGE; + } - if (ret != RETURNVALUE_NOERROR && ret != RETURNVALUE_NOTENOUGHROOM) { - return ret; - } + //check if enough capacity + if (!hasCapacity(item, count)) { + return RETURNVALUE_NOTENOUGHCAPACITY; + } - //need an exchange with source? - const Item* inventoryItem = getInventoryItem(static_cast(index)); - if (inventoryItem && (!inventoryItem->isStackable() || inventoryItem->getID() != item->getID())) { - return RETURNVALUE_NEEDEXCHANGE; - } - - //check if enough capacity - if (!hasCapacity(item, count)) { - return RETURNVALUE_NOTENOUGHCAPACITY; - } - - if (!g_moveEvents->onPlayerEquip(const_cast(this), const_cast(item), static_cast(index), true)) { - return RETURNVALUE_CANNOTBEDRESSED; + if (!g_moveEvents->onPlayerEquip(const_cast(this), const_cast(item), static_cast(index), true)) { + return RETURNVALUE_CANNOTBEDRESSED; + } } return ret; } ReturnValue Player::queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount, - uint32_t flags) const + uint32_t flags) const { const Item* item = thing.getItem(); if (item == nullptr) { @@ -2514,15 +2207,18 @@ ReturnValue Player::queryMaxCount(int32_t index, const Thing& thing, uint32_t co } if (destItem) { - if (destItem->isStackable() && item->equals(destItem) && destItem->getItemCount() < 100) { + if (!destItem->isRune() && destItem->isStackable() && item->equals(destItem) && destItem->getItemCount() < 100) { maxQueryCount = 100 - destItem->getItemCount(); - } else { + } + else { maxQueryCount = 0; } - } else if (queryAdd(index, *item, count, flags) == RETURNVALUE_NOERROR) { //empty slot + } + else if (queryAdd(index, *item, count, flags) == RETURNVALUE_NOERROR) { //empty slot if (item->isStackable()) { maxQueryCount = 100; - } else { + } + else { maxQueryCount = 1; } @@ -2561,7 +2257,7 @@ ReturnValue Player::queryRemove(const Thing& thing, uint32_t count, uint32_t fla } Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** destItem, - uint32_t& flags) + uint32_t& flags) { if (index == 0 /*drop to capacity window*/ || index == INDEX_WHEREEVER) { *destItem = nullptr; @@ -2626,9 +2322,11 @@ Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** de n--; } - for (Item* tmpContainerItem : tmpContainer->getItemList()) { - if (Container* subContainer = tmpContainerItem->getContainer()) { - containers.push_back(subContainer); + if (!g_config.getBoolean(ConfigManager::DROP_ITEMS)) { + for (Item* tmpContainerItem : tmpContainer->getItemList()) { + if (Container* subContainer = tmpContainerItem->getContainer()) { + containers.push_back(subContainer); + } } } @@ -2653,8 +2351,10 @@ Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** de return tmpContainer; } - if (Container* subContainer = tmpItem->getContainer()) { - containers.push_back(subContainer); + if (!g_config.getBoolean(ConfigManager::DROP_ITEMS)) { + if (Container* subContainer = tmpItem->getContainer()) { + containers.push_back(subContainer); + } } n++; @@ -2868,7 +2568,23 @@ bool Player::removeItemOfType(uint16_t itemId, uint32_t amount, int32_t subType, g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable); return true; } - } else if (Container* container = item->getContainer()) { + } + else if (Container* container = item->getContainer()) { + if (container->getID() == itemId) { + uint32_t itemCount = Item::countByType(item, subType); + if (itemCount == 0) { + continue; + } + + itemList.push_back(item); + + count += itemCount; + if (count >= amount) { + g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable); + return true; + } + } + for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) { Item* containerItem = *it; if (containerItem->getID() == itemId) { @@ -2918,28 +2634,14 @@ Thing* Player::getThing(size_t index) const return nullptr; } -void Player::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/) +void Player::postAddNotification(Thing* thing, const Cylinder*, int32_t index, cylinderlink_t link /*= LINK_OWNER*/) { if (link == LINK_OWNER) { //calling movement scripts g_moveEvents->onPlayerEquip(this, thing->getItem(), static_cast(index), false); } - bool requireListUpdate = false; - if (link == LINK_OWNER || link == LINK_TOPPARENT) { - const Item* i = (oldParent ? oldParent->getItem() : nullptr); - - // Check if we owned the old container too, so we don't need to do anything, - // as the list was updated in postRemoveNotification - assert(i ? i->getContainer() != nullptr : true); - - if (i) { - requireListUpdate = i->getContainer()->getHoldingPlayer() != this; - } else { - requireListUpdate = oldParent != this; - } - updateInventoryWeight(); updateItemsLight(); sendStats(); @@ -2949,10 +2651,6 @@ void Player::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_ if (const Container* container = item->getContainer()) { onSendContainer(container); } - - if (shopOwner && requireListUpdate) { - updateSaleShopList(item); - } } else if (const Creature* creature = thing->getCreature()) { if (creature == this) { //check containers @@ -2972,28 +2670,14 @@ void Player::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_ } } -void Player::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/) +void Player::postRemoveNotification(Thing* thing, const Cylinder*, int32_t index, cylinderlink_t link /*= LINK_OWNER*/) { if (link == LINK_OWNER) { //calling movement scripts g_moveEvents->onPlayerDeEquip(this, thing->getItem(), static_cast(index)); } - bool requireListUpdate = false; - if (link == LINK_OWNER || link == LINK_TOPPARENT) { - const Item* i = (newParent ? newParent->getItem() : nullptr); - - // Check if we owned the old container too, so we don't need to do anything, - // as the list was updated in postRemoveNotification - assert(i ? i->getContainer() != nullptr : true); - - if (i) { - requireListUpdate = i->getContainer()->getHoldingPlayer() != this; - } else { - requireListUpdate = newParent != this; - } - updateInventoryWeight(); updateItemsLight(); sendStats(); @@ -3006,11 +2690,11 @@ void Player::postRemoveNotification(Thing* thing, const Cylinder* newParent, int } else if (container->getTopParent() == this) { onSendContainer(container); } else if (const Container* topContainer = dynamic_cast(container->getTopParent())) { - if (const DepotChest* depotChest = dynamic_cast(topContainer)) { + if (const DepotLocker* depotLocker = dynamic_cast(topContainer)) { bool isOwner = false; - for (const auto& it : depotChests) { - if (it.second == depotChest) { + for (const auto& it : depotLockerMap) { + if (it.second == depotLocker) { isOwner = true; onSendContainer(container); } @@ -3026,45 +2710,9 @@ void Player::postRemoveNotification(Thing* thing, const Cylinder* newParent, int autoCloseContainers(container); } } - - if (shopOwner && requireListUpdate) { - updateSaleShopList(item); - } } } -bool Player::updateSaleShopList(const Item* item) -{ - uint16_t itemId = item->getID(); - if (itemId != ITEM_GOLD_COIN && itemId != ITEM_PLATINUM_COIN && itemId != ITEM_CRYSTAL_COIN) { - auto it = std::find_if(shopItemList.begin(), shopItemList.end(), [itemId](const ShopInfo& shopInfo) { return shopInfo.itemId == itemId && shopInfo.sellPrice != 0; }); - if (it == shopItemList.end()) { - const Container* container = item->getContainer(); - if (!container) { - return false; - } - - const auto& items = container->getItemList(); - return std::any_of(items.begin(), items.end(), [this](const Item* containerItem) { - return updateSaleShopList(containerItem); - }); - } - } - - if (client) { - client->sendSaleItemList(shopItemList); - } - return true; -} - -bool Player::hasShopItemForSale(uint32_t itemId, uint8_t subType) const -{ - const ItemType& itemType = Item::items[itemId]; - return std::any_of(shopItemList.begin(), shopItemList.end(), [&](const ShopInfo& shopInfo) { - return shopInfo.itemId == itemId && shopInfo.buyPrice != 0 && (!itemType.isFluidContainer() || shopInfo.subType == subType); - }); -} - void Player::internalAddThing(Thing* thing) { internalAddThing(0, thing); @@ -3088,6 +2736,51 @@ void Player::internalAddThing(uint32_t index, Thing* thing) } } +uint32_t Player::checkPlayerKilling() +{ + time_t today = std::time(nullptr); + int32_t lastDay = 0; + int32_t lastWeek = 0; + int32_t lastMonth = 0; + int64_t egibleMurders = 0; + + time_t dayTimestamp = today - (24 * 60 * 60); + time_t weekTimestamp = today - (7 * 24 * 60 * 60); + time_t monthTimestamp = today - (30 * 24 * 60 * 60); + + for (time_t currentMurderTimestamp : murderTimeStamps) { + if (currentMurderTimestamp > dayTimestamp) { + lastDay++; + } + + if (currentMurderTimestamp > weekTimestamp) { + lastWeek++; + } + + egibleMurders = lastMonth + 1; + + if (currentMurderTimestamp <= monthTimestamp) { + egibleMurders = lastMonth; + } + + lastMonth = egibleMurders; + } + + if (lastDay >= g_config.getNumber(ConfigManager::KILLS_DAY_BANISHMENT) || + lastWeek >= g_config.getNumber(ConfigManager::KILLS_WEEK_BANISHMENT) || + lastMonth >= g_config.getNumber(ConfigManager::KILLS_MONTH_BANISHMENT)) { + return 2; // banishment! + } + + if (lastDay >= g_config.getNumber(ConfigManager::KILLS_DAY_RED_SKULL) || + lastWeek >= g_config.getNumber(ConfigManager::KILLS_WEEK_RED_SKULL) || + lastMonth >= g_config.getNumber(ConfigManager::KILLS_MONTH_RED_SKULL)) { + return 1; // red skull! + } + + return 0; +} + bool Player::setFollowCreature(Creature* creature) { if (!Creature::setFollowCreature(creature)) { @@ -3109,7 +2802,7 @@ bool Player::setAttackedCreature(Creature* creature) return false; } - if (chaseMode && creature) { + if (chaseMode == CHASEMODE_FOLLOW && creature) { if (followCreature != creature) { //chase opponent setFollowCreature(creature); @@ -3156,33 +2849,8 @@ void Player::doAttacking(uint32_t) } if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) { - bool result = false; - - Item* tool = getWeapon(); - const Weapon* weapon = g_weapons->getWeapon(tool); - uint32_t delay = getAttackSpeed(); - bool classicSpeed = g_config.getBoolean(ConfigManager::CLASSIC_ATTACK_SPEED); - - if (weapon) { - if (!weapon->interruptSwing()) { - result = weapon->useWeapon(this, tool, attackedCreature); - } else if (!classicSpeed && !canDoAction()) { - delay = getNextActionTime(); - } else { - result = weapon->useWeapon(this, tool, attackedCreature); - } - } else { - result = Weapon::useFist(this, attackedCreature); - } - - SchedulerTask* task = createSchedulerTask(std::max(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID())); - if (!classicSpeed) { - setNextActionTask(task); - } else { - g_scheduler.addEvent(task); - } - - if (result) { + if (Combat::attack(this, attackedCreature)) { + earliestAttackTime = OTSYS_TIME() + 2000; lastAttack = OTSYS_TIME(); } } @@ -3206,13 +2874,13 @@ void Player::onFollowCreature(const Creature* creature) } } -void Player::setChaseMode(bool mode) +void Player::setChaseMode(chaseMode_t mode) { - bool prevChaseMode = chaseMode; + chaseMode_t prevChaseMode = chaseMode; chaseMode = mode; if (prevChaseMode != chaseMode) { - if (chaseMode) { + if (chaseMode == CHASEMODE_FOLLOW) { if (!followCreature && attackedCreature) { //chase opponent setFollowCreature(attackedCreature); @@ -3243,25 +2911,27 @@ void Player::stopWalk() cancelNextWalk = true; } -LightInfo Player::getCreatureLight() const +void Player::getCreatureLight(LightInfo& light) const { if (internalLight.level > itemsLight.level) { - return internalLight; + light = internalLight; + } else { + light = itemsLight; } - return itemsLight; } void Player::updateItemsLight(bool internal /*=false*/) { LightInfo maxLight; + LightInfo curLight; for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) { Item* item = inventory[i]; if (item) { - LightInfo curLight = item->getLightInfo(); + item->getLight(curLight); if (curLight.level > maxLight.level) { - maxLight = std::move(curLight); + maxLight = curLight; } } } @@ -3278,11 +2948,6 @@ void Player::updateItemsLight(bool internal /*=false*/) void Player::onAddCondition(ConditionType_t type) { Creature::onAddCondition(type); - - if (type == CONDITION_OUTFIT && isMounted()) { - dismount(); - } - sendIcons(); } @@ -3290,39 +2955,23 @@ void Player::onAddCombatCondition(ConditionType_t type) { switch (type) { case CONDITION_POISON: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are poisoned."); - break; + sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are poisoned."); + break; case CONDITION_DROWN: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drowning."); - break; + sendTextMessage(MESSAGE_STATUS_SMALL, "You are drowning."); + break; case CONDITION_PARALYZE: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are paralyzed."); - break; + sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are paralyzed."); + break; case CONDITION_DRUNK: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drunk."); - break; - - case CONDITION_CURSED: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are cursed."); - break; - - case CONDITION_FREEZING: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are freezing."); - break; - - case CONDITION_DAZZLED: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are dazzled."); - break; - - case CONDITION_BLEEDING: - sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are bleeding."); - break; + sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drunk."); + break; default: - break; + break; } } @@ -3335,7 +2984,7 @@ void Player::onEndCondition(ConditionType_t type) pzLocked = false; clearAttacked(); - if (getSkull() != SKULL_RED && getSkull() != SKULL_BLACK) { + if (getSkull() != SKULL_RED) { setSkull(SKULL_NONE); } } @@ -3390,30 +3039,28 @@ void Player::onAttackedCreature(Creature* target) } Player* targetPlayer = target->getPlayer(); - if (targetPlayer && !isPartner(targetPlayer) && !isGuildMate(targetPlayer)) { - if (!pzLocked && g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) { + if (targetPlayer) { + if (!pzLocked && g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { pzLocked = true; sendIcons(); } - if (getSkull() == SKULL_NONE && getSkullClient(targetPlayer) == SKULL_YELLOW) { - addAttacked(targetPlayer); - targetPlayer->sendCreatureSkull(this); - } else if (!targetPlayer->hasAttacked(this)) { - if (!pzLocked) { - pzLocked = true; - sendIcons(); - } - - if (!Combat::isInPvpZone(this, targetPlayer) && !isInWar(targetPlayer)) { + if (!isPartner(targetPlayer)) { + if (getSkull() == SKULL_NONE && getSkullClient(targetPlayer) == SKULL_YELLOW) { addAttacked(targetPlayer); - - if (targetPlayer->getSkull() == SKULL_NONE && getSkull() == SKULL_NONE) { - setSkull(SKULL_WHITE); - } - - if (getSkull() == SKULL_NONE) { - targetPlayer->sendCreatureSkull(this); + targetPlayer->sendCreatureSkull(this); + } else { + if (!targetPlayer->hasAttacked(this)) { + if (!Combat::isInPvpZone(this, targetPlayer) && !isInWar(targetPlayer)) { + addAttacked(targetPlayer); + if (targetPlayer->getSkull() == SKULL_NONE && getSkull() == SKULL_NONE) { + setSkull(SKULL_WHITE); + } + } + + if (getSkull() == SKULL_NONE) { + targetPlayer->sendCreatureSkull(this); + } } } } @@ -3490,26 +3137,23 @@ bool Player::onKilledCreature(Creature* target, bool lastHit/* = true*/) Creature::onKilledCreature(target, lastHit); - Player* targetPlayer = target->getPlayer(); - if (!targetPlayer) { - return false; - } - - if (targetPlayer->getZone() == ZONE_PVP) { - targetPlayer->setDropLoot(false); - targetPlayer->setSkillLoss(false); - } else if (!hasFlag(PlayerFlag_NotGainInFight) && !isPartner(targetPlayer)) { - if (!Combat::isInPvpZone(this, targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(this) && !isGuildMate(targetPlayer) && targetPlayer != this) { - if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) { - unjustified = true; - addUnjustifiedDead(targetPlayer); + if (Player* targetPlayer = target->getPlayer()) { + if (targetPlayer && targetPlayer->getZone() == ZONE_PVP) { + targetPlayer->setDropLoot(false); + targetPlayer->setLossSkill(false); + } else if (!hasFlag(PlayerFlag_NotGainInFight) && !isPartner(targetPlayer)) { + if (!Combat::isInPvpZone(this, targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(this) && targetPlayer != this) { + if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) { + unjustified = true; + addUnjustifiedDead(targetPlayer); + } } + } - if (lastHit && hasCondition(CONDITION_INFIGHT)) { - pzLocked = true; - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::WHITE_SKULL_TIME), 0); - addCondition(condition); - } + if (lastHit && hasCondition(CONDITION_INFIGHT)) { + pzLocked = true; + Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::WHITE_SKULL_TIME) * 1000, 0); + addCondition(condition); } } @@ -3592,7 +3236,8 @@ void Player::changeMana(int32_t manaChange) if (!hasFlag(PlayerFlag_HasInfiniteMana)) { if (manaChange > 0) { mana += std::min(manaChange, getMaxMana() - mana); - } else { + } + else { mana = std::max(0, mana + manaChange); } } @@ -3747,24 +3392,22 @@ Skulls_t Player::getSkullClient(const Creature* creature) const } const Player* player = creature->getPlayer(); - if (!player || player->getSkull() != SKULL_NONE) { - return Creature::getSkullClient(creature); - } + if (player && player->getSkull() == SKULL_NONE) { + if (isInWar(player)) { + return SKULL_GREEN; + } - if (isInWar(player)) { - return SKULL_GREEN; - } + if (!player->getGuildWarList().empty() && guild == player->getGuild()) { + return SKULL_GREEN; + } - if (!player->getGuildWarVector().empty() && guild == player->getGuild()) { - return SKULL_GREEN; - } + if (player->hasAttacked(this)) { + return SKULL_YELLOW; + } - if (player->hasAttacked(this)) { - return SKULL_YELLOW; - } - - if (isPartner(player)) { - return SKULL_GREEN; + if (isPartner(player)) { + return SKULL_GREEN; + } } return Creature::getSkullClient(creature); } @@ -3775,7 +3418,7 @@ bool Player::hasAttacked(const Player* attacked) const return false; } - return attackedSet.find(attacked->guid) != attackedSet.end(); + return attackedSet.find(attacked->id) != attackedSet.end(); } void Player::addAttacked(const Player* attacked) @@ -3784,7 +3427,7 @@ void Player::addAttacked(const Player* attacked) return; } - attackedSet.insert(attacked->guid); + attackedSet.insert(attacked->id); } void Player::removeAttacked(const Player* attacked) @@ -3810,29 +3453,48 @@ void Player::addUnjustifiedDead(const Player* attacked) return; } - sendTextMessage(MESSAGE_EVENT_ADVANCE, "Warning! The murder of " + attacked->getName() + " was not justified."); + // current unjustified kill! + murderTimeStamps.push_back(std::time(nullptr)); - skullTicks += g_config.getNumber(ConfigManager::FRAG_TIME); + sendTextMessage(MESSAGE_STATUS_WARNING, "Warning! The murder of " + attacked->getName() + " was not justified."); - if (getSkull() != SKULL_BLACK) { - if (g_config.getNumber(ConfigManager::KILLS_TO_BLACK) != 0 && skullTicks > (g_config.getNumber(ConfigManager::KILLS_TO_BLACK) - 1) * static_cast(g_config.getNumber(ConfigManager::FRAG_TIME))) { - setSkull(SKULL_BLACK); - } else if (getSkull() != SKULL_RED && g_config.getNumber(ConfigManager::KILLS_TO_RED) != 0 && skullTicks > (g_config.getNumber(ConfigManager::KILLS_TO_RED) - 1) * static_cast(g_config.getNumber(ConfigManager::FRAG_TIME))) { - setSkull(SKULL_RED); + if (playerKillerEnd == 0) { + // white skull time, it only sets on first kill! + playerKillerEnd = std::time(nullptr) + g_config.getNumber(ConfigManager::WHITE_SKULL_TIME); + } + + uint32_t murderResult = checkPlayerKilling(); + if (murderResult >= 1) { + // red skull player + playerKillerEnd = std::time(nullptr) + g_config.getNumber(ConfigManager::RED_SKULL_TIME); + setSkull(SKULL_RED); + + if (murderResult == 2) { + // banishment for too many unjustified kills + Database* db = Database::getInstance(); + + std::ostringstream ss; + ss << "INSERT INTO `account_bans` (`account_id`, `reason`, `banned_at`, `expires_at`, `banned_by`) VALUES ("; + ss << getAccount() << ", "; + ss << db->escapeString("Too many unjustified kills") << ", "; + ss << std::time(nullptr) << ", "; + ss << std::time(nullptr) + g_config.getNumber(ConfigManager::BAN_LENGTH) << ", "; + ss << "1);"; + + db->executeQuery(ss.str()); + + g_game.addMagicEffect(getPosition(), CONST_ME_GREEN_RINGS); + g_game.removeCreature(this); + disconnect(); } } } -void Player::checkSkullTicks(int64_t ticks) +void Player::checkSkullTicks() { - int64_t newTicks = skullTicks - ticks; - if (newTicks < 0) { - skullTicks = 0; - } else { - skullTicks = newTicks; - } + time_t today = std::time(nullptr); - if ((skull == SKULL_RED || skull == SKULL_BLACK) && skullTicks < 1 && !hasCondition(CONDITION_INFIGHT)) { + if (!hasCondition(CONDITION_INFIGHT) && ((skull == SKULL_RED && today >= playerKillerEnd) || (skull == SKULL_WHITE))) { setSkull(SKULL_NONE); } } @@ -3865,12 +3527,11 @@ double Player::getLostPercent() const lossPercent = 10; } - double percentReduction = 0; if (isPromoted()) { - percentReduction += 30; + lossPercent *= 0.7; } - percentReduction += blessingCount * 8; - return lossPercent * (1 - (percentReduction / 100.)) / 100.; + + return lossPercent * pow(0.92, blessingCount) / 100; } void Player::learnInstantSpell(const std::string& spellName) @@ -3919,7 +3580,7 @@ bool Player::isInWar(const Player* player) const bool Player::isInWarList(uint32_t guildId) const { - return std::find(guildWarVector.begin(), guildWarVector.end(), guildId) != guildWarVector.end(); + return std::find(guildWarList.begin(), guildWarList.end(), guildId) != guildWarList.end(); } bool Player::isPremium() const @@ -3934,7 +3595,6 @@ bool Player::isPremium() const void Player::setPremiumDays(int32_t v) { premiumDays = v; - sendBasicData(); } PartyShields_t Player::getPartyShield(const Player* player) const @@ -3945,34 +3605,10 @@ PartyShields_t Player::getPartyShield(const Player* player) const if (party) { if (party->getLeader() == player) { - if (party->isSharedExperienceActive()) { - if (party->isSharedExperienceEnabled()) { - return SHIELD_YELLOW_SHAREDEXP; - } - - if (party->canUseSharedExperience(player)) { - return SHIELD_YELLOW_NOSHAREDEXP; - } - - return SHIELD_YELLOW_NOSHAREDEXP_BLINK; - } - return SHIELD_YELLOW; } if (player->party == party) { - if (party->isSharedExperienceActive()) { - if (party->isSharedExperienceEnabled()) { - return SHIELD_BLUE_SHAREDEXP; - } - - if (party->canUseSharedExperience(player)) { - return SHIELD_BLUE_NOSHAREDEXP; - } - - return SHIELD_BLUE_NOSHAREDEXP_BLINK; - } - return SHIELD_BLUE; } @@ -3985,10 +3621,6 @@ PartyShields_t Player::getPartyShield(const Player* player) const return SHIELD_WHITEYELLOW; } - if (player->party) { - return SHIELD_GRAY; - } - return SHIELD_NONE; } @@ -4002,7 +3634,7 @@ bool Player::isInviting(const Player* player) const bool Player::isPartner(const Player* player) const { - if (!player || !party || player == this) { + if (!player || !party) { return false; } return party == player->party; @@ -4046,379 +3678,6 @@ void Player::clearPartyInvitations() invitePartyList.clear(); } -GuildEmblems_t Player::getGuildEmblem(const Player* player) const -{ - if (!player) { - return GUILDEMBLEM_NONE; - } - - const Guild* playerGuild = player->getGuild(); - if (!playerGuild) { - return GUILDEMBLEM_NONE; - } - - if (player->getGuildWarVector().empty()) { - if (guild == playerGuild) { - return GUILDEMBLEM_MEMBER; - } else { - return GUILDEMBLEM_OTHER; - } - } else if (guild == playerGuild) { - return GUILDEMBLEM_ALLY; - } else if (isInWar(player)) { - return GUILDEMBLEM_ENEMY; - } - - return GUILDEMBLEM_NEUTRAL; -} - -uint8_t Player::getCurrentMount() const -{ - int32_t value; - if (getStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, value)) { - return value; - } - return 0; -} - -void Player::setCurrentMount(uint8_t mountId) -{ - addStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, mountId); -} - -bool Player::toggleMount(bool mount) -{ - if ((OTSYS_TIME() - lastToggleMount) < 3000 && !wasMounted) { - sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED); - return false; - } - - if (mount) { - if (isMounted()) { - return false; - } - - if (!group->access && tile->hasFlag(TILESTATE_PROTECTIONZONE)) { - sendCancelMessage(RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE); - return false; - } - - const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType); - if (!playerOutfit) { - return false; - } - - uint8_t currentMountId = getCurrentMount(); - if (currentMountId == 0) { - sendOutfitWindow(); - return false; - } - - Mount* currentMount = g_game.mounts.getMountByID(currentMountId); - if (!currentMount) { - return false; - } - - if (!hasMount(currentMount)) { - setCurrentMount(0); - sendOutfitWindow(); - return false; - } - - if (currentMount->premium && !isPremium()) { - sendCancelMessage(RETURNVALUE_YOUNEEDPREMIUMACCOUNT); - return false; - } - - if (hasCondition(CONDITION_OUTFIT)) { - sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); - return false; - } - - defaultOutfit.lookMount = currentMount->clientId; - - if (currentMount->speed != 0) { - g_game.changeSpeed(this, currentMount->speed); - } - } else { - if (!isMounted()) { - return false; - } - - dismount(); - } - - g_game.internalCreatureChangeOutfit(this, defaultOutfit); - lastToggleMount = OTSYS_TIME(); - return true; -} - -bool Player::tameMount(uint8_t mountId) -{ - if (!g_game.mounts.getMountByID(mountId)) { - return false; - } - - const uint8_t tmpMountId = mountId - 1; - const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31); - - int32_t value; - if (getStorageValue(key, value)) { - value |= (1 << (tmpMountId % 31)); - } else { - value = (1 << (tmpMountId % 31)); - } - - addStorageValue(key, value); - return true; -} - -bool Player::untameMount(uint8_t mountId) -{ - if (!g_game.mounts.getMountByID(mountId)) { - return false; - } - - const uint8_t tmpMountId = mountId - 1; - const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31); - - int32_t value; - if (!getStorageValue(key, value)) { - return true; - } - - value &= ~(1 << (tmpMountId % 31)); - addStorageValue(key, value); - - if (getCurrentMount() == mountId) { - if (isMounted()) { - dismount(); - g_game.internalCreatureChangeOutfit(this, defaultOutfit); - } - - setCurrentMount(0); - } - - return true; -} - -bool Player::hasMount(const Mount* mount) const -{ - if (isAccessPlayer()) { - return true; - } - - if (mount->premium && !isPremium()) { - return false; - } - - const uint8_t tmpMountId = mount->id - 1; - - int32_t value; - if (!getStorageValue(PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31), value)) { - return false; - } - - return ((1 << (tmpMountId % 31)) & value) != 0; -} - -void Player::dismount() -{ - Mount* mount = g_game.mounts.getMountByID(getCurrentMount()); - if (mount && mount->speed > 0) { - g_game.changeSpeed(this, -mount->speed); - } - - defaultOutfit.lookMount = 0; -} - -bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries) -{ - if (tries == 0 || skill == SKILL_LEVEL) { - return false; - } - - bool sendUpdate = false; - uint32_t oldSkillValue, newSkillValue; - long double oldPercentToNextLevel, newPercentToNextLevel; - - if (skill == SKILL_MAGLEVEL) { - uint64_t currReqMana = vocation->getReqMana(magLevel); - uint64_t nextReqMana = vocation->getReqMana(magLevel + 1); - - if (currReqMana >= nextReqMana) { - return false; - } - - oldSkillValue = magLevel; - oldPercentToNextLevel = static_cast(manaSpent * 100) / nextReqMana; - - g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, tries); - uint32_t currMagLevel = magLevel; - - while ((manaSpent + tries) >= nextReqMana) { - tries -= nextReqMana - manaSpent; - - magLevel++; - manaSpent = 0; - - g_creatureEvents->playerAdvance(this, SKILL_MAGLEVEL, magLevel - 1, magLevel); - - sendUpdate = true; - currReqMana = nextReqMana; - nextReqMana = vocation->getReqMana(magLevel + 1); - - if (currReqMana >= nextReqMana) { - tries = 0; - break; - } - } - - manaSpent += tries; - - if (magLevel != currMagLevel) { - std::ostringstream ss; - ss << "You advanced to magic level " << magLevel << '.'; - sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str()); - } - - uint8_t newPercent; - if (nextReqMana > currReqMana) { - newPercent = Player::getPercentLevel(manaSpent, nextReqMana); - newPercentToNextLevel = static_cast(manaSpent * 100) / nextReqMana; - } else { - newPercent = 0; - newPercentToNextLevel = 0; - } - - if (newPercent != magLevelPercent) { - magLevelPercent = newPercent; - sendUpdate = true; - } - - newSkillValue = magLevel; - } else { - uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level); - uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1); - if (currReqTries >= nextReqTries) { - return false; - } - - oldSkillValue = skills[skill].level; - oldPercentToNextLevel = static_cast(skills[skill].tries * 100) / nextReqTries; - - g_events->eventPlayerOnGainSkillTries(this, skill, tries); - uint32_t currSkillLevel = skills[skill].level; - - while ((skills[skill].tries + tries) >= nextReqTries) { - tries -= nextReqTries - skills[skill].tries; - - skills[skill].level++; - skills[skill].tries = 0; - skills[skill].percent = 0; - - g_creatureEvents->playerAdvance(this, skill, (skills[skill].level - 1), skills[skill].level); - - sendUpdate = true; - currReqTries = nextReqTries; - nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1); - - if (currReqTries >= nextReqTries) { - tries = 0; - break; - } - } - - skills[skill].tries += tries; - - if (currSkillLevel != skills[skill].level) { - std::ostringstream ss; - ss << "You advanced to " << getSkillName(skill) << " level " << skills[skill].level << '.'; - sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str()); - } - - uint8_t newPercent; - if (nextReqTries > currReqTries) { - newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries); - newPercentToNextLevel = static_cast(skills[skill].tries * 100) / nextReqTries; - } else { - newPercent = 0; - newPercentToNextLevel = 0; - } - - if (skills[skill].percent != newPercent) { - skills[skill].percent = newPercent; - sendUpdate = true; - } - - newSkillValue = skills[skill].level; - } - - if (sendUpdate) { - sendSkills(); - } - - std::ostringstream ss; - ss << std::fixed << std::setprecision(2) << "Your " << ucwords(getSkillName(skill)) << " skill changed from level " << oldSkillValue << " (with " << oldPercentToNextLevel << "% progress towards level " << (oldSkillValue + 1) << ") to level " << newSkillValue << " (with " << newPercentToNextLevel << "% progress towards level " << (newSkillValue + 1) << ')'; - sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str()); - return sendUpdate; -} - -bool Player::hasModalWindowOpen(uint32_t modalWindowId) const -{ - return find(modalWindows.begin(), modalWindows.end(), modalWindowId) != modalWindows.end(); -} - -void Player::onModalWindowHandled(uint32_t modalWindowId) -{ - modalWindows.remove(modalWindowId); -} - -void Player::sendModalWindow(const ModalWindow& modalWindow) -{ - if (!client) { - return; - } - - modalWindows.push_front(modalWindow.id); - client->sendModalWindow(modalWindow); -} - -void Player::clearModalWindows() -{ - modalWindows.clear(); -} - -uint16_t Player::getHelpers() const -{ - uint16_t helpers; - - if (guild && party) { - std::unordered_set helperSet; - - const auto& guildMembers = guild->getMembersOnline(); - helperSet.insert(guildMembers.begin(), guildMembers.end()); - - const auto& partyMembers = party->getMembers(); - helperSet.insert(partyMembers.begin(), partyMembers.end()); - - const auto& partyInvitees = party->getInvitees(); - helperSet.insert(partyInvitees.begin(), partyInvitees.end()); - - helperSet.insert(party->getLeader()); - - helpers = helperSet.size(); - } else if (guild) { - helpers = guild->getMembersOnline().size(); - } else if (party) { - helpers = party->getMemberCount() + party->getInvitationCount() + 1; - } else { - helpers = 0; - } - - return helpers; -} - void Player::sendClosePrivate(uint16_t channelId) { if (channelId == CHANNEL_GUILD || channelId == CHANNEL_PARTY) { @@ -4481,6 +3740,7 @@ size_t Player::getMaxDepotItems() const } else if (isPremium()) { return 2000; } + return 1000; } diff --git a/src/player.h b/src/player.h index db0dc4e..f088f13 100644 --- a/src/player.h +++ b/src/player.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,14 +29,12 @@ #include "protocolgame.h" #include "ioguild.h" #include "party.h" -#include "inbox.h" -#include "depotchest.h" #include "depotlocker.h" #include "guild.h" #include "groups.h" #include "town.h" -#include "mounts.h" +class BehaviourDatabase; class House; class NetworkMessage; class Weapon; @@ -53,17 +51,9 @@ enum skillsid_t { SKILLVALUE_PERCENT = 2, }; -enum fightMode_t : uint8_t { - FIGHTMODE_ATTACK = 1, - FIGHTMODE_BALANCED = 2, - FIGHTMODE_DEFENSE = 3, -}; - -enum pvpMode_t : uint8_t { - PVP_MODE_DOVE = 0, - PVP_MODE_WHITE_HAND = 1, - PVP_MODE_YELLOW_HAND = 2, - PVP_MODE_RED_FIST = 3, +enum chaseMode_t : uint8_t { + CHASEMODE_STANDSTILL = 0, + CHASEMODE_FOLLOW = 1, }; enum tradestate_t : uint8_t { @@ -75,14 +65,11 @@ enum tradestate_t : uint8_t { }; struct VIPEntry { - VIPEntry(uint32_t guid, std::string name, std::string description, uint32_t icon, bool notify) : - guid(guid), name(std::move(name)), description(std::move(description)), icon(icon), notify(notify) {} + VIPEntry(uint32_t guid, std::string name) : + guid(guid), name(std::move(name)) {} uint32_t guid; std::string name; - std::string description; - uint32_t icon; - bool notify; }; struct OpenContainer { @@ -103,10 +90,10 @@ struct Skill { uint8_t percent = 0; }; -using MuteCountMap = std::map; +typedef std::map MuteCountMap; static constexpr int32_t PLAYER_MAX_SPEED = 1500; -static constexpr int32_t PLAYER_MIN_SPEED = 10; +static constexpr int32_t PLAYER_MIN_SPEED = 0; class Player final : public Creature, public Cylinder { @@ -118,14 +105,14 @@ class Player final : public Creature, public Cylinder Player(const Player&) = delete; Player& operator=(const Player&) = delete; - Player* getPlayer() override { + Player* getPlayer() final { return this; } - const Player* getPlayer() const override { + const Player* getPlayer() const final { return this; } - void setID() override { + void setID() final { if (id == 0) { id = playerAutoID++; } @@ -133,37 +120,16 @@ class Player final : public Creature, public Cylinder static MuteCountMap muteCountMap; - const std::string& getName() const override { + const std::string& getName() const final { return name; } void setName(std::string name) { this->name = std::move(name); } - const std::string& getNameDescription() const override { + const std::string& getNameDescription() const final { return name; } - std::string getDescription(int32_t lookDistance) const override; - - CreatureType_t getType() const override { - return CREATURETYPE_PLAYER; - } - - uint8_t getCurrentMount() const; - void setCurrentMount(uint8_t mountId); - bool isMounted() const { - return defaultOutfit.lookMount != 0; - } - bool toggleMount(bool mount); - bool tameMount(uint8_t mountId); - bool untameMount(uint8_t mountId); - bool hasMount(const Mount* mount) const; - void dismount(); - - void sendFYIBox(const std::string& message) { - if (client) { - client->sendFYIBox(message); - } - } + std::string getDescription(int32_t lookDistance) const final; void setGUID(uint32_t guid) { this->guid = guid; @@ -171,12 +137,12 @@ class Player final : public Creature, public Cylinder uint32_t getGUID() const { return guid; } - bool canSeeInvisibility() const override { + bool canSeeInvisibility() const final { return hasFlag(PlayerFlag_CanSenseInvisibility) || group->access; } - void removeList() override; - void addList() override; + void removeList() final; + void addList() final; void kickPlayer(bool displayEffect); static uint64_t getExpForLevel(int32_t lv) { @@ -188,25 +154,6 @@ class Player final : public Creature, public Cylinder return staminaMinutes; } - bool addOfflineTrainingTries(skills_t skill, uint64_t tries); - - void addOfflineTrainingTime(int32_t addTime) { - offlineTrainingTime = std::min(12 * 3600 * 1000, offlineTrainingTime + addTime); - } - void removeOfflineTrainingTime(int32_t removeTime) { - offlineTrainingTime = std::max(0, offlineTrainingTime - removeTime); - } - int32_t getOfflineTrainingTime() const { - return offlineTrainingTime; - } - - int32_t getOfflineTrainingSkill() const { - return offlineTrainingSkill; - } - void setOfflineTrainingSkill(int32_t skill) { - offlineTrainingSkill = skill; - } - uint64_t getBankBalance() const { return bankBalance; } @@ -236,23 +183,12 @@ class Player final : public Creature, public Cylinder } bool isInWar(const Player* player) const; - bool isInWarList(uint32_t guildId) const; - - void setLastWalkthroughAttempt(int64_t walkthroughAttempt) { - lastWalkthroughAttempt = walkthroughAttempt; - } - void setLastWalkthroughPosition(Position walkthroughPosition) { - lastWalkthroughPosition = walkthroughPosition; - } - - Inbox* getInbox() const { - return inbox; - } + bool isInWarList(uint32_t guild_id) const; uint16_t getClientIcons() const; - const GuildWarVector& getGuildWarVector() const { - return guildWarVector; + const GuildWarList& getGuildWarList() const { + return guildWarList; } Vocation* getVocation() const { @@ -292,8 +228,6 @@ class Player final : public Creature, public Cylinder void removePartyInvitation(Party* party); void clearPartyInvitations(); - GuildEmblems_t getGuildEmblem(const Player* player) const; - uint64_t getSpentMana() const { return manaSpent; } @@ -339,7 +273,7 @@ class Player final : public Creature, public Cylinder bool canOpenCorpse(uint32_t ownerId) const; - void addStorageValue(const uint32_t key, const int32_t value, const bool isLogin = false); + void addStorageValue(const uint32_t key, const int32_t value); bool getStorageValue(const uint32_t key, int32_t& value) const; void genReservedStorageRange(); @@ -350,25 +284,24 @@ class Player final : public Creature, public Cylinder return group; } - void setInMarket(bool value) { - inMarket = value; - } - bool isInMarket() const { - return inMarket; - } - - void setLastDepotId(int16_t newId) { - lastDepotId = newId; - } - int16_t getLastDepotId() const { - return lastDepotId; - } - void resetIdleTime() { idleTime = 0; + resetLastWalkingTime(); } - bool isInGhostMode() const override { + int32_t getIdleTime() const { + return idleTime; + } + + void resetLastWalkingTime() { + lastWalkingTime = 0; + } + + int32_t getLastWalkingTime() const { + return lastWalkingTime; + } + + bool isInGhostMode() const { return ghostMode; } void switchGhostMode() { @@ -405,12 +338,13 @@ class Player final : public Creature, public Cylinder bool isPremium() const; void setPremiumDays(int32_t v); - uint16_t getHelpers() const; - bool setVocation(uint16_t vocId); uint16_t getVocationId() const { return vocation->getId(); } + uint16_t getVocationFlagId() const { + return vocation->getFlagId(); + } PlayerSex_t getSex() const { return sex; @@ -441,11 +375,7 @@ class Player final : public Creature, public Cylinder this->town = town; } - void clearModalWindows(); - bool hasModalWindowOpen(uint32_t modalWindowId) const; - void onModalWindowHandled(uint32_t modalWindowId); - - bool isPushable() const override; + bool isPushable() const final; uint32_t isMuted() const; void addMessageBuffer(); void removeMessageBuffer(); @@ -471,12 +401,14 @@ class Player final : public Creature, public Cylinder } } - int32_t getMaxHealth() const override { + int32_t getMaxHealth() const final { return std::max(1, healthMax + varStats[STAT_MAXHITPOINTS]); } + uint32_t getMana() const { return mana; } + uint32_t getMaxMana() const { return std::max(0, manaMax + varStats[STAT_MAXMANAPOINTS]); } @@ -494,28 +426,20 @@ class Player final : public Creature, public Cylinder varSkills[skill] += modifier; } - void setVarSpecialSkill(SpecialSkills_t skill, int32_t modifier) { - varSpecialSkills[skill] += modifier; - } - void setVarStats(stats_t stat, int32_t modifier); int32_t getDefaultStats(stats_t stat) const; void addConditionSuppressions(uint32_t conditions); void removeConditionSuppressions(uint32_t conditions); - DepotChest* getDepotChest(uint32_t depotId, bool autoCreate); - DepotLocker* getDepotLocker(uint32_t depotId); - void onReceiveMail() const; - bool isNearDepotBox() const; + DepotLocker* getDepotLocker(uint32_t depotId, bool autoCreate); + void onReceiveMail(uint32_t townId) const; + bool isNearDepotBox(uint32_t townId) const; - bool canSee(const Position& pos) const override; - bool canSeeCreature(const Creature* creature) const override; + bool canSee(const Position& pos) const final; + bool canSeeCreature(const Creature* creature) const final; - bool canWalkthrough(const Creature* creature) const; - bool canWalkthroughEx(const Creature* creature) const; - - RaceType_t getRace() const override { + RaceType_t getRace() const final { return RACE_BLOOD; } @@ -532,51 +456,27 @@ class Player final : public Creature, public Cylinder return tradeItem; } - //shop functions - void setShopOwner(Npc* owner, int32_t onBuy, int32_t onSell) { - shopOwner = owner; - purchaseCallback = onBuy; - saleCallback = onSell; - } - - Npc* getShopOwner(int32_t& onBuy, int32_t& onSell) { - onBuy = purchaseCallback; - onSell = saleCallback; - return shopOwner; - } - - const Npc* getShopOwner(int32_t& onBuy, int32_t& onSell) const { - onBuy = purchaseCallback; - onSell = saleCallback; - return shopOwner; - } - //V.I.P. functions - void notifyStatusChange(Player* loginPlayer, VipStatus_t status); + void notifyStatusChange(Player* player, VipStatus_t status); bool removeVIP(uint32_t vipGuid); bool addVIP(uint32_t vipGuid, const std::string& vipName, VipStatus_t status); bool addVIPInternal(uint32_t vipGuid); - bool editVIP(uint32_t vipGuid, const std::string& description, uint32_t icon, bool notify); //follow functions - bool setFollowCreature(Creature* creature) override; - void goToFollowCreature() override; + bool setFollowCreature(Creature* creature) final; + void goToFollowCreature() final; //follow events - void onFollowCreature(const Creature* creature) override; + void onFollowCreature(const Creature* creature) final; //walk events - void onWalk(Direction& dir) override; - void onWalkAborted() override; - void onWalkComplete() override; + void onWalk(Direction& dir) final; + void onWalkAborted() final; + void onWalkComplete() final; void stopWalk(); - void openShopWindow(Npc* npc, const std::list& shop); - bool closeShopWindow(bool sendCloseShopWindow = true); - bool updateSaleShopList(const Item* item); - bool hasShopItemForSale(uint32_t itemId, uint8_t subType) const; - void setChaseMode(bool mode); + void setChaseMode(chaseMode_t mode); void setFightMode(fightMode_t mode) { fightMode = mode; } @@ -585,14 +485,14 @@ class Player final : public Creature, public Cylinder } //combat functions - bool setAttackedCreature(Creature* creature) override; - bool isImmune(CombatType_t type) const override; - bool isImmune(ConditionType_t type) const override; + bool setAttackedCreature(Creature* creature) final; + bool isImmune(CombatType_t type) const final; + bool isImmune(ConditionType_t type) const final; bool hasShield() const; - bool isAttackable() const override; + bool isAttackable() const final; static bool lastHitIsPlayer(Creature* lastHitCreature); - void changeHealth(int32_t healthChange, bool sendHealthChange = true) override; + void changeHealth(int32_t healthChange, bool sendHealthChange = true) final; void changeMana(int32_t manaChange); void changeSoul(int32_t soulChange); @@ -600,15 +500,12 @@ class Player final : public Creature, public Cylinder return pzLocked; } BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, - bool checkDefense = false, bool checkArmor = false, bool field = false) override; - void doAttacking(uint32_t interval) override; - bool hasExtraSwing() override { + bool checkDefense = false, bool checkArmor = false, bool field = false) final; + void doAttacking(uint32_t interval) final; + bool hasExtraSwing() final { return lastAttack > 0 && ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()); } - uint16_t getSpecialSkill(uint8_t skill) const { - return std::max(0, varSpecialSkills[skill]); - } uint16_t getSkillLevel(uint8_t skill) const { return std::max(0, skills[skill].level + varSkills[skill]); } @@ -626,51 +523,48 @@ class Player final : public Creature, public Cylinder return lastAttackBlockType; } - Item* getWeapon(slots_t slot, bool ignoreAmmo) const; - Item* getWeapon(bool ignoreAmmo = false) const; - WeaponType_t getWeaponType() const; - int32_t getWeaponSkill(const Item* item) const; + Item* getWeapon() const; + Item* getAmmunition() const; void getShieldAndWeapon(const Item*& shield, const Item*& weapon) const; - void drainHealth(Creature* attacker, int32_t damage) override; + void drainHealth(Creature* attacker, int32_t damage) final; void drainMana(Creature* attacker, int32_t manaLoss); void addManaSpent(uint64_t amount); void addSkillAdvance(skills_t skill, uint64_t count); - int32_t getArmor() const override; - int32_t getDefense() const override; - float getAttackFactor() const override; - float getDefenseFactor() const override; + int32_t getArmor() const final; + int32_t getDefense() final; + fightMode_t getFightMode() const; void addInFightTicks(bool pzlock = false); - uint64_t getGainedExperience(Creature* attacker) const override; + uint64_t getGainedExperience(Creature* attacker) const final; //combat event functions - void onAddCondition(ConditionType_t type) override; - void onAddCombatCondition(ConditionType_t type) override; - void onEndCondition(ConditionType_t type) override; - void onCombatRemoveCondition(Condition* condition) override; - void onAttackedCreature(Creature* target) override; - void onAttacked() override; - void onAttackedCreatureDrainHealth(Creature* target, int32_t points) override; - void onTargetCreatureGainHealth(Creature* target, int32_t points) override; - bool onKilledCreature(Creature* target, bool lastHit = true) override; - void onGainExperience(uint64_t gainExp, Creature* target) override; + void onAddCondition(ConditionType_t type) final; + void onAddCombatCondition(ConditionType_t type) final; + void onEndCondition(ConditionType_t type) final; + void onCombatRemoveCondition(Condition* condition) final; + void onAttackedCreature(Creature* target) final; + void onAttacked() final; + void onAttackedCreatureDrainHealth(Creature* target, int32_t points) final; + void onTargetCreatureGainHealth(Creature* target, int32_t points) final; + bool onKilledCreature(Creature* target, bool lastHit = true) final; + void onGainExperience(uint64_t gainExp, Creature* target) final; void onGainSharedExperience(uint64_t gainExp, Creature* source); - void onAttackedCreatureBlockHit(BlockType_t blockType) override; - void onBlockHit() override; - void onChangeZone(ZoneType_t zone) override; - void onAttackedCreatureChangeZone(ZoneType_t zone) override; - void onIdleStatus() override; - void onPlacedCreature() override; + void onAttackedCreatureBlockHit(BlockType_t blockType) final; + void onBlockHit() final; + void onChangeZone(ZoneType_t zone) final; + void onAttackedCreatureChangeZone(ZoneType_t zone) final; + void onIdleStatus() final; + void onPlacedCreature() final; - LightInfo getCreatureLight() const override; + void getCreatureLight(LightInfo& light) const final; - Skulls_t getSkull() const override; - Skulls_t getSkullClient(const Creature* creature) const override; - int64_t getSkullTicks() const { return skullTicks; } - void setSkullTicks(int64_t ticks) { skullTicks = ticks; } + Skulls_t getSkull() const final; + Skulls_t getSkullClient(const Creature* creature) const final; + time_t getPlayerKillerEnd() const { return playerKillerEnd; } + void setPlayerKillerEnd(time_t ticks) { playerKillerEnd = ticks; } bool hasAttacked(const Player* attacked) const; void addAttacked(const Player* attacked); @@ -682,7 +576,7 @@ class Player final : public Creature, public Cylinder client->sendCreatureSkull(creature); } } - void checkSkullTicks(int64_t ticks); + void checkSkullTicks(); bool canWear(uint32_t lookType, uint8_t addons) const; void addOutfit(uint16_t lookType, uint8_t addons); @@ -690,6 +584,7 @@ class Player final : public Creature, public Cylinder bool removeOutfitAddon(uint16_t lookType, uint8_t addons); bool getOutfitAddons(const Outfit& outfit, uint8_t& addons) const; + bool canLogout(); size_t getMaxVIPEntries() const; @@ -701,7 +596,7 @@ class Player final : public Creature, public Cylinder if (client) { int32_t stackpos = tile->getStackposOfItem(this, item); if (stackpos != -1) { - client->sendAddTileItem(pos, stackpos, item); + client->sendAddTileItem(pos, item, stackpos); } } } @@ -724,16 +619,6 @@ class Player final : public Creature, public Cylinder } } - void sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel) { - if (client) { - client->sendChannelMessage(author, text, type, channel); - } - } - void sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent) { - if (client) { - client->sendChannelEvent(channelId, playerName, channelEvent); - } - } void sendCreatureAppear(const Creature* creature, const Position& pos, bool isLogin) { if (client) { client->sendAddCreature(creature, pos, creature->getTile()->getStackposOfCreature(this, creature), isLogin); @@ -804,37 +689,11 @@ class Player final : public Creature, public Cylinder client->sendCreatureLight(creature); } } - void sendCreatureWalkthrough(const Creature* creature, bool walkthrough) { - if (client) { - client->sendCreatureWalkthrough(creature, walkthrough); - } - } void sendCreatureShield(const Creature* creature) { if (client) { client->sendCreatureShield(creature); } } - void sendCreatureType(uint32_t creatureId, uint8_t creatureType) { - if (client) { - client->sendCreatureType(creatureId, creatureType); - } - } - void sendCreatureHelpers(uint32_t creatureId, uint16_t helpers) { - if (client) { - client->sendCreatureHelpers(creatureId, helpers); - } - } - void sendSpellCooldown(uint8_t spellId, uint32_t time) { - if (client) { - client->sendSpellCooldown(spellId, time); - } - } - void sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time) { - if (client) { - client->sendSpellGroupCooldown(groupId, time); - } - } - void sendModalWindow(const ModalWindow& modalWindow); //container void sendAddContainerItem(const Container* container, const Item* item); @@ -852,25 +711,20 @@ class Player final : public Creature, public Cylinder client->sendInventoryItem(slot, item); } } - void sendItems() { - if (client) { - client->sendItems(); - } - } //event methods void onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem, - const ItemType& oldType, const Item* newItem, const ItemType& newType) override; + const ItemType& oldType, const Item* newItem, const ItemType& newType) final; void onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType, - const Item* item) override; + const Item* item) final; - void onCreatureAppear(Creature* creature, bool isLogin) override; - void onRemoveCreature(Creature* creature, bool isLogout) override; + void onCreatureAppear(Creature* creature, bool isLogin) final; + void onRemoveCreature(Creature* creature, bool isLogout) final; void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, - const Tile* oldTile, const Position& oldPos, bool teleport) override; + const Tile* oldTile, const Position& oldPos, bool teleport) final; - void onAttackedCreatureDisappear(bool isLogout) override; - void onFollowCreatureDisappear(bool isLogout) override; + void onAttackedCreatureDisappear(bool isLogout) final; + void onFollowCreatureDisappear(bool isLogout) final; //container void onAddContainerItem(const Item* item); @@ -940,16 +794,16 @@ class Player final : public Creature, public Cylinder } } void sendStats(); - void sendBasicData() const { - if (client) { - client->sendBasicData(); - } - } void sendSkills() const { if (client) { client->sendSkills(); } } + void sendAnimatedText(const Position& pos, uint8_t color, const std::string& text) const { + if (client) { + client->sendAnimatedText(pos, color, text); + } + } void sendTextMessage(MessageClasses mclass, const std::string& message) const { if (client) { client->sendTextMessage(TextMessage(mclass, message)); @@ -960,11 +814,6 @@ class Player final : public Creature, public Cylinder client->sendTextMessage(message); } } - void sendReLoginWindow(uint8_t unfairFightReduction) const { - if (client) { - client->sendReLoginWindow(unfairFightReduction); - } - } void sendTextWindow(Item* item, uint16_t maxlen, bool canWrite) const { if (client) { client->sendTextWindow(windowTextId, item, maxlen, canWrite); @@ -980,62 +829,6 @@ class Player final : public Creature, public Cylinder client->sendToChannel(creature, type, text, channelId); } } - void sendShop(Npc* npc) const { - if (client) { - client->sendShop(npc, shopItemList); - } - } - void sendSaleItemList() const { - if (client) { - client->sendSaleItemList(shopItemList); - } - } - void sendCloseShop() const { - if (client) { - client->sendCloseShop(); - } - } - void sendMarketEnter(uint32_t depotId) const { - if (client) { - client->sendMarketEnter(depotId); - } - } - void sendMarketLeave() { - inMarket = false; - if (client) { - client->sendMarketLeave(); - } - } - void sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) const { - if (client) { - client->sendMarketBrowseItem(itemId, buyOffers, sellOffers); - } - } - void sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) const { - if (client) { - client->sendMarketBrowseOwnOffers(buyOffers, sellOffers); - } - } - void sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers) const { - if (client) { - client->sendMarketBrowseOwnHistory(buyOffers, sellOffers); - } - } - void sendMarketDetail(uint16_t itemId) const { - if (client) { - client->sendMarketDetail(itemId); - } - } - void sendMarketAcceptOffer(const MarketOfferEx& offer) const { - if (client) { - client->sendMarketAcceptOffer(offer); - } - } - void sendMarketCancelOffer(const MarketOfferEx& offer) const { - if (client) { - client->sendMarketCancelOffer(offer); - } - } void sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack) const { if (client) { client->sendTradeItemRequest(traderName, item, ack); @@ -1046,7 +839,7 @@ class Player final : public Creature, public Cylinder client->sendCloseTrade(); } } - void sendWorldLight(LightInfo lightInfo) { + void sendWorldLight(const LightInfo& lightInfo) { if (client) { client->sendWorldLight(lightInfo); } @@ -1071,35 +864,30 @@ class Player final : public Creature, public Cylinder client->sendCloseContainer(cid); } } + void sendRemoveRuleViolationReport(const std::string& name) const { + if (client) { + client->sendRemoveRuleViolationReport(name); + } + } + void sendRuleViolationCancel(const std::string& name) const { + if (client) { + client->sendRuleViolationCancel(name); + } + } + void sendLockRuleViolationReport() const { + if (client) { + client->sendLockRuleViolation(); + } + } + void sendRuleViolationsChannel(uint16_t channelId) const { + if (client) { + client->sendRuleViolationsChannel(channelId); + } + } - void sendChannel(uint16_t channelId, const std::string& channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers) { + void sendChannel(uint16_t channelId, const std::string& channelName) { if (client) { - client->sendChannel(channelId, channelName, channelUsers, invitedUsers); - } - } - void sendTutorial(uint8_t tutorialId) { - if (client) { - client->sendTutorial(tutorialId); - } - } - void sendAddMarker(const Position& pos, uint8_t markType, const std::string& desc) { - if (client) { - client->sendAddMarker(pos, markType, desc); - } - } - void sendQuestLog() { - if (client) { - client->sendQuestLog(); - } - } - void sendQuestLine(const Quest* quest) { - if (client) { - client->sendQuestLine(quest); - } - } - void sendEnterWorld() { - if (client) { - client->sendEnterWorld(); + client->sendChannel(channelId, channelName); } } void sendFightModes() { @@ -1117,10 +905,10 @@ class Player final : public Creature, public Cylinder lastPong = OTSYS_TIME(); } - void onThink(uint32_t interval) override; + void onThink(uint32_t interval) final; - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; + void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; + void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; void setNextAction(int64_t time) { if (time > nextAction) { @@ -1142,7 +930,7 @@ class Player final : public Creature, public Cylinder void forgetInstantSpell(const std::string& spellName); bool hasLearnedInstantSpell(const std::string& spellName) const; - private: + protected: std::forward_list getMuteConditions() const; void checkTradeState(const Item* item); @@ -1150,7 +938,7 @@ class Player final : public Creature, public Cylinder void gainExperience(uint64_t gainExp, Creature* source); void addExperience(Creature* source, uint64_t exp, bool sendText = false); - void removeExperience(uint64_t exp, bool sendText = false); + void removeExperience(uint64_t exp); void updateInventoryWeight(); @@ -1158,55 +946,56 @@ class Player final : public Creature, public Cylinder void setNextWalkTask(SchedulerTask* task); void setNextActionTask(SchedulerTask* task); - void death(Creature* lastHitCreature) override; - bool dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified) override; - Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) override; + void dropLoot(Container* corpse, Creature*) final; + void death(Creature* lastHitCreature) final; + bool dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified) final; + Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) final; //cylinder implementations ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor = nullptr) const override; + uint32_t flags, Creature* actor = nullptr) const final; ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount, - uint32_t flags) const override; - ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override; + uint32_t flags) const final; + ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, - uint32_t& flags) override; + uint32_t& flags) final; - void addThing(Thing*) override {} - void addThing(int32_t index, Thing* thing) override; + void addThing(Thing*) final {} + void addThing(int32_t index, Thing* thing) final; - void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override; - void replaceThing(uint32_t index, Thing* thing) override; + void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; + void replaceThing(uint32_t index, Thing* thing) final; - void removeThing(Thing* thing, uint32_t count) override; + void removeThing(Thing* thing, uint32_t count) final; - int32_t getThingIndex(const Thing* thing) const override; - size_t getFirstIndex() const override; - size_t getLastIndex() const override; - uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override; - std::map& getAllItemTypeCount(std::map& countMap) const override; - Thing* getThing(size_t index) const override; + int32_t getThingIndex(const Thing* thing) const final; + size_t getFirstIndex() const final; + size_t getLastIndex() const final; + uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final; + std::map& getAllItemTypeCount(std::map& countMap) const final; + Thing* getThing(size_t index) const final; - void internalAddThing(Thing* thing) override; - void internalAddThing(uint32_t index, Thing* thing) override; + void internalAddThing(Thing* thing) final; + void internalAddThing(uint32_t index, Thing* thing) final; + + uint32_t checkPlayerKilling(); std::unordered_set attackedSet; std::unordered_set VIPList; std::map openContainers; std::map depotLockerMap; - std::map depotChests; std::map storageMap; std::vector outfits; - GuildWarVector guildWarVector; - - std::list shopItemList; + GuildWarList guildWarList; std::forward_list invitePartyList; - std::forward_list modalWindows; std::forward_list learnedInstantSpellList; std::forward_list storedConditionList; // TODO: This variable is only temporarily used when logging in, get rid of it somehow + std::list murderTimeStamps; + std::string name; std::string guildNick; @@ -1217,30 +1006,26 @@ class Player final : public Creature, public Cylinder time_t lastLoginSaved = 0; time_t lastLogout = 0; + time_t playerKillerEnd = 0; uint64_t experience = 0; uint64_t manaSpent = 0; - uint64_t lastAttack = 0; uint64_t bankBalance = 0; - uint64_t lastQuestlogUpdate = 0; + int64_t lastAttack = 0; int64_t lastFailedFollow = 0; - int64_t skullTicks = 0; - int64_t lastWalkthroughAttempt = 0; - int64_t lastToggleMount = 0; int64_t lastPing; int64_t lastPong; int64_t nextAction = 0; - + int64_t earliestAttackTime = 0; + BedItem* bedItem = nullptr; Guild* guild = nullptr; const GuildRank* guildRank = nullptr; Group* group = nullptr; - Inbox* inbox; Item* tradeItem = nullptr; Item* inventory[CONST_SLOT_LAST + 1] = {}; Item* writeItem = nullptr; House* editHouse = nullptr; - Npc* shopOwner = nullptr; Party* party = nullptr; Player* tradePartner = nullptr; ProtocolGame_ptr client; @@ -1267,22 +1052,16 @@ class Player final : public Creature, public Cylinder uint32_t mana = 0; uint32_t manaMax = 0; int32_t varSkills[SKILL_LAST + 1] = {}; - int32_t varSpecialSkills[SPECIALSKILL_LAST + 1] = {}; int32_t varStats[STAT_LAST + 1] = {}; - int32_t purchaseCallback = -1; - int32_t saleCallback = -1; int32_t MessageBufferCount = 0; int32_t premiumDays = 0; int32_t bloodHitCount = 0; int32_t shieldBlockCount = 0; - int32_t offlineTrainingSkill = -1; - int32_t offlineTrainingTime = 0; int32_t idleTime = 0; + int32_t lastWalkingTime = 0; - uint16_t lastStatsTrainingTime = 0; - uint16_t staminaMinutes = 2520; + uint16_t staminaMinutes = 3360; uint16_t maxWriteLen = 0; - int16_t lastDepotId = -1; uint8_t soul = 0; uint8_t blessings = 0; @@ -1293,13 +1072,11 @@ class Player final : public Creature, public Cylinder OperatingSystem_t operatingSystem = CLIENTOS_NONE; BlockType_t lastAttackBlockType = BLOCK_NONE; tradestate_t tradeState = TRADE_NONE; + chaseMode_t chaseMode = CHASEMODE_STANDSTILL; fightMode_t fightMode = FIGHTMODE_ATTACK; AccountType_t accountType = ACCOUNT_TYPE_NORMAL; - bool chaseMode = false; bool secureMode = false; - bool inMarket = false; - bool wasMounted = false; bool ghostMode = false; bool pzLocked = false; bool isConnecting = false; @@ -1309,12 +1086,12 @@ class Player final : public Creature, public Cylinder static uint32_t playerAutoID; void updateItemsLight(bool internal = false); - int32_t getStepSpeed() const override { + int32_t getStepSpeed() const final { return std::max(PLAYER_MIN_SPEED, std::min(PLAYER_MAX_SPEED, getSpeed())); } void updateBaseSpeed() { if (!hasFlag(PlayerFlag_SetMaxSpeed)) { - baseSpeed = vocation->getBaseSpeed() + (2 * (level - 1)); + baseSpeed = vocation->getBaseSpeed() + (level - 1); } else { baseSpeed = PLAYER_MAX_SPEED; } @@ -1327,21 +1104,22 @@ class Player final : public Creature, public Cylinder } static uint8_t getPercentLevel(uint64_t count, uint64_t nextLevelCount); + static uint16_t getDropLootPercent(); double getLostPercent() const; - uint64_t getLostExperience() const override { + uint64_t getLostExperience() const final { return skillLoss ? static_cast(experience * getLostPercent()) : 0; } - uint32_t getDamageImmunities() const override { + uint32_t getDamageImmunities() const final { return damageImmunities; } - uint32_t getConditionImmunities() const override { + uint32_t getConditionImmunities() const final { return conditionImmunities; } - uint32_t getConditionSuppressions() const override { + uint32_t getConditionSuppressions() const final { return conditionSuppressions; } - uint16_t getLookCorpse() const override; - void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const override; + uint16_t getLookCorpse() const final; + void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const final; friend class Game; friend class Npc; @@ -1350,6 +1128,8 @@ class Player final : public Creature, public Cylinder friend class Actions; friend class IOLoginData; friend class ProtocolGame; + friend class BehaviourDatabase; + friend class ConjureSpell; }; #endif diff --git a/src/position.cpp b/src/position.cpp index 247aace..a972899 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/position.h b/src/position.h index 1d48840..abd83ad 100644 --- a/src/position.h +++ b/src/position.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,32 +42,32 @@ struct Position constexpr Position(uint16_t x, uint16_t y, uint8_t z) : x(x), y(y), z(z) {} template - static bool areInRange(const Position& p1, const Position& p2) { + inline static bool areInRange(const Position& p1, const Position& p2) { return Position::getDistanceX(p1, p2) <= deltax && Position::getDistanceY(p1, p2) <= deltay; } template - static bool areInRange(const Position& p1, const Position& p2) { + inline static bool areInRange(const Position& p1, const Position& p2) { return Position::getDistanceX(p1, p2) <= deltax && Position::getDistanceY(p1, p2) <= deltay && Position::getDistanceZ(p1, p2) <= deltaz; } - static int_fast32_t getOffsetX(const Position& p1, const Position& p2) { + inline static int_fast32_t getOffsetX(const Position& p1, const Position& p2) { return p1.getX() - p2.getX(); } - static int_fast32_t getOffsetY(const Position& p1, const Position& p2) { + inline static int_fast32_t getOffsetY(const Position& p1, const Position& p2) { return p1.getY() - p2.getY(); } - static int_fast16_t getOffsetZ(const Position& p1, const Position& p2) { + inline static int_fast16_t getOffsetZ(const Position& p1, const Position& p2) { return p1.getZ() - p2.getZ(); } - static int32_t getDistanceX(const Position& p1, const Position& p2) { + inline static int32_t getDistanceX(const Position& p1, const Position& p2) { return std::abs(Position::getOffsetX(p1, p2)); } - static int32_t getDistanceY(const Position& p1, const Position& p2) { + inline static int32_t getDistanceY(const Position& p1, const Position& p2) { return std::abs(Position::getOffsetY(p1, p2)); } - static int16_t getDistanceZ(const Position& p1, const Position& p2) { + inline static int16_t getDistanceZ(const Position& p1, const Position& p2) { return std::abs(Position::getOffsetZ(p1, p2)); } @@ -123,9 +123,9 @@ struct Position return Position(x - p1.x, y - p1.y, z - p1.z); } - int_fast32_t getX() const { return x; } - int_fast32_t getY() const { return y; } - int_fast16_t getZ() const { return z; } + inline int_fast32_t getX() const { return x; } + inline int_fast32_t getY() const { return y; } + inline int_fast16_t getZ() const { return z; } }; std::ostream& operator<<(std::ostream&, const Position&); diff --git a/src/protocol.cpp b/src/protocol.cpp index 6980580..2686f1a 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,7 +22,6 @@ #include "protocol.h" #include "outputmessage.h" #include "rsa.h" -#include "xtea.h" extern RSA g_RSA; @@ -33,7 +32,7 @@ void Protocol::onSendMessage(const OutputMessage_ptr& msg) const if (encryptionEnabled) { XTEA_encrypt(*msg); - msg->addCryptoHeader(checksumEnabled); + msg->addCryptoHeader(); } } } @@ -52,7 +51,8 @@ OutputMessage_ptr Protocol::getOutputBuffer(int32_t size) //dispatcher thread if (!outputBuffer) { outputBuffer = OutputMessagePool::getOutputMessage(); - } else if ((outputBuffer->getLength() + size) > NetworkMessage::MAX_PROTOCOL_BODY_LENGTH) { + } + else if ((outputBuffer->getLength() + size) > NetworkMessage::MAX_PROTOCOL_BODY_LENGTH) { send(outputBuffer); outputBuffer = OutputMessagePool::getOutputMessage(); } @@ -61,27 +61,73 @@ OutputMessage_ptr Protocol::getOutputBuffer(int32_t size) void Protocol::XTEA_encrypt(OutputMessage& msg) const { + const uint32_t delta = 0x61C88647; + // The message must be a multiple of 8 - size_t paddingBytes = msg.getLength() % 8u; + size_t paddingBytes = msg.getLength() % 8; if (paddingBytes != 0) { msg.addPaddingBytes(8 - paddingBytes); } uint8_t* buffer = msg.getOutputBuffer(); - xtea::encrypt(buffer, msg.getLength(), key); + const size_t messageLength = msg.getLength(); + size_t readPos = 0; + const uint32_t k[] = { key[0], key[1], key[2], key[3] }; + while (readPos < messageLength) { + uint32_t v0; + memcpy(&v0, buffer + readPos, 4); + uint32_t v1; + memcpy(&v1, buffer + readPos + 4, 4); + + uint32_t sum = 0; + + for (int32_t i = 32; --i >= 0;) { + v0 += ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); + sum -= delta; + v1 += ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); + } + + memcpy(buffer + readPos, &v0, 4); + readPos += 4; + memcpy(buffer + readPos, &v1, 4); + readPos += 4; + } } bool Protocol::XTEA_decrypt(NetworkMessage& msg) const { - if (((msg.getLength() - 6) & 7) != 0) { + if (((msg.getLength() - 2) & 7) != 0) { return false; } - uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition(); - xtea::decrypt(buffer, msg.getLength() - 6, key); + const uint32_t delta = 0x61C88647; - uint16_t innerLength = msg.get(); - if (innerLength + 8 > msg.getLength()) { + uint8_t* buffer = msg.getBuffer() + msg.getBufferPosition(); + const size_t messageLength = (msg.getLength() - 2); + size_t readPos = 0; + const uint32_t k[] = { key[0], key[1], key[2], key[3] }; + while (readPos < messageLength) { + uint32_t v0; + memcpy(&v0, buffer + readPos, 4); + uint32_t v1; + memcpy(&v1, buffer + readPos + 4, 4); + + uint32_t sum = 0xC6EF3720; + + for (int32_t i = 32; --i >= 0;) { + v1 -= ((v0 << 4 ^ v0 >> 5) + v0) ^ (sum + k[(sum >> 11) & 3]); + sum += delta; + v0 -= ((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + k[sum & 3]); + } + + memcpy(buffer + readPos, &v0, 4); + readPos += 4; + memcpy(buffer + readPos, &v1, 4); + readPos += 4; + } + + int innerLength = msg.get(); + if (innerLength > msg.getLength() - 4) { return false; } @@ -91,7 +137,7 @@ bool Protocol::XTEA_decrypt(NetworkMessage& msg) const bool Protocol::RSA_decrypt(NetworkMessage& msg) { - if ((msg.getLength() - msg.getBufferPosition()) < 128) { + if ((msg.getLength() - msg.getBufferPosition()) != 128) { return false; } diff --git a/src/protocol.h b/src/protocol.h index 3cd757d..43ad54d 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -1,6 +1,6 @@ /** * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Copyright (C) 2016 Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,85 +21,77 @@ #define FS_PROTOCOL_H_D71405071ACF4137A4B1203899DE80E1 #include "connection.h" -#include "xtea.h" class Protocol : public std::enable_shared_from_this { - public: - explicit Protocol(Connection_ptr connection) : connection(connection) {} - virtual ~Protocol() = default; +public: + explicit Protocol(Connection_ptr connection) : connection(connection), key(), encryptionEnabled(false), rawMessages(false) {} + virtual ~Protocol() = default; - // non-copyable - Protocol(const Protocol&) = delete; - Protocol& operator=(const Protocol&) = delete; + // non-copyable + Protocol(const Protocol&) = delete; + Protocol& operator=(const Protocol&) = delete; - virtual void parsePacket(NetworkMessage&) {} + virtual void parsePacket(NetworkMessage&) {} - virtual void onSendMessage(const OutputMessage_ptr& msg) const; - void onRecvMessage(NetworkMessage& msg); - virtual void onRecvFirstMessage(NetworkMessage& msg) = 0; - virtual void onConnect() {} + virtual void onSendMessage(const OutputMessage_ptr& msg) const; + void onRecvMessage(NetworkMessage& msg); + virtual void onRecvFirstMessage(NetworkMessage& msg) = 0; + virtual void onConnect() {} - bool isConnectionExpired() const { - return connection.expired(); + bool isConnectionExpired() const { + return connection.expired(); + } + + Connection_ptr getConnection() const { + return connection.lock(); + } + + uint32_t getIP() const; + + //Use this function for autosend messages only + OutputMessage_ptr getOutputBuffer(int32_t size); + + OutputMessage_ptr& getCurrentBuffer() { + return outputBuffer; + } + + void send(OutputMessage_ptr msg) const { + if (auto connection = getConnection()) { + connection->send(msg); } + } - Connection_ptr getConnection() const { - return connection.lock(); +protected: + void disconnect() const { + if (auto connection = getConnection()) { + connection->close(); } + } + void enableXTEAEncryption() { + encryptionEnabled = true; + } + void setXTEAKey(const uint32_t* key) { + memcpy(this->key, key, sizeof(*key) * 4); + } - uint32_t getIP() const; + void XTEA_encrypt(OutputMessage& msg) const; + bool XTEA_decrypt(NetworkMessage& msg) const; + static bool RSA_decrypt(NetworkMessage& msg); - //Use this function for autosend messages only - OutputMessage_ptr getOutputBuffer(int32_t size); + void setRawMessages(bool value) { + rawMessages = value; + } - OutputMessage_ptr& getCurrentBuffer() { - return outputBuffer; - } + virtual void release() {} + friend class Connection; - void send(OutputMessage_ptr msg) const { - if (auto connection = getConnection()) { - connection->send(msg); - } - } - - protected: - void disconnect() const { - if (auto connection = getConnection()) { - connection->close(); - } - } - void enableXTEAEncryption() { - encryptionEnabled = true; - } - void setXTEAKey(xtea::key key) { - this->key = std::move(key); - } - void disableChecksum() { - checksumEnabled = false; - } - - static bool RSA_decrypt(NetworkMessage& msg); - - void setRawMessages(bool value) { - rawMessages = value; - } - - virtual void release() {} - - private: - void XTEA_encrypt(OutputMessage& msg) const; - bool XTEA_decrypt(NetworkMessage& msg) const; - - friend class Connection; - - OutputMessage_ptr outputBuffer; - - const ConnectionWeak_ptr connection; - xtea::key key; - bool encryptionEnabled = false; - bool checksumEnabled = true; - bool rawMessages = false; + OutputMessage_ptr outputBuffer; +private: + const ConnectionWeak_ptr connection; + uint32_t key[4]; + bool encryptionEnabled; + bool rawMessages; }; #endif diff --git a/src/protocolgame.cpp b/src/protocolgame.cpp index b979d99..faa9e5b 100644 --- a/src/protocolgame.cpp +++ b/src/protocolgame.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,6 @@ #include "actions.h" #include "game.h" #include "iologindata.h" -#include "iomarket.h" #include "waitlist.h" #include "ban.h" #include "scheduler.h" @@ -108,9 +107,8 @@ void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingS } } - WaitingList& waitingList = WaitingList::getInstance(); - if (!waitingList.clientLogin(player)) { - uint32_t currentSlot = waitingList.getClientSlot(player); + if (!WaitingList::getInstance()->clientLogin(player)) { + uint32_t currentSlot = WaitingList::getInstance()->getClientSlot(player); uint32_t retryTime = WaitingList::getTime(currentSlot); std::ostringstream ss; @@ -126,7 +124,7 @@ void ProtocolGame::login(const std::string& name, uint32_t accountId, OperatingS return; } - if (!IOLoginData::loadPlayerById(player, player->getGUID())) { + if (!IOLoginData::loadPlayerByName(player, name)) { disconnectClient("Your character could not be loaded."); return; } @@ -186,7 +184,6 @@ void ProtocolGame::connect(uint32_t playerId, OperatingSystem_t operatingSystem) player->incrementReferenceCounter(); g_chat->removeUserFromAllChannels(*player); - player->clearModalWindows(); player->setOperatingSystem(operatingSystem); player->isConnecting = false; @@ -212,7 +209,7 @@ void ProtocolGame::logout(bool displayEffect, bool forced) return; } - if (!player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) && player->hasCondition(CONDITION_INFIGHT)) { + if (player->hasCondition(CONDITION_INFIGHT)) { player->sendCancelMessage(RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT); return; } @@ -245,20 +242,18 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) OperatingSystem_t operatingSystem = static_cast(msg.get()); version = msg.get(); - msg.skipBytes(7); // U32 client version, U8 client type, U16 dat revision - if (!Protocol::RSA_decrypt(msg)) { disconnect(); return; } - xtea::key key; + uint32_t key[4]; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(std::move(key)); + setXTEAKey(key); if (operatingSystem >= CLIENTOS_OTCLIENT_LINUX) { NetworkMessage opcodeMessage; @@ -270,48 +265,15 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) msg.skipBytes(1); // gamemaster flag - std::string sessionKey = msg.getString(); - - auto sessionArgs = explodeString(sessionKey, "\n", 4); - if (sessionArgs.size() != 4) { - disconnect(); - return; - } - - std::string& accountName = sessionArgs[0]; - std::string& password = sessionArgs[1]; - std::string& token = sessionArgs[2]; - uint32_t tokenTime = 0; - try { - tokenTime = std::stoul(sessionArgs[3]); - } catch (const std::invalid_argument&) { - disconnectClient("Malformed token packet."); - return; - } catch (const std::out_of_range&) { - disconnectClient("Token time is too long."); - return; - } - - if (accountName.empty()) { - disconnectClient("You must enter your account name."); - return; - } - + uint32_t accountNumber = msg.get(); std::string characterName = msg.getString(); + std::string password = msg.getString(); - uint32_t timeStamp = msg.get(); - uint8_t randNumber = msg.getByte(); - if (challengeTimestamp != timeStamp || challengeRandom != randNumber) { - disconnect(); + /*if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { + //sendUpdateRequest(); + disconnectClient("Use Tibia 7.72 to login!"); return; - } - - if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { - std::ostringstream ss; - ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; - disconnectClient(ss.str()); - return; - } + }*/ if (g_game.getGameState() == GAME_STATE_STARTUP) { disconnectClient("Gameworld is starting up. Please wait."); @@ -335,41 +297,29 @@ void ProtocolGame::onRecvFirstMessage(NetworkMessage& msg) return; } - uint32_t accountId = IOLoginData::gameworldAuthentication(accountName, password, characterName, token, tokenTime); + uint32_t accountId = IOLoginData::gameworldAuthentication(accountNumber, password, characterName); if (accountId == 0) { - disconnectClient("Account name or password is not correct."); + disconnectClient("Account number or password is not correct."); return; } + Account account; + if (!IOLoginData::loginserverAuthentication(accountNumber, password, account)) { + disconnectClient("Account number or password is not correct."); + return; + } + + //Update premium days + Game::updatePremium(account); + g_dispatcher.addTask(createTask(std::bind(&ProtocolGame::login, getThis(), characterName, accountId, operatingSystem))); + } void ProtocolGame::onConnect() { - auto output = OutputMessagePool::getOutputMessage(); - static std::random_device rd; - static std::ranlux24 generator(rd()); - static std::uniform_int_distribution randNumber(0x00, 0xFF); - // Skip checksum - output->skipBytes(sizeof(uint32_t)); - // Packet length & type - output->add(0x0006); - output->addByte(0x1F); - - // Add timestamp & random number - challengeTimestamp = static_cast(time(nullptr)); - output->add(challengeTimestamp); - - challengeRandom = randNumber(generator); - output->addByte(challengeRandom); - - // Go back and write checksum - output->skipBytes(-12); - output->add(adlerChecksum(output->getOutputBuffer() + sizeof(uint32_t), 8)); - - send(output); } void ProtocolGame::disconnectClient(const std::string& message) const @@ -434,12 +384,7 @@ void ProtocolGame::parsePacket(NetworkMessage& msg) case 0x70: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_EAST); break; case 0x71: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_SOUTH); break; case 0x72: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerTurn, player->getID(), DIRECTION_WEST); break; - case 0x77: parseEquipObject(msg); break; case 0x78: parseThrow(msg); break; - case 0x79: parseLookInShop(msg); break; - case 0x7A: parsePlayerPurchase(msg); break; - case 0x7B: parsePlayerSale(msg); break; - case 0x7C: addGameTask(&Game::playerCloseShop, player->getID()); break; case 0x7D: parseRequestTrade(msg); break; case 0x7E: parseLookInTrade(msg); break; case 0x7F: addGameTask(&Game::playerAcceptTrade, player->getID()); break; @@ -454,13 +399,14 @@ void ProtocolGame::parsePacket(NetworkMessage& msg) case 0x8A: parseHouseWindow(msg); break; case 0x8C: parseLookAt(msg); break; case 0x8D: parseLookInBattleList(msg); break; - case 0x8E: /* join aggression */ break; case 0x96: parseSay(msg); break; case 0x97: addGameTask(&Game::playerRequestChannels, player->getID()); break; case 0x98: parseOpenChannel(msg); break; case 0x99: parseCloseChannel(msg); break; case 0x9A: parseOpenPrivateChannel(msg); break; - case 0x9E: addGameTask(&Game::playerCloseNpcChannel, player->getID()); break; + case 0x9B: parseProcessRuleViolationReport(msg); break; + case 0x9C: parseCloseRuleViolationReport(msg); break; + case 0x9D: addGameTask(&Game::playerCancelRuleViolationReport, player->getID()); break; case 0xA0: parseFightModes(msg); break; case 0xA1: parseAttack(msg); break; case 0xA2: parseFollow(msg); break; @@ -469,37 +415,22 @@ void ProtocolGame::parsePacket(NetworkMessage& msg) case 0xA5: parseRevokePartyInvite(msg); break; case 0xA6: parsePassPartyLeadership(msg); break; case 0xA7: addGameTask(&Game::playerLeaveParty, player->getID()); break; - case 0xA8: parseEnableSharedPartyExperience(msg); break; case 0xAA: addGameTask(&Game::playerCreatePrivateChannel, player->getID()); break; case 0xAB: parseChannelInvite(msg); break; case 0xAC: parseChannelExclude(msg); break; case 0xBE: addGameTask(&Game::playerCancelAttackAndFollow, player->getID()); break; case 0xC9: /* update tile */ break; case 0xCA: parseUpdateContainer(msg); break; - case 0xCB: parseBrowseField(msg); break; case 0xCC: parseSeekInContainer(msg); break; case 0xD2: addGameTask(&Game::playerRequestOutfit, player->getID()); break; case 0xD3: parseSetOutfit(msg); break; - case 0xD4: parseToggleMount(msg); break; case 0xDC: parseAddVip(msg); break; case 0xDD: parseRemoveVip(msg); break; - case 0xDE: parseEditVip(msg); break; case 0xE6: parseBugReport(msg); break; - case 0xE7: /* thank you */ break; + case 0xE7: /* violation window */ break; case 0xE8: parseDebugAssert(msg); break; - case 0xF0: addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerShowQuestLog, player->getID()); break; - case 0xF1: parseQuestLine(msg); break; - case 0xF2: parseRuleViolationReport(msg); break; - case 0xF3: /* get object info */ break; - case 0xF4: parseMarketLeave(); break; - case 0xF5: parseMarketBrowse(msg); break; - case 0xF6: parseMarketCreateOffer(msg); break; - case 0xF7: parseMarketCancelOffer(msg); break; - case 0xF8: parseMarketAcceptOffer(msg); break; - case 0xF9: parseModalWindowAnswer(msg); break; - default: - // std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast(recvbyte) << std::dec << "!" << std::endl; + std::cout << "Player: " << player->getName() << " sent an unknown packet header: 0x" << std::hex << static_cast(recvbyte) << std::dec << "!" << std::endl; break; } @@ -510,14 +441,13 @@ void ProtocolGame::parsePacket(NetworkMessage& msg) void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) { - msg.add(0x00); //environmental effects - int32_t count; Item* ground = tile->getGround(); if (ground) { msg.addItem(ground); count = 1; - } else { + } + else { count = 0; } @@ -529,7 +459,8 @@ void ProtocolGame::GetTileDescription(const Tile* tile, NetworkMessage& msg) count++; if (count == 9 && tile->getPosition() == player->getPosition()) { break; - } else if (count == 10) { + } + else if (count == 10) { return; } } @@ -632,7 +563,7 @@ void ProtocolGame::checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& remo known = false; - if (knownCreatureSet.size() > 1300) { + if (knownCreatureSet.size() > 150) { // Look for a creature to remove for (auto it = knownCreatureSet.begin(), end = knownCreatureSet.end(); it != end; ++it) { Creature* creature = g_game.getCreatureByID(*it); @@ -738,7 +669,7 @@ void ProtocolGame::parseOpenPrivateChannel(NetworkMessage& msg) void ProtocolGame::parseAutoWalk(NetworkMessage& msg) { uint8_t numdirs = msg.getByte(); - if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 8)) { + if (numdirs == 0 || (msg.getBufferPosition() + numdirs) != (msg.getLength() + 4)) { return; } @@ -776,16 +707,9 @@ void ProtocolGame::parseSetOutfit(NetworkMessage& msg) newOutfit.lookLegs = msg.getByte(); newOutfit.lookFeet = msg.getByte(); newOutfit.lookAddons = msg.getByte(); - newOutfit.lookMount = msg.get(); addGameTask(&Game::playerChangeOutfit, player->getID(), newOutfit); } -void ProtocolGame::parseToggleMount(NetworkMessage& msg) -{ - bool mount = msg.getByte() != 0; - addGameTask(&Game::playerToggleMount, player->getID(), mount); -} - void ProtocolGame::parseUseItem(NetworkMessage& msg) { Position pos = msg.getPosition(); @@ -867,14 +791,16 @@ void ProtocolGame::parseSay(NetworkMessage& msg) SpeakClasses type = static_cast(msg.getByte()); switch (type) { - case TALKTYPE_PRIVATE_TO: - case TALKTYPE_PRIVATE_RED_TO: + case TALKTYPE_PRIVATE: + case TALKTYPE_PRIVATE_RED: + case TALKTYPE_RVR_ANSWER: receiver = msg.getString(); channelId = 0; break; case TALKTYPE_CHANNEL_Y: case TALKTYPE_CHANNEL_R1: + case TALKTYPE_CHANNEL_R2: channelId = msg.get(); break; @@ -884,7 +810,7 @@ void ProtocolGame::parseSay(NetworkMessage& msg) } const std::string text = msg.getString(); - if (text.length() > 255) { + if (text.length() > 255 || text.find('\n') != std::string::npos) { return; } @@ -898,6 +824,13 @@ void ProtocolGame::parseFightModes(NetworkMessage& msg) uint8_t rawSecureMode = msg.getByte(); // 0 - can't attack unmarked, 1 - can attack unmarked // uint8_t rawPvpMode = msg.getByte(); // pvp mode introduced in 10.0 + chaseMode_t chaseMode; + if (rawChaseMode == 1) { + chaseMode = CHASEMODE_FOLLOW; + } else { + chaseMode = CHASEMODE_STANDSTILL; + } + fightMode_t fightMode; if (rawFightMode == 1) { fightMode = FIGHTMODE_ATTACK; @@ -907,29 +840,31 @@ void ProtocolGame::parseFightModes(NetworkMessage& msg) fightMode = FIGHTMODE_DEFENSE; } - addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, rawChaseMode != 0, rawSecureMode != 0); + addGameTask(&Game::playerSetFightModes, player->getID(), fightMode, chaseMode, rawSecureMode != 0); } void ProtocolGame::parseAttack(NetworkMessage& msg) { uint32_t creatureId = msg.get(); - // msg.get(); creatureId (same as above) addGameTask(&Game::playerSetAttackedCreature, player->getID(), creatureId); } void ProtocolGame::parseFollow(NetworkMessage& msg) { uint32_t creatureId = msg.get(); - // msg.get(); creatureId (same as above) addGameTask(&Game::playerFollowCreature, player->getID(), creatureId); } -void ProtocolGame::parseEquipObject(NetworkMessage& msg) +void ProtocolGame::parseProcessRuleViolationReport(NetworkMessage& msg) { - uint16_t spriteId = msg.get(); - // msg.get(); + const std::string reporter = msg.getString(); + addGameTask(&Game::playerProcessRuleViolationReport, player->getID(), reporter); +} - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerEquipItem, player->getID(), spriteId); +void ProtocolGame::parseCloseRuleViolationReport(NetworkMessage& msg) +{ + const std::string reporter = msg.getString(); + addGameTask(&Game::playerCloseRuleViolationReport, player->getID(), reporter); } void ProtocolGame::parseTextWindow(NetworkMessage& msg) @@ -947,32 +882,6 @@ void ProtocolGame::parseHouseWindow(NetworkMessage& msg) addGameTask(&Game::playerUpdateHouseWindow, player->getID(), doorId, id, text); } -void ProtocolGame::parseLookInShop(NetworkMessage& msg) -{ - uint16_t id = msg.get(); - uint8_t count = msg.getByte(); - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerLookInShop, player->getID(), id, count); -} - -void ProtocolGame::parsePlayerPurchase(NetworkMessage& msg) -{ - uint16_t id = msg.get(); - uint8_t count = msg.getByte(); - uint8_t amount = msg.getByte(); - bool ignoreCap = msg.getByte() != 0; - bool inBackpacks = msg.getByte() != 0; - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerPurchaseItem, player->getID(), id, count, amount, ignoreCap, inBackpacks); -} - -void ProtocolGame::parsePlayerSale(NetworkMessage& msg) -{ - uint16_t id = msg.get(); - uint8_t count = msg.getByte(); - uint8_t amount = msg.getByte(); - bool ignoreEquipped = msg.getByte() != 0; - addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerSellItem, player->getID(), id, count, amount, ignoreEquipped); -} - void ProtocolGame::parseRequestTrade(NetworkMessage& msg) { Position pos = msg.getPosition(); @@ -1001,15 +910,6 @@ void ProtocolGame::parseRemoveVip(NetworkMessage& msg) addGameTask(&Game::playerRequestRemoveVip, player->getID(), guid); } -void ProtocolGame::parseEditVip(NetworkMessage& msg) -{ - uint32_t guid = msg.get(); - const std::string description = msg.getString(); - uint32_t icon = std::min(10, msg.get()); // 10 is max icon in 9.63 - bool notify = msg.getByte() != 0; - addGameTask(&Game::playerRequestEditVip, player->getID(), guid, description, icon, notify); -} - void ProtocolGame::parseRotateItem(NetworkMessage& msg) { Position pos = msg.getPosition(); @@ -1018,34 +918,10 @@ void ProtocolGame::parseRotateItem(NetworkMessage& msg) addGameTaskTimed(DISPATCHER_TASK_EXPIRATION, &Game::playerRotateItem, player->getID(), pos, stackpos, spriteId); } -void ProtocolGame::parseRuleViolationReport(NetworkMessage& msg) -{ - uint8_t reportType = msg.getByte(); - uint8_t reportReason = msg.getByte(); - const std::string& targetName = msg.getString(); - const std::string& comment = msg.getString(); - std::string translation; - if (reportType == REPORT_TYPE_NAME) { - translation = msg.getString(); - } else if (reportType == REPORT_TYPE_STATEMENT) { - translation = msg.getString(); - msg.get(); // statement id, used to get whatever player have said, we don't log that. - } - - addGameTask(&Game::playerReportRuleViolation, player->getID(), targetName, reportType, reportReason, comment, translation); -} - void ProtocolGame::parseBugReport(NetworkMessage& msg) { - uint8_t category = msg.getByte(); - std::string message = msg.getString(); - - Position position; - if (category == BUG_CATEGORY_MAP) { - position = msg.getPosition(); - } - - addGameTask(&Game::playerReportBug, player->getID(), message, position, category); + std::string bug = msg.getString(); + addGameTask(&Game::playerReportBug, player->getID(), bug); } void ProtocolGame::parseDebugAssert(NetworkMessage& msg) @@ -1087,75 +963,6 @@ void ProtocolGame::parsePassPartyLeadership(NetworkMessage& msg) addGameTask(&Game::playerPassPartyLeadership, player->getID(), targetId); } -void ProtocolGame::parseEnableSharedPartyExperience(NetworkMessage& msg) -{ - bool sharedExpActive = msg.getByte() == 1; - addGameTask(&Game::playerEnableSharedPartyExperience, player->getID(), sharedExpActive); -} - -void ProtocolGame::parseQuestLine(NetworkMessage& msg) -{ - uint16_t questId = msg.get(); - addGameTask(&Game::playerShowQuestLine, player->getID(), questId); -} - -void ProtocolGame::parseMarketLeave() -{ - addGameTask(&Game::playerLeaveMarket, player->getID()); -} - -void ProtocolGame::parseMarketBrowse(NetworkMessage& msg) -{ - uint16_t browseId = msg.get(); - - if (browseId == MARKETREQUEST_OWN_OFFERS) { - addGameTask(&Game::playerBrowseMarketOwnOffers, player->getID()); - } else if (browseId == MARKETREQUEST_OWN_HISTORY) { - addGameTask(&Game::playerBrowseMarketOwnHistory, player->getID()); - } else { - addGameTask(&Game::playerBrowseMarket, player->getID(), browseId); - } -} - -void ProtocolGame::parseMarketCreateOffer(NetworkMessage& msg) -{ - uint8_t type = msg.getByte(); - uint16_t spriteId = msg.get(); - uint16_t amount = msg.get(); - uint32_t price = msg.get(); - bool anonymous = (msg.getByte() != 0); - addGameTask(&Game::playerCreateMarketOffer, player->getID(), type, spriteId, amount, price, anonymous); -} - -void ProtocolGame::parseMarketCancelOffer(NetworkMessage& msg) -{ - uint32_t timestamp = msg.get(); - uint16_t counter = msg.get(); - addGameTask(&Game::playerCancelMarketOffer, player->getID(), timestamp, counter); -} - -void ProtocolGame::parseMarketAcceptOffer(NetworkMessage& msg) -{ - uint32_t timestamp = msg.get(); - uint16_t counter = msg.get(); - uint16_t amount = msg.get(); - addGameTask(&Game::playerAcceptMarketOffer, player->getID(), timestamp, counter, amount); -} - -void ProtocolGame::parseModalWindowAnswer(NetworkMessage& msg) -{ - uint32_t id = msg.get(); - uint8_t button = msg.getByte(); - uint8_t choice = msg.getByte(); - addGameTask(&Game::playerAnswerModalWindow, player->getID(), id, button, choice); -} - -void ProtocolGame::parseBrowseField(NetworkMessage& msg) -{ - const Position& pos = msg.getPosition(); - addGameTask(&Game::playerBrowseField, player->getID(), pos); -} - void ProtocolGame::parseSeekInContainer(NetworkMessage& msg) { uint8_t containerId = msg.getByte(); @@ -1172,16 +979,6 @@ void ProtocolGame::sendOpenPrivateChannel(const std::string& receiver) writeToOutputBuffer(msg); } -void ProtocolGame::sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent) -{ - NetworkMessage msg; - msg.addByte(0xF3); - msg.add(channelId); - msg.addString(playerName); - msg.addByte(channelEvent); - writeToOutputBuffer(msg); -} - void ProtocolGame::sendCreatureOutfit(const Creature* creature, const Outfit_t& outfit) { if (!canSee(creature)) { @@ -1206,26 +1003,13 @@ void ProtocolGame::sendCreatureLight(const Creature* creature) writeToOutputBuffer(msg); } -void ProtocolGame::sendWorldLight(LightInfo lightInfo) +void ProtocolGame::sendWorldLight(const LightInfo& lightInfo) { NetworkMessage msg; AddWorldLight(msg, lightInfo); writeToOutputBuffer(msg); } -void ProtocolGame::sendCreatureWalkthrough(const Creature* creature, bool walkthrough) -{ - if (!canSee(creature)) { - return; - } - - NetworkMessage msg; - msg.addByte(0x92); - msg.add(creature->getID()); - msg.addByte(walkthrough ? 0x00 : 0x01); - writeToOutputBuffer(msg); -} - void ProtocolGame::sendCreatureShield(const Creature* creature) { if (!canSee(creature)) { @@ -1256,24 +1040,6 @@ void ProtocolGame::sendCreatureSkull(const Creature* creature) writeToOutputBuffer(msg); } -void ProtocolGame::sendCreatureType(uint32_t creatureId, uint8_t creatureType) -{ - NetworkMessage msg; - msg.addByte(0x95); - msg.add(creatureId); - msg.addByte(creatureType); - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendCreatureHelpers(uint32_t creatureId, uint16_t helpers) -{ - NetworkMessage msg; - msg.addByte(0x94); - msg.add(creatureId); - msg.add(helpers); - writeToOutputBuffer(msg); -} - void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t color) { if (!canSee(creature)) { @@ -1281,37 +1047,55 @@ void ProtocolGame::sendCreatureSquare(const Creature* creature, SquareColor_t co } NetworkMessage msg; - msg.addByte(0x93); + msg.addByte(0x86); msg.add(creature->getID()); - msg.addByte(0x01); msg.addByte(color); writeToOutputBuffer(msg); } -void ProtocolGame::sendTutorial(uint8_t tutorialId) +void ProtocolGame::sendRemoveRuleViolationReport(const std::string& name) { NetworkMessage msg; - msg.addByte(0xDC); - msg.addByte(tutorialId); + msg.addByte(0xAF); + msg.addString(name); writeToOutputBuffer(msg); } -void ProtocolGame::sendAddMarker(const Position& pos, uint8_t markType, const std::string& desc) +void ProtocolGame::sendLockRuleViolation() { NetworkMessage msg; - msg.addByte(0xDD); - msg.addPosition(pos); - msg.addByte(markType); - msg.addString(desc); + msg.addByte(0xB1); writeToOutputBuffer(msg); } -void ProtocolGame::sendReLoginWindow(uint8_t unfairFightReduction) +void ProtocolGame::sendRuleViolationCancel(const std::string& name) { NetworkMessage msg; - msg.addByte(0x28); - msg.addByte(0x00); - msg.addByte(unfairFightReduction); + msg.addByte(0xB0); + msg.addString(name); + writeToOutputBuffer(msg); +} + +void ProtocolGame::sendRuleViolationsChannel(uint16_t channelId) +{ + NetworkMessage msg; + msg.addByte(0xAE); + msg.add(channelId); + auto it = g_game.getRuleViolationReports().begin(); + for (; it != g_game.getRuleViolationReports().end(); ++it) { + const RuleViolation& rvr = it->second; + if (rvr.pending) { + Player* reporter = g_game.getPlayerByID(rvr.reporterId); + if (reporter) { + msg.addByte(0xAA); + msg.add(0); + msg.addString(reporter->getName()); + msg.addByte(TALKTYPE_RVR_CHANNEL); + msg.add(0); + msg.addString(rvr.text); + } + } + } writeToOutputBuffer(msg); } @@ -1322,63 +1106,25 @@ void ProtocolGame::sendStats() writeToOutputBuffer(msg); } -void ProtocolGame::sendBasicData() -{ - NetworkMessage msg; - msg.addByte(0x9F); - if (player->isPremium()) { - msg.addByte(1); - msg.add(time(nullptr) + (player->premiumDays * 86400)); - } else { - msg.addByte(0); - msg.add(0); - } - msg.addByte(player->getVocation()->getClientId()); - msg.add(0xFF); // number of known spells - for (uint8_t spellId = 0x00; spellId < 0xFF; spellId++) { - msg.addByte(spellId); - } - writeToOutputBuffer(msg); -} - void ProtocolGame::sendTextMessage(const TextMessage& message) { NetworkMessage msg; msg.addByte(0xB4); msg.addByte(message.type); - switch (message.type) { - case MESSAGE_DAMAGE_DEALT: - case MESSAGE_DAMAGE_RECEIVED: - case MESSAGE_DAMAGE_OTHERS: { - msg.addPosition(message.position); - msg.add(message.primary.value); - msg.addByte(message.primary.color); - msg.add(message.secondary.value); - msg.addByte(message.secondary.color); - break; - } - case MESSAGE_HEALED: - case MESSAGE_HEALED_OTHERS: - case MESSAGE_EXPERIENCE: - case MESSAGE_EXPERIENCE_OTHERS: { - msg.addPosition(message.position); - msg.add(message.primary.value); - msg.addByte(message.primary.color); - break; - } - case MESSAGE_GUILD: - case MESSAGE_PARTY_MANAGEMENT: - case MESSAGE_PARTY: - msg.add(message.channelId); - break; - default: { - break; - } - } msg.addString(message.text); writeToOutputBuffer(msg); } +void ProtocolGame::sendAnimatedText(const Position& pos, uint8_t color, const std::string& text) +{ + NetworkMessage msg; + msg.addByte(0x84); + msg.addPosition(pos); + msg.addByte(color); + msg.addString(text); + writeToOutputBuffer(msg); +} + void ProtocolGame::sendClosePrivate(uint16_t channelId) { NetworkMessage msg; @@ -1393,9 +1139,6 @@ void ProtocolGame::sendCreatePrivateChannel(uint16_t channelId, const std::strin msg.addByte(0xB2); msg.add(channelId); msg.addString(channelName); - msg.add(0x01); - msg.addString(player->getName()); - msg.add(0x00); writeToOutputBuffer(msg); } @@ -1414,7 +1157,7 @@ void ProtocolGame::sendChannelsDialog() writeToOutputBuffer(msg); } -void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers) +void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelName) { NetworkMessage msg; msg.addByte(0xAC); @@ -1422,38 +1165,9 @@ void ProtocolGame::sendChannel(uint16_t channelId, const std::string& channelNam msg.add(channelId); msg.addString(channelName); - if (channelUsers) { - msg.add(channelUsers->size()); - for (const auto& it : *channelUsers) { - msg.addString(it.second->getName()); - } - } else { - msg.add(0x00); - } - - if (invitedUsers) { - msg.add(invitedUsers->size()); - for (const auto& it : *invitedUsers) { - msg.addString(it.second->getName()); - } - } else { - msg.add(0x00); - } writeToOutputBuffer(msg); } -void ProtocolGame::sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel) -{ - NetworkMessage msg; - msg.addByte(0xAA); - msg.add(0x00); - msg.addString(author); - msg.add(0x00); - msg.addByte(type); - msg.add(channel); - msg.addString(text); - writeToOutputBuffer(msg); -} void ProtocolGame::sendIcons(uint16_t icons) { @@ -1470,24 +1184,14 @@ void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool h msg.addByte(cid); - if (container->getID() == ITEM_BROWSEFIELD) { - msg.addItem(1987, 1); - msg.addString("Browse Field"); - } else { - msg.addItem(container); - msg.addString(container->getName()); - } + msg.addItem(container); + msg.addString(container->getName()); msg.addByte(container->capacity()); msg.addByte(hasParent ? 0x01 : 0x00); - msg.addByte(container->isUnlocked() ? 0x01 : 0x00); // Drag and drop - msg.addByte(container->hasPagination() ? 0x01 : 0x00); // Pagination - uint32_t containerSize = container->size(); - msg.add(containerSize); - msg.add(firstIndex); if (firstIndex < containerSize) { uint8_t itemsToSend = std::min(std::min(container->capacity(), containerSize - firstIndex), std::numeric_limits::max()); @@ -1501,562 +1205,7 @@ void ProtocolGame::sendContainer(uint8_t cid, const Container* container, bool h writeToOutputBuffer(msg); } -void ProtocolGame::sendShop(Npc* npc, const ShopInfoList& itemList) -{ - NetworkMessage msg; - msg.addByte(0x7A); - msg.addString(npc->getName()); - uint16_t itemsToSend = std::min(itemList.size(), std::numeric_limits::max()); - msg.add(itemsToSend); - - uint16_t i = 0; - for (auto it = itemList.begin(); i < itemsToSend; ++it, ++i) { - AddShopItem(msg, *it); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendCloseShop() -{ - NetworkMessage msg; - msg.addByte(0x7C); - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendSaleItemList(const std::list& shop) -{ - NetworkMessage msg; - msg.addByte(0x7B); - msg.add(player->getMoney() + player->getBankBalance()); - - std::map saleMap; - - if (shop.size() <= 5) { - // For very small shops it's not worth it to create the complete map - for (const ShopInfo& shopInfo : shop) { - if (shopInfo.sellPrice == 0) { - continue; - } - - int8_t subtype = -1; - - const ItemType& itemType = Item::items[shopInfo.itemId]; - if (itemType.hasSubType() && !itemType.stackable) { - subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType); - } - - uint32_t count = player->getItemTypeCount(shopInfo.itemId, subtype); - if (count > 0) { - saleMap[shopInfo.itemId] = count; - } - } - } else { - // Large shop, it's better to get a cached map of all item counts and use it - // We need a temporary map since the finished map should only contain items - // available in the shop - std::map tempSaleMap; - player->getAllItemTypeCount(tempSaleMap); - - // We must still check manually for the special items that require subtype matches - // (That is, fluids such as potions etc., actually these items are very few since - // health potions now use their own ID) - for (const ShopInfo& shopInfo : shop) { - if (shopInfo.sellPrice == 0) { - continue; - } - - int8_t subtype = -1; - - const ItemType& itemType = Item::items[shopInfo.itemId]; - if (itemType.hasSubType() && !itemType.stackable) { - subtype = (shopInfo.subType == 0 ? -1 : shopInfo.subType); - } - - if (subtype != -1) { - uint32_t count; - if (!itemType.isFluidContainer() && !itemType.isSplash()) { - count = player->getItemTypeCount(shopInfo.itemId, subtype); // This shop item requires extra checks - } else { - count = subtype; - } - - if (count > 0) { - saleMap[shopInfo.itemId] = count; - } - } else { - std::map::const_iterator findIt = tempSaleMap.find(shopInfo.itemId); - if (findIt != tempSaleMap.end() && findIt->second > 0) { - saleMap[shopInfo.itemId] = findIt->second; - } - } - } - } - - uint8_t itemsToSend = std::min(saleMap.size(), std::numeric_limits::max()); - msg.addByte(itemsToSend); - - uint8_t i = 0; - for (std::map::const_iterator it = saleMap.begin(); i < itemsToSend; ++it, ++i) { - msg.addItemId(it->first); - msg.addByte(std::min(it->second, std::numeric_limits::max())); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketEnter(uint32_t depotId) -{ - NetworkMessage msg; - msg.addByte(0xF6); - - msg.add(player->getBankBalance()); - msg.addByte(std::min(IOMarket::getPlayerOfferCount(player->getGUID()), std::numeric_limits::max())); - - DepotChest* depotChest = player->getDepotChest(depotId, false); - if (!depotChest) { - msg.add(0x00); - writeToOutputBuffer(msg); - return; - } - - player->setInMarket(true); - - std::map depotItems; - std::forward_list containerList { depotChest, player->getInbox() }; - - do { - Container* container = containerList.front(); - containerList.pop_front(); - - for (Item* item : container->getItemList()) { - Container* c = item->getContainer(); - if (c && !c->empty()) { - containerList.push_front(c); - continue; - } - - const ItemType& itemType = Item::items[item->getID()]; - if (itemType.wareId == 0) { - continue; - } - - if (c && (!itemType.isContainer() || c->capacity() != itemType.maxItems)) { - continue; - } - - if (!item->hasMarketAttributes()) { - continue; - } - - depotItems[itemType.wareId] += Item::countByType(item, -1); - } - } while (!containerList.empty()); - - uint16_t itemsToSend = std::min(depotItems.size(), std::numeric_limits::max()); - msg.add(itemsToSend); - - uint16_t i = 0; - for (std::map::const_iterator it = depotItems.begin(); i < itemsToSend; ++it, ++i) { - msg.add(it->first); - msg.add(std::min(0xFFFF, it->second)); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketLeave() -{ - NetworkMessage msg; - msg.addByte(0xF7); - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) -{ - NetworkMessage msg; - - msg.addByte(0xF9); - msg.addItemId(itemId); - - msg.add(buyOffers.size()); - for (const MarketOffer& offer : buyOffers) { - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.add(offer.amount); - msg.add(offer.price); - msg.addString(offer.playerName); - } - - msg.add(sellOffers.size()); - for (const MarketOffer& offer : sellOffers) { - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.add(offer.amount); - msg.add(offer.price); - msg.addString(offer.playerName); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketAcceptOffer(const MarketOfferEx& offer) -{ - NetworkMessage msg; - msg.addByte(0xF9); - msg.addItemId(offer.itemId); - - if (offer.type == MARKETACTION_BUY) { - msg.add(0x01); - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.add(offer.amount); - msg.add(offer.price); - msg.addString(offer.playerName); - msg.add(0x00); - } else { - msg.add(0x00); - msg.add(0x01); - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.add(offer.amount); - msg.add(offer.price); - msg.addString(offer.playerName); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers) -{ - NetworkMessage msg; - msg.addByte(0xF9); - msg.add(MARKETREQUEST_OWN_OFFERS); - - msg.add(buyOffers.size()); - for (const MarketOffer& offer : buyOffers) { - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.addItemId(offer.itemId); - msg.add(offer.amount); - msg.add(offer.price); - } - - msg.add(sellOffers.size()); - for (const MarketOffer& offer : sellOffers) { - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.addItemId(offer.itemId); - msg.add(offer.amount); - msg.add(offer.price); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketCancelOffer(const MarketOfferEx& offer) -{ - NetworkMessage msg; - msg.addByte(0xF9); - msg.add(MARKETREQUEST_OWN_OFFERS); - - if (offer.type == MARKETACTION_BUY) { - msg.add(0x01); - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.addItemId(offer.itemId); - msg.add(offer.amount); - msg.add(offer.price); - msg.add(0x00); - } else { - msg.add(0x00); - msg.add(0x01); - msg.add(offer.timestamp); - msg.add(offer.counter); - msg.addItemId(offer.itemId); - msg.add(offer.amount); - msg.add(offer.price); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers) -{ - uint32_t i = 0; - std::map counterMap; - uint32_t buyOffersToSend = std::min(buyOffers.size(), 810 + std::max(0, 810 - sellOffers.size())); - uint32_t sellOffersToSend = std::min(sellOffers.size(), 810 + std::max(0, 810 - buyOffers.size())); - - NetworkMessage msg; - msg.addByte(0xF9); - msg.add(MARKETREQUEST_OWN_HISTORY); - - msg.add(buyOffersToSend); - for (auto it = buyOffers.begin(); i < buyOffersToSend; ++it, ++i) { - msg.add(it->timestamp); - msg.add(counterMap[it->timestamp]++); - msg.addItemId(it->itemId); - msg.add(it->amount); - msg.add(it->price); - msg.addByte(it->state); - } - - counterMap.clear(); - i = 0; - - msg.add(sellOffersToSend); - for (auto it = sellOffers.begin(); i < sellOffersToSend; ++it, ++i) { - msg.add(it->timestamp); - msg.add(counterMap[it->timestamp]++); - msg.addItemId(it->itemId); - msg.add(it->amount); - msg.add(it->price); - msg.addByte(it->state); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendMarketDetail(uint16_t itemId) -{ - NetworkMessage msg; - msg.addByte(0xF8); - msg.addItemId(itemId); - - const ItemType& it = Item::items[itemId]; - if (it.armor != 0) { - msg.addString(std::to_string(it.armor)); - } else { - msg.add(0x00); - } - - if (it.attack != 0) { - // TODO: chance to hit, range - // example: - // "attack +x, chance to hit +y%, z fields" - if (it.abilities && it.abilities->elementType != COMBAT_NONE && it.abilities->elementDamage != 0) { - std::ostringstream ss; - ss << it.attack << " physical +" << it.abilities->elementDamage << ' ' << getCombatName(it.abilities->elementType); - msg.addString(ss.str()); - } else { - msg.addString(std::to_string(it.attack)); - } - } else { - msg.add(0x00); - } - - if (it.isContainer()) { - msg.addString(std::to_string(it.maxItems)); - } else { - msg.add(0x00); - } - - if (it.defense != 0) { - if (it.extraDefense != 0) { - std::ostringstream ss; - ss << it.defense << ' ' << std::showpos << it.extraDefense << std::noshowpos; - msg.addString(ss.str()); - } else { - msg.addString(std::to_string(it.defense)); - } - } else { - msg.add(0x00); - } - - if (!it.description.empty()) { - const std::string& descr = it.description; - if (descr.back() == '.') { - msg.addString(std::string(descr, 0, descr.length() - 1)); - } else { - msg.addString(descr); - } - } else { - msg.add(0x00); - } - - if (it.decayTime != 0) { - std::ostringstream ss; - ss << it.decayTime << " seconds"; - msg.addString(ss.str()); - } else { - msg.add(0x00); - } - - if (it.abilities) { - std::ostringstream ss; - bool separator = false; - - for (size_t i = 0; i < COMBAT_COUNT; ++i) { - if (it.abilities->absorbPercent[i] == 0) { - continue; - } - - if (separator) { - ss << ", "; - } else { - separator = true; - } - - ss << getCombatName(indexToCombatType(i)) << ' ' << std::showpos << it.abilities->absorbPercent[i] << std::noshowpos << '%'; - } - - msg.addString(ss.str()); - } else { - msg.add(0x00); - } - - if (it.minReqLevel != 0) { - msg.addString(std::to_string(it.minReqLevel)); - } else { - msg.add(0x00); - } - - if (it.minReqMagicLevel != 0) { - msg.addString(std::to_string(it.minReqMagicLevel)); - } else { - msg.add(0x00); - } - - msg.addString(it.vocationString); - - msg.addString(it.runeSpellName); - - if (it.abilities) { - std::ostringstream ss; - bool separator = false; - - for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; i++) { - if (!it.abilities->skills[i]) { - continue; - } - - if (separator) { - ss << ", "; - } else { - separator = true; - } - - ss << getSkillName(i) << ' ' << std::showpos << it.abilities->skills[i] << std::noshowpos; - } - - if (it.abilities->stats[STAT_MAGICPOINTS] != 0) { - if (separator) { - ss << ", "; - } else { - separator = true; - } - - ss << "magic level " << std::showpos << it.abilities->stats[STAT_MAGICPOINTS] << std::noshowpos; - } - - if (it.abilities->speed != 0) { - if (separator) { - ss << ", "; - } - - ss << "speed " << std::showpos << (it.abilities->speed >> 1) << std::noshowpos; - } - - msg.addString(ss.str()); - } else { - msg.add(0x00); - } - - if (it.charges != 0) { - msg.addString(std::to_string(it.charges)); - } else { - msg.add(0x00); - } - - std::string weaponName = getWeaponName(it.weaponType); - - if (it.slotPosition & SLOTP_TWO_HAND) { - if (!weaponName.empty()) { - weaponName += ", two-handed"; - } else { - weaponName = "two-handed"; - } - } - - msg.addString(weaponName); - - if (it.weight != 0) { - std::ostringstream ss; - if (it.weight < 10) { - ss << "0.0" << it.weight; - } else if (it.weight < 100) { - ss << "0." << it.weight; - } else { - std::string weightString = std::to_string(it.weight); - weightString.insert(weightString.end() - 2, '.'); - ss << weightString; - } - ss << " oz"; - msg.addString(ss.str()); - } else { - msg.add(0x00); - } - - MarketStatistics* statistics = IOMarket::getInstance().getPurchaseStatistics(itemId); - if (statistics) { - msg.addByte(0x01); - msg.add(statistics->numTransactions); - msg.add(std::min(std::numeric_limits::max(), statistics->totalPrice)); - msg.add(statistics->highestPrice); - msg.add(statistics->lowestPrice); - } else { - msg.addByte(0x00); - } - - statistics = IOMarket::getInstance().getSaleStatistics(itemId); - if (statistics) { - msg.addByte(0x01); - msg.add(statistics->numTransactions); - msg.add(std::min(std::numeric_limits::max(), statistics->totalPrice)); - msg.add(statistics->highestPrice); - msg.add(statistics->lowestPrice); - } else { - msg.addByte(0x00); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendQuestLog() -{ - NetworkMessage msg; - msg.addByte(0xF0); - msg.add(g_game.quests.getQuestsCount(player)); - - for (const Quest& quest : g_game.quests.getQuests()) { - if (quest.isStarted(player)) { - msg.add(quest.getID()); - msg.addString(quest.getName()); - msg.addByte(quest.isCompleted(player)); - } - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendQuestLine(const Quest* quest) -{ - NetworkMessage msg; - msg.addByte(0xF1); - msg.add(quest->getID()); - msg.addByte(quest->getMissionsCount(player)); - - for (const Mission& mission : quest->getMissions()) { - if (mission.isStarted(player)) { - msg.addString(mission.getName(player)); - msg.addString(mission.getDescription(player)); - } - } - - writeToOutputBuffer(msg); -} void ProtocolGame::sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack) { @@ -2125,7 +1274,6 @@ void ProtocolGame::sendCreatureTurn(const Creature* creature, uint32_t stackPos) msg.add(0x63); msg.add(creature->getID()); msg.addByte(creature->getDirection()); - msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01); writeToOutputBuffer(msg); } @@ -2142,14 +1290,16 @@ void ProtocolGame::sendCreatureSay(const Creature* creature, SpeakClasses type, //Add level only for players if (const Player* speaker = creature->getPlayer()) { msg.add(speaker->getLevel()); - } else { + } + else { msg.add(0x00); } msg.addByte(type); if (pos) { msg.addPosition(*pos); - } else { + } + else { msg.addPosition(creature->getPosition()); } @@ -2166,15 +1316,18 @@ void ProtocolGame::sendToChannel(const Creature* creature, SpeakClasses type, co msg.add(++statementId); if (!creature) { msg.add(0x00); - } else if (type == TALKTYPE_CHANNEL_R2) { + } + else if (type == TALKTYPE_CHANNEL_R2) { msg.add(0x00); type = TALKTYPE_CHANNEL_R1; - } else { + } + else { msg.addString(creature->getName()); //Add level only for players if (const Player* speaker = creature->getPlayer()) { msg.add(speaker->getLevel()); - } else { + } + else { msg.add(0x00); } } @@ -2194,7 +1347,8 @@ void ProtocolGame::sendPrivateMessage(const Player* speaker, SpeakClasses type, if (speaker) { msg.addString(speaker->getName()); msg.add(speaker->getLevel()); - } else { + } + else { msg.add(0x00); } msg.addByte(type); @@ -2206,7 +1360,6 @@ void ProtocolGame::sendCancelTarget() { NetworkMessage msg; msg.addByte(0xA3); - msg.add(0x00); writeToOutputBuffer(msg); } @@ -2215,8 +1368,7 @@ void ProtocolGame::sendChangeSpeed(const Creature* creature, uint32_t speed) NetworkMessage msg; msg.addByte(0x8F); msg.add(creature->getID()); - msg.add(creature->getBaseSpeed() / 2); - msg.add(speed / 2); + msg.add(speed); writeToOutputBuffer(msg); } @@ -2238,7 +1390,12 @@ void ProtocolGame::sendSkills() void ProtocolGame::sendPing() { NetworkMessage msg; - msg.addByte(0x1D); + if (player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX) { + msg.addByte(0x1D); + } else { + // classic clients ping + msg.addByte(0x1E); + } writeToOutputBuffer(msg); } @@ -2280,17 +1437,11 @@ void ProtocolGame::sendCreatureHealth(const Creature* creature) if (creature->isHealthHidden()) { msg.addByte(0x00); - } else { + } + else { msg.addByte(std::ceil((static_cast(creature->getHealth()) / std::max(creature->getMaxHealth(), 1)) * 100)); } - writeToOutputBuffer(msg); -} -void ProtocolGame::sendFYIBox(const std::string& message) -{ - NetworkMessage msg; - msg.addByte(0x15); - msg.addString(message); writeToOutputBuffer(msg); } @@ -2304,7 +1455,7 @@ void ProtocolGame::sendMapDescription(const Position& pos) writeToOutputBuffer(msg); } -void ProtocolGame::sendAddTileItem(const Position& pos, uint32_t stackpos, const Item* item) +void ProtocolGame::sendAddTileItem(const Position& pos, const Item* item, uint32_t stackpos) { if (!canSee(pos)) { return; @@ -2313,7 +1464,9 @@ void ProtocolGame::sendAddTileItem(const Position& pos, uint32_t stackpos, const NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); - msg.addByte(stackpos); + if (player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX) { + msg.addByte(stackpos); + } msg.addItem(item); writeToOutputBuffer(msg); } @@ -2365,20 +1518,6 @@ void ProtocolGame::sendUpdateTile(const Tile* tile, const Position& pos) writeToOutputBuffer(msg); } -void ProtocolGame::sendPendingStateEntered() -{ - NetworkMessage msg; - msg.addByte(0x0A); - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendEnterWorld() -{ - NetworkMessage msg; - msg.addByte(0x0F); - writeToOutputBuffer(msg); -} - void ProtocolGame::sendFightModes() { NetworkMessage msg; @@ -2386,7 +1525,6 @@ void ProtocolGame::sendFightModes() msg.addByte(player->fightMode); msg.addByte(player->chaseMode); msg.addByte(player->secureMode); - msg.addByte(PVP_MODE_DOVE); writeToOutputBuffer(msg); } @@ -2401,7 +1539,10 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos NetworkMessage msg; msg.addByte(0x6A); msg.addPosition(pos); - msg.addByte(stackpos); + + if (player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX) { + msg.addByte(stackpos); + } bool known; uint32_t removedKnown; @@ -2417,14 +1558,11 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos } NetworkMessage msg; - msg.addByte(0x17); + msg.addByte(0x0A); msg.add(player->getID()); - msg.add(0x32); // beat duration (50) - - msg.addDouble(Creature::speedA, 3); - msg.addDouble(Creature::speedB, 3); - msg.addDouble(Creature::speedC, 3); + msg.addByte(0x32); // beat duration (50) + msg.addByte(0x00); // can report bugs? if (player->getAccountType() >= ACCOUNT_TYPE_TUTOR) { @@ -2433,16 +1571,15 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos msg.addByte(0x00); } - msg.addByte(0x00); // can change pvp framing option - msg.addByte(0x00); // expert mode button enabled - - msg.add(0x00); // URL (string) to ingame store images - msg.add(25); // premium coin package size + if (player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER) { + msg.addByte(0x0B); + for (uint8_t i = 0; i < 32; i++) { + msg.addByte(0xFF); + } + } writeToOutputBuffer(msg); - sendPendingStateEntered(); - sendEnterWorld(); sendMapDescription(pos); if (isLogin) { @@ -2457,14 +1594,16 @@ void ProtocolGame::sendAddCreature(const Creature* creature, const Position& pos sendSkills(); //gameworld light-settings - sendWorldLight(g_game.getWorldLightInfo()); + LightInfo lightInfo; + g_game.getWorldLightInfo(lightInfo); + sendWorldLight(lightInfo); //player light level sendCreatureLight(creature); + //player vip sendVIPEntries(); - sendBasicData(); player->sendIcons(); } @@ -2545,34 +1684,11 @@ void ProtocolGame::sendInventoryItem(slots_t slot, const Item* item) writeToOutputBuffer(msg); } -void ProtocolGame::sendItems() -{ - NetworkMessage msg; - msg.addByte(0xF5); - - const std::vector& inventory = Item::items.getInventory(); - msg.add(inventory.size() + 11); - for (uint16_t i = 1; i <= 11; i++) { - msg.add(i); - msg.addByte(0); //always 0 - msg.add(1); // always 1 - } - - for (auto clientId : inventory) { - msg.add(clientId); - msg.addByte(0); //always 0 - msg.add(1); - } - - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendAddContainerItem(uint8_t cid, uint16_t slot, const Item* item) +void ProtocolGame::sendAddContainerItem(uint8_t cid, const Item* item) { NetworkMessage msg; msg.addByte(0x70); msg.addByte(cid); - msg.add(slot); msg.addItem(item); writeToOutputBuffer(msg); } @@ -2582,22 +1698,17 @@ void ProtocolGame::sendUpdateContainerItem(uint8_t cid, uint16_t slot, const Ite NetworkMessage msg; msg.addByte(0x71); msg.addByte(cid); - msg.add(slot); + msg.addByte(static_cast(slot)); msg.addItem(item); writeToOutputBuffer(msg); } -void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item* lastItem) +void ProtocolGame::sendRemoveContainerItem(uint8_t cid, uint16_t slot) { NetworkMessage msg; msg.addByte(0x72); msg.addByte(cid); - msg.add(slot); - if (lastItem) { - msg.addItem(lastItem); - } else { - msg.add(0x00); - } + msg.addByte(static_cast(slot)); writeToOutputBuffer(msg); } @@ -2624,11 +1735,13 @@ void ProtocolGame::sendTextWindow(uint32_t windowTextId, Item* item, uint16_t ma msg.add(0x00); } - time_t writtenDate = item->getDate(); - if (writtenDate != 0) { - msg.addString(formatDateShort(writtenDate)); - } else { - msg.add(0x00); + if (player->getOperatingSystem() >= CLIENTOS_OTCLIENT_LINUX) { + time_t writtenDate = item->getDate(); + if (writtenDate != 0) { + msg.addString(formatDateShort(writtenDate)); + } else { + msg.add(0x00); + } } writeToOutputBuffer(msg); @@ -2643,7 +1756,6 @@ void ProtocolGame::sendTextWindow(uint32_t windowTextId, uint32_t itemId, const msg.add(text.size()); msg.addString(text); msg.add(0x00); - msg.add(0x00); writeToOutputBuffer(msg); } @@ -2663,11 +1775,6 @@ void ProtocolGame::sendOutfitWindow() msg.addByte(0xC8); Outfit_t currentOutfit = player->getDefaultOutfit(); - Mount* currentMount = g_game.mounts.getMountByID(player->getCurrentMount()); - if (currentMount) { - currentOutfit.lookMount = currentMount->clientId; - } - AddOutfit(msg, currentOutfit); std::vector protocolOutfits; @@ -2685,7 +1792,7 @@ void ProtocolGame::sendOutfitWindow() } protocolOutfits.emplace_back(outfit.name, outfit.lookType, addons); - if (protocolOutfits.size() == 100) { // Game client doesn't allow more than 100 outfits + if (protocolOutfits.size() == 15) { // Game client doesn't allow more than 15 outfits break; } } @@ -2693,44 +1800,26 @@ void ProtocolGame::sendOutfitWindow() msg.addByte(protocolOutfits.size()); for (const ProtocolOutfit& outfit : protocolOutfits) { msg.add(outfit.lookType); - msg.addString(outfit.name); msg.addByte(outfit.addons); } - std::vector mounts; - for (const Mount& mount : g_game.mounts.getMounts()) { - if (player->hasMount(&mount)) { - mounts.push_back(&mount); - } - } - - msg.addByte(mounts.size()); - for (const Mount* mount : mounts) { - msg.add(mount->clientId); - msg.addString(mount->name); - } - writeToOutputBuffer(msg); } void ProtocolGame::sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus) { NetworkMessage msg; - msg.addByte(0xD3); + msg.addByte(newStatus == VIPSTATUS_ONLINE ? 0xD3 : 0xD4); msg.add(guid); - msg.addByte(newStatus); writeToOutputBuffer(msg); } -void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, const std::string& description, uint32_t icon, bool notify, VipStatus_t status) +void ProtocolGame::sendVIP(uint32_t guid, const std::string& name, VipStatus_t status) { NetworkMessage msg; msg.addByte(0xD2); msg.add(guid); msg.addString(name); - msg.addString(description); - msg.add(std::min(10, icon)); - msg.addByte(notify ? 0x01 : 0x00); msg.addByte(status); writeToOutputBuffer(msg); } @@ -2743,66 +1832,17 @@ void ProtocolGame::sendVIPEntries() VipStatus_t vipStatus = VIPSTATUS_ONLINE; Player* vipPlayer = g_game.getPlayerByGUID(entry.guid); - - if (!vipPlayer || vipPlayer->isInGhostMode() || player->isAccessPlayer()) { + if (!vipPlayer || (vipPlayer->isInGhostMode() && !player->isAccessPlayer())) { vipStatus = VIPSTATUS_OFFLINE; } - sendVIP(entry.guid, entry.name, entry.description, entry.icon, entry.notify, vipStatus); + sendVIP(entry.guid, entry.name, vipStatus); } } -void ProtocolGame::sendSpellCooldown(uint8_t spellId, uint32_t time) -{ - NetworkMessage msg; - msg.addByte(0xA4); - msg.addByte(spellId); - msg.add(time); - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time) -{ - NetworkMessage msg; - msg.addByte(0xA5); - msg.addByte(groupId); - msg.add(time); - writeToOutputBuffer(msg); -} - -void ProtocolGame::sendModalWindow(const ModalWindow& modalWindow) -{ - NetworkMessage msg; - msg.addByte(0xFA); - - msg.add(modalWindow.id); - msg.addString(modalWindow.title); - msg.addString(modalWindow.message); - - msg.addByte(modalWindow.buttons.size()); - for (const auto& it : modalWindow.buttons) { - msg.addString(it.first); - msg.addByte(it.second); - } - - msg.addByte(modalWindow.choices.size()); - for (const auto& it : modalWindow.choices) { - msg.addString(it.first); - msg.addByte(it.second); - } - - msg.addByte(modalWindow.defaultEscapeButton); - msg.addByte(modalWindow.defaultEnterButton); - msg.addByte(modalWindow.priority ? 0x01 : 0x00); - - writeToOutputBuffer(msg); -} - ////////////// Add common messages void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bool known, uint32_t remove) { - CreatureType_t creatureType = creature->getType(); - const Player* otherPlayer = creature->getPlayer(); if (known) { @@ -2812,16 +1852,16 @@ void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bo msg.add(0x61); msg.add(remove); msg.add(creature->getID()); - msg.addByte(creatureType); msg.addString(creature->getName()); } if (creature->isHealthHidden()) { msg.addByte(0x00); - } else { + } + else { msg.addByte(std::ceil((static_cast(creature->getHealth()) / std::max(creature->getMaxHealth(), 1)) * 100)); } - + msg.addByte(creature->getDirection()); if (!creature->isInGhostMode() && !creature->isInvisible()) { @@ -2831,44 +1871,15 @@ void ProtocolGame::AddCreature(NetworkMessage& msg, const Creature* creature, bo AddOutfit(msg, outfit); } - LightInfo lightInfo = creature->getCreatureLight(); + LightInfo lightInfo; + creature->getCreatureLight(lightInfo); msg.addByte(player->isAccessPlayer() ? 0xFF : lightInfo.level); msg.addByte(lightInfo.color); - msg.add(creature->getStepSpeed() / 2); + msg.add(creature->getStepSpeed()); msg.addByte(player->getSkullClient(creature)); msg.addByte(player->getPartyShield(otherPlayer)); - - if (!known) { - msg.addByte(player->getGuildEmblem(otherPlayer)); - } - - if (creatureType == CREATURETYPE_MONSTER) { - const Creature* master = creature->getMaster(); - if (master) { - const Player* masterPlayer = master->getPlayer(); - if (masterPlayer) { - if (masterPlayer == player) { - creatureType = CREATURETYPE_SUMMON_OWN; - } else { - creatureType = CREATURETYPE_SUMMON_OTHERS; - } - } - } - } - - msg.addByte(creatureType); // Type (for summons) - msg.addByte(creature->getSpeechBubble()); - msg.addByte(0xFF); // MARK_UNMARKED - - if (otherPlayer) { - msg.add(otherPlayer->getHelpers()); - } else { - msg.add(0x00); - } - - msg.addByte(player->canWalkthroughEx(creature) ? 0x00 : 0x01); } void ProtocolGame::AddPlayerStats(NetworkMessage& msg) @@ -2877,41 +1888,23 @@ void ProtocolGame::AddPlayerStats(NetworkMessage& msg) msg.add(std::min(player->getHealth(), std::numeric_limits::max())); msg.add(std::min(player->getMaxHealth(), std::numeric_limits::max())); + + msg.add(player->getFreeCapacity() / 100); - msg.add(player->getFreeCapacity()); - msg.add(player->getCapacity()); - - msg.add(player->getExperience()); + msg.add(std::min(player->getExperience(), 0x7FFFFFFF)); msg.add(player->getLevel()); msg.addByte(player->getLevelPercent()); - msg.add(100); // base xp gain rate - msg.add(0); // xp voucher - msg.add(0); // low level bonus - msg.add(0); // xp boost - msg.add(100); // stamina multiplier (100 = x1.0) - msg.add(std::min(player->getMana(), std::numeric_limits::max())); msg.add(std::min(player->getMaxMana(), std::numeric_limits::max())); msg.addByte(std::min(player->getMagicLevel(), std::numeric_limits::max())); - msg.addByte(std::min(player->getBaseMagicLevel(), std::numeric_limits::max())); msg.addByte(player->getMagicLevelPercent()); msg.addByte(player->getSoul()); msg.add(player->getStaminaMinutes()); - - msg.add(player->getBaseSpeed() / 2); - - Condition* condition = player->getCondition(CONDITION_REGENERATION); - msg.add(condition ? condition->getTicks() / 1000 : 0x00); - - msg.add(player->getOfflineTrainingTime() / 60 / 1000); - - msg.add(0); // xp boost time (seconds) - msg.addByte(0); // enables exp boost in the store } void ProtocolGame::AddPlayerSkills(NetworkMessage& msg) @@ -2919,15 +1912,9 @@ void ProtocolGame::AddPlayerSkills(NetworkMessage& msg) msg.addByte(0xA1); for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { - msg.add(std::min(player->getSkillLevel(i), std::numeric_limits::max())); - msg.add(player->getBaseSkill(i)); + msg.addByte(std::min(player->getSkillLevel(i), std::numeric_limits::max())); msg.addByte(player->getSkillPercent(i)); } - - for (uint8_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) { - msg.add(std::min(100, player->varSpecialSkills[i])); - msg.add(0); - } } void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit) @@ -2943,11 +1930,9 @@ void ProtocolGame::AddOutfit(NetworkMessage& msg, const Outfit_t& outfit) } else { msg.addItemId(outfit.lookTypeEx); } - - msg.add(outfit.lookMount); } -void ProtocolGame::AddWorldLight(NetworkMessage& msg, LightInfo lightInfo) +void ProtocolGame::AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo) { msg.addByte(0x82); msg.addByte((player->isAccessPlayer() ? 0xFF : lightInfo.level)); @@ -2956,7 +1941,8 @@ void ProtocolGame::AddWorldLight(NetworkMessage& msg, LightInfo lightInfo) void ProtocolGame::AddCreatureLight(NetworkMessage& msg, const Creature* creature) { - LightInfo lightInfo = creature->getCreatureLight(); + LightInfo lightInfo; + creature->getCreatureLight(lightInfo); msg.addByte(0x8D); msg.add(creature->getID()); @@ -3064,23 +2050,6 @@ void ProtocolGame::MoveDownCreature(NetworkMessage& msg, const Creature* creatur GetMapDescription(oldPos.x - 8, oldPos.y + 7, newPos.z, 18, 1, msg); } -void ProtocolGame::AddShopItem(NetworkMessage& msg, const ShopInfo& item) -{ - const ItemType& it = Item::items[item.itemId]; - msg.add(it.clientId); - - if (it.isSplash() || it.isFluidContainer()) { - msg.addByte(serverFluidToClient(item.subType)); - } else { - msg.addByte(0x00); - } - - msg.addString(item.realName); - msg.add(it.weight); - msg.add(item.buyPrice); - msg.add(item.sellPrice); -} - void ProtocolGame::parseExtendedOpcode(NetworkMessage& msg) { uint8_t opcode = msg.getByte(); diff --git a/src/protocolgame.h b/src/protocolgame.h index e8eb135..9d0cbe3 100644 --- a/src/protocolgame.h +++ b/src/protocolgame.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,21 +34,14 @@ class Tile; class Connection; class Quest; class ProtocolGame; -using ProtocolGame_ptr = std::shared_ptr; +typedef std::shared_ptr ProtocolGame_ptr; extern Game g_game; struct TextMessage { - MessageClasses type = MESSAGE_STATUS_DEFAULT; + MessageClasses type; std::string text; - Position position; - uint16_t channelId; - struct { - int32_t value = 0; - TextColor_t color; - } primary, secondary; - TextMessage() = default; TextMessage(MessageClasses type, std::string text) : type(type), text(std::move(text)) {} }; @@ -57,16 +50,16 @@ class ProtocolGame final : public Protocol { public: // static protocol information - enum {server_sends_first = true}; - enum {protocol_identifier = 0}; // Not required as we send first - enum {use_checksum = true}; + enum { server_sends_first = true }; + enum { protocol_identifier = 0 }; // Not required as we send first + static const char* protocol_name() { return "gameworld protocol"; } explicit ProtocolGame(Connection_ptr connection) : Protocol(connection) {} - void login(const std::string& name, uint32_t accountId, OperatingSystem_t operatingSystem); + void login(const std::string& name, uint32_t accnumber, OperatingSystem_t operatingSystem); void logout(bool displayEffect, bool forced); uint16_t getVersion() const { @@ -81,7 +74,7 @@ class ProtocolGame final : public Protocol void disconnectClient(const std::string& message) const; void writeToOutputBuffer(const NetworkMessage& msg); - void release() override; + void release() final; void checkCreatureAsKnown(uint32_t id, bool& known, uint32_t& removedKnown); @@ -90,8 +83,8 @@ class ProtocolGame final : public Protocol bool canSee(const Position& pos) const; // we have all the parse methods - void parsePacket(NetworkMessage& msg) override; - void onRecvFirstMessage(NetworkMessage& msg) override; + void parsePacket(NetworkMessage& msg) final; + void onRecvFirstMessage(NetworkMessage& msg) final; void onConnect() override; //Parse methods @@ -103,11 +96,12 @@ class ProtocolGame final : public Protocol void parseFightModes(NetworkMessage& msg); void parseAttack(NetworkMessage& msg); void parseFollow(NetworkMessage& msg); - void parseEquipObject(NetworkMessage& msg); + + void parseProcessRuleViolationReport(NetworkMessage& msg); + void parseCloseRuleViolationReport(NetworkMessage& msg); void parseBugReport(NetworkMessage& msg); void parseDebugAssert(NetworkMessage& msg); - void parseRuleViolationReport(NetworkMessage& msg); void parseThrow(NetworkMessage& msg); void parseUseItemEx(NetworkMessage& msg); @@ -119,40 +113,20 @@ class ProtocolGame final : public Protocol void parseTextWindow(NetworkMessage& msg); void parseHouseWindow(NetworkMessage& msg); - void parseLookInShop(NetworkMessage& msg); - void parsePlayerPurchase(NetworkMessage& msg); - void parsePlayerSale(NetworkMessage& msg); - - void parseQuestLine(NetworkMessage& msg); - void parseInviteToParty(NetworkMessage& msg); void parseJoinParty(NetworkMessage& msg); void parseRevokePartyInvite(NetworkMessage& msg); void parsePassPartyLeadership(NetworkMessage& msg); - void parseEnableSharedPartyExperience(NetworkMessage& msg); - void parseToggleMount(NetworkMessage& msg); - - void parseModalWindowAnswer(NetworkMessage& msg); - - void parseBrowseField(NetworkMessage& msg); void parseSeekInContainer(NetworkMessage& msg); //trade methods void parseRequestTrade(NetworkMessage& msg); void parseLookInTrade(NetworkMessage& msg); - //market methods - void parseMarketLeave(); - void parseMarketBrowse(NetworkMessage& msg); - void parseMarketCreateOffer(NetworkMessage& msg); - void parseMarketCancelOffer(NetworkMessage& msg); - void parseMarketAcceptOffer(NetworkMessage& msg); - //VIP methods void parseAddVip(NetworkMessage& msg); void parseRemoveVip(NetworkMessage& msg); - void parseEditVip(NetworkMessage& msg); void parseRotateItem(NetworkMessage& msg); @@ -164,17 +138,14 @@ class ProtocolGame final : public Protocol void parseCloseChannel(NetworkMessage& msg); //Send functions - void sendChannelMessage(const std::string& author, const std::string& text, SpeakClasses type, uint16_t channel); - void sendChannelEvent(uint16_t channelId, const std::string& playerName, ChannelEvent_t channelEvent); void sendClosePrivate(uint16_t channelId); void sendCreatePrivateChannel(uint16_t channelId, const std::string& channelName); void sendChannelsDialog(); - void sendChannel(uint16_t channelId, const std::string& channelName, const UsersMap* channelUsers, const InvitedMap* invitedUsers); + void sendChannel(uint16_t channelId, const std::string& channelName); void sendOpenPrivateChannel(const std::string& receiver); void sendToChannel(const Creature* creature, SpeakClasses type, const std::string& text, uint16_t channelId); void sendPrivateMessage(const Player* speaker, SpeakClasses type, const std::string& text); void sendIcons(uint16_t icons); - void sendFYIBox(const std::string& message); void sendDistanceShoot(const Position& from, const Position& to, uint8_t type); void sendMagicEffect(const Position& pos, uint8_t type); @@ -182,41 +153,20 @@ class ProtocolGame final : public Protocol void sendSkills(); void sendPing(); void sendPingBack(); - void sendCreatureTurn(const Creature* creature, uint32_t stackPos); + void sendCreatureTurn(const Creature* creature, uint32_t stackpos); void sendCreatureSay(const Creature* creature, SpeakClasses type, const std::string& text, const Position* pos = nullptr); - void sendQuestLog(); - void sendQuestLine(const Quest* quest); - void sendCancelWalk(); void sendChangeSpeed(const Creature* creature, uint32_t speed); void sendCancelTarget(); void sendCreatureOutfit(const Creature* creature, const Outfit_t& outfit); void sendStats(); - void sendBasicData(); void sendTextMessage(const TextMessage& message); - void sendReLoginWindow(uint8_t unfairFightReduction); + void sendAnimatedText(const Position& pos, uint8_t color, const std::string& text); - void sendTutorial(uint8_t tutorialId); - void sendAddMarker(const Position& pos, uint8_t markType, const std::string& desc); - - void sendCreatureWalkthrough(const Creature* creature, bool walkthrough); void sendCreatureShield(const Creature* creature); void sendCreatureSkull(const Creature* creature); - void sendCreatureType(uint32_t creatureId, uint8_t creatureType); - void sendCreatureHelpers(uint32_t creatureId, uint16_t helpers); - void sendShop(Npc* npc, const ShopInfoList& itemList); - void sendCloseShop(); - void sendSaleItemList(const std::list& shop); - void sendMarketEnter(uint32_t depotId); - void sendMarketLeave(); - void sendMarketBrowseItem(uint16_t itemId, const MarketOfferList& buyOffers, const MarketOfferList& sellOffers); - void sendMarketAcceptOffer(const MarketOfferEx& offer); - void sendMarketBrowseOwnOffers(const MarketOfferList& buyOffers, const MarketOfferList& sellOffers); - void sendMarketCancelOffer(const MarketOfferEx& offer); - void sendMarketBrowseOwnHistory(const HistoryMarketOfferList& buyOffers, const HistoryMarketOfferList& sellOffers); - void sendMarketDetail(uint16_t itemId); void sendTradeItemRequest(const std::string& traderName, const Item* item, bool ack); void sendCloseTrade(); @@ -226,26 +176,26 @@ class ProtocolGame final : public Protocol void sendOutfitWindow(); void sendUpdatedVIPStatus(uint32_t guid, VipStatus_t newStatus); - void sendVIP(uint32_t guid, const std::string& name, const std::string& description, uint32_t icon, bool notify, VipStatus_t status); + void sendVIP(uint32_t guid, const std::string& name, VipStatus_t status); void sendVIPEntries(); - void sendPendingStateEntered(); - void sendEnterWorld(); - void sendFightModes(); void sendCreatureLight(const Creature* creature); - void sendWorldLight(LightInfo lightInfo); + void sendWorldLight(const LightInfo& lightInfo); void sendCreatureSquare(const Creature* creature, SquareColor_t color); - void sendSpellCooldown(uint8_t spellId, uint32_t time); - void sendSpellGroupCooldown(SpellGroup_t groupId, uint32_t time); + //rule violations + void sendRemoveRuleViolationReport(const std::string& name); + void sendLockRuleViolation(); + void sendRuleViolationCancel(const std::string& name); + void sendRuleViolationsChannel(uint16_t channelId); //tiles void sendMapDescription(const Position& pos); - void sendAddTileItem(const Position& pos, uint32_t stackpos, const Item* item); + void sendAddTileItem(const Position& pos, const Item* item, uint32_t stackpos); void sendUpdateTileItem(const Position& pos, uint32_t stackpos, const Item* item); void sendRemoveTileThing(const Position& pos, uint32_t stackpos); void sendUpdateTile(const Tile* tile, const Position& pos); @@ -255,19 +205,15 @@ class ProtocolGame final : public Protocol const Position& oldPos, int32_t oldStackPos, bool teleport); //containers - void sendAddContainerItem(uint8_t cid, uint16_t slot, const Item* item); + void sendAddContainerItem(uint8_t cid, const Item* item); void sendUpdateContainerItem(uint8_t cid, uint16_t slot, const Item* item); - void sendRemoveContainerItem(uint8_t cid, uint16_t slot, const Item* lastItem); + void sendRemoveContainerItem(uint8_t cid, uint16_t slot); void sendContainer(uint8_t cid, const Container* container, bool hasParent, uint16_t firstIndex); void sendCloseContainer(uint8_t cid); //inventory void sendInventoryItem(slots_t slot, const Item* item); - void sendItems(); - - //messages - void sendModalWindow(const ModalWindow& modalWindow); //Help functions @@ -286,7 +232,7 @@ class ProtocolGame final : public Protocol void AddPlayerStats(NetworkMessage& msg); void AddOutfit(NetworkMessage& msg, const Outfit_t& outfit); void AddPlayerSkills(NetworkMessage& msg); - void AddWorldLight(NetworkMessage& msg, LightInfo lightInfo); + void AddWorldLight(NetworkMessage& msg, const LightInfo& lightInfo); void AddCreatureLight(NetworkMessage& msg, const Creature* creature); //tiles @@ -295,9 +241,6 @@ class ProtocolGame final : public Protocol void MoveUpCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos); void MoveDownCreature(NetworkMessage& msg, const Creature* creature, const Position& newPos, const Position& oldPos); - //shop - void AddShopItem(NetworkMessage& msg, const ShopInfo& item); - //otclient void parseExtendedOpcode(NetworkMessage& msg); diff --git a/src/protocollogin.cpp b/src/protocollogin.cpp index 91da0ab..27e2ea3 100644 --- a/src/protocollogin.cpp +++ b/src/protocollogin.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -43,28 +43,15 @@ void ProtocolLogin::disconnectClient(const std::string& message, uint16_t versio disconnect(); } -void ProtocolLogin::getCharacterList(const std::string& accountName, const std::string& password, const std::string& token, uint16_t version) +void ProtocolLogin::getCharacterList(uint32_t accountNumber, const std::string& password, uint16_t version) { Account account; - if (!IOLoginData::loginserverAuthentication(accountName, password, account)) { - disconnectClient("Account name or password is not correct.", version); + if (!IOLoginData::loginserverAuthentication(accountNumber, password, account)) { + disconnectClient("Accountnumber or password is not correct.", version); return; } - uint32_t ticks = time(nullptr) / AUTHENTICATOR_PERIOD; - auto output = OutputMessagePool::getOutputMessage(); - if (!account.key.empty()) { - if (token.empty() || !(token == generateToken(account.key, ticks) || token == generateToken(account.key, ticks - 1) || token == generateToken(account.key, ticks + 1))) { - output->addByte(0x0D); - output->addByte(0); - send(output); - disconnect(); - return; - } - output->addByte(0x0C); - output->addByte(0); - } //Update premium days Game::updatePremium(account); @@ -79,53 +66,24 @@ void ProtocolLogin::getCharacterList(const std::string& accountName, const std:: output->addString(ss.str()); } - //Add session key - output->addByte(0x28); - output->addString(accountName + "\n" + password + "\n" + token + "\n" + std::to_string(ticks)); - //Add char list output->addByte(0x64); uint8_t size = std::min(std::numeric_limits::max(), account.characters.size()); - - if (g_config.getBoolean(ConfigManager::ONLINE_OFFLINE_CHARLIST)) { - output->addByte(2); // number of worlds - - for (uint8_t i = 0; i < 2; i++) { - output->addByte(i); // world id - output->addString(i == 0 ? "Offline" : "Online"); - output->addString(g_config.getString(ConfigManager::IP)); - output->add(g_config.getNumber(ConfigManager::GAME_PORT)); - output->addByte(0); - } - } else { - output->addByte(1); // number of worlds - output->addByte(0); // world id - output->addString(g_config.getString(ConfigManager::SERVER_NAME)); - output->addString(g_config.getString(ConfigManager::IP)); - output->add(g_config.getNumber(ConfigManager::GAME_PORT)); - output->addByte(0); - } - output->addByte(size); for (uint8_t i = 0; i < size; i++) { - const std::string& character = account.characters[i]; - if (g_config.getBoolean(ConfigManager::ONLINE_OFFLINE_CHARLIST)) { - output->addByte(g_game.getPlayerByName(character) ? 1 : 0); - } else { - output->addByte(0); - } - output->addString(character); + output->addString(account.characters[i]); + output->addString(g_config.getString(ConfigManager::SERVER_NAME)); + output->add(inet_addr(g_config.getString(ConfigManager::IP).c_str())); + output->add(g_config.getNumber(ConfigManager::GAME_PORT)); } //Add premium days - output->addByte(0); if (g_config.getBoolean(ConfigManager::FREE_PREMIUM)) { - output->addByte(1); - output->add(0); - } else { - output->addByte(account.premiumDays > 0 ? 1 : 0); - output->add(time(nullptr) + (account.premiumDays * 86400)); + output->add(0xFFFF); + } + else { + output->add(account.premiumDays); } send(output); @@ -133,6 +91,7 @@ void ProtocolLogin::getCharacterList(const std::string& accountName, const std:: disconnect(); } + void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) { if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { @@ -145,9 +104,11 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) uint16_t version = msg.get(); if (version >= 971) { msg.skipBytes(17); - } else { + } + else { msg.skipBytes(12); } + /* * Skipped bytes: * 4 bytes: protocolVersion @@ -167,13 +128,13 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) return; } - xtea::key key; + uint32_t key[4]; key[0] = msg.get(); key[1] = msg.get(); key[2] = msg.get(); key[3] = msg.get(); enableXTEAEncryption(); - setXTEAKey(std::move(key)); + setXTEAKey(key); if (version < CLIENT_VERSION_MIN || version > CLIENT_VERSION_MAX) { std::ostringstream ss; @@ -209,9 +170,9 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) return; } - std::string accountName = msg.getString(); - if (accountName.empty()) { - disconnectClient("Invalid account name.", version); + uint32_t accountNumber = msg.get(); + if (!accountNumber) { + disconnectClient("Invalid account number.", version); return; } @@ -221,15 +182,6 @@ void ProtocolLogin::onRecvFirstMessage(NetworkMessage& msg) return; } - // read authenticator token and stay logged in flag from last 128 bytes - msg.skipBytes((msg.getLength() - 128) - msg.getBufferPosition()); - if (!Protocol::RSA_decrypt(msg)) { - disconnectClient("Invalid authentification token.", version); - return; - } - - std::string authToken = msg.getString(); - auto thisPtr = std::static_pointer_cast(shared_from_this()); - g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountName, password, authToken, version))); + g_dispatcher.addTask(createTask(std::bind(&ProtocolLogin::getCharacterList, thisPtr, accountNumber, password, version))); } diff --git a/src/protocollogin.h b/src/protocollogin.h index 9781446..a45898f 100644 --- a/src/protocollogin.h +++ b/src/protocollogin.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,6 @@ class ProtocolLogin : public Protocol // static protocol information enum {server_sends_first = false}; enum {protocol_identifier = 0x01}; - enum {use_checksum = true}; static const char* protocol_name() { return "login protocol"; } @@ -43,7 +42,7 @@ class ProtocolLogin : public Protocol private: void disconnectClient(const std::string& message, uint16_t version); - void getCharacterList(const std::string& accountName, const std::string& password, const std::string& token, uint16_t version); + void getCharacterList(uint32_t accountNumber, const std::string& password, uint16_t version); }; #endif diff --git a/src/protocolold.cpp b/src/protocolold.cpp deleted file mode 100644 index e981d56..0000000 --- a/src/protocolold.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "protocolold.h" -#include "outputmessage.h" - -#include "game.h" - -extern Game g_game; - -void ProtocolOld::disconnectClient(const std::string& message) -{ - auto output = OutputMessagePool::getOutputMessage(); - output->addByte(0x0A); - output->addString(message); - send(output); - - disconnect(); -} - -void ProtocolOld::onRecvFirstMessage(NetworkMessage& msg) -{ - if (g_game.getGameState() == GAME_STATE_SHUTDOWN) { - disconnect(); - return; - } - - /*uint16_t clientOS =*/ msg.get(); - uint16_t version = msg.get(); - msg.skipBytes(12); - - if (version <= 760) { - std::ostringstream ss; - ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; - disconnectClient(ss.str()); - return; - } - - if (!Protocol::RSA_decrypt(msg)) { - disconnect(); - return; - } - - xtea::key key; - key[0] = msg.get(); - key[1] = msg.get(); - key[2] = msg.get(); - key[3] = msg.get(); - enableXTEAEncryption(); - setXTEAKey(std::move(key)); - - if (version <= 822) { - disableChecksum(); - } - - std::ostringstream ss; - ss << "Only clients with protocol " << CLIENT_VERSION_STR << " allowed!"; - disconnectClient(ss.str()); -} diff --git a/src/protocolold.h b/src/protocolold.h deleted file mode 100644 index 708e763..0000000 --- a/src/protocolold.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_PROTOCOLOLD_H_5487B862FE144AE0904D098A3238E161 -#define FS_PROTOCOLOLD_H_5487B862FE144AE0904D098A3238E161 - -#include "protocol.h" - -class NetworkMessage; - -class ProtocolOld final : public Protocol -{ - public: - // static protocol information - enum {server_sends_first = false}; - enum {protocol_identifier = 0x01}; - enum {use_checksum = false}; - static const char* protocol_name() { - return "old login protocol"; - } - - explicit ProtocolOld(Connection_ptr connection) : Protocol(connection) {} - - void onRecvFirstMessage(NetworkMessage& msg) override; - - private: - void disconnectClient(const std::string& message); -}; - -#endif diff --git a/src/protocolstatus.cpp b/src/protocolstatus.cpp index aba1bb2..d29b19e 100644 --- a/src/protocolstatus.cpp +++ b/src/protocolstatus.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -117,7 +117,26 @@ void ProtocolStatus::sendStatusString() owner.append_attribute("email") = g_config.getString(ConfigManager::OWNER_EMAIL).c_str(); pugi::xml_node players = tsqp.append_child("players"); - players.append_attribute("online") = std::to_string(g_game.getPlayersOnline()).c_str(); + uint32_t real = 0; + + std::map listIP; + + for (const auto& it : g_game.getPlayers()) { + if (it.second->getIP() != 0) { + auto ip = listIP.find(it.second->getIP()); + if (ip != listIP.end()) { + listIP[it.second->getIP()]++; + if (listIP[it.second->getIP()] < 5) { + real++; + } + } else { + listIP[it.second->getIP()] = 1; + real++; + } + } + } + players.append_attribute("online") = std::to_string(real).c_str(); + players.append_attribute("max") = std::to_string(g_config.getNumber(ConfigManager::MAX_PLAYERS)).c_str(); players.append_attribute("peak") = std::to_string(g_game.getPlayersRecord()).c_str(); diff --git a/src/protocolstatus.h b/src/protocolstatus.h index 5df99e4..2e6cae2 100644 --- a/src/protocolstatus.h +++ b/src/protocolstatus.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,21 +29,20 @@ class ProtocolStatus final : public Protocol // static protocol information enum {server_sends_first = false}; enum {protocol_identifier = 0xFF}; - enum {use_checksum = false}; static const char* protocol_name() { return "status protocol"; } explicit ProtocolStatus(Connection_ptr connection) : Protocol(connection) {} - void onRecvFirstMessage(NetworkMessage& msg) override; + void onRecvFirstMessage(NetworkMessage& msg) final; void sendStatusString(); void sendInfo(uint16_t requestedInfo, const std::string& characterName); static const uint64_t start; - private: + protected: static std::map ipConnectMap; }; diff --git a/src/pugicast.h b/src/pugicast.h index 9b188ed..456a2c0 100644 --- a/src/pugicast.h +++ b/src/pugicast.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/quests.cpp b/src/quests.cpp deleted file mode 100644 index 2e4f552..0000000 --- a/src/quests.cpp +++ /dev/null @@ -1,228 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "quests.h" - -#include "pugicast.h" - -std::string Mission::getDescription(Player* player) const -{ - int32_t value; - player->getStorageValue(storageID, value); - - if (!mainDescription.empty()) { - std::string desc = mainDescription; - replaceString(desc, "|STATE|", std::to_string(value)); - replaceString(desc, "\\n", "\n"); - return desc; - } - - if (ignoreEndValue) { - for (int32_t current = endValue; current >= startValue; current--) { - if (value >= current) { - auto sit = descriptions.find(current); - if (sit != descriptions.end()) { - return sit->second; - } - } - } - } else { - for (int32_t current = endValue; current >= startValue; current--) { - if (value == current) { - auto sit = descriptions.find(current); - if (sit != descriptions.end()) { - return sit->second; - } - } - } - } - return "An error has occurred, please contact a gamemaster."; -} - -bool Mission::isStarted(Player* player) const -{ - if (!player) { - return false; - } - - int32_t value; - if (!player->getStorageValue(storageID, value)) { - return false; - } - - if (value < startValue) { - return false; - } - - if (!ignoreEndValue && value > endValue) { - return false; - } - - return true; -} - -bool Mission::isCompleted(Player* player) const -{ - if (!player) { - return false; - } - - int32_t value; - if (!player->getStorageValue(storageID, value)) { - return false; - } - - if (ignoreEndValue) { - return value >= endValue; - } - - return value == endValue; -} - -std::string Mission::getName(Player* player) const -{ - if (isCompleted(player)) { - return name + " (completed)"; - } - return name; -} - -uint16_t Quest::getMissionsCount(Player* player) const -{ - uint16_t count = 0; - for (const Mission& mission : missions) { - if (mission.isStarted(player)) { - count++; - } - } - return count; -} - -bool Quest::isCompleted(Player* player) const -{ - for (const Mission& mission : missions) { - if (!mission.isCompleted(player)) { - return false; - } - } - return true; -} - -bool Quest::isStarted(Player* player) const -{ - if (!player) { - return false; - } - - int32_t value; - if (!player->getStorageValue(startStorageID, value) || value < startStorageValue) { - return false; - } - - return true; -} - -bool Quests::reload() -{ - quests.clear(); - return loadFromXml(); -} - -bool Quests::loadFromXml() -{ - pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_file("data/XML/quests.xml"); - if (!result) { - printXMLError("Error - Quests::loadFromXml", "data/XML/quests.xml", result); - return false; - } - - uint16_t id = 0; - for (auto questNode : doc.child("quests").children()) { - quests.emplace_back( - questNode.attribute("name").as_string(), - ++id, - pugi::cast(questNode.attribute("startstorageid").value()), - pugi::cast(questNode.attribute("startstoragevalue").value()) - ); - Quest& quest = quests.back(); - - for (auto missionNode : questNode.children()) { - std::string mainDescription = missionNode.attribute("description").as_string(); - - quest.missions.emplace_back( - missionNode.attribute("name").as_string(), - pugi::cast(missionNode.attribute("storageid").value()), - pugi::cast(missionNode.attribute("startvalue").value()), - pugi::cast(missionNode.attribute("endvalue").value()), - missionNode.attribute("ignoreendvalue").as_bool() - ); - Mission& mission = quest.missions.back(); - - if (mainDescription.empty()) { - for (auto missionStateNode : missionNode.children()) { - int32_t missionId = pugi::cast(missionStateNode.attribute("id").value()); - mission.descriptions.emplace(missionId, missionStateNode.attribute("description").as_string()); - } - } else { - mission.mainDescription = mainDescription; - } - } - } - return true; -} - -Quest* Quests::getQuestByID(uint16_t id) -{ - for (Quest& quest : quests) { - if (quest.id == id) { - return ? - } - } - return nullptr; -} - -uint16_t Quests::getQuestsCount(Player* player) const -{ - uint16_t count = 0; - for (const Quest& quest : quests) { - if (quest.isStarted(player)) { - count++; - } - } - return count; -} - -bool Quests::isQuestStorage(const uint32_t key, const int32_t value, const int32_t oldValue) const -{ - for (const Quest& quest : quests) { - if (quest.getStartStorageId() == key && quest.getStartStorageValue() == value) { - return true; - } - - for (const Mission& mission : quest.getMissions()) { - if (mission.getStorageId() == key && value >= mission.getStartStorageValue() && value <= mission.getEndStorageValue()) { - return mission.mainDescription.empty() || oldValue < mission.getStartStorageValue() || oldValue > mission.getEndStorageValue(); - } - } - } - return false; -} diff --git a/src/quests.h b/src/quests.h deleted file mode 100644 index 3ccd180..0000000 --- a/src/quests.h +++ /dev/null @@ -1,119 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_QUESTS_H_16E44051F23547BE8097F8EA9FCAACA0 -#define FS_QUESTS_H_16E44051F23547BE8097F8EA9FCAACA0 - -#include "player.h" -#include "networkmessage.h" - -class Mission; -class Quest; - -using MissionsList = std::list; -using QuestsList = std::list; - -class Mission -{ - public: - Mission(std::string name, int32_t storageID, int32_t startValue, int32_t endValue, bool ignoreEndValue) : - name(std::move(name)), storageID(storageID), startValue(startValue), endValue(endValue), ignoreEndValue(ignoreEndValue) {} - - bool isCompleted(Player* player) const; - bool isStarted(Player* player) const; - std::string getName(Player* player) const; - std::string getDescription(Player* player) const; - - uint32_t getStorageId() const { - return storageID; - } - int32_t getStartStorageValue() const { - return startValue; - } - int32_t getEndStorageValue() const { - return endValue; - } - - std::map descriptions; - std::string mainDescription; - - private: - std::string name; - uint32_t storageID; - int32_t startValue, endValue; - bool ignoreEndValue; -}; - -class Quest -{ - public: - Quest(std::string name, uint16_t id, int32_t startStorageID, int32_t startStorageValue) : - name(std::move(name)), startStorageID(startStorageID), startStorageValue(startStorageValue), id(id) {} - - bool isCompleted(Player* player) const; - bool isStarted(Player* player) const; - uint16_t getID() const { - return id; - } - std::string getName() const { - return name; - } - uint16_t getMissionsCount(Player* player) const; - - uint32_t getStartStorageId() const { - return startStorageID; - } - int32_t getStartStorageValue() const { - return startStorageValue; - } - - const MissionsList& getMissions() const { - return missions; - } - - private: - std::string name; - - uint32_t startStorageID; - int32_t startStorageValue; - uint16_t id; - - MissionsList missions; - - friend class Quests; -}; - -class Quests -{ - public: - const QuestsList& getQuests() const { - return quests; - } - - bool loadFromXml(); - Quest* getQuestByID(uint16_t id); - bool isQuestStorage(const uint32_t key, const int32_t value, const int32_t oldValue) const; - uint16_t getQuestsCount(Player* player) const; - bool reload(); - - private: - QuestsList quests; -}; - -#endif diff --git a/src/raids.cpp b/src/raids.cpp index d831bdc..6fd4f1f 100644 --- a/src/raids.cpp +++ b/src/raids.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -228,9 +228,7 @@ bool Raid::loadFromXml(const std::string& filename) } //sort by delay time - std::sort(raidEvents.begin(), raidEvents.end(), [](const RaidEvent* lhs, const RaidEvent* rhs) { - return lhs->getDelay() < rhs->getDelay(); - }); + std::sort(raidEvents.begin(), raidEvents.end(), RaidEvent::compareEvents); loaded = true; return true; @@ -481,6 +479,10 @@ bool AreaSpawnEvent::configureRaidEvent(const pugi::xml_node& eventNode) } } + if ((attr = eventNode.attribute("lifetime"))) { + lifetime = pugi::cast(attr.value()); + } + for (auto monsterNode : eventNode.children()) { const char* name; @@ -543,6 +545,10 @@ bool AreaSpawnEvent::executeEvent() if (!success) { delete monster; } + + if (lifetime > 0) { + monster->lifetime = OTSYS_TIME() + lifetime; + } } } return true; diff --git a/src/raids.h b/src/raids.h index bb6d75d..3c97d1d 100644 --- a/src/raids.h +++ b/src/raids.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -167,6 +167,10 @@ class RaidEvent return delay; } + static bool compareEvents(const RaidEvent* lhs, const RaidEvent* rhs) { + return lhs->getDelay() < rhs->getDelay(); + } + private: uint32_t delay; }; @@ -176,9 +180,9 @@ class AnnounceEvent final : public RaidEvent public: AnnounceEvent() = default; - bool configureRaidEvent(const pugi::xml_node& eventNode) override; + bool configureRaidEvent(const pugi::xml_node& eventNode) final; - bool executeEvent() override; + bool executeEvent() final; private: std::string message; @@ -188,9 +192,9 @@ class AnnounceEvent final : public RaidEvent class SingleSpawnEvent final : public RaidEvent { public: - bool configureRaidEvent(const pugi::xml_node& eventNode) override; + bool configureRaidEvent(const pugi::xml_node& eventNode) final; - bool executeEvent() override; + bool executeEvent() final; private: std::string monsterName; @@ -200,13 +204,14 @@ class SingleSpawnEvent final : public RaidEvent class AreaSpawnEvent final : public RaidEvent { public: - bool configureRaidEvent(const pugi::xml_node& eventNode) override; + bool configureRaidEvent(const pugi::xml_node& eventNode) final; - bool executeEvent() override; + bool executeEvent() final; private: std::list spawnList; Position fromPos, toPos; + uint32_t lifetime = 0; }; class ScriptEvent final : public RaidEvent, public Event @@ -214,15 +219,15 @@ class ScriptEvent final : public RaidEvent, public Event public: explicit ScriptEvent(LuaScriptInterface* interface) : Event(interface) {} - bool configureRaidEvent(const pugi::xml_node& eventNode) override; - bool configureEvent(const pugi::xml_node&) override { + bool configureRaidEvent(const pugi::xml_node& eventNode) final; + bool configureEvent(const pugi::xml_node&) final { return false; } - bool executeEvent() override; + bool executeEvent() final; - private: - std::string getScriptEventName() const override; + protected: + std::string getScriptEventName() const final; }; #endif diff --git a/src/rsa.cpp b/src/rsa.cpp index 0c95e4d..ba557ac 100644 --- a/src/rsa.cpp +++ b/src/rsa.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,7 +31,7 @@ static CryptoPP::AutoSeededRandomPool prng; void RSA::decrypt(char* msg) const { - CryptoPP::Integer m{reinterpret_cast(msg), 128}; + CryptoPP::Integer m{ reinterpret_cast(msg), 128 }; auto c = pk.CalculateInverse(prng, m); c.Encode(reinterpret_cast(msg), 128); } @@ -41,11 +41,11 @@ static const std::string footer = "-----END RSA PRIVATE KEY-----"; void RSA::loadPEM(const std::string& filename) { - std::ifstream file{filename}; + std::ifstream file{ filename }; if (!file.is_open()) { throw std::runtime_error("Missing file " + filename + "."); - } + } std::ostringstream oss; for (std::string line; std::getline(file, line); oss << line); @@ -73,7 +73,8 @@ void RSA::loadPEM(const std::string& filename) if (!pk.Validate(prng, 3)) { throw std::runtime_error("RSA private key is not valid."); } - } catch (const CryptoPP::Exception& e) { + } + catch (const CryptoPP::Exception& e) { std::cout << e.what() << '\n'; } } diff --git a/src/rsa.h b/src/rsa.h index 888e3b6..0783e7c 100644 --- a/src/rsa.h +++ b/src/rsa.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,18 +26,18 @@ class RSA { - public: - RSA() = default; +public: + RSA() = default; - // non-copyable - RSA(const RSA&) = delete; - RSA& operator=(const RSA&) = delete; + // non-copyable + RSA(const RSA&) = delete; + RSA& operator=(const RSA&) = delete; - void loadPEM(const std::string& filename); - void decrypt(char* msg) const; + void loadPEM(const std::string& filename); + void decrypt(char* msg) const; - private: - CryptoPP::RSA::PrivateKey pk; +private: + CryptoPP::RSA::PrivateKey pk; }; -#endif +#endif \ No newline at end of file diff --git a/src/scheduler.cpp b/src/scheduler.cpp index f489f61..28fe462 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,7 +35,7 @@ void Scheduler::threadMain() } // the mutex is locked again now... - if (ret == std::cv_status::timeout && !eventList.empty()) { + if (ret == std::cv_status::timeout) { // ok we had a timeout, so there has to be an event we have to execute... SchedulerTask* task = eventList.top(); eventList.pop(); @@ -60,54 +60,54 @@ void Scheduler::threadMain() uint32_t Scheduler::addEvent(SchedulerTask* task) { + bool do_signal = false; eventLock.lock(); - if (getState() != THREAD_STATE_RUNNING) { + if (getState() == THREAD_STATE_RUNNING) { + // check if the event has a valid id + if (task->getEventId() == 0) { + // if not generate one + if (++lastEventId == 0) { + lastEventId = 1; + } + + task->setEventId(lastEventId); + } + + // insert the event id in the list of active events + eventIds.insert(task->getEventId()); + + // add the event to the queue + eventList.push(task); + + // if the list was empty or this event is the top in the list + // we have to signal it + do_signal = (task == eventList.top()); + } else { eventLock.unlock(); delete task; return 0; } - // check if the event has a valid id - if (task->getEventId() == 0) { - // if not generate one - if (++lastEventId == 0) { - lastEventId = 1; - } - - task->setEventId(lastEventId); - } - - // insert the event id in the list of active events - uint32_t eventId = task->getEventId(); - eventIds.insert(eventId); - - // add the event to the queue - eventList.push(task); - - // if the list was empty or this event is the top in the list - // we have to signal it - bool do_signal = (task == eventList.top()); - eventLock.unlock(); if (do_signal) { eventSignal.notify_one(); } - return eventId; + return task->getEventId(); } -bool Scheduler::stopEvent(uint32_t eventId) +bool Scheduler::stopEvent(uint32_t eventid) { - if (eventId == 0) { + if (eventid == 0) { return false; } std::lock_guard lockClass(eventLock); // search the event id.. - auto it = eventIds.find(eventId); + auto it = eventIds.find(eventid); if (it == eventIds.end()) { return false; } @@ -132,7 +132,3 @@ void Scheduler::shutdown() eventSignal.notify_one(); } -SchedulerTask* createSchedulerTask(uint32_t delay, std::function f) -{ - return new SchedulerTask(delay, std::move(f)); -} diff --git a/src/scheduler.h b/src/scheduler.h index e42f59f..cc8a839 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -42,15 +42,18 @@ class SchedulerTask : public Task return expiration; } - private: - SchedulerTask(uint32_t delay, std::function&& f) : Task(delay, std::move(f)) {} + protected: + SchedulerTask(uint32_t delay, const std::function& f) : Task(delay, f) {} uint32_t eventId = 0; - friend SchedulerTask* createSchedulerTask(uint32_t, std::function); + friend SchedulerTask* createSchedulerTask(uint32_t, const std::function&); }; -SchedulerTask* createSchedulerTask(uint32_t delay, std::function f); +inline SchedulerTask* createSchedulerTask(uint32_t delay, const std::function& f) +{ + return new SchedulerTask(delay, f); +} struct TaskComparator { bool operator()(const SchedulerTask* lhs, const SchedulerTask* rhs) const { @@ -67,8 +70,7 @@ class Scheduler : public ThreadHolder void shutdown(); void threadMain(); - - private: + protected: std::thread thread; std::mutex eventLock; std::condition_variable eventSignal; diff --git a/src/script.cpp b/src/script.cpp index 144d9d2..40e955e 100644 --- a/src/script.cpp +++ b/src/script.cpp @@ -1,99 +1,432 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +* Tibia GIMUD Server - a free and open-source MMORPG server emulator +* Copyright (C) 2019 Sabrehaven and Mark Samman +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ #include "otpch.h" #include "script.h" -#include -#include "configmanager.h" -extern LuaEnvironment g_luaEnvironment; -extern ConfigManager g_config; - -Scripts::Scripts() : - scriptInterface("Scripts Interface") +void ScriptReader::nextToken() { - scriptInterface.initState(); -} + signed int v1; // esi@3 + int v4; // eax@5 + int v5; // eax@8 + int v6; // edi@8 + int v7; // eax@24 + int v9; // eax@32 + int v10; // eax@36 + int v11; // edi@36 + int v12; // eax@45 + int v13; // edi@45 + int v14; // eax@52 + int v15; // edi@52 + int v16; // eax@55 + int v17; // eax@62 + int v18; // eax@70 + int v19; // eax@78 + int v20; // edi@78 + int v21; // eax@86 + int v22; // eax@90 + int v23; // edi@90 + int v24; // eax@94 + int v25; // eax@98 + int v26; // edi@98 + int v27; // eax@102 + int v28; // eax@121 + int v29; // eax@127 + int v30; // eax@131 + int v31; // eax@136 + int v32; // eax@139 + std::string FileName; // [sp+1Ch] [bp-1Ch]@2 + int Sign; // [sp+20h] [bp-18h]@5 + FILE *f; // [sp+24h] [bp-14h]@5 + int pos; // [sp+28h] [bp-10h]@3 -Scripts::~Scripts() -{ - scriptInterface.reInitState(); -} - -bool Scripts::loadScripts(std::string folderName, bool isLib, bool reload) -{ - namespace fs = boost::filesystem; - - const auto dir = fs::current_path() / "data" / folderName; - if(!fs::exists(dir) || !fs::is_directory(dir)) { - std::cout << "[Warning - Scripts::loadScripts] Can not load folder '" << folderName << "'." << std::endl; - return false; + if (this->RecursionDepth == -1) + { + error("ScriptReader::nextToken: Kein Skript zum Lesen geffnet.\n"); + LABEL_30: + this->Token = ENDOFFILE; + return; } - - fs::recursive_directory_iterator endit; - std::vector v; - std::string disable = ("#"); - for(fs::recursive_directory_iterator it(dir); it != endit; ++it) { - auto fn = it->path().parent_path().filename(); - if ((fn == "lib" && !isLib) || fn == "events") { + FileName = String; +LABEL_3: + pos = 0; + v1 = 0; + this->String = ""; + v4 = this->RecursionDepth; + this->Number = 0; + Sign = 1; + f = this->File[v4]; + while (2) + { + if (pos == 3999) + error("string too long"); + switch (v1) + { + case 0: + v5 = getc(f); + v6 = v5; + if (v5 == -1) + goto LABEL_24; + if (v5 == 10) + { + ++this->Line[this->RecursionDepth]; + } else if (!isspace(v5)) + { + v1 = 1; + if (v6 != 35) + { + v1 = 30; + if (v6 != 64) + { + if (isalpha(v6)) + { + v1 = 2; + this->String += v6; + pos++; + } else if (IsDigit(v6)) + { + this->Number = v6 - 48; + v1 = 3; + } else + { + v1 = 6; + if (v6 != 34) + { + v1 = 11; + if (v6 != 91) + { + v1 = 22; + if (v6 != 60) + { + v1 = 25; + if (v6 != 62) + { + v1 = 27; + if (v6 != 45) + { + v1 = 10; + this->Special = v6; + } + } + } + } + } + } + } + } + } continue; - } - if(fs::is_regular_file(*it) && it->path().extension() == ".lua") { - size_t found = it->path().filename().string().find(disable); - if (found != std::string::npos) { - if (g_config.getBoolean(ConfigManager::SCRIPTS_CONSOLE_LOGS)) { - std::cout << "> " << it->path().filename().string() << " [disabled]" << std::endl; + case 1: + v9 = getc(f); + if (v9 != -1) + { + if (v9 == 10) + { + ++this->Line[this->RecursionDepth]; + LABEL_35: + v1 = 0; } continue; } - v.push_back(it->path()); - } - } - sort(v.begin(), v.end()); - std::string redir; - for (auto it = v.begin(); it != v.end(); ++it) { - const std::string scriptFile = it->string(); - if (!isLib) { - if (redir.empty() || redir != it->parent_path().string()) { - auto p = fs::path(it->relative_path()); - if (g_config.getBoolean(ConfigManager::SCRIPTS_CONSOLE_LOGS)) { - std::cout << ">> [" << p.parent_path().filename() << "]" << std::endl; + LABEL_24: + v7 = this->RecursionDepth; + if (v7 <= 0) + goto LABEL_30; + if (v7 == -1) + { + printf("ScriptReader::close: Keine Datei offen.\n"); + } else + { + if (fclose(this->File[v7])) + { + printf("ScriptReader::close: Fehler %d beim Schlieen der Datei.\n", RecursionDepth); } - redir = it->parent_path().string(); + --this->RecursionDepth; } - } - - if(scriptInterface.loadFile(scriptFile) == -1) { - std::cout << "> " << it->filename().string() << " [error]" << std::endl; - std::cout << "^ " << scriptInterface.getLastLuaError() << std::endl; + goto LABEL_3; + case 2: + v10 = getc(f); + v11 = v10; + if (pos == 30) + error("identifier too long"); + if (v10 == -1) + goto LABEL_43; + if (isalpha(v10) || IsDigit(v11) || v11 == 95) + goto LABEL_39; + ungetc(v11, f); + LABEL_43: + this->Token = IDENTIFIER; + return; + case 3: + v12 = getc(f); + v13 = v12; + if (v12 == -1) + goto LABEL_50; + if (IsDigit(v12)) + goto LABEL_51; + if (v13 == 45) + goto LABEL_48; + ungetc(v13, f); + LABEL_50: + this->Token = NUMBER; + return; + case 4: + v14 = getc(f); + v15 = v14; + if (v14 == -1) + error("unexpected end of file"); + if (!IsDigit(v14)) + error("syntax error"); + v1 = 5; + this->Number = v15 - 48; continue; - } - - if (g_config.getBoolean(ConfigManager::SCRIPTS_CONSOLE_LOGS)) { - if (!reload) { - std::cout << "> " << it->filename().string() << " [loaded]" << std::endl; - } else { - std::cout << "> " << it->filename().string() << " [reloaded]" << std::endl; + case 5: + v16 = getc(f); + v13 = v16; + if (v16 == -1) + goto LABEL_59; + if (IsDigit(v16)) + goto LABEL_51; + if (v13 != 45) + { + ungetc(v13, f); + LABEL_59: + this->Bytes[pos] = this->Number; + this->Token = BYTES; + return; } + LABEL_48: + this->Bytes[pos++] = this->Number; + v1 = 4; + continue; + case 6: + v17 = getc(f); + v11 = v17; + if (v17 == -1) + error("unexpected end of file"); + if (v17 == 34) + { + v1 = 8; + } else if (v17 == 92) + { + v1 = 7; + } else + { + if (v17 == 10) + ++this->Line[this->RecursionDepth]; + LABEL_39: + this->String += v11; + pos++; + } + continue; + case 7: + v18 = getc(f); + if (v18 == -1) + error("unexpected end of file"); + if (v18 == 110) { + this->String += 10; + pos++; + } else { + this->String += v18; + pos++; + } + v1 = 6; + continue; + case 8: + this->Token = STRING; + return; + case 10: + goto LABEL_77; + case 11: + v19 = getc(f); + this->Special = 91; + v20 = v19; + if (v19 == -1) + goto LABEL_77; + if (IsDigit(v19)) + { + Sign = 1; + this->Number = v20 - 48; + LABEL_82: + v1 = 12; + continue; + } + if (v20 == 45) + { + Sign = -1; + this->Number = 0; + goto LABEL_82; + } + LABEL_83: + ungetc(v20, f); + LABEL_77: + this->Token = SPECIAL; + return; + case 12: + v21 = getc(f); + v13 = v21; + if (v21 == -1) + error("unexpected end of file"); + if (IsDigit(v21)) + goto LABEL_51; + if (v13 != 44) + error("syntax error"); + v1 = 13; + this->CoordX = this->Number * Sign; + continue; + case 13: + v22 = getc(f); + v23 = v22; + if (v22 == -1) + error("unexpected end of file"); + if (IsDigit(v22)) + { + Sign = 1; + this->Number = v23 - 48; + } else + { + if (v23 != 45) + error("syntax error"); + Sign = -1; + this->Number = 0; + } + v1 = 14; + continue; + case 14: + v24 = getc(f); + v13 = v24; + if (v24 == -1) + error("unexpected end of file"); + if (IsDigit(v24)) + goto LABEL_51; + if (v13 != 44) + error("syntax error"); + v1 = 15; + this->CoordY = this->Number * Sign; + continue; + case 15: + v25 = getc(f); + v26 = v25; + if (v25 == -1) + error("unexpected end of file"); + if (IsDigit(v25)) + { + Sign = 1; + this->Number = v26 - 48; + } else + { + if (v26 != 45) + error("syntax error"); + Sign = -1; + this->Number = 0; + } + v1 = 16; + continue; + case 16: + v27 = getc(f); + v13 = v27; + if (v27 == -1) + error("unexpected end of file"); + if (IsDigit(v27)) + { + LABEL_51: + this->Number = v13 + 10 * this->Number - 48; + } else + { + if (v13 != 93) + error("syntax error"); + v1 = 17; + this->CoordZ = this->Number * Sign; + } + continue; + case 17: + this->Token = COORDINATE; + return; + case 22: + v28 = getc(f); + this->Special = 60; + if (v28 == -1) + goto LABEL_77; + v1 = 23; + if (v28 == 61) + continue; + v1 = 24; + if (v28 == 62) + continue; + ungetc(v28, f); + goto LABEL_77; + case 23: + this->Special = 76; + goto LABEL_77; + case 24: + this->Special = 78; + goto LABEL_77; + case 25: + v29 = getc(f); + this->Special = 62; + v20 = v29; + if (v29 == -1) + goto LABEL_77; + v1 = 26; + if (v29 != 61) + goto LABEL_83; + continue; + case 26: + this->Special = 71; + goto LABEL_77; + case 27: + v30 = getc(f); + this->Special = 45; + if (v30 == -1) + goto LABEL_77; + v1 = 28; + if (v30 == 62) + continue; + ungetc(v30, f); + goto LABEL_77; + case 28: + this->Special = 73; + goto LABEL_77; + default: + error("ScriptReader::nextToken: Ungnltiger Zustand.\n"); + goto LABEL_35; + case 30: + v31 = getc(f); + if (v31 == -1) + error("unexpected end of file"); + if (v31 != 34) + error("syntax error"); + v1 = 31; + continue; + case 31: + v32 = getc(f); + v11 = v32; + if (v32 == -1) + error("unexpected end of file"); + if (v32 != 34) + goto LABEL_39; + v1 = 32; + continue; + case 32: + open(CurrentDirectory + "/" + String); + goto LABEL_3; } } - - return true; } diff --git a/src/script.h b/src/script.h index 1a23cb3..60d646a 100644 --- a/src/script.h +++ b/src/script.h @@ -1,40 +1,277 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ +* Tibia GIMUD Server - a free and open-source MMORPG server emulator +* Copyright (C) 2019 Sabrehaven and Mark Samman +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License along +* with this program; if not, write to the Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ -#ifndef FS_SCRIPTS_H -#define FS_SCRIPTS_H +#ifndef FS_SCRIPT_H_2905B3D5EAB34B4BA8830167262D2DC1 +#define FS_SCRIPT_H_2905B3D5EAB34B4BA8830167262D2DC1 -#include "luascript.h" -#include "enums.h" +#include "tools.h" -class Scripts +enum TOKEN { - public: - Scripts(); - ~Scripts(); + ENDOFFILE = 0, + IDENTIFIER, + NUMBER, + STRING, + BYTES, + COORDINATE, + SPECIAL +}; - bool loadScripts(std::string folderName, bool isLib, bool reload); - LuaScriptInterface& getScriptInterface() { - return scriptInterface; +class ScriptReader +{ +public: + ScriptReader() + { + Token = ENDOFFILE; + RecursionDepth = -1; + } + + ~ScriptReader() + { + if (RecursionDepth != -1) + { + std::cout << "ScriptReader::~ScriptReader: File is still open.\n"; + for (int i = RecursionDepth; i != -1; i = RecursionDepth) + { + if (fclose(File[i])) + { + std::cout << "ScriptReader::close: Error when closing the file.\n"; + } + --RecursionDepth; + } } - private: - LuaScriptInterface scriptInterface; + } + + TOKEN Token; + FILE* File[3]; + int RecursionDepth; + char Filename[3][4096]; + std::string CurrentDirectory; + std::string String; + unsigned char Bytes[1000]; + int Line[3]; + int Number; + uint16_t CoordX; + uint16_t CoordY; + uint8_t CoordZ; + char Special; + + bool open(const std::string& FileName) + { + RecursionDepth++; + if (RecursionDepth == 3) + { + error("ScriptReader::open: too big recursion.\n"); + error("Recursion depth too high.\n"); + return false; + } + + if (RecursionDepth > -1) + { + CurrentDirectory = FileName; + if (FileName.find('/') != std::string::npos) { + int32_t end = FileName.find_last_of('/'); + CurrentDirectory = FileName.substr(0, end); + strcpy(Filename[RecursionDepth], FileName.substr(end + 1, FileName.length() - end).c_str()); + } else { + strcpy(Filename[RecursionDepth], FileName.c_str()); + } + + File[RecursionDepth] = fopen(FileName.c_str(), "rb"); + if (!File[RecursionDepth]) + { + printf("ScriptReader::open: Can not open file %s.\n", FileName.c_str()); + RecursionDepth--; + printf("Cannot open script-file\n"); + return false; + } + } + + Line[RecursionDepth] = 1; + return true; + } + + void close() + { + int depth; // eax@1 + + depth = RecursionDepth; + if (depth == -1) + { + std::cout << "ScriptReader::close: Invalid recursion depth.\n"; + } else + { + if (fclose(this->File[depth])) + { + std::cout << "ScriptReader::close: Error when closing file.\n"; + } + --RecursionDepth; + } + } + + void error(const std::string& text) + { + int depth = this->RecursionDepth; + if (depth != -1) + { + printf("error in script-file \"%s\", line %d: %s\n", Filename[this->RecursionDepth], this->Line[this->RecursionDepth], text.c_str()); + do + { + if (fclose(this->File[depth])) + { + std::cout << "ScriptReader::close: Error when closing file.\n"; + } + --this->RecursionDepth; + depth = this->RecursionDepth; + } while (this->RecursionDepth != -1); + } + } + + void nextToken(); + + std::string readIdentifier() + { + nextToken(); + if (this->Token != IDENTIFIER) + error("identifier expected"); + if (this->Token != IDENTIFIER) + error("identifier expected"); + String = asLowerCaseString(String); + return std::string(this->String); + } + + int readNumber() + { + TOKEN v1; // edx@1 + int v2; // esi@1 + + nextToken(); + v1 = this->Token; + v2 = 1; + if (this->Token == SPECIAL && this->Special == 45) + { + v2 = -1; + nextToken(); + v1 = this->Token; + } + if (v1 != NUMBER) + error("number expected"); + if (this->Token != NUMBER) + error("number expected"); + return this->Number * v2; + } + + std::string readString() + { + nextToken(); + if (this->Token != STRING) + error("string expected"); + if (this->Token != STRING) + error("string expected"); + return this->String; + } + + uint8_t* readBytesequence() + { + nextToken(); + if (this->Token != 4) + error("byte-sequence expected"); + if (this->Token != 4) + error("byte-sequence expected"); + return this->Bytes; + } + + void readCoordinate(uint16_t& x, uint16_t& y, uint8_t& z) + { + nextToken(); + if (this->Token != COORDINATE) + error("coordinates expected"); + if (this->Token != COORDINATE) + error("coordinates expected"); + x = this->CoordX; + y = this->CoordY; + z = this->CoordZ; + } + + int readSpecial() + { + nextToken(); + if (this->Token != SPECIAL) + error("special-char expected"); + if (this->Token != SPECIAL) + error("special-char expected"); + return this->Special; + } + + void readSymbol(char Symbol) + { + nextToken(); + if (this->Token != SPECIAL) + error("special-char expected"); + if (Symbol != this->Special) + error("special-char expected"); + } + + std::string getIdentifier() + { + if (this->Token != IDENTIFIER) + error("identifier expected"); + String = asLowerCaseString(String); + return this->String; + } + + int getNumber() + { + if (this->Token != NUMBER) + error("number expected"); + return this->Number; + } + + std::string getString() + { + if (this->Token != STRING) + error("string expected"); + return this->String; + } + + uint8_t* getBytesequence() + { + if (this->Token != BYTES) + error("byte-sequence expected"); + return this->Bytes; + } + + void getcoordinate(int32_t& x, int32_t& y, int32_t& z) + { + if (this->Token != COORDINATE) + error("coordinates expected"); + x = this->CoordX; + y = this->CoordY; + z = this->CoordZ; + } + + int getSpecial() + { + if (this->Token != SPECIAL) + error("special-char expected"); + return this->Special; + } }; #endif diff --git a/src/scriptmanager.cpp b/src/scriptmanager.cpp index b166da7..f372962 100644 --- a/src/scriptmanager.cpp +++ b/src/scriptmanager.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,10 +26,8 @@ #include "talkaction.h" #include "spells.h" #include "movement.h" -#include "weapons.h" #include "globalevent.h" #include "events.h" -#include "script.h" Actions* g_actions = nullptr; CreatureEvents* g_creatureEvents = nullptr; @@ -39,15 +37,12 @@ GlobalEvents* g_globalEvents = nullptr; Spells* g_spells = nullptr; TalkActions* g_talkActions = nullptr; MoveEvents* g_moveEvents = nullptr; -Weapons* g_weapons = nullptr; -Scripts* g_scripts = nullptr; extern LuaEnvironment g_luaEnvironment; ScriptingManager::~ScriptingManager() { delete g_events; - delete g_weapons; delete g_spells; delete g_actions; delete g_talkActions; @@ -55,7 +50,6 @@ ScriptingManager::~ScriptingManager() delete g_chat; delete g_creatureEvents; delete g_globalEvents; - delete g_scripts; } bool ScriptingManager::loadScriptSystems() @@ -64,23 +58,8 @@ bool ScriptingManager::loadScriptSystems() std::cout << "[Warning - ScriptingManager::loadScriptSystems] Can not load data/global.lua" << std::endl; } - g_scripts = new Scripts(); - std::cout << ">> Loading lua libs" << std::endl; - if (!g_scripts->loadScripts("scripts/lib", true, false)) { - std::cout << "> ERROR: Unable to load lua libs!" << std::endl; - return false; - } - g_chat = new Chat(); - g_weapons = new Weapons(); - if (!g_weapons->loadFromXml()) { - std::cout << "> ERROR: Unable to load weapons!" << std::endl; - return false; - } - - g_weapons->loadDefaults(); - g_spells = new Spells(); if (!g_spells->loadFromXml()) { std::cout << "> ERROR: Unable to load spells!" << std::endl; @@ -123,5 +102,6 @@ bool ScriptingManager::loadScriptSystems() return false; } + return true; } diff --git a/src/scriptmanager.h b/src/scriptmanager.h index d3b52e4..fc5da53 100644 --- a/src/scriptmanager.h +++ b/src/scriptmanager.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,9 +30,9 @@ class ScriptingManager ScriptingManager(const ScriptingManager&) = delete; ScriptingManager& operator=(const ScriptingManager&) = delete; - static ScriptingManager& getInstance() { + static ScriptingManager* getInstance() { static ScriptingManager instance; - return instance; + return &instance; } bool loadScriptSystems(); diff --git a/src/server.cpp b/src/server.cpp index 7d58986..d681456 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -56,7 +56,8 @@ void ServiceManager::stop() for (auto& servicePortIt : acceptors) { try { io_service.post(std::bind(&ServicePort::onStopServer, servicePortIt.second)); - } catch (boost::system::system_error& e) { + } + catch (boost::system::system_error& e) { std::cout << "[ServiceManager::stop] Network Error: " << e.what() << std::endl; } } @@ -114,25 +115,28 @@ void ServicePort::onAccept(Connection_ptr connection, const boost::system::error Service_ptr service = services.front(); if (service->is_single_socket()) { connection->accept(service->make_protocol(connection)); - } else { + } + else { connection->accept(); } - } else { + } + else { connection->close(Connection::FORCE_CLOSE); } accept(); - } else if (error != boost::asio::error::operation_aborted) { + } + else if (error != boost::asio::error::operation_aborted) { if (!pendingStart) { close(); pendingStart = true; g_scheduler.addEvent(createSchedulerTask(15000, - std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort))); + std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), serverPort))); } } } -Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const +Protocol_ptr ServicePort::make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const { uint8_t protocolID = msg.getByte(); for (auto& service : services) { @@ -140,9 +144,7 @@ Protocol_ptr ServicePort::make_protocol(bool checksummed, NetworkMessage& msg, c continue; } - if ((checksummed && service->is_checksummed()) || !service->is_checksummed()) { - return service->make_protocol(connection); - } + return service->make_protocol(connection); } return nullptr; } @@ -169,21 +171,23 @@ void ServicePort::open(uint16_t port) try { if (g_config.getBoolean(ConfigManager::BIND_ONLY_GLOBAL_ADDRESS)) { acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint( - boost::asio::ip::address(boost::asio::ip::address_v4::from_string(g_config.getString(ConfigManager::IP))), serverPort))); - } else { + boost::asio::ip::address(boost::asio::ip::address_v4::from_string(g_config.getString(ConfigManager::IP))), serverPort))); + } + else { acceptor.reset(new boost::asio::ip::tcp::acceptor(io_service, boost::asio::ip::tcp::endpoint( - boost::asio::ip::address(boost::asio::ip::address_v4(INADDR_ANY)), serverPort))); + boost::asio::ip::address(boost::asio::ip::address_v4(INADDR_ANY)), serverPort))); } acceptor->set_option(boost::asio::ip::tcp::no_delay(true)); accept(); - } catch (boost::system::system_error& e) { + } + catch (boost::system::system_error& e) { std::cout << "[ServicePort::open] Error: " << e.what() << std::endl; pendingStart = true; g_scheduler.addEvent(createSchedulerTask(15000, - std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port))); + std::bind(&ServicePort::openAcceptor, std::weak_ptr(shared_from_this()), port))); } } @@ -197,7 +201,7 @@ void ServicePort::close() bool ServicePort::add_service(const Service_ptr& new_svc) { - if (std::any_of(services.begin(), services.end(), [](const Service_ptr& svc) {return svc->is_single_socket();})) { + if (std::any_of(services.begin(), services.end(), [](const Service_ptr& svc) {return svc->is_single_socket(); })) { return false; } diff --git a/src/server.h b/src/server.h index 02cb373..e1d9aa2 100644 --- a/src/server.h +++ b/src/server.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,106 +21,100 @@ #define FS_SERVER_H_984DA68ABF744127850F90CC710F281B #include "connection.h" -#include "signals.h" #include class Protocol; class ServiceBase { - public: - virtual bool is_single_socket() const = 0; - virtual bool is_checksummed() const = 0; - virtual uint8_t get_protocol_identifier() const = 0; - virtual const char* get_protocol_name() const = 0; +public: + virtual bool is_single_socket() const = 0; + virtual uint8_t get_protocol_identifier() const = 0; + virtual const char* get_protocol_name() const = 0; - virtual Protocol_ptr make_protocol(const Connection_ptr& c) const = 0; + virtual Protocol_ptr make_protocol(const Connection_ptr& c) const = 0; }; template class Service final : public ServiceBase { - public: - bool is_single_socket() const override { - return ProtocolType::server_sends_first; - } - bool is_checksummed() const override { - return ProtocolType::use_checksum; - } - uint8_t get_protocol_identifier() const override { - return ProtocolType::protocol_identifier; - } - const char* get_protocol_name() const override { - return ProtocolType::protocol_name(); - } +public: + bool is_single_socket() const final { + return ProtocolType::server_sends_first; + } + uint8_t get_protocol_identifier() const final { + return ProtocolType::protocol_identifier; + } + const char* get_protocol_name() const final { + return ProtocolType::protocol_name(); + } - Protocol_ptr make_protocol(const Connection_ptr& c) const override { - return std::make_shared(c); - } + Protocol_ptr make_protocol(const Connection_ptr& c) const final { + return std::make_shared(c); + } }; class ServicePort : public std::enable_shared_from_this { - public: - explicit ServicePort(boost::asio::io_service& io_service) : io_service(io_service) {} - ~ServicePort(); +public: + explicit ServicePort(boost::asio::io_service& io_service) : io_service(io_service) {} + ~ServicePort(); - // non-copyable - ServicePort(const ServicePort&) = delete; - ServicePort& operator=(const ServicePort&) = delete; + // non-copyable + ServicePort(const ServicePort&) = delete; + ServicePort& operator=(const ServicePort&) = delete; - static void openAcceptor(std::weak_ptr weak_service, uint16_t port); - void open(uint16_t port); - void close(); - bool is_single_socket() const; - std::string get_protocol_names() const; + static void openAcceptor(std::weak_ptr weak_service, uint16_t port); + void open(uint16_t port); + void close(); + bool is_single_socket() const; + std::string get_protocol_names() const; - bool add_service(const Service_ptr& new_svc); - Protocol_ptr make_protocol(bool checksummed, NetworkMessage& msg, const Connection_ptr& connection) const; + bool add_service(const Service_ptr& new_svc); + Protocol_ptr make_protocol(NetworkMessage& msg, const Connection_ptr& connection) const; - void onStopServer(); - void onAccept(Connection_ptr connection, const boost::system::error_code& error); + void onStopServer(); + void onAccept(Connection_ptr connection, const boost::system::error_code& error); - private: - void accept(); +protected: + void accept(); - boost::asio::io_service& io_service; - std::unique_ptr acceptor; - std::vector services; + boost::asio::io_service& io_service; + std::unique_ptr acceptor; + std::vector services; - uint16_t serverPort = 0; - bool pendingStart = false; + uint16_t serverPort = 0; + bool pendingStart = false; }; class ServiceManager { - public: - ServiceManager() = default; - ~ServiceManager(); +public: + ServiceManager() = default; + ~ServiceManager(); - // non-copyable - ServiceManager(const ServiceManager&) = delete; - ServiceManager& operator=(const ServiceManager&) = delete; + // non-copyable + ServiceManager(const ServiceManager&) = delete; + ServiceManager& operator=(const ServiceManager&) = delete; - void run(); - void stop(); + void run(); + void stop(); - template - bool add(uint16_t port); + template + bool add(uint16_t port); - bool is_running() const { - return acceptors.empty() == false; - } + bool is_running() const { + return acceptors.empty() == false; + } - private: - void die(); +protected: + void die(); - std::unordered_map acceptors; + std::unordered_map acceptors; - boost::asio::io_service io_service; - Signals signals{io_service}; - boost::asio::deadline_timer death_timer { io_service }; - bool running = false; + boost::asio::io_service io_service; + boost::asio::deadline_timer death_timer{ io_service }; + bool running = false; }; template @@ -139,13 +133,14 @@ bool ServiceManager::add(uint16_t port) service_port = std::make_shared(io_service); service_port->open(port); acceptors[port] = service_port; - } else { + } + else { service_port = foundServicePort->second; if (service_port->is_single_socket() || ProtocolType::server_sends_first) { std::cout << "ERROR: " << ProtocolType::protocol_name() << - " and " << service_port->get_protocol_names() << - " cannot use the same port " << port << '.' << std::endl; + " and " << service_port->get_protocol_names() << + " cannot use the same port " << port << '.' << std::endl; return false; } } diff --git a/src/signals.cpp b/src/signals.cpp deleted file mode 100644 index 9ed5277..0000000 --- a/src/signals.cpp +++ /dev/null @@ -1,212 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" -#include - -#include "signals.h" -#include "tasks.h" -#include "game.h" -#include "actions.h" -#include "configmanager.h" -#include "spells.h" -#include "talkaction.h" -#include "movement.h" -#include "weapons.h" -#include "raids.h" -#include "quests.h" -#include "mounts.h" -#include "globalevent.h" -#include "monster.h" -#include "events.h" -#include "scheduler.h" -#include "databasetasks.h" - - -extern Scheduler g_scheduler; -extern DatabaseTasks g_databaseTasks; -extern Dispatcher g_dispatcher; - -extern ConfigManager g_config; -extern Actions* g_actions; -extern Monsters g_monsters; -extern TalkActions* g_talkActions; -extern MoveEvents* g_moveEvents; -extern Spells* g_spells; -extern Weapons* g_weapons; -extern Game g_game; -extern CreatureEvents* g_creatureEvents; -extern GlobalEvents* g_globalEvents; -extern Events* g_events; -extern Chat* g_chat; -extern LuaEnvironment g_luaEnvironment; - -using ErrorCode = boost::system::error_code; - -Signals::Signals(boost::asio::io_service& service) : - set(service) -{ - set.add(SIGINT); - set.add(SIGTERM); -#ifndef _WIN32 - set.add(SIGUSR1); - set.add(SIGHUP); -#else - // This must be a blocking call as Windows calls it in a new thread and terminates - // the process when the handler returns (or after 5 seconds, whichever is earlier). - // On Windows it is called in a new thread. - signal(SIGBREAK, dispatchSignalHandler); -#endif - - asyncWait(); -} - -void Signals::asyncWait() -{ - set.async_wait([this] (ErrorCode err, int signal) { - if (err) { - std::cerr << "Signal handling error: " << err.message() << std::endl; - return; - } - dispatchSignalHandler(signal); - asyncWait(); - }); -} - -// On Windows this function does not need to be signal-safe, -// as it is called in a new thread. -// https://github.com/otland/forgottenserver/pull/2473 -void Signals::dispatchSignalHandler(int signal) -{ - switch(signal) { - case SIGINT: //Shuts the server down - g_dispatcher.addTask(createTask(sigintHandler)); - break; - case SIGTERM: //Shuts the server down - g_dispatcher.addTask(createTask(sigtermHandler)); - break; -#ifndef _WIN32 - case SIGHUP: //Reload config/data - g_dispatcher.addTask(createTask(sighupHandler)); - break; - case SIGUSR1: //Saves game state - g_dispatcher.addTask(createTask(sigusr1Handler)); - break; -#else - case SIGBREAK: //Shuts the server down - g_dispatcher.addTask(createTask(sigbreakHandler)); - // hold the thread until other threads end - g_scheduler.join(); - g_databaseTasks.join(); - g_dispatcher.join(); - break; -#endif - default: - break; - } -} - -void Signals::sigbreakHandler() -{ - //Dispatcher thread - std::cout << "SIGBREAK received, shutting game server down..." << std::endl; - g_game.setGameState(GAME_STATE_SHUTDOWN); -} - -void Signals::sigtermHandler() -{ - //Dispatcher thread - std::cout << "SIGTERM received, shutting game server down..." << std::endl; - g_game.setGameState(GAME_STATE_SHUTDOWN); -} - -void Signals::sigusr1Handler() -{ - //Dispatcher thread - std::cout << "SIGUSR1 received, saving the game state..." << std::endl; - g_game.saveGameState(); -} - -void Signals::sighupHandler() -{ - //Dispatcher thread - std::cout << "SIGHUP received, reloading config files..." << std::endl; - - g_actions->reload(); - std::cout << "Reloaded actions." << std::endl; - - g_config.reload(); - std::cout << "Reloaded config." << std::endl; - - g_creatureEvents->reload(); - std::cout << "Reloaded creature scripts." << std::endl; - - g_moveEvents->reload(); - std::cout << "Reloaded movements." << std::endl; - - Npcs::reload(); - std::cout << "Reloaded npcs." << std::endl; - - g_game.raids.reload(); - g_game.raids.startup(); - std::cout << "Reloaded raids." << std::endl; - - g_spells->reload(); - std::cout << "Reloaded monsters." << std::endl; - - g_monsters.reload(); - std::cout << "Reloaded spells." << std::endl; - - g_talkActions->reload(); - std::cout << "Reloaded talk actions." << std::endl; - - Item::items.reload(); - std::cout << "Reloaded items." << std::endl; - - g_weapons->reload(); - g_weapons->loadDefaults(); - std::cout << "Reloaded weapons." << std::endl; - - g_game.quests.reload(); - std::cout << "Reloaded quests." << std::endl; - - g_game.mounts.reload(); - std::cout << "Reloaded mounts." << std::endl; - - g_globalEvents->reload(); - std::cout << "Reloaded globalevents." << std::endl; - - g_events->load(); - std::cout << "Reloaded events." << std::endl; - - g_chat->load(); - std::cout << "Reloaded chatchannels." << std::endl; - - g_luaEnvironment.loadFile("data/global.lua"); - std::cout << "Reloaded global.lua." << std::endl; - - lua_gc(g_luaEnvironment.getLuaState(), LUA_GCCOLLECT, 0); -} - -void Signals::sigintHandler() -{ - //Dispatcher thread - std::cout << "SIGINT received, shutting game server down..." << std::endl; - g_game.setGameState(GAME_STATE_SHUTDOWN); -} diff --git a/src/signals.h b/src/signals.h deleted file mode 100644 index 5306852..0000000 --- a/src/signals.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_SIGNALHANDLINGTHREAD_H_01C6BF08B0EFE9E200175D108CF0B35F -#define FS_SIGNALHANDLINGTHREAD_H_01C6BF08B0EFE9E200175D108CF0B35F - -#include - -class Signals -{ - boost::asio::signal_set set; - public: - explicit Signals(boost::asio::io_service& service); - - private: - void asyncWait(); - static void dispatchSignalHandler(int signal); - - static void sigbreakHandler(); - static void sigintHandler(); - static void sighupHandler(); - static void sigtermHandler(); - static void sigusr1Handler(); -}; - -#endif diff --git a/src/spawn.cpp b/src/spawn.cpp index 446d4e0..8b08492 100644 --- a/src/spawn.cpp +++ b/src/spawn.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -64,9 +64,6 @@ bool Spawns::loadFromXml(const std::string& filename) radius = -1; } - spawnList.emplace_front(centerPos, radius); - Spawn& spawn = spawnList.front(); - for (auto childNode : spawnNode.children()) { if (strcasecmp(childNode.name(), "monster") == 0) { pugi::xml_attribute nameAttribute = childNode.attribute("name"); @@ -88,9 +85,18 @@ bool Spawns::loadFromXml(const std::string& filename) centerPos.y + pugi::cast(childNode.attribute("y").value()), centerPos.z ); + + spawnList.emplace_front(pos, radius); + Spawn& spawn = spawnList.front(); + uint32_t interval = pugi::cast(childNode.attribute("spawntime").value()) * 1000; if (interval > MINSPAWN_INTERVAL) { - spawn.addMonster(nameAttribute.as_string(), pos, dir, interval); + uint32_t exInterval = g_config.getNumber(ConfigManager::RATE_SPAWN); + if (exInterval) { + spawn.addMonster(nameAttribute.as_string(), pos, dir, exInterval * 1000); + } else { + spawn.addMonster(nameAttribute.as_string(), pos, dir, interval); + } } else { std::cout << "[Warning - Spawns::loadFromXml] " << nameAttribute.as_string() << ' ' << pos << " spawntime can not be less than " << MINSPAWN_INTERVAL / 1000 << " seconds." << std::endl; } @@ -180,9 +186,9 @@ Spawn::~Spawn() bool Spawn::findPlayer(const Position& pos) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, pos, false, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + g_game.map.getSpectators(list, pos, false, true); + for (Creature* spectator : list) { if (!spectator->getPlayer()->hasFlag(PlayerFlag_IgnoredByMonsters)) { return true; } @@ -220,6 +226,26 @@ bool Spawn::spawnMonster(uint32_t spawnId, MonsterType* mType, const Position& p return true; } +uint32_t Spawn::getInterval() const +{ + uint32_t newInterval = interval; + + if (newInterval > 500000) { + size_t playersOnline = g_game.getPlayersOnline(); + if (playersOnline <= 800) { + if (playersOnline > 200) { + newInterval = 200 * interval / (playersOnline / 2 + 100); + } + } else { + newInterval = 2 * interval / 5; + } + + return normal_random(newInterval / 2, newInterval); + } + + return newInterval; +} + void Spawn::startup() { for (const auto& it : spawnMap) { @@ -235,8 +261,6 @@ void Spawn::checkSpawn() cleanup(); - uint32_t spawnCount = 0; - for (auto& it : spawnMap) { uint32_t spawnId = it.first; if (spawnedMap.find(spawnId) != spawnedMap.end()) { @@ -251,9 +275,6 @@ void Spawn::checkSpawn() } spawnMonster(spawnId, sb.mType, sb.pos, sb.direction); - if (++spawnCount >= static_cast(g_config.getNumber(ConfigManager::RATE_SPAWN))) { - break; - } } } @@ -275,9 +296,6 @@ void Spawn::cleanup() monster->decrementReferenceCounter(); it = spawnedMap.erase(it); - } else if (!isInSpawnZone(monster->getPosition()) && spawnId != 0) { - spawnedMap.insert(spawned_pair(0, monster)); - it = spawnedMap.erase(it); } else { ++it; } diff --git a/src/spawn.h b/src/spawn.h index 15657e6..aece74e 100644 --- a/src/spawn.h +++ b/src/spawn.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -48,9 +48,7 @@ class Spawn bool addMonster(const std::string& name, const Position& pos, Direction dir, uint32_t interval); void removeMonster(Monster* monster); - uint32_t getInterval() const { - return interval; - } + uint32_t getInterval() const; void startup(); void startSpawnCheck(); @@ -61,8 +59,8 @@ class Spawn private: //map of the spawned creatures - using SpawnedMap = std::multimap; - using spawned_pair = SpawnedMap::value_type; + typedef std::multimap SpawnedMap; + typedef SpawnedMap::value_type spawned_pair; SpawnedMap spawnedMap; //map of creatures in the spawn diff --git a/src/spectators.h b/src/spectators.h deleted file mode 100644 index 990d957..0000000 --- a/src/spectators.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_SPECTATORS_H_D78A7CCB7080406E8CAA6B1D31D3DA71 -#define FS_SPECTATORS_H_D78A7CCB7080406E8CAA6B1D31D3DA71 - -#include - -class Creature; - -class SpectatorVec -{ - using Vec = std::vector; - using Iterator = Vec::iterator; - using ConstIterator = Vec::const_iterator; -public: - SpectatorVec() { - vec.reserve(32); - } - - void addSpectators(const SpectatorVec& spectators) { - const size_t size = vec.size(); - for (Creature* spectator : spectators.vec) { - bool duplicate = false; - for (size_t i = 0; i < size; ++i) { - if (vec[i] == spectator) { - duplicate = true; - break; - } - } - - if (!duplicate) { - vec.emplace_back(spectator); - } - } - } - - void erase(Creature* spectator) { - for (size_t i = 0, len = vec.size(); i < len; i++) { - if (vec[i] == spectator) { - Creature* tmp = vec[len - 1]; - vec[len - 1] = vec[i]; - vec[i] = tmp; - vec.pop_back(); - break; - } - } - } - - inline size_t size() const { return vec.size(); } - inline bool empty() const { return vec.empty(); } - inline Iterator begin() { return vec.begin(); } - inline ConstIterator begin() const { return vec.begin(); } - inline ConstIterator cbegin() const { return vec.cbegin(); } - inline Iterator end() { return vec.end(); } - inline ConstIterator end() const { return vec.end(); } - inline ConstIterator cend() const { return vec.cend(); } - inline void emplace_back(Creature* c) { return vec.emplace_back(c); } - - template - inline void insert(Iterator pos, InputIterator first, InputIterator last) { vec.insert(pos, first, last); } - -private: - Vec vec; -}; - -#endif diff --git a/src/spells.cpp b/src/spells.cpp index 36b79e0..03500da 100644 --- a/src/spells.cpp +++ b/src/spells.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,7 +40,7 @@ Spells::Spells() Spells::~Spells() { - clear(false); + clear(); } TalkActionResult_t Spells::playerSaySpell(Player* player, std::string& words) @@ -50,6 +50,15 @@ TalkActionResult_t Spells::playerSaySpell(Player* player, std::string& words) //strip trailing spaces trimString(str_words); + std::ostringstream str_instantSpell; + for (size_t i = 0; i < str_words.length(); i++) { + if (!isspace(str_words[i]) || (i < str_words.length() - 1 && !isspace(str_words[i + 1]))) { + str_instantSpell << str_words[i]; + } + } + + str_words = str_instantSpell.str(); + InstantSpell* instantSpell = getInstantSpell(str_words); if (!instantSpell) { return TALKACTION_CONTINUE; @@ -85,42 +94,25 @@ TalkActionResult_t Spells::playerSaySpell(Player* player, std::string& words) } if (instantSpell->playerCastInstant(player, param)) { - words = instantSpell->getWords(); - - if (instantSpell->getHasParam() && !param.empty()) { - words += " \"" + param + "\""; - } - return TALKACTION_BREAK; } return TALKACTION_FAILED; } -void Spells::clearMaps(bool fromLua) +void Spells::clear() { - for (auto instant = instants.begin(); instant != instants.end(); ) { - if (fromLua == instant->second.fromLua) { - instant = instants.erase(instant); - } else { - ++instant; - } + for (const auto& it : runes) { + delete it.second; } + runes.clear(); - for (auto rune = runes.begin(); rune != runes.end(); ) { - if (fromLua == rune->second.fromLua) { - rune = runes.erase(rune); - } else { - ++rune; - } + for (const auto& it : instants) { + delete it.second; } -} + instants.clear(); -void Spells::clear(bool fromLua) -{ - clearMaps(fromLua); - - reInitState(fromLua); + scriptInterface.reInitState(); } LuaScriptInterface& Spells::getScriptInterface() @@ -133,30 +125,32 @@ std::string Spells::getScriptBaseName() const return "spells"; } -Event_ptr Spells::getEvent(const std::string& nodeName) +Event* Spells::getEvent(const std::string& nodeName) { if (strcasecmp(nodeName.c_str(), "rune") == 0) { - return Event_ptr(new RuneSpell(&scriptInterface)); + return new RuneSpell(&scriptInterface); } else if (strcasecmp(nodeName.c_str(), "instant") == 0) { - return Event_ptr(new InstantSpell(&scriptInterface)); + return new InstantSpell(&scriptInterface); + } else if (strcasecmp(nodeName.c_str(), "conjure") == 0) { + return new ConjureSpell(&scriptInterface); } return nullptr; } -bool Spells::registerEvent(Event_ptr event, const pugi::xml_node&) +bool Spells::registerEvent(Event* event, const pugi::xml_node&) { - InstantSpell* instant = dynamic_cast(event.get()); + InstantSpell* instant = dynamic_cast(event); if (instant) { - auto result = instants.emplace(instant->getWords(), std::move(*instant)); + auto result = instants.emplace(instant->getWords(), instant); if (!result.second) { std::cout << "[Warning - Spells::registerEvent] Duplicate registered instant spell with words: " << instant->getWords() << std::endl; } return result.second; } - RuneSpell* rune = dynamic_cast(event.get()); + RuneSpell* rune = dynamic_cast(event); if (rune) { - auto result = runes.emplace(rune->getRuneItemId(), std::move(*rune)); + auto result = runes.emplace(rune->getRuneItemId(), rune); if (!result.second) { std::cout << "[Warning - Spells::registerEvent] Duplicate registered rune with id: " << rune->getRuneItemId() << std::endl; } @@ -166,36 +160,6 @@ bool Spells::registerEvent(Event_ptr event, const pugi::xml_node&) return false; } -bool Spells::registerInstantLuaEvent(InstantSpell* event) -{ - InstantSpell_ptr instant { event }; - if (instant) { - std::string words = instant->getWords(); - auto result = instants.emplace(instant->getWords(), std::move(*instant)); - if (!result.second) { - std::cout << "[Warning - Spells::registerInstantLuaEvent] Duplicate registered instant spell with words: " << words << std::endl; - } - return result.second; - } - - return false; -} - -bool Spells::registerRuneLuaEvent(RuneSpell* event) -{ - RuneSpell_ptr rune { event }; - if (rune) { - uint16_t id = rune->getRuneItemId(); - auto result = runes.emplace(rune->getRuneItemId(), std::move(*rune)); - if (!result.second) { - std::cout << "[Warning - Spells::registerRuneLuaEvent] Duplicate registered rune with id: " << id << std::endl; - } - return result.second; - } - - return false; -} - Spell* Spells::getSpellByName(const std::string& name) { Spell* spell = getRuneSpellByName(name); @@ -209,21 +173,16 @@ RuneSpell* Spells::getRuneSpell(uint32_t id) { auto it = runes.find(id); if (it == runes.end()) { - for (auto& rune : runes) { - if (rune.second.getId() == id) { - return &rune.second; - } - } return nullptr; } - return &it->second; + return it->second; } RuneSpell* Spells::getRuneSpellByName(const std::string& name) { - for (auto& it : runes) { - if (strcasecmp(it.second.getName().c_str(), name.c_str()) == 0) { - return &it.second; + for (const auto& it : runes) { + if (strcasecmp(it.second->getName().c_str(), name.c_str()) == 0) { + return it.second; } } return nullptr; @@ -233,12 +192,14 @@ InstantSpell* Spells::getInstantSpell(const std::string& words) { InstantSpell* result = nullptr; - for (auto& it : instants) { - const std::string& instantSpellWords = it.second.getWords(); + for (const auto& it : instants) { + InstantSpell* instantSpell = it.second; + + const std::string& instantSpellWords = instantSpell->getWords(); size_t spellLen = instantSpellWords.length(); if (strncasecmp(instantSpellWords.c_str(), words.c_str(), spellLen) == 0) { if (!result || spellLen > result->getWords().length()) { - result = &it.second; + result = instantSpell; if (words.length() == spellLen) { break; } @@ -249,26 +210,41 @@ InstantSpell* Spells::getInstantSpell(const std::string& words) if (result) { const std::string& resultWords = result->getWords(); if (words.length() > resultWords.length()) { - if (!result->getHasParam()) { - return nullptr; - } - size_t spellLen = resultWords.length(); size_t paramLen = words.length() - spellLen; if (paramLen < 2 || words[spellLen] != ' ') { return nullptr; } } + return result; } + return nullptr; } -InstantSpell* Spells::getInstantSpellById(uint32_t spellId) +uint32_t Spells::getInstantSpellCount(const Player* player) const { - for (auto& it : instants) { - if (it.second.getId() == spellId) { - return &it.second; + uint32_t count = 0; + for (const auto& it : instants) { + InstantSpell* instantSpell = it.second; + if (instantSpell->canCast(player)) { + ++count; + } + } + return count; +} + +InstantSpell* Spells::getInstantSpellByIndex(const Player* player, uint32_t index) +{ + uint32_t count = 0; + for (const auto& it : instants) { + InstantSpell* instantSpell = it.second; + if (instantSpell->canCast(player)) { + if (count == index) { + return instantSpell; + } + ++count; } } return nullptr; @@ -276,9 +252,9 @@ InstantSpell* Spells::getInstantSpellById(uint32_t spellId) InstantSpell* Spells::getInstantSpellByName(const std::string& name) { - for (auto& it : instants) { - if (strcasecmp(it.second.getName().c_str(), name.c_str()) == 0) { - return &it.second; + for (const auto& it : instants) { + if (strcasecmp(it.second->getName().c_str(), name.c_str()) == 0) { + return it.second; } } return nullptr; @@ -402,7 +378,7 @@ bool Spell::configureSpell(const pugi::xml_node& node) name = nameAttribute.as_string(); - static const char* reservedList[] = { + /*static const char* reservedList[] = { "melee", "physical", "poison", @@ -423,9 +399,6 @@ bool Spell::configureSpell(const pugi::xml_node& node) "poisoncondition", "energycondition", "drowncondition", - "freezecondition", - "cursecondition", - "dazzlecondition" }; //static size_t size = sizeof(reservedList) / sizeof(const char*); @@ -435,60 +408,18 @@ bool Spell::configureSpell(const pugi::xml_node& node) std::cout << "[Error - Spell::configureSpell] Spell is using a reserved name: " << reserved << std::endl; return false; } - } + }*/ pugi::xml_attribute attr; if ((attr = node.attribute("spellid"))) { spellId = pugi::cast(attr.value()); } - if ((attr = node.attribute("group"))) { - std::string tmpStr = asLowerCaseString(attr.as_string()); - if (tmpStr == "none" || tmpStr == "0") { - group = SPELLGROUP_NONE; - } else if (tmpStr == "attack" || tmpStr == "1") { - group = SPELLGROUP_ATTACK; - } else if (tmpStr == "healing" || tmpStr == "2") { - group = SPELLGROUP_HEALING; - } else if (tmpStr == "support" || tmpStr == "3") { - group = SPELLGROUP_SUPPORT; - } else if (tmpStr == "special" || tmpStr == "4") { - group = SPELLGROUP_SPECIAL; - } else { - std::cout << "[Warning - Spell::configureSpell] Unknown group: " << attr.as_string() << std::endl; - } - } - - if ((attr = node.attribute("groupcooldown"))) { - groupCooldown = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("secondarygroup"))) { - std::string tmpStr = asLowerCaseString(attr.as_string()); - if (tmpStr == "none" || tmpStr == "0") { - secondaryGroup = SPELLGROUP_NONE; - } else if (tmpStr == "attack" || tmpStr == "1") { - secondaryGroup = SPELLGROUP_ATTACK; - } else if (tmpStr == "healing" || tmpStr == "2") { - secondaryGroup = SPELLGROUP_HEALING; - } else if (tmpStr == "support" || tmpStr == "3") { - secondaryGroup = SPELLGROUP_SUPPORT; - } else if (tmpStr == "special" || tmpStr == "4") { - secondaryGroup = SPELLGROUP_SPECIAL; - } else { - std::cout << "[Warning - Spell::configureSpell] Unknown secondarygroup: " << attr.as_string() << std::endl; - } - } - - if ((attr = node.attribute("secondarygroupcooldown"))) { - secondaryGroupCooldown = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("level")) || (attr = node.attribute("lvl"))) { + if ((attr = node.attribute("lvl"))) { level = pugi::cast(attr.value()); } - if ((attr = node.attribute("magiclevel")) || (attr = node.attribute("maglv"))) { + if ((attr = node.attribute("maglv"))) { magLevel = pugi::cast(attr.value()); } @@ -508,11 +439,11 @@ bool Spell::configureSpell(const pugi::xml_node& node) range = pugi::cast(attr.value()); } - if ((attr = node.attribute("cooldown")) || (attr = node.attribute("exhaustion"))) { + if ((attr = node.attribute("exhaustion")) || (attr = node.attribute("cooldown"))) { cooldown = pugi::cast(attr.value()); } - if ((attr = node.attribute("premium")) || (attr = node.attribute("prem"))) { + if ((attr = node.attribute("prem"))) { premium = attr.as_bool(); } @@ -559,10 +490,6 @@ bool Spell::configureSpell(const pugi::xml_node& node) aggressive = booleanString(attr.as_string()); } - if (group == SPELLGROUP_NONE) { - group = (aggressive ? SPELLGROUP_ATTACK : SPELLGROUP_HEALING); - } - for (auto vocationNode : node.children()) { if (!(attr = vocationNode.attribute("name"))) { continue; @@ -593,23 +520,12 @@ bool Spell::playerSpellCheck(Player* player) const return false; } - if (aggressive && (range < 1 || (range > 0 && !player->getAttackedCreature())) && player->getSkull() == SKULL_BLACK) { - player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); - return false; - } - - if (aggressive && player->hasCondition(CONDITION_PACIFIED)) { - player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED); - g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); - return false; - } - - if (aggressive && !player->hasFlag(PlayerFlag_IgnoreProtectionZone) && player->getZone() == ZONE_PROTECTION) { + if (aggressive && !player->hasFlag(PlayerFlag_IgnoreProtectionZone) && player->getZone() == ZONE_PROTECTION) { player->sendCancelMessage(RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE); return false; } - if (player->hasCondition(CONDITION_SPELLGROUPCOOLDOWN, group) || player->hasCondition(CONDITION_SPELLCOOLDOWN, spellId) || (secondaryGroup != SPELLGROUP_NONE && player->hasCondition(CONDITION_SPELLGROUPCOOLDOWN, secondaryGroup))) { + if (player->hasCondition(CONDITION_EXHAUST)) { player->sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED); if (isInstant()) { @@ -656,7 +572,14 @@ bool Spell::playerSpellCheck(Player* player) const } if (needWeapon) { - switch (player->getWeaponType()) { + Item* weapon = player->getWeapon(); + if (!weapon) { + player->sendCancelMessage(RETURNVALUE_YOUNEEDAWEAPONTOUSETHISSPELL); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + switch (weapon->getWeaponType()) { case WEAPON_SWORD: case WEAPON_CLUB: case WEAPON_AXE: @@ -765,31 +688,33 @@ bool Spell::playerRuneSpellCheck(Player* player, const Position& toPos) return false; } - const Creature* topVisibleCreature = tile->getBottomVisibleCreature(player); - if (blockingCreature && topVisibleCreature) { + const Creature* visibleCreature = tile->getTopCreature(); + if (blockingCreature && visibleCreature) { player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; - } else if (blockingSolid && tile->hasFlag(TILESTATE_BLOCKSOLID)) { + } + else if (blockingSolid && tile->hasFlag(TILESTATE_BLOCKSOLID)) { player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } - if (needTarget && !topVisibleCreature) { + if (needTarget && !visibleCreature) { player->sendCancelMessage(RETURNVALUE_CANONLYUSETHISRUNEONCREATURES); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } - if (aggressive && needTarget && topVisibleCreature && player->hasSecureMode()) { - const Player* targetPlayer = topVisibleCreature->getPlayer(); + if (aggressive && needTarget && visibleCreature && player->hasSecureMode()) { + const Player* targetPlayer = visibleCreature->getPlayer(); if (targetPlayer && targetPlayer != player && player->getSkullClient(targetPlayer) == SKULL_NONE && !Combat::isInPvpZone(player, targetPlayer)) { player->sendCancelMessage(RETURNVALUE_TURNSECUREMODETOATTACKUNMARKEDPLAYERS); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; } } + return true; } @@ -797,19 +722,22 @@ void Spell::postCastSpell(Player* player, bool finishedCast /*= true*/, bool pay { if (finishedCast) { if (!player->hasFlag(PlayerFlag_HasNoExhaustion)) { - if (cooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, cooldown, 0, false, spellId); - player->addCondition(condition); - } - - if (groupCooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLGROUPCOOLDOWN, groupCooldown, 0, false, group); - player->addCondition(condition); - } - - if (secondaryGroupCooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLGROUPCOOLDOWN, secondaryGroupCooldown, 0, false, secondaryGroup); - player->addCondition(condition); + if (aggressive) { + if (cooldown > 0) { + Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, cooldown); + player->addCondition(condition); + } else { + Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 2000); + player->addCondition(condition); + } + } else { + if (cooldown > 0) { + Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, cooldown); + player->addCondition(condition); + } else { + Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_EXHAUST, 1000); + player->addCondition(condition); + } } } @@ -844,14 +772,50 @@ uint32_t Spell::getManaCost(const Player* player) const } if (manaPercent != 0) { - uint32_t maxMana = player->getMaxMana(); - uint32_t manaCost = (maxMana * manaPercent) / 100; - return manaCost; + return player->getLevel() * manaPercent; } return 0; } +ReturnValue Spell::CreateIllusion(Creature* creature, const Outfit_t& outfit, int32_t time) +{ + ConditionOutfit* outfitCondition = new ConditionOutfit(CONDITIONID_COMBAT, CONDITION_OUTFIT, time); + outfitCondition->setOutfit(outfit); + creature->addCondition(outfitCondition); + return RETURNVALUE_NOERROR; +} + +ReturnValue Spell::CreateIllusion(Creature* creature, const std::string& name, int32_t time) +{ + const auto mType = g_monsters.getMonsterType(name); + if (mType == nullptr) { + return RETURNVALUE_CREATUREDOESNOTEXIST; + } + + Player* player = creature->getPlayer(); + if (player && !player->hasFlag(PlayerFlag_CanIllusionAll)) { + if (!mType->info.isIllusionable) { + return RETURNVALUE_NOTPOSSIBLE; + } + } + + return CreateIllusion(creature, mType->info.outfit, time); +} + +ReturnValue Spell::CreateIllusion(Creature* creature, uint32_t itemId, int32_t time) +{ + const ItemType& it = Item::items[itemId]; + if (it.id == 0) { + return RETURNVALUE_NOTPOSSIBLE; + } + + Outfit_t outfit; + outfit.lookTypeEx = itemId; + + return CreateIllusion(creature, outfit, time); +} + std::string InstantSpell::getScriptEventName() const { return "onCastSpell"; @@ -867,8 +831,6 @@ bool InstantSpell::configureEvent(const pugi::xml_node& node) return false; } - spellType = SPELL_INSTANT; - pugi::xml_attribute attr; if ((attr = node.attribute("params"))) { hasParam = attr.as_bool(); @@ -890,6 +852,34 @@ bool InstantSpell::configureEvent(const pugi::xml_node& node) return true; } +bool InstantSpell::loadFunction(const pugi::xml_attribute& attr) +{ + const char* functionName = attr.as_string(); + if (strcasecmp(functionName, "edithouseguest") == 0) { + function = HouseGuestList; + } else if (strcasecmp(functionName, "edithousesubowner") == 0) { + function = HouseSubOwnerList; + } else if (strcasecmp(functionName, "edithousedoor") == 0) { + function = HouseDoorList; + } else if (strcasecmp(functionName, "housekick") == 0) { + function = HouseKick; + } else if (strcasecmp(functionName, "searchplayer") == 0) { + function = SearchPlayer; + } else if (strcasecmp(functionName, "levitate") == 0) { + function = Levitate; + } else if (strcasecmp(functionName, "illusion") == 0) { + function = Illusion; + } else if (strcasecmp(functionName, "summonmonster") == 0) { + function = SummonMonster; + } else { + std::cout << "[Warning - InstantSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + return false; + } + + scripted = false; + return true; +} + bool InstantSpell::playerCastInstant(Player* player, std::string& param) { if (!playerSpellCheck(player)) { @@ -916,21 +906,6 @@ bool InstantSpell::playerCastInstant(Player* player, std::string& param) target = playerTarget; if (!target || target->getHealth() <= 0) { if (!casterTargetOrDirection) { - if (cooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, cooldown, 0, false, spellId); - player->addCondition(condition); - } - - if (groupCooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLGROUPCOOLDOWN, groupCooldown, 0, false, group); - player->addCondition(condition); - } - - if (secondaryGroupCooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLGROUPCOOLDOWN, secondaryGroupCooldown, 0, false, secondaryGroup); - player->addCondition(condition); - } - player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; @@ -980,21 +955,6 @@ bool InstantSpell::playerCastInstant(Player* player, std::string& param) ReturnValue ret = g_game.getPlayerByNameWildcard(param, playerTarget); if (ret != RETURNVALUE_NOERROR) { - if (cooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLCOOLDOWN, cooldown, 0, false, spellId); - player->addCondition(condition); - } - - if (groupCooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLGROUPCOOLDOWN, groupCooldown, 0, false, group); - player->addCondition(condition); - } - - if (secondaryGroupCooldown > 0) { - Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_SPELLGROUPCOOLDOWN, secondaryGroupCooldown, 0, false, secondaryGroup); - player->addCondition(condition); - } - player->sendCancelMessage(ret); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); return false; @@ -1082,7 +1042,13 @@ bool InstantSpell::castSpell(Creature* creature, Creature* target) bool InstantSpell::internalCastSpell(Creature* creature, const LuaVariant& var) { - return executeCastSpell(creature, var); + if (scripted) { + return executeCastSpell(creature, var); + } else if (function) { + return function(this, creature, var.text); + } + + return false; } bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant& var) @@ -1108,6 +1074,409 @@ bool InstantSpell::executeCastSpell(Creature* creature, const LuaVariant& var) return scriptInterface->callFunction(2); } +House* InstantSpell::getHouseFromPos(Creature* creature) +{ + if (!creature) { + return nullptr; + } + + Player* player = creature->getPlayer(); + if (!player) { + return nullptr; + } + + HouseTile* houseTile = dynamic_cast(player->getTile()); + if (!houseTile) { + return nullptr; + } + + House* house = houseTile->getHouse(); + if (!house) { + return nullptr; + } + + return house; +} + +bool InstantSpell::HouseGuestList(const InstantSpell*, Creature* creature, const std::string&) +{ + House* house = getHouseFromPos(creature); + if (!house) { + return false; + } + + Player* player = creature->getPlayer(); + if (house->canEditAccessList(GUEST_LIST, player)) { + player->setEditHouse(house, GUEST_LIST); + player->sendHouseWindow(house, GUEST_LIST); + } else { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + } + return true; +} + +bool InstantSpell::HouseSubOwnerList(const InstantSpell*, Creature* creature, const std::string&) +{ + House* house = getHouseFromPos(creature); + if (!house) { + return false; + } + + Player* player = creature->getPlayer(); + if (house->canEditAccessList(SUBOWNER_LIST, player)) { + player->setEditHouse(house, SUBOWNER_LIST); + player->sendHouseWindow(house, SUBOWNER_LIST); + } else { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + } + return true; +} + +bool InstantSpell::HouseDoorList(const InstantSpell*, Creature* creature, const std::string&) +{ + House* house = getHouseFromPos(creature); + if (!house) { + return false; + } + + Player* player = creature->getPlayer(); + Position pos = Spells::getCasterPosition(player, player->getDirection()); + Door* door = house->getDoorByPosition(pos); + if (door && house->canEditAccessList(door->getDoorId(), player)) { + player->setEditHouse(house, door->getDoorId()); + player->sendHouseWindow(house, door->getDoorId()); + } else { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + } + return true; +} + +bool InstantSpell::HouseKick(const InstantSpell*, Creature* creature, const std::string& param) +{ + Player* player = creature->getPlayer(); + + Player* targetPlayer = g_game.getPlayerByName(param); + if (!targetPlayer) { + targetPlayer = player; + } + + House* house = getHouseFromPos(targetPlayer); + if (!house) { + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return false; + } + + if (!house->kickPlayer(player, targetPlayer)) { + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + return false; + } + return true; +} + +bool InstantSpell::SearchPlayer(const InstantSpell*, Creature* creature, const std::string& param) +{ + //a. From 1 to 4 sq's [Person] is standing next to you. + //b. From 5 to 100 sq's [Person] is to the south, north, east, west. + //c. From 101 to 274 sq's [Person] is far to the south, north, east, west. + //d. From 275 to infinite sq's [Person] is very far to the south, north, east, west. + //e. South-west, s-e, n-w, n-e (corner coordinates): this phrase appears if the player you're looking for has moved five squares in any direction from the south, north, east or west. + //f. Lower level to the (direction): this phrase applies if the person you're looking for is from 1-25 squares up/down the actual floor you're in. + //g. Higher level to the (direction): this phrase applies if the person you're looking for is from 1-25 squares up/down the actual floor you're in. + + Player* player = creature->getPlayer(); + if (!player) { + return false; + } + + enum distance_t { + DISTANCE_BESIDE, + DISTANCE_CLOSE, + DISTANCE_FAR, + DISTANCE_VERYFAR, + }; + + enum direction_t { + DIR_N, DIR_S, DIR_E, DIR_W, + DIR_NE, DIR_NW, DIR_SE, DIR_SW, + }; + + enum level_t { + LEVEL_HIGHER, + LEVEL_LOWER, + LEVEL_SAME, + }; + + Player* playerExiva = g_game.getPlayerByName(param); + if (!playerExiva) { + return false; + } + + if (playerExiva->isAccessPlayer() && !player->isAccessPlayer()) { + player->sendCancelMessage(RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + const Position& lookPos = player->getPosition(); + const Position& searchPos = playerExiva->getPosition(); + + int32_t dx = Position::getOffsetX(lookPos, searchPos); + int32_t dy = Position::getOffsetY(lookPos, searchPos); + int32_t dz = Position::getOffsetZ(lookPos, searchPos); + + distance_t distance; + + direction_t direction; + + level_t level; + + //getting floor + if (dz > 0) { + level = LEVEL_HIGHER; + } else if (dz < 0) { + level = LEVEL_LOWER; + } else { + level = LEVEL_SAME; + } + + //getting distance + if (std::abs(dx) < 4 && std::abs(dy) < 4) { + distance = DISTANCE_BESIDE; + } else { + int32_t distance2 = dx * dx + dy * dy; + if (distance2 < 10000) { + distance = DISTANCE_CLOSE; + } else if (distance2 < 75076) { + distance = DISTANCE_FAR; + } else { + distance = DISTANCE_VERYFAR; + } + } + + //getting direction + float tan; + if (dx != 0) { + tan = static_cast(dy) / dx; + } else { + tan = 10.; + } + + if (std::abs(tan) < 0.4142) { + if (dx > 0) { + direction = DIR_W; + } else { + direction = DIR_E; + } + } else if (std::abs(tan) < 2.4142) { + if (tan > 0) { + if (dy > 0) { + direction = DIR_NW; + } else { + direction = DIR_SE; + } + } else { + if (dx > 0) { + direction = DIR_SW; + } else { + direction = DIR_NE; + } + } + } else { + if (dy > 0) { + direction = DIR_N; + } else { + direction = DIR_S; + } + } + + std::ostringstream ss; + ss << playerExiva->getName(); + + if (distance == DISTANCE_BESIDE) { + if (level == LEVEL_SAME) { + ss << " is standing next to you."; + } else if (level == LEVEL_HIGHER) { + ss << " is above you."; + } else if (level == LEVEL_LOWER) { + ss << " is below you."; + } + } else { + switch (distance) { + case DISTANCE_CLOSE: + if (level == LEVEL_SAME) { + ss << " is to the "; + } else if (level == LEVEL_HIGHER) { + ss << " is on a higher level to the "; + } else if (level == LEVEL_LOWER) { + ss << " is on a lower level to the "; + } + break; + case DISTANCE_FAR: + ss << " is far to the "; + break; + case DISTANCE_VERYFAR: + ss << " is very far to the "; + break; + default: + break; + } + + switch (direction) { + case DIR_N: + ss << "north."; + break; + case DIR_S: + ss << "south."; + break; + case DIR_E: + ss << "east."; + break; + case DIR_W: + ss << "west."; + break; + case DIR_NE: + ss << "north-east."; + break; + case DIR_NW: + ss << "north-west."; + break; + case DIR_SE: + ss << "south-east."; + break; + case DIR_SW: + ss << "south-west."; + break; + } + } + player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); + g_game.addMagicEffect(player->getPosition(), CONST_ME_MAGIC_BLUE); + return true; +} + +bool InstantSpell::SummonMonster(const InstantSpell* spell, Creature* creature, const std::string& param) +{ + Player* player = creature->getPlayer(); + if (!player) { + return false; + } + + MonsterType* mType = g_monsters.getMonsterType(param); + if (!mType) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + if (!player->hasFlag(PlayerFlag_CanSummonAll)) { + if (!mType->info.isSummonable) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + if (player->getMana() < mType->info.manaCost) { + player->sendCancelMessage(RETURNVALUE_NOTENOUGHMANA); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + if (player->getSummonCount() >= 2) { + player->sendCancelMessage("You cannot summon more creatures."); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + } + + Monster* monster = Monster::createMonster(param); + if (!monster) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + // Place the monster + creature->addSummon(monster); + + if (!g_game.placeCreature(monster, creature->getPosition(), true)) { + creature->removeSummon(monster); + player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + Spell::postCastSpell(player, mType->info.manaCost, spell->getSoulCost()); + g_game.addMagicEffect(player->getPosition(), CONST_ME_MAGIC_BLUE); + g_game.addMagicEffect(monster->getPosition(), CONST_ME_TELEPORT); + return true; +} + +bool InstantSpell::Levitate(const InstantSpell*, Creature* creature, const std::string& param) +{ + Player* player = creature->getPlayer(); + if (!player) { + return false; + } + + const Position& currentPos = creature->getPosition(); + const Position& destPos = Spells::getCasterPosition(creature, creature->getDirection()); + + ReturnValue ret = RETURNVALUE_NOTPOSSIBLE; + + if (strcasecmp(param.c_str(), "up") == 0) { + if (currentPos.z != 8) { + Tile* tmpTile = g_game.map.getTile(currentPos.x, currentPos.y, currentPos.getZ() - 1); + if (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID))) { + tmpTile = g_game.map.getTile(destPos.x, destPos.y, destPos.getZ() - 1); + if (tmpTile && tmpTile->getGround() && !tmpTile->hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID)) { + ret = g_game.internalMoveCreature(*player, *tmpTile, FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE); + } + } + } + } else if (strcasecmp(param.c_str(), "down") == 0) { + if (currentPos.z != 7) { + Tile* tmpTile = g_game.map.getTile(destPos); + if (tmpTile == nullptr || (tmpTile->getGround() == nullptr && !tmpTile->hasFlag(TILESTATE_BLOCKSOLID))) { + tmpTile = g_game.map.getTile(destPos.x, destPos.y, destPos.z + 1); + if (tmpTile && tmpTile->getGround() && !tmpTile->hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID)) { + ret = g_game.internalMoveCreature(*player, *tmpTile, FLAG_IGNOREBLOCKITEM | FLAG_IGNOREBLOCKCREATURE); + } + } + } + } + + if (ret != RETURNVALUE_NOERROR) { + player->sendCancelMessage(ret); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + g_game.addMagicEffect(player->getPosition(), CONST_ME_TELEPORT); + return true; +} + +bool InstantSpell::Illusion(const InstantSpell*, Creature* creature, const std::string& param) +{ + Player* player = creature->getPlayer(); + if (!player) { + return false; + } + + ReturnValue ret = CreateIllusion(creature, param, 180000); + if (ret != RETURNVALUE_NOERROR) { + player->sendCancelMessage(ret); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + g_game.addMagicEffect(player->getPosition(), CONST_ME_MAGIC_RED); + return true; +} + bool InstantSpell::canCast(const Player* player) const { if (player->hasFlag(PlayerFlag_CannotUseSpells)) { @@ -1131,6 +1500,152 @@ bool InstantSpell::canCast(const Player* player) const return false; } +std::string ConjureSpell::getScriptEventName() const +{ + return "onCastSpell"; +} + +bool ConjureSpell::configureEvent(const pugi::xml_node& node) +{ + if (!InstantSpell::configureEvent(node)) { + return false; + } + + pugi::xml_attribute attr; + if ((attr = node.attribute("conjureId"))) { + conjureId = pugi::cast(attr.value()); + } + + if ((attr = node.attribute("conjureCount"))) { + conjureCount = pugi::cast(attr.value()); + } else if (conjureId != 0) { + // load default charges from items.xml + const ItemType& it = Item::items[conjureId]; + if (it.charges != 0) { + conjureCount = it.charges; + } + } + + if ((attr = node.attribute("reagentId"))) { + reagentId = pugi::cast(attr.value()); + } + + ItemType& iType = Item::items.getItemType(conjureId); + if (iType.isRune()) { + iType.runeSpellName = words; + } + + return true; +} + +bool ConjureSpell::loadFunction(const pugi::xml_attribute&) +{ + scripted = false; + return true; +} + +bool ConjureSpell::conjureItem(Creature* creature) const +{ + Player* player = creature->getPlayer(); + if (!player) { + return false; + } + + const uint32_t conjureCost = getManaCost(player); + const uint32_t soulCost = getSoulCost(); + + if (reagentId != 0) { + bool foundReagent = false; + + Item* item = player->getInventoryItem(CONST_SLOT_LEFT); + if (item && item->getID() == reagentId) { + foundReagent = true; + + // left arm conjure + int32_t index = player->getThingIndex(item); + g_game.internalRemoveItem(item); + + Item* newItem = Item::CreateItem(conjureId, conjureCount); + if (!newItem) { + return false; + } + + ReturnValue ret = g_game.internalAddItem(player, newItem, index); + if (ret != RETURNVALUE_NOERROR) { + delete newItem; + return false; + } + + g_game.startDecay(newItem); + + Spell::postCastSpell(player, conjureCost, soulCost); + } + + item = player->getInventoryItem(CONST_SLOT_RIGHT); + if (item && item->getID() == reagentId && player->getMana() >= conjureCost) { + foundReagent = true; + + // right arm conjure + int32_t index = player->getThingIndex(item); + g_game.internalRemoveItem(item); + + Item* newItem = Item::CreateItem(conjureId, conjureCount); + if (!newItem) { + return false; + } + + ReturnValue ret = g_game.internalAddItem(player, newItem, index); + if (ret != RETURNVALUE_NOERROR) { + delete newItem; + return false; + } + + g_game.startDecay(newItem); + + Spell::postCastSpell(player, conjureCost, soulCost); + } + + if (!foundReagent) { + player->sendCancelMessage(RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + } else { + Item* newItem = Item::CreateItem(conjureId, conjureCount); + if (!newItem) { + return false; + } + + ReturnValue ret = g_game.internalPlayerAddItem(player, newItem); + if (ret != RETURNVALUE_NOERROR) { + delete newItem; + return false; + } + + g_game.startDecay(newItem); + Spell::postCastSpell(player, conjureCost, soulCost); + } + + postCastSpell(player, true, false); + g_game.addMagicEffect(player->getPosition(), CONST_ME_MAGIC_RED); + return true; +} + +bool ConjureSpell::playerCastInstant(Player* player, std::string& param) +{ + if (!playerSpellCheck(player)) { + return false; + } + + if (scripted) { + LuaVariant var; + var.type = VARIANT_STRING; + var.text = param; + return executeCastSpell(player, var); + } + return conjureItem(player); +} + std::string RuneSpell::getScriptEventName() const { return "onCastSpell"; @@ -1146,8 +1661,6 @@ bool RuneSpell::configureEvent(const pugi::xml_node& node) return false; } - spellType = SPELL_RUNE; - pugi::xml_attribute attr; if (!(attr = node.attribute("id"))) { std::cout << "[Error - RuneSpell::configureSpell] Rune spell without id." << std::endl; @@ -1155,6 +1668,7 @@ bool RuneSpell::configureEvent(const pugi::xml_node& node) } runeId = pugi::cast(attr.value()); + uint32_t charges; if ((attr = node.attribute("charges"))) { charges = pugi::cast(attr.value()); } else { @@ -1162,14 +1676,107 @@ bool RuneSpell::configureEvent(const pugi::xml_node& node) } hasCharges = (charges > 0); - if (magLevel != 0 || level != 0) { - //Change information in the ItemType to get accurate description - ItemType& iType = Item::items.getItemType(runeId); - iType.runeMagLevel = magLevel; - iType.runeLevel = level; - iType.charges = charges; + + //Change information in the ItemType to get accurate description + ItemType& iType = Item::items.getItemType(runeId); + iType.runeMagLevel = magLevel; + iType.runeLevel = level; + iType.charges = charges; + + return true; +} + +bool RuneSpell::loadFunction(const pugi::xml_attribute& attr) +{ + const char* functionName = attr.as_string(); + if (strcasecmp(functionName, "chameleon") == 0) { + runeFunction = Illusion; + } else if (strcasecmp(functionName, "convince") == 0) { + runeFunction = Convince; + } else { + std::cout << "[Warning - RuneSpell::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; + return false; } + scripted = false; + return true; +} + +bool RuneSpell::Illusion(const RuneSpell*, Player* player, const Position& posTo) +{ + Thing* thing = g_game.internalGetThing(player, posTo, 0, 0, STACKPOS_MOVE); + if (!thing) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + Item* illusionItem = thing->getItem(); + if (!illusionItem || !illusionItem->isMoveable()) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + uint32_t itemId = illusionItem->getID(); + if (illusionItem->isDisguised()) { + itemId = illusionItem->getDisguiseId(); + } + + ReturnValue ret = CreateIllusion(player, itemId, 200000); + if (ret != RETURNVALUE_NOERROR) { + player->sendCancelMessage(ret); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + g_game.addMagicEffect(player->getPosition(), CONST_ME_MAGIC_RED); + return true; +} + +bool RuneSpell::Convince(const RuneSpell* spell, Player* player, const Position& posTo) +{ + if (!player->hasFlag(PlayerFlag_CanConvinceAll)) { + if (player->getSummonCount() >= 2) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + } + + Thing* thing = g_game.internalGetThing(player, posTo, 0, 0, STACKPOS_LOOK); + if (!thing) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + Creature* convinceCreature = thing->getCreature(); + if (!convinceCreature) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + uint32_t manaCost = 0; + if (convinceCreature->getMonster()) { + manaCost = convinceCreature->getMonster()->getManaCost(); + } + + if (!player->hasFlag(PlayerFlag_HasInfiniteMana) && player->getMana() < manaCost) { + player->sendCancelMessage(RETURNVALUE_NOTENOUGHMANA); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + if (!convinceCreature->convinceCreature(player)) { + player->sendCancelMessage(RETURNVALUE_NOTPOSSIBLE); + g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); + return false; + } + + Spell::postCastSpell(player, manaCost, spell->getSoulCost()); + g_game.addMagicEffect(player->getPosition(), CONST_ME_MAGIC_RED); return true; } @@ -1218,10 +1825,12 @@ bool RuneSpell::executeUse(Player* player, Item* item, const Position&, Thing* t var.number = visibleCreature->getID(); } } - } else { + } + else { var.number = target->getCreature()->getID(); } - } else { + } + else { var.type = VARIANT_POSITION; var.pos = toPosition; } @@ -1259,7 +1868,8 @@ bool RuneSpell::internalCastSpell(Creature* creature, const LuaVariant& var, boo bool result; if (scripted) { result = executeCastSpell(creature, var, isHotkey); - } else { + } + else { result = false; } return result; diff --git a/src/spells.h b/src/spells.h index d59dd99..d0907fe 100644 --- a/src/spells.h +++ b/src/spells.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,12 +27,11 @@ #include "baseevents.h" class InstantSpell; +class ConjureSpell; class RuneSpell; class Spell; -using VocSpellMap = std::map; -using InstantSpell_ptr = std::unique_ptr; -using RuneSpell_ptr = std::unique_ptr; +typedef std::map VocSpellMap; class Spells final : public BaseEvents { @@ -51,35 +50,29 @@ class Spells final : public BaseEvents InstantSpell* getInstantSpell(const std::string& words); InstantSpell* getInstantSpellByName(const std::string& name); - InstantSpell* getInstantSpellById(uint32_t spellId); + uint32_t getInstantSpellCount(const Player* player) const; + InstantSpell* getInstantSpellByIndex(const Player* player, uint32_t index); TalkActionResult_t playerSaySpell(Player* player, std::string& words); static Position getCasterPosition(Creature* creature, Direction dir); std::string getScriptBaseName() const override; - const std::map& getInstantSpells() const { - return instants; - }; + protected: + void clear() final; + LuaScriptInterface& getScriptInterface() final; + Event* getEvent(const std::string& nodeName) final; + bool registerEvent(Event* event, const pugi::xml_node& node) final; - void clearMaps(bool fromLua); - void clear(bool fromLua) override final; - bool registerInstantLuaEvent(InstantSpell* event); - bool registerRuneLuaEvent(RuneSpell* event); - - private: - LuaScriptInterface& getScriptInterface() override; - Event_ptr getEvent(const std::string& nodeName) override; - bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; - - std::map runes; - std::map instants; + std::map runes; + std::map instants; friend class CombatSpell; LuaScriptInterface scriptInterface { "Spell Interface" }; }; -using RuneSpellFunction = std::function; +typedef bool (InstantSpellFunction)(const InstantSpell* spell, Creature* creature, const std::string& param); +typedef bool (RuneSpellFunction)(const RuneSpell* spell, Player* player, const Position& posTo); class BaseSpell { @@ -115,7 +108,7 @@ class CombatSpell final : public Event, public BaseSpell return combat; } - private: + protected: std::string getScriptEventName() const override { return "onCastSpell"; } @@ -135,191 +128,58 @@ class Spell : public BaseSpell const std::string& getName() const { return name; } - void setName(std::string n) { - name = n; - } - uint8_t getId() const { - return spellId; - } - void setId(uint8_t id) { - spellId = id; - } - void postCastSpell(Player* player, bool finishedCast = true, bool payCost = true) const; + void postCastSpell(Player* player, bool finishedSpell = true, bool payCost = true) const; static void postCastSpell(Player* player, uint32_t manaCost, uint32_t soulCost); uint32_t getManaCost(const Player* player) const; uint32_t getSoulCost() const { return soul; } - void setSoulCost(uint32_t s) { - soul = s; - } uint32_t getLevel() const { return level; } - void setLevel(uint32_t lvl) { - level = lvl; - } uint32_t getMagicLevel() const { return magLevel; } - void setMagicLevel(uint32_t lvl) { - magLevel = lvl; - } - uint32_t getMana() const { - return mana; - } - void setMana(uint32_t m) { - mana = m; - } uint32_t getManaPercent() const { return manaPercent; } - void setManaPercent(uint32_t m) { - manaPercent = m; - } bool isPremium() const { return premium; } - void setPremium(bool p) { - premium = p; - } - bool isEnabled() const { - return enabled; - } - void setEnabled(bool e) { - enabled = e; - } virtual bool isInstant() const = 0; bool isLearnable() const { return learnable; } - void setLearnable(bool l) { - learnable = l; - } + + static ReturnValue CreateIllusion(Creature* creature, const Outfit_t& outfit, int32_t time); + static ReturnValue CreateIllusion(Creature* creature, const std::string& name, int32_t time); + static ReturnValue CreateIllusion(Creature* creature, uint32_t itemId, int32_t time); const VocSpellMap& getVocMap() const { return vocSpellMap; } - void addVocMap(uint16_t n, bool b) { - vocSpellMap[n] = b; - } - - const SpellGroup_t getGroup() const { - return group; - } - void setGroup(SpellGroup_t g) { - group = g; - } - const SpellGroup_t getSecondaryGroup() const { - return secondaryGroup; - } - void setSecondaryGroup(SpellGroup_t g) { - secondaryGroup = g; - } - - uint32_t getCooldown() const { - return cooldown; - } - void setCooldown(uint32_t cd) { - cooldown = cd; - } - uint32_t getSecondaryCooldown() const { - return secondaryGroupCooldown; - } - void setSecondaryCooldown(uint32_t cd) { - secondaryGroupCooldown = cd; - } - uint32_t getGroupCooldown() const { - return groupCooldown; - } - void setGroupCooldown(uint32_t cd) { - groupCooldown = cd; - } - - int32_t getRange() const { - return range; - } - void setRange(int32_t r) { - range = r; - } - - bool getNeedTarget() const { - return needTarget; - } - void setNeedTarget(bool n) { - needTarget = n; - } - bool getNeedWeapon() const { - return needWeapon; - } - void setNeedWeapon(bool n) { - needWeapon = n; - } - bool getNeedLearn() const { - return learnable; - } - void setNeedLearn(bool n) { - learnable = n; - } - bool getSelfTarget() const { - return selfTarget; - } - void setSelfTarget(bool s) { - selfTarget = s; - } - bool getBlockingSolid() const { - return blockingSolid; - } - void setBlockingSolid(bool b) { - blockingSolid = b; - } - bool getBlockingCreature() const { - return blockingCreature; - } - void setBlockingCreature(bool b) { - blockingCreature = b; - } - bool getAggressive() const { - return aggressive; - } - void setAggressive(bool a) { - aggressive = a; - } - - SpellType_t spellType = SPELL_UNDEFINED; protected: bool playerSpellCheck(Player* player) const; bool playerInstantSpellCheck(Player* player, const Position& toPos); bool playerRuneSpellCheck(Player* player, const Position& toPos); - VocSpellMap vocSpellMap; - - SpellGroup_t group = SPELLGROUP_NONE; - SpellGroup_t secondaryGroup = SPELLGROUP_NONE; - - uint32_t cooldown = 1000; - uint32_t groupCooldown = 1000; - uint32_t secondaryGroupCooldown = 0; - uint32_t level = 0; - uint32_t magLevel = 0; - int32_t range = -1; - uint8_t spellId = 0; - bool selfTarget = false; - bool needTarget = false; - - private: - uint32_t mana = 0; uint32_t manaPercent = 0; uint32_t soul = 0; + uint32_t cooldown = 0; + uint32_t level = 0; + uint32_t magLevel = 0; + int32_t range = -1; + bool needTarget = false; bool needWeapon = false; + bool selfTarget = false; bool blockingSolid = false; bool blockingCreature = false; bool aggressive = true; @@ -327,17 +187,19 @@ class Spell : public BaseSpell bool enabled = true; bool premium = false; + VocSpellMap vocSpellMap; private: std::string name; }; -class InstantSpell final : public TalkAction, public Spell +class InstantSpell : public TalkAction, public Spell { public: explicit InstantSpell(LuaScriptInterface* interface) : TalkAction(interface) {} bool configureEvent(const pugi::xml_node& node) override; + bool loadFunction(const pugi::xml_attribute& attr) override; virtual bool playerCastInstant(Player* player, std::string& param); @@ -353,41 +215,30 @@ class InstantSpell final : public TalkAction, public Spell bool getHasParam() const { return hasParam; } - void setHasParam(bool p) { - hasParam = p; - } bool getHasPlayerNameParam() const { return hasPlayerNameParam; } - void setHasPlayerNameParam(bool p) { - hasPlayerNameParam = p; - } - bool getNeedDirection() const { - return needDirection; - } - void setNeedDirection(bool n) { - needDirection = n; - } - bool getNeedCasterTargetOrDirection() const { - return casterTargetOrDirection; - } - void setNeedCasterTargetOrDirection(bool d) { - casterTargetOrDirection = d; - } - bool getBlockWalls() const { - return checkLineOfSight; - } - void setBlockWalls(bool w) { - checkLineOfSight = w; - } bool canCast(const Player* player) const; bool canThrowSpell(const Creature* creature, const Creature* target) const; - private: + protected: std::string getScriptEventName() const override; + static InstantSpellFunction HouseGuestList; + static InstantSpellFunction HouseSubOwnerList; + static InstantSpellFunction HouseDoorList; + static InstantSpellFunction HouseKick; + static InstantSpellFunction SearchPlayer; + static InstantSpellFunction SummonMonster; + static InstantSpellFunction Levitate; + static InstantSpellFunction Illusion; + + static House* getHouseFromPos(Creature* creature); + bool internalCastSpell(Creature* creature, const LuaVariant& var); + InstantSpellFunction* function = nullptr; + bool needDirection = false; bool hasParam = false; bool hasPlayerNameParam = false; @@ -395,56 +246,77 @@ class InstantSpell final : public TalkAction, public Spell bool casterTargetOrDirection = false; }; +class ConjureSpell final : public InstantSpell +{ + public: + explicit ConjureSpell(LuaScriptInterface* interface) : InstantSpell(interface) { + aggressive = false; + } + + bool configureEvent(const pugi::xml_node& node) final; + bool loadFunction(const pugi::xml_attribute& attr) final; + + bool playerCastInstant(Player* player, std::string& param) final; + + bool castSpell(Creature*) final { + return false; + } + bool castSpell(Creature*, Creature*) final { + return false; + } + + protected: + std::string getScriptEventName() const final; + + bool conjureItem(Creature* creature) const; + + uint32_t conjureId = 0; + uint32_t conjureCount = 1; + uint32_t reagentId = 0; +}; + class RuneSpell final : public Action, public Spell { public: explicit RuneSpell(LuaScriptInterface* interface) : Action(interface) {} - bool configureEvent(const pugi::xml_node& node) override; + bool configureEvent(const pugi::xml_node& node) final; + bool loadFunction(const pugi::xml_attribute& attr) final; - ReturnValue canExecuteAction(const Player* player, const Position& toPos) override; - bool hasOwnErrorHandler() override { + ReturnValue canExecuteAction(const Player* player, const Position& toPos) final; + bool hasOwnErrorHandler() final { return true; } - Thing* getTarget(Player*, Creature* targetCreature, const Position&, uint8_t) const override { + Thing* getTarget(Player*, Creature* targetCreature, const Position&, uint8_t) const final { return targetCreature; } bool executeUse(Player* player, Item* item, const Position& fromPosition, Thing* target, const Position& toPosition, bool isHotkey) override; - bool castSpell(Creature* creature) override; - bool castSpell(Creature* creature, Creature* target) override; + bool castSpell(Creature* creature) final; + bool castSpell(Creature* creature, Creature* target) final; //scripting bool executeCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey); - bool isInstant() const override { + bool isInstant() const final { return false; } uint16_t getRuneItemId() const { return runeId; } - void setRuneItemId(uint16_t i) { - runeId = i; - } - uint32_t getCharges() const { - return charges; - } - void setCharges(uint32_t c) { - if (c > 0) { - hasCharges = true; - } - charges = c; - } - private: - std::string getScriptEventName() const override; + protected: + std::string getScriptEventName() const final; + + static RuneSpellFunction Illusion; + static RuneSpellFunction Convince; bool internalCastSpell(Creature* creature, const LuaVariant& var, bool isHotkey); + RuneSpellFunction* runeFunction = nullptr; uint16_t runeId = 0; - uint32_t charges = 0; - bool hasCharges = false; + bool hasCharges = true; }; #endif diff --git a/src/talkaction.cpp b/src/talkaction.cpp index 2a30410..8e2a0b7 100644 --- a/src/talkaction.cpp +++ b/src/talkaction.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,20 +31,14 @@ TalkActions::TalkActions() TalkActions::~TalkActions() { - clear(false); + clear(); } -void TalkActions::clear(bool fromLua) +void TalkActions::clear() { - for (auto it = talkActions.begin(); it != talkActions.end(); ) { - if (fromLua == it->second.fromLua) { - it = talkActions.erase(it); - } else { - ++it; - } - } + talkActions.clear(); - reInitState(fromLua); + scriptInterface.reInitState(); } LuaScriptInterface& TalkActions::getScriptInterface() @@ -57,36 +51,29 @@ std::string TalkActions::getScriptBaseName() const return "talkactions"; } -Event_ptr TalkActions::getEvent(const std::string& nodeName) +Event* TalkActions::getEvent(const std::string& nodeName) { if (strcasecmp(nodeName.c_str(), "talkaction") != 0) { return nullptr; } - return Event_ptr(new TalkAction(&scriptInterface)); + return new TalkAction(&scriptInterface); } -bool TalkActions::registerEvent(Event_ptr event, const pugi::xml_node&) +bool TalkActions::registerEvent(Event* event, const pugi::xml_node&) { - TalkAction_ptr talkAction{static_cast(event.release())}; // event is guaranteed to be a TalkAction - talkActions.emplace(talkAction->getWords(), std::move(*talkAction)); - return true; -} + auto talkAction = std::unique_ptr(static_cast(event)); // event is guaranteed to be a TalkAction + talkActions.push_front(std::move(*talkAction)); -bool TalkActions::registerLuaEvent(TalkAction* event) -{ - TalkAction_ptr talkAction{ event }; - talkActions.emplace(talkAction->getWords(), std::move(*talkAction)); return true; } TalkActionResult_t TalkActions::playerSaySpell(Player* player, SpeakClasses type, const std::string& words) const { size_t wordsLength = words.length(); - for (auto it = talkActions.begin(); it != talkActions.end(); ) { - const std::string& talkactionWords = it->first; + for (const TalkAction& talkAction : talkActions) { + const std::string& talkactionWords = talkAction.getWords(); size_t talkactionLength = talkactionWords.length(); if (wordsLength < talkactionLength || strncasecmp(words.c_str(), talkactionWords.c_str(), talkactionLength) != 0) { - ++it; continue; } @@ -94,16 +81,14 @@ TalkActionResult_t TalkActions::playerSaySpell(Player* player, SpeakClasses type if (wordsLength != talkactionLength) { param = words.substr(talkactionLength); if (param.front() != ' ') { - ++it; continue; } trim_left(param, ' '); - std::string separator = it->second.getSeparator(); - if (separator != " ") { + char separator = talkAction.getSeparator(); + if (separator != ' ') { if (!param.empty()) { - if (param != separator) { - ++it; + if (param.front() != separator) { continue; } else { param.erase(param.begin()); @@ -112,7 +97,7 @@ TalkActionResult_t TalkActions::playerSaySpell(Player* player, SpeakClasses type } } - if (it->second.executeSay(player, param, type)) { + if (talkAction.executeSay(player, param, type)) { return TALKACTION_CONTINUE; } else { return TALKACTION_BREAK; diff --git a/src/talkaction.h b/src/talkaction.h index 686c38b..765f336 100644 --- a/src/talkaction.h +++ b/src/talkaction.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,15 +24,38 @@ #include "baseevents.h" #include "const.h" -class TalkAction; -using TalkAction_ptr = std::unique_ptr; - enum TalkActionResult_t { TALKACTION_CONTINUE, TALKACTION_BREAK, TALKACTION_FAILED, }; +class TalkAction; + +class TalkActions : public BaseEvents +{ + public: + TalkActions(); + ~TalkActions(); + + // non-copyable + TalkActions(const TalkActions&) = delete; + TalkActions& operator=(const TalkActions&) = delete; + + TalkActionResult_t playerSaySpell(Player* player, SpeakClasses type, const std::string& words) const; + + protected: + LuaScriptInterface& getScriptInterface() final; + std::string getScriptBaseName() const final; + Event* getEvent(const std::string& nodeName) final; + bool registerEvent(Event* event, const pugi::xml_node& node) final; + void clear() final; + + std::forward_list talkActions; + + LuaScriptInterface scriptInterface; +}; + class TalkAction : public Event { public: @@ -43,51 +66,19 @@ class TalkAction : public Event const std::string& getWords() const { return words; } - void setWords(std::string word) { - words = word; - } - std::string getSeparator() const { + char getSeparator() const { return separator; } - void setSeparator(std::string sep) { - separator = sep; - } //scripting bool executeSay(Player* player, const std::string& param, SpeakClasses type) const; // - private: + protected: std::string getScriptEventName() const override; std::string words; - std::string separator = "\""; -}; - -class TalkActions final : public BaseEvents -{ - public: - TalkActions(); - ~TalkActions(); - - // non-copyable - TalkActions(const TalkActions&) = delete; - TalkActions& operator=(const TalkActions&) = delete; - - TalkActionResult_t playerSaySpell(Player* player, SpeakClasses type, const std::string& words) const; - - bool registerLuaEvent(TalkAction* event); - void clear(bool fromLua) override final; - - private: - LuaScriptInterface& getScriptInterface() override; - std::string getScriptBaseName() const override; - Event_ptr getEvent(const std::string& nodeName) override; - bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; - - std::map talkActions; - - LuaScriptInterface scriptInterface; + char separator = '"'; }; #endif diff --git a/src/tasks.cpp b/src/tasks.cpp index 58a82a0..1ed727f 100644 --- a/src/tasks.cpp +++ b/src/tasks.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,16 +24,6 @@ extern Game g_game; -Task* createTask(std::function f) -{ - return new Task(std::move(f)); -} - -Task* createTask(uint32_t expiration, std::function f) -{ - return new Task(expiration, std::move(f)); -} - void Dispatcher::threadMain() { // NOTE: second argument defer_lock is to prevent from immediate locking @@ -58,6 +48,8 @@ void Dispatcher::threadMain() ++dispatcherCycle; // execute it (*task)(); + + g_game.map.clearSpectatorCache(); } delete task; } else { diff --git a/src/tasks.h b/src/tasks.h index 6f6af57..4ea459c 100644 --- a/src/tasks.h +++ b/src/tasks.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -31,8 +31,8 @@ class Task { public: // DO NOT allocate this class on the stack - explicit Task(std::function&& f) : func(std::move(f)) {} - Task(uint32_t ms, std::function&& f) : + explicit Task(std::function f) : func(std::move(f)) {} + Task(uint32_t ms, std::function f) : expiration(std::chrono::system_clock::now() + std::chrono::milliseconds(ms)), func(std::move(f)) {} virtual ~Task() = default; @@ -52,17 +52,22 @@ class Task } protected: - std::chrono::system_clock::time_point expiration = SYSTEM_TIME_ZERO; - - private: // Expiration has another meaning for scheduler tasks, // then it is the time the task should be added to the // dispatcher + std::chrono::system_clock::time_point expiration = SYSTEM_TIME_ZERO; std::function func; }; -Task* createTask(std::function f); -Task* createTask(uint32_t expiration, std::function f); +inline Task* createTask(const std::function& f) +{ + return new Task(f); +} + +inline Task* createTask(uint32_t expiration, const std::function& f) +{ + return new Task(expiration, f); +} class Dispatcher : public ThreadHolder { public: @@ -76,7 +81,7 @@ class Dispatcher : public ThreadHolder { void threadMain(); - private: + protected: std::thread thread; std::mutex taskLock; std::condition_variable taskSignal; diff --git a/src/teleport.cpp b/src/teleport.cpp index e0dddfd..562fd0b 100644 --- a/src/teleport.cpp +++ b/src/teleport.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/teleport.h b/src/teleport.h index f79579c..410b2ab 100644 --- a/src/teleport.h +++ b/src/teleport.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,16 +27,16 @@ class Teleport final : public Item, public Cylinder public: explicit Teleport(uint16_t type) : Item(type) {}; - Teleport* getTeleport() override { + Teleport* getTeleport() final { return this; } - const Teleport* getTeleport() const override { + const Teleport* getTeleport() const final { return this; } //serialization - Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; - void serializeAttr(PropWriteStream& propWriteStream) const override; + Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final; + void serializeAttr(PropWriteStream& propWriteStream) const final; const Position& getDestPos() const { return destPos; @@ -47,23 +47,23 @@ class Teleport final : public Item, public Cylinder //cylinder implementations ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, - uint32_t flags, Creature* actor = nullptr) const override; + uint32_t flags, Creature* actor = nullptr) const final; ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, - uint32_t& maxQueryCount, uint32_t flags) const override; - ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override; + uint32_t& maxQueryCount, uint32_t flags) const final; + ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, - uint32_t& flags) override; + uint32_t& flags) final; - void addThing(Thing* thing) override; - void addThing(int32_t index, Thing* thing) override; + void addThing(Thing* thing) final; + void addThing(int32_t index, Thing* thing) final; - void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override; - void replaceThing(uint32_t index, Thing* thing) override; + void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; + void replaceThing(uint32_t index, Thing* thing) final; - void removeThing(Thing* thing, uint32_t count) override; + void removeThing(Thing* thing, uint32_t count) final; - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; + void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; + void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; private: Position destPos; diff --git a/src/thing.cpp b/src/thing.cpp index abda31f..21e72f6 100644 --- a/src/thing.cpp +++ b/src/thing.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/thing.h b/src/thing.h index 3fee9d2..b23be2b 100644 --- a/src/thing.h +++ b/src/thing.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,10 +30,11 @@ class Container; class Thing { - public: + protected: constexpr Thing() = default; - virtual ~Thing() = default; + ~Thing() = default; + public: // non-copyable Thing(const Thing&) = delete; Thing& operator=(const Thing&) = delete; diff --git a/src/thread_holder_base.h b/src/thread_holder_base.h index 1ab206b..91280db 100644 --- a/src/thread_holder_base.h +++ b/src/thread_holder_base.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/tile.cpp b/src/tile.cpp index d5347dc..6240678 100644 --- a/src/tile.cpp +++ b/src/tile.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,7 +30,6 @@ #include "monster.h" #include "movement.h" #include "teleport.h" -#include "trashholder.h" extern Game g_game; extern MoveEvents* g_moveEvents; @@ -101,6 +100,25 @@ bool Tile::hasHeight(uint32_t n) const return false; } +int32_t Tile::getHeight() { + int32_t height = 0; + if (ground) { + if (ground->hasProperty(CONST_PROP_HASHEIGHT)) { + ++height; + } + } + + if (const TileItemVector* items = getItemList()) { + for (ItemVector::const_iterator it = items->begin(); it != items->end(); ++it) { + if ((*it)->hasProperty(CONST_PROP_HASHEIGHT)) { + ++height; + } + } + } + + return std::min(height, 4); +} + size_t Tile::getCreatureCount() const { if (const CreatureVector* creatures = getCreatures()) { @@ -174,26 +192,6 @@ MagicField* Tile::getFieldItem() const return nullptr; } -TrashHolder* Tile::getTrashHolder() const -{ - if (!hasFlag(TILESTATE_TRASHHOLDER)) { - return nullptr; - } - - if (ground && ground->getTrashHolder()) { - return ground->getTrashHolder(); - } - - if (const TileItemVector* items = getItemList()) { - for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) { - if ((*it)->getTrashHolder()) { - return (*it)->getTrashHolder(); - } - } - } - return nullptr; -} - Mailbox* Tile::getMailbox() const { if (!hasFlag(TILESTATE_MAILBOX)) { @@ -214,6 +212,27 @@ Mailbox* Tile::getMailbox() const return nullptr; } +DepotLocker* Tile::getDepotLocker() const +{ + if (!hasFlag(TILESTATE_DEPOT)) { + return nullptr; + } + + if (ground && ground->getDepotLocker()) { + return ground->getDepotLocker(); + } + + if (const TileItemVector* items = getItemList()) { + for (auto it = items->rbegin(), end = items->rend(); it != end; ++it) { + if ((*it)->getDepotLocker()) { + return (*it)->getDepotLocker(); + } + } + } + return nullptr; +} + + BedItem* Tile::getBedItem() const { if (!hasFlag(TILESTATE_BED)) { @@ -353,17 +372,11 @@ Thing* Tile::getTopVisibleThing(const Creature* creature) TileItemVector* items = getItemList(); if (items) { for (ItemVector::const_iterator it = items->getBeginDownItem(), end = items->getEndDownItem(); it != end; ++it) { - const ItemType& iit = Item::items[(*it)->getID()]; - if (!iit.lookThrough) { - return (*it); - } + return (*it); } for (auto it = ItemVector::const_reverse_iterator(items->getEndTopItem()), end = ItemVector::const_reverse_iterator(items->getBeginTopItem()); it != end; ++it) { - const ItemType& iit = Item::items[(*it)->getID()]; - if (!iit.lookThrough) { - return (*it); - } + return (*it); } } @@ -372,81 +385,48 @@ Thing* Tile::getTopVisibleThing(const Creature* creature) void Tile::onAddTileItem(Item* item) { - if (item->hasProperty(CONST_PROP_MOVEABLE) || item->getContainer()) { - auto it = g_game.browseFields.find(this); - if (it != g_game.browseFields.end()) { - it->second->addItemBack(item); - item->setParent(this); - } - } - setTileFlags(item); const Position& cylinderMapPos = getPosition(); - SpectatorVec spectators; - g_game.map.getSpectators(spectators, cylinderMapPos, true); + SpectatorVec list; + g_game.map.getSpectators(list, cylinderMapPos, true); //send to client - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { tmpPlayer->sendAddTileItem(this, cylinderMapPos, item); } } //event methods - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onAddTileItem(this, cylinderMapPos); } } void Tile::onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType) { - if (newItem->hasProperty(CONST_PROP_MOVEABLE) || newItem->getContainer()) { - auto it = g_game.browseFields.find(this); - if (it != g_game.browseFields.end()) { - int32_t index = it->second->getThingIndex(oldItem); - if (index != -1) { - it->second->replaceThing(index, newItem); - newItem->setParent(this); - } - } - } else if (oldItem->hasProperty(CONST_PROP_MOVEABLE) || oldItem->getContainer()) { - auto it = g_game.browseFields.find(this); - if (it != g_game.browseFields.end()) { - Cylinder* oldParent = oldItem->getParent(); - it->second->removeThing(oldItem, oldItem->getItemCount()); - oldItem->setParent(oldParent); - } - } - const Position& cylinderMapPos = getPosition(); - SpectatorVec spectators; - g_game.map.getSpectators(spectators, cylinderMapPos, true); + SpectatorVec list; + g_game.map.getSpectators(list, cylinderMapPos, true); //send to client - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { tmpPlayer->sendUpdateTileItem(this, cylinderMapPos, newItem); } } //event methods - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onUpdateTileItem(this, cylinderMapPos, oldItem, oldType, newItem, newType); } } -void Tile::onRemoveTileItem(const SpectatorVec& spectators, const std::vector& oldStackPosVector, Item* item) +void Tile::onRemoveTileItem(const SpectatorVec& list, const std::vector& oldStackPosVector, Item* item) { - if (item->hasProperty(CONST_PROP_MOVEABLE) || item->getContainer()) { - auto it = g_game.browseFields.find(this); - if (it != g_game.browseFields.end()) { - it->second->removeThing(item, item->getItemCount()); - } - } - resetTileFlags(item); const Position& cylinderMapPos = getPosition(); @@ -454,24 +434,24 @@ void Tile::onRemoveTileItem(const SpectatorVec& spectators, const std::vectorgetPlayer()) { tmpPlayer->sendRemoveTileThing(cylinderMapPos, oldStackPosVector[i++]); } } //event methods - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->onRemoveTileItem(this, cylinderMapPos, iType, item); } } -void Tile::onUpdateTile(const SpectatorVec& spectators) +void Tile::onUpdateTile(const SpectatorVec& list) { const Position& cylinderMapPos = getPosition(); //send to clients - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->sendUpdateTile(this, cylinderMapPos); } } @@ -483,7 +463,7 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags return RETURNVALUE_NOERROR; } - if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_FLOORCHANGE | TILESTATE_TELEPORT)) { + if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_IMMOVABLENOFIELDBLOCKPATH)) { return RETURNVALUE_NOTPOSSIBLE; } @@ -492,7 +472,15 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags } if (const Monster* monster = creature->getMonster()) { - if (hasFlag(TILESTATE_PROTECTIONZONE | TILESTATE_FLOORCHANGE | TILESTATE_TELEPORT)) { + if (hasFlag(TILESTATE_PROTECTIONZONE | TILESTATE_TELEPORT)) { + return RETURNVALUE_NOTPOSSIBLE; + } + + if (hasFlag(TILESTATE_IMMOVABLEBLOCKPATH | TILESTATE_IMMOVABLENOFIELDBLOCKPATH)) { + return RETURNVALUE_NOTPOSSIBLE; + } + + if (hasBitSet(FLAG_PLACECHECK, flags) && hasFlag(TILESTATE_BLOCKSOLID)) { return RETURNVALUE_NOTPOSSIBLE; } @@ -506,7 +494,7 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags const Monster* creatureMonster = tileCreature->getMonster(); if (!creatureMonster || !tileCreature->isPushable() || - (creatureMonster->isSummon() && creatureMonster->getMaster()->getPlayer())) { + (creatureMonster->isSummon() && creatureMonster->getMaster()->getPlayer())) { return RETURNVALUE_NOTPOSSIBLE; } } @@ -533,23 +521,17 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags } } - MagicField* field = getFieldItem(); - if (!field || field->isBlocking() || field->getDamage() == 0) { - return RETURNVALUE_NOERROR; - } + if (!monster->hasCondition(CONDITION_AGGRESSIVE) && + !hasBitSet(FLAG_IGNOREFIELDDAMAGE, flags)) { + if (hasFlag(TILESTATE_FIREDAMAGE) && !monster->isImmune(COMBAT_FIREDAMAGE)) { + return RETURNVALUE_NOTPOSSIBLE; + } - CombatType_t combatType = field->getCombatType(); + if (hasFlag(TILESTATE_POISONDAMAGE) && !monster->isImmune(COMBAT_EARTHDAMAGE)) { + return RETURNVALUE_NOTPOSSIBLE; + } - //There is 3 options for a monster to enter a magic field - //1) Monster is immune - if (!monster->isImmune(combatType)) { - //1) Monster is able to walk over field type - //2) Being attacked while random stepping will make it ignore field damages - if (hasBitSet(FLAG_IGNOREFIELDDAMAGE, flags)) { - if (!(monster->canWalkOnFieldType(combatType) || monster->isIgnoringFieldDamage())) { - return RETURNVALUE_NOTPOSSIBLE; - } - } else { + if (hasFlag(TILESTATE_ENERGYDAMAGE) && !monster->isImmune(COMBAT_ENERGYDAMAGE)) { return RETURNVALUE_NOTPOSSIBLE; } } @@ -560,11 +542,11 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags const CreatureVector* creatures = getCreatures(); if (const Player* player = creature->getPlayer()) { if (creatures && !creatures->empty() && !hasBitSet(FLAG_IGNOREBLOCKCREATURE, flags) && !player->isAccessPlayer()) { - for (const Creature* tileCreature : *creatures) { - if (!player->canWalkthrough(tileCreature)) { - return RETURNVALUE_NOTPOSSIBLE; - } - } + return RETURNVALUE_NOTPOSSIBLE; + } + + if (hasBitSet(FLAG_PATHFINDING, flags) && hasFlag(TILESTATE_BLOCKPATH)) { + return RETURNVALUE_NOTPOSSIBLE; } if (player->getParent() == nullptr && hasFlag(TILESTATE_NOLOGOUT)) { @@ -607,7 +589,7 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags //FLAG_IGNOREBLOCKITEM is set if (ground) { const ItemType& iiType = Item::items[ground->getID()]; - if (iiType.blockSolid && (!iiType.moveable || ground->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID))) { + if (iiType.blockSolid) { return RETURNVALUE_NOTPOSSIBLE; } } @@ -615,7 +597,7 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags if (const auto items = getItemList()) { for (const Item* item : *items) { const ItemType& iiType = Item::items[item->getID()]; - if (iiType.blockSolid && (!iiType.moveable || item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID))) { + if (iiType.blockSolid && !iiType.moveable) { return RETURNVALUE_NOTPOSSIBLE; } } @@ -645,6 +627,10 @@ ReturnValue Tile::queryAdd(int32_t, const Thing& thing, uint32_t, uint32_t flags } } + if (item->isMagicField() && hasProperty(CONST_PROP_IMMOVABLENOFIELDBLOCKPATH)) { + return RETURNVALUE_NOTENOUGHROOM; + } + if (itemIsHangable && hasFlag(TILESTATE_SUPPORTS_HANGABLE)) { if (items) { for (const Item* tileItem : *items) { @@ -723,101 +709,14 @@ ReturnValue Tile::queryRemove(const Thing& thing, uint32_t count, uint32_t flags return RETURNVALUE_NOERROR; } -Tile* Tile::queryDestination(int32_t&, const Thing&, Item** destItem, uint32_t& flags) +Tile* Tile::queryDestination(int32_t&, const Thing&, Item** destItem, uint32_t&) { - Tile* destTile = nullptr; - *destItem = nullptr; - - if (hasFlag(TILESTATE_FLOORCHANGE_DOWN)) { - uint16_t dx = tilePos.x; - uint16_t dy = tilePos.y; - uint8_t dz = tilePos.z + 1; - - Tile* southDownTile = g_game.map.getTile(dx, dy - 1, dz); - if (southDownTile && southDownTile->hasFlag(TILESTATE_FLOORCHANGE_SOUTH_ALT)) { - dy -= 2; - destTile = g_game.map.getTile(dx, dy, dz); - } else { - Tile* eastDownTile = g_game.map.getTile(dx - 1, dy, dz); - if (eastDownTile && eastDownTile->hasFlag(TILESTATE_FLOORCHANGE_EAST_ALT)) { - dx -= 2; - destTile = g_game.map.getTile(dx, dy, dz); - } else { - Tile* downTile = g_game.map.getTile(dx, dy, dz); - if (downTile) { - if (downTile->hasFlag(TILESTATE_FLOORCHANGE_NORTH)) { - ++dy; - } - - if (downTile->hasFlag(TILESTATE_FLOORCHANGE_SOUTH)) { - --dy; - } - - if (downTile->hasFlag(TILESTATE_FLOORCHANGE_SOUTH_ALT)) { - dy -= 2; - } - - if (downTile->hasFlag(TILESTATE_FLOORCHANGE_EAST)) { - --dx; - } - - if (downTile->hasFlag(TILESTATE_FLOORCHANGE_EAST_ALT)) { - dx -= 2; - } - - if (downTile->hasFlag(TILESTATE_FLOORCHANGE_WEST)) { - ++dx; - } - - destTile = g_game.map.getTile(dx, dy, dz); - } - } - } - } else if (hasFlag(TILESTATE_FLOORCHANGE)) { - uint16_t dx = tilePos.x; - uint16_t dy = tilePos.y; - uint8_t dz = tilePos.z - 1; - - if (hasFlag(TILESTATE_FLOORCHANGE_NORTH)) { - --dy; - } - - if (hasFlag(TILESTATE_FLOORCHANGE_SOUTH)) { - ++dy; - } - - if (hasFlag(TILESTATE_FLOORCHANGE_EAST)) { - ++dx; - } - - if (hasFlag(TILESTATE_FLOORCHANGE_WEST)) { - --dx; - } - - if (hasFlag(TILESTATE_FLOORCHANGE_SOUTH_ALT)) { - dy += 2; - } - - if (hasFlag(TILESTATE_FLOORCHANGE_EAST_ALT)) { - dx += 2; - } - - destTile = g_game.map.getTile(dx, dy, dz); + Thing* destThing = getTopDownItem(); + if (destThing) { + *destItem = destThing->getItem(); } - if (destTile == nullptr) { - destTile = this; - } else { - flags |= FLAG_NOLIMIT; //Will ignore that there is blocking items/creatures - } - - if (destTile) { - Thing* destThing = destTile->getTopDownItem(); - if (destThing) { - *destItem = destThing->getItem(); - } - } - return destTile; + return this; } void Tile::addThing(Thing* thing) @@ -830,13 +729,9 @@ void Tile::addThing(int32_t, Thing* thing) Creature* creature = thing->getCreature(); if (creature) { g_game.map.clearSpectatorCache(); - if (creature->getPlayer()) { - g_game.map.clearPlayersSpectatorCache(); - } - creature->setParent(this); CreatureVector* creatures = makeCreatures(); - creatures->insert(creatures->begin(), creature); + creatures->insert(creatures->end(), creature); } else { Item* item = thing->getItem(); if (item == nullptr) { @@ -1039,10 +934,6 @@ void Tile::removeThing(Thing* thing, uint32_t count) auto it = std::find(creatures->begin(), creatures->end(), thing); if (it != creatures->end()) { g_game.map.clearSpectatorCache(); - if (creature->getPlayer()) { - g_game.map.clearPlayersSpectatorCache(); - } - creatures->erase(it); } } @@ -1063,9 +954,9 @@ void Tile::removeThing(Thing* thing, uint32_t count) ground->setParent(nullptr); ground = nullptr; - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), true); - onRemoveTileItem(spectators, std::vector(spectators.size(), 0), item); + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), true); + onRemoveTileItem(list, std::vector(list.size(), 0), item); return; } @@ -1083,9 +974,9 @@ void Tile::removeThing(Thing* thing, uint32_t count) std::vector oldStackPosVector; - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), true); - for (Creature* spectator : spectators) { + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), true); + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { oldStackPosVector.push_back(getStackposOfItem(tmpPlayer, item)); } @@ -1093,7 +984,7 @@ void Tile::removeThing(Thing* thing, uint32_t count) item->setParent(nullptr); items->erase(it); - onRemoveTileItem(spectators, oldStackPosVector, item); + onRemoveTileItem(list, oldStackPosVector, item); } else { auto it = std::find(items->getBeginDownItem(), items->getEndDownItem(), item); if (it == items->getEndDownItem()) { @@ -1107,9 +998,9 @@ void Tile::removeThing(Thing* thing, uint32_t count) } else { std::vector oldStackPosVector; - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), true); - for (Creature* spectator : spectators) { + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), true); + for (Creature* spectator : list) { if (Player* tmpPlayer = spectator->getPlayer()) { oldStackPosVector.push_back(getStackposOfItem(tmpPlayer, item)); } @@ -1118,7 +1009,7 @@ void Tile::removeThing(Thing* thing, uint32_t count) item->setParent(nullptr); items->erase(it); items->addDownItemCount(-1); - onRemoveTileItem(spectators, oldStackPosVector, item); + onRemoveTileItem(list, oldStackPosVector, item); } } } @@ -1350,9 +1241,9 @@ Thing* Tile::getThing(size_t index) const void Tile::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), true, true); - for (Creature* spectator : spectators) { + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), true, true); + for (Creature* spectator : list) { spectator->getPlayer()->postAddNotification(thing, oldParent, index, LINK_NEAR); } @@ -1375,11 +1266,6 @@ void Tile::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t if (teleport) { teleport->addThing(thing); } - } else if (hasFlag(TILESTATE_TRASHHOLDER)) { - TrashHolder* trashholder = getTrashHolder(); - if (trashholder) { - trashholder->addThing(thing); - } } else if (hasFlag(TILESTATE_MAILBOX)) { Mailbox* mailbox = getMailbox(); if (mailbox) { @@ -1405,14 +1291,14 @@ void Tile::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t void Tile::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t) { - SpectatorVec spectators; - g_game.map.getSpectators(spectators, getPosition(), true, true); + SpectatorVec list; + g_game.map.getSpectators(list, getPosition(), true, true); if (getThingCount() > 8) { - onUpdateTile(spectators); + onUpdateTile(list); } - for (Creature* spectator : spectators) { + for (Creature* spectator : list) { spectator->getPlayer()->postRemoveNotification(thing, newParent, index, LINK_NEAR); } @@ -1440,12 +1326,8 @@ void Tile::internalAddThing(uint32_t, Thing* thing) Creature* creature = thing->getCreature(); if (creature) { g_game.map.clearSpectatorCache(); - if (creature->getPlayer()) { - g_game.map.clearPlayersSpectatorCache(); - } - CreatureVector* creatures = makeCreatures(); - creatures->insert(creatures->begin(), creature); + creatures->insert(creatures->end(), creature); } else { Item* item = thing->getItem(); if (item == nullptr) { @@ -1490,13 +1372,6 @@ void Tile::internalAddThing(uint32_t, Thing* thing) void Tile::setTileFlags(const Item* item) { - if (!hasFlag(TILESTATE_FLOORCHANGE)) { - const ItemType& it = Item::items[item->getID()]; - if (it.floorChange != 0) { - setFlag(it.floorChange); - } - } - if (item->hasProperty(CONST_PROP_IMMOVABLEBLOCKSOLID)) { setFlag(TILESTATE_IMMOVABLEBLOCKSOLID); } @@ -1525,10 +1400,6 @@ void Tile::setTileFlags(const Item* item) setFlag(TILESTATE_MAILBOX); } - if (item->getTrashHolder()) { - setFlag(TILESTATE_TRASHHOLDER); - } - if (item->hasProperty(CONST_PROP_BLOCKSOLID)) { setFlag(TILESTATE_BLOCKSOLID); } @@ -1537,6 +1408,18 @@ void Tile::setTileFlags(const Item* item) setFlag(TILESTATE_BED); } + if (item->getCombatType() == COMBAT_FIREDAMAGE) { + setFlag(TILESTATE_FIREDAMAGE); + } + + if (item->getCombatType() == COMBAT_ENERGYDAMAGE) { + setFlag(TILESTATE_ENERGYDAMAGE); + } + + if (item->getCombatType() == COMBAT_EARTHDAMAGE) { + setFlag(TILESTATE_POISONDAMAGE); + } + const Container* container = item->getContainer(); if (container && container->getDepotLocker()) { setFlag(TILESTATE_DEPOT); @@ -1549,11 +1432,6 @@ void Tile::setTileFlags(const Item* item) void Tile::resetTileFlags(const Item* item) { - const ItemType& it = Item::items[item->getID()]; - if (it.floorChange != 0) { - resetFlag(TILESTATE_FLOORCHANGE); - } - if (item->hasProperty(CONST_PROP_BLOCKSOLID) && !hasProperty(item, CONST_PROP_BLOCKSOLID)) { resetFlag(TILESTATE_BLOCKSOLID); } @@ -1590,14 +1468,22 @@ void Tile::resetTileFlags(const Item* item) resetFlag(TILESTATE_MAILBOX); } - if (item->getTrashHolder()) { - resetFlag(TILESTATE_TRASHHOLDER); - } - if (item->getBed()) { resetFlag(TILESTATE_BED); } + if (item->getCombatType() == COMBAT_FIREDAMAGE) { + resetFlag(TILESTATE_FIREDAMAGE); + } + + if (item->getCombatType() == COMBAT_ENERGYDAMAGE) { + resetFlag(TILESTATE_ENERGYDAMAGE); + } + + if (item->getCombatType() == COMBAT_EARTHDAMAGE) { + resetFlag(TILESTATE_POISONDAMAGE); + } + const Container* container = item->getContainer(); if (container && container->getDepotLocker()) { resetFlag(TILESTATE_DEPOT); diff --git a/src/tile.h b/src/tile.h index d04ecc7..0b68759 100644 --- a/src/tile.h +++ b/src/tile.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,51 +20,47 @@ #ifndef FS_TILE_H_96C7EE7CF8CD48E59D5D554A181F0C56 #define FS_TILE_H_96C7EE7CF8CD48E59D5D554A181F0C56 +#include + #include "cylinder.h" #include "item.h" #include "tools.h" -#include "spectators.h" class Creature; class Teleport; -class TrashHolder; class Mailbox; +class DepotLocker; class MagicField; class QTreeLeafNode; class BedItem; -using CreatureVector = std::vector; -using ItemVector = std::vector; +typedef std::vector CreatureVector; +typedef std::vector ItemVector; +typedef std::unordered_set SpectatorVec; enum tileflags_t : uint32_t { TILESTATE_NONE = 0, - TILESTATE_FLOORCHANGE_DOWN = 1 << 0, - TILESTATE_FLOORCHANGE_NORTH = 1 << 1, - TILESTATE_FLOORCHANGE_SOUTH = 1 << 2, - TILESTATE_FLOORCHANGE_EAST = 1 << 3, - TILESTATE_FLOORCHANGE_WEST = 1 << 4, - TILESTATE_FLOORCHANGE_SOUTH_ALT = 1 << 5, - TILESTATE_FLOORCHANGE_EAST_ALT = 1 << 6, - TILESTATE_PROTECTIONZONE = 1 << 7, - TILESTATE_NOPVPZONE = 1 << 8, - TILESTATE_NOLOGOUT = 1 << 9, - TILESTATE_PVPZONE = 1 << 10, - TILESTATE_TELEPORT = 1 << 11, - TILESTATE_MAGICFIELD = 1 << 12, - TILESTATE_MAILBOX = 1 << 13, - TILESTATE_TRASHHOLDER = 1 << 14, - TILESTATE_BED = 1 << 15, - TILESTATE_DEPOT = 1 << 16, - TILESTATE_BLOCKSOLID = 1 << 17, - TILESTATE_BLOCKPATH = 1 << 18, - TILESTATE_IMMOVABLEBLOCKSOLID = 1 << 19, - TILESTATE_IMMOVABLEBLOCKPATH = 1 << 20, - TILESTATE_IMMOVABLENOFIELDBLOCKPATH = 1 << 21, - TILESTATE_NOFIELDBLOCKPATH = 1 << 22, - TILESTATE_SUPPORTS_HANGABLE = 1 << 23, - - TILESTATE_FLOORCHANGE = TILESTATE_FLOORCHANGE_DOWN | TILESTATE_FLOORCHANGE_NORTH | TILESTATE_FLOORCHANGE_SOUTH | TILESTATE_FLOORCHANGE_EAST | TILESTATE_FLOORCHANGE_WEST | TILESTATE_FLOORCHANGE_SOUTH_ALT | TILESTATE_FLOORCHANGE_EAST_ALT, + TILESTATE_PROTECTIONZONE = 1 << 0, + TILESTATE_NOPVPZONE = 1 << 1, + TILESTATE_NOLOGOUT = 1 << 2, + TILESTATE_PVPZONE = 1 << 3, + TILESTATE_REFRESH = 1 << 4, + TILESTATE_TELEPORT = 1 << 5, + TILESTATE_MAGICFIELD = 1 << 6, + TILESTATE_MAILBOX = 1 << 7, + TILESTATE_BED = 1 << 8, + TILESTATE_DEPOT = 1 << 9, + TILESTATE_BLOCKSOLID = 1 << 10, + TILESTATE_BLOCKPATH = 1 << 11, + TILESTATE_IMMOVABLEBLOCKSOLID = 1 << 12, + TILESTATE_IMMOVABLEBLOCKPATH = 1 << 13, + TILESTATE_IMMOVABLENOFIELDBLOCKPATH = 1 << 14, + TILESTATE_NOFIELDBLOCKPATH = 1 << 15, + TILESTATE_SUPPORTS_HANGABLE = 1 << 16, + TILESTATE_FIREDAMAGE = 1 << 17, + TILESTATE_POISONDAMAGE = 1 << 18, + TILESTATE_ENERGYDAMAGE = 1 << 19, }; enum ZoneType_t { @@ -150,9 +146,7 @@ class Tile : public Cylinder public: static Tile& nullptr_tile; Tile(uint16_t x, uint16_t y, uint8_t z) : tilePos(x, y, z) {} - virtual ~Tile() { - delete ground; - }; + virtual ~Tile(); // non-copyable Tile(const Tile&) = delete; @@ -166,17 +160,17 @@ class Tile : public Cylinder virtual const CreatureVector* getCreatures() const = 0; virtual CreatureVector* makeCreatures() = 0; - int32_t getThrowRange() const override final { + int32_t getThrowRange() const final { return 0; } - bool isPushable() const override final { + bool isPushable() const final { return false; } MagicField* getFieldItem() const; Teleport* getTeleportItem() const; - TrashHolder* getTrashHolder() const; Mailbox* getMailbox() const; + DepotLocker* getDepotLocker() const; BedItem* getBedItem() const; Creature* getTopCreature() const; @@ -205,13 +199,13 @@ class Tile : public Cylinder bool hasProperty(ITEMPROPERTY prop) const; bool hasProperty(const Item* exclude, ITEMPROPERTY prop) const; - bool hasFlag(uint32_t flag) const { + inline bool hasFlag(uint32_t flag) const { return hasBitSet(flag, this->flags); } - void setFlag(uint32_t flag) { + inline void setFlag(uint32_t flag) { this->flags |= flag; } - void resetFlag(uint32_t flag) { + inline void resetFlag(uint32_t flag) { this->flags &= ~flag; } @@ -228,8 +222,9 @@ class Tile : public Cylinder } bool hasHeight(uint32_t n) const; + int32_t getHeight(); - std::string getDescription(int32_t lookDistance) const override final; + std::string getDescription(int32_t lookDistance) const final; int32_t getClientIndexOfCreature(const Player* player, const Creature* creature) const; int32_t getStackposOfCreature(const Player* player, const Creature* creature) const; @@ -239,37 +234,37 @@ class Tile : public Cylinder ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor = nullptr) const override; ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, - uint32_t& maxQueryCount, uint32_t flags) const override final; - ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override final; + uint32_t& maxQueryCount, uint32_t flags) const final; + ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem, uint32_t& flags) override; - void addThing(Thing* thing) override final; + void addThing(Thing* thing) final; void addThing(int32_t index, Thing* thing) override; - void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override final; - void replaceThing(uint32_t index, Thing* thing) override final; + void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; + void replaceThing(uint32_t index, Thing* thing) final; - void removeThing(Thing* thing, uint32_t count) override final; + void removeThing(Thing* thing, uint32_t count) final; void removeCreature(Creature* creature); - int32_t getThingIndex(const Thing* thing) const override final; - size_t getFirstIndex() const override final; - size_t getLastIndex() const override final; - uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override final; - Thing* getThing(size_t index) const override final; + int32_t getThingIndex(const Thing* thing) const final; + size_t getFirstIndex() const final; + size_t getLastIndex() const final; + uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final; + Thing* getThing(size_t index) const final; - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override final; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override final; + void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; + void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; - void internalAddThing(Thing* thing) override final; + void internalAddThing(Thing* thing) final; void internalAddThing(uint32_t index, Thing* thing) override; - const Position& getPosition() const override final { + const Position& getPosition() const final { return tilePos; } - bool isRemoved() const override final { + bool isRemoved() const final { return false; } @@ -285,12 +280,13 @@ class Tile : public Cylinder private: void onAddTileItem(Item* item); void onUpdateTileItem(Item* oldItem, const ItemType& oldType, Item* newItem, const ItemType& newType); - void onRemoveTileItem(const SpectatorVec& spectators, const std::vector& oldStackPosVector, Item* item); - void onUpdateTile(const SpectatorVec& spectators); + void onRemoveTileItem(const SpectatorVec& list, const std::vector& oldStackPosVector, Item* item); + void onUpdateTile(const SpectatorVec& list); void setTileFlags(const Item* item); void resetTileFlags(const Item* item); + protected: Item* ground = nullptr; Position tilePos; uint32_t flags = 0; @@ -306,33 +302,29 @@ class DynamicTile : public Tile public: DynamicTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) {} - ~DynamicTile() { - for (Item* item : items) { - item->decrementReferenceCounter(); - } - } + ~DynamicTile(); // non-copyable DynamicTile(const DynamicTile&) = delete; DynamicTile& operator=(const DynamicTile&) = delete; - TileItemVector* getItemList() override { + TileItemVector* getItemList() final { return &items; } - const TileItemVector* getItemList() const override { + const TileItemVector* getItemList() const final { return &items; } - TileItemVector* makeItemList() override { + TileItemVector* makeItemList() final { return &items; } - CreatureVector* getCreatures() override { + CreatureVector* getCreatures() final { return &creatures; } - const CreatureVector* getCreatures() const override { + const CreatureVector* getCreatures() const final { return &creatures; } - CreatureVector* makeCreatures() override { + CreatureVector* makeCreatures() final { return &creatures; } }; @@ -346,38 +338,32 @@ class StaticTile final : public Tile public: StaticTile(uint16_t x, uint16_t y, uint8_t z) : Tile(x, y, z) {} - ~StaticTile() { - if (items) { - for (Item* item : *items) { - item->decrementReferenceCounter(); - } - } - } + ~StaticTile(); // non-copyable StaticTile(const StaticTile&) = delete; StaticTile& operator=(const StaticTile&) = delete; - TileItemVector* getItemList() override { + TileItemVector* getItemList() final { return items.get(); } - const TileItemVector* getItemList() const override { + const TileItemVector* getItemList() const final { return items.get(); } - TileItemVector* makeItemList() override { + TileItemVector* makeItemList() final { if (!items) { items.reset(new TileItemVector); } return items.get(); } - CreatureVector* getCreatures() override { + CreatureVector* getCreatures() final { return creatures.get(); } - const CreatureVector* getCreatures() const override { + const CreatureVector* getCreatures() const final { return creatures.get(); } - CreatureVector* makeCreatures() override { + CreatureVector* makeCreatures() final { if (!creatures) { creatures.reset(new CreatureVector); } @@ -385,4 +371,25 @@ class StaticTile final : public Tile } }; +inline Tile::~Tile() +{ + delete ground; +} + +inline StaticTile::~StaticTile() +{ + if (items) { + for (Item* item : *items) { + item->decrementReferenceCounter(); + } + } +} + +inline DynamicTile::~DynamicTile() +{ + for (Item* item : items) { + item->decrementReferenceCounter(); + } +} + #endif diff --git a/src/tools.cpp b/src/tools.cpp index 81a9834..506f512 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -73,7 +73,7 @@ void printXMLError(const std::string& where, const std::string& fileName, const std::cout << '^' << std::endl; } -static uint32_t circularShift(int bits, uint32_t value) +inline static uint32_t circularShift(int bits, uint32_t value) { return (value << bits) | (value >> (32 - bits)); } @@ -186,6 +186,95 @@ std::string transformToSHA1(const std::string& input) return std::string(hexstring, 40); } +uint8_t getLiquidColor(uint8_t type) +{ + uint8_t result = FLUID_COLOR_NONE; + switch (type) + { + case FLUID_WATER: + result = FLUID_COLOR_BLUE; + break; + case FLUID_NONE: + result = FLUID_COLOR_NONE; + break; + case FLUID_SLIME: + result = FLUID_COLOR_GREEN; + break; + case FLUID_BEER: + case FLUID_MUD: + case FLUID_OIL: + case FLUID_RUM: + result = FLUID_COLOR_BROWN; + break; + case FLUID_MILK: + case FLUID_COCONUTMILK: + result = FLUID_COLOR_WHITE; + break; + case FLUID_WINE: + case FLUID_MANAFLUID: + result = FLUID_COLOR_PURPLE; + break; + case FLUID_BLOOD: + case FLUID_LIFEFLUID: + result = FLUID_COLOR_RED; + break; + case FLUID_URINE: + case FLUID_LEMONADE: + case FLUID_FRUITJUICE: + result = FLUID_COLOR_YELLOW; + break; + default: + result = FLUID_COLOR_NONE; + break; + } + return result; +} + +void extractArticleAndName(std::string& data, std::string& article, std::string& name) +{ + std::string xarticle = data.substr(0, 3); + if (xarticle == "an ") + { + name = data.substr(3, data.size()); + article = "an"; + } else { + xarticle = data.substr(0, 2); + if (xarticle == "a ") + { + name = data.substr(2, data.size()); + article = "a"; + } else { + name = data; + article = ""; + } + } +} + +std::string pluralizeString(std::string str) +{ + if (str == "meat") return "meat"; + + int n = str.length(); + char ch = str[n - 1]; + char ch2 = str[n - 2]; + + std::string str2; + if (ch == 'y') + str2 = str.substr(0, n - 1) + "ies"; + else if (ch == 'o' || ch == 's' || ch == 'x') + str2 = str + "es"; + else if (ch == 'h'&& ch2 == 'c') + str2 = str + "es"; + else if (ch == 'f') + str2 = str.substr(0, n - 1) + "ves"; + else if (ch == 'e'&&ch2 == 'f') + str2 = str.substr(0, n - 2) + "ves"; + else + str2 = str + "s"; + + return str2; +} + std::string generateToken(const std::string& key, uint32_t ticks) { // generate message from ticks @@ -271,9 +360,9 @@ std::string asUpperCaseString(std::string source) return source; } -StringVector explodeString(const std::string& inString, const std::string& separator, int32_t limit/* = -1*/) +StringVec explodeString(const std::string& inString, const std::string& separator, int32_t limit/* = -1*/) { - StringVector returnVector; + StringVec returnVector; std::string::size_type start = 0, end = 0; while (--limit != -1 && (end = inString.find(separator, start)) != std::string::npos) { @@ -285,9 +374,9 @@ StringVector explodeString(const std::string& inString, const std::string& separ return returnVector; } -IntegerVector vectorAtoi(const StringVector& stringVector) +IntegerVec vectorAtoi(const StringVec& stringVector) { - IntegerVector returnVector; + IntegerVec returnVector; for (const auto& string : stringVector) { returnVector.push_back(std::stoi(string)); } @@ -497,14 +586,42 @@ Direction getDirectionTo(const Position& from, const Position& to) return dir; } -using MagicEffectNames = std::unordered_map; -using ShootTypeNames = std::unordered_map; -using CombatTypeNames = std::unordered_map>; -using AmmoTypeNames = std::unordered_map; -using WeaponActionNames = std::unordered_map; -using SkullNames = std::unordered_map; +struct MagicEffectNames { + const char* name; + MagicEffectClasses effect; +}; -MagicEffectNames magicEffectNames = { +struct ShootTypeNames { + const char* name; + ShootType_t shoot; +}; + +struct CombatTypeNames { + const char* name; + CombatType_t combat; +}; + +struct AmmoTypeNames { + const char* name; + Ammo_t ammoType; +}; + +struct WeaponActionNames { + const char* name; + WeaponAction_t weaponAction; +}; + +struct SkullNames { + const char* name; + Skulls_t skull; +}; + +struct FluidNames { + const char* name; + FluidTypes_t fluidType; +}; + +MagicEffectNames magicEffectNames[] = { {"redspark", CONST_ME_DRAWBLOOD}, {"bluebubble", CONST_ME_LOSEENERGY}, {"poff", CONST_ME_POFF}, @@ -532,63 +649,9 @@ MagicEffectNames magicEffectNames = { {"whitenote", CONST_ME_SOUND_WHITE}, {"bubbles", CONST_ME_BUBBLES}, {"dice", CONST_ME_CRAPS}, - {"giftwraps", CONST_ME_GIFT_WRAPS}, - {"yellowfirework", CONST_ME_FIREWORK_YELLOW}, - {"redfirework", CONST_ME_FIREWORK_RED}, - {"bluefirework", CONST_ME_FIREWORK_BLUE}, - {"stun", CONST_ME_STUN}, - {"sleep", CONST_ME_SLEEP}, - {"watercreature", CONST_ME_WATERCREATURE}, - {"groundshaker", CONST_ME_GROUNDSHAKER}, - {"hearts", CONST_ME_HEARTS}, - {"fireattack", CONST_ME_FIREATTACK}, - {"energyarea", CONST_ME_ENERGYAREA}, - {"smallclouds", CONST_ME_SMALLCLOUDS}, - {"holydamage", CONST_ME_HOLYDAMAGE}, - {"bigclouds", CONST_ME_BIGCLOUDS}, - {"icearea", CONST_ME_ICEAREA}, - {"icetornado", CONST_ME_ICETORNADO}, - {"iceattack", CONST_ME_ICEATTACK}, - {"stones", CONST_ME_STONES}, - {"smallplants", CONST_ME_SMALLPLANTS}, - {"carniphila", CONST_ME_CARNIPHILA}, - {"purpleenergy", CONST_ME_PURPLEENERGY}, - {"yellowenergy", CONST_ME_YELLOWENERGY}, - {"holyarea", CONST_ME_HOLYAREA}, - {"bigplants", CONST_ME_BIGPLANTS}, - {"cake", CONST_ME_CAKE}, - {"giantice", CONST_ME_GIANTICE}, - {"watersplash", CONST_ME_WATERSPLASH}, - {"plantattack", CONST_ME_PLANTATTACK}, - {"tutorialarrow", CONST_ME_TUTORIALARROW}, - {"tutorialsquare", CONST_ME_TUTORIALSQUARE}, - {"mirrorhorizontal", CONST_ME_MIRRORHORIZONTAL}, - {"mirrorvertical", CONST_ME_MIRRORVERTICAL}, - {"skullhorizontal", CONST_ME_SKULLHORIZONTAL}, - {"skullvertical", CONST_ME_SKULLVERTICAL}, - {"assassin", CONST_ME_ASSASSIN}, - {"stepshorizontal", CONST_ME_STEPSHORIZONTAL}, - {"bloodysteps", CONST_ME_BLOODYSTEPS}, - {"stepsvertical", CONST_ME_STEPSVERTICAL}, - {"yalaharighost", CONST_ME_YALAHARIGHOST}, - {"bats", CONST_ME_BATS}, - {"smoke", CONST_ME_SMOKE}, - {"insects", CONST_ME_INSECTS}, - {"dragonhead", CONST_ME_DRAGONHEAD}, - {"orcshaman", CONST_ME_ORCSHAMAN}, - {"orcshamanfire", CONST_ME_ORCSHAMAN_FIRE}, - {"thunder", CONST_ME_THUNDER}, - {"ferumbras", CONST_ME_FERUMBRAS}, - {"confettihorizontal", CONST_ME_CONFETTI_HORIZONTAL}, - {"confettivertical", CONST_ME_CONFETTI_VERTICAL}, - {"blacksmoke", CONST_ME_BLACKSMOKE}, - {"redsmoke", CONST_ME_REDSMOKE}, - {"yellowsmoke", CONST_ME_YELLOWSMOKE}, - {"greensmoke", CONST_ME_GREENSMOKE}, - {"purplesmoke", CONST_ME_PURPLESMOKE}, }; -ShootTypeNames shootTypeNames = { +ShootTypeNames shootTypeNames[] = { {"spear", CONST_ANI_SPEAR}, {"bolt", CONST_ANI_BOLT}, {"arrow", CONST_ANI_ARROW}, @@ -604,59 +667,22 @@ ShootTypeNames shootTypeNames = { {"snowball", CONST_ANI_SNOWBALL}, {"powerbolt", CONST_ANI_POWERBOLT}, {"poison", CONST_ANI_POISON}, - {"infernalbolt", CONST_ANI_INFERNALBOLT}, - {"huntingspear", CONST_ANI_HUNTINGSPEAR}, - {"enchantedspear", CONST_ANI_ENCHANTEDSPEAR}, - {"redstar", CONST_ANI_REDSTAR}, - {"greenstar", CONST_ANI_GREENSTAR}, - {"royalspear", CONST_ANI_ROYALSPEAR}, - {"sniperarrow", CONST_ANI_SNIPERARROW}, - {"onyxarrow", CONST_ANI_ONYXARROW}, - {"piercingbolt", CONST_ANI_PIERCINGBOLT}, - {"whirlwindsword", CONST_ANI_WHIRLWINDSWORD}, - {"whirlwindaxe", CONST_ANI_WHIRLWINDAXE}, - {"whirlwindclub", CONST_ANI_WHIRLWINDCLUB}, - {"etherealspear", CONST_ANI_ETHEREALSPEAR}, - {"ice", CONST_ANI_ICE}, - {"earth", CONST_ANI_EARTH}, - {"holy", CONST_ANI_HOLY}, - {"suddendeath", CONST_ANI_SUDDENDEATH}, - {"flasharrow", CONST_ANI_FLASHARROW}, - {"flammingarrow", CONST_ANI_FLAMMINGARROW}, - {"shiverarrow", CONST_ANI_SHIVERARROW}, - {"energyball", CONST_ANI_ENERGYBALL}, - {"smallice", CONST_ANI_SMALLICE}, - {"smallholy", CONST_ANI_SMALLHOLY}, - {"smallearth", CONST_ANI_SMALLEARTH}, - {"eartharrow", CONST_ANI_EARTHARROW}, - {"explosion", CONST_ANI_EXPLOSION}, - {"cake", CONST_ANI_CAKE}, - {"tarsalarrow", CONST_ANI_TARSALARROW}, - {"vortexbolt", CONST_ANI_VORTEXBOLT}, - {"prismaticbolt", CONST_ANI_PRISMATICBOLT}, - {"crystallinearrow", CONST_ANI_CRYSTALLINEARROW}, - {"drillbolt", CONST_ANI_DRILLBOLT}, - {"envenomedarrow", CONST_ANI_ENVENOMEDARROW}, - {"gloothspear", CONST_ANI_GLOOTHSPEAR}, - {"simplearrow", CONST_ANI_SIMPLEARROW}, }; -CombatTypeNames combatTypeNames = { - {COMBAT_PHYSICALDAMAGE, "physical"}, - {COMBAT_ENERGYDAMAGE, "energy"}, - {COMBAT_EARTHDAMAGE, "earth"}, - {COMBAT_FIREDAMAGE, "fire"}, - {COMBAT_UNDEFINEDDAMAGE, "undefined"}, - {COMBAT_LIFEDRAIN, "lifedrain"}, - {COMBAT_MANADRAIN, "manadrain"}, - {COMBAT_HEALING, "healing"}, - {COMBAT_DROWNDAMAGE, "drown"}, - {COMBAT_ICEDAMAGE, "ice"}, - {COMBAT_HOLYDAMAGE, "holy"}, - {COMBAT_DEATHDAMAGE, "death"}, +CombatTypeNames combatTypeNames[] = { + {"physical", COMBAT_PHYSICALDAMAGE}, + {"energy", COMBAT_ENERGYDAMAGE}, + {"drown", COMBAT_DROWNDAMAGE}, + {"earth", COMBAT_EARTHDAMAGE}, + {"poison", COMBAT_EARTHDAMAGE}, + {"fire", COMBAT_FIREDAMAGE}, + {"undefined", COMBAT_UNDEFINEDDAMAGE}, + {"lifedrain", COMBAT_LIFEDRAIN}, + {"manadrain", COMBAT_MANADRAIN}, + {"healing", COMBAT_HEALING}, }; -AmmoTypeNames ammoTypeNames = { +AmmoTypeNames ammoTypeNames[] = { {"spear", AMMO_SPEAR}, {"bolt", AMMO_BOLT}, {"arrow", AMMO_ARROW}, @@ -668,114 +694,119 @@ AmmoTypeNames ammoTypeNames = { {"largerock", AMMO_STONE}, {"snowball", AMMO_SNOWBALL}, {"powerbolt", AMMO_BOLT}, - {"infernalbolt", AMMO_BOLT}, - {"huntingspear", AMMO_SPEAR}, - {"enchantedspear", AMMO_SPEAR}, - {"royalspear", AMMO_SPEAR}, - {"sniperarrow", AMMO_ARROW}, - {"onyxarrow", AMMO_ARROW}, - {"piercingbolt", AMMO_BOLT}, - {"etherealspear", AMMO_SPEAR}, - {"flasharrow", AMMO_ARROW}, - {"flammingarrow", AMMO_ARROW}, - {"shiverarrow", AMMO_ARROW}, - {"eartharrow", AMMO_ARROW}, }; -WeaponActionNames weaponActionNames = { +WeaponActionNames weaponActionNames[] = { {"move", WEAPONACTION_MOVE}, {"removecharge", WEAPONACTION_REMOVECHARGE}, {"removecount", WEAPONACTION_REMOVECOUNT}, }; -SkullNames skullNames = { +SkullNames skullNames[] = { {"none", SKULL_NONE}, {"yellow", SKULL_YELLOW}, {"green", SKULL_GREEN}, {"white", SKULL_WHITE}, {"red", SKULL_RED}, - {"black", SKULL_BLACK}, - {"orange", SKULL_ORANGE}, +}; + +FluidNames fluidNames[] = { + {"none", FLUID_NONE}, + {"water", FLUID_WATER}, + {"wine", FLUID_WINE}, + {"beer", FLUID_BEER}, + {"mud", FLUID_MUD}, + {"blood", FLUID_BLOOD}, + {"slime", FLUID_SLIME}, + {"oil", FLUID_OIL}, + {"urine", FLUID_URINE}, + {"milk", FLUID_MILK}, + {"manafluid", FLUID_MANAFLUID}, + {"lifefluid", FLUID_LIFEFLUID}, + {"lemonade", FLUID_LEMONADE}, + {"rum", FLUID_RUM}, + {"coconutmilk", FLUID_COCONUTMILK}, + {"fruitjuice", FLUID_FRUITJUICE} }; MagicEffectClasses getMagicEffect(const std::string& strValue) { - auto magicEffect = magicEffectNames.find(strValue); - if (magicEffect != magicEffectNames.end()) { - return magicEffect->second; + for (auto& magicEffectName : magicEffectNames) { + if (strcasecmp(strValue.c_str(), magicEffectName.name) == 0) { + return magicEffectName.effect; + } } return CONST_ME_NONE; } ShootType_t getShootType(const std::string& strValue) { - auto shootType = shootTypeNames.find(strValue); - if (shootType != shootTypeNames.end()) { - return shootType->second; + for (size_t i = 0, size = sizeof(shootTypeNames) / sizeof(ShootTypeNames); i < size; ++i) { + if (strcasecmp(strValue.c_str(), shootTypeNames[i].name) == 0) { + return shootTypeNames[i].shoot; + } } return CONST_ANI_NONE; } +CombatType_t getCombatType(const std::string& strValue) +{ + for (size_t i = 0, size = sizeof(combatTypeNames) / sizeof(CombatTypeNames); i < size; ++i) { + if (strcasecmp(strValue.c_str(), combatTypeNames[i].name) == 0) { + return combatTypeNames[i].combat; + } + } + return COMBAT_NONE; +} + std::string getCombatName(CombatType_t combatType) { - auto combatName = combatTypeNames.find(combatType); - if (combatName != combatTypeNames.end()) { - return combatName->second; + for (size_t i = 0, size = sizeof(combatTypeNames) / sizeof(CombatTypeNames); i < size; ++i) { + if (combatTypeNames[i].combat == combatType) { + return combatTypeNames[i].name; + } } return "unknown"; } Ammo_t getAmmoType(const std::string& strValue) { - auto ammoType = ammoTypeNames.find(strValue); - if (ammoType != ammoTypeNames.end()) { - return ammoType->second; + for (size_t i = 0, size = sizeof(ammoTypeNames) / sizeof(AmmoTypeNames); i < size; ++i) { + if (strcasecmp(strValue.c_str(), ammoTypeNames[i].name) == 0) { + return ammoTypeNames[i].ammoType; + } } return AMMO_NONE; } WeaponAction_t getWeaponAction(const std::string& strValue) { - auto weaponAction = weaponActionNames.find(strValue); - if (weaponAction != weaponActionNames.end()) { - return weaponAction->second; + for (size_t i = 0, size = sizeof(weaponActionNames) / sizeof(WeaponActionNames); i < size; ++i) { + if (strcasecmp(strValue.c_str(), weaponActionNames[i].name) == 0) { + return weaponActionNames[i].weaponAction; + } } return WEAPONACTION_NONE; } Skulls_t getSkullType(const std::string& strValue) { - auto skullType = skullNames.find(strValue); - if (skullType != skullNames.end()) { - return skullType->second; + for (size_t i = 0, size = sizeof(skullNames) / sizeof(SkullNames); i < size; ++i) { + if (strcasecmp(strValue.c_str(), skullNames[i].name) == 0) { + return skullNames[i].skull; + } } return SKULL_NONE; } -std::string getSpecialSkillName(uint8_t skillid) +FluidTypes_t getFluidType(const std::string& strValue) { - switch (skillid) { - case SPECIALSKILL_CRITICALHITCHANCE: - return "critical hit chance"; - - case SPECIALSKILL_CRITICALHITAMOUNT: - return "critical extra damage"; - - case SPECIALSKILL_LIFELEECHCHANCE: - return "hitpoints leech chance"; - - case SPECIALSKILL_LIFELEECHAMOUNT: - return "hitpoints leech amount"; - - case SPECIALSKILL_MANALEECHCHANCE: - return "manapoints leech chance"; - - case SPECIALSKILL_MANALEECHAMOUNT: - return "mana points leech amount"; - - default: - return "unknown"; + for (size_t i = 0, size = sizeof(fluidNames) / sizeof(FluidNames); i < size; ++i) { + if (strcasecmp(strValue.c_str(), fluidNames[i].name) == 0) { + return fluidNames[i].fluidType; + } } + return FLUID_NONE; } std::string getSkillName(uint8_t skillid) @@ -813,31 +844,6 @@ std::string getSkillName(uint8_t skillid) } } -uint32_t adlerChecksum(const uint8_t* data, size_t length) -{ - if (length > NETWORKMESSAGE_MAXSIZE) { - return 0; - } - - const uint16_t adler = 65521; - - uint32_t a = 1, b = 0; - - while (length > 0) { - size_t tmp = length > 5552 ? 5552 : length; - length -= tmp; - - do { - a += *data++; - b += a; - } while (--tmp); - - a %= adler; - b %= adler; - } - - return (b << 16) | a; -} std::string ucfirst(std::string str) { @@ -911,12 +917,6 @@ size_t combatTypeToIndex(CombatType_t combatType) return 7; case COMBAT_DROWNDAMAGE: return 8; - case COMBAT_ICEDAMAGE: - return 9; - case COMBAT_HOLYDAMAGE: - return 10; - case COMBAT_DEATHDAMAGE: - return 11; default: return 0; } @@ -927,32 +927,12 @@ CombatType_t indexToCombatType(size_t v) return static_cast(1 << v); } -uint8_t serverFluidToClient(uint8_t serverFluid) -{ - uint8_t size = sizeof(clientToServerFluidMap) / sizeof(uint8_t); - for (uint8_t i = 0; i < size; ++i) { - if (clientToServerFluidMap[i] == serverFluid) { - return i; - } - } - return 0; -} - -uint8_t clientFluidToServer(uint8_t clientFluid) -{ - uint8_t size = sizeof(clientToServerFluidMap) / sizeof(uint8_t); - if (clientFluid >= size) { - return 0; - } - return clientToServerFluidMap[clientFluid]; -} - itemAttrTypes stringToItemAttribute(const std::string& str) { if (str == "aid") { return ITEM_ATTRIBUTE_ACTIONID; - } else if (str == "uid") { - return ITEM_ATTRIBUTE_UNIQUEID; + } else if (str == "mid") { + return ITEM_ATTRIBUTE_MOVEMENTID; } else if (str == "description") { return ITEM_ATTRIBUTE_DESCRIPTION; } else if (str == "text") { @@ -973,12 +953,8 @@ itemAttrTypes stringToItemAttribute(const std::string& str) return ITEM_ATTRIBUTE_ATTACK; } else if (str == "defense") { return ITEM_ATTRIBUTE_DEFENSE; - } else if (str == "extradefense") { - return ITEM_ATTRIBUTE_EXTRADEFENSE; } else if (str == "armor") { return ITEM_ATTRIBUTE_ARMOR; - } else if (str == "hitchance") { - return ITEM_ATTRIBUTE_HITCHANCE; } else if (str == "shootrange") { return ITEM_ATTRIBUTE_SHOOTRANGE; } else if (str == "owner") { @@ -1181,8 +1157,8 @@ const char* getReturnMessage(ReturnValue value) case RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS: return "You need to split your spears first."; - case RETURNVALUE_NAMEISTOOAMBIGUOUS: - return "Player name is ambiguous."; + case RETURNVALUE_NAMEISTOOAMBIGIOUS: + return "Name is too ambigious."; case RETURNVALUE_CANONLYUSEONESHIELD: return "You may use only one shield."; @@ -1193,12 +1169,6 @@ const char* getReturnMessage(ReturnValue value) case RETURNVALUE_YOUARENOTTHEOWNER: return "You are not the owner."; - case RETURNVALUE_NOSUCHRAIDEXISTS: - return "No such raid exists."; - - case RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING: - return "Another raid is already executing."; - case RETURNVALUE_TRADEPLAYERFARAWAY: return "Trade player is too far away."; @@ -1219,23 +1189,23 @@ const char* getReturnMessage(ReturnValue value) } } -int64_t OTSYS_TIME() +void getFilesInDirectory(const boost::filesystem::path& root, const std::string& ext, std::vector& ret) { - return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); -} - -SpellGroup_t stringToSpellGroup(std::string value) -{ - std::string tmpStr = asLowerCaseString(value); - if (tmpStr == "attack" || tmpStr == "1") { - return SPELLGROUP_ATTACK; - } else if (tmpStr == "healing" || tmpStr == "2") { - return SPELLGROUP_HEALING; - } else if (tmpStr == "support" || tmpStr == "3") { - return SPELLGROUP_SUPPORT; - } else if (tmpStr == "special" || tmpStr == "4") { - return SPELLGROUP_SPECIAL; + if (!boost::filesystem::exists(root)) { + return; } - return SPELLGROUP_NONE; + if (boost::filesystem::is_directory(root)) + { + boost::filesystem::recursive_directory_iterator it(root); + boost::filesystem::recursive_directory_iterator endit; + while (it != endit) + { + if (boost::filesystem::is_regular_file(*it) && it->path().extension() == ext) + { + ret.push_back(it->path().filename()); + } + ++it; + } + } } diff --git a/src/tools.h b/src/tools.h index f59004e..8f05a93 100644 --- a/src/tools.h +++ b/src/tools.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ #define FS_TOOLS_H_5F9A9742DA194628830AA1C64909AE43 #include +#include #include "position.h" #include "const.h" @@ -30,7 +31,10 @@ void printXMLError(const std::string& where, const std::string& fileName, const std::string transformToSHA1(const std::string& input); std::string generateToken(const std::string& key, uint32_t ticks); +uint8_t getLiquidColor(uint8_t type); +void extractArticleAndName(std::string& data, std::string& article, std::string& name); +std::string pluralizeString(std::string str); void replaceString(std::string& str, const std::string& sought, const std::string& replacement); void trim_right(std::string& source, char t); void trim_left(std::string& source, char t); @@ -38,15 +42,20 @@ void toLowerCaseString(std::string& source); std::string asLowerCaseString(std::string source); std::string asUpperCaseString(std::string source); -using StringVector = std::vector; -using IntegerVector = std::vector; +typedef std::vector StringVec; +typedef std::vector IntegerVec; -StringVector explodeString(const std::string& inString, const std::string& separator, int32_t limit = -1); -IntegerVector vectorAtoi(const StringVector& stringVector); -constexpr bool hasBitSet(uint32_t flag, uint32_t flags) { +StringVec explodeString(const std::string& inString, const std::string& separator, int32_t limit = -1); +IntegerVec vectorAtoi(const StringVec& stringVector); +inline bool hasBitSet(uint32_t flag, uint32_t flags) { return (flags & flag) != 0; } +inline bool IsDigit(char c) +{ + return ('0' <= c && c <= '9'); +} + std::mt19937& getRandomGenerator(); int32_t uniform_random(int32_t minNumber, int32_t maxNumber); int32_t normal_random(int32_t minNumber, int32_t maxNumber); @@ -68,14 +77,13 @@ MagicEffectClasses getMagicEffect(const std::string& strValue); ShootType_t getShootType(const std::string& strValue); Ammo_t getAmmoType(const std::string& strValue); WeaponAction_t getWeaponAction(const std::string& strValue); +CombatType_t getCombatType(const std::string& strValue); Skulls_t getSkullType(const std::string& strValue); +FluidTypes_t getFluidType(const std::string& strValue); std::string getCombatName(CombatType_t combatType); -std::string getSpecialSkillName(uint8_t skillid); std::string getSkillName(uint8_t skillid); -uint32_t adlerChecksum(const uint8_t* data, size_t length); - std::string ucfirst(std::string str); std::string ucwords(std::string str); bool booleanString(const std::string& str); @@ -85,15 +93,15 @@ std::string getWeaponName(WeaponType_t weaponType); size_t combatTypeToIndex(CombatType_t combatType); CombatType_t indexToCombatType(size_t v); -uint8_t serverFluidToClient(uint8_t serverFluid); -uint8_t clientFluidToServer(uint8_t clientFluid); - itemAttrTypes stringToItemAttribute(const std::string& str); const char* getReturnMessage(ReturnValue value); -int64_t OTSYS_TIME(); +void getFilesInDirectory(const boost::filesystem::path& root, const std::string& ext, std::vector& ret); -SpellGroup_t stringToSpellGroup(std::string value); +inline int64_t OTSYS_TIME() +{ + return std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); +} #endif diff --git a/src/town.h b/src/town.h index be3c9e9..9e50720 100644 --- a/src/town.h +++ b/src/town.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -50,7 +50,7 @@ class Town Position templePosition; }; -using TownMap = std::map; +typedef std::map TownMap; class Towns { diff --git a/src/trashholder.cpp b/src/trashholder.cpp deleted file mode 100644 index 5d90758..0000000 --- a/src/trashholder.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "trashholder.h" -#include "game.h" - -extern Game g_game; - -ReturnValue TrashHolder::queryAdd(int32_t, const Thing&, uint32_t, uint32_t, Creature*) const -{ - return RETURNVALUE_NOERROR; -} - -ReturnValue TrashHolder::queryMaxCount(int32_t, const Thing&, uint32_t count, uint32_t& maxQueryCount, uint32_t) const -{ - maxQueryCount = std::max(1, count); - return RETURNVALUE_NOERROR; -} - -ReturnValue TrashHolder::queryRemove(const Thing&, uint32_t, uint32_t) const -{ - return RETURNVALUE_NOTPOSSIBLE; -} - -Cylinder* TrashHolder::queryDestination(int32_t&, const Thing&, Item**, uint32_t&) -{ - return this; -} - -void TrashHolder::addThing(Thing* thing) -{ - return addThing(0, thing); -} - -void TrashHolder::addThing(int32_t, Thing* thing) -{ - Item* item = thing->getItem(); - if (!item) { - return; - } - - if (item == this || !item->hasProperty(CONST_PROP_MOVEABLE)) { - return; - } - - const ItemType& it = Item::items[id]; - if (item->isHangable() && it.isGroundTile()) { - Tile* tile = dynamic_cast(getParent()); - if (tile && tile->hasFlag(TILESTATE_SUPPORTS_HANGABLE)) { - return; - } - } - - g_game.internalRemoveItem(item); - - if (it.magicEffect != CONST_ME_NONE) { - g_game.addMagicEffect(getPosition(), it.magicEffect); - } -} - -void TrashHolder::updateThing(Thing*, uint16_t, uint32_t) -{ - // -} - -void TrashHolder::replaceThing(uint32_t, Thing*) -{ - // -} - -void TrashHolder::removeThing(Thing*, uint32_t) -{ - // -} - -void TrashHolder::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t) -{ - getParent()->postAddNotification(thing, oldParent, index, LINK_PARENT); -} - -void TrashHolder::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t) -{ - getParent()->postRemoveNotification(thing, newParent, index, LINK_PARENT); -} diff --git a/src/trashholder.h b/src/trashholder.h deleted file mode 100644 index a8d1f46..0000000 --- a/src/trashholder.h +++ /dev/null @@ -1,57 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_TRASHHOLDER_H_BA162024D67B4D388147F5EE06F33098 -#define FS_TRASHHOLDER_H_BA162024D67B4D388147F5EE06F33098 - -#include "item.h" -#include "cylinder.h" -#include "const.h" - -class TrashHolder final : public Item, public Cylinder -{ - public: - explicit TrashHolder(uint16_t itemId) : Item(itemId) {} - - TrashHolder* getTrashHolder() override { - return this; - } - const TrashHolder* getTrashHolder() const override { - return this; - } - - //cylinder implementations - ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor = nullptr) const override; - ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount, uint32_t flags) const override; - ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override; - Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, uint32_t& flags) override; - - void addThing(Thing* thing) override; - void addThing(int32_t index, Thing* thing) override; - - void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override; - void replaceThing(uint32_t index, Thing* thing) override; - - void removeThing(Thing* thing, uint32_t count) override; - - void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; - void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override; -}; - -#endif diff --git a/src/vocation.cpp b/src/vocation.cpp index 015fdac..98a9973 100644 --- a/src/vocation.cpp +++ b/src/vocation.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -46,12 +46,15 @@ bool Vocations::loadFromXml() std::forward_as_tuple(id), std::forward_as_tuple(id)); Vocation& voc = res.first->second; - if ((attr = vocationNode.attribute("name"))) { - voc.name = attr.as_string(); + if (!(attr = vocationNode.attribute("flagid"))) { + std::cout << "[Warning - Vocations::loadFromXml] Missing vocation flag id" << std::endl; + continue; } - if ((attr = vocationNode.attribute("clientid"))) { - voc.clientId = pugi::cast(attr.value()); + voc.flagid = pugi::cast(attr.value()); + + if ((attr = vocationNode.attribute("name"))) { + voc.name = attr.as_string(); } if ((attr = vocationNode.attribute("description"))) { @@ -123,26 +126,6 @@ bool Vocations::loadFromXml() } else { std::cout << "[Notice - Vocations::loadFromXml] Missing skill id for vocation: " << voc.id << std::endl; } - } else if (strcasecmp(childNode.name(), "formula") == 0) { - pugi::xml_attribute meleeDamageAttribute = childNode.attribute("meleeDamage"); - if (meleeDamageAttribute) { - voc.meleeDamageMultiplier = pugi::cast(meleeDamageAttribute.value()); - } - - pugi::xml_attribute distDamageAttribute = childNode.attribute("distDamage"); - if (distDamageAttribute) { - voc.distDamageMultiplier = pugi::cast(distDamageAttribute.value()); - } - - pugi::xml_attribute defenseAttribute = childNode.attribute("defense"); - if (defenseAttribute) { - voc.defenseMultiplier = pugi::cast(defenseAttribute.value()); - } - - pugi::xml_attribute armorAttribute = childNode.attribute("armor"); - if (armorAttribute) { - voc.armorMultiplier = pugi::cast(armorAttribute.value()); - } } } } @@ -204,7 +187,7 @@ uint64_t Vocation::getReqMana(uint32_t magLevel) return it->second; } - uint64_t reqMana = static_cast(1600 * std::pow(manaMultiplier, static_cast(magLevel) - 1)); + uint64_t reqMana = static_cast(400 * std::pow(manaMultiplier, static_cast(magLevel) - 1)); uint32_t modResult = reqMana % 20; if (modResult < 10) { reqMana -= modResult; diff --git a/src/vocation.h b/src/vocation.h index 757d45a..b6bc48e 100644 --- a/src/vocation.h +++ b/src/vocation.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,9 +40,8 @@ class Vocation uint16_t getId() const { return id; } - - uint8_t getClientId() const { - return clientId; + uint16_t getFlagId() const { + return flagid; } uint32_t getHPGain() const { @@ -86,12 +85,7 @@ class Vocation return fromVocation; } - float meleeDamageMultiplier = 1.0f; - float distDamageMultiplier = 1.0f; - float defenseMultiplier = 1.0f; - float armorMultiplier = 1.0f; - - private: + protected: friend class Vocations; std::map cacheMana; @@ -112,13 +106,13 @@ class Vocation uint32_t gainHP = 5; uint32_t fromVocation = VOCATION_NONE; uint32_t attackSpeed = 1500; - uint32_t baseSpeed = 220; + uint32_t baseSpeed = 70; uint16_t id; + uint16_t flagid; uint16_t gainSoulTicks = 120; uint8_t soulMax = 100; - uint8_t clientId = 0; static uint32_t skillBase[SKILL_LAST + 1]; }; diff --git a/src/waitlist.cpp b/src/waitlist.cpp index 355e88b..2743629 100644 --- a/src/waitlist.cpp +++ b/src/waitlist.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,71 +26,26 @@ extern ConfigManager g_config; extern Game g_game; - -namespace { - -struct Wait +WaitListIterator WaitingList::findClient(const Player* player, uint32_t& slot) { - constexpr Wait(std::size_t timeout, uint32_t playerGUID) : - timeout(timeout), playerGUID(playerGUID) {} - - std::size_t timeout; - uint32_t playerGUID; -}; - -using WaitList = std::list; - -void cleanupList(WaitList& list) -{ - int64_t time = OTSYS_TIME(); - - auto it = list.begin(), end = list.end(); - while (it != end) { - if ((it->timeout - time) <= 0) { - it = list.erase(it); - } else { - ++it; + slot = 1; + for (auto it = priorityWaitList.begin(), end = priorityWaitList.end(); it != end; ++it) { + if (it->playerGUID == player->getGUID()) { + return it; } + ++slot; } -} -std::size_t getTimeout(std::size_t slot) -{ - //timeout is set to 15 seconds longer than expected retry attempt - return WaitingList::getTime(slot) + 15; -} - -} // namespace - -struct WaitListInfo -{ - WaitList priorityWaitList; - WaitList waitList; - - std::pair findClient(const Player *player) { - std::size_t slot = 1; - for (auto it = priorityWaitList.begin(), end = priorityWaitList.end(); it != end; ++it, ++slot) { - if (it->playerGUID == player->getGUID()) { - return {it, slot}; - } + for (auto it = waitList.begin(), end = waitList.end(); it != end; ++it) { + if (it->playerGUID == player->getGUID()) { + return it; } - - for (auto it = waitList.begin(), end = waitList.end(); it != end; ++it, ++slot) { - if (it->playerGUID == player->getGUID()) { - return {it, slot}; - } - } - return {waitList.end(), slot}; + ++slot; } -}; - -WaitingList& WaitingList::getInstance() -{ - static WaitingList waitingList; - return waitingList; + return waitList.end(); } -std::size_t WaitingList::getTime(std::size_t slot) +uint32_t WaitingList::getTime(uint32_t slot) { if (slot < 5) { return 5; @@ -105,6 +60,12 @@ std::size_t WaitingList::getTime(std::size_t slot) } } +uint32_t WaitingList::getTimeout(uint32_t slot) +{ + //timeout is set to 15 seconds longer than expected retry attempt + return getTime(slot) + 15; +} + bool WaitingList::clientLogin(const Player* player) { if (player->hasFlag(PlayerFlag_CanAlwaysLogin) || player->getAccountType() >= ACCOUNT_TYPE_GAMEMASTER) { @@ -112,20 +73,20 @@ bool WaitingList::clientLogin(const Player* player) } uint32_t maxPlayers = static_cast(g_config.getNumber(ConfigManager::MAX_PLAYERS)); - if (maxPlayers == 0 || (info->priorityWaitList.empty() && info->waitList.empty() && g_game.getPlayersOnline() < maxPlayers)) { + if (maxPlayers == 0 || (priorityWaitList.empty() && waitList.empty() && g_game.getPlayersOnline() < maxPlayers)) { return true; } - cleanupList(info->priorityWaitList); - cleanupList(info->waitList); + WaitingList::cleanupList(priorityWaitList); + WaitingList::cleanupList(waitList); - WaitList::iterator it; - WaitList::size_type slot; - std::tie(it, slot) = info->findClient(player); - if (it != info->waitList.end()) { + uint32_t slot; + + auto it = findClient(player, slot); + if (it != waitList.end()) { if ((g_game.getPlayersOnline() + slot) <= maxPlayers) { //should be able to login now - info->waitList.erase(it); + waitList.erase(it); return true; } @@ -134,25 +95,36 @@ bool WaitingList::clientLogin(const Player* player) return false; } - slot = info->priorityWaitList.size(); + slot = priorityWaitList.size(); if (player->isPremium()) { - info->priorityWaitList.emplace_back(OTSYS_TIME() + (getTimeout(slot + 1) * 1000), player->getGUID()); + priorityWaitList.emplace_back(OTSYS_TIME() + (getTimeout(slot + 1) * 1000), player->getGUID()); } else { - slot += info->waitList.size(); - info->waitList.emplace_back(OTSYS_TIME() + (getTimeout(slot + 1) * 1000), player->getGUID()); + slot += waitList.size(); + waitList.emplace_back(OTSYS_TIME() + (getTimeout(slot + 1) * 1000), player->getGUID()); } return false; } -std::size_t WaitingList::getClientSlot(const Player* player) +uint32_t WaitingList::getClientSlot(const Player* player) { - WaitList::iterator it; - WaitList::size_type slot; - std::tie(it, slot) = info->findClient(player); - if (it == info->waitList.end()) { + uint32_t slot; + auto it = findClient(player, slot); + if (it == waitList.end()) { return 0; } return slot; } -WaitingList::WaitingList() : info(new WaitListInfo) {} +void WaitingList::cleanupList(WaitList& list) +{ + int64_t time = OTSYS_TIME(); + + auto it = list.begin(), end = list.end(); + while (it != end) { + if ((it->timeout - time) <= 0) { + it = list.erase(it); + } else { + ++it; + } + } +} diff --git a/src/waitlist.h b/src/waitlist.h index 661e7b9..77dd2c5 100644 --- a/src/waitlist.h +++ b/src/waitlist.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,21 +22,36 @@ #include "player.h" -struct WaitListInfo; +struct Wait { + constexpr Wait(int64_t timeout, uint32_t playerGUID) : + timeout(timeout), playerGUID(playerGUID) {} + + int64_t timeout; + uint32_t playerGUID; +}; + +typedef std::list WaitList; +typedef WaitList::iterator WaitListIterator; class WaitingList { public: - static WaitingList& getInstance(); + static WaitingList* getInstance() { + static WaitingList waitingList; + return &waitingList; + } bool clientLogin(const Player* player); - std::size_t getClientSlot(const Player* player); - static std::size_t getTime(std::size_t slot); + uint32_t getClientSlot(const Player* player); + static uint32_t getTime(uint32_t slot); - private: - WaitingList(); + protected: + WaitList priorityWaitList; + WaitList waitList; - std::unique_ptr info; + static uint32_t getTimeout(uint32_t slot); + WaitListIterator findClient(const Player* player, uint32_t& slot); + static void cleanupList(WaitList& list); }; #endif diff --git a/src/weapons.cpp b/src/weapons.cpp deleted file mode 100644 index 2452f8a..0000000 --- a/src/weapons.cpp +++ /dev/null @@ -1,944 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "otpch.h" - -#include "combat.h" -#include "configmanager.h" -#include "game.h" -#include "pugicast.h" -#include "weapons.h" - -extern Game g_game; -extern Vocations g_vocations; -extern ConfigManager g_config; -extern Weapons* g_weapons; - -Weapons::Weapons() -{ - scriptInterface.initState(); -} - -Weapons::~Weapons() -{ - clear(false); -} - -const Weapon* Weapons::getWeapon(const Item* item) const -{ - if (!item) { - return nullptr; - } - - auto it = weapons.find(item->getID()); - if (it == weapons.end()) { - return nullptr; - } - return it->second; -} - -void Weapons::clear(bool fromLua) -{ - for (auto it = weapons.begin(); it != weapons.end(); ) { - if (fromLua == it->second->fromLua) { - it = weapons.erase(it); - } else { - ++it; - } - } - - reInitState(fromLua); -} - -LuaScriptInterface& Weapons::getScriptInterface() -{ - return scriptInterface; -} - -std::string Weapons::getScriptBaseName() const -{ - return "weapons"; -} - -void Weapons::loadDefaults() -{ - for (size_t i = 100, size = Item::items.size(); i < size; ++i) { - const ItemType& it = Item::items.getItemType(i); - if (it.id == 0 || weapons.find(i) != weapons.end()) { - continue; - } - - switch (it.weaponType) { - case WEAPON_AXE: - case WEAPON_SWORD: - case WEAPON_CLUB: { - WeaponMelee* weapon = new WeaponMelee(&scriptInterface); - weapon->configureWeapon(it); - weapons[i] = weapon; - break; - } - - case WEAPON_AMMO: - case WEAPON_DISTANCE: { - if (it.weaponType == WEAPON_DISTANCE && it.ammoType != AMMO_NONE) { - continue; - } - - WeaponDistance* weapon = new WeaponDistance(&scriptInterface); - weapon->configureWeapon(it); - weapons[i] = weapon; - break; - } - - default: - break; - } - } -} - -Event_ptr Weapons::getEvent(const std::string& nodeName) -{ - if (strcasecmp(nodeName.c_str(), "melee") == 0) { - return Event_ptr(new WeaponMelee(&scriptInterface)); - } else if (strcasecmp(nodeName.c_str(), "distance") == 0) { - return Event_ptr(new WeaponDistance(&scriptInterface)); - } else if (strcasecmp(nodeName.c_str(), "wand") == 0) { - return Event_ptr(new WeaponWand(&scriptInterface)); - } - return nullptr; -} - -bool Weapons::registerEvent(Event_ptr event, const pugi::xml_node&) -{ - Weapon* weapon = static_cast(event.release()); //event is guaranteed to be a Weapon - - auto result = weapons.emplace(weapon->getID(), weapon); - if (!result.second) { - std::cout << "[Warning - Weapons::registerEvent] Duplicate registered item with id: " << weapon->getID() << std::endl; - } - return result.second; -} - -bool Weapons::registerLuaEvent(Weapon* event) -{ - Weapon_ptr weapon{ event }; - weapons[weapon->getID()] = weapon.release(); - - return true; -} - -//monsters -int32_t Weapons::getMaxMeleeDamage(int32_t attackSkill, int32_t attackValue) -{ - return static_cast(std::ceil((attackSkill * (attackValue * 0.05)) + (attackValue * 0.5))); -} - -//players -int32_t Weapons::getMaxWeaponDamage(uint32_t level, int32_t attackSkill, int32_t attackValue, float attackFactor) -{ - return static_cast(std::round((level / 5) + (((((attackSkill / 4.) + 1) * (attackValue / 3.)) * 1.03) / attackFactor))); -} - -bool Weapon::configureEvent(const pugi::xml_node& node) -{ - pugi::xml_attribute attr; - if (!(attr = node.attribute("id"))) { - std::cout << "[Error - Weapon::configureEvent] Weapon without id." << std::endl; - return false; - } - id = pugi::cast(attr.value()); - - if ((attr = node.attribute("level"))) { - level = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("maglv")) || (attr = node.attribute("maglevel"))) { - magLevel = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("mana"))) { - mana = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("manapercent"))) { - manaPercent = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("soul"))) { - soul = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("prem"))) { - premium = attr.as_bool(); - } - - if ((attr = node.attribute("breakchance"))) { - breakChance = std::min(100, pugi::cast(attr.value())); - } - - if ((attr = node.attribute("action"))) { - action = getWeaponAction(asLowerCaseString(attr.as_string())); - if (action == WEAPONACTION_NONE) { - std::cout << "[Warning - Weapon::configureEvent] Unknown action " << attr.as_string() << std::endl; - } - } - - if ((attr = node.attribute("enabled"))) { - enabled = attr.as_bool(); - } - - if ((attr = node.attribute("unproperly"))) { - wieldUnproperly = attr.as_bool(); - } - - std::list vocStringList; - for (auto vocationNode : node.children()) { - if (!(attr = vocationNode.attribute("name"))) { - continue; - } - - int32_t vocationId = g_vocations.getVocationId(attr.as_string()); - if (vocationId != -1) { - vocWeaponMap[vocationId] = true; - int32_t promotedVocation = g_vocations.getPromotedVocation(vocationId); - if (promotedVocation != VOCATION_NONE) { - vocWeaponMap[promotedVocation] = true; - } - - if (vocationNode.attribute("showInDescription").as_bool(true)) { - vocStringList.push_back(asLowerCaseString(attr.as_string())); - } - } - } - - std::string vocationString; - for (const std::string& str : vocStringList) { - if (!vocationString.empty()) { - if (str != vocStringList.back()) { - vocationString.push_back(','); - vocationString.push_back(' '); - } else { - vocationString += " and "; - } - } - - vocationString += str; - vocationString.push_back('s'); - } - - uint32_t wieldInfo = 0; - if (getReqLevel() > 0) { - wieldInfo |= WIELDINFO_LEVEL; - } - - if (getReqMagLv() > 0) { - wieldInfo |= WIELDINFO_MAGLV; - } - - if (!vocationString.empty()) { - wieldInfo |= WIELDINFO_VOCREQ; - } - - if (isPremium()) { - wieldInfo |= WIELDINFO_PREMIUM; - } - - if (wieldInfo != 0) { - ItemType& it = Item::items.getItemType(id); - it.wieldInfo = wieldInfo; - it.vocationString = vocationString; - it.minReqLevel = getReqLevel(); - it.minReqMagicLevel = getReqMagLv(); - } - - configureWeapon(Item::items[id]); - return true; -} - -void Weapon::configureWeapon(const ItemType& it) -{ - id = it.id; -} - -std::string Weapon::getScriptEventName() const -{ - return "onUseWeapon"; -} - -int32_t Weapon::playerWeaponCheck(Player* player, Creature* target, uint8_t shootRange) const -{ - const Position& playerPos = player->getPosition(); - const Position& targetPos = target->getPosition(); - if (playerPos.z != targetPos.z) { - return 0; - } - - if (std::max(Position::getDistanceX(playerPos, targetPos), Position::getDistanceY(playerPos, targetPos)) > shootRange) { - return 0; - } - - if (!player->hasFlag(PlayerFlag_IgnoreWeaponCheck)) { - if (!enabled) { - return 0; - } - - if (player->getMana() < getManaCost(player)) { - return 0; - } - - if (player->getHealth() < getHealthCost(player)) { - return 0; - } - - if (player->getSoul() < soul) { - return 0; - } - - if (isPremium() && !player->isPremium()) { - return 0; - } - - if (!vocWeaponMap.empty()) { - if (vocWeaponMap.find(player->getVocationId()) == vocWeaponMap.end()) { - return 0; - } - } - - int32_t damageModifier = 100; - if (player->getLevel() < getReqLevel()) { - damageModifier = (isWieldedUnproperly() ? damageModifier / 2 : 0); - } - - if (player->getMagicLevel() < getReqMagLv()) { - damageModifier = (isWieldedUnproperly() ? damageModifier / 2 : 0); - } - return damageModifier; - } - - return 100; -} - -bool Weapon::useWeapon(Player* player, Item* item, Creature* target) const -{ - int32_t damageModifier = playerWeaponCheck(player, target, item->getShootRange()); - if (damageModifier == 0) { - return false; - } - - internalUseWeapon(player, item, target, damageModifier); - return true; -} - -bool Weapon::useFist(Player* player, Creature* target) -{ - if (!Position::areInRange<1, 1>(player->getPosition(), target->getPosition())) { - return false; - } - - float attackFactor = player->getAttackFactor(); - int32_t attackSkill = player->getSkillLevel(SKILL_FIST); - int32_t attackValue = 7; - - int32_t maxDamage = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor); - - CombatParams params; - params.combatType = COMBAT_PHYSICALDAMAGE; - params.blockedByArmor = true; - params.blockedByShield = true; - - CombatDamage damage; - damage.origin = ORIGIN_MELEE; - damage.primary.type = params.combatType; - damage.primary.value = -normal_random(0, maxDamage); - - Combat::doCombatHealth(player, target, damage, params); - if (!player->hasFlag(PlayerFlag_NotGainSkill) && player->getAddAttackSkill()) { - player->addSkillAdvance(SKILL_FIST, 1); - } - - return true; -} - -void Weapon::internalUseWeapon(Player* player, Item* item, Creature* target, int32_t damageModifier) const -{ - if (scripted) { - LuaVariant var; - var.type = VARIANT_NUMBER; - var.number = target->getID(); - executeUseWeapon(player, var); - } else { - CombatDamage damage; - WeaponType_t weaponType = item->getWeaponType(); - if (weaponType == WEAPON_AMMO || weaponType == WEAPON_DISTANCE) { - damage.origin = ORIGIN_RANGED; - } else { - damage.origin = ORIGIN_MELEE; - } - damage.primary.type = params.combatType; - damage.primary.value = (getWeaponDamage(player, target, item) * damageModifier) / 100; - damage.secondary.type = getElementType(); - damage.secondary.value = getElementDamage(player, target, item); - Combat::doCombatHealth(player, target, damage, params); - } - - onUsedWeapon(player, item, target->getTile()); -} - -void Weapon::internalUseWeapon(Player* player, Item* item, Tile* tile) const -{ - if (scripted) { - LuaVariant var; - var.type = VARIANT_TARGETPOSITION; - var.pos = tile->getPosition(); - executeUseWeapon(player, var); - } else { - Combat::postCombatEffects(player, tile->getPosition(), params); - g_game.addMagicEffect(tile->getPosition(), CONST_ME_POFF); - } - - onUsedWeapon(player, item, tile); -} - -void Weapon::onUsedWeapon(Player* player, Item* item, Tile* destTile) const -{ - if (!player->hasFlag(PlayerFlag_NotGainSkill)) { - skills_t skillType; - uint32_t skillPoint; - if (getSkillType(player, item, skillType, skillPoint)) { - player->addSkillAdvance(skillType, skillPoint); - } - } - - uint32_t manaCost = getManaCost(player); - if (manaCost != 0) { - player->addManaSpent(manaCost); - player->changeMana(-static_cast(manaCost)); - } - - uint32_t healthCost = getHealthCost(player); - if (healthCost != 0) { - player->changeHealth(-static_cast(healthCost)); - } - - if (!player->hasFlag(PlayerFlag_HasInfiniteSoul) && soul > 0) { - player->changeSoul(-static_cast(soul)); - } - - if (breakChance != 0 && uniform_random(1, 100) <= breakChance) { - Weapon::decrementItemCount(item); - return; - } - - switch (action) { - case WEAPONACTION_REMOVECOUNT: - Weapon::decrementItemCount(item); - break; - - case WEAPONACTION_REMOVECHARGE: { - uint16_t charges = item->getCharges(); - if (charges != 0) { - g_game.transformItem(item, item->getID(), charges - 1); - } - break; - } - - case WEAPONACTION_MOVE: - g_game.internalMoveItem(item->getParent(), destTile, INDEX_WHEREEVER, item, 1, nullptr, FLAG_NOLIMIT); - break; - - default: - break; - } -} - -uint32_t Weapon::getManaCost(const Player* player) const -{ - if (mana != 0) { - return mana; - } - - if (manaPercent == 0) { - return 0; - } - - return (player->getMaxMana() * manaPercent) / 100; -} - -int32_t Weapon::getHealthCost(const Player* player) const -{ - if (health != 0) { - return health; - } - - if (healthPercent == 0) { - return 0; - } - - return (player->getMaxHealth() * healthPercent) / 100; -} - -bool Weapon::executeUseWeapon(Player* player, const LuaVariant& var) const -{ - //onUseWeapon(player, var) - if (!scriptInterface->reserveScriptEnv()) { - std::cout << "[Error - Weapon::executeUseWeapon] Call stack overflow" << std::endl; - return false; - } - - ScriptEnvironment* env = scriptInterface->getScriptEnv(); - env->setScriptId(scriptId, scriptInterface); - - lua_State* L = scriptInterface->getLuaState(); - - scriptInterface->pushFunction(scriptId); - LuaScriptInterface::pushUserdata(L, player); - LuaScriptInterface::setMetatable(L, -1, "Player"); - scriptInterface->pushVariant(L, var); - - return scriptInterface->callFunction(2); -} - -void Weapon::decrementItemCount(Item* item) -{ - uint16_t count = item->getItemCount(); - if (count > 1) { - g_game.transformItem(item, item->getID(), count - 1); - } else { - g_game.internalRemoveItem(item); - } -} - -WeaponMelee::WeaponMelee(LuaScriptInterface* interface) : - Weapon(interface) -{ - params.blockedByArmor = true; - params.blockedByShield = true; - params.combatType = COMBAT_PHYSICALDAMAGE; -} - -void WeaponMelee::configureWeapon(const ItemType& it) -{ - if (it.abilities) { - elementType = it.abilities->elementType; - elementDamage = it.abilities->elementDamage; - params.aggressive = true; - params.useCharges = true; - } else { - elementType = COMBAT_NONE; - elementDamage = 0; - } - Weapon::configureWeapon(it); -} - -bool WeaponMelee::useWeapon(Player* player, Item* item, Creature* target) const -{ - int32_t damageModifier = playerWeaponCheck(player, target, item->getShootRange()); - if (damageModifier == 0) { - return false; - } - - internalUseWeapon(player, item, target, damageModifier); - return true; -} - -bool WeaponMelee::getSkillType(const Player* player, const Item* item, - skills_t& skill, uint32_t& skillpoint) const -{ - if (player->getAddAttackSkill() && player->getLastAttackBlockType() != BLOCK_IMMUNITY) { - skillpoint = 1; - } else { - skillpoint = 0; - } - - WeaponType_t weaponType = item->getWeaponType(); - switch (weaponType) { - case WEAPON_SWORD: { - skill = SKILL_SWORD; - return true; - } - - case WEAPON_CLUB: { - skill = SKILL_CLUB; - return true; - } - - case WEAPON_AXE: { - skill = SKILL_AXE; - return true; - } - - default: - break; - } - return false; -} - -int32_t WeaponMelee::getElementDamage(const Player* player, const Creature*, const Item* item) const -{ - if (elementType == COMBAT_NONE) { - return 0; - } - - int32_t attackSkill = player->getWeaponSkill(item); - int32_t attackValue = elementDamage; - float attackFactor = player->getAttackFactor(); - - int32_t maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor); - return -normal_random(0, static_cast(maxValue * player->getVocation()->meleeDamageMultiplier)); -} - -int32_t WeaponMelee::getWeaponDamage(const Player* player, const Creature*, const Item* item, bool maxDamage /*= false*/) const -{ - int32_t attackSkill = player->getWeaponSkill(item); - int32_t attackValue = std::max(0, item->getAttack()); - float attackFactor = player->getAttackFactor(); - - int32_t maxValue = static_cast(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor) * player->getVocation()->meleeDamageMultiplier); - if (maxDamage) { - return -maxValue; - } - - return -normal_random(0, maxValue); -} - -WeaponDistance::WeaponDistance(LuaScriptInterface* interface) : - Weapon(interface) -{ - params.blockedByArmor = true; - params.combatType = COMBAT_PHYSICALDAMAGE; -} - -void WeaponDistance::configureWeapon(const ItemType& it) -{ - params.distanceEffect = it.shootType; - - if (it.abilities) { - elementType = it.abilities->elementType; - elementDamage = it.abilities->elementDamage; - params.aggressive = true; - params.useCharges = true; - } else { - elementType = COMBAT_NONE; - elementDamage = 0; - } - - Weapon::configureWeapon(it); -} - -bool WeaponDistance::useWeapon(Player* player, Item* item, Creature* target) const -{ - int32_t damageModifier; - const ItemType& it = Item::items[id]; - if (it.weaponType == WEAPON_AMMO) { - Item* mainWeaponItem = player->getWeapon(true); - const Weapon* mainWeapon = g_weapons->getWeapon(mainWeaponItem); - if (mainWeapon) { - damageModifier = mainWeapon->playerWeaponCheck(player, target, mainWeaponItem->getShootRange()); - } else { - damageModifier = playerWeaponCheck(player, target, mainWeaponItem->getShootRange()); - } - } else { - damageModifier = playerWeaponCheck(player, target, item->getShootRange()); - } - - if (damageModifier == 0) { - return false; - } - - int32_t chance; - if (it.hitChance == 0) { - //hit chance is based on distance to target and distance skill - uint32_t skill = player->getSkillLevel(SKILL_DISTANCE); - const Position& playerPos = player->getPosition(); - const Position& targetPos = target->getPosition(); - uint32_t distance = std::max(Position::getDistanceX(playerPos, targetPos), Position::getDistanceY(playerPos, targetPos)); - - uint32_t maxHitChance; - if (it.maxHitChance != -1) { - maxHitChance = it.maxHitChance; - } else if (it.ammoType != AMMO_NONE) { - //hit chance on two-handed weapons is limited to 90% - maxHitChance = 90; - } else { - //one-handed is set to 75% - maxHitChance = 75; - } - - if (maxHitChance == 75) { - //chance for one-handed weapons - switch (distance) { - case 1: - case 5: - chance = std::min(skill, 74) + 1; - break; - case 2: - chance = static_cast(std::min(skill, 28) * 2.40f) + 8; - break; - case 3: - chance = static_cast(std::min(skill, 45) * 1.55f) + 6; - break; - case 4: - chance = static_cast(std::min(skill, 58) * 1.25f) + 3; - break; - case 6: - chance = static_cast(std::min(skill, 90) * 0.80f) + 3; - break; - case 7: - chance = static_cast(std::min(skill, 104) * 0.70f) + 2; - break; - default: - chance = it.hitChance; - break; - } - } else if (maxHitChance == 90) { - //formula for two-handed weapons - switch (distance) { - case 1: - case 5: - chance = static_cast(std::min(skill, 74) * 1.20f) + 1; - break; - case 2: - chance = static_cast(std::min(skill, 28) * 3.20f); - break; - case 3: - chance = std::min(skill, 45) * 2; - break; - case 4: - chance = static_cast(std::min(skill, 58) * 1.55f); - break; - case 6: - case 7: - chance = std::min(skill, 90); - break; - default: - chance = it.hitChance; - break; - } - } else if (maxHitChance == 100) { - switch (distance) { - case 1: - case 5: - chance = static_cast(std::min(skill, 73) * 1.35f) + 1; - break; - case 2: - chance = static_cast(std::min(skill, 30) * 3.20f) + 4; - break; - case 3: - chance = static_cast(std::min(skill, 48) * 2.05f) + 2; - break; - case 4: - chance = static_cast(std::min(skill, 65) * 1.50f) + 2; - break; - case 6: - chance = static_cast(std::min(skill, 87) * 1.20f) - 4; - break; - case 7: - chance = static_cast(std::min(skill, 90) * 1.10f) + 1; - break; - default: - chance = it.hitChance; - break; - } - } else { - chance = maxHitChance; - } - } else { - chance = it.hitChance; - } - - if (item->getWeaponType() == WEAPON_AMMO) { - Item* bow = player->getWeapon(true); - if (bow && bow->getHitChance() != 0) { - chance += bow->getHitChance(); - } - } - - if (chance >= uniform_random(1, 100)) { - Weapon::internalUseWeapon(player, item, target, damageModifier); - } else { - //miss target - Tile* destTile = target->getTile(); - - if (!Position::areInRange<1, 1, 0>(player->getPosition(), target->getPosition())) { - static std::vector> destList { - {-1, -1}, {0, -1}, {1, -1}, - {-1, 0}, {0, 0}, {1, 0}, - {-1, 1}, {0, 1}, {1, 1} - }; - std::shuffle(destList.begin(), destList.end(), getRandomGenerator()); - - Position destPos = target->getPosition(); - - for (const auto& dir : destList) { - // Blocking tiles or tiles without ground ain't valid targets for spears - Tile* tmpTile = g_game.map.getTile(destPos.x + dir.first, destPos.y + dir.second, destPos.z); - if (tmpTile && !tmpTile->hasFlag(TILESTATE_IMMOVABLEBLOCKSOLID) && tmpTile->getGround() != nullptr) { - destTile = tmpTile; - break; - } - } - } - - Weapon::internalUseWeapon(player, item, destTile); - } - return true; -} - -int32_t WeaponDistance::getElementDamage(const Player* player, const Creature* target, const Item* item) const -{ - if (elementType == COMBAT_NONE) { - return 0; - } - - int32_t attackValue = elementDamage; - if (item->getWeaponType() == WEAPON_AMMO) { - Item* weapon = player->getWeapon(true); - if (weapon) { - attackValue += weapon->getAttack(); - } - } - - int32_t attackSkill = player->getSkillLevel(SKILL_DISTANCE); - float attackFactor = player->getAttackFactor(); - - int32_t minValue = 0; - int32_t maxValue = Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor); - if (target) { - if (target->getPlayer()) { - minValue = static_cast(std::ceil(player->getLevel() * 0.1)); - } else { - minValue = static_cast(std::ceil(player->getLevel() * 0.2)); - } - } - - return -normal_random(minValue, static_cast(maxValue * player->getVocation()->distDamageMultiplier)); -} - -int32_t WeaponDistance::getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage /*= false*/) const -{ - int32_t attackValue = item->getAttack(); - - if (item->getWeaponType() == WEAPON_AMMO) { - Item* weapon = player->getWeapon(true); - if (weapon) { - attackValue += weapon->getAttack(); - } - } - - int32_t attackSkill = player->getSkillLevel(SKILL_DISTANCE); - float attackFactor = player->getAttackFactor(); - - int32_t maxValue = static_cast(Weapons::getMaxWeaponDamage(player->getLevel(), attackSkill, attackValue, attackFactor) * player->getVocation()->distDamageMultiplier); - if (maxDamage) { - return -maxValue; - } - - int32_t minValue; - if (target) { - if (target->getPlayer()) { - minValue = static_cast(std::ceil(player->getLevel() * 0.1)); - } else { - minValue = static_cast(std::ceil(player->getLevel() * 0.2)); - } - } else { - minValue = 0; - } - return -normal_random(minValue, maxValue); -} - -bool WeaponDistance::getSkillType(const Player* player, const Item*, skills_t& skill, uint32_t& skillpoint) const -{ - skill = SKILL_DISTANCE; - - if (player->getAddAttackSkill()) { - switch (player->getLastAttackBlockType()) { - case BLOCK_NONE: { - skillpoint = 2; - break; - } - - case BLOCK_DEFENSE: - case BLOCK_ARMOR: { - skillpoint = 1; - break; - } - - default: - skillpoint = 0; - break; - } - } else { - skillpoint = 0; - } - return true; -} - -bool WeaponWand::configureEvent(const pugi::xml_node& node) -{ - if (!Weapon::configureEvent(node)) { - return false; - } - - pugi::xml_attribute attr; - if ((attr = node.attribute("min"))) { - minChange = pugi::cast(attr.value()); - } - - if ((attr = node.attribute("max"))) { - maxChange = pugi::cast(attr.value()); - } - - attr = node.attribute("type"); - if (!attr) { - return true; - } - - std::string tmpStrValue = asLowerCaseString(attr.as_string()); - if (tmpStrValue == "earth") { - params.combatType = COMBAT_EARTHDAMAGE; - } else if (tmpStrValue == "ice") { - params.combatType = COMBAT_ICEDAMAGE; - } else if (tmpStrValue == "energy") { - params.combatType = COMBAT_ENERGYDAMAGE; - } else if (tmpStrValue == "fire") { - params.combatType = COMBAT_FIREDAMAGE; - } else if (tmpStrValue == "death") { - params.combatType = COMBAT_DEATHDAMAGE; - } else if (tmpStrValue == "holy") { - params.combatType = COMBAT_HOLYDAMAGE; - } else { - std::cout << "[Warning - WeaponWand::configureEvent] Type \"" << attr.as_string() << "\" does not exist." << std::endl; - } - return true; -} - -void WeaponWand::configureWeapon(const ItemType& it) -{ - params.distanceEffect = it.shootType; - - Weapon::configureWeapon(it); -} - -int32_t WeaponWand::getWeaponDamage(const Player*, const Creature*, const Item*, bool maxDamage /*= false*/) const -{ - if (maxDamage) { - return -maxChange; - } - return -normal_random(minChange, maxChange); -} diff --git a/src/weapons.h b/src/weapons.h deleted file mode 100644 index e889784..0000000 --- a/src/weapons.h +++ /dev/null @@ -1,311 +0,0 @@ -/** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef FS_WEAPONS_H_69D1993478AA42948E24C0B90B8F5BF5 -#define FS_WEAPONS_H_69D1993478AA42948E24C0B90B8F5BF5 - -#include "luascript.h" -#include "player.h" -#include "baseevents.h" -#include "combat.h" -#include "const.h" -#include "vocation.h" - -extern Vocations g_vocations; - -class Weapon; -class WeaponMelee; -class WeaponDistance; -class WeaponWand; - -using Weapon_ptr = std::unique_ptr; - -class Weapons final : public BaseEvents -{ - public: - Weapons(); - ~Weapons(); - - // non-copyable - Weapons(const Weapons&) = delete; - Weapons& operator=(const Weapons&) = delete; - - void loadDefaults(); - const Weapon* getWeapon(const Item* item) const; - - static int32_t getMaxMeleeDamage(int32_t attackSkill, int32_t attackValue); - static int32_t getMaxWeaponDamage(uint32_t level, int32_t attackSkill, int32_t attackValue, float attackFactor); - - bool registerLuaEvent(Weapon* event); - void clear(bool fromLua) override final; - - private: - LuaScriptInterface& getScriptInterface() override; - std::string getScriptBaseName() const override; - Event_ptr getEvent(const std::string& nodeName) override; - bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; - - std::map weapons; - - LuaScriptInterface scriptInterface { "Weapon Interface" }; -}; - -class Weapon : public Event -{ - public: - explicit Weapon(LuaScriptInterface* interface) : Event(interface) {} - - bool configureEvent(const pugi::xml_node& node) override; - bool loadFunction(const pugi::xml_attribute&, bool) final { - return true; - } - virtual void configureWeapon(const ItemType& it); - virtual bool interruptSwing() const { - return false; - } - - int32_t playerWeaponCheck(Player* player, Creature* target, uint8_t shootRange) const; - static bool useFist(Player* player, Creature* target); - virtual bool useWeapon(Player* player, Item* item, Creature* target) const; - - virtual int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const = 0; - virtual int32_t getElementDamage(const Player* player, const Creature* target, const Item* item) const = 0; - virtual CombatType_t getElementType() const = 0; - - uint16_t getID() const { - return id; - } - void setID(uint16_t newId) { - id = newId; - } - - uint32_t getReqLevel() const { - return level; - } - void setRequiredLevel(uint32_t reqlvl) { - level = reqlvl; - } - - uint32_t getReqMagLv() const { - return magLevel; - } - void setRequiredMagLevel(uint32_t reqlvl) { - magLevel = reqlvl; - } - - bool isPremium() const { - return premium; - } - void setNeedPremium(bool prem) { - premium = prem; - } - - bool isWieldedUnproperly() const { - return wieldUnproperly; - } - void setWieldUnproperly(bool unproperly) { - wieldUnproperly = unproperly; - } - - uint32_t getMana() const { - return mana; - } - void setMana(uint32_t m) { - mana = m; - } - - uint32_t getManaPercent() const { - return manaPercent; - } - void setManaPercent(uint32_t m) { - manaPercent = m; - } - - int32_t getHealth() const { - return health; - } - void setHealth(int32_t h) { - health = h; - } - - uint32_t getHealthPercent() const { - return healthPercent; - } - void setHealthPercent(uint32_t m) { - healthPercent = m; - } - - uint32_t getSoul() const { - return soul; - } - void setSoul(uint32_t s) { - soul = s; - } - - uint8_t getBreakChance() const { - return breakChance; - } - void setBreakChance(uint8_t b) { - breakChance = b; - } - - bool isEnabled() const { - return enabled; - } - void setIsEnabled(bool e) { - enabled = e; - } - - uint32_t getWieldInfo() const { - return wieldInfo; - } - void setWieldInfo(uint32_t info) { - wieldInfo |= info; - } - - void addVocWeaponMap(std::string vocName) { - int32_t vocationId = g_vocations.getVocationId(vocName); - if (vocationId != -1) { - vocWeaponMap[vocationId] = true; - } - } - - const std::string& getVocationString() const { - return vocationString; - } - void setVocationString(const std::string& str) { - vocationString = str; - } - - WeaponAction_t action = WEAPONACTION_NONE; - CombatParams params; - WeaponType_t weaponType; - std::map vocWeaponMap; - - protected: - void internalUseWeapon(Player* player, Item* item, Creature* target, int32_t damageModifier) const; - void internalUseWeapon(Player* player, Item* item, Tile* tile) const; - - uint16_t id = 0; - - private: - virtual bool getSkillType(const Player*, const Item*, skills_t&, uint32_t&) const { - return false; - } - - uint32_t getManaCost(const Player* player) const; - int32_t getHealthCost(const Player* player) const; - - uint32_t level = 0; - uint32_t magLevel = 0; - uint32_t mana = 0; - uint32_t manaPercent = 0; - uint32_t health = 0; - uint32_t healthPercent = 0; - uint32_t soul = 0; - uint32_t wieldInfo = WIELDINFO_NONE; - uint8_t breakChance = 0; - bool enabled = true; - bool premium = false; - bool wieldUnproperly = false; - std::string vocationString = ""; - - std::string getScriptEventName() const override final; - - bool executeUseWeapon(Player* player, const LuaVariant& var) const; - void onUsedWeapon(Player* player, Item* item, Tile* destTile) const; - - static void decrementItemCount(Item* item); - - friend class Combat; -}; - -class WeaponMelee final : public Weapon -{ - public: - explicit WeaponMelee(LuaScriptInterface* interface); - - void configureWeapon(const ItemType& it) override; - - bool useWeapon(Player* player, Item* item, Creature* target) const override; - - int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const override; - int32_t getElementDamage(const Player* player, const Creature* target, const Item* item) const override; - CombatType_t getElementType() const override { return elementType; } - - private: - bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint32_t& skillpoint) const override; - - CombatType_t elementType = COMBAT_NONE; - uint16_t elementDamage = 0; -}; - -class WeaponDistance final : public Weapon -{ - public: - explicit WeaponDistance(LuaScriptInterface* interface); - - void configureWeapon(const ItemType& it) override; - bool interruptSwing() const override { - return true; - } - - bool useWeapon(Player* player, Item* item, Creature* target) const override; - - int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const override; - int32_t getElementDamage(const Player* player, const Creature* target, const Item* item) const override; - CombatType_t getElementType() const override { return elementType; } - - private: - bool getSkillType(const Player* player, const Item* item, skills_t& skill, uint32_t& skillpoint) const override; - - CombatType_t elementType = COMBAT_NONE; - uint16_t elementDamage = 0; -}; - -class WeaponWand final : public Weapon -{ - public: - explicit WeaponWand(LuaScriptInterface* interface) : Weapon(interface) {} - - bool configureEvent(const pugi::xml_node& node) override; - void configureWeapon(const ItemType& it) override; - - int32_t getWeaponDamage(const Player* player, const Creature* target, const Item* item, bool maxDamage = false) const override; - int32_t getElementDamage(const Player*, const Creature*, const Item*) const override { return 0; } - CombatType_t getElementType() const override { return COMBAT_NONE; } - - void setMinChange(int32_t change) { - minChange = change; - } - - void setMaxChange(int32_t change) { - maxChange = change; - } - - private: - bool getSkillType(const Player*, const Item*, skills_t&, uint32_t&) const override { - return false; - } - - int32_t minChange = 0; - int32_t maxChange = 0; -}; - -#endif diff --git a/src/wildcardtree.cpp b/src/wildcardtree.cpp index ec182b6..fa5eef8 100644 --- a/src/wildcardtree.cpp +++ b/src/wildcardtree.cpp @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -119,7 +119,7 @@ ReturnValue WildcardTreeNode::findOne(const std::string& query, std::string& res if (size == 0) { return RETURNVALUE_NOERROR; } else if (size > 1 || cur->breakpoint) { - return RETURNVALUE_NAMEISTOOAMBIGUOUS; + return RETURNVALUE_NAMEISTOOAMBIGIOUS; } auto it = cur->children.begin(); diff --git a/src/wildcardtree.h b/src/wildcardtree.h index 38aa7ed..2d16981 100644 --- a/src/wildcardtree.h +++ b/src/wildcardtree.h @@ -1,6 +1,6 @@ /** - * The Forgotten Server - a free and open-source MMORPG server emulator - * Copyright (C) 2019 Mark Samman + * Tibia GIMUD Server - a free and open-source MMORPG server emulator + * Copyright (C) 2019 Sabrehaven and Mark Samman * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by