Revert "commit newest tfs branch only for compare"

This reverts commit 1f7dcd73477eae559e0f5915aece1388a54d0b76.
This commit is contained in:
ErikasKontenis 2020-01-02 19:40:35 +02:00
parent 1f7dcd7347
commit 447b858551
160 changed files with 10972 additions and 24876 deletions

View File

@ -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)
)

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<std::string> characters;
std::string name;
std::string key;
time_t lastDay = 0;
uint32_t id = 0;
uint16_t premiumDays = 0;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Action*> 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<Action*>(event.release())}; //event is guaranteed to be an Action
Action* action = static_cast<Action*>(event); //event is guaranteed to be an Action
pugi::xml_attribute attr;
if ((attr = node.attribute("itemid"))) {
uint16_t id = pugi::cast<uint16_t>(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<uint16_t>(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<uint16_t>(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<uint16_t>(attr.value());
uint16_t iterUid = fromUid;
uint16_t toUid = pugi::cast<uint16_t>(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<uint16_t>(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<uint16_t>(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);
}
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<bool(Player* player, Item* item, const Posi
class Action : public Event
{
public:
explicit Action(const Action* copy);
explicit Action(LuaScriptInterface* interface);
bool configureEvent(const pugi::xml_node& node) override;
bool loadFunction(const pugi::xml_attribute& attr, bool isScripted) override;
//scripting
virtual bool executeUse(Player* player, Item* item, const Position& fromPosition,
Thing* target, const Position& toPosition, bool isHotkey);
//
bool getAllowFarUse() const {
return allowFarUse;
@ -118,25 +119,22 @@ class Actions final : public BaseEvents
ReturnValue canUse(const Player* player, const Position& pos, const Item* item);
ReturnValue canUseFar(const Creature* creature, const Position& toPos, bool checkLineOfSight, bool checkFloor);
bool registerLuaEvent(Action* event);
void clear(bool fromLua) override final;
private:
ReturnValue internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey);
static void showUseHotkeyMessage(Player* player, const Item* item, uint32_t count);
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 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;
using ActionUseMap = std::map<uint16_t, Action>;
typedef std::map<uint16_t, Action*> ActionUseMap;
ActionUseMap useItemMap;
ActionUseMap uniqueItemMap;
ActionUseMap actionItemMap;
Action* getAction(const Item* item);
void clearMap(ActionUseMap& map, bool fromLua);
void clearMap(ActionUseMap& map);
LuaScriptInterface scriptInterface;
};

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<std::recursive_mutex> 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<time_t>("banned_at") << ',' << expiresAt << ',' << result->getNumber<uint32_t>("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<time_t>("banned_at") << ',' << expiresAt << ',' << result->getNumber<uint32_t>("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<int64_t>("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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t, ConnectBlock>;
typedef std::map<uint32_t, ConnectBlock> 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);
};

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Event>;
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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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];

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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);

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint16_t> 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<uint16_t>(channelNode.attribute("id").value());
std::string channelName = channelNode.attribute("name").as_string();
bool isPublic = channelNode.attribute("public").as_bool();
ChatChannel channel(pugi::cast<uint16_t>(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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t, Player*>;
using InvitedMap = std::map<uint32_t, const Player*>;
typedef std::map<uint32_t, Player*> UsersMap;
typedef std::map<uint32_t, const Player*> 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<ChatChannel*>;
typedef std::list<ChatChannel*> ChannelList;
class Chat
{

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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> 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<void(Creature*, Creature*, const CombatParams&, CombatDamage*)>;
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<uint32_t>& 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<uint32_t>& 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:

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<int64_t>::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<int32_t>& 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<IntervalInfo> 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <luajit/lua.hpp>
#else
#include <lua.hpp>
#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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <luajit/lua.hpp>
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] = {};

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t>();
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<Connection>(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<std::recursive_mutex> lockClass(connectionLock);

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Protocol>;
typedef std::shared_ptr<Protocol> Protocol_ptr;
class OutputMessage;
using OutputMessage_ptr = std::shared_ptr<OutputMessage>;
typedef std::shared_ptr<OutputMessage> OutputMessage_ptr;
class Connection;
using Connection_ptr = std::shared_ptr<Connection> ;
using ConnectionWeak_ptr = std::weak_ptr<Connection>;
typedef std::shared_ptr<Connection> Connection_ptr;
typedef std::weak_ptr<Connection> ConnectionWeak_ptr;
class ServiceBase;
using Service_ptr = std::shared_ptr<ServiceBase>;
typedef std::shared_ptr<ServiceBase> Service_ptr;
class ServicePort;
using ServicePort_ptr = std::shared_ptr<ServicePort>;
using ConstServicePort_ptr = std::shared_ptr<const ServicePort>;
typedef std::shared_ptr<ServicePort> ServicePort_ptr;
typedef std::shared_ptr<const ServicePort> 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<Connection_ptr> connections;
@ -78,8 +78,12 @@ class Connection : public std::enable_shared_from_this<Connection>
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<Connection>
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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint64_t>(1) << 35,
PlayerFlag_CannotBeMuted = static_cast<uint64_t>(1) << 36,
PlayerFlag_IsAlwaysPremium = static_cast<uint64_t>(1) << 37,
PlayerFlag_SpecialMoveUse = static_cast<uint64_t>(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))

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<const Player*>(getParent()) == nullptr;
return dynamic_cast<const Container*>(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<const Inbox*>(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<int32_t>(capacity())) {
}
else if (index >= static_cast<int32_t>(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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<const Container*> 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<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& 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<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& 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;
};

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Direction>(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);
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Condition*>;
using CreatureEventList = std::list<CreatureEvent*>;
typedef std::list<Condition*> ConditionList;
typedef std::list<CreatureEvent*> 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<Creature*>& 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<Tile*>(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<uint32_t, CountBlock_t>;
typedef std::map<uint32_t, CountBlock_t> CountMap;
CountMap damageMap;
std::list<Creature*> 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<CreatureEvent*>(event.release())}; //event is guaranteed to be a CreatureEvent
CreatureEvent* creatureEvent = static_cast<CreatureEvent*>(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<int32_t>(L, -4));
damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
damage.secondary.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
}
else {
damage.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
damage.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
damage.min = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
damage.max = LuaScriptInterface::getNumber<CombatType_t>(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<int32_t>(L, -4);
damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
damage.secondary.value = LuaScriptInterface::getNumber<int32_t>(L, -2);
damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
lua_pop(L, 4);
}
else {
damage = LuaScriptInterface::getCombatDamage(L);
}
scriptInterface->resetScriptEnv();

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<CreatureEvent>;
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<std::string, CreatureEvent>;
CreatureEventMap creatureEvents;
typedef std::map<std::string, CreatureEvent*> 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 {

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <mysql/mysql.h>
class DBResult;
using DBResult_ptr = std::shared_ptr<DBResult>;
typedef std::shared_ptr<DBResult> 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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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());
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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);

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<void(DBResult_ptr, bool)> callback/* = nullptr*/, bool store/* = false*/)
void DatabaseTasks::addTask(const std::string& query, const std::function<void(DBResult_ptr, bool)>& 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<std::mutex> 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();
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<void(DBResult_ptr, bool)>&& callback, bool store) :
DatabaseTask(std::string query, std::function<void(DBResult_ptr, bool)> callback, bool store) :
query(std::move(query)), callback(std::move(callback)), store(store) {}
std::string query;
@ -42,7 +42,7 @@ class DatabaseTasks : public ThreadHolder<DatabaseTasks>
void flush();
void shutdown();
void addTask(std::string query, std::function<void(DBResult_ptr, bool)> callback = nullptr, bool store = false);
void addTask(const std::string& query, const std::function<void(DBResult_ptr, bool)>& callback = nullptr, bool store = false);
void threadMain();
private:

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,82 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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;
}

View File

@ -1,57 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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);
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<std::pair<std::string, uint8_t>> 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<MarketOffer>;
using HistoryMarketOfferList = std::list<HistoryMarketOffer>;
using ShopInfoList = std::list<ShopInfo>;
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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
*
* 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<std::string> 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<Creature>(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<Creature>(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<ReturnValue>(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<Creature>(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<ReturnValue>(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<Party>(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<Party>(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<Party>(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<Party>(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<uint64_t>(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<Player>(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<Player>(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<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
} else if (Item* item = thing->getItem()) {
}
else if (Item* item = thing->getItem()) {
LuaScriptInterface::pushUserdata<Item>(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<Player>(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<Player>(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<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushUserdata<const ItemType>(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<Player>(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<Player>(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<Player>(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<Player>(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<Player>(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<Player>(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<Player>(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<Player>(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<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
@ -781,7 +738,8 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
if (source) {
LuaScriptInterface::pushUserdata<Creature>(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<uint64_t>(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<Player>(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<uint64_t>(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<Player>(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<uint64_t>(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<Monster>(L, monster);
LuaScriptInterface::setMetatable(L, -1, "Monster");
LuaScriptInterface::pushUserdata<Container>(L, corpse);
LuaScriptInterface::setMetatable(L, -1, "Container");
return scriptInterface.callVoidFunction(2);
}
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <stack>
#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<Node*, std::vector<Node*>>;
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<uint32_t>(32768, std::max<uint32_t>(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<uint8_t>(*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<uint8_t>(*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<const char*>(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<char>(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<int32_t>(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<long>(cached_data[i].base) - base_pos) > static_cast<long>(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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <limits>
#include <vector>
#include <boost/iostreams/device/mapped_file.hpp>
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<char, 4>;
struct Node
class FileLoader
{
using ChildrenVector = std::vector<Node>;
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<uint32_t>::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<char> 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<char> buffer;
};

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Direction>& 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<Item*> 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<uint32_t, RuleViolation>& getRuleViolationReports() const { return ruleViolations; }
const std::unordered_map<uint32_t, Player*>& getPlayers() const { return players; }
const std::map<uint32_t, Npc*>& 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<Tile*, Container*> browseFields;
void internalRemoveItems(std::vector<Item*> 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<Item*> 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<uint32_t, RuleViolation> ruleViolations;
std::unordered_map<uint32_t, Player*> players;
std::unordered_map<std::string, Player*> mappedPlayerNames;
std::unordered_map<uint32_t, Guild*> guilds;
std::unordered_map<uint16_t, Item*> uniqueItems;
std::map<uint32_t, uint32_t> stages;
std::list<Item*> decayItems[EVENT_DECAY_BUCKETS];
std::list<Creature*> checkCreatureLists[EVENT_CREATURECOUNT];
std::forward_list<Item*> toDecayItems;
std::vector<Creature*> ToReleaseCreatures;
std::vector<Item*> ToReleaseItems;
@ -540,8 +526,6 @@ class Game
std::map<uint32_t, BedItem*> bedSleepersMap;
ModalWindow offlineTrainingWindow { std::numeric_limits<uint32_t>::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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<GlobalEvent*>(event.release())}; //event is guaranteed to be a GlobalEvent
GlobalEvent* globalEvent = static_cast<GlobalEvent*>(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<int64_t>::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<int64_t>::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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<GlobalEvent>;
using GlobalEventMap = std::map<std::string, GlobalEvent>;
typedef std::map<std::string, GlobalEvent*> 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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<std::string, PlayerFlags> 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<uint32_t>(groupNode.attribute("id").value());
group.name = groupNode.attribute("name").as_string();
group.flags = pugi::cast<uint64_t>(groupNode.attribute("flags").value());
group.access = groupNode.attribute("access").as_bool();
group.maxDepotItems = pugi::cast<uint32_t>(groupNode.attribute("maxdepotitems").value());
group.maxVipEntries = pugi::cast<uint32_t>(groupNode.attribute("maxvipentries").value());
group.flags = pugi::cast<uint64_t>(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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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) {

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<GuildRank>& 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<Player*> membersOnline;
std::vector<GuildRank> ranks;
std::string name;
std::string motd;
uint32_t id;
uint32_t memberCount = 0;
};

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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);

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <regex>
#include <set>
#include <unordered_set>
#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<uint32_t> playerList;
std::unordered_set<uint32_t> guildRankList;
std::unordered_set<uint32_t> guildList; // TODO: include ranks
std::list<std::string> expressionList;
std::list<std::pair<std::regex, bool>> 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> accessList;
friend class House;
@ -111,8 +110,8 @@ enum AccessHouseLevel_t {
HOUSE_OWNER = 3,
};
using HouseTileList = std::list<HouseTile*>;
using HouseBedItemList = std::list<BedItem*>;
typedef std::list<HouseTile*> HouseTileList;
typedef std::list<BedItem*> 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<uint32_t, House*>;
typedef std::map<uint32_t, House*> HouseMap;
enum RentPeriod_t {
RENTPERIOD_DAILY,

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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;

View File

@ -1,72 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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;
}

View File

@ -1,49 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("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<uint32_t>("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<uint32_t>("guild1");
if (guildId != guild1) {
guildWarVector.push_back(guild1);
guildWarList.push_back(guild1);
} else {
guildWarVector.push_back(result->getNumber<uint32_t>("guild2"));
guildWarList.push_back(result->getNumber<uint32_t>("guild2"));
}
} while (result->next());
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t>;
typedef std::vector<uint32_t> 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t>("id");
account.name = result->getString("name");
account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
account.premiumDays = result->getNumber<uint16_t>("premdays");
account.lastDay = result->getNumber<time_t>("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<char>(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<uint32_t>("id");
account.name = result->getString("name");
account.key = decodeSecret(result->getString("secret"));
account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
account.premiumDays = result->getNumber<uint16_t>("premdays");
account.lastDay = result->getNumber<time_t>("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<uint64_t>("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<uint32_t>("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<uint16_t>(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<uint32_t>("account_id");
Account acc = loadAccount(accno);
@ -297,7 +252,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
}
player->soul = result->getNumber<uint16_t>("soul");
player->capacity = result->getNumber<uint32_t>("cap") * 100;
player->capacity = std::max<uint32_t>(400, result->getNumber<uint32_t>("cap")) * 100;
player->blessings = result->getNumber<uint16_t>("blessings");
unsigned long conditionsSize;
@ -343,20 +298,17 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
player->defaultOutfit.lookFeet = result->getNumber<uint16_t>("lookfeet");
player->defaultOutfit.lookAddons = result->getNumber<uint16_t>("lookaddons");
player->currentOutfit = player->defaultOutfit;
player->direction = static_cast<Direction> (result->getNumber<uint16_t>("direction"));
if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
const time_t skullSeconds = result->getNumber<time_t>("skulltime") - time(nullptr);
if (skullSeconds > 0) {
//ensure that we round up the number of ticks
player->skullTicks = (skullSeconds + 2);
player->playerKillerEnd = result->getNumber<time_t>("skulltime");
uint16_t skull = result->getNumber<uint16_t>("skull");
if (skull == SKULL_RED) {
player->skull = SKULL_RED;
} else if (skull == SKULL_BLACK) {
player->skull = SKULL_BLACK;
}
uint16_t skull = result->getNumber<uint16_t>("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<time_t>("lastlogin");
player->lastLogout = result->getNumber<time_t>("lastlogout");
player->offlineTrainingTime = result->getNumber<int32_t>("offlinetraining_time") * 1000;
player->offlineTrainingSkill = result->getNumber<int32_t>("offlinetraining_skill");
Town* town = g_game.map.towns.getTown(result->getNumber<uint32_t>("town_id"));
if (!town) {
std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Town ID " << result->getNumber<uint32_t>("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<time_t>("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<uint32_t>("guild_id");
uint32_t playerRankId = result->getNumber<uint32_t>("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<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("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<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("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<uint32_t>("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<Item*, int32_t>& 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<uint32_t>("key"), result->getNumber<int32_t>("value"), true);
player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("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<uint32_t>("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<Container*, int32_t>;
std::list<ContainerBlock> queue;
typedef std::pair<Container*, int32_t> containerBlock;
std::list<containerBlock> 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<uint16_t>("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<uint16_t>(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<int64_t>(skull) << ',';
query << "`skull` = " << static_cast<uint32_t>(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<uint16_t> (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<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId)
@ -974,58 +911,46 @@ std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId)
std::forward_list<VIPEntry> 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<uint32_t>("player_id"),
result->getString("name"),
result->getString("description"),
result->getNumber<uint32_t>("icon"),
result->getNumber<uint16_t>("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());
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<std::pair<int32_t, Item*>>;
typedef std::list<std::pair<int32_t, Item*>> 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<VIPEntry> 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<uint32_t, std::pair<Item*, uint32_t>>;
protected:
typedef std::map<uint32_t, std::pair<Item*, uint32_t>> 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t>(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<HouseTile*>(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<uint8_t>(attribute)) {
switch (attribute) {
case OTBM_ATTR_TILE_FLAGS: {
uint32_t flags;
if (!propStream.read<uint32_t>(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<uint32_t>(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<HouseTile*>(tile));
isHouseTile = true;
}
case OTBM_ATTR_ITEM: {
Item* item = Item::CreateItem(propStream);
//read tile attributes
while (propStream.read<uint8_t>(attribute)) {
switch (attribute) {
case OTBM_ATTR_TILE_FLAGS: {
uint32_t flags;
if (!propStream.read<uint32_t>(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_t>(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<uint32_t>(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_t>(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<uint32_t>(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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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;
};

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t>("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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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);

View File

@ -1,347 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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<uint16_t>("amount");
offer.price = result->getNumber<uint32_t>("price");
offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration;
offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
if (result->getNumber<uint16_t>("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<uint16_t>("amount");
offer.price = result->getNumber<uint32_t>("price");
offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration;
offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
offer.itemId = result->getNumber<uint16_t>("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<uint16_t>("itemtype");
offer.amount = result->getNumber<uint16_t>("amount");
offer.price = result->getNumber<uint32_t>("price");
offer.timestamp = result->getNumber<uint32_t>("expires_at");
MarketOfferState_t offerState = static_cast<MarketOfferState_t>(result->getNumber<uint16_t>("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<uint32_t>("id"), OFFERSTATE_EXPIRED)) {
continue;
}
const uint32_t playerId = result->getNumber<uint32_t>("player_id");
const uint16_t amount = result->getNumber<uint16_t>("amount");
if (result->getNumber<uint16_t>("sale") == 1) {
const ItemType& itemType = Item::items[result->getNumber<uint16_t>("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<uint16_t>(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<uint64_t>("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<int32_t>("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<uint32_t>("id");
offer.type = static_cast<MarketAction_t>(result->getNumber<uint16_t>("sale"));
offer.amount = result->getNumber<uint16_t>("amount");
offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
offer.timestamp = result->getNumber<uint32_t>("created");
offer.price = result->getNumber<uint32_t>("price");
offer.itemId = result->getNumber<uint16_t>("itemtype");
offer.playerId = result->getNumber<uint32_t>("player_id");
if (result->getNumber<uint16_t>("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<uint32_t>("player_id"), static_cast<MarketAction_t>(result->getNumber<uint16_t>("sale")), result->getNumber<uint16_t>("itemtype"), result->getNumber<uint16_t>("amount"), result->getNumber<uint32_t>("price"), result->getNumber<uint32_t>("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<uint16_t>("sale") == MARKETACTION_BUY) {
statistics = &purchaseStatistics[result->getNumber<uint16_t>("itemtype")];
} else {
statistics = &saleStatistics[result->getNumber<uint16_t>("itemtype")];
}
statistics->numTransactions = result->getNumber<uint32_t>("num");
statistics->lowestPrice = result->getNumber<uint32_t>("min");
statistics->totalPrice = result->getNumber<uint64_t>("sum");
statistics->highestPrice = result->getNumber<uint32_t>("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;
}

View File

@ -1,63 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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<uint16_t, MarketStatistics> purchaseStatistics;
std::map<uint16_t, MarketStatistics> saleStatistics;
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <typeinfo>
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>
#include <deque>
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<uint16_t>(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<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
uint16_t getMovementId() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID));
}
void setKeyNumber(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, n);
}
uint16_t getKeyNumber() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER));
}
void setKeyHoleNumber(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER, n);
}
uint16_t getKeyHoleNumber() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER));
}
void setDoorQuestNumber(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER, n);
}
uint16_t getDoorQuestNumber() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER));
}
void setDoorQuestValue(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE, n);
}
uint16_t getDoorQuestValue() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE));
}
void setDoorLevel(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_DOORLEVEL, n);
}
uint16_t getDoorLevel() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_DOORLEVEL));
}
void setChestQuestNumber(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER, n);
}
uint16_t getChestQuestNumber() const {
return static_cast<uint16_t>(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<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE));
}
struct CustomAttribute
{
typedef boost::variant<boost::blank, std::string, int64_t, double, bool> VariantAttribute;
VariantAttribute value;
CustomAttribute() : value(boost::blank()) {}
template<typename T>
explicit CustomAttribute(const T& v) : value(v) {}
template<typename T>
void set(const T& v) {
value = v;
}
template<typename T>
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<typename T>
void operator()(const T& v) const {
propWriteStream.write<T>(v);
}
};
void serialize(PropWriteStream& propWriteStream) const {
propWriteStream.write<uint8_t>(static_cast<uint8_t>(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<uint8_t>(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<int64_t>(tmp)) {
return false;
}
value = tmp;
break;
}
case 3: { // double
double tmp;
if (!propStream.read<double>(tmp)) {
return false;
}
value = tmp;
break;
}
case 4: { // bool
bool tmp;
if (!propStream.read<bool>(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<std::string, CustomAttribute> 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<typename R>
void setCustomAttribute(int64_t key, R value) {
std::string tmp = boost::lexical_cast<std::string>(key);
setCustomAttribute(tmp, value);
}
void setCustomAttribute(int64_t key, CustomAttribute& value) {
std::string tmp = boost::lexical_cast<std::string>(key);
setCustomAttribute(tmp, value);
}
template<typename R>
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<std::string>(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<std::string>(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<Attribute>& 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<typename R>
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<uint16_t>(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<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID));
}
void setCharges(uint16_t n) {
@ -767,49 +557,42 @@ class Item : virtual public Thing
return static_cast<ItemDecayState_t>(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<ItemAttributes>& 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<ItemAttributes> 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<Item*>;
using ItemDeque = std::deque<Item*>;
typedef std::list<Item*> ItemList;
typedef std::deque<Item*> ItemDeque;
inline uint32_t Item::countByType(const Item* i, int32_t subType)
{
if (subType == -1 || subType == i->getSubType()) {
return i->getItemCount();
}
return 0;
}
#endif

View File

@ -1,198 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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> 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<std::string, uint16_t>;
using InventoryVector = std::vector<uint16_t>;
using nameMap = std::unordered_multimap<std::string, uint16_t>;
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<uint16_t, uint16_t> reverseItemMap;
protected:
std::vector<ItemType> items;
InventoryVector inventory;
};
#endif

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <boost/lockfree/stack.hpp>
/*
* 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 <std::size_t TSize, size_t CAPACITY>
* boost::lockfree::stack<void*, boost::lockfree::capacity<CAPACITY> lockfreeFreeList;
*/
template <std::size_t TSize, size_t CAPACITY>
struct LockfreeFreeList
{
using FreeList = boost::lockfree::stack<void*, boost::lockfree::capacity<CAPACITY>>;
static FreeList& get()
{
static FreeList freeList;
return freeList;
}
};
template <typename T, size_t CAPACITY>
class LockfreePoolingAllocator : public std::allocator<T>
{
public:
LockfreePoolingAllocator() = default;
template <typename U, class = typename std::enable_if<!std::is_same<U, T>::value>::type>
template <typename U>
explicit constexpr LockfreePoolingAllocator(const U&) {}
using value_type = T;
typedef T value_type;
T* allocate(size_t) const {
auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::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<T*>(operator new (sizeof(T)));
}
return static_cast<T*>(p);
return p;
}
void deallocate(T* p, size_t) const {
auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::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<T*, boost::lockfree::capacity<CAPACITY>> FreeList;
static FreeList& getFreeList() {
static FreeList freeList;
return freeList;
}
};
#endif

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <luajit/lua.hpp>
#else
#include <lua.hpp>
#endif
#if LUA_VERSION_NUM >= 502
#ifndef LUA_COMPAT_ALL
@ -39,7 +35,6 @@
#include "database.h"
#include "enums.h"
#include "position.h"
#include <boost/lexical_cast.hpp>
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<const LuaVariant*>;
using StorageMap = std::map<uint32_t, int32_t>;
using DBResultMap = std::map<uint32_t, DBResult_ptr>;
typedef std::vector<const LuaVariant*> VariantVector;
typedef std::map<uint32_t, int32_t> StorageMap;
typedef std::map<uint32_t, DBResult_ptr> 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<typename T>
static typename std::enable_if<std::is_enum<T>::value, T>::type
inline static typename std::enable_if<std::is_enum<T>::value, T>::type
getNumber(lua_State* L, int32_t arg)
{
return static_cast<T>(static_cast<int64_t>(lua_tonumber(L, arg)));
}
template<typename T>
static typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, T>::type
inline static typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, T>::type
getNumber(lua_State* L, int32_t arg)
{
return static_cast<T>(lua_tonumber(L, arg));
@ -309,16 +301,16 @@ class LuaScriptInterface
return *userdata;
}
template<class T>
static T** getRawUserdata(lua_State* L, int32_t arg)
inline static T** getRawUserdata(lua_State* L, int32_t arg)
{
return static_cast<T**>(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<LootBlock>& 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<int32_t, std::string> 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<uint32_t>& 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<int32_t, std::string> 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();

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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);

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<int32_t> 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;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Position, SpectatorVec>;
typedef std::map<Position, SpectatorVec> 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<typename Leaf, typename Node>
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;

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Creature*> 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<int32_t>::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<uint32_t>(uniform_random(1, 100))) {
if (updateLook) {
updateLookDirection();
updateLook = false;
}
}
for (spellBlock_t& spellBlock : mType->info.attackSpells) {
if (spellBlock.range != 0 && std::max<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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<uint32_t>(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);

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Creature*>;
using CreatureList = std::list<Creature*>;
typedef std::unordered_set<Creature*> CreatureHashSet;
typedef std::list<Creature*> 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

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<LootBlock> 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<Item*> 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<LuaScriptInterface> 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<std::pair<MonsterType*, std::string>>& monsterScriptList, bool reloading = false);
void loadLootContainer(const pugi::xml_node& node, LootBlock&);
bool loadLootItem(const pugi::xml_node& node, LootBlock&);
std::map<std::string, MonsterType> monsters;
std::map<std::string, std::string> unloadedMonsters;
std::unique_ptr<LuaScriptInterface> scriptInterface;
bool loaded = false;
};

View File

@ -1,82 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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<uint8_t>(pugi::cast<uint16_t>(mountNode.attribute("id").value())),
pugi::cast<uint16_t>(mountNode.attribute("clientid").value()),
mountNode.attribute("name").as_string(),
pugi::cast<int32_t>(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 &it;
}
}
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;
}

View File

@ -1,52 +0,0 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
*
* 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<Mount>& getMounts() const {
return mounts;
}
private:
std::vector<Mount> mounts;
};
#endif

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<MoveEvent*> 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<MoveEvent*>(event.release())}; //event is guaranteed to be a MoveEvent
MoveEvent* moveEvent = static_cast<MoveEvent*>(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<int32_t>(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<uint32_t>(attr.value());
uint32_t endId = pugi::cast<uint32_t>(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<int32_t>(attr.value()), uniqueIdMap);
} else if ((attr = node.attribute("fromuid"))) {
} else if ((attr = node.attribute("movementid"))) {
addEvent(moveEvent, pugi::cast<int32_t>(attr.value()), movementIdMap);
} else if ((attr = node.attribute("frommovementid"))) {
uint32_t id = pugi::cast<uint32_t>(attr.value());
uint32_t endId = pugi::cast<uint32_t>(node.attribute("touid").value());
addEvent(*moveEvent, id, uniqueIdMap);
uint32_t endId = pugi::cast<uint32_t>(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<int32_t>(attr.value()), actionIdMap);
} else if ((attr = node.attribute("fromaid"))) {
uint32_t id = pugi::cast<uint32_t>(attr.value());
uint32_t endId = pugi::cast<uint32_t>(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<int32_t> 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<MoveEvent>& moveEventList = it->second.moveEvent[moveEvent.getEventType()];
for (MoveEvent& existingMoveEvent : moveEventList) {
if (existingMoveEvent.getSlot() == moveEvent.getSlot()) {
std::list<MoveEvent*>& 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<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
for (MoveEvent& moveEvent : moveEventList) {
if ((moveEvent.getSlot() & slotp) != 0) {
return &moveEvent;
std::list<MoveEvent*>& 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<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
if (item->hasAttribute(ITEM_ATTRIBUTE_MOVEMENTID)) {
it = movementIdMap.find(item->getMovementId());
if (it != movementIdMap.end()) {
std::list<MoveEvent*>& 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<MoveEvent>& 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<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
std::list<MoveEvent*>& 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<MoveEvent>& moveEventList = it->second.moveEvent[moveEvent.getEventType()];
std::list<MoveEvent*>& 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<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
std::list<MoveEvent*>& 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<ConditionId_t>(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<SpecialSkills_t>(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<SpecialSkills_t>(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)

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<MoveEvent>;
struct MoveEventList {
std::list<MoveEvent> moveEvent[MOVE_EVENT_LAST];
std::list<MoveEvent*> moveEvent[MOVE_EVENT_LAST];
};
using VocEquipMap = std::map<uint16_t, bool>;
typedef std::map<uint16_t, bool> 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<int32_t, MoveEventList> MoveListMap;
void clearMap(MoveListMap& map);
private:
using MoveListMap = std::map<int32_t, MoveEventList>;
using MovePosListMap = std::map<Position, MoveEventList>;
void clearMap(MoveListMap& map, bool fromLua);
void clearPosMap(MovePosListMap& map, bool fromLua);
typedef std::map<Position, MoveEventList> 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<uint32_t(Creature* creature, Item* item, const Position& pos)>;
using MoveFunction = std::function<uint32_t(Item* item, Item* tileItem, const Position& pos)>;
using EquipFunction = std::function<uint32_t(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean)>;
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<uint32_t> getItemIdRange() {
return itemIdRange;
}
void addItemId(uint32_t id) {
itemIdRange.emplace_back(id);
}
std::vector<uint32_t> getActionIdRange() {
return actionIdRange;
}
void addActionId(uint32_t id) {
actionIdRange.emplace_back(id);
}
std::vector<uint32_t> getUniqueIdRange() {
return uniqueIdRange;
}
void addUniqueId(uint32_t id) {
uniqueIdRange.emplace_back(id);
}
std::vector<Position> 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<uint32_t> itemIdRange;
std::vector<uint32_t> actionIdRange;
std::vector<uint32_t> uniqueIdRange;
std::vector<Position> posList;
};
#endif

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint16_t>(it.clientId);
addByte(0xFF); // MARK_UNMARKED
if (it.disguise) {
add<uint16_t>(it.disguiseId);
} else {
add<uint16_t>(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<uint16_t>(it.clientId);
addByte(0xFF); // MARK_UNMARKED
if (it.disguise) {
add<uint16_t>(it.disguiseId);
} else {
add<uint16_t>(it.id);
}
if (it.stackable) {
addByte(std::min<uint16_t>(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<uint16_t>(Item::items[itemId].clientId);
add<uint16_t>(itemId);
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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__

File diff suppressed because it is too large Load Diff

171
src/npc.h
View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <set>
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<std::string, std::string> parameters;
std::set<Player*> shopPlayerSet;
std::set<Player*> 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <csignal> // 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 <fstream>
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<ProtocolGame>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::GAME_PORT)));
services->add<ProtocolLogin>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
services->add<ProtocolGame>(g_config.getNumber(ConfigManager::GAME_PORT));
services->add<ProtocolLogin>(g_config.getNumber(ConfigManager::LOGIN_PORT));
// OT protocols
services->add<ProtocolStatus>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::STATUS_PORT)));
// Legacy login protocol
services->add<ProtocolOld>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
services->add<ProtocolStatus>(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

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<typename U>
struct rebind {typedef LockfreePoolingAllocator<U, OUTPUTMESSAGE_FREE_LIST_CAPACITY> 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<void,...> will leave (void* allocate) ill-formed because
// of sizeof(T), so this guaranatees that only one list will be initialized
return std::allocate_shared<OutputMessage>(LockfreePoolingAllocator<void, OUTPUTMESSAGE_FREE_LIST_CAPACITY>());
return std::allocate_shared<OutputMessage>(OutputMessageAllocator());
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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 <typename T>
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 <typename T>
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<Protocol_ptr> 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<Protocol_ptr> bufferedProtocols;
};

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<uint32_t> 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<float>(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<uint32_t>(std::ceil((static_cast<float>(highestLevel) * 2) / 3));
uint32_t minLevel = static_cast<int32_t>(std::ceil((static_cast<float>(highestLevel) * 2) / 3));
if (player->getLevel() < minLevel) {
return false;
}

View File

@ -1,6 +1,6 @@
/**
* The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
*
* 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<Player*>;
typedef std::vector<Player*> 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<uint32_t, int64_t> ticksMap;
@ -90,6 +93,8 @@ class Party
Player* leader;
float extraExpRate = 0.20f;
bool sharedExpActive = false;
bool sharedExpEnabled = false;
};

Some files were not shown because too many files have changed in this diff Show More