Revert "commit newest tfs branch only for compare"

This reverts commit 1f7dcd7347.
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}/ban.cpp
${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp ${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp
${CMAKE_CURRENT_LIST_DIR}/bed.cpp ${CMAKE_CURRENT_LIST_DIR}/bed.cpp
${CMAKE_CURRENT_LIST_DIR}/behaviourdatabase.cpp
${CMAKE_CURRENT_LIST_DIR}/chat.cpp ${CMAKE_CURRENT_LIST_DIR}/chat.cpp
${CMAKE_CURRENT_LIST_DIR}/combat.cpp ${CMAKE_CURRENT_LIST_DIR}/combat.cpp
${CMAKE_CURRENT_LIST_DIR}/condition.cpp ${CMAKE_CURRENT_LIST_DIR}/condition.cpp
@@ -16,7 +17,6 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/database.cpp ${CMAKE_CURRENT_LIST_DIR}/database.cpp
${CMAKE_CURRENT_LIST_DIR}/databasemanager.cpp ${CMAKE_CURRENT_LIST_DIR}/databasemanager.cpp
${CMAKE_CURRENT_LIST_DIR}/databasetasks.cpp ${CMAKE_CURRENT_LIST_DIR}/databasetasks.cpp
${CMAKE_CURRENT_LIST_DIR}/depotchest.cpp
${CMAKE_CURRENT_LIST_DIR}/depotlocker.cpp ${CMAKE_CURRENT_LIST_DIR}/depotlocker.cpp
${CMAKE_CURRENT_LIST_DIR}/events.cpp ${CMAKE_CURRENT_LIST_DIR}/events.cpp
${CMAKE_CURRENT_LIST_DIR}/fileloader.cpp ${CMAKE_CURRENT_LIST_DIR}/fileloader.cpp
@@ -26,12 +26,10 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/groups.cpp ${CMAKE_CURRENT_LIST_DIR}/groups.cpp
${CMAKE_CURRENT_LIST_DIR}/house.cpp ${CMAKE_CURRENT_LIST_DIR}/house.cpp
${CMAKE_CURRENT_LIST_DIR}/housetile.cpp ${CMAKE_CURRENT_LIST_DIR}/housetile.cpp
${CMAKE_CURRENT_LIST_DIR}/inbox.cpp
${CMAKE_CURRENT_LIST_DIR}/ioguild.cpp ${CMAKE_CURRENT_LIST_DIR}/ioguild.cpp
${CMAKE_CURRENT_LIST_DIR}/iologindata.cpp ${CMAKE_CURRENT_LIST_DIR}/iologindata.cpp
${CMAKE_CURRENT_LIST_DIR}/iomap.cpp ${CMAKE_CURRENT_LIST_DIR}/iomap.cpp
${CMAKE_CURRENT_LIST_DIR}/iomapserialize.cpp ${CMAKE_CURRENT_LIST_DIR}/iomapserialize.cpp
${CMAKE_CURRENT_LIST_DIR}/iomarket.cpp
${CMAKE_CURRENT_LIST_DIR}/item.cpp ${CMAKE_CURRENT_LIST_DIR}/item.cpp
${CMAKE_CURRENT_LIST_DIR}/items.cpp ${CMAKE_CURRENT_LIST_DIR}/items.cpp
${CMAKE_CURRENT_LIST_DIR}/luascript.cpp ${CMAKE_CURRENT_LIST_DIR}/luascript.cpp
@@ -39,7 +37,6 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/map.cpp ${CMAKE_CURRENT_LIST_DIR}/map.cpp
${CMAKE_CURRENT_LIST_DIR}/monster.cpp ${CMAKE_CURRENT_LIST_DIR}/monster.cpp
${CMAKE_CURRENT_LIST_DIR}/monsters.cpp ${CMAKE_CURRENT_LIST_DIR}/monsters.cpp
${CMAKE_CURRENT_LIST_DIR}/mounts.cpp
${CMAKE_CURRENT_LIST_DIR}/movement.cpp ${CMAKE_CURRENT_LIST_DIR}/movement.cpp
${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp ${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp
${CMAKE_CURRENT_LIST_DIR}/npc.cpp ${CMAKE_CURRENT_LIST_DIR}/npc.cpp
@@ -52,29 +49,24 @@ set(tfs_SRC
${CMAKE_CURRENT_LIST_DIR}/protocol.cpp ${CMAKE_CURRENT_LIST_DIR}/protocol.cpp
${CMAKE_CURRENT_LIST_DIR}/protocolgame.cpp ${CMAKE_CURRENT_LIST_DIR}/protocolgame.cpp
${CMAKE_CURRENT_LIST_DIR}/protocollogin.cpp ${CMAKE_CURRENT_LIST_DIR}/protocollogin.cpp
${CMAKE_CURRENT_LIST_DIR}/protocolold.cpp
${CMAKE_CURRENT_LIST_DIR}/protocolstatus.cpp ${CMAKE_CURRENT_LIST_DIR}/protocolstatus.cpp
${CMAKE_CURRENT_LIST_DIR}/quests.cpp
${CMAKE_CURRENT_LIST_DIR}/raids.cpp ${CMAKE_CURRENT_LIST_DIR}/raids.cpp
${CMAKE_CURRENT_LIST_DIR}/rsa.cpp ${CMAKE_CURRENT_LIST_DIR}/rsa.cpp
${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp ${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp
${CMAKE_CURRENT_LIST_DIR}/scriptmanager.cpp ${CMAKE_CURRENT_LIST_DIR}/scriptmanager.cpp
${CMAKE_CURRENT_LIST_DIR}/script.cpp
${CMAKE_CURRENT_LIST_DIR}/server.cpp ${CMAKE_CURRENT_LIST_DIR}/server.cpp
${CMAKE_CURRENT_LIST_DIR}/signals.cpp
${CMAKE_CURRENT_LIST_DIR}/spawn.cpp ${CMAKE_CURRENT_LIST_DIR}/spawn.cpp
${CMAKE_CURRENT_LIST_DIR}/spells.cpp ${CMAKE_CURRENT_LIST_DIR}/spells.cpp
${CMAKE_CURRENT_LIST_DIR}/script.cpp
${CMAKE_CURRENT_LIST_DIR}/talkaction.cpp ${CMAKE_CURRENT_LIST_DIR}/talkaction.cpp
${CMAKE_CURRENT_LIST_DIR}/tasks.cpp ${CMAKE_CURRENT_LIST_DIR}/tasks.cpp
${CMAKE_CURRENT_LIST_DIR}/teleport.cpp ${CMAKE_CURRENT_LIST_DIR}/teleport.cpp
${CMAKE_CURRENT_LIST_DIR}/thing.cpp ${CMAKE_CURRENT_LIST_DIR}/thing.cpp
${CMAKE_CURRENT_LIST_DIR}/tile.cpp ${CMAKE_CURRENT_LIST_DIR}/tile.cpp
${CMAKE_CURRENT_LIST_DIR}/tools.cpp ${CMAKE_CURRENT_LIST_DIR}/tools.cpp
${CMAKE_CURRENT_LIST_DIR}/trashholder.cpp
${CMAKE_CURRENT_LIST_DIR}/vocation.cpp ${CMAKE_CURRENT_LIST_DIR}/vocation.cpp
${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp ${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp
${CMAKE_CURRENT_LIST_DIR}/weapons.cpp
${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp ${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp
${CMAKE_CURRENT_LIST_DIR}/xtea.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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -24,8 +24,6 @@
struct Account { struct Account {
std::vector<std::string> characters; std::vector<std::string> characters;
std::string name;
std::string key;
time_t lastDay = 0; time_t lastDay = 0;
uint32_t id = 0; uint32_t id = 0;
uint16_t premiumDays = 0; uint16_t premiumDays = 0;

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -40,27 +40,29 @@ Actions::Actions() :
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(); ) { // Filter out duplicates to avoid double-free
if (fromLua == it->second.fromLua) { std::unordered_set<Action*> set;
it = map.erase(it); for (const auto& it : map) {
} else { set.insert(it.second);
++it; }
} map.clear();
for (Action* action : set) {
delete action;
} }
} }
void Actions::clear(bool fromLua) void Actions::clear()
{ {
clearMap(useItemMap, fromLua); clearMap(useItemMap);
clearMap(uniqueItemMap, fromLua); clearMap(actionItemMap);
clearMap(actionItemMap, fromLua);
reInitState(fromLua); scriptInterface.reInitState();
} }
LuaScriptInterface& Actions::getScriptInterface() LuaScriptInterface& Actions::getScriptInterface()
@@ -73,23 +75,23 @@ std::string Actions::getScriptBaseName() const
return "actions"; return "actions";
} }
Event_ptr Actions::getEvent(const std::string& nodeName) Event* Actions::getEvent(const std::string& nodeName)
{ {
if (strcasecmp(nodeName.c_str(), "action") != 0) { if (strcasecmp(nodeName.c_str(), "action") != 0) {
return nullptr; 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; pugi::xml_attribute attr;
if ((attr = node.attribute("itemid"))) { if ((attr = node.attribute("itemid"))) {
uint16_t id = pugi::cast<uint16_t>(attr.value()); 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) { if (!result.second) {
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << id << std::endl; 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 iterId = fromId;
uint16_t toId = pugi::cast<uint16_t>(toIdAttribute.value()); uint16_t toId = pugi::cast<uint16_t>(toIdAttribute.value());
auto result = useItemMap.emplace(iterId, *action); auto result = useItemMap.emplace(iterId, action);
if (!result.second) { if (!result.second) {
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl; std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
} }
bool success = result.second; bool success = result.second;
while (++iterId <= toId) { while (++iterId <= toId) {
result = useItemMap.emplace(iterId, *action); result = useItemMap.emplace(iterId, action);
if (!result.second) { if (!result.second) {
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl; std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
continue; continue;
@@ -120,44 +122,10 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
success = true; success = true;
} }
return success; 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"))) { } else if ((attr = node.attribute("actionid"))) {
uint16_t aid = pugi::cast<uint16_t>(attr.value()); 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) { if (!result.second) {
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with actionid: " << aid << std::endl; 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 iterAid = fromAid;
uint16_t toAid = pugi::cast<uint16_t>(toAidAttribute.value()); uint16_t toAid = pugi::cast<uint16_t>(toAidAttribute.value());
auto result = actionItemMap.emplace(iterAid, *action); auto result = actionItemMap.emplace(iterAid, action);
if (!result.second) { if (!result.second) {
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl; std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
} }
bool success = result.second; bool success = result.second;
while (++iterAid <= toAid) { while (++iterAid <= toAid) {
result = actionItemMap.emplace(iterAid, *action); result = actionItemMap.emplace(iterAid, action);
if (!result.second) { if (!result.second) {
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl; std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
continue; continue;
@@ -192,69 +160,6 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
return false; 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) ReturnValue Actions::canUse(const Player* player, const Position& pos)
{ {
if (pos.x != 0xFFFF) { if (pos.x != 0xFFFF) {
@@ -303,23 +208,16 @@ ReturnValue Actions::canUseFar(const Creature* creature, const Position& toPos,
Action* Actions::getAction(const Item* item) 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)) { if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) {
auto it = actionItemMap.find(item->getActionId()); auto it = actionItemMap.find(item->getActionId());
if (it != actionItemMap.end()) { if (it != actionItemMap.end()) {
return &it->second; return it->second;
} }
} }
auto it = useItemMap.find(item->getID()); auto it = useItemMap.find(item->getID());
if (it != useItemMap.end()) { if (it != useItemMap.end()) {
return &it->second; return it->second;
} }
//rune items //rune items
@@ -358,41 +256,44 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
if (bed->trySleep(player)) { if (bed->trySleep(player)) {
player->setBedItem(bed); player->setBedItem(bed);
g_game.sendOfflineTrainingDialog(player); if (!bed->sleep(player)) {
return RETURNVALUE_CANNOTUSETHISOBJECT;
}
} }
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
if (Container* container = item->getContainer()) { if (Container* container = item->getContainer()) {
Container* openContainer; if (!item->isChestQuest()) {
Container* openContainer;
//depot container //depot container
if (DepotLocker* depot = container->getDepotLocker()) { if (DepotLocker* depot = container->getDepotLocker()) {
DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId()); DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId(), true);
myDepotLocker->setParent(depot->getParent()->getTile()); myDepotLocker->setParent(depot->getParent()->getTile());
openContainer = myDepotLocker; openContainer = myDepotLocker;
player->setLastDepotId(depot->getDepotId()); } else {
} else { openContainer = container;
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()]; const ItemType& it = Item::items[item->getID()];
@@ -406,6 +307,12 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
} }
return RETURNVALUE_NOERROR; 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; return RETURNVALUE_CANNOTUSETHISOBJECT;
@@ -417,7 +324,7 @@ bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item*
player->stopWalk(); player->stopWalk();
if (isHotkey) { 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); ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey);
@@ -447,7 +354,7 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position&
} }
if (isHotkey) { 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)) { 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()]; const ItemType& it = Item::items[item->getID()];
if (!it.showCount) { if (!it.showCount) {
ss << "Using one of " << item->getName() << "..."; ss << "Using one of " << item->getName() << "...";
} else if (count == 1) { }
else if (count == 1) {
ss << "Using the last " << item->getName() << "..."; ss << "Using the last " << item->getName() << "...";
} else { }
else {
ss << "Using one of " << count << ' ' << item->getPluralName() << "..."; ss << "Using one of " << count << ' ' << item->getPluralName() << "...";
} }
player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); 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) : Action::Action(LuaScriptInterface* interface) :
Event(interface), function(nullptr), allowFarUse(false), checkFloor(true), checkLineOfSight(true) {} 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) bool Action::configureEvent(const pugi::xml_node& node)
{ {
pugi::xml_attribute allowFarUseAttr = node.attribute("allowfaruse"); pugi::xml_attribute allowFarUseAttr = node.attribute("allowfaruse");
@@ -497,38 +409,6 @@ bool Action::configureEvent(const pugi::xml_node& node)
return true; 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 std::string Action::getScriptEventName() const
{ {
return "onUse"; return "onUse";
@@ -538,7 +418,8 @@ ReturnValue Action::canExecuteAction(const Player* player, const Position& toPos
{ {
if (!allowFarUse) { if (!allowFarUse) {
return g_actions->canUse(player, toPos); return g_actions->canUse(player, toPos);
} else { }
else {
return g_actions->canUseFar(player, toPos, checkLineOfSight, checkFloor); return g_actions->canUseFar(player, toPos, checkLineOfSight, checkFloor);
} }
} }

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -24,15 +24,15 @@
#include "databasetasks.h" #include "databasetasks.h"
#include "tools.h" #include "tools.h"
bool Ban::acceptConnection(uint32_t clientIP) bool Ban::acceptConnection(uint32_t clientip)
{ {
std::lock_guard<std::recursive_mutex> lockClass(lock); std::lock_guard<std::recursive_mutex> lockClass(lock);
uint64_t currentTime = OTSYS_TIME(); uint64_t currentTime = OTSYS_TIME();
auto it = ipConnectMap.find(clientIP); auto it = ipConnectMap.find(clientip);
if (it == ipConnectMap.end()) { if (it == ipConnectMap.end()) {
ipConnectMap.emplace(clientIP, ConnectBlock(currentTime, 0, 1)); ipConnectMap.emplace(clientip, ConnectBlock(currentTime, 0, 1));
return true; return true;
} }
@@ -60,12 +60,12 @@ bool Ban::acceptConnection(uint32_t clientIP)
bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo) bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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; 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) { if (!result) {
return false; return false;
} }
@@ -74,7 +74,7 @@ bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo)
if (expiresAt != 0 && time(nullptr) > expiresAt) { if (expiresAt != 0 && time(nullptr) > expiresAt) {
// Move the ban to history if it has expired // Move the ban to history if it has expired
query.str(std::string()); 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()); g_databaseTasks.addTask(query.str());
query.str(std::string()); query.str(std::string());
@@ -89,18 +89,18 @@ bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo)
return true; 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; return false;
} }
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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) { if (!result) {
return false; return false;
} }
@@ -108,7 +108,7 @@ bool IOBan::isIpBanned(uint32_t clientIP, BanInfo& banInfo)
int64_t expiresAt = result->getNumber<int64_t>("expires_at"); int64_t expiresAt = result->getNumber<int64_t>("expires_at");
if (expiresAt != 0 && time(nullptr) > expiresAt) { if (expiresAt != 0 && time(nullptr) > expiresAt) {
query.str(std::string()); 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()); g_databaseTasks.addTask(query.str());
return false; return false;
} }
@@ -123,5 +123,5 @@ bool IOBan::isPlayerNamelocked(uint32_t playerId)
{ {
std::ostringstream query; std::ostringstream query;
query << "SELECT 1 FROM `player_namelocks` WHERE `player_id` = " << playerId; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -35,14 +35,14 @@ struct ConnectBlock {
uint32_t count; uint32_t count;
}; };
using IpConnectMap = std::map<uint32_t, ConnectBlock>; typedef std::map<uint32_t, ConnectBlock> IpConnectMap;
class Ban class Ban
{ {
public: public:
bool acceptConnection(uint32_t clientIP); bool acceptConnection(uint32_t clientip);
private: protected:
IpConnectMap ipConnectMap; IpConnectMap ipConnectMap;
std::recursive_mutex lock; std::recursive_mutex lock;
}; };
@@ -51,7 +51,7 @@ class IOBan
{ {
public: public:
static bool isAccountBanned(uint32_t accountId, BanInfo& banInfo); 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); static bool isPlayerNamelocked(uint32_t playerId);
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -51,13 +51,14 @@ bool BaseEvents::loadFromXml()
loaded = true; loaded = true;
for (auto node : doc.child(scriptsName.c_str()).children()) { for (auto node : doc.child(scriptsName.c_str()).children()) {
Event_ptr event = getEvent(node.name()); Event* event = getEvent(node.name());
if (!event) { if (!event) {
continue; continue;
} }
if (!event->configureEvent(node)) { if (!event->configureEvent(node)) {
std::cout << "[Warning - BaseEvents::loadFromXml] Failed to configure event" << std::endl; std::cout << "[Warning - BaseEvents::loadFromXml] Failed to configure event" << std::endl;
delete event;
continue; continue;
} }
@@ -67,15 +68,12 @@ bool BaseEvents::loadFromXml()
if (scriptAttribute) { if (scriptAttribute) {
std::string scriptFile = "scripts/" + std::string(scriptAttribute.as_string()); std::string scriptFile = "scripts/" + std::string(scriptAttribute.as_string());
success = event->checkScript(basePath, scriptsName, scriptFile) && event->loadScript(basePath + scriptFile); success = event->checkScript(basePath, scriptsName, scriptFile) && event->loadScript(basePath + scriptFile);
if (node.attribute("function")) {
event->loadFunction(node.attribute("function"), true);
}
} else { } else {
success = event->loadFunction(node.attribute("function"), false); success = event->loadFunction(node.attribute("function"));
} }
if (success) { if (!success || !registerEvent(event, node)) {
registerEvent(std::move(event), node); delete event;
} }
} }
return true; return true;
@@ -84,19 +82,15 @@ bool BaseEvents::loadFromXml()
bool BaseEvents::reload() bool BaseEvents::reload()
{ {
loaded = false; loaded = false;
clear(false); clear();
return loadFromXml(); return loadFromXml();
} }
void BaseEvents::reInitState(bool fromLua)
{
if (!fromLua) {
getScriptInterface().reInitState();
}
}
Event::Event(LuaScriptInterface* interface) : scriptInterface(interface) {} 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 bool Event::checkScript(const std::string& basePath, const std::string& scriptsName, const std::string& scriptFile) const
{ {
LuaScriptInterface* testInterface = g_luaEnvironment.getTestInterface(); LuaScriptInterface* testInterface = g_luaEnvironment.getTestInterface();
@@ -149,24 +143,6 @@ bool Event::loadScript(const std::string& scriptFile)
return true; 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) bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string& name)
{ {
if (!interface) { if (!interface) {
@@ -182,6 +158,7 @@ bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string& na
return false; return false;
} }
callbackName = name;
scriptId = id; scriptId = id;
loaded = true; loaded = true;
return true; return true;

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -22,21 +22,18 @@
#include "luascript.h" #include "luascript.h"
class Event;
using Event_ptr = std::unique_ptr<Event>;
class Event class Event
{ {
public: public:
explicit Event(LuaScriptInterface* interface); explicit Event(LuaScriptInterface* interface);
explicit Event(const Event* copy);
virtual ~Event() = default; virtual ~Event() = default;
virtual bool configureEvent(const pugi::xml_node& node) = 0; 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 checkScript(const std::string& basePath, const std::string& scriptsName, const std::string& scriptFile) const;
bool loadScript(const std::string& scriptFile); bool loadScript(const std::string& scriptFile);
bool loadCallback(); virtual bool loadFunction(const pugi::xml_attribute&) {
virtual bool loadFunction(const pugi::xml_attribute&, bool) {
return false; return false;
} }
@@ -44,12 +41,10 @@ class Event
return scripted; return scripted;
} }
bool scripted = false;
bool fromLua = false;
protected: protected:
virtual std::string getScriptEventName() const = 0; virtual std::string getScriptEventName() const = 0;
bool scripted = false;
int32_t scriptId = 0; int32_t scriptId = 0;
LuaScriptInterface* scriptInterface = nullptr; LuaScriptInterface* scriptInterface = nullptr;
}; };
@@ -65,14 +60,13 @@ class BaseEvents
bool isLoaded() const { bool isLoaded() const {
return loaded; return loaded;
} }
void reInitState(bool fromLua);
private: protected:
virtual LuaScriptInterface& getScriptInterface() = 0; virtual LuaScriptInterface& getScriptInterface() = 0;
virtual std::string getScriptBaseName() const = 0; virtual std::string getScriptBaseName() const = 0;
virtual Event_ptr getEvent(const std::string& nodeName) = 0; virtual Event* getEvent(const std::string& nodeName) = 0;
virtual bool registerEvent(Event_ptr event, const pugi::xml_node& node) = 0; virtual bool registerEvent(Event* event, const pugi::xml_node& node) = 0;
virtual void clear(bool) = 0; virtual void clear() = 0;
bool loaded = false; bool loaded = false;
}; };
@@ -88,8 +82,9 @@ class CallBack
int32_t scriptId = 0; int32_t scriptId = 0;
LuaScriptInterface* scriptInterface = nullptr; LuaScriptInterface* scriptInterface = nullptr;
private:
bool loaded = false; bool loaded = false;
std::string callbackName;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 // make the player walk onto the bed
g_game.map.moveCreature(*player, *getTile()); g_game.map.moveCreature(*player, *getTile());
// display 'Zzzz'/sleep effect // display poff effect
g_game.addMagicEffect(player->getPosition(), CONST_ME_SLEEP); g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
// kick player after he sees himself walk onto the bed and it change id // kick player after he sees himself walk onto the bed and it change id
uint32_t playerId = player->getID(); uint32_t playerId = player->getID();
@@ -246,10 +246,10 @@ void BedItem::updateAppearance(const Player* player)
{ {
const ItemType& it = Item::items[id]; const ItemType& it = Item::items[id];
if (it.type == ITEM_TYPE_BED) { if (it.type == ITEM_TYPE_BED) {
if (player && it.transformToOnUse[player->getSex()] != 0) { if (player && it.transformToOnUse != 0) {
const ItemType& newType = Item::items[it.transformToOnUse[player->getSex()]]; const ItemType& newType = Item::items[it.transformToOnUse];
if (newType.type == ITEM_TYPE_BED) { 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) { } else if (it.transformToFree != 0) {
const ItemType& newType = Item::items[it.transformToFree]; const ItemType& newType = Item::items[it.transformToFree];

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -30,17 +30,17 @@ class BedItem final : public Item
public: public:
explicit BedItem(uint16_t id); explicit BedItem(uint16_t id);
BedItem* getBed() override { BedItem* getBed() final {
return this; return this;
} }
const BedItem* getBed() const override { const BedItem* getBed() const final {
return this; return this;
} }
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final;
void serializeAttr(PropWriteStream& propWriteStream) const override; void serializeAttr(PropWriteStream& propWriteStream) const final;
bool canRemove() const override { bool canRemove() const final {
return house == nullptr; return house == nullptr;
} }
@@ -48,6 +48,9 @@ class BedItem final : public Item
return sleeperGUID; return sleeperGUID;
} }
House* getHouse() const {
return house;
}
void setHouse(House* h) { void setHouse(House* h) {
house = h; house = h;
} }
@@ -60,7 +63,7 @@ class BedItem final : public Item
BedItem* getNextBedItem() const; BedItem* getNextBedItem() const;
private: protected:
void updateAppearance(const Player* player); void updateAppearance(const Player* player);
void regeneratePlayer(Player* player) const; void regeneratePlayer(Player* player) const;
void internalSetSleeper(const Player* player); void internalSetSleeper(const Player* player);

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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.str(std::string());
ss << invitePlayer.getName() << " has been invited."; ss << invitePlayer.getName() << " has been invited.";
player.sendTextMessage(MESSAGE_INFO_DESCR, ss.str()); 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) 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()); player.sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
excludePlayer.sendClosePrivate(id); excludePlayer.sendClosePrivate(id);
for (const auto& it : users) {
it.second->sendChannelEvent(id, excludePlayer.getName(), CHANNELEVENT_EXCLUDE);
}
} }
void PrivateChatChannel::closeChannel() const void PrivateChatChannel::closeChannel() const
@@ -96,20 +88,6 @@ bool ChatChannel::addUser(Player& player)
return false; 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; users[player.getID()] = &player;
return true; return true;
} }
@@ -123,27 +101,10 @@ bool ChatChannel::removeUser(const Player& player)
users.erase(iter); users.erase(iter);
if (!publicChannel) {
for (const auto& it : users) {
it.second->sendChannelEvent(id, player.getName(), CHANNELEVENT_LEAVE);
}
}
executeOnLeaveEvent(player); executeOnLeaveEvent(player);
return true; 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) bool ChatChannel::talk(const Player& fromPlayer, SpeakClasses type, const std::string& text)
{ {
if (users.find(fromPlayer.getID()) == users.end()) { if (users.find(fromPlayer.getID()) == users.end()) {
@@ -294,39 +255,21 @@ bool Chat::load()
return false; 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()) { for (auto channelNode : doc.child("channels").children()) {
uint16_t channelId = pugi::cast<uint16_t>(channelNode.attribute("id").value()); ChatChannel channel(pugi::cast<uint16_t>(channelNode.attribute("id").value()), channelNode.attribute("name").as_string());
std::string channelName = channelNode.attribute("name").as_string(); channel.publicChannel = channelNode.attribute("public").as_bool();
bool isPublic = channelNode.attribute("public").as_bool();
pugi::xml_attribute scriptAttribute = channelNode.attribute("script"); 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 (scriptAttribute) {
if (scriptInterface.loadFile("data/chatchannels/scripts/" + std::string(scriptAttribute.as_string())) == 0) { if (scriptInterface.loadFile("data/chatchannels/scripts/" + std::string(scriptAttribute.as_string())) == 0) {
channel.onSpeakEvent = scriptInterface.getEvent("onSpeak"); channel.onSpeakEvent = scriptInterface.getEvent("onSpeak");
@@ -338,8 +281,13 @@ bool Chat::load()
} }
} }
removedChannels.remove(channel.id);
normalChannels[channel.id] = channel; normalChannels[channel.id] = channel;
} }
for (uint16_t channelId : removedChannels) {
normalChannels.erase(channelId);
}
return true; return true;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -26,24 +26,23 @@
class Party; class Party;
class Player; class Player;
using UsersMap = std::map<uint32_t, Player*>; typedef std::map<uint32_t, Player*> UsersMap;
using InvitedMap = std::map<uint32_t, const Player*>; typedef std::map<uint32_t, const Player*> InvitedMap;
class ChatChannel class ChatChannel
{ {
public: public:
ChatChannel() = default; ChatChannel() = default;
ChatChannel(uint16_t channelId, std::string channelName): ChatChannel(uint16_t channelId, std::string channelName):
id{channelId}, name{std::move(channelName)} {} name(std::move(channelName)),
id(channelId) {}
virtual ~ChatChannel() = default; virtual ~ChatChannel() = default;
bool addUser(Player& player); bool addUser(Player& player);
bool removeUser(const Player& player); bool removeUser(const Player& player);
bool hasUser(const Player& player);
bool talk(const Player& fromPlayer, SpeakClasses type, const std::string& text); 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 { const std::string& getName() const {
return name; return name;
@@ -72,9 +71,6 @@ class ChatChannel
protected: protected:
UsersMap users; UsersMap users;
uint16_t id;
private:
std::string name; std::string name;
int32_t canJoinEvent = -1; int32_t canJoinEvent = -1;
@@ -82,6 +78,7 @@ class ChatChannel
int32_t onLeaveEvent = -1; int32_t onLeaveEvent = -1;
int32_t onSpeakEvent = -1; int32_t onSpeakEvent = -1;
uint16_t id;
bool publicChannel = false; bool publicChannel = false;
friend class Chat; friend class Chat;
@@ -92,7 +89,7 @@ class PrivateChatChannel final : public ChatChannel
public: public:
PrivateChatChannel(uint16_t channelId, std::string channelName) : ChatChannel(channelId, channelName) {} PrivateChatChannel(uint16_t channelId, std::string channelName) : ChatChannel(channelId, channelName) {}
uint32_t getOwner() const override { uint32_t getOwner() const final {
return owner; return owner;
} }
void setOwner(uint32_t owner) { void setOwner(uint32_t owner) {
@@ -108,16 +105,16 @@ class PrivateChatChannel final : public ChatChannel
void closeChannel() const; void closeChannel() const;
const InvitedMap* getInvitedUsers() const override { const InvitedMap* getInvitedUsers() const final {
return &invites; return &invites;
} }
private: protected:
InvitedMap invites; InvitedMap invites;
uint32_t owner = 0; uint32_t owner = 0;
}; };
using ChannelList = std::list<ChatChannel*>; typedef std::list<ChatChannel*> ChannelList;
class Chat 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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) {} explicit ValueCallback(formulaType_t type): type(type) {}
void getMinMaxValues(Player* player, CombatDamage& damage, bool useCharges) const; void getMinMaxValues(Player* player, CombatDamage& damage, bool useCharges) const;
private: protected:
formulaType_t type; formulaType_t type;
}; };
@@ -46,12 +46,18 @@ class TileCallback final : public CallBack
{ {
public: public:
void onTileCombat(Creature* creature, Tile* tile) const; void onTileCombat(Creature* creature, Tile* tile) const;
protected:
formulaType_t type;
}; };
class TargetCallback final : public CallBack class TargetCallback final : public CallBack
{ {
public: public:
void onTargetCombat(Creature* creature, Creature* target) const; void onTargetCombat(Creature* creature, Creature* target) const;
protected:
formulaType_t type;
}; };
struct CombatParams { struct CombatParams {
@@ -62,6 +68,8 @@ struct CombatParams {
std::unique_ptr<TargetCallback> targetCallback; std::unique_ptr<TargetCallback> targetCallback;
uint16_t itemId = 0; uint16_t itemId = 0;
uint16_t decreaseDamage = 0;
uint16_t maximumDecreasedDamage = 0;
ConditionType_t dispelType = CONDITION_NONE; ConditionType_t dispelType = CONDITION_NONE;
CombatType_t combatType = COMBAT_NONE; CombatType_t combatType = COMBAT_NONE;
@@ -77,7 +85,39 @@ struct CombatParams {
bool useCharges = false; 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 class MatrixArea
{ {
@@ -145,14 +185,14 @@ class MatrixArea
return cols; return cols;
} }
const bool* operator[](uint32_t i) const { inline const bool* operator[](uint32_t i) const {
return data_[i]; return data_[i];
} }
bool* operator[](uint32_t i) { inline bool* operator[](uint32_t i) {
return data_[i]; return data_[i];
} }
private: protected:
uint32_t centerX; uint32_t centerX;
uint32_t centerY; uint32_t centerY;
@@ -182,7 +222,7 @@ class AreaCombat
void setupExtArea(const std::list<uint32_t>& list, uint32_t rows); void setupExtArea(const std::list<uint32_t>& list, uint32_t rows);
void clear(); void clear();
private: protected:
enum MatrixOperation_t { enum MatrixOperation_t {
MATRIXOPERATION_COPY, MATRIXOPERATION_COPY,
MATRIXOPERATION_MIRROR, MATRIXOPERATION_MIRROR,
@@ -193,7 +233,7 @@ class AreaCombat
}; };
MatrixArea* createArea(const std::list<uint32_t>& list, uint32_t rows); 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 { MatrixArea* getArea(const Position& centerPos, const Position& targetPos) const {
int32_t dx = Position::getOffsetX(targetPos, centerPos); int32_t dx = Position::getOffsetX(targetPos, centerPos);
@@ -242,7 +282,18 @@ class Combat
Combat(const Combat&) = delete; Combat(const Combat&) = delete;
Combat& operator=(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 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); 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 ReturnValue canDoCombat(Creature* attacker, Creature* target);
static void postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params); 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, Creature* target) const;
void doCombat(Creature* caster, const Position& position) const; void doCombat(Creature* caster, const Position& pos) const;
bool setCallback(CallBackParam_t key); bool setCallback(CallBackParam_t key);
CallBack* getCallback(CallBackParam_t key); CallBack* getCallback(CallBackParam_t key);
@@ -281,12 +332,9 @@ class Combat
bool hasArea() const { bool hasArea() const {
return area != nullptr; return area != nullptr;
} }
void addCondition(const Condition* condition) { void setCondition(const Condition* condition) {
params.conditionList.emplace_front(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 setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb);
void postCombatEffects(Creature* caster, const Position& pos) const { void postCombatEffects(Creature* caster, const Position& pos) const {
postCombatEffects(caster, pos, params); postCombatEffects(caster, pos, params);
@@ -296,10 +344,13 @@ class Combat
params.origin = origin; 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 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 CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static void CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage); 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 CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
static void CombatNullFunc(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); static void combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params);
CombatDamage getCombatDamage(Creature* creature, Creature* target) const; CombatDamage getCombatDamage(Creature* creature) const;
//configureable //configureable
CombatParams params; CombatParams params;
@@ -328,10 +379,10 @@ class MagicField final : public Item
public: public:
explicit MagicField(uint16_t type) : Item(type), createTime(OTSYS_TIME()) {} explicit MagicField(uint16_t type) : Item(type), createTime(OTSYS_TIME()) {}
MagicField* getMagicField() override { MagicField* getMagicField() final {
return this; return this;
} }
const MagicField* getMagicField() const override { const MagicField* getMagicField() const final {
return this; return this;
} }
@@ -342,13 +393,6 @@ class MagicField final : public Item
const ItemType& it = items[getID()]; const ItemType& it = items[getID()];
return it.combatType; 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); void onStepInField(Creature* creature);
private: 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -35,10 +35,13 @@ enum ConditionAttr_t {
CONDITIONATTR_HEALTHGAIN, CONDITIONATTR_HEALTHGAIN,
CONDITIONATTR_MANATICKS, CONDITIONATTR_MANATICKS,
CONDITIONATTR_MANAGAIN, CONDITIONATTR_MANAGAIN,
CONDITIONATTR_DELAYED,
CONDITIONATTR_OWNER, CONDITIONATTR_OWNER,
CONDITIONATTR_INTERVALDATA, CONDITIONATTR_CYCLE,
CONDITIONATTR_COUNT,
CONDITIONATTR_MAX_COUNT,
CONDITIONATTR_FACTOR_PERCENT,
CONDITIONATTR_SPEEDDELTA, CONDITIONATTR_SPEEDDELTA,
CONDITIONATTR_APPLIEDSPEEDDELTA,
CONDITIONATTR_FORMULA_MINA, CONDITIONATTR_FORMULA_MINA,
CONDITIONATTR_FORMULA_MINB, CONDITIONATTR_FORMULA_MINB,
CONDITIONATTR_FORMULA_MAXA, CONDITIONATTR_FORMULA_MAXA,
@@ -52,8 +55,6 @@ enum ConditionAttr_t {
CONDITIONATTR_SKILLS, CONDITIONATTR_SKILLS,
CONDITIONATTR_STATS, CONDITIONATTR_STATS,
CONDITIONATTR_OUTFIT, CONDITIONATTR_OUTFIT,
CONDITIONATTR_PERIODDAMAGE,
CONDITIONATTR_ISBUFF,
CONDITIONATTR_SUBID, CONDITIONATTR_SUBID,
//reserved for serialization //reserved for serialization
@@ -70,9 +71,9 @@ class Condition
{ {
public: public:
Condition() = default; 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), 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 ~Condition() = default;
virtual bool startCondition(Creature* creature); virtual bool startCondition(Creature* creature);
@@ -100,7 +101,7 @@ class Condition
} }
void setTicks(int32_t newTicks); 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); static Condition* createCondition(PropStream& propStream);
virtual bool setParam(ConditionParam_t param, int32_t value); virtual bool setParam(ConditionParam_t param, int32_t value);
@@ -113,23 +114,20 @@ class Condition
bool isPersistent() const; bool isPersistent() const;
protected: protected:
virtual bool updateCondition(const Condition* addCondition);
int64_t endTime; int64_t endTime;
uint32_t subId; uint32_t subId;
int32_t ticks; int32_t ticks;
ConditionType_t conditionType; ConditionType_t conditionType;
bool isBuff;
private:
ConditionId_t id; ConditionId_t id;
virtual bool updateCondition(const Condition* addCondition);
}; };
class ConditionGeneric : public Condition class ConditionGeneric : public Condition
{ {
public: public:
ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0): ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0):
Condition(id, type, ticks, buff, subId) {} Condition(id, type, ticks, subId) {}
bool startCondition(Creature* creature) override; bool startCondition(Creature* creature) override;
bool executeCondition(Creature* creature, int32_t interval) override; bool executeCondition(Creature* creature, int32_t interval) override;
@@ -145,35 +143,32 @@ class ConditionGeneric : public Condition
class ConditionAttributes final : public ConditionGeneric class ConditionAttributes final : public ConditionGeneric
{ {
public: public:
ConditionAttributes(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : ConditionAttributes(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
ConditionGeneric(id, type, ticks, buff, subId) {} ConditionGeneric(id, type, ticks, subId) {}
bool startCondition(Creature* creature) override; bool startCondition(Creature* creature) final;
bool executeCondition(Creature* creature, int32_t interval) override; bool executeCondition(Creature* creature, int32_t interval) final;
void endCondition(Creature* creature) override; void endCondition(Creature* creature) final;
void addCondition(Creature* creature, const Condition* condition) override; 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); return new ConditionAttributes(*this);
} }
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) override; void serialize(PropWriteStream& propWriteStream) final;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
private: protected:
int32_t skills[SKILL_LAST + 1] = {}; int32_t skills[SKILL_LAST + 1] = {};
int32_t skillsPercent[SKILL_LAST + 1] = {}; int32_t skillsPercent[SKILL_LAST + 1] = {};
int32_t specialSkills[SPECIALSKILL_LAST + 1] = {};
int32_t stats[STAT_LAST + 1] = {}; int32_t stats[STAT_LAST + 1] = {};
int32_t statsPercent[STAT_LAST + 1] = {}; int32_t statsPercent[STAT_LAST + 1] = {};
int32_t currentSkill = 0; int32_t currentSkill = 0;
int32_t currentStat = 0; int32_t currentStat = 0;
bool disableDefense = false;
void updatePercentStats(Player* player); void updatePercentStats(Player* player);
void updateStats(Player* player); void updateStats(Player* player);
void updatePercentSkills(Player* player); void updatePercentSkills(Player* player);
@@ -183,23 +178,23 @@ class ConditionAttributes final : public ConditionGeneric
class ConditionRegeneration final : public ConditionGeneric class ConditionRegeneration final : public ConditionGeneric
{ {
public: public:
ConditionRegeneration(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0): ConditionRegeneration(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0):
ConditionGeneric(id, type, ticks, buff, subId) {} ConditionGeneric(id, type, ticks, subId) {}
void addCondition(Creature* creature, const Condition* condition) override; void addCondition(Creature* creature, const Condition* addCondition) final;
bool executeCondition(Creature* creature, int32_t interval) override; 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); return new ConditionRegeneration(*this);
} }
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) override; void serialize(PropWriteStream& propWriteStream) final;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
private: protected:
uint32_t internalHealthTicks = 0; uint32_t internalHealthTicks = 0;
uint32_t internalManaTicks = 0; uint32_t internalManaTicks = 0;
@@ -212,23 +207,23 @@ class ConditionRegeneration final : public ConditionGeneric
class ConditionSoul final : public ConditionGeneric class ConditionSoul final : public ConditionGeneric
{ {
public: public:
ConditionSoul(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : ConditionSoul(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
ConditionGeneric(id, type, ticks, buff, subId) {} ConditionGeneric(id, type, ticks, subId) {}
void addCondition(Creature* creature, const Condition* condition) override; void addCondition(Creature* creature, const Condition* addCondition) final;
bool executeCondition(Creature* creature, int32_t interval) override; 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); return new ConditionSoul(*this);
} }
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) override; void serialize(PropWriteStream& propWriteStream) final;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
private: protected:
uint32_t internalSoulTicks = 0; uint32_t internalSoulTicks = 0;
uint32_t soulTicks = 0; uint32_t soulTicks = 0;
uint32_t soulGain = 0; uint32_t soulGain = 0;
@@ -237,13 +232,13 @@ class ConditionSoul final : public ConditionGeneric
class ConditionInvisible final : public ConditionGeneric class ConditionInvisible final : public ConditionGeneric
{ {
public: public:
ConditionInvisible(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : ConditionInvisible(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
ConditionGeneric(id, type, ticks, buff, subId) {} ConditionGeneric(id, type, ticks, subId) {}
bool startCondition(Creature* creature) override; bool startCondition(Creature* creature) final;
void endCondition(Creature* creature) override; void endCondition(Creature* creature) final;
ConditionInvisible* clone() const override { ConditionInvisible* clone() const final {
return new ConditionInvisible(*this); return new ConditionInvisible(*this);
} }
}; };
@@ -252,170 +247,133 @@ class ConditionDamage final : public Condition
{ {
public: public:
ConditionDamage() = default; ConditionDamage() = default;
ConditionDamage(ConditionId_t id, ConditionType_t type, bool buff = false, uint32_t subId = 0) : ConditionDamage(ConditionId_t id, ConditionType_t type, uint32_t subId = 0) :
Condition(id, type, 0, buff, subId) {} 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; ConditionDamage* clone() const final {
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 {
return new ConditionDamage(*this); 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; int32_t getTotalDamage() const;
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) override; void serialize(PropWriteStream& propWriteStream) final;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
protected:
private: int32_t cycle = 0;
int32_t maxDamage = 0; int32_t count = 0;
int32_t minDamage = 0; int32_t max_count = 0;
int32_t startDamage = 0; int32_t factor_percent = -1;
int32_t periodDamage = 0; int32_t hit_damage = 0;
int32_t periodDamageTick = 0; bool isFirstCycle = true;
int32_t tickInterval = 2000;
bool forceUpdate = false;
bool delayed = false;
bool field = false;
uint32_t owner = 0; uint32_t owner = 0;
bool init();
std::list<IntervalInfo> damageList;
bool getNextDamage(int32_t& damage);
bool doDamage(Creature* creature, int32_t healthChange); bool doDamage(Creature* creature, int32_t healthChange);
bool updateCondition(const Condition* addCondition) override; bool updateCondition(const Condition* addCondition) final;
}; };
class ConditionSpeed final : public Condition class ConditionSpeed final : public Condition
{ {
public: public:
ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, int32_t changeSpeed) : ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId, int32_t changeSpeed) :
Condition(id, type, ticks, buff, subId), speedDelta(changeSpeed) {} Condition(id, type, ticks, subId), speedDelta(changeSpeed) {}
bool startCondition(Creature* creature) override; bool startCondition(Creature* creature) final;
bool executeCondition(Creature* creature, int32_t interval) override; bool executeCondition(Creature* creature, int32_t interval) final;
void endCondition(Creature* creature) override; void endCondition(Creature* creature) final;
void addCondition(Creature* creature, const Condition* condition) override; void addCondition(Creature* creature, const Condition* condition) final;
uint32_t getIcons() const override; uint32_t getIcons() const final;
ConditionSpeed* clone() const override { ConditionSpeed* clone() const final {
return new ConditionSpeed(*this); return new ConditionSpeed(*this);
} }
bool setParam(ConditionParam_t param, int32_t value) override; void setVariation(int32_t newVariation) {
variation = newVariation;
void setFormulaVars(float mina, float minb, float maxa, float maxb); }
void setSpeedDelta(int32_t newSpeedDelta) {
speedDelta = newSpeedDelta;
}
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) override; void serialize(PropWriteStream& propWriteStream) final;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
private: protected:
void getFormulaValues(int32_t var, int32_t& min, int32_t& max) const; int32_t appliedSpeedDelta = 0;
int32_t speedDelta = 0;
int32_t speedDelta; int32_t variation = 0;
//formula variables
float mina = 0.0f;
float minb = 0.0f;
float maxa = 0.0f;
float maxb = 0.0f;
}; };
class ConditionOutfit final : public Condition class ConditionOutfit final : public Condition
{ {
public: public:
ConditionOutfit(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) : ConditionOutfit(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
Condition(id, type, ticks, buff, subId) {} Condition(id, type, ticks, subId) {}
bool startCondition(Creature* creature) override; bool startCondition(Creature* creature) final;
bool executeCondition(Creature* creature, int32_t interval) override; bool executeCondition(Creature* creature, int32_t interval) final;
void endCondition(Creature* creature) override; void endCondition(Creature* creature) final;
void addCondition(Creature* creature, const Condition* condition) override; void addCondition(Creature* creature, const Condition* condition) final;
ConditionOutfit* clone() const override { ConditionOutfit* clone() const final {
return new ConditionOutfit(*this); return new ConditionOutfit(*this);
} }
void setOutfit(const Outfit_t& outfit); void setOutfit(const Outfit_t& outfit);
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) override; void serialize(PropWriteStream& propWriteStream) final;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
private: protected:
Outfit_t outfit; Outfit_t outfit;
}; };
class ConditionLight final : public Condition class ConditionLight final : public Condition
{ {
public: public:
ConditionLight(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) : ConditionLight(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) :
Condition(id, type, ticks, buff, subId), lightInfo(lightlevel, lightcolor) {} Condition(id, type, ticks, subId), lightInfo(lightlevel, lightcolor) {}
bool startCondition(Creature* creature) override; bool startCondition(Creature* creature) final;
bool executeCondition(Creature* creature, int32_t interval) override; bool executeCondition(Creature* creature, int32_t interval) final;
void endCondition(Creature* creature) override; void endCondition(Creature* creature) final;
void addCondition(Creature* creature, const Condition* condition) override; void addCondition(Creature* creature, const Condition* addCondition) final;
ConditionLight* clone() const override { ConditionLight* clone() const final {
return new ConditionLight(*this); return new ConditionLight(*this);
} }
bool setParam(ConditionParam_t param, int32_t value) override; bool setParam(ConditionParam_t param, int32_t value) final;
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) override; void serialize(PropWriteStream& propWriteStream) final;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
private: protected:
LightInfo lightInfo; LightInfo lightInfo;
uint32_t internalLightTicks = 0; uint32_t internalLightTicks = 0;
uint32_t lightChangeInterval = 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 #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -19,12 +19,6 @@
#include "otpch.h" #include "otpch.h"
#if __has_include("luajit/lua.hpp")
#include <luajit/lua.hpp>
#else
#include <lua.hpp>
#endif
#include "configmanager.h" #include "configmanager.h"
#include "game.h" #include "game.h"
@@ -35,57 +29,6 @@
extern Game g_game; 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() bool ConfigManager::load()
{ {
lua_State* L = luaL_newstate(); lua_State* L = luaL_newstate();
@@ -120,10 +63,9 @@ bool ConfigManager::load()
integer[GAME_PORT] = getGlobalNumber(L, "gameProtocolPort", 7172); integer[GAME_PORT] = getGlobalNumber(L, "gameProtocolPort", 7172);
integer[LOGIN_PORT] = getGlobalNumber(L, "loginProtocolPort", 7171); integer[LOGIN_PORT] = getGlobalNumber(L, "loginProtocolPort", 7171);
integer[STATUS_PORT] = getGlobalNumber(L, "statusProtocolPort", 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[ALLOW_CHANGEOUTFIT] = getGlobalBoolean(L, "allowChangeOutfit", true);
boolean[ONE_PLAYER_ON_ACCOUNT] = getGlobalBoolean(L, "onePlayerOnlinePerAccount", true); boolean[ONE_PLAYER_ON_ACCOUNT] = getGlobalBoolean(L, "onePlayerOnlinePerAccount", true);
boolean[AIMBOT_HOTKEY_ENABLED] = getGlobalBoolean(L, "hotkeyAimbotEnabled", true); boolean[AIMBOT_HOTKEY_ENABLED] = getGlobalBoolean(L, "hotkeyAimbotEnabled", true);
@@ -132,19 +74,14 @@ bool ConfigManager::load()
boolean[FREE_PREMIUM] = getGlobalBoolean(L, "freePremium", false); boolean[FREE_PREMIUM] = getGlobalBoolean(L, "freePremium", false);
boolean[REPLACE_KICK_ON_LOGIN] = getGlobalBoolean(L, "replaceKickOnLogin", true); boolean[REPLACE_KICK_ON_LOGIN] = getGlobalBoolean(L, "replaceKickOnLogin", true);
boolean[ALLOW_CLONES] = getGlobalBoolean(L, "allowClones", false); 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[STAMINA_SYSTEM] = getGlobalBoolean(L, "staminaSystem", true);
boolean[WARN_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "warnUnsafeScripts", true); boolean[WARN_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "warnUnsafeScripts", true);
boolean[CONVERT_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "convertUnsafeScripts", true); boolean[CONVERT_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "convertUnsafeScripts", true);
boolean[CLASSIC_EQUIPMENT_SLOTS] = getGlobalBoolean(L, "classicEquipmentSlots", false); boolean[TELEPORT_NEWBIES] = getGlobalBoolean(L, "teleportNewbies", true);
boolean[CLASSIC_ATTACK_SPEED] = getGlobalBoolean(L, "classicAttackSpeed", false); boolean[STACK_CUMULATIVES] = getGlobalBoolean(L, "autoStackCumulatives", false);
boolean[SCRIPTS_CONSOLE_LOGS] = getGlobalBoolean(L, "showScriptsLogInConsole", true); boolean[BLOCK_HEIGHT] = getGlobalBoolean(L, "blockHeight", false);
boolean[SERVER_SAVE_NOTIFY_MESSAGE] = getGlobalBoolean(L, "serverSaveNotifyMessage", true); boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false);
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);
string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high"); string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high");
string[SERVER_NAME] = getGlobalString(L, "serverName", ""); string[SERVER_NAME] = getGlobalString(L, "serverName", "");
@@ -164,9 +101,7 @@ bool ConfigManager::load()
integer[RATE_LOOT] = getGlobalNumber(L, "rateLoot", 2); integer[RATE_LOOT] = getGlobalNumber(L, "rateLoot", 2);
integer[RATE_MAGIC] = getGlobalNumber(L, "rateMagic", 3); integer[RATE_MAGIC] = getGlobalNumber(L, "rateMagic", 3);
integer[RATE_SPAWN] = getGlobalNumber(L, "rateSpawn", 1); integer[RATE_SPAWN] = getGlobalNumber(L, "rateSpawn", 1);
integer[HOUSE_PRICE] = getGlobalNumber(L, "housePriceEachSQM", 1000); integer[BAN_LENGTH] = getGlobalNumber(L, "banLength", 30 * 24 * 60 * 60);
integer[KILLS_TO_RED] = getGlobalNumber(L, "killsToRedSkull", 3);
integer[KILLS_TO_BLACK] = getGlobalNumber(L, "killsToBlackSkull", 6);
integer[ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenActions", 200); integer[ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenActions", 200);
integer[EX_ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenExActions", 1000); integer[EX_ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenExActions", 1000);
integer[MAX_MESSAGEBUFFER] = getGlobalNumber(L, "maxMessageBuffer", 4); integer[MAX_MESSAGEBUFFER] = getGlobalNumber(L, "maxMessageBuffer", 4);
@@ -174,14 +109,20 @@ bool ConfigManager::load()
integer[PROTECTION_LEVEL] = getGlobalNumber(L, "protectionLevel", 1); integer[PROTECTION_LEVEL] = getGlobalNumber(L, "protectionLevel", 1);
integer[DEATH_LOSE_PERCENT] = getGlobalNumber(L, "deathLosePercent", -1); integer[DEATH_LOSE_PERCENT] = getGlobalNumber(L, "deathLosePercent", -1);
integer[STATUSQUERY_TIMEOUT] = getGlobalNumber(L, "statusTimeout", 5000); 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);
integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60 * 1000); 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[STAIRHOP_DELAY] = getGlobalNumber(L, "stairJumpExhaustion", 2000);
integer[EXP_FROM_PLAYERS_LEVEL_RANGE] = getGlobalNumber(L, "expFromPlayersLevelRange", 75); 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[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; loaded = true;
lua_close(L); lua_close(L);
@@ -197,13 +138,11 @@ bool ConfigManager::reload()
return result; return result;
} }
static std::string dummyStr;
const std::string& ConfigManager::getString(string_config_t what) const const std::string& ConfigManager::getString(string_config_t what) const
{ {
if (what >= LAST_STRING_CONFIG) { if (what >= LAST_STRING_CONFIG) {
std::cout << "[Warning - ConfigManager::getString] Accessing invalid index: " << what << std::endl; std::cout << "[Warning - ConfigManager::getString] Accessing invalid index: " << what << std::endl;
return dummyStr; return string[DUMMY_STR];
} }
return string[what]; return string[what];
} }
@@ -225,3 +164,47 @@ bool ConfigManager::getBoolean(boolean_config_t what) const
} }
return boolean[what]; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -20,10 +20,13 @@
#ifndef FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39 #ifndef FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39
#define FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39 #define FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39
#include <luajit/lua.hpp>
class ConfigManager class ConfigManager
{ {
public: public:
enum boolean_config_t { enum boolean_config_t {
SHOW_MONSTER_LOOT,
ALLOW_CHANGEOUTFIT, ALLOW_CHANGEOUTFIT,
ONE_PLAYER_ON_ACCOUNT, ONE_PLAYER_ON_ACCOUNT,
AIMBOT_HOTKEY_ENABLED, AIMBOT_HOTKEY_ENABLED,
@@ -34,24 +37,19 @@ class ConfigManager
ALLOW_CLONES, ALLOW_CLONES,
BIND_ONLY_GLOBAL_ADDRESS, BIND_ONLY_GLOBAL_ADDRESS,
OPTIMIZE_DATABASE, OPTIMIZE_DATABASE,
MARKET_PREMIUM,
EMOTE_SPELLS,
STAMINA_SYSTEM, STAMINA_SYSTEM,
WARN_UNSAFE_SCRIPTS, WARN_UNSAFE_SCRIPTS,
CONVERT_UNSAFE_SCRIPTS, CONVERT_UNSAFE_SCRIPTS,
CLASSIC_EQUIPMENT_SLOTS, TELEPORT_NEWBIES,
CLASSIC_ATTACK_SPEED, STACK_CUMULATIVES,
SCRIPTS_CONSOLE_LOGS, BLOCK_HEIGHT,
SERVER_SAVE_NOTIFY_MESSAGE, DROP_ITEMS,
SERVER_SAVE_CLEAN_MAP,
SERVER_SAVE_CLOSE,
SERVER_SAVE_SHUTDOWN,
ONLINE_OFFLINE_CHARLIST,
LAST_BOOLEAN_CONFIG /* this must be the last one */ LAST_BOOLEAN_CONFIG /* this must be the last one */
}; };
enum string_config_t { enum string_config_t {
DUMMY_STR,
MAP_NAME, MAP_NAME,
HOUSE_RENT_PERIOD, HOUSE_RENT_PERIOD,
SERVER_NAME, SERVER_NAME,
@@ -84,9 +82,7 @@ class ConfigManager
RATE_LOOT, RATE_LOOT,
RATE_MAGIC, RATE_MAGIC,
RATE_SPAWN, RATE_SPAWN,
HOUSE_PRICE, BAN_LENGTH,
KILLS_TO_RED,
KILLS_TO_BLACK,
MAX_MESSAGEBUFFER, MAX_MESSAGEBUFFER,
ACTIONS_DELAY_INTERVAL, ACTIONS_DELAY_INTERVAL,
EX_ACTIONS_DELAY_INTERVAL, EX_ACTIONS_DELAY_INTERVAL,
@@ -94,18 +90,23 @@ class ConfigManager
PROTECTION_LEVEL, PROTECTION_LEVEL,
DEATH_LOSE_PERCENT, DEATH_LOSE_PERCENT,
STATUSQUERY_TIMEOUT, STATUSQUERY_TIMEOUT,
FRAG_TIME,
WHITE_SKULL_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, GAME_PORT,
LOGIN_PORT, LOGIN_PORT,
STATUS_PORT, STATUS_PORT,
STAIRHOP_DELAY, STAIRHOP_DELAY,
MARKET_OFFER_DURATION,
CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES,
MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER,
EXP_FROM_PLAYERS_LEVEL_RANGE, EXP_FROM_PLAYERS_LEVEL_RANGE,
MAX_PACKETS_PER_SECOND, MAX_PACKETS_PER_SECOND,
SERVER_SAVE_NOTIFY_DURATION, NEWBIE_TOWN,
NEWBIE_LEVEL_THRESHOLD,
MONEY_RATE,
LAST_INTEGER_CONFIG /* this must be the last one */ LAST_INTEGER_CONFIG /* this must be the last one */
}; };
@@ -118,6 +119,10 @@ class ConfigManager
bool getBoolean(boolean_config_t what) const; bool getBoolean(boolean_config_t what) const;
private: 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] = {}; std::string string[LAST_STRING_CONFIG] = {};
int32_t integer[LAST_INTEGER_CONFIG] = {}; int32_t integer[LAST_INTEGER_CONFIG] = {};
bool boolean[LAST_BOOLEAN_CONFIG] = {}; bool boolean[LAST_BOOLEAN_CONFIG] = {};

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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) { if (error) {
close(FORCE_CLOSE); close(FORCE_CLOSE);
return; return;
} else if (connectionState != CONNECTION_STATE_OPEN) { }
else if (connectionState != CONNECTION_STATE_OPEN) {
return; 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) { if (!receivedFirst) {
// First message received // First message received
receivedFirst = true; receivedFirst = true;
if (!protocol) { if (!protocol) {
// Game protocol has already been created at this point // 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) { if (!protocol) {
close(FORCE_CLOSE); close(FORCE_CLOSE);
return; return;
} }
} else { }
else {
msg.skipBytes(1); // Skip protocol ID msg.skipBytes(1); // Skip protocol ID
} }
protocol->onRecvFirstMessage(msg); protocol->onRecvFirstMessage(msg);
} else { }
else {
protocol->onRecvMessage(msg); // Send the packet to the current protocol protocol->onRecvMessage(msg); // Send the packet to the current protocol
} }
try { try {
readTimer.expires_from_now(boost::posix_time::seconds(CONNECTION_READ_TIMEOUT)); 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()), 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 // Wait to the next packet
boost::asio::async_read(socket, boost::asio::async_read(socket,
boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH), boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1)); std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
} catch (boost::system::system_error& e) { }
catch (boost::system::system_error& e) {
std::cout << "[Network error - Connection::parsePacket] " << e.what() << std::endl; std::cout << "[Network error - Connection::parsePacket] " << e.what() << std::endl;
close(FORCE_CLOSE); close(FORCE_CLOSE);
} }
} }
void Connection::send(const OutputMessage_ptr& msg) void Connection::send(const OutputMessage_ptr& msg)
{ {
std::lock_guard<std::recursive_mutex> lockClass(connectionLock); 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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; static constexpr int32_t CONNECTION_READ_TIMEOUT = 30;
class Protocol; class Protocol;
using Protocol_ptr = std::shared_ptr<Protocol>; typedef std::shared_ptr<Protocol> Protocol_ptr;
class OutputMessage; class OutputMessage;
using OutputMessage_ptr = std::shared_ptr<OutputMessage>; typedef std::shared_ptr<OutputMessage> OutputMessage_ptr;
class Connection; class Connection;
using Connection_ptr = std::shared_ptr<Connection> ; typedef std::shared_ptr<Connection> Connection_ptr;
using ConnectionWeak_ptr = std::weak_ptr<Connection>; typedef std::weak_ptr<Connection> ConnectionWeak_ptr;
class ServiceBase; class ServiceBase;
using Service_ptr = std::shared_ptr<ServiceBase>; typedef std::shared_ptr<ServiceBase> Service_ptr;
class ServicePort; class ServicePort;
using ServicePort_ptr = std::shared_ptr<ServicePort>; typedef std::shared_ptr<ServicePort> ServicePort_ptr;
using ConstServicePort_ptr = std::shared_ptr<const ServicePort>; typedef std::shared_ptr<const ServicePort> ConstServicePort_ptr;
class ConnectionManager class ConnectionManager
{ {
@@ -52,7 +52,7 @@ class ConnectionManager
void releaseConnection(const Connection_ptr& connection); void releaseConnection(const Connection_ptr& connection);
void closeAll(); void closeAll();
private: protected:
ConnectionManager() = default; ConnectionManager() = default;
std::unordered_set<Connection_ptr> connections; std::unordered_set<Connection_ptr> connections;
@@ -78,8 +78,12 @@ class Connection : public std::enable_shared_from_this<Connection>
readTimer(io_service), readTimer(io_service),
writeTimer(io_service), writeTimer(io_service),
service_port(std::move(service_port)), service_port(std::move(service_port)),
socket(io_service), socket(io_service) {
timeConnected(time(nullptr)) {} connectionState = CONNECTION_STATE_OPEN;
receivedFirst = false;
packetsSent = 0;
timeConnected = time(nullptr);
}
~Connection(); ~Connection();
friend class ConnectionManager; friend class ConnectionManager;
@@ -124,10 +128,10 @@ class Connection : public std::enable_shared_from_this<Connection>
boost::asio::ip::tcp::socket socket; boost::asio::ip::tcp::socket socket;
time_t timeConnected; time_t timeConnected;
uint32_t packetsSent = 0; uint32_t packetsSent;
bool connectionState = CONNECTION_STATE_OPEN; bool connectionState;
bool receivedFirst = false; bool receivedFirst;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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_SOUND_WHITE = 25,
CONST_ME_BUBBLES = 26, CONST_ME_BUBBLES = 26,
CONST_ME_CRAPS = 27, 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 { enum ShootType_t : uint8_t {
@@ -133,192 +72,75 @@ enum ShootType_t : uint8_t {
CONST_ANI_SNOWBALL = 13, CONST_ANI_SNOWBALL = 13,
CONST_ANI_POWERBOLT = 14, CONST_ANI_POWERBOLT = 14,
CONST_ANI_POISON = 15, 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 { enum SpeakClasses : uint8_t {
TALKTYPE_SAY = 1, TALKTYPE_SAY = 1,
TALKTYPE_WHISPER = 2, TALKTYPE_WHISPER = 2,
TALKTYPE_YELL = 3, TALKTYPE_YELL = 3,
TALKTYPE_PRIVATE_FROM = 4, TALKTYPE_PRIVATE = 4,
TALKTYPE_PRIVATE_TO = 5, TALKTYPE_CHANNEL_Y = 5, // Yellow
TALKTYPE_CHANNEL_Y = 7, TALKTYPE_RVR_CHANNEL = 6,
TALKTYPE_CHANNEL_O = 8, TALKTYPE_RVR_ANSWER = 7,
TALKTYPE_PRIVATE_NP = 10, TALKTYPE_RVR_CONTINUE = 8,
TALKTYPE_PRIVATE_PN = 12, TALKTYPE_BROADCAST = 9,
TALKTYPE_BROADCAST = 13, TALKTYPE_CHANNEL_R1 = 10, // Red - #c text
TALKTYPE_CHANNEL_R1 = 14, //red - #c text TALKTYPE_PRIVATE_RED = 11, // @name@text
TALKTYPE_PRIVATE_RED_FROM = 15, //@name@text TALKTYPE_CHANNEL_O = 12, // orange
TALKTYPE_PRIVATE_RED_TO = 16, //@name@text TALKTYPE_CHANNEL_R2 = 13, // red anonymous - #d text
TALKTYPE_MONSTER_SAY = 36, TALKTYPE_MONSTER_YELL = 0x10,
TALKTYPE_MONSTER_YELL = 37, TALKTYPE_MONSTER_SAY = 0x11,
TALKTYPE_CHANNEL_R2 = 0xFF, //#d
}; };
enum MessageClasses : uint8_t { 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_CLASS_FIRST = MESSAGE_STATUS_CONSOLE_YELLOW,
MESSAGE_CLASS_LAST = MESSAGE_STATUS_CONSOLE_RED,
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*/
}; };
enum FluidColors_t : uint8_t { enum FluidTypes_t : uint8_t
FLUID_EMPTY, {
FLUID_BLUE, FLUID_NONE = 0,
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,
FLUID_WATER, FLUID_WATER,
FLUID_MANA, FLUID_WINE,
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_BEER, FLUID_BEER,
FLUID_MUD, FLUID_MUD,
FLUID_BLOOD, FLUID_BLOOD,
FLUID_SLIME, FLUID_SLIME,
FLUID_RUM,
FLUID_LEMONADE,
FLUID_MILK,
FLUID_WINE,
FLUID_LIFE,
FLUID_URINE,
FLUID_OIL, FLUID_OIL,
FLUID_FRUITJUICE, FLUID_URINE,
FLUID_MILK,
FLUID_MANAFLUID,
FLUID_LIFEFLUID,
FLUID_LEMONADE,
FLUID_RUM,
FLUID_COCONUTMILK, FLUID_COCONUTMILK,
FLUID_TEA, FLUID_FRUITJUICE,
FLUID_MEAD,
}; };
enum ClientFluidTypes_t : uint8_t { enum FluidColor_t : uint8_t
CLIENTFLUID_EMPTY = 0, {
CLIENTFLUID_BLUE = 1, FLUID_COLOR_NONE = 0,
CLIENTFLUID_PURPLE = 2, FLUID_COLOR_BLUE = 1,
CLIENTFLUID_BROWN_1 = 3, FLUID_COLOR_PURPLE = 2,
CLIENTFLUID_BROWN_2 = 4, FLUID_COLOR_BROWN = 3,
CLIENTFLUID_RED = 5, FLUID_COLOR_BROWN1 = 4,
CLIENTFLUID_GREEN = 6, FLUID_COLOR_RED = 5,
CLIENTFLUID_BROWN = 7, FLUID_COLOR_GREEN = 6,
CLIENTFLUID_YELLOW = 8, FLUID_COLOR_BROWN2 = 7,
CLIENTFLUID_WHITE = 9, FLUID_COLOR_YELLOW = 8,
}; FLUID_COLOR_WHITE = 9,
const uint8_t fluidMap[] = {
CLIENTFLUID_EMPTY,
CLIENTFLUID_BLUE,
CLIENTFLUID_RED,
CLIENTFLUID_BROWN_1,
CLIENTFLUID_GREEN,
CLIENTFLUID_YELLOW,
CLIENTFLUID_WHITE,
CLIENTFLUID_PURPLE,
}; };
enum SquareColor_t : uint8_t { enum SquareColor_t : uint8_t {
@@ -333,10 +155,8 @@ enum TextColor_t : uint8_t {
TEXTCOLOR_DARKRED = 108, TEXTCOLOR_DARKRED = 108,
TEXTCOLOR_LIGHTGREY = 129, TEXTCOLOR_LIGHTGREY = 129,
TEXTCOLOR_SKYBLUE = 143, TEXTCOLOR_SKYBLUE = 143,
TEXTCOLOR_PURPLE = 154, TEXTCOLOR_PURPLE = 155,
TEXTCOLOR_ELECTRICPURPLE = 155,
TEXTCOLOR_RED = 180, TEXTCOLOR_RED = 180,
TEXTCOLOR_PASTELRED = 194,
TEXTCOLOR_ORANGE = 198, TEXTCOLOR_ORANGE = 198,
TEXTCOLOR_YELLOW = 210, TEXTCOLOR_YELLOW = 210,
TEXTCOLOR_WHITE_EXP = 215, TEXTCOLOR_WHITE_EXP = 215,
@@ -353,13 +173,6 @@ enum Icons_t {
ICON_HASTE = 1 << 6, ICON_HASTE = 1 << 6,
ICON_SWORDS = 1 << 7, ICON_SWORDS = 1 << 7,
ICON_DROWNING = 1 << 8, 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 { enum WeaponType_t : uint8_t {
@@ -392,7 +205,6 @@ enum WeaponAction_t : uint8_t {
}; };
enum WieldInfo_t { enum WieldInfo_t {
WIELDINFO_NONE = 0 << 0,
WIELDINFO_LEVEL = 1 << 0, WIELDINFO_LEVEL = 1 << 0,
WIELDINFO_MAGLV = 1 << 1, WIELDINFO_MAGLV = 1 << 1,
WIELDINFO_VOCREQ = 1 << 2, WIELDINFO_VOCREQ = 1 << 2,
@@ -405,8 +217,6 @@ enum Skulls_t : uint8_t {
SKULL_GREEN = 2, SKULL_GREEN = 2,
SKULL_WHITE = 3, SKULL_WHITE = 3,
SKULL_RED = 4, SKULL_RED = 4,
SKULL_BLACK = 5,
SKULL_ORANGE = 6,
}; };
enum PartyShields_t : uint8_t { enum PartyShields_t : uint8_t {
@@ -414,97 +224,54 @@ enum PartyShields_t : uint8_t {
SHIELD_WHITEYELLOW = 1, SHIELD_WHITEYELLOW = 1,
SHIELD_WHITEBLUE = 2, SHIELD_WHITEBLUE = 2,
SHIELD_BLUE = 3, SHIELD_BLUE = 3,
SHIELD_YELLOW = 4, 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,
}; };
enum item_t : uint16_t { 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_POISONFIELD_PVP = 2121,
ITEM_FIREFIELD_PVP_MEDIUM = 1488, ITEM_POISONFIELD_PERSISTENT = 2127,
ITEM_FIREFIELD_PVP_SMALL = 1489, ITEM_POISONFIELD_NOPVP = 2134,
ITEM_FIREFIELD_PERSISTENT_FULL = 1492,
ITEM_FIREFIELD_PERSISTENT_MEDIUM = 1493,
ITEM_FIREFIELD_PERSISTENT_SMALL = 1494,
ITEM_FIREFIELD_NOPVP = 1500,
ITEM_POISONFIELD_PVP = 1490, ITEM_ENERGYFIELD_PVP = 2122,
ITEM_POISONFIELD_PERSISTENT = 1496, ITEM_ENERGYFIELD_PERSISTENT = 2126,
ITEM_POISONFIELD_NOPVP = 1503, ITEM_ENERGYFIELD_NOPVP = 2135,
ITEM_ENERGYFIELD_PVP = 1491, ITEM_MAGICWALL = 2128,
ITEM_ENERGYFIELD_PERSISTENT = 1495, ITEM_MAGICWALL_PERSISTENT = 2128,
ITEM_ENERGYFIELD_NOPVP = 1504,
ITEM_MAGICWALL = 1497, ITEM_WILDGROWTH = 2130,
ITEM_MAGICWALL_PERSISTENT = 1498, ITEM_WILDGROWTH_PERSISTENT = 2130,
ITEM_MAGICWALL_SAFE = 11098,
ITEM_WILDGROWTH = 1499, ITEM_GOLD_COIN = 3031,
ITEM_WILDGROWTH_PERSISTENT = 2721, ITEM_PLATINUM_COIN = 3035,
ITEM_WILDGROWTH_SAFE = 11099, ITEM_CRYSTAL_COIN = 3043,
ITEM_BAG = 1987, ITEM_DEPOT = 3502,
ITEM_SHOPPING_BAG = 23782, ITEM_LOCKER1 = 3497,
ITEM_GOLD_COIN = 2148, ITEM_MALE_CORPSE = 4240,
ITEM_PLATINUM_COIN = 2152, ITEM_FEMALE_CORPSE = 4247,
ITEM_CRYSTAL_COIN = 2160,
ITEM_STORE_COIN = 24774, // in-game store currency
ITEM_DEPOT = 2594, ITEM_FULLSPLASH = 2886,
ITEM_LOCKER1 = 2589, ITEM_SMALLSPLASH = 2889,
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_MALE_CORPSE = 3058, ITEM_PARCEL = 3503,
ITEM_FEMALE_CORPSE = 3065, ITEM_PARCEL_STAMPED = 3504,
ITEM_LETTER = 3505,
ITEM_LETTER_STAMPED = 3506,
ITEM_LABEL = 3507,
ITEM_FULLSPLASH = 2016, ITEM_AMULETOFLOSS = 3057,
ITEM_SMALLSPLASH = 2019,
ITEM_PARCEL = 2595, ITEM_DOCUMENT_RO = 2819, //read-only
ITEM_LETTER = 2597,
ITEM_LETTER_STAMPED = 2598,
ITEM_LABEL = 2599,
ITEM_AMULETOFLOSS = 2173,
ITEM_DOCUMENT_RO = 1968, //read-only
}; };
enum PlayerFlags : uint64_t { enum PlayerFlags : uint64_t {
@@ -546,12 +313,14 @@ enum PlayerFlags : uint64_t {
PlayerFlag_IgnoreWeaponCheck = static_cast<uint64_t>(1) << 35, PlayerFlag_IgnoreWeaponCheck = static_cast<uint64_t>(1) << 35,
PlayerFlag_CannotBeMuted = static_cast<uint64_t>(1) << 36, PlayerFlag_CannotBeMuted = static_cast<uint64_t>(1) << 36,
PlayerFlag_IsAlwaysPremium = static_cast<uint64_t>(1) << 37, 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_ALL,
RELOAD_TYPE_ACTIONS, RELOAD_TYPE_ACTIONS,
RELOAD_TYPE_CHAT, RELOAD_TYPE_CHAT,
RELOAD_TYPE_COMMANDS,
RELOAD_TYPE_CONFIG, RELOAD_TYPE_CONFIG,
RELOAD_TYPE_CREATURESCRIPTS, RELOAD_TYPE_CREATURESCRIPTS,
RELOAD_TYPE_EVENTS, RELOAD_TYPE_EVENTS,
@@ -564,7 +333,6 @@ enum ReloadTypes_t : uint8_t {
RELOAD_TYPE_NPCS, RELOAD_TYPE_NPCS,
RELOAD_TYPE_QUESTS, RELOAD_TYPE_QUESTS,
RELOAD_TYPE_RAIDS, RELOAD_TYPE_RAIDS,
RELOAD_TYPE_SCRIPTS,
RELOAD_TYPE_SPELLS, RELOAD_TYPE_SPELLS,
RELOAD_TYPE_TALKACTIONS, RELOAD_TYPE_TALKACTIONS,
RELOAD_TYPE_WEAPONS, RELOAD_TYPE_WEAPONS,
@@ -572,6 +340,7 @@ enum ReloadTypes_t : uint8_t {
static constexpr int32_t CHANNEL_GUILD = 0x00; static constexpr int32_t CHANNEL_GUILD = 0x00;
static constexpr int32_t CHANNEL_PARTY = 0x01; static constexpr int32_t CHANNEL_PARTY = 0x01;
static constexpr int32_t CHANNEL_RULE_REP = 0x02;
static constexpr int32_t CHANNEL_PRIVATE = 0xFFFF; static constexpr int32_t CHANNEL_PRIVATE = 0xFFFF;
//Reserved player storage key ranges; //Reserved player storage key ranges;
@@ -581,11 +350,6 @@ static constexpr int32_t PSTRG_RESERVED_RANGE_SIZE = 10000000;
//[1000 - 1500]; //[1000 - 1500];
static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000); static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000);
static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500; 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)) #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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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::Container(uint16_t type) :
Container(type, items[type].maxItems) {} 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), Item(type),
maxSize(size), maxSize(size)
unlocked(unlocked),
pagination(pagination)
{} {}
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() Container::~Container()
{ {
if (getID() == ITEM_BROWSEFIELD) { for (Item* item : itemlist) {
g_game.browseFields.erase(getTile()); item->setParent(nullptr);
item->decrementReferenceCounter();
for (Item* item : itemlist) {
item->setParent(parent);
}
} else {
for (Item* item : itemlist) {
item->setParent(nullptr);
item->decrementReferenceCounter();
}
} }
} }
@@ -87,7 +62,7 @@ Container* Container::getParentContainer()
bool Container::hasParent() const 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) void Container::addItem(Item* item)
@@ -107,22 +82,24 @@ Attr_ReadValue Container::readAttr(AttrTypes_t attr, PropStream& propStream)
return Item::readAttr(attr, 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) { if (!ret) {
return false; return false;
} }
for (auto& itemNode : node.children) { uint32_t type;
NODE nodeItem = f.getChildNode(node, type);
while (nodeItem) {
//load container items //load container items
if (itemNode.type != OTBM_ITEM) { if (type != OTBM_ITEM) {
// unknown type // unknown type
return false; return false;
} }
PropStream itemPropStream; PropStream itemPropStream;
if (!loader.getProps(itemNode, itemPropStream)) { if (!f.getProps(nodeItem, itemPropStream)) {
return false; return false;
} }
@@ -131,12 +108,14 @@ bool Container::unserializeItemNode(OTB::Loader& loader, const OTB::Node& node,
return false; return false;
} }
if (!item->unserializeItemNode(loader, itemNode, itemPropStream)) { if (!item->unserializeItemNode(f, nodeItem, itemPropStream)) {
return false; return false;
} }
addItem(item); addItem(item);
updateItemWeight(item->getWeight()); updateItemWeight(item->getWeight());
nodeItem = f.getNextNode(nodeItem, type);
} }
return true; return true;
} }
@@ -215,48 +194,48 @@ bool Container::isHoldingItem(const Item* item) const
void Container::onAddContainerItem(Item* item) void Container::onAddContainerItem(Item* item)
{ {
SpectatorVec spectators; SpectatorVec list;
g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2); g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2);
//send to client //send to client
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
spectator->getPlayer()->sendAddContainerItem(this, item); spectator->getPlayer()->sendAddContainerItem(this, item);
} }
//event methods //event methods
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
spectator->getPlayer()->onAddContainerItem(item); spectator->getPlayer()->onAddContainerItem(item);
} }
} }
void Container::onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem) void Container::onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem)
{ {
SpectatorVec spectators; SpectatorVec list;
g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2); g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2);
//send to client //send to client
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
spectator->getPlayer()->sendUpdateContainerItem(this, index, newItem); spectator->getPlayer()->sendUpdateContainerItem(this, index, newItem);
} }
//event methods //event methods
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
spectator->getPlayer()->onUpdateContainerItem(this, oldItem, newItem); spectator->getPlayer()->onUpdateContainerItem(this, oldItem, newItem);
} }
} }
void Container::onRemoveContainerItem(uint32_t index, Item* item) void Container::onRemoveContainerItem(uint32_t index, Item* item)
{ {
SpectatorVec spectators; SpectatorVec list;
g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2); g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2);
//send change to client //send change to client
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
spectator->getPlayer()->sendRemoveContainerItem(this, index); spectator->getPlayer()->sendRemoveContainerItem(this, index);
} }
//event methods //event methods
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
spectator->getPlayer()->onRemoveContainerItem(this, item); spectator->getPlayer()->onRemoveContainerItem(this, item);
} }
} }
@@ -271,10 +250,6 @@ ReturnValue Container::queryAdd(int32_t index, const Thing& thing, uint32_t coun
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
if (!unlocked) {
return RETURNVALUE_NOTPOSSIBLE;
}
const Item* item = thing.getItem(); const Item* item = thing.getItem();
if (item == nullptr) { if (item == nullptr) {
return RETURNVALUE_NOTPOSSIBLE; return RETURNVALUE_NOTPOSSIBLE;
@@ -295,10 +270,6 @@ ReturnValue Container::queryAdd(int32_t index, const Thing& thing, uint32_t coun
return RETURNVALUE_THISISIMPOSSIBLE; return RETURNVALUE_THISISIMPOSSIBLE;
} }
if (dynamic_cast<const Inbox*>(cylinder)) {
return RETURNVALUE_CONTAINERNOTENOUGHROOM;
}
cylinder = cylinder->getParent(); cylinder = cylinder->getParent();
} }
@@ -355,7 +326,7 @@ ReturnValue Container::queryMaxCount(int32_t index, const Thing& thing, uint32_t
} }
} else { } else {
const Item* destItem = getItemByIndex(index); 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(); uint32_t remainder = 100 - destItem->getItemCount();
if (queryAdd(index, *item, remainder, flags) == RETURNVALUE_NOERROR) { if (queryAdd(index, *item, remainder, flags) == RETURNVALUE_NOERROR) {
n = remainder; n = remainder;
@@ -398,14 +369,9 @@ ReturnValue Container::queryRemove(const Thing& thing, uint32_t count, uint32_t
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item** destItem, Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** destItem,
uint32_t& flags) uint32_t& flags)
{ {
if (!unlocked) {
*destItem = nullptr;
return this;
}
if (index == 254 /*move up*/) { if (index == 254 /*move up*/) {
index = INDEX_WHEREEVER; index = INDEX_WHEREEVER;
*destItem = nullptr; *destItem = nullptr;
@@ -420,7 +386,8 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item**
if (index == 255 /*add wherever*/) { if (index == 255 /*add wherever*/) {
index = INDEX_WHEREEVER; index = INDEX_WHEREEVER;
*destItem = nullptr; *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 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 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 (g_config.getBoolean(ConfigManager::STACK_CUMULATIVES)) {
if (autoStack && item->isStackable() && item->getParent() != this) { bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags);
//try find a suitable item to stack with if (autoStack && item->isStackable() && item->getParent() != this) {
uint32_t n = 0; //try find a suitable item to stack with
for (Item* listItem : itemlist) { uint32_t n = 0;
if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) { for (Item* listItem : itemlist) {
*destItem = listItem; if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) {
index = n; *destItem = listItem;
return this; index = n;
return this;
}
++n;
} }
++n;
} }
} }
return this; return this;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -26,7 +26,6 @@
#include "item.h" #include "item.h"
class Container; class Container;
class DepotChest;
class DepotLocker; class DepotLocker;
class ContainerIterator class ContainerIterator
@@ -39,7 +38,7 @@ class ContainerIterator
void advance(); void advance();
Item* operator*(); Item* operator*();
private: protected:
std::list<const Container*> over; std::list<const Container*> over;
ItemDeque::const_iterator cur; ItemDeque::const_iterator cur;
@@ -50,32 +49,31 @@ class Container : public Item, public Cylinder
{ {
public: public:
explicit Container(uint16_t type); explicit Container(uint16_t type);
Container(uint16_t type, uint16_t size, bool unlocked = true, bool pagination = false); Container(uint16_t type, uint16_t size);
explicit Container(Tile* tile);
~Container(); ~Container();
// non-copyable // non-copyable
Container(const Container&) = delete; Container(const Container&) = delete;
Container& operator=(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; return this;
} }
const Container* getContainer() const override final { const Container* getContainer() const final {
return this; return this;
} }
virtual DepotLocker* getDepotLocker() { virtual DepotLocker* getDepotLocker() override {
return nullptr; return nullptr;
} }
virtual const DepotLocker* getDepotLocker() const { virtual const DepotLocker* getDepotLocker() const override {
return nullptr; return nullptr;
} }
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; 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; std::string getContentDescription() const;
size_t size() const { size_t size() const {
@@ -107,60 +105,41 @@ class Container : public Item, public Cylinder
bool isHoldingItem(const Item* item) const; bool isHoldingItem(const Item* item) const;
uint32_t getItemHoldingCount() const; uint32_t getItemHoldingCount() const;
uint32_t getWeight() const override final; uint32_t getWeight() const final;
bool isUnlocked() const {
return unlocked;
}
bool hasPagination() const {
return pagination;
}
//cylinder implementations //cylinder implementations
virtual ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, virtual 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 override;
ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount, ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount,
uint32_t flags) const override final; uint32_t flags) const final;
ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override final; ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final;
Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, 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(Thing* thing) final;
void addThing(int32_t index, Thing* thing) override final; void addThing(int32_t index, Thing* thing) final;
void addItemBack(Item* item); void addItemBack(Item* item);
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override final; void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final;
void replaceThing(uint32_t index, Thing* thing) override 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; int32_t getThingIndex(const Thing* thing) const final;
size_t getFirstIndex() const override final; size_t getFirstIndex() const final;
size_t getLastIndex() const override final; size_t getLastIndex() const final;
uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override 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 override final; std::map<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const final;
Thing* getThing(size_t index) const override 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 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 postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
void internalAddThing(Thing* thing) override final; void internalAddThing(Thing* thing) final;
void internalAddThing(uint32_t index, Thing* thing) override final; void internalAddThing(uint32_t index, Thing* thing) final;
void startDecaying() override final; void startDecaying() final;
protected:
ItemDeque itemlist;
private: 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 onAddContainerItem(Item* item);
void onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem); void onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem);
void onRemoveContainerItem(uint32_t index, Item* item); void onRemoveContainerItem(uint32_t index, Item* item);
@@ -168,6 +147,14 @@ class Container : public Item, public Cylinder
Container* getParentContainer(); Container* getParentContainer();
void updateItemWeight(int32_t diff); 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 ContainerIterator;
friend class IOMapSerialize; friend class IOMapSerialize;
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -25,10 +25,6 @@
#include "configmanager.h" #include "configmanager.h"
#include "scheduler.h" #include "scheduler.h"
double Creature::speedA = 857.36;
double Creature::speedB = 261.29;
double Creature::speedC = -4795.01;
extern Game g_game; extern Game g_game;
extern ConfigManager g_config; extern ConfigManager g_config;
extern CreatureEvents* g_creatureEvents; extern CreatureEvents* g_creatureEvents;
@@ -42,7 +38,8 @@ Creature::~Creature()
{ {
for (Creature* summon : summons) { for (Creature* summon : summons) {
summon->setAttackedCreature(nullptr); summon->setAttackedCreature(nullptr);
summon->removeMaster(); summon->setMaster(nullptr);
summon->decrementReferenceCounter();
} }
for (Condition* condition : conditions) { for (Condition* condition : conditions) {
@@ -230,7 +227,7 @@ void Creature::onWalk(Direction& dir)
if (r < DIRECTION_DIAGONAL_MASK) { if (r < DIRECTION_DIAGONAL_MASK) {
dir = static_cast<Direction>(r); 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) void Creature::updateTileCache(const Tile* tile, int32_t dx, int32_t dy)
{ {
if (std::abs(dx) <= maxWalkCacheWidth && std::abs(dy) <= maxWalkCacheHeight) { 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); onCreatureDisappear(creature, true);
if (creature == this) { if (creature == this) {
if (master && !master->isRemoved()) { if (master && !master->isRemoved()) {
setMaster(nullptr); master->removeSummon(this);
} }
} else if (isMapLoaded) { } else if (isMapLoaded) {
if (creature->getPosition().z == getPosition().z) { 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 (creature == followCreature || (creature == this && followCreature)) {
if (hasFollowPath) { if (hasFollowPath) {
isUpdatingPath = true; 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())) { if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) {
@@ -617,7 +619,8 @@ void Creature::onDeath()
if (lastHitCreature) { if (lastHitCreature) {
lastHitUnjustified = lastHitCreature->onKilledCreature(this); lastHitUnjustified = lastHitCreature->onKilledCreature(this);
lastHitCreatureMaster = lastHitCreature->getMaster(); lastHitCreatureMaster = lastHitCreature->getMaster();
} else { }
else {
lastHitCreatureMaster = nullptr; lastHitCreatureMaster = nullptr;
} }
@@ -639,7 +642,6 @@ void Creature::onDeath()
uint64_t gainExp = getGainedExperience(attacker); uint64_t gainExp = getGainedExperience(attacker);
if (Player* attackerPlayer = attacker->getPlayer()) { if (Player* attackerPlayer = attacker->getPlayer()) {
attackerPlayer->removeAttacked(getPlayer()); attackerPlayer->removeAttacked(getPlayer());
Party* party = attackerPlayer->getParty(); Party* party = attackerPlayer->getParty();
if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) { if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
attacker = party->getLeader(); attacker = party->getLeader();
@@ -649,7 +651,8 @@ void Creature::onDeath()
auto tmpIt = experienceMap.find(attacker); auto tmpIt = experienceMap.find(attacker);
if (tmpIt == experienceMap.end()) { if (tmpIt == experienceMap.end()) {
experienceMap[attacker] = gainExp; experienceMap[attacker] = gainExp;
} else { }
else {
tmpIt->second += gainExp; tmpIt->second += gainExp;
} }
} }
@@ -673,7 +676,7 @@ void Creature::onDeath()
death(lastHitCreature); death(lastHitCreature);
if (master) { if (master) {
setMaster(nullptr); master->removeSummon(this);
} }
if (droppedCorpse) { if (droppedCorpse) {
@@ -683,53 +686,41 @@ void Creature::onDeath()
bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified) bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified)
{ {
if (!lootDrop && getMonster()) { Item* splash;
if (master) { switch (getRace()) {
//scripting event - onDeath case RACE_VENOM:
const CreatureEventList& deathEvents = getCreatureEvents(CREATURE_EVENT_DEATH); splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME);
for (CreatureEvent* deathEvent : deathEvents) { break;
deathEvent->executeOnDeath(this, nullptr, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
}
}
g_game.addMagicEffect(getPosition(), CONST_ME_POFF); case RACE_BLOOD:
} else { splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD);
Item* splash; break;
switch (getRace()) {
case RACE_VENOM:
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME);
break;
case RACE_BLOOD: default:
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD); splash = nullptr;
break; break;
}
default: Tile* tile = getTile();
splash = nullptr;
break;
}
Tile* tile = getTile(); if (splash) {
g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
g_game.startDecay(splash);
}
if (splash) { Item* corpse = getCorpse(lastHitCreature, mostDamageCreature);
g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT); if (corpse) {
g_game.startDecay(splash); g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT);
} g_game.startDecay(corpse);
}
Item* corpse = getCorpse(lastHitCreature, mostDamageCreature); //scripting event - onDeath
if (corpse) { for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) {
g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT); deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
g_game.startDecay(corpse); }
}
//scripting event - onDeath if (corpse) {
for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) { dropLoot(corpse->getContainer(), lastHitCreature);
deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
}
if (corpse) {
dropLoot(corpse->getContainer(), lastHitCreature);
}
} }
return true; return true;
@@ -790,6 +781,28 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3
damage = 0; damage = 0;
blockType = BLOCK_IMMUNITY; blockType = BLOCK_IMMUNITY;
} else if (checkDefense || checkArmor) { } 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; bool hasDefense = false;
if (blockCount > 0) { if (blockCount > 0) {
@@ -797,30 +810,6 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3
hasDefense = true; 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) { if (hasDefense && blockType != BLOCK_NONE) {
onBlockHit(); onBlockHit();
} }
@@ -844,6 +833,16 @@ bool Creature::setAttackedCreature(Creature* creature)
return false; 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; attackedCreature = creature;
onAttackedCreature(attackedCreature); onAttackedCreature(attackedCreature);
attackedCreature->onAttacked(); attackedCreature->onAttacked();
@@ -1027,21 +1026,9 @@ void Creature::onTickCondition(ConditionType_t type, bool& bRemove)
case CONDITION_POISON: case CONDITION_POISON:
bRemove = (field->getCombatType() != COMBAT_EARTHDAMAGE); bRemove = (field->getCombatType() != COMBAT_EARTHDAMAGE);
break; 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: case CONDITION_DROWN:
bRemove = (field->getCombatType() != COMBAT_DROWNDAMAGE); bRemove = (field->getCombatType() != COMBAT_DROWNDAMAGE);
break; break;
case CONDITION_BLEEDING:
bRemove = (field->getCombatType() != COMBAT_PHYSICALDAMAGE);
break;
default: default:
break; break;
} }
@@ -1064,8 +1051,15 @@ void Creature::onAttackedCreatureDrainHealth(Creature* target, int32_t points)
bool Creature::onKilledCreature(Creature* target, bool) bool Creature::onKilledCreature(Creature* target, bool)
{ {
if (latestKillEvent == target->getID()) {
return false;
}
latestKillEvent = target->getID();
if (master) { if (master) {
master->onKilledCreature(target); master->onKilledCreature(target);
return false;
} }
//scripting event - onKill //scripting event - onKill
@@ -1085,43 +1079,28 @@ void Creature::onGainExperience(uint64_t gainExp, Creature* target)
gainExp /= 2; gainExp /= 2;
master->onGainExperience(gainExp, target); master->onGainExperience(gainExp, target);
SpectatorVec spectators; g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(gainExp));
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);
}
} }
bool Creature::setMaster(Creature* newMaster) { void Creature::addSummon(Creature* creature)
if (!newMaster && !master) { {
return false; creature->setDropLoot(false);
} creature->setLossSkill(false);
creature->setMaster(this);
creature->incrementReferenceCounter();
summons.push_back(creature);
}
if (newMaster) { void Creature::removeSummon(Creature* creature)
incrementReferenceCounter(); {
newMaster->summons.push_back(this); 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*/) 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) void Creature::executeConditions(uint32_t interval)
{ {
ConditionList tempConditions{ conditions }; auto it = conditions.begin(), end = conditions.end();
for (Condition* condition : tempConditions) { while (it != end) {
auto it = std::find(conditions.begin(), conditions.end(), condition); Condition* condition = *it;
if (it == conditions.end()) {
continue;
}
if (!condition->executeCondition(this, interval)) { if (!condition->executeCondition(this, interval)) {
it = std::find(conditions.begin(), conditions.end(), condition); ConditionType_t type = condition->getType();
if (it != conditions.end()) {
conditions.erase(it); it = conditions.erase(it);
condition->endCondition(this);
onEndCondition(condition->getType()); condition->endCondition(this);
delete condition; delete condition;
}
onEndCondition(type);
} else {
++it;
} }
} }
} }
@@ -1311,7 +1289,7 @@ bool Creature::hasCondition(ConditionType_t type, uint32_t subId/* = 0*/) const
continue; continue;
} }
if (condition->getEndTime() >= timeNow || condition->getTicks() == -1) { if (condition->getEndTime() >= timeNow) {
return true; return true;
} }
} }
@@ -1348,18 +1326,8 @@ int64_t Creature::getStepDuration() const
return 0; return 0;
} }
uint32_t calculatedStepSpeed;
uint32_t groundSpeed; uint32_t groundSpeed;
int32_t stepSpeed = getStepSpeed(); 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(); Item* ground = tile->getGround();
if (ground) { if (ground) {
@@ -1371,12 +1339,12 @@ int64_t Creature::getStepDuration() const
groundSpeed = 150; groundSpeed = 150;
} }
double duration = std::floor(1000 * groundSpeed / calculatedStepSpeed); double duration = std::floor(1000 * groundSpeed) / stepSpeed;
int64_t stepDuration = std::ceil(duration / 50) * 50; int64_t stepDuration = std::ceil(duration / 50) * 50;
const Monster* monster = getMonster(); const Monster* monster = getMonster();
if (monster && monster->isTargetNearby() && !monster->isFleeing() && !monster->getMaster()) { if (monster && monster->isTargetNearby() && !monster->isFleeing() && !monster->getMaster()) {
stepDuration *= 2; stepDuration *= 3;
} }
return stepDuration; return stepDuration;
@@ -1396,18 +1364,15 @@ int64_t Creature::getEventStepTicks(bool onlyDelay) const
return ret; return ret;
} }
LightInfo Creature::getCreatureLight() const void Creature::getCreatureLight(LightInfo& light) const
{ {
return internalLight; light = internalLight;
}
void Creature::setCreatureLight(LightInfo lightInfo) {
internalLight = std::move(lightInfo);
} }
void Creature::setNormalCreatureLight() void Creature::setNormalCreatureLight()
{ {
internalLight = {}; internalLight.level = 0;
internalLight.color = 0;
} }
bool Creature::registerCreatureEvent(const std::string& name) bool Creature::registerCreatureEvent(const std::string& name)
@@ -1475,10 +1440,6 @@ CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type)
} }
for (CreatureEvent* creatureEvent : eventsList) { for (CreatureEvent* creatureEvent : eventsList) {
if (!creatureEvent->isLoaded()) {
continue;
}
if (creatureEvent->getEventType() == type) { if (creatureEvent->getEventType() == type) {
tmpEventList.push_back(creatureEvent); tmpEventList.push_back(creatureEvent);
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,8 +28,8 @@
#include "enums.h" #include "enums.h"
#include "creatureevent.h" #include "creatureevent.h"
using ConditionList = std::list<Condition*>; typedef std::list<Condition*> ConditionList;
using CreatureEventList = std::list<CreatureEvent*>; typedef std::list<CreatureEvent*> CreatureEventList;
enum slots_t : uint8_t { enum slots_t : uint8_t {
CONST_SLOT_WHEREEVER = 0, CONST_SLOT_WHEREEVER = 0,
@@ -66,6 +66,7 @@ class Monster;
class Npc; class Npc;
class Item; class Item;
class Tile; class Tile;
class Combat;
static constexpr int32_t EVENT_CREATURECOUNT = 10; static constexpr int32_t EVENT_CREATURECOUNT = 10;
static constexpr int32_t EVENT_CREATURE_THINK_INTERVAL = 1000; static constexpr int32_t EVENT_CREATURE_THINK_INTERVAL = 1000;
@@ -82,7 +83,7 @@ class FrozenPathingConditionCall
bool isInRange(const Position& startPos, const Position& testPos, bool isInRange(const Position& startPos, const Position& testPos,
const FindPathParams& fpp) const; const FindPathParams& fpp) const;
private: protected:
Position targetPos; Position targetPos;
}; };
@@ -96,18 +97,16 @@ class Creature : virtual public Thing
Creature(); Creature();
public: public:
static double speedA, speedB, speedC;
virtual ~Creature(); virtual ~Creature();
// non-copyable // non-copyable
Creature(const Creature&) = delete; Creature(const Creature&) = delete;
Creature& operator=(const Creature&) = delete; Creature& operator=(const Creature&) = delete;
Creature* getCreature() override final { Creature* getCreature() final {
return this; return this;
} }
const Creature* getCreature() const override final { const Creature* getCreature() const final {
return this; return this;
} }
virtual Player* getPlayer() { virtual Player* getPlayer() {
@@ -132,8 +131,6 @@ class Creature : virtual public Thing
virtual const std::string& getName() const = 0; virtual const std::string& getName() const = 0;
virtual const std::string& getNameDescription() const = 0; virtual const std::string& getNameDescription() const = 0;
virtual CreatureType_t getType() const = 0;
virtual void setID() = 0; virtual void setID() = 0;
void setRemoved() { void setRemoved() {
isInternalRemoved = true; isInternalRemoved = true;
@@ -172,13 +169,13 @@ class Creature : virtual public Thing
hiddenHealth = b; hiddenHealth = b;
} }
int32_t getThrowRange() const override final { int32_t getThrowRange() const final {
return 1; return 1;
} }
bool isPushable() const override { bool isPushable() const override {
return getWalkDelay() <= 0; return getWalkDelay() <= 0;
} }
bool isRemoved() const override final { bool isRemoved() const final {
return isInternalRemoved; return isInternalRemoved;
} }
virtual bool canSeeInvisibility() const { virtual bool canSeeInvisibility() const {
@@ -199,11 +196,15 @@ class Creature : virtual public Thing
return getSpeed(); return getSpeed();
} }
int32_t getSpeed() const { int32_t getSpeed() const {
return baseSpeed + varSpeed; if (baseSpeed == 0) {
return 0;
}
return (2 * (varSpeed + baseSpeed)) + 80;
} }
void setSpeed(int32_t varSpeedDelta) { void setSpeed(int32_t varSpeedDelta) {
int32_t oldSpeed = getSpeed(); int32_t oldSpeed = getSpeed();
varSpeed = varSpeedDelta; varSpeed += varSpeedDelta;
if (getSpeed() <= 0) { if (getSpeed() <= 0) {
stopEventWalk(); stopEventWalk();
@@ -270,15 +271,9 @@ class Creature : virtual public Thing
virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
bool checkDefense = false, bool checkArmor = false, bool field = false); bool checkDefense = false, bool checkArmor = false, bool field = false);
bool setMaster(Creature* newMaster); void setMaster(Creature* creature) {
master = creature;
void removeMaster() {
if (master) {
master = nullptr;
decrementReferenceCounter();
}
} }
bool isSummon() const { bool isSummon() const {
return master != nullptr; return master != nullptr;
} }
@@ -286,6 +281,8 @@ class Creature : virtual public Thing
return master; return master;
} }
void addSummon(Creature* creature);
void removeSummon(Creature* creature);
const std::list<Creature*>& getSummons() const { const std::list<Creature*>& getSummons() const {
return summons; return summons;
} }
@@ -293,19 +290,9 @@ class Creature : virtual public Thing
virtual int32_t getArmor() const { virtual int32_t getArmor() const {
return 0; return 0;
} }
virtual int32_t getDefense() const { virtual int32_t getDefense() {
return 0; 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 addCondition(Condition* condition, bool force = false);
bool addCombatCondition(Condition* condition); bool addCombatCondition(Condition* condition);
@@ -335,12 +322,15 @@ class Creature : virtual public Thing
virtual void changeHealth(int32_t healthChange, bool sendHealthChange = true); 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 void drainHealth(Creature* attacker, int32_t damage);
virtual bool challengeCreature(Creature*) { virtual bool challengeCreature(Creature*) {
return false; return false;
} }
virtual bool convinceCreature(Creature*) {
return false;
}
void onDeath(); void onDeath();
virtual uint64_t getGainedExperience(Creature* attacker) const; virtual uint64_t getGainedExperience(Creature* attacker) const;
@@ -365,9 +355,11 @@ class Creature : virtual public Thing
virtual void onAttackedCreatureChangeZone(ZoneType_t zone); virtual void onAttackedCreatureChangeZone(ZoneType_t zone);
virtual void onIdleStatus(); virtual void onIdleStatus();
virtual LightInfo getCreatureLight() const; virtual void getCreatureLight(LightInfo& light) const;
virtual void setNormalCreatureLight(); virtual void setNormalCreatureLight();
void setCreatureLight(LightInfo lightInfo); void setCreatureLight(LightInfo light) {
internalLight = light;
}
virtual void onThink(uint32_t interval); virtual void onThink(uint32_t interval);
void onAttacking(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 onCreatureSay(Creature*, SpeakClasses, const std::string&) {}
virtual void onCreatureConvinced(const Creature*, const Creature*) {}
virtual void onPlacedCreature() {} virtual void onPlacedCreature() {}
virtual bool getCombatValues(int32_t&, int32_t&) { virtual bool getCombatValues(int32_t&, int32_t&) {
@@ -402,33 +395,30 @@ class Creature : virtual public Thing
void setDropLoot(bool lootDrop) { void setDropLoot(bool lootDrop) {
this->lootDrop = lootDrop; this->lootDrop = lootDrop;
} }
void setSkillLoss(bool skillLoss) { void setLossSkill(bool skillLoss) {
this->skillLoss = skillLoss; this->skillLoss = skillLoss;
} }
void setUseDefense(bool useDefense) {
canUseDefense = useDefense;
}
//creature script events //creature script events
bool registerCreatureEvent(const std::string& name); bool registerCreatureEvent(const std::string& name);
bool unregisterCreatureEvent(const std::string& name); bool unregisterCreatureEvent(const std::string& name);
Cylinder* getParent() const override final { Cylinder* getParent() const final {
return tile; return tile;
} }
void setParent(Cylinder* cylinder) override final { void setParent(Cylinder* cylinder) final {
tile = static_cast<Tile*>(cylinder); tile = static_cast<Tile*>(cylinder);
position = tile->getPosition(); position = tile->getPosition();
} }
const Position& getPosition() const override final { inline const Position& getPosition() const final {
return position; return position;
} }
Tile* getTile() override final { Tile* getTile() final {
return tile; return tile;
} }
const Tile* getTile() const override final { const Tile* getTile() const final {
return tile; return tile;
} }
@@ -474,7 +464,7 @@ class Creature : virtual public Thing
Position position; Position position;
using CountMap = std::map<uint32_t, CountBlock_t>; typedef std::map<uint32_t, CountBlock_t> CountMap;
CountMap damageMap; CountMap damageMap;
std::list<Creature*> summons; std::list<Creature*> summons;
@@ -488,6 +478,10 @@ class Creature : virtual public Thing
Creature* master = nullptr; Creature* master = nullptr;
Creature* followCreature = nullptr; Creature* followCreature = nullptr;
int64_t earliestDefendTime = 0;
int64_t lastDefendTime = 0;
uint64_t lastWalkUpdate = 0;
uint64_t lastStep = 0; uint64_t lastStep = 0;
uint32_t referenceCounter = 0; uint32_t referenceCounter = 0;
uint32_t id = 0; uint32_t id = 0;
@@ -498,7 +492,8 @@ class Creature : virtual public Thing
uint32_t blockCount = 0; uint32_t blockCount = 0;
uint32_t blockTicks = 0; uint32_t blockTicks = 0;
uint32_t lastStepCost = 1; uint32_t lastStepCost = 1;
uint32_t baseSpeed = 220; uint32_t baseSpeed = 70;
uint32_t latestKillEvent = 0;
int32_t varSpeed = 0; int32_t varSpeed = 0;
int32_t health = 1000; int32_t health = 1000;
int32_t healthMax = 1000; int32_t healthMax = 1000;
@@ -524,7 +519,6 @@ class Creature : virtual public Thing
bool hasFollowPath = false; bool hasFollowPath = false;
bool forceUpdateFollowPath = false; bool forceUpdateFollowPath = false;
bool hiddenHealth = false; bool hiddenHealth = false;
bool canUseDefense = true;
//creature script events //creature script events
bool hasEventRegistered(CreatureEventType_t event) const { bool hasEventRegistered(CreatureEventType_t event) const {
@@ -556,6 +550,7 @@ class Creature : virtual public Thing
friend class Game; friend class Game;
friend class Map; friend class Map;
friend class LuaScriptInterface; friend class LuaScriptInterface;
friend class Combat;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -29,15 +29,22 @@ CreatureEvents::CreatureEvents() :
scriptInterface.initState(); scriptInterface.initState();
} }
void CreatureEvents::clear(bool fromLua) CreatureEvents::~CreatureEvents()
{ {
for (auto it = creatureEvents.begin(); it != creatureEvents.end(); ++it) { for (const auto& it : creatureEvents) {
if (fromLua == it->second.fromLua) { delete it.second;
it->second.clearEvent(); }
} }
void CreatureEvents::clear()
{
//clear creature events
for (const auto& it : creatureEvents) {
it.second->clearEvent();
} }
reInitState(fromLua); //clear lua state
scriptInterface.reInitState();
} }
LuaScriptInterface& CreatureEvents::getScriptInterface() LuaScriptInterface& CreatureEvents::getScriptInterface()
@@ -50,17 +57,17 @@ std::string CreatureEvents::getScriptBaseName() const
return "creaturescripts"; return "creaturescripts";
} }
Event_ptr CreatureEvents::getEvent(const std::string& nodeName) Event* CreatureEvents::getEvent(const std::string& nodeName)
{ {
if (strcasecmp(nodeName.c_str(), "event") != 0) { if (strcasecmp(nodeName.c_str(), "event") != 0) {
return nullptr; 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) { if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) {
std::cout << "Error: [CreatureEvents::registerEvent] Trying to register event without type!" << std::endl; std::cout << "Error: [CreatureEvents::registerEvent] Trying to register event without type!" << std::endl;
return false; 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 //if there was an event with the same that is not loaded
//(happens when realoading), it is reused //(happens when realoading), it is reused
if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) { if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) {
oldEvent->copyEvent(creatureEvent.get()); oldEvent->copyEvent(creatureEvent);
} }
return false; return false;
} else { } else {
//if not, register it normally //if not, register it normally
creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent)); creatureEvents[creatureEvent->getName()] = 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));
return true; return true;
} }
} }
@@ -110,8 +93,8 @@ CreatureEvent* CreatureEvents::getEventByName(const std::string& name, bool forc
{ {
auto it = creatureEvents.find(name); auto it = creatureEvents.find(name);
if (it != creatureEvents.end()) { if (it != creatureEvents.end()) {
if (!forceLoaded || it->second.isLoaded()) { if (!forceLoaded || it->second->isLoaded()) {
return &it->second; return it->second;
} }
} }
return nullptr; return nullptr;
@@ -121,8 +104,8 @@ bool CreatureEvents::playerLogin(Player* player) const
{ {
//fire global event if is registered //fire global event if is registered
for (const auto& it : creatureEvents) { for (const auto& it : creatureEvents) {
if (it.second.getEventType() == CREATURE_EVENT_LOGIN) { if (it.second->getEventType() == CREATURE_EVENT_LOGIN) {
if (!it.second.executeOnLogin(player)) { if (!it.second->executeOnLogin(player)) {
return false; return false;
} }
} }
@@ -134,8 +117,8 @@ bool CreatureEvents::playerLogout(Player* player) const
{ {
//fire global event if is registered //fire global event if is registered
for (const auto& it : creatureEvents) { for (const auto& it : creatureEvents) {
if (it.second.getEventType() == CREATURE_EVENT_LOGOUT) { if (it.second->getEventType() == CREATURE_EVENT_LOGOUT) {
if (!it.second.executeOnLogout(player)) { if (!it.second->executeOnLogout(player)) {
return false; return false;
} }
} }
@@ -146,9 +129,9 @@ bool CreatureEvents::playerLogout(Player* player) const
bool CreatureEvents::playerAdvance(Player* player, skills_t skill, uint32_t oldLevel, bool CreatureEvents::playerAdvance(Player* player, skills_t skill, uint32_t oldLevel,
uint32_t newLevel) uint32_t newLevel)
{ {
for (auto& it : creatureEvents) { for (const auto& it : creatureEvents) {
if (it.second.getEventType() == CREATURE_EVENT_ADVANCE) { if (it.second->getEventType() == CREATURE_EVENT_ADVANCE) {
if (!it.second.executeAdvance(player, skill, oldLevel, newLevel)) { if (!it.second->executeAdvance(player, skill, oldLevel, newLevel)) {
return false; return false;
} }
} }
@@ -194,10 +177,6 @@ bool CreatureEvent::configureEvent(const pugi::xml_node& node)
type = CREATURE_EVENT_KILL; type = CREATURE_EVENT_KILL;
} else if (tmpStr == "advance") { } else if (tmpStr == "advance") {
type = CREATURE_EVENT_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") { } else if (tmpStr == "healthchange") {
type = CREATURE_EVENT_HEALTHCHANGE; type = CREATURE_EVENT_HEALTHCHANGE;
} else if (tmpStr == "manachange") { } else if (tmpStr == "manachange") {
@@ -238,12 +217,6 @@ std::string CreatureEvent::getScriptEventName() const
case CREATURE_EVENT_ADVANCE: case CREATURE_EVENT_ADVANCE:
return "onAdvance"; return "onAdvance";
case CREATURE_EVENT_MODALWINDOW:
return "onModalWindow";
case CREATURE_EVENT_TEXTEDIT:
return "onTextEdit";
case CREATURE_EVENT_HEALTHCHANGE: case CREATURE_EVENT_HEALTHCHANGE:
return "onHealthChange"; return "onHealthChange";
@@ -275,7 +248,7 @@ void CreatureEvent::clearEvent()
loaded = false; loaded = false;
} }
bool CreatureEvent::executeOnLogin(Player* player) const bool CreatureEvent::executeOnLogin(Player* player)
{ {
//onLogin(player) //onLogin(player)
if (!scriptInterface->reserveScriptEnv()) { if (!scriptInterface->reserveScriptEnv()) {
@@ -294,7 +267,7 @@ bool CreatureEvent::executeOnLogin(Player* player) const
return scriptInterface->callFunction(1); return scriptInterface->callFunction(1);
} }
bool CreatureEvent::executeOnLogout(Player* player) const bool CreatureEvent::executeOnLogout(Player* player)
{ {
//onLogout(player) //onLogout(player)
if (!scriptInterface->reserveScriptEnv()) { if (!scriptInterface->reserveScriptEnv()) {
@@ -446,56 +419,9 @@ void CreatureEvent::executeOnKill(Creature* creature, Creature* target)
scriptInterface->callVoidFunction(2); 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) 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()) { if (!scriptInterface->reserveScriptEnv()) {
std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl; std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl;
return; return;
@@ -512,7 +438,8 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker,
if (attacker) { if (attacker) {
LuaScriptInterface::pushUserdata(L, attacker); LuaScriptInterface::pushUserdata(L, attacker);
LuaScriptInterface::setCreatureMetatable(L, -1, attacker); LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
} else { }
else {
lua_pushnil(L); lua_pushnil(L);
} }
@@ -520,16 +447,16 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker,
if (scriptInterface->protectedCall(L, 7, 4) != 0) { if (scriptInterface->protectedCall(L, 7, 4) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
damage.primary.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4)); else {
damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3); damage.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
damage.secondary.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2)); damage.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1); damage.min = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
damage.max = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
lua_pop(L, 4); lua_pop(L, 4);
if (damage.primary.type != COMBAT_HEALING) { if (damage.type != COMBAT_HEALING) {
damage.primary.value = -damage.primary.value; damage.value = -damage.value;
damage.secondary.value = -damage.secondary.value;
} }
} }
@@ -537,7 +464,7 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker,
} }
void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage) { 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()) { if (!scriptInterface->reserveScriptEnv()) {
std::cout << "[Error - CreatureEvent::executeManaChange] Call stack overflow" << std::endl; std::cout << "[Error - CreatureEvent::executeManaChange] Call stack overflow" << std::endl;
return; return;
@@ -554,7 +481,8 @@ void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, Co
if (attacker) { if (attacker) {
LuaScriptInterface::pushUserdata(L, attacker); LuaScriptInterface::pushUserdata(L, attacker);
LuaScriptInterface::setCreatureMetatable(L, -1, attacker); LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
} else { }
else {
lua_pushnil(L); lua_pushnil(L);
} }
@@ -562,12 +490,9 @@ void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, Co
if (scriptInterface->protectedCall(L, 7, 4) != 0) { if (scriptInterface->protectedCall(L, 7, 4) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
damage.primary.value = LuaScriptInterface::getNumber<int32_t>(L, -4); else {
damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3); damage = LuaScriptInterface::getCombatDamage(L);
damage.secondary.value = LuaScriptInterface::getNumber<int32_t>(L, -2);
damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
lua_pop(L, 4);
} }
scriptInterface->resetScriptEnv(); scriptInterface->resetScriptEnv();

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -24,9 +24,6 @@
#include "baseevents.h" #include "baseevents.h"
#include "enums.h" #include "enums.h"
class CreatureEvent;
using CreatureEvent_ptr = std::unique_ptr<CreatureEvent>;
enum CreatureEventType_t { enum CreatureEventType_t {
CREATURE_EVENT_NONE, CREATURE_EVENT_NONE,
CREATURE_EVENT_LOGIN, CREATURE_EVENT_LOGIN,
@@ -36,69 +33,18 @@ enum CreatureEventType_t {
CREATURE_EVENT_DEATH, CREATURE_EVENT_DEATH,
CREATURE_EVENT_KILL, CREATURE_EVENT_KILL,
CREATURE_EVENT_ADVANCE, CREATURE_EVENT_ADVANCE,
CREATURE_EVENT_MODALWINDOW,
CREATURE_EVENT_TEXTEDIT,
CREATURE_EVENT_HEALTHCHANGE, CREATURE_EVENT_HEALTHCHANGE,
CREATURE_EVENT_MANACHANGE, CREATURE_EVENT_MANACHANGE,
CREATURE_EVENT_EXTENDED_OPCODE, // otclient additional network opcodes CREATURE_EVENT_EXTENDED_OPCODE, // otclient additional network opcodes
}; };
class CreatureEvent final : public Event class CreatureEvent;
{
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 CreatureEvents final : public BaseEvents class CreatureEvents final : public BaseEvents
{ {
public: public:
CreatureEvents(); CreatureEvents();
~CreatureEvents();
// non-copyable // non-copyable
CreatureEvents(const CreatureEvents&) = delete; CreatureEvents(const CreatureEvents&) = delete;
@@ -111,20 +57,59 @@ class CreatureEvents final : public BaseEvents
CreatureEvent* getEventByName(const std::string& name, bool forceLoaded = true); CreatureEvent* getEventByName(const std::string& name, bool forceLoaded = true);
bool registerLuaEvent(CreatureEvent* event); protected:
void clear(bool fromLua) override final; LuaScriptInterface& getScriptInterface() final;
std::string getScriptBaseName() const final;
private: Event* getEvent(const std::string& nodeName) final;
LuaScriptInterface& getScriptInterface() override; bool registerEvent(Event* event, const pugi::xml_node& node) final;
std::string getScriptBaseName() const override; void clear() final;
Event_ptr getEvent(const std::string& nodeName) override;
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
//creature events //creature events
using CreatureEventMap = std::map<std::string, CreatureEvent>; typedef std::map<std::string, CreatureEvent*> CreatureEventList;
CreatureEventMap creatureEvents; CreatureEventList creatureEvents;
LuaScriptInterface scriptInterface; 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 #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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_IGNOREFIELDDAMAGE = 1 << 5, //Bypass field damage checks
FLAG_IGNORENOTMOVEABLE = 1 << 6, //Bypass check for mobility FLAG_IGNORENOTMOVEABLE = 1 << 6, //Bypass check for mobility
FLAG_IGNOREAUTOSTACK = 1 << 7, //queryDestination will not try to stack items together 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 { enum cylinderlink_t {

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 // adds new row to buffer
const size_t rowLength = row.length(); const size_t rowLength = row.length();
length += rowLength; length += rowLength;
if (length > Database::getInstance().getMaxPacketSize() && !execute()) { if (length > Database::getInstance()->getMaxPacketSize() && !execute()) {
return false; return false;
} }
@@ -288,7 +288,7 @@ bool DBInsert::execute()
} }
// executes buffer // executes buffer
bool res = Database::getInstance().executeQuery(query + values); bool res = Database::getInstance()->executeQuery(query + values);
values.clear(); values.clear();
length = query.length(); length = query.length();
return res; return res;

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
#include <mysql/mysql.h> #include <mysql/mysql.h>
class DBResult; class DBResult;
using DBResult_ptr = std::shared_ptr<DBResult>; typedef std::shared_ptr<DBResult> DBResult_ptr;
class Database class Database
{ {
@@ -42,10 +42,10 @@ class Database
* *
* @return database connection handler singleton * @return database connection handler singleton
*/ */
static Database& getInstance() static Database* getInstance()
{ {
static Database instance; static Database instance;
return instance; return &instance;
} }
/** /**
@@ -117,7 +117,7 @@ class Database
return maxPacketSize; return maxPacketSize;
} }
private: protected:
/** /**
* Transaction related methods. * Transaction related methods.
* *
@@ -129,6 +129,7 @@ class Database
bool rollback(); bool rollback();
bool commit(); bool commit();
private:
MYSQL* handle = nullptr; MYSQL* handle = nullptr;
std::recursive_mutex databaseLock; std::recursive_mutex databaseLock;
uint64_t maxPacketSize = 1048576; uint64_t maxPacketSize = 1048576;
@@ -194,7 +195,7 @@ class DBInsert
bool addRow(std::ostringstream& row); bool addRow(std::ostringstream& row);
bool execute(); bool execute();
private: protected:
std::string query; std::string query;
std::string values; std::string values;
size_t length; size_t length;
@@ -207,7 +208,7 @@ class DBTransaction
~DBTransaction() { ~DBTransaction() {
if (state == STATE_START) { if (state == STATE_START) {
Database::getInstance().rollback(); Database::getInstance()->rollback();
} }
} }
@@ -217,7 +218,7 @@ class DBTransaction
bool begin() { bool begin() {
state = STATE_START; state = STATE_START;
return Database::getInstance().beginTransaction(); return Database::getInstance()->beginTransaction();
} }
bool commit() { bool commit() {
@@ -225,15 +226,15 @@ class DBTransaction
return false; return false;
} }
state = STATE_COMMIT; state = STEATE_COMMIT;
return Database::getInstance().commit(); return Database::getInstance()->commit();
} }
private: private:
enum TransactionStates_t { enum TransactionStates_t {
STATE_NO_START, STATE_NO_START,
STATE_START, STATE_START,
STATE_COMMIT, STEATE_COMMIT,
}; };
TransactionStates_t state = STATE_NO_START; TransactionStates_t state = STATE_NO_START;

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -27,11 +27,11 @@ extern ConfigManager g_config;
bool DatabaseManager::optimizeTables() bool DatabaseManager::optimizeTables()
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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"; 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()); DBResult_ptr result = db->storeQuery(query.str());
if (!result) { if (!result) {
return false; return false;
} }
@@ -43,7 +43,7 @@ bool DatabaseManager::optimizeTables()
query.str(std::string()); query.str(std::string());
query << "OPTIMIZE TABLE `" << tableName << '`'; query << "OPTIMIZE TABLE `" << tableName << '`';
if (db.executeQuery(query.str())) { if (db->executeQuery(query.str())) {
std::cout << " [success]" << std::endl; std::cout << " [success]" << std::endl;
} else { } else {
std::cout << " [failed]" << std::endl; std::cout << " [failed]" << std::endl;
@@ -54,27 +54,27 @@ bool DatabaseManager::optimizeTables()
bool DatabaseManager::tableExists(const std::string& tableName) bool DatabaseManager::tableExists(const std::string& tableName)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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"; 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; return db->storeQuery(query.str()).get() != nullptr;
} }
bool DatabaseManager::isDatabaseSetup() bool DatabaseManager::isDatabaseSetup()
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)); 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; return db->storeQuery(query.str()).get() != nullptr;
} }
int32_t DatabaseManager::getDatabaseVersion() int32_t DatabaseManager::getDatabaseVersion()
{ {
if (!tableExists("server_config")) { if (!tableExists("server_config")) {
Database& db = Database::getInstance(); 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("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)"); db->executeQuery("INSERT INTO `server_config` VALUES ('db_version', 0)");
return 0; return 0;
} }
@@ -85,68 +85,13 @@ int32_t DatabaseManager::getDatabaseVersion()
return -1; 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) bool DatabaseManager::getDatabaseConfig(const std::string& config, int32_t& value)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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) { if (!result) {
return false; 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) void DatabaseManager::registerDatabaseConfig(const std::string& config, int32_t value)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
int32_t tmp; int32_t tmp;
if (!getDatabaseConfig(config, 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 { } 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -24,13 +24,12 @@
class DatabaseManager class DatabaseManager
{ {
public: public:
static bool tableExists(const std::string& tableName); static bool tableExists(const std::string& table);
static int32_t getDatabaseVersion(); static int32_t getDatabaseVersion();
static bool isDatabaseSetup(); static bool isDatabaseSetup();
static bool optimizeTables(); static bool optimizeTables();
static void updateDatabase();
static bool getDatabaseConfig(const std::string& config, int32_t& value); static bool getDatabaseConfig(const std::string& config, int32_t& value);
static void registerDatabaseConfig(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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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; bool signal = false;
taskLock.lock(); taskLock.lock();
if (getState() == THREAD_STATE_RUNNING) { if (getState() == THREAD_STATE_RUNNING) {
signal = tasks.empty(); signal = tasks.empty();
tasks.emplace_back(std::move(query), std::move(callback), store); tasks.emplace_back(query, callback, store);
} }
taskLock.unlock(); taskLock.unlock();
@@ -85,13 +85,9 @@ void DatabaseTasks::runTask(const DatabaseTask& task)
void DatabaseTasks::flush() void DatabaseTasks::flush()
{ {
std::unique_lock<std::mutex> guard{ taskLock };
while (!tasks.empty()) { while (!tasks.empty()) {
auto task = std::move(tasks.front()); runTask(tasks.front());
tasks.pop_front(); tasks.pop_front();
guard.unlock();
runTask(task);
guard.lock();
} }
} }
@@ -99,7 +95,7 @@ void DatabaseTasks::shutdown()
{ {
taskLock.lock(); taskLock.lock();
setState(THREAD_STATE_TERMINATED); setState(THREAD_STATE_TERMINATED);
taskLock.unlock();
flush(); flush();
taskLock.unlock();
taskSignal.notify_one(); taskSignal.notify_one();
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
#include "enums.h" #include "enums.h"
struct DatabaseTask { 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) {} query(std::move(query)), callback(std::move(callback)), store(store) {}
std::string query; std::string query;
@@ -42,7 +42,7 @@ class DatabaseTasks : public ThreadHolder<DatabaseTasks>
void flush(); void flush();
void shutdown(); 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(); void threadMain();
private: private:

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -20,13 +20,13 @@
#ifndef FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963 #ifndef FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
#define FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963 #define FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
static constexpr auto STATUS_SERVER_NAME = "The Forgotten Server"; static constexpr auto STATUS_SERVER_NAME = "Sabrehaven";
static constexpr auto STATUS_SERVER_VERSION = "1.3"; static constexpr auto STATUS_SERVER_VERSION = "1.0";
static constexpr auto STATUS_SERVER_DEVELOPERS = "Mark Samman"; static constexpr auto STATUS_SERVER_DEVELOPERS = "OTLand community & Sabrehaven Developers Team";
static constexpr auto CLIENT_VERSION_MIN = 1097; static constexpr auto CLIENT_VERSION_MIN = 781;
static constexpr auto CLIENT_VERSION_MAX = 1098; static constexpr auto CLIENT_VERSION_MAX = 781;
static constexpr auto CLIENT_VERSION_STR = "10.98"; static constexpr auto CLIENT_VERSION_STR = "7.81";
static constexpr auto AUTHENTICATOR_DIGITS = 6U; static constexpr auto AUTHENTICATOR_DIGITS = 6U;
static constexpr auto AUTHENTICATOR_PERIOD = 30U; 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: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:4351) // new behavior: elements of array will be default initialized
#pragma warning(disable:4458) // declaration hides class member #pragma warning(disable:4458) // declaration hides class member
#pragma warning(disable:4996) // inetpton warning
#endif #endif
#define strcasecmp _stricmp #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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -20,9 +20,12 @@
#include "otpch.h" #include "otpch.h"
#include "depotlocker.h" #include "depotlocker.h"
#include "creature.h"
#include "player.h"
#include "tools.h"
DepotLocker::DepotLocker(uint16_t type) : DepotLocker::DepotLocker(uint16_t type) :
Container(type, 3), depotId(0) {} Container(type, 30), depotId(0) {}
Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream) 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); 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) 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); 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -21,24 +21,21 @@
#define FS_DEPOTLOCKER_H_53AD8E0606A34070B87F792611F4F3F8 #define FS_DEPOTLOCKER_H_53AD8E0606A34070B87F792611F4F3F8
#include "container.h" #include "container.h"
#include "inbox.h"
class DepotLocker final : public Container class DepotLocker final : public Container
{ {
public: public:
explicit DepotLocker(uint16_t type); explicit DepotLocker(uint16_t type);
DepotLocker* getDepotLocker() override { DepotLocker* getDepotLocker() final {
return this; return this;
} }
const DepotLocker* getDepotLocker() const override { const DepotLocker* getDepotLocker() const final {
return this; return this;
} }
void removeInbox(Inbox* inbox);
//serialization //serialization
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final;
uint16_t getDepotId() const { uint16_t getDepotId() const {
return depotId; return depotId;
@@ -49,12 +46,12 @@ class DepotLocker final : public Container
//cylinder implementations //cylinder implementations
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, 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 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) override; 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; return false;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -20,43 +20,6 @@
#ifndef FS_ENUMS_H_003445999FEE4A67BCECBE918B0124CE #ifndef FS_ENUMS_H_003445999FEE4A67BCECBE918B0124CE
#define 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 { enum ThreadState {
THREAD_STATE_RUNNING, THREAD_STATE_RUNNING,
THREAD_STATE_CLOSING, THREAD_STATE_CLOSING,
@@ -67,7 +30,7 @@ enum itemAttrTypes : uint32_t {
ITEM_ATTRIBUTE_NONE, ITEM_ATTRIBUTE_NONE,
ITEM_ATTRIBUTE_ACTIONID = 1 << 0, ITEM_ATTRIBUTE_ACTIONID = 1 << 0,
ITEM_ATTRIBUTE_UNIQUEID = 1 << 1, ITEM_ATTRIBUTE_MOVEMENTID = 1 << 1,
ITEM_ATTRIBUTE_DESCRIPTION = 1 << 2, ITEM_ATTRIBUTE_DESCRIPTION = 1 << 2,
ITEM_ATTRIBUTE_TEXT = 1 << 3, ITEM_ATTRIBUTE_TEXT = 1 << 3,
ITEM_ATTRIBUTE_DATE = 1 << 4, ITEM_ATTRIBUTE_DATE = 1 << 4,
@@ -78,60 +41,26 @@ enum itemAttrTypes : uint32_t {
ITEM_ATTRIBUTE_WEIGHT = 1 << 9, ITEM_ATTRIBUTE_WEIGHT = 1 << 9,
ITEM_ATTRIBUTE_ATTACK = 1 << 10, ITEM_ATTRIBUTE_ATTACK = 1 << 10,
ITEM_ATTRIBUTE_DEFENSE = 1 << 11, ITEM_ATTRIBUTE_DEFENSE = 1 << 11,
ITEM_ATTRIBUTE_EXTRADEFENSE = 1 << 12, ITEM_ATTRIBUTE_ARMOR = 1 << 12,
ITEM_ATTRIBUTE_ARMOR = 1 << 13, ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 13,
ITEM_ATTRIBUTE_HITCHANCE = 1 << 14, ITEM_ATTRIBUTE_OWNER = 1 << 14,
ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 15, ITEM_ATTRIBUTE_DURATION = 1 << 15,
ITEM_ATTRIBUTE_OWNER = 1 << 16, ITEM_ATTRIBUTE_DECAYSTATE = 1 << 16,
ITEM_ATTRIBUTE_DURATION = 1 << 17, ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 17,
ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18, ITEM_ATTRIBUTE_CHARGES = 1 << 18,
ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 19, ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 19,
ITEM_ATTRIBUTE_CHARGES = 1 << 20, ITEM_ATTRIBUTE_DOORID = 1 << 20,
ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 21, ITEM_ATTRIBUTE_KEYNUMBER = 1 << 21,
ITEM_ATTRIBUTE_DOORID = 1 << 22, ITEM_ATTRIBUTE_KEYHOLENUMBER = 1 << 22,
ITEM_ATTRIBUTE_DECAYTO = 1 << 23, ITEM_ATTRIBUTE_DOORQUESTNUMBER = 1 << 23,
ITEM_ATTRIBUTE_DOORQUESTVALUE = 1 << 24,
ITEM_ATTRIBUTE_CUSTOM = 1U << 31 ITEM_ATTRIBUTE_DOORLEVEL = 1 << 25,
ITEM_ATTRIBUTE_CHESTQUESTNUMBER = 1 << 26,
}; };
enum VipStatus_t : uint8_t { enum VipStatus_t : uint8_t {
VIPSTATUS_OFFLINE = 0, VIPSTATUS_OFFLINE = 0,
VIPSTATUS_ONLINE = 1, 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 { enum OperatingSystem_t : uint8_t {
@@ -146,20 +75,6 @@ enum OperatingSystem_t : uint8_t {
CLIENTOS_OTCLIENT_MAC = 12, 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 { enum AccountType_t : uint8_t {
ACCOUNT_TYPE_NORMAL = 1, ACCOUNT_TYPE_NORMAL = 1,
ACCOUNT_TYPE_TUTOR = 2, ACCOUNT_TYPE_TUTOR = 2,
@@ -174,7 +89,6 @@ enum RaceType_t : uint8_t {
RACE_BLOOD, RACE_BLOOD,
RACE_UNDEAD, RACE_UNDEAD,
RACE_FIRE, RACE_FIRE,
RACE_ENERGY,
}; };
enum CombatType_t : uint16_t { enum CombatType_t : uint16_t {
@@ -189,11 +103,8 @@ enum CombatType_t : uint16_t {
COMBAT_MANADRAIN = 1 << 6, COMBAT_MANADRAIN = 1 << 6,
COMBAT_HEALING = 1 << 7, COMBAT_HEALING = 1 << 7,
COMBAT_DROWNDAMAGE = 1 << 8, 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 { enum CombatParam_t {
@@ -207,6 +118,14 @@ enum CombatParam_t {
COMBAT_PARAM_AGGRESSIVE, COMBAT_PARAM_AGGRESSIVE,
COMBAT_PARAM_DISPEL, COMBAT_PARAM_DISPEL,
COMBAT_PARAM_USECHARGES, 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 { enum CallBackParam_t {
@@ -234,7 +153,6 @@ enum ConditionParam_t {
CONDITION_PARAM_MAXVALUE = 15, CONDITION_PARAM_MAXVALUE = 15,
CONDITION_PARAM_STARTVALUE = 16, CONDITION_PARAM_STARTVALUE = 16,
CONDITION_PARAM_TICKINTERVAL = 17, CONDITION_PARAM_TICKINTERVAL = 17,
CONDITION_PARAM_FORCEUPDATE = 18,
CONDITION_PARAM_SKILL_MELEE = 19, CONDITION_PARAM_SKILL_MELEE = 19,
CONDITION_PARAM_SKILL_FIST = 20, CONDITION_PARAM_SKILL_FIST = 20,
CONDITION_PARAM_SKILL_CLUB = 21, CONDITION_PARAM_SKILL_CLUB = 21,
@@ -260,16 +178,13 @@ enum ConditionParam_t {
CONDITION_PARAM_SKILL_DISTANCEPERCENT = 41, CONDITION_PARAM_SKILL_DISTANCEPERCENT = 41,
CONDITION_PARAM_SKILL_SHIELDPERCENT = 42, CONDITION_PARAM_SKILL_SHIELDPERCENT = 42,
CONDITION_PARAM_SKILL_FISHINGPERCENT = 43, CONDITION_PARAM_SKILL_FISHINGPERCENT = 43,
CONDITION_PARAM_BUFF_SPELL = 44, // CONDITION_PARAM_BUFF_SPELL = 44,
CONDITION_PARAM_SUBID = 45, CONDITION_PARAM_SUBID = 45,
CONDITION_PARAM_FIELD = 46, CONDITION_PARAM_FIELD = 46,
CONDITION_PARAM_DISABLE_DEFENSE = 47, CONDITION_PARAM_CYCLE = 47,
CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE = 48, CONDITION_PARAM_HIT_DAMAGE = 48,
CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT = 49, CONDITION_PARAM_COUNT = 49,
CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE = 50, CONDITION_PARAM_MAX_COUNT = 50,
CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT = 51,
CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE = 52,
CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT = 53,
}; };
enum BlockType_t : uint8_t { enum BlockType_t : uint8_t {
@@ -305,18 +220,6 @@ enum stats_t {
STAT_LAST = STAT_MAGICPOINTS 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 { enum formulaType_t {
COMBAT_FORMULA_UNDEFINED, COMBAT_FORMULA_UNDEFINED,
COMBAT_FORMULA_LEVELMAGIC, COMBAT_FORMULA_LEVELMAGIC,
@@ -330,31 +233,24 @@ enum ConditionType_t {
CONDITION_POISON = 1 << 0, CONDITION_POISON = 1 << 0,
CONDITION_FIRE = 1 << 1, CONDITION_FIRE = 1 << 1,
CONDITION_ENERGY = 1 << 2, CONDITION_ENERGY = 1 << 2,
CONDITION_BLEEDING = 1 << 3, CONDITION_HASTE = 1 << 3,
CONDITION_HASTE = 1 << 4, CONDITION_PARALYZE = 1 << 4,
CONDITION_PARALYZE = 1 << 5, CONDITION_OUTFIT = 1 << 5,
CONDITION_OUTFIT = 1 << 6, CONDITION_INVISIBLE = 1 << 6,
CONDITION_INVISIBLE = 1 << 7, CONDITION_LIGHT = 1 << 7,
CONDITION_LIGHT = 1 << 8, CONDITION_MANASHIELD = 1 << 8,
CONDITION_MANASHIELD = 1 << 9, CONDITION_INFIGHT = 1 << 9,
CONDITION_INFIGHT = 1 << 10, CONDITION_DRUNK = 1 << 10,
CONDITION_DRUNK = 1 << 11, CONDITION_REGENERATION = 1 << 11,
CONDITION_EXHAUST_WEAPON = 1 << 12, // unused CONDITION_SOUL = 1 << 12,
CONDITION_REGENERATION = 1 << 13, CONDITION_MUTED = 1 << 13,
CONDITION_SOUL = 1 << 14, CONDITION_CHANNELMUTEDTICKS = 1 << 14,
CONDITION_DROWN = 1 << 15, CONDITION_YELLTICKS = 1 << 15,
CONDITION_MUTED = 1 << 16, CONDITION_ATTRIBUTES = 1 << 16,
CONDITION_CHANNELMUTEDTICKS = 1 << 17, CONDITION_EXHAUST = 1 << 17,
CONDITION_YELLTICKS = 1 << 18, CONDITION_PACIFIED = 1 << 18,
CONDITION_ATTRIBUTES = 1 << 19, CONDITION_AGGRESSIVE = 1 << 19,
CONDITION_FREEZING = 1 << 20, CONDITION_DROWN = 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,
}; };
enum ConditionId_t : int8_t { enum ConditionId_t : int8_t {
@@ -380,7 +276,11 @@ enum PlayerSex_t : uint8_t {
}; };
enum Vocation_t : uint16_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 { enum ReturnValue {
@@ -443,12 +343,10 @@ enum ReturnValue {
RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL, RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL,
RETURNVALUE_CANNOTCONJUREITEMHERE, RETURNVALUE_CANNOTCONJUREITEMHERE,
RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS, RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS,
RETURNVALUE_NAMEISTOOAMBIGUOUS, RETURNVALUE_NAMEISTOOAMBIGIOUS,
RETURNVALUE_CANONLYUSEONESHIELD, RETURNVALUE_CANONLYUSEONESHIELD,
RETURNVALUE_NOPARTYMEMBERSINRANGE, RETURNVALUE_NOPARTYMEMBERSINRANGE,
RETURNVALUE_YOUARENOTTHEOWNER, RETURNVALUE_YOUARENOTTHEOWNER,
RETURNVALUE_NOSUCHRAIDEXISTS,
RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING,
RETURNVALUE_TRADEPLAYERFARAWAY, RETURNVALUE_TRADEPLAYERFARAWAY,
RETURNVALUE_YOUDONTOWNTHISHOUSE, RETURNVALUE_YOUDONTOWNTHISHOUSE,
RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE, RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE,
@@ -456,43 +354,9 @@ enum ReturnValue {
RETURNVALUE_YOUCANNOTTRADETHISHOUSE, 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 { struct Outfit_t {
uint16_t lookType = 0; uint16_t lookType = 0;
uint16_t lookTypeEx = 0; uint16_t lookTypeEx = 0;
uint16_t lookMount = 0;
uint8_t lookHead = 0; uint8_t lookHead = 0;
uint8_t lookBody = 0; uint8_t lookBody = 0;
uint8_t lookLegs = 0; uint8_t lookLegs = 0;
@@ -507,85 +371,6 @@ struct LightInfo {
constexpr LightInfo(uint8_t level, uint8_t color) : level(level), color(color) {} 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 enum CombatOrigin
{ {
ORIGIN_NONE, ORIGIN_NONE,
@@ -597,31 +382,20 @@ enum CombatOrigin
struct CombatDamage struct CombatDamage
{ {
struct { CombatType_t type;
CombatType_t type; int32_t value;
int32_t value; int32_t min;
} primary, secondary; int32_t max;
CombatOrigin origin; CombatOrigin origin;
CombatDamage() CombatDamage()
{ {
origin = ORIGIN_NONE; origin = ORIGIN_NONE;
primary.type = secondary.type = COMBAT_NONE; type = COMBAT_NONE;
primary.value = secondary.value = 0; 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 #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * 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 * 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 * it under the terms of the GNU General Public License as published by
@@ -29,9 +29,39 @@
Events::Events() : Events::Events() :
scriptInterface("Event Interface") scriptInterface("Event Interface")
{ {
clear();
scriptInterface.initState(); 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() bool Events::load()
{ {
pugi::xml_document doc; pugi::xml_document doc;
@@ -41,8 +71,6 @@ bool Events::load()
return false; return false;
} }
info = {};
std::set<std::string> classes; std::set<std::string> classes;
for (auto eventNode : doc.child("events").children()) { for (auto eventNode : doc.child("events").children()) {
if (!eventNode.attribute("enabled").as_bool()) { if (!eventNode.attribute("enabled").as_bool()) {
@@ -63,69 +91,80 @@ bool Events::load()
const int32_t event = scriptInterface.getMetaEvent(className, methodName); const int32_t event = scriptInterface.getMetaEvent(className, methodName);
if (className == "Creature") { if (className == "Creature") {
if (methodName == "onChangeOutfit") { if (methodName == "onChangeOutfit") {
info.creatureOnChangeOutfit = event; creatureOnChangeOutfit = event;
} else if (methodName == "onAreaCombat") { }
info.creatureOnAreaCombat = event; else if (methodName == "onAreaCombat") {
} else if (methodName == "onTargetCombat") { creatureOnAreaCombat = event;
info.creatureOnTargetCombat = event; }
} else { else if (methodName == "onTargetCombat") {
creatureOnTargetCombat = event;
}
else {
std::cout << "[Warning - Events::load] Unknown creature method: " << methodName << std::endl; std::cout << "[Warning - Events::load] Unknown creature method: " << methodName << std::endl;
} }
} else if (className == "Party") { }
else if (className == "Party") {
if (methodName == "onJoin") { if (methodName == "onJoin") {
info.partyOnJoin = event; partyOnJoin = event;
} else if (methodName == "onLeave") { }
info.partyOnLeave = event; else if (methodName == "onLeave") {
} else if (methodName == "onDisband") { partyOnLeave = event;
info.partyOnDisband = event; }
} else if (methodName == "onShareExperience") { else if (methodName == "onDisband") {
info.partyOnShareExperience = event; partyOnDisband = event;
} else { }
else if (methodName == "onShareExperience") {
partyOnShareExperience = event;
}
else {
std::cout << "[Warning - Events::load] Unknown party method: " << methodName << std::endl; std::cout << "[Warning - Events::load] Unknown party method: " << methodName << std::endl;
} }
} else if (className == "Player") { }
if (methodName == "onBrowseField") { else if (className == "Player") {
info.playerOnBrowseField = event; if (methodName == "onLook") {
} else if (methodName == "onLook") { playerOnLook = event;
info.playerOnLook = event; }
} else if (methodName == "onLookInBattleList") { else if (methodName == "onLookInBattleList") {
info.playerOnLookInBattleList = event; playerOnLookInBattleList = event;
} else if (methodName == "onLookInTrade") { }
info.playerOnLookInTrade = event; else if (methodName == "onLookInTrade") {
} else if (methodName == "onLookInShop") { playerOnLookInTrade = event;
info.playerOnLookInShop = event; }
} else if (methodName == "onTradeRequest") { else if (methodName == "onTradeRequest") {
info.playerOnTradeRequest = event; playerOnTradeRequest = event;
} else if (methodName == "onTradeAccept") { }
info.playerOnTradeAccept = event; else if (methodName == "onTradeAccept") {
} else if (methodName == "onMoveItem") { playerOnTradeAccept = event;
info.playerOnMoveItem = event; }
} else if (methodName == "onItemMoved") { else if (methodName == "onMoveItem") {
info.playerOnItemMoved = event; playerOnMoveItem = event;
} else if (methodName == "onMoveCreature") { }
info.playerOnMoveCreature = event; else if (methodName == "onItemMoved") {
} else if (methodName == "onReportRuleViolation") { playerOnItemMoved = event;
info.playerOnReportRuleViolation = event; }
} else if (methodName == "onReportBug") { else if (methodName == "onMoveCreature") {
info.playerOnReportBug = event; playerOnMoveCreature = event;
} else if (methodName == "onTurn") { }
info.playerOnTurn = event; else if (methodName == "onReportBug") {
} else if (methodName == "onGainExperience") { playerOnReportBug = event;
info.playerOnGainExperience = event; }
} else if (methodName == "onLoseExperience") { else if (methodName == "onTurn") {
info.playerOnLoseExperience = event; playerOnTurn = event;
} else if (methodName == "onGainSkillTries") { }
info.playerOnGainSkillTries = event; else if (methodName == "onGainExperience") {
} else { 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; std::cout << "[Warning - Events::load] Unknown player method: " << methodName << std::endl;
} }
} else if (className == "Monster") { }
if (methodName == "onDropLoot") { else {
info.monsterOnDropLoot = event;
} else {
std::cout << "[Warning - Events::load] Unknown monster method: " << methodName << std::endl;
}
} else {
std::cout << "[Warning - Events::load] Unknown class: " << className << std::endl; 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) bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit)
{ {
// Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit) // Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit)
if (info.creatureOnChangeOutfit == -1) { if (creatureOnChangeOutfit == -1) {
return true; return true;
} }
@@ -146,10 +185,10 @@ bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& out
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.creatureOnChangeOutfit, &scriptInterface); env->setScriptId(creatureOnChangeOutfit, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.creatureOnChangeOutfit); scriptInterface.pushFunction(creatureOnChangeOutfit);
LuaScriptInterface::pushUserdata<Creature>(L, creature); LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, 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) ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive)
{ {
// Creature:onAreaCombat(tile, aggressive) or Creature.onAreaCombat(self, tile, aggressive) // Creature:onAreaCombat(tile, aggressive) or Creature.onAreaCombat(self, tile, aggressive)
if (info.creatureOnAreaCombat == -1) { if (creatureOnAreaCombat == -1) {
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
@@ -172,15 +211,16 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.creatureOnAreaCombat, &scriptInterface); env->setScriptId(creatureOnAreaCombat, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.creatureOnAreaCombat); scriptInterface.pushFunction(creatureOnAreaCombat);
if (creature) { if (creature) {
LuaScriptInterface::pushUserdata<Creature>(L, creature); LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature);
} else { }
else {
lua_pushnil(L); lua_pushnil(L);
} }
@@ -193,7 +233,8 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo
if (scriptInterface.protectedCall(L, 3, 1) != 0) { if (scriptInterface.protectedCall(L, 3, 1) != 0) {
returnValue = RETURNVALUE_NOTPOSSIBLE; returnValue = RETURNVALUE_NOTPOSSIBLE;
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
else {
returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1); returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1);
lua_pop(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) ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* target)
{ {
// Creature:onTargetCombat(target) or Creature.onTargetCombat(self, target) // Creature:onTargetCombat(target) or Creature.onTargetCombat(self, target)
if (info.creatureOnTargetCombat == -1) { if (creatureOnTargetCombat == -1) {
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
@@ -215,15 +256,16 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.creatureOnTargetCombat, &scriptInterface); env->setScriptId(creatureOnTargetCombat, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.creatureOnTargetCombat); scriptInterface.pushFunction(creatureOnTargetCombat);
if (creature) { if (creature) {
LuaScriptInterface::pushUserdata<Creature>(L, creature); LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature);
} else { }
else {
lua_pushnil(L); lua_pushnil(L);
} }
@@ -234,7 +276,8 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta
if (scriptInterface.protectedCall(L, 2, 1) != 0) { if (scriptInterface.protectedCall(L, 2, 1) != 0) {
returnValue = RETURNVALUE_NOTPOSSIBLE; returnValue = RETURNVALUE_NOTPOSSIBLE;
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
else {
returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1); returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
} }
@@ -247,7 +290,7 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta
bool Events::eventPartyOnJoin(Party* party, Player* player) bool Events::eventPartyOnJoin(Party* party, Player* player)
{ {
// Party:onJoin(player) or Party.onJoin(self, player) // Party:onJoin(player) or Party.onJoin(self, player)
if (info.partyOnJoin == -1) { if (partyOnJoin == -1) {
return true; return true;
} }
@@ -257,10 +300,10 @@ bool Events::eventPartyOnJoin(Party* party, Player* player)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.partyOnJoin, &scriptInterface); env->setScriptId(partyOnJoin, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.partyOnJoin); scriptInterface.pushFunction(partyOnJoin);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::setMetatable(L, -1, "Party");
@@ -274,7 +317,7 @@ bool Events::eventPartyOnJoin(Party* party, Player* player)
bool Events::eventPartyOnLeave(Party* party, Player* player) bool Events::eventPartyOnLeave(Party* party, Player* player)
{ {
// Party:onLeave(player) or Party.onLeave(self, player) // Party:onLeave(player) or Party.onLeave(self, player)
if (info.partyOnLeave == -1) { if (partyOnLeave == -1) {
return true; return true;
} }
@@ -284,10 +327,10 @@ bool Events::eventPartyOnLeave(Party* party, Player* player)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.partyOnLeave, &scriptInterface); env->setScriptId(partyOnLeave, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.partyOnLeave); scriptInterface.pushFunction(partyOnLeave);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::setMetatable(L, -1, "Party");
@@ -301,7 +344,7 @@ bool Events::eventPartyOnLeave(Party* party, Player* player)
bool Events::eventPartyOnDisband(Party* party) bool Events::eventPartyOnDisband(Party* party)
{ {
// Party:onDisband() or Party.onDisband(self) // Party:onDisband() or Party.onDisband(self)
if (info.partyOnDisband == -1) { if (partyOnDisband == -1) {
return true; return true;
} }
@@ -311,10 +354,10 @@ bool Events::eventPartyOnDisband(Party* party)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.partyOnDisband, &scriptInterface); env->setScriptId(partyOnDisband, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.partyOnDisband); scriptInterface.pushFunction(partyOnDisband);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::setMetatable(L, -1, "Party");
@@ -325,7 +368,7 @@ bool Events::eventPartyOnDisband(Party* party)
void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp) void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
{ {
// Party:onShareExperience(exp) or Party.onShareExperience(self, exp) // Party:onShareExperience(exp) or Party.onShareExperience(self, exp)
if (info.partyOnShareExperience == -1) { if (partyOnShareExperience == -1) {
return; return;
} }
@@ -335,10 +378,10 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.partyOnShareExperience, &scriptInterface); env->setScriptId(partyOnShareExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.partyOnShareExperience); scriptInterface.pushFunction(partyOnShareExperience);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "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) { if (scriptInterface.protectedCall(L, 2, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
else {
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1); exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
} }
@@ -355,37 +399,10 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
scriptInterface.resetScriptEnv(); 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) 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) // Player:onLook(thing, position, distance) or Player.onLook(self, thing, position, distance)
if (info.playerOnLook == -1) { if (playerOnLook == -1) {
return; return;
} }
@@ -395,10 +412,10 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing*
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnLook, &scriptInterface); env->setScriptId(playerOnLook, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnLook); scriptInterface.pushFunction(playerOnLook);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -406,10 +423,12 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing*
if (Creature* creature = thing->getCreature()) { if (Creature* creature = thing->getCreature()) {
LuaScriptInterface::pushUserdata<Creature>(L, creature); LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature);
} else if (Item* item = thing->getItem()) { }
else if (Item* item = thing->getItem()) {
LuaScriptInterface::pushUserdata<Item>(L, item); LuaScriptInterface::pushUserdata<Item>(L, item);
LuaScriptInterface::setItemMetatable(L, -1, item); LuaScriptInterface::setItemMetatable(L, -1, item);
} else { }
else {
lua_pushnil(L); 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) void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance)
{ {
// Player:onLookInBattleList(creature, position, distance) or Player.onLookInBattleList(self, creature, position, distance) // Player:onLookInBattleList(creature, position, distance) or Player.onLookInBattleList(self, creature, position, distance)
if (info.playerOnLookInBattleList == -1) { if (playerOnLookInBattleList == -1) {
return; return;
} }
@@ -432,10 +451,10 @@ void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, i
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnLookInBattleList, &scriptInterface); env->setScriptId(playerOnLookInBattleList, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnLookInBattleList); scriptInterface.pushFunction(playerOnLookInBattleList);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "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) void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance)
{ {
// Player:onLookInTrade(partner, item, distance) or Player.onLookInTrade(self, partner, item, distance) // Player:onLookInTrade(partner, item, distance) or Player.onLookInTrade(self, partner, item, distance)
if (info.playerOnLookInTrade == -1) { if (playerOnLookInTrade == -1) {
return; return;
} }
@@ -461,10 +480,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnLookInTrade, &scriptInterface); env->setScriptId(playerOnLookInTrade, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnLookInTrade); scriptInterface.pushFunction(playerOnLookInTrade);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -480,39 +499,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite
scriptInterface.callVoidFunction(4); 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) 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) // 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; return true;
} }
@@ -522,10 +512,10 @@ bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, c
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnMoveItem, &scriptInterface); env->setScriptId(playerOnMoveItem, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnMoveItem); scriptInterface.pushFunction(playerOnMoveItem);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "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) 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) // Player:onItemMoved(item, count, fromPosition, toPosition) or Player.onItemMoved(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder)
if (info.playerOnItemMoved == -1) { if (playerOnItemMoved == -1) {
return; return;
} }
@@ -556,10 +546,10 @@ void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count,
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnItemMoved, &scriptInterface); env->setScriptId(playerOnItemMoved, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnItemMoved); scriptInterface.pushFunction(playerOnItemMoved);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "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) 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) // Player:onMoveCreature(creature, fromPosition, toPosition) or Player.onMoveCreature(self, creature, fromPosition, toPosition)
if (info.playerOnMoveCreature == -1) { if (playerOnMoveCreature == -1) {
return true; return true;
} }
@@ -590,10 +580,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnMoveCreature, &scriptInterface); env->setScriptId(playerOnMoveCreature, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnMoveCreature); scriptInterface.pushFunction(playerOnMoveCreature);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -607,42 +597,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const
return scriptInterface.callFunction(4); 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) bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position)
{
// 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)
{ {
// Player:onReportBug(message, position, category) // Player:onReportBug(message, position, category)
if (info.playerOnReportBug == -1) { if (playerOnReportBug == -1) {
return true; return true;
} }
@@ -652,25 +610,24 @@ bool Events::eventPlayerOnReportBug(Player* player, const std::string& message,
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnReportBug, &scriptInterface); env->setScriptId(playerOnReportBug, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnReportBug); scriptInterface.pushFunction(playerOnReportBug);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushString(L, message); LuaScriptInterface::pushString(L, message);
LuaScriptInterface::pushPosition(L, position); LuaScriptInterface::pushPosition(L, position);
lua_pushnumber(L, category);
return scriptInterface.callFunction(4); return scriptInterface.callFunction(3);
} }
bool Events::eventPlayerOnTurn(Player* player, Direction direction) bool Events::eventPlayerOnTurn(Player* player, Direction direction)
{ {
// Player:onTurn(direction) or Player.onTurn(self, direction) // Player:onTurn(direction) or Player.onTurn(self, direction)
if (info.playerOnTurn == -1) { if (playerOnTurn == -1) {
return true; return true;
} }
@@ -680,10 +637,10 @@ bool Events::eventPlayerOnTurn(Player* player, Direction direction)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnTurn, &scriptInterface); env->setScriptId(playerOnTurn, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnTurn); scriptInterface.pushFunction(playerOnTurn);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "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) bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* item)
{ {
// Player:onTradeRequest(target, item) // Player:onTradeRequest(target, item)
if (info.playerOnTradeRequest == -1) { if (playerOnTradeRequest == -1) {
return true; return true;
} }
@@ -706,10 +663,10 @@ bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* ite
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnTradeRequest, &scriptInterface); env->setScriptId(playerOnTradeRequest, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnTradeRequest); scriptInterface.pushFunction(playerOnTradeRequest);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "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) bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem)
{ {
// Player:onTradeAccept(target, item, targetItem) // Player:onTradeAccept(target, item, targetItem)
if (info.playerOnTradeAccept == -1) { if (playerOnTradeAccept == -1) {
return true; return true;
} }
@@ -736,10 +693,10 @@ bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnTradeAccept, &scriptInterface); env->setScriptId(playerOnTradeAccept, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnTradeAccept); scriptInterface.pushFunction(playerOnTradeAccept);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -760,7 +717,7 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
{ {
// Player:onGainExperience(source, exp, rawExp) // Player:onGainExperience(source, exp, rawExp)
// rawExp gives the original exp which is not multiplied // rawExp gives the original exp which is not multiplied
if (info.playerOnGainExperience == -1) { if (playerOnGainExperience == -1) {
return; return;
} }
@@ -770,10 +727,10 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnGainExperience, &scriptInterface); env->setScriptId(playerOnGainExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnGainExperience); scriptInterface.pushFunction(playerOnGainExperience);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -781,7 +738,8 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
if (source) { if (source) {
LuaScriptInterface::pushUserdata<Creature>(L, source); LuaScriptInterface::pushUserdata<Creature>(L, source);
LuaScriptInterface::setCreatureMetatable(L, -1, source); LuaScriptInterface::setCreatureMetatable(L, -1, source);
} else { }
else {
lua_pushnil(L); lua_pushnil(L);
} }
@@ -790,7 +748,8 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
if (scriptInterface.protectedCall(L, 4, 1) != 0) { if (scriptInterface.protectedCall(L, 4, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
else {
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1); exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(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) void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
{ {
// Player:onLoseExperience(exp) // Player:onLoseExperience(exp)
if (info.playerOnLoseExperience == -1) { if (playerOnLoseExperience == -1) {
return; return;
} }
@@ -811,10 +770,10 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnLoseExperience, &scriptInterface); env->setScriptId(playerOnLoseExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnLoseExperience); scriptInterface.pushFunction(playerOnLoseExperience);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "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) { if (scriptInterface.protectedCall(L, 2, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
else {
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1); exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(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) void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries)
{ {
// Player:onGainSkillTries(skill, tries) // Player:onGainSkillTries(skill, tries)
if (info.playerOnGainSkillTries == -1) { if (playerOnGainSkillTries == -1) {
return; return;
} }
@@ -844,10 +804,10 @@ void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnGainSkillTries, &scriptInterface); env->setScriptId(playerOnGainSkillTries, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnGainSkillTries); scriptInterface.pushFunction(playerOnGainSkillTries);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "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) { if (scriptInterface.protectedCall(L, 3, 1) != 0) {
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L)); LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
} else { }
else {
tries = LuaScriptInterface::getNumber<uint64_t>(L, -1); tries = LuaScriptInterface::getNumber<uint64_t>(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
} }
scriptInterface.resetScriptEnv(); 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 * 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 * 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 * it under the terms of the GNU General Public License as published by
@@ -28,80 +28,68 @@ class Tile;
class Events class Events
{ {
struct EventsInfo { public:
// Creature Events();
int32_t creatureOnChangeOutfit = -1;
int32_t creatureOnAreaCombat = -1;
int32_t creatureOnTargetCombat = -1;
// Party void clear();
int32_t partyOnJoin = -1; bool load();
int32_t partyOnLeave = -1;
int32_t partyOnDisband = -1;
int32_t partyOnShareExperience = -1;
// Player // Creature
int32_t playerOnBrowseField = -1; bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit);
int32_t playerOnLook = -1; ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive);
int32_t playerOnLookInBattleList = -1; ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target);
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;
// Monster // Party
int32_t monsterOnDropLoot = -1; bool eventPartyOnJoin(Party* party, Player* player);
}; bool eventPartyOnLeave(Party* party, Player* player);
bool eventPartyOnDisband(Party* party);
void eventPartyOnShareExperience(Party* party, uint64_t& exp);
public: // Player
Events(); 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 // Creature
bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit); int32_t creatureOnChangeOutfit;
ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive); int32_t creatureOnAreaCombat;
ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target); int32_t creatureOnTargetCombat;
// Party // Party
bool eventPartyOnJoin(Party* party, Player* player); int32_t partyOnJoin;
bool eventPartyOnLeave(Party* party, Player* player); int32_t partyOnLeave;
bool eventPartyOnDisband(Party* party); int32_t partyOnDisband;
void eventPartyOnShareExperience(Party* party, uint64_t& exp); int32_t partyOnShareExperience;
// Player // Player
bool eventPlayerOnBrowseField(Player* player, const Position& position); int32_t playerOnLook;
void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance); int32_t playerOnLookInBattleList;
void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance); int32_t playerOnLookInTrade;
void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance); int32_t playerOnMoveItem;
bool eventPlayerOnLookInShop(Player* player, const ItemType* itemType, uint8_t count); int32_t playerOnItemMoved;
bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); int32_t playerOnMoveCreature;
void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder); int32_t playerOnReportRuleViolation;
bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition); int32_t playerOnReportBug;
void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation); int32_t playerOnTurn;
bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position, uint8_t category); int32_t playerOnTradeRequest;
bool eventPlayerOnTurn(Player* player, Direction direction); int32_t playerOnTradeAccept;
bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item); int32_t playerOnGainExperience;
bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem); int32_t playerOnLoseExperience;
void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp); int32_t playerOnGainSkillTries;
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;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -19,106 +19,387 @@
#include "otpch.h" #include "otpch.h"
#include <stack>
#include "fileloader.h" #include "fileloader.h"
FileLoader::~FileLoader()
namespace OTB {
constexpr Identifier wildcard = {{'\0', '\0', '\0', '\0'}};
Loader::Loader(const std::string& fileName, const Identifier& acceptedIdentifier):
fileContents(fileName)
{ {
constexpr auto minimalSize = sizeof(Identifier) + sizeof(Node::START) + sizeof(Node::type) + sizeof(Node::END); if (file) {
if (fileContents.size() <= minimalSize) { fclose(file);
throw InvalidOTBFormat{}; file = nullptr;
} }
Identifier fileIdentifier; NodeStruct::clearNet(root);
std::copy(fileContents.begin(), fileContents.begin() + fileIdentifier.size(), fileIdentifier.begin()); delete[] buffer;
if (fileIdentifier != acceptedIdentifier && fileIdentifier != wildcard) {
throw InvalidOTBFormat{}; for (auto& i : cached_data) {
delete[] i.data;
} }
} }
using NodeStack = std::stack<Node*, std::vector<Node*>>; bool FileLoader::openFile(const char* filename, const char* accept_identifier)
static Node& getCurrentNode(const NodeStack& nodeStack) { {
if (nodeStack.empty()) { file = fopen(filename, "rb");
throw InvalidOTBFormat{}; 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); int32_t byte, pos;
if (static_cast<uint8_t>(*it) != Node::START) { NODE currentNode = node;
throw InvalidOTBFormat{};
}
root.type = *(++it);
root.propsBegin = ++it;
NodeStack parseStack;
parseStack.push(&root);
for (; it != fileContents.end(); ++it) { while (readByte(byte)) {
switch(static_cast<uint8_t>(*it)) { currentNode->type = byte;
case Node::START: { bool setPropsSize = false;
auto& currentNode = getCurrentNode(parseStack);
if (currentNode.children.empty()) { while (true) {
currentNode.propsEnd = it; if (!readByte(byte)) {
} return false;
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;
} }
case Node::END: {
auto& currentNode = getCurrentNode(parseStack); bool skipNode = false;
if (currentNode.children.empty()) {
currentNode.propsEnd = it; 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_END: {
} //current node end
case Node::ESCAPE: { if (!setPropsSize) {
if (++it == fileContents.end()) { if (!safeTell(pos)) {
throw InvalidOTBFormat{}; 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; break;
} }
} }
} }
if (!parseStack.empty()) { return false;
throw InvalidOTBFormat{}; }
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; 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 (!prev) {
if (size == 0) { 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; return false;
} }
propBuffer.resize(size);
bool lastEscaped = false;
auto escapedPropEnd = std::copy_if(node.propsBegin, node.propsEnd, propBuffer.begin(), [&lastEscaped](const char& byte) { if (cache_offset >= cached_data[cache_index].size) {
lastEscaped = byte == static_cast<char>(Node::ESCAPE) && !lastEscaped; int32_t pos = cache_offset + cached_data[cache_index].base;
return !lastEscaped; int32_t tmp = getCacheBlock(pos);
}); if (tmp < 0) {
props.init(&propBuffer[0], std::distance(propBuffer.begin(), escapedPropEnd)); 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; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -22,53 +22,133 @@
#include <limits> #include <limits>
#include <vector> #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; class PropStream;
namespace OTB { class FileLoader
using MappedFile = boost::iostreams::mapped_file_source;
using ContentIt = MappedFile::iterator;
using Identifier = std::array<char, 4>;
struct Node
{ {
using ChildrenVector = std::vector<Node>; public:
FileLoader() = default;
~FileLoader();
ChildrenVector children; // non-copyable
ContentIt propsBegin; FileLoader(const FileLoader&) = delete;
ContentIt propsEnd; FileLoader& operator=(const FileLoader&) = delete;
uint8_t type;
enum NodeChar: uint8_t bool openFile(const char* filename, const char* identifier);
{ const uint8_t* getProps(const NODE, size_t& size);
ESCAPE = 0xFD, bool getProps(const NODE, PropStream& props);
START = 0xFE, NODE getChildNode(const NODE parent, uint32_t& type);
END = 0xFF, 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 class PropStream
{ {
public: public:
@@ -120,7 +200,7 @@ class PropStream
return true; return true;
} }
private: protected:
const char* p = nullptr; const char* p = nullptr;
const char* end = nullptr; const char* end = nullptr;
}; };
@@ -160,7 +240,7 @@ class PropWriteStream
std::copy(str.begin(), str.end(), std::back_inserter(buffer)); std::copy(str.begin(), str.end(), std::back_inserter(buffer));
} }
private: protected:
std::vector<char> buffer; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,7 +31,6 @@
#include "raids.h" #include "raids.h"
#include "npc.h" #include "npc.h"
#include "wildcardtree.h" #include "wildcardtree.h"
#include "quests.h"
class ServiceManager; class ServiceManager;
class Creature; class Creature;
@@ -70,6 +69,22 @@ enum LightState_t {
LIGHT_STATE_SUNRISE, 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_LIGHTINTERVAL = 10000;
static constexpr int32_t EVENT_DECAYINTERVAL = 250; static constexpr int32_t EVENT_DECAYINTERVAL = 250;
static constexpr int32_t EVENT_DECAY_BUCKETS = 4; static constexpr int32_t EVENT_DECAY_BUCKETS = 4;
@@ -82,7 +97,7 @@ static constexpr int32_t EVENT_DECAY_BUCKETS = 4;
class Game class Game
{ {
public: public:
Game(); Game() = default;
~Game(); ~Game();
// non-copyable // non-copyable
@@ -204,7 +219,7 @@ class Game
* \param extendedPos If true, the creature will in first-hand be placed 2 tiles away * \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) * \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. * Remove Creature from the map.
@@ -229,7 +244,7 @@ class Game
return playersRecord; return playersRecord;
} }
LightInfo getWorldLightInfo() const; void getWorldLightInfo(LightInfo& lightInfo) const;
ReturnValue internalMoveCreature(Creature* creature, Direction direction, uint32_t flags = 0); ReturnValue internalMoveCreature(Creature* creature, Direction direction, uint32_t flags = 0);
ReturnValue internalMoveCreature(Creature& creature, Tile& toTile, 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 * \param text The text to say
*/ */
bool internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text, 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 loadPlayersRecord();
void checkPlayersRecord(); void checkPlayersRecord();
void sendGuildMotd(uint32_t playerId);
void kickPlayer(uint32_t playerId, bool displayEffect); 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 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); void internalCloseTrade(Player* player);
bool playerBroadcastMessage(Player* player, const std::string& text) const; bool playerBroadcastMessage(Player* player, const std::string& text) const;
void broadcastMessage(const std::string& text, MessageClasses type) 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, void playerMoveThing(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos,
const Position& toPos, uint8_t count); const Position& toPos, uint8_t count);
void playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, const Position& movingCreatureOrigPos, const Position& toPos); 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 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, void playerMoveItem(Player* player, const Position& fromPos,
uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count, Item* item, Cylinder* toCylinder); 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 playerMove(uint32_t playerId, Direction direction);
void playerCreatePrivateChannel(uint32_t playerId); void playerCreatePrivateChannel(uint32_t playerId);
void playerChannelInvite(uint32_t playerId, const std::string& name); void playerChannelInvite(uint32_t playerId, const std::string& name);
@@ -341,7 +352,6 @@ class Game
void playerOpenChannel(uint32_t playerId, uint16_t channelId); void playerOpenChannel(uint32_t playerId, uint16_t channelId);
void playerCloseChannel(uint32_t playerId, uint16_t channelId); void playerCloseChannel(uint32_t playerId, uint16_t channelId);
void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver); void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver);
void playerCloseNpcChannel(uint32_t playerId);
void playerReceivePing(uint32_t playerId); void playerReceivePing(uint32_t playerId);
void playerReceivePingBack(uint32_t playerId); void playerReceivePingBack(uint32_t playerId);
void playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir); 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 playerUpdateContainer(uint32_t playerId, uint8_t cid);
void playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stackPos, const uint16_t spriteId); 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 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 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 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, void playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t stackPos,
uint32_t tradePlayerId, uint16_t spriteId); uint32_t tradePlayerId, uint16_t spriteId);
void playerAcceptTrade(uint32_t playerId); void playerAcceptTrade(uint32_t playerId);
void playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index); 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 playerCloseTrade(uint32_t playerId);
void playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId); void playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId);
void playerFollowCreature(uint32_t playerId, uint32_t creatureId); void playerFollowCreature(uint32_t playerId, uint32_t creatureId);
void playerCancelAttackAndFollow(uint32_t playerId); 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 playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos);
void playerLookInBattleList(uint32_t playerId, uint32_t creatureId); void playerLookInBattleList(uint32_t playerId, uint32_t creatureId);
void playerRequestAddVip(uint32_t playerId, const std::string& name); void playerRequestAddVip(uint32_t playerId, const std::string& name);
void playerRequestRemoveVip(uint32_t playerId, uint32_t guid); 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 playerTurn(uint32_t playerId, Direction dir);
void playerRequestOutfit(uint32_t playerId); 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, void playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type,
const std::string& receiver, const std::string& text); const std::string& receiver, const std::string& text);
void playerChangeOutfit(uint32_t playerId, Outfit_t outfit); void playerChangeOutfit(uint32_t playerId, Outfit_t outfit);
@@ -391,18 +391,15 @@ class Game
void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId); void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId);
void playerLeaveParty(uint32_t playerId); void playerLeaveParty(uint32_t playerId);
void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive); void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive);
void playerToggleMount(uint32_t playerId, bool mount); void playerProcessRuleViolationReport(uint32_t playerId, const std::string& name);
void playerLeaveMarket(uint32_t playerId); void playerCloseRuleViolationReport(uint32_t playerId, const std::string& name);
void playerBrowseMarket(uint32_t playerId, uint16_t spriteId); void playerCancelRuleViolationReport(uint32_t playerId);
void playerBrowseMarketOwnOffers(uint32_t playerId); void playerReportRuleViolationReport(Player* player, const std::string& text);
void playerBrowseMarketOwnHistory(uint32_t playerId); void playerContinueRuleViolationReport(Player* player, const std::string& text);
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 parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer); 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); static void updatePremium(Account& account);
@@ -413,17 +410,14 @@ class Game
bool canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight = true, bool canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight = true,
int32_t rangex = Map::maxClientViewportX, int32_t rangey = Map::maxClientViewportY) const; 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 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 internalCreatureChangeVisible(Creature* creature, bool visible);
void changeLight(const Creature* creature); void changeLight(const Creature* creature);
void updateCreatureSkull(const Creature* creature); void updateCreatureSkull(const Creature* player);
void updatePlayerShield(Player* player); void updatePlayerShield(Player* player);
void updatePlayerHelpers(const Player& player);
void updateCreatureType(Creature* creature);
void updateCreatureWalkthrough(const Creature* creature);
GameState_t getGameState() const; GameState_t getGameState() const;
void setGameState(GameState_t newState); void setGameState(GameState_t newState);
@@ -445,11 +439,14 @@ class Game
//animation help functions //animation help functions
void addCreatureHealth(const Creature* target); 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); 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); 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); void startDecay(Item* item);
int32_t getLightHour() const { int32_t getLightHour() const {
@@ -465,8 +462,7 @@ class Game
uint32_t getMotdNum() const { return motdNum; } uint32_t getMotdNum() const { return motdNum; }
void incrementMotdNum() { 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::unordered_map<uint32_t, Player*>& getPlayers() const { return players; }
const std::map<uint32_t, Npc*>& getNpcs() const { return npcs; } const std::map<uint32_t, Npc*>& getNpcs() const { return npcs; }
@@ -476,55 +472,45 @@ class Game
void addNpc(Npc* npc); void addNpc(Npc* npc);
void removeNpc(Npc* npc); void removeNpc(Npc* npc);
void addMonster(Monster* monster); void addMonster(Monster* npc);
void removeMonster(Monster* monster); void removeMonster(Monster* npc);
Guild* getGuild(uint32_t id) const; Guild* getGuild(uint32_t id) const;
void addGuild(Guild* guild); void addGuild(Guild* guild);
void removeGuild(uint32_t guildId); 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); void internalRemoveItems(std::vector<Item*> itemList, uint32_t amount, bool stackable);
BedItem* getBedBySleeper(uint32_t guid) const; BedItem* getBedBySleeper(uint32_t guid) const;
void setBedSleeper(BedItem* bed, uint32_t guid); void setBedSleeper(BedItem* bed, uint32_t guid);
void removeBedSleeper(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); bool reload(ReloadTypes_t reloadType);
Groups groups; Groups groups;
Map map; Map map;
Mounts mounts;
Raids raids; Raids raids;
Quests quests;
std::forward_list<Item*> toDecayItems; protected:
private:
bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text); bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text);
void playerWhisper(Player* player, const std::string& text); void playerWhisper(Player* player, const std::string& text);
bool playerYell(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); 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 checkDecay();
void internalDecayItem(Item* item); 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<uint32_t, Player*> players;
std::unordered_map<std::string, Player*> mappedPlayerNames; std::unordered_map<std::string, Player*> mappedPlayerNames;
std::unordered_map<uint32_t, Guild*> guilds; std::unordered_map<uint32_t, Guild*> guilds;
std::unordered_map<uint16_t, Item*> uniqueItems;
std::map<uint32_t, uint32_t> stages; std::map<uint32_t, uint32_t> stages;
std::list<Item*> decayItems[EVENT_DECAY_BUCKETS]; std::list<Item*> decayItems[EVENT_DECAY_BUCKETS];
std::list<Creature*> checkCreatureLists[EVENT_CREATURECOUNT]; std::list<Creature*> checkCreatureLists[EVENT_CREATURECOUNT];
std::forward_list<Item*> toDecayItems;
std::vector<Creature*> ToReleaseCreatures; std::vector<Creature*> ToReleaseCreatures;
std::vector<Item*> ToReleaseItems; std::vector<Item*> ToReleaseItems;
@@ -540,8 +526,6 @@ class Game
std::map<uint32_t, BedItem*> bedSleepersMap; 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_DAY = 250;
static constexpr int32_t LIGHT_LEVEL_NIGHT = 40; static constexpr int32_t LIGHT_LEVEL_NIGHT = 40;
static constexpr int32_t SUNSET = 1305; static constexpr int32_t SUNSET = 1305;

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -35,47 +35,44 @@ GlobalEvents::GlobalEvents() :
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(); ) { for (const auto& it : map) {
if (fromLua == it->second.fromLua) { delete it.second;
it = map.erase(it);
} else {
++it;
}
} }
map.clear();
} }
void GlobalEvents::clear(bool fromLua) void GlobalEvents::clear()
{ {
g_scheduler.stopEvent(thinkEventId); g_scheduler.stopEvent(thinkEventId);
thinkEventId = 0; thinkEventId = 0;
g_scheduler.stopEvent(timerEventId); g_scheduler.stopEvent(timerEventId);
timerEventId = 0; timerEventId = 0;
clearMap(thinkMap, fromLua); clearMap(thinkMap);
clearMap(serverMap, fromLua); clearMap(serverMap);
clearMap(timerMap, fromLua); 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) { if (strcasecmp(nodeName.c_str(), "globalevent") != 0) {
return nullptr; 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) { 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 (result.second) {
if (timerEventId == 0) { if (timerEventId == 0) {
timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this))); 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; return true;
} }
} else if (globalEvent->getEventType() != GLOBALEVENT_NONE) { } 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) { if (result.second) {
return true; return true;
} }
} else { // think event } 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)));
}
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));
if (result.second) { if (result.second) {
if (thinkEventId == 0) { if (thinkEventId == 0) {
thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this))); thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this)));
@@ -144,9 +111,9 @@ void GlobalEvents::timer()
auto it = timerMap.begin(); auto it = timerMap.begin();
while (it != timerMap.end()) { 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 > 0) {
if (nextExecutionTime < nextScheduledTime) { if (nextExecutionTime < nextScheduledTime) {
nextScheduledTime = nextExecutionTime; nextScheduledTime = nextExecutionTime;
@@ -156,7 +123,7 @@ void GlobalEvents::timer()
continue; continue;
} }
if (!globalEvent.executeEvent()) { if (!globalEvent->executeEvent()) {
it = timerMap.erase(it); it = timerMap.erase(it);
continue; continue;
} }
@@ -166,7 +133,7 @@ void GlobalEvents::timer()
nextScheduledTime = nextExecutionTime; nextScheduledTime = nextExecutionTime;
} }
globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime);
++it; ++it;
} }
@@ -182,10 +149,10 @@ void GlobalEvents::think()
int64_t now = OTSYS_TIME(); int64_t now = OTSYS_TIME();
int64_t nextScheduledTime = std::numeric_limits<int64_t>::max(); int64_t nextScheduledTime = std::numeric_limits<int64_t>::max();
for (auto& it : thinkMap) { for (const auto& it : thinkMap) {
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 > 0) {
if (nextExecutionTime < nextScheduledTime) { if (nextExecutionTime < nextScheduledTime) {
nextScheduledTime = nextExecutionTime; nextScheduledTime = nextExecutionTime;
@@ -193,16 +160,16 @@ void GlobalEvents::think()
continue; continue;
} }
if (!globalEvent.executeEvent()) { if (!globalEvent->executeEvent()) {
std::cout << "[Error - GlobalEvents::think] Failed to execute event: " << globalEvent.getName() << std::endl; std::cout << "[Error - GlobalEvents::think] Failed to execute event: " << globalEvent->getName() << std::endl;
} }
nextExecutionTime = globalEvent.getInterval(); nextExecutionTime = globalEvent->getInterval();
if (nextExecutionTime < nextScheduledTime) { if (nextExecutionTime < nextScheduledTime) {
nextScheduledTime = nextExecutionTime; nextScheduledTime = nextExecutionTime;
} }
globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime); globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime);
} }
if (nextScheduledTime != std::numeric_limits<int64_t>::max()) { if (nextScheduledTime != std::numeric_limits<int64_t>::max()) {
@@ -213,16 +180,15 @@ void GlobalEvents::think()
void GlobalEvents::execute(GlobalEvent_t type) const void GlobalEvents::execute(GlobalEvent_t type) const
{ {
for (const auto& it : serverMap) { for (const auto& it : serverMap) {
const GlobalEvent& globalEvent = it.second; GlobalEvent* globalEvent = it.second;
if (globalEvent.getEventType() == type) { if (globalEvent->getEventType() == type) {
globalEvent.executeEvent(); globalEvent->executeEvent();
} }
} }
} }
GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type) GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type)
{ {
// TODO: This should be better implemented. Maybe have a map for every type.
switch (type) { switch (type) {
case GLOBALEVENT_NONE: return thinkMap; case GLOBALEVENT_NONE: return thinkMap;
case GLOBALEVENT_TIMER: return timerMap; case GLOBALEVENT_TIMER: return timerMap;
@@ -231,8 +197,8 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type)
case GLOBALEVENT_RECORD: { case GLOBALEVENT_RECORD: {
GlobalEventMap retMap; GlobalEventMap retMap;
for (const auto& it : serverMap) { for (const auto& it : serverMap) {
if (it.second.getEventType() == type) { if (it.second->getEventType() == type) {
retMap.emplace(it.first, it.second); retMap[it.first] = it.second;
} }
} }
return retMap; return retMap;
@@ -349,7 +315,7 @@ bool GlobalEvent::executeRecord(uint32_t current, uint32_t old)
return scriptInterface->callFunction(2); return scriptInterface->callFunction(2);
} }
bool GlobalEvent::executeEvent() const bool GlobalEvent::executeEvent()
{ {
if (!scriptInterface->reserveScriptEnv()) { if (!scriptInterface->reserveScriptEnv()) {
std::cout << "[Error - GlobalEvent::executeEvent] Call stack overflow" << std::endl; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -33,8 +33,7 @@ enum GlobalEvent_t {
}; };
class GlobalEvent; class GlobalEvent;
using GlobalEvent_ptr = std::unique_ptr<GlobalEvent>; typedef std::map<std::string, GlobalEvent*> GlobalEventMap;
using GlobalEventMap = std::map<std::string, GlobalEvent>;
class GlobalEvents final : public BaseEvents class GlobalEvents final : public BaseEvents
{ {
@@ -53,20 +52,18 @@ class GlobalEvents final : public BaseEvents
void execute(GlobalEvent_t type) const; void execute(GlobalEvent_t type) const;
GlobalEventMap getEventMap(GlobalEvent_t type); GlobalEventMap getEventMap(GlobalEvent_t type);
static void clearMap(GlobalEventMap& map, bool fromLua); static void clearMap(GlobalEventMap& map);
bool registerLuaEvent(GlobalEvent* event); protected:
void clear(bool fromLua) override final; std::string getScriptBaseName() const final {
private:
std::string getScriptBaseName() const override {
return "globalevents"; return "globalevents";
} }
void clear() final;
Event_ptr getEvent(const std::string& nodeName) override; Event* getEvent(const std::string& nodeName) final;
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override; bool registerEvent(Event* event, const pugi::xml_node& node) final;
LuaScriptInterface& getScriptInterface() override { LuaScriptInterface& getScriptInterface() final {
return scriptInterface; return scriptInterface;
} }
LuaScriptInterface scriptInterface; LuaScriptInterface scriptInterface;
@@ -80,31 +77,22 @@ class GlobalEvent final : public Event
public: public:
explicit GlobalEvent(LuaScriptInterface* interface); 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 executeRecord(uint32_t current, uint32_t old);
bool executeEvent() const; bool executeEvent();
GlobalEvent_t getEventType() const { GlobalEvent_t getEventType() const {
return eventType; return eventType;
} }
void setEventType(GlobalEvent_t type) {
eventType = type;
}
const std::string& getName() const { const std::string& getName() const {
return name; return name;
} }
void setName(std::string eventName) {
name = eventName;
}
uint32_t getInterval() const { uint32_t getInterval() const {
return interval; return interval;
} }
void setInterval(uint32_t eventInterval) {
interval |= eventInterval;
}
int64_t getNextExecution() const { int64_t getNextExecution() const {
return nextExecution; return nextExecution;
@@ -113,10 +101,10 @@ class GlobalEvent final : public Event
nextExecution = time; nextExecution = time;
} }
private: protected:
GlobalEvent_t eventType = GLOBALEVENT_NONE; GlobalEvent_t eventType = GLOBALEVENT_NONE;
std::string getScriptEventName() const override; std::string getScriptEventName() const final;
std::string name; std::string name;
int64_t nextExecution = 0; int64_t nextExecution = 0;

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -24,47 +24,6 @@
#include "pugicast.h" #include "pugicast.h"
#include "tools.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() bool Groups::load()
{ {
pugi::xml_document doc; pugi::xml_document doc;
@@ -78,24 +37,10 @@ bool Groups::load()
Group group; Group group;
group.id = pugi::cast<uint32_t>(groupNode.attribute("id").value()); group.id = pugi::cast<uint32_t>(groupNode.attribute("id").value());
group.name = groupNode.attribute("name").as_string(); 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.access = groupNode.attribute("access").as_bool();
group.maxDepotItems = pugi::cast<uint32_t>(groupNode.attribute("maxdepotitems").value()); group.maxDepotItems = pugi::cast<uint32_t>(groupNode.attribute("maxdepotitems").value());
group.maxVipEntries = pugi::cast<uint32_t>(groupNode.attribute("maxvipentries").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); groups.push_back(group);
} }
return true; return true;

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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) void Guild::addMember(Player* player)
{ {
membersOnline.push_back(player); membersOnline.push_back(player);
for (Player* member : membersOnline) {
g_game.updatePlayerHelpers(*member);
}
} }
void Guild::removeMember(Player* player) void Guild::removeMember(Player* player)
{ {
membersOnline.remove(player); membersOnline.remove(player);
for (Player* member : membersOnline) {
g_game.updatePlayerHelpers(*member);
}
g_game.updatePlayerHelpers(*player);
if (membersOnline.empty()) { if (membersOnline.empty()) {
g_game.removeGuild(id); g_game.removeGuild(id);
@@ -57,16 +50,6 @@ GuildRank* Guild::getRankById(uint32_t rankId)
return nullptr; 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 const GuildRank* Guild::getRankByLevel(uint8_t level) const
{ {
for (const auto& rank : ranks) { for (const auto& rank : ranks) {

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -55,26 +55,14 @@ class Guild
memberCount = count; memberCount = count;
} }
const std::vector<GuildRank>& getRanks() const { GuildRank* getRankById(uint32_t id);
return ranks;
}
GuildRank* getRankById(uint32_t rankId);
const GuildRank* getRankByName(const std::string& name) const;
const GuildRank* getRankByLevel(uint8_t level) const; const GuildRank* getRankByLevel(uint8_t level) const;
void addRank(uint32_t rankId, const std::string& rankName, uint8_t level); void addRank(uint32_t id, const std::string& name, uint8_t level);
const std::string& getMotd() const {
return motd;
}
void setMotd(const std::string& motd) {
this->motd = motd;
}
private: private:
std::list<Player*> membersOnline; std::list<Player*> membersOnline;
std::vector<GuildRank> ranks; std::vector<GuildRank> ranks;
std::string name; std::string name;
std::string motd;
uint32_t id; uint32_t id;
uint32_t memberCount = 0; uint32_t memberCount = 0;
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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*/) void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* player/* = nullptr*/)
{ {
if (updateDatabase && owner != guid) { if (updateDatabase && owner != guid) {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "UPDATE `houses` SET `owner` = " << guid << ", `bid` = 0, `bid_end` = 0, `last_bid` = 0, `highest_bidder` = 0 WHERE `id` = " << id; 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) { if (isLoaded && owner == guid) {
@@ -85,26 +85,12 @@ void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* play
for (Door* door : doorSet) { for (Door* door : doorSet) {
door->setAccessList(""); 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) { if (guid != 0) {
std::string name = IOLoginData::getNameByGuid(guid); std::string name = IOLoginData::getNameByGuid(guid);
if (!name.empty()) { if (!name.empty()) {
@@ -124,9 +110,9 @@ void House::updateDoorDescription() const
} else { } else {
ss << "It belongs to house '" << houseName << "'. Nobody owns this house."; 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) { 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); transferToDepot(&tmpPlayer);
IOLoginData::savePlayer(&tmpPlayer); IOLoginData::savePlayer(&tmpPlayer);
} }
return true; return true;
} }
@@ -258,8 +245,9 @@ bool House::transferToDepot(Player* player) const
} }
for (Item* item : moveItemList) { 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; return true;
} }
@@ -408,7 +396,7 @@ bool House::executeTransfer(HouseTransferItem* item, Player* newOwner)
void AccessList::parseList(const std::string& list) void AccessList::parseList(const std::string& list)
{ {
playerList.clear(); playerList.clear();
guildRankList.clear(); guildList.clear();
expressionList.clear(); expressionList.clear();
regExList.clear(); regExList.clear();
this->list = list; this->list = list;
@@ -433,11 +421,7 @@ void AccessList::parseList(const std::string& list)
std::string::size_type at_pos = line.find("@"); std::string::size_type at_pos = line.find("@");
if (at_pos != std::string::npos) { if (at_pos != std::string::npos) {
if (at_pos == 0) { addGuild(line.substr(at_pos + 1));
addGuild(line.substr(1));
} else {
addGuildRank(line.substr(0, at_pos - 1), line.substr(at_pos + 1));
}
} else if (line.find("!") != std::string::npos || line.find("*") != std::string::npos || line.find("?") != std::string::npos) { } else if (line.find("!") != std::string::npos || line.find("*") != std::string::npos || line.find("?") != std::string::npos) {
addExpression(line); addExpression(line);
} else { } 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) void AccessList::addGuild(const std::string& name)
{ {
const Guild* guild = getGuildByName(name); uint32_t guildId = IOGuild::getGuildIdByName(name);
if (guild) { if (guildId != 0) {
for (const auto& rank : guild->getRanks()) { guildList.insert(guildId);
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);
}
} }
} }
@@ -550,8 +502,8 @@ bool AccessList::isInList(const Player* player)
return true; return true;
} }
const GuildRank* rank = player->getGuildRank(); const Guild* guild = player->getGuild();
return rank && guildRankList.find(rank->id) != guildRankList.end(); return guild && guildList.find(guild->getId()) != guildList.end();
} }
void AccessList::getList(std::string& list) const void AccessList::getList(std::string& list) const
@@ -715,9 +667,7 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const
continue; continue;
} }
if (player.getBankBalance() >= rent) { if (g_game.removeMoney(player.getDepotLocker(house->getTownId(), true), house->getRent(), FLAG_NOLIMIT)) {
player.setBankBalance(player.getBankBalance() - rent);
time_t paidUntil = currentTime; time_t paidUntil = currentTime;
switch (rentPeriod) { switch (rentPeriod) {
case RENTPERIOD_DAILY: case RENTPERIOD_DAILY:
@@ -768,7 +718,7 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const
std::ostringstream ss; 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."; 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()); 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); house->setPayRentWarnings(house->getPayRentWarnings() + 1);
} else { } else {
house->setOwner(0, true, &player); house->setOwner(0, true, &player);

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -22,7 +22,6 @@
#include <regex> #include <regex>
#include <set> #include <set>
#include <unordered_set>
#include "container.h" #include "container.h"
#include "housetile.h" #include "housetile.h"
@@ -38,7 +37,6 @@ class AccessList
void parseList(const std::string& list); void parseList(const std::string& list);
void addPlayer(const std::string& name); void addPlayer(const std::string& name);
void addGuild(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); void addExpression(const std::string& expression);
bool isInList(const Player* player); bool isInList(const Player* player);
@@ -48,7 +46,7 @@ class AccessList
private: private:
std::string list; std::string list;
std::unordered_set<uint32_t> playerList; 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::string> expressionList;
std::list<std::pair<std::regex, bool>> regExList; std::list<std::pair<std::regex, bool>> regExList;
}; };
@@ -62,10 +60,10 @@ class Door final : public Item
Door(const Door&) = delete; Door(const Door&) = delete;
Door& operator=(const Door&) = delete; Door& operator=(const Door&) = delete;
Door* getDoor() override { Door* getDoor() final {
return this; return this;
} }
const Door* getDoor() const override { const Door* getDoor() const final {
return this; return this;
} }
@@ -74,8 +72,8 @@ class Door final : public Item
} }
//serialization //serialization
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final;
void serializeAttr(PropWriteStream&) const override {} void serializeAttr(PropWriteStream&) const final {}
void setDoorId(uint32_t doorId) { void setDoorId(uint32_t doorId) {
setIntAttr(ITEM_ATTRIBUTE_DOORID, doorId); setIntAttr(ITEM_ATTRIBUTE_DOORID, doorId);
@@ -89,11 +87,12 @@ class Door final : public Item
void setAccessList(const std::string& textlist); void setAccessList(const std::string& textlist);
bool getAccessList(std::string& list) const; bool getAccessList(std::string& list) const;
void onRemoved() override; void onRemoved() final;
private: protected:
void setHouse(House* house); void setHouse(House* house);
private:
House* house = nullptr; House* house = nullptr;
std::unique_ptr<AccessList> accessList; std::unique_ptr<AccessList> accessList;
friend class House; friend class House;
@@ -111,8 +110,8 @@ enum AccessHouseLevel_t {
HOUSE_OWNER = 3, HOUSE_OWNER = 3,
}; };
using HouseTileList = std::list<HouseTile*>; typedef std::list<HouseTile*> HouseTileList;
using HouseBedItemList = std::list<BedItem*>; typedef std::list<BedItem*> HouseBedItemList;
class HouseTransferItem final : public Item class HouseTransferItem final : public Item
{ {
@@ -121,12 +120,12 @@ class HouseTransferItem final : public Item
explicit HouseTransferItem(House* house) : Item(0), house(house) {} explicit HouseTransferItem(House* house) : Item(0), house(house) {}
void onTradeEvent(TradeEvents_t event, Player* owner) override; void onTradeEvent(TradeEvents_t event, Player* owner) final;
bool canTransform() const override { bool canTransform() const final {
return false; return false;
} }
private: protected:
House* house; House* house;
}; };
@@ -208,7 +207,7 @@ class House
HouseTransferItem* getTransferItem(); HouseTransferItem* getTransferItem();
void resetTransferItem(); void resetTransferItem();
bool executeTransfer(HouseTransferItem* item, Player* newOwner); bool executeTransfer(HouseTransferItem* item, Player* player);
const HouseTileList& getTiles() const { const HouseTileList& getTiles() const {
return houseTiles; return houseTiles;
@@ -218,6 +217,7 @@ class House
return doorSet; return doorSet;
} }
void addBed(BedItem* bed); void addBed(BedItem* bed);
const HouseBedItemList& getBeds() const { const HouseBedItemList& getBeds() const {
return bedsList; return bedsList;
@@ -257,7 +257,7 @@ class House
bool isLoaded = false; bool isLoaded = false;
}; };
using HouseMap = std::map<uint32_t, House*>; typedef std::map<uint32_t, House*> HouseMap;
enum RentPeriod_t { enum RentPeriod_t {
RENTPERIOD_DAILY, RENTPERIOD_DAILY,

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,13 +31,13 @@ class HouseTile final : public DynamicTile
//cylinder implementations //cylinder implementations
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, 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, 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 addThing(int32_t index, Thing* thing) final;
void internalAddThing(uint32_t index, Thing* thing) override; void internalAddThing(uint32_t index, Thing* thing) final;
House* getHouse() { House* getHouse() {
return house; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -19,51 +19,29 @@
#include "otpch.h" #include "otpch.h"
#include "database.h"
#include "guild.h"
#include "ioguild.h" #include "ioguild.h"
#include "database.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;
}
uint32_t IOGuild::getGuildIdByName(const std::string& name) uint32_t IOGuild::getGuildIdByName(const std::string& name)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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) { if (!result) {
return 0; return 0;
} }
return result->getNumber<uint32_t>("id"); 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; std::ostringstream query;
query << "SELECT `guild1`, `guild2` FROM `guild_wars` WHERE (`guild1` = " << guildId << " OR `guild2` = " << guildId << ") AND `ended` = 0 AND `status` = 1"; 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) { if (!result) {
return; return;
} }
@@ -71,9 +49,9 @@ void IOGuild::getWarList(uint32_t guildId, GuildWarVector& guildWarVector)
do { do {
uint32_t guild1 = result->getNumber<uint32_t>("guild1"); uint32_t guild1 = result->getNumber<uint32_t>("guild1");
if (guildId != guild1) { if (guildId != guild1) {
guildWarVector.push_back(guild1); guildWarList.push_back(guild1);
} else { } else {
guildWarVector.push_back(result->getNumber<uint32_t>("guild2")); guildWarList.push_back(result->getNumber<uint32_t>("guild2"));
} }
} while (result->next()); } while (result->next());
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -20,15 +20,13 @@
#ifndef FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF #ifndef FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF
#define FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF #define FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF
class Guild; typedef std::vector<uint32_t> GuildWarList;
using GuildWarVector = std::vector<uint32_t>;
class IOGuild class IOGuild
{ {
public: public:
static Guild* loadGuild(uint32_t guildId);
static uint32_t getGuildIdByName(const std::string& name); 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 #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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; Account account;
std::ostringstream query; std::ostringstream query;
query << "SELECT `id`, `name`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno; query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno;
DBResult_ptr result = Database::getInstance().storeQuery(query.str()); DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
if (!result) { if (!result) {
return account; return account;
} }
account.id = result->getNumber<uint32_t>("id"); account.id = result->getNumber<uint32_t>("id");
account.name = result->getString("name");
account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type")); account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
account.premiumDays = result->getNumber<uint16_t>("premdays"); account.premiumDays = result->getNumber<uint16_t>("premdays");
account.lastDay = result->getNumber<time_t>("lastday"); account.lastDay = result->getNumber<time_t>("lastday");
@@ -49,45 +48,16 @@ bool IOLoginData::saveAccount(const Account& acc)
{ {
std::ostringstream query; std::ostringstream query;
query << "UPDATE `accounts` SET `premdays` = " << acc.premiumDays << ", `lastday` = " << acc.lastDay << " WHERE `id` = " << acc.id; 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 Database* db = Database::getInstance();
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();
std::ostringstream query; std::ostringstream query;
query << "SELECT `id`, `name`, `password`, `secret`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `name` = " << db.escapeString(name); query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accountNumber;
DBResult_ptr result = db.storeQuery(query.str()); DBResult_ptr result = db->storeQuery(query.str());
if (!result) { if (!result) {
return false; return false;
} }
@@ -97,15 +67,13 @@ bool IOLoginData::loginserverAuthentication(const std::string& name, const std::
} }
account.id = result->getNumber<uint32_t>("id"); 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.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
account.premiumDays = result->getNumber<uint16_t>("premdays"); account.premiumDays = result->getNumber<uint16_t>("premdays");
account.lastDay = result->getNumber<time_t>("lastday"); account.lastDay = result->getNumber<time_t>("lastday");
query.str(std::string()); query.str(std::string());
query << "SELECT `name`, `deletion` FROM `players` WHERE `account_id` = " << account.id; query << "SELECT `name`, `deletion` FROM `players` WHERE `account_id` = " << account.id;
result = db.storeQuery(query.str()); result = db->storeQuery(query.str());
if (result) { if (result) {
do { do {
if (result->getNumber<uint64_t>("deletion") == 0) { if (result->getNumber<uint64_t>("deletion") == 0) {
@@ -117,29 +85,17 @@ bool IOLoginData::loginserverAuthentication(const std::string& name, const std::
return true; 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; std::ostringstream query;
query << "SELECT `id`, `password`, `secret` FROM `accounts` WHERE `name` = " << db.escapeString(accountName); query << "SELECT `id`, `password` FROM `accounts` WHERE `id` = " << accountNumber;
DBResult_ptr result = db.storeQuery(query.str()); DBResult_ptr result = db->storeQuery(query.str());
if (!result) { if (!result) {
return 0; 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")) { if (transformToSHA1(password) != result->getString("password")) {
return 0; return 0;
} }
@@ -147,8 +103,8 @@ uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, co
uint32_t accountId = result->getNumber<uint32_t>("id"); uint32_t accountId = result->getNumber<uint32_t>("id");
query.str(std::string()); query.str(std::string());
query << "SELECT `account_id`, `name`, `deletion` FROM `players` WHERE `name` = " << db.escapeString(characterName); query << "SELECT `account_id`, `name`, `deletion` FROM `players` WHERE `name` = " << db->escapeString(characterName);
result = db.storeQuery(query.str()); result = db->storeQuery(query.str());
if (!result) { if (!result) {
return 0; return 0;
} }
@@ -164,7 +120,7 @@ AccountType_t IOLoginData::getAccountType(uint32_t accountId)
{ {
std::ostringstream query; std::ostringstream query;
query << "SELECT `type` FROM `accounts` WHERE `id` = " << accountId; 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) { if (!result) {
return ACCOUNT_TYPE_NORMAL; return ACCOUNT_TYPE_NORMAL;
} }
@@ -175,7 +131,7 @@ void IOLoginData::setAccountType(uint32_t accountId, AccountType_t accountType)
{ {
std::ostringstream query; std::ostringstream query;
query << "UPDATE `accounts` SET `type` = " << static_cast<uint16_t>(accountType) << " WHERE `id` = " << accountId; 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) void IOLoginData::updateOnlineStatus(uint32_t guid, bool login)
@@ -190,20 +146,20 @@ void IOLoginData::updateOnlineStatus(uint32_t guid, bool login)
} else { } else {
query << "DELETE FROM `players_online` WHERE `player_id` = " << guid; 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) bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `id`, `account_id`, `group_id`, `deletion`, (SELECT `type` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `account_type`"; 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)) { if (!g_config.getBoolean(ConfigManager::FREE_PREMIUM)) {
query << ", (SELECT `premdays` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days`"; query << ", (SELECT `premdays` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days`";
} }
query << " FROM `players` WHERE `name` = " << db.escapeString(name); query << " FROM `players` WHERE `name` = " << db->escapeString(name);
DBResult_ptr result = db.storeQuery(query.str()); DBResult_ptr result = db->storeQuery(query.str());
if (!result) { if (!result) {
return false; return false;
} }
@@ -231,18 +187,17 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
bool IOLoginData::loadPlayerById(Player* player, uint32_t id) bool IOLoginData::loadPlayerById(Player* player, uint32_t id)
{ {
Database& db = Database::getInstance();
std::ostringstream query; 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; 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, db.storeQuery(query.str())); return loadPlayer(player, Database::getInstance()->storeQuery(query.str()));
} }
bool IOLoginData::loadPlayerByName(Player* player, const std::string& name) bool IOLoginData::loadPlayerByName(Player* player, const std::string& name)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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); 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())); return loadPlayer(player, db->storeQuery(query.str()));
} }
bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
@@ -251,7 +206,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
return false; return false;
} }
Database& db = Database::getInstance(); Database* db = Database::getInstance();
uint32_t accno = result->getNumber<uint32_t>("account_id"); uint32_t accno = result->getNumber<uint32_t>("account_id");
Account acc = loadAccount(accno); Account acc = loadAccount(accno);
@@ -297,7 +252,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
} }
player->soul = result->getNumber<uint16_t>("soul"); 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"); player->blessings = result->getNumber<uint16_t>("blessings");
unsigned long conditionsSize; 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.lookFeet = result->getNumber<uint16_t>("lookfeet");
player->defaultOutfit.lookAddons = result->getNumber<uint16_t>("lookaddons"); player->defaultOutfit.lookAddons = result->getNumber<uint16_t>("lookaddons");
player->currentOutfit = player->defaultOutfit; player->currentOutfit = player->defaultOutfit;
player->direction = static_cast<Direction> (result->getNumber<uint16_t>("direction"));
if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
const time_t skullSeconds = result->getNumber<time_t>("skulltime") - time(nullptr); player->playerKillerEnd = result->getNumber<time_t>("skulltime");
if (skullSeconds > 0) {
//ensure that we round up the number of ticks
player->skullTicks = (skullSeconds + 2);
uint16_t skull = result->getNumber<uint16_t>("skull"); uint16_t skull = result->getNumber<uint16_t>("skull");
if (skull == SKULL_RED) { if (skull == SKULL_RED) {
player->skull = SKULL_RED; player->skull = SKULL_RED;
} else if (skull == SKULL_BLACK) { }
player->skull = SKULL_BLACK;
} 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->lastLoginSaved = result->getNumber<time_t>("lastlogin");
player->lastLogout = result->getNumber<time_t>("lastlogout"); 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")); Town* town = g_game.map.towns.getTown(result->getNumber<uint32_t>("town_id"));
if (!town) { if (!town) {
std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Town ID " << result->getNumber<uint32_t>("town_id") << " which doesn't exist" << std::endl; 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; 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(); 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 guildId = result->getNumber<uint32_t>("guild_id");
uint32_t playerRankId = result->getNumber<uint32_t>("rank_id"); uint32_t playerRankId = result->getNumber<uint32_t>("rank_id");
player->guildNick = result->getString("nick"); player->guildNick = result->getString("nick");
Guild* guild = g_game.getGuild(guildId); Guild* guild = g_game.getGuild(guildId);
if (!guild) { if (!guild) {
guild = IOGuild::loadGuild(guildId); query.str(std::string());
g_game.addGuild(guild); 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) { if (guild) {
@@ -421,7 +392,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
query.str(std::string()); query.str(std::string());
query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = " << playerRankId; 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")); 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; player->guildRank = rank;
IOGuild::getWarList(guildId, player->guildWarVector); IOGuild::getWarList(guildId, player->guildWarList);
query.str(std::string()); query.str(std::string());
query << "SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = " << guildId; 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")); guild->setMemberCount(result->getNumber<uint32_t>("members"));
} }
} }
@@ -445,7 +416,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
query.str(std::string()); query.str(std::string());
query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID(); 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 { do {
player->learnedInstantSpellList.emplace_front(result->getString("name")); player->learnedInstantSpellList.emplace_front(result->getString("name"));
} while (result->next()); } while (result->next());
@@ -456,7 +427,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
query.str(std::string()); query.str(std::string());
query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; 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); loadItems(itemMap, result);
for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { 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.str(std::string());
query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC"; 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); loadItems(itemMap, result);
for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) { 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; int32_t pid = pair.second;
if (pid >= 0 && pid < 100) { if (pid >= 0 && pid < 100) {
DepotChest* depotChest = player->getDepotChest(pid, true); Container* itemContainer = item->getContainer();
if (depotChest) { if (itemContainer) {
depotChest->internalAddThing(item); DepotLocker* locker = itemContainer->getDepotLocker();
if (locker) {
DepotLocker* existingLocker = player->getDepotLocker(pid, false);
if (!existingLocker) {
player->depotLockerMap[pid] = locker;
}
}
} }
} else { } else {
ItemMap::const_iterator it2 = itemMap.find(pid); 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 //load storage map
query.str(std::string()); query.str(std::string());
query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID(); 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 { 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()); } while (result->next());
} }
//load vip //load vip
query.str(std::string()); query.str(std::string());
query << "SELECT `player_id` FROM `account_viplist` WHERE `account_id` = " << player->getAccount(); 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 { do {
player->addVIPInternal(result->getNumber<uint32_t>("player_id")); player->addVIPInternal(result->getNumber<uint32_t>("player_id"));
} while (result->next()); } while (result->next());
@@ -569,12 +516,12 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
{ {
std::ostringstream ss; std::ostringstream ss;
using ContainerBlock = std::pair<Container*, int32_t>; typedef std::pair<Container*, int32_t> containerBlock;
std::list<ContainerBlock> queue; std::list<containerBlock> queue;
int32_t runningId = 100; int32_t runningId = 100;
Database& db = Database::getInstance(); Database* db = Database::getInstance();
for (const auto& it : itemList) { for (const auto& it : itemList) {
int32_t pid = it.first; int32_t pid = it.first;
Item* item = it.second; Item* item = it.second;
@@ -586,7 +533,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
size_t attributesSize; size_t attributesSize;
const char* attributes = propWriteStream.getStream(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)) { if (!query_insert.addRow(ss)) {
return false; return false;
} }
@@ -597,7 +544,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
} }
while (!queue.empty()) { while (!queue.empty()) {
const ContainerBlock& cb = queue.front(); const containerBlock& cb = queue.front();
Container* container = cb.first; Container* container = cb.first;
int32_t parentId = cb.second; int32_t parentId = cb.second;
queue.pop_front(); queue.pop_front();
@@ -616,7 +563,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
size_t attributesSize; size_t attributesSize;
const char* attributes = propWriteStream.getStream(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)) { if (!query_insert.addRow(ss)) {
return false; return false;
} }
@@ -631,11 +578,11 @@ bool IOLoginData::savePlayer(Player* player)
player->changeHealth(1); player->changeHealth(1);
} }
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID(); 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) { if (!result) {
return false; return false;
} }
@@ -643,7 +590,7 @@ bool IOLoginData::savePlayer(Player* player)
if (result->getNumber<uint16_t>("save") == 0) { if (result->getNumber<uint16_t>("save") == 0) {
query.str(std::string()); query.str(std::string());
query << "UPDATE `players` SET `lastlogin` = " << player->lastLoginSaved << ", `lastip` = " << player->lastIP << " WHERE `id` = " << player->getGUID(); 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 //serialize conditions
@@ -686,7 +633,7 @@ bool IOLoginData::savePlayer(Player* player)
query << "`posz` = " << loginPosition.getZ() << ','; query << "`posz` = " << loginPosition.getZ() << ',';
query << "`cap` = " << (player->capacity / 100) << ','; query << "`cap` = " << (player->capacity / 100) << ',';
query << "`sex` = " << static_cast<uint16_t>(player->sex) << ','; query << "`sex` = " << player->sex << ',';
if (player->lastLoginSaved != 0) { if (player->lastLoginSaved != 0) {
query << "`lastlogin` = " << player->lastLoginSaved << ','; query << "`lastlogin` = " << player->lastLoginSaved << ',';
@@ -696,29 +643,21 @@ bool IOLoginData::savePlayer(Player* player)
query << "`lastip` = " << player->lastIP << ','; query << "`lastip` = " << player->lastIP << ',';
} }
query << "`conditions` = " << db.escapeBlob(conditions, conditionsSize) << ','; query << "`conditions` = " << db->escapeBlob(conditions, conditionsSize) << ',';
if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) { if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
int64_t skullTime = 0; query << "`skulltime` = " << player->getPlayerKillerEnd() << ',';
if (player->skullTicks > 0) {
skullTime = time(nullptr) + player->skullTicks;
}
query << "`skulltime` = " << skullTime << ',';
Skulls_t skull = SKULL_NONE; Skulls_t skull = SKULL_NONE;
if (player->skull == SKULL_RED) { if (player->skull == SKULL_RED) {
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 << "`lastlogout` = " << player->getLastLogout() << ',';
query << "`balance` = " << player->bankBalance << ','; query << "`balance` = " << player->bankBalance << ',';
query << "`offlinetraining_time` = " << player->getOfflineTrainingTime() / 1000 << ',';
query << "`offlinetraining_skill` = " << player->getOfflineTrainingSkill() << ',';
query << "`stamina` = " << player->getStaminaMinutes() << ','; query << "`stamina` = " << player->getStaminaMinutes() << ',';
query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ','; 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_shielding_tries` = " << player->skills[SKILL_SHIELD].tries << ',';
query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ','; query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ',';
query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ','; query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ',';
query << "`direction` = " << static_cast<uint16_t> (player->getDirection()) << ',';
if (!player->isOffline()) { if (!player->isOffline()) {
query << "`onlinetime` = `onlinetime` + " << (time(nullptr) - player->lastLoginSaved) << ','; query << "`onlinetime` = `onlinetime` + " << (time(nullptr) - player->lastLoginSaved) << ',';
@@ -748,14 +686,14 @@ bool IOLoginData::savePlayer(Player* player)
return false; return false;
} }
if (!db.executeQuery(query.str())) { if (!db->executeQuery(query.str())) {
return false; return false;
} }
// learned spells // learned spells
query.str(std::string()); query.str(std::string());
query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID(); query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID();
if (!db.executeQuery(query.str())) { if (!db->executeQuery(query.str())) {
return false; return false;
} }
@@ -763,7 +701,7 @@ bool IOLoginData::savePlayer(Player* player)
DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES "); DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES ");
for (const std::string& spellName : player->learnedInstantSpellList) { for (const std::string& spellName : player->learnedInstantSpellList) {
query << player->getGUID() << ',' << db.escapeString(spellName); query << player->getGUID() << ',' << db->escapeString(spellName);
if (!spellsQuery.addRow(query)) { if (!spellsQuery.addRow(query)) {
return false; return false;
} }
@@ -773,9 +711,31 @@ bool IOLoginData::savePlayer(Player* player)
return false; 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 //item saving
query.str(std::string());
query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID(); query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID();
if (!db.executeQuery(query.str())) { if (!db->executeQuery(query.str())) {
return false; return false;
} }
@@ -793,51 +753,28 @@ bool IOLoginData::savePlayer(Player* player)
return false; return false;
} }
if (player->lastDepotId != -1) { //save depot items
//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
query.str(std::string()); query.str(std::string());
query << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID(); query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID();
if (!db.executeQuery(query.str())) {
if (!db->executeQuery(query.str())) {
return false; 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(); itemList.clear();
for (Item* item : player->getInbox()->getItemList()) { for (const auto& it : player->depotLockerMap) {
itemList.emplace_back(0, item); itemList.emplace_back(it.first, it.second);
} }
if (!saveItems(player, itemList, inboxQuery, propWriteStream)) { if (!saveItems(player, itemList, depotQuery, propWriteStream)) {
return false; return false;
} }
query.str(std::string()); query.str(std::string());
query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID(); query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID();
if (!db.executeQuery(query.str())) { if (!db->executeQuery(query.str())) {
return false; return false;
} }
@@ -865,7 +802,7 @@ std::string IOLoginData::getNameByGuid(uint32_t guid)
{ {
std::ostringstream query; std::ostringstream query;
query << "SELECT `name` FROM `players` WHERE `id` = " << guid; 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) { if (!result) {
return std::string(); return std::string();
} }
@@ -874,11 +811,11 @@ std::string IOLoginData::getNameByGuid(uint32_t guid)
uint32_t IOLoginData::getGuidByName(const std::string& name) uint32_t IOLoginData::getGuidByName(const std::string& name)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `id` FROM `players` WHERE `name` = " << db.escapeString(name); query << "SELECT `id` FROM `players` WHERE `name` = " << db->escapeString(name);
DBResult_ptr result = db.storeQuery(query.str()); DBResult_ptr result = db->storeQuery(query.str());
if (!result) { if (!result) {
return 0; 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) bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string& name)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = " << db.escapeString(name); query << "SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = " << db->escapeString(name);
DBResult_ptr result = db.storeQuery(query.str()); DBResult_ptr result = db->storeQuery(query.str());
if (!result) { if (!result) {
return false; return false;
} }
@@ -913,12 +850,12 @@ bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string&
bool IOLoginData::formatPlayerName(std::string& name) bool IOLoginData::formatPlayerName(std::string& name)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; 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) { if (!result) {
return false; return false;
} }
@@ -957,16 +894,16 @@ void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance)
{ {
std::ostringstream query; std::ostringstream query;
query << "UPDATE `players` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid; 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) bool IOLoginData::hasBiddedOnHouse(uint32_t guid)
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `id` FROM `houses` WHERE `highest_bidder` = " << guid << " LIMIT 1"; 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) 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::forward_list<VIPEntry> entries;
std::ostringstream query; 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) { if (result) {
do { do {
entries.emplace_front( entries.emplace_front(
result->getNumber<uint32_t>("player_id"), result->getNumber<uint32_t>("player_id"),
result->getString("name"), result->getString("name")
result->getString("description"),
result->getNumber<uint32_t>("icon"),
result->getNumber<uint16_t>("notify") != 0
); );
} while (result->next()); } while (result->next());
} }
return entries; 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; std::ostringstream query;
query << "INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES (" << accountId << ',' << guid << ',' << db.escapeString(description) << ',' << icon << ',' << notify << ')'; query << "INSERT INTO `account_viplist` (`account_id`, `player_id`) VALUES (" << accountId << ',' << guid << ')';
db.executeQuery(query.str()); 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());
} }
void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid) void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid)
{ {
std::ostringstream query; std::ostringstream query;
query << "DELETE FROM `account_viplist` WHERE `account_id` = " << accountId << " AND `player_id` = " << guid; 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) void IOLoginData::addPremiumDays(uint32_t accountId, int32_t addDays)
{ {
std::ostringstream query; std::ostringstream query;
query << "UPDATE `accounts` SET `premdays` = `premdays` + " << addDays << " WHERE `id` = " << accountId; 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) void IOLoginData::removePremiumDays(uint32_t accountId, int32_t removeDays)
{ {
std::ostringstream query; std::ostringstream query;
query << "UPDATE `accounts` SET `premdays` = `premdays` - " << removeDays << " WHERE `id` = " << accountId; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -24,7 +24,7 @@
#include "player.h" #include "player.h"
#include "database.h" #include "database.h"
using ItemBlockList = std::list<std::pair<int32_t, Item*>>; typedef std::list<std::pair<int32_t, Item*>> ItemBlockList;
class IOLoginData class IOLoginData
{ {
@@ -32,8 +32,8 @@ class IOLoginData
static Account loadAccount(uint32_t accno); static Account loadAccount(uint32_t accno);
static bool saveAccount(const Account& acc); static bool saveAccount(const Account& acc);
static bool loginserverAuthentication(const std::string& name, const std::string& password, Account& account); static bool loginserverAuthentication(uint32_t accountNumber, 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 uint32_t gameworldAuthentication(uint32_t accountNumber, const std::string& password, std::string& characterName);
static AccountType_t getAccountType(uint32_t accountId); static AccountType_t getAccountType(uint32_t accountId);
static void setAccountType(uint32_t accountId, AccountType_t accountType); static void setAccountType(uint32_t accountId, AccountType_t accountType);
@@ -52,18 +52,17 @@ class IOLoginData
static bool hasBiddedOnHouse(uint32_t guid); static bool hasBiddedOnHouse(uint32_t guid);
static std::forward_list<VIPEntry> getVIPEntries(uint32_t accountId); 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 addVIPEntry(uint32_t accountId, uint32_t guid);
static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify);
static void removeVIPEntry(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 addPremiumDays(uint32_t accountId, int32_t addDays);
static void removePremiumDays(uint32_t accountId, int32_t removeDays); static void removePremiumDays(uint32_t accountId, int32_t removeDays);
private: protected:
using ItemMap = std::map<uint32_t, std::pair<Item*, uint32_t>>; typedef std::map<uint32_t, std::pair<Item*, uint32_t>> ItemMap;
static void loadItems(ItemMap& itemMap, DBResult_ptr result); 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 #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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; 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(); 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; PropStream propStream;
if (!loader.getProps(root, propStream)) {
NODE root = f.getChildNode(nullptr, type);
if (!f.getProps(root, propStream)) {
setLastErrorString("Could not read root property."); setLastErrorString("Could not read root property.");
return false; return false;
} }
@@ -85,7 +94,7 @@ bool IOMap::loadMap(Map* map, const std::string& fileName)
} }
uint32_t headerVersion = root_header.version; uint32_t headerVersion = root_header.version;
if (headerVersion == 0) { if (headerVersion <= 0) {
//In otbm version 1 the count variable after splashes/fluidcontainers and stackables //In otbm version 1 the count variable after splashes/fluidcontainers and stackables
//are saved as attributes instead, this solves alot of problems with items //are saved as attributes instead, this solves alot of problems with items
//that is changed (stackable/charges/fluidcontainer/splash) during an update. //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; 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; std::cout << "> Map size: " << root_header.width << "x" << root_header.height << '.' << std::endl;
map->width = root_header.width; map->width = root_header.width;
map->height = root_header.height; 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."); setLastErrorString("Could not read data node.");
return false; return false;
} }
auto& mapNode = root.children[0]; if (!f.getProps(nodeMap, propStream)) {
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)) {
setLastErrorString("Could not read map data attributes."); setLastErrorString("Could not read map data attributes.");
return false; return false;
} }
@@ -181,8 +141,8 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode
return false; return false;
} }
map.spawnfile = fileName.substr(0, fileName.rfind('/') + 1); map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1);
map.spawnfile += tmp; map->spawnfile += tmp;
break; break;
case OTBM_ATTR_EXT_HOUSE_FILE: case OTBM_ATTR_EXT_HOUSE_FILE:
@@ -191,8 +151,8 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode
return false; return false;
} }
map.housefile = fileName.substr(0, fileName.rfind('/') + 1); map->housefile = identifier.substr(0, identifier.rfind('/') + 1);
map.housefile += tmp; map->housefile += tmp;
break; break;
default: default:
@@ -200,104 +160,172 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode
return false; return false;
} }
} }
return true;
}
bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map) NODE nodeMapData = f.getChildNode(nodeMap, type);
{ while (nodeMapData != NO_NODE) {
PropStream propStream; if (f.getError() != ERROR_NONE) {
if (!loader.getProps(tileAreaNode, propStream)) { setLastErrorString("Invalid map node.");
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.");
return false; return false;
} }
if (!loader.getProps(tileNode, propStream)) { if (type == OTBM_TILE_AREA) {
setLastErrorString("Could not read node data."); if (!f.getProps(nodeMapData, propStream)) {
return false; setLastErrorString("Invalid map node.");
}
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());
return false; return false;
} }
house = map.houses.addHouse(houseId); OTBM_Destination_coords area_coord;
if (!house) { if (!propStream.read(area_coord)) {
std::ostringstream ss; setLastErrorString("Invalid map node.");
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId;
setLastErrorString(ss.str());
return false; return false;
} }
tile = new HouseTile(x, y, z, house); uint16_t base_x = area_coord.x;
house->addTile(static_cast<HouseTile*>(tile)); uint16_t base_y = area_coord.y;
isHouseTile = true; uint16_t z = area_coord.z;
}
uint8_t attribute; NODE nodeTile = f.getChildNode(nodeMapData, type);
//read tile attributes while (nodeTile != NO_NODE) {
while (propStream.read<uint8_t>(attribute)) { if (f.getError() != ERROR_NONE) {
switch (attribute) { setLastErrorString("Could not read node data.");
case OTBM_ATTR_TILE_FLAGS: { return false;
uint32_t flags; }
if (!propStream.read<uint32_t>(flags)) {
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; 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()); setLastErrorString(ss.str());
return false; return false;
} }
if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) { house = map->houses.addHouse(houseId);
tileflags |= TILESTATE_PROTECTIONZONE; if (!house) {
} else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) { std::ostringstream ss;
tileflags |= TILESTATE_NOPVPZONE; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId;
} else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) { setLastErrorString(ss.str());
tileflags |= TILESTATE_PVPZONE; return false;
} }
if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) { tile = new HouseTile(x, y, z, house);
tileflags |= TILESTATE_NOLOGOUT; house->addTile(static_cast<HouseTile*>(tile));
} isHouseTile = true;
break;
} }
case OTBM_ATTR_ITEM: { //read tile attributes
Item* item = Item::CreateItem(propStream); 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) { if (!item) {
std::ostringstream ss; std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item."; 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; 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()) { 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; delete item;
} else { } else {
if (item->getItemCount() == 0) { if (item->getItemCount() <= 0) {
item->setItemCount(1); item->setItemCount(1);
} }
@@ -327,156 +363,100 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma
item->setLoadedFromMap(true); item->setLoadedFromMap(true);
} }
} }
break;
nodeItem = f.getNextNode(nodeItem, type);
} }
default: if (!tile) {
std::ostringstream ss; tile = createTile(ground_item, nullptr, x, y, z);
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute."; }
setLastErrorString(ss.str());
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; 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) { if (!f.getProps(nodeTown, propStream)) {
tile->internalAddThing(item); setLastErrorString("Could not read town data.");
item->startDecaying(); return false;
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);
} }
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) { nodeMapData = f.getNextNode(nodeMapData, type);
tile = createTile(ground_item, nullptr, x, y, z);
}
tile->setFlag(static_cast<tileflags_t>(tileflags));
map.setTile(x, y, z, tile);
} }
std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl;
return true; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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_EXT_FILE = 2,
OTBM_ATTR_TILE_FLAGS = 3, OTBM_ATTR_TILE_FLAGS = 3,
OTBM_ATTR_ACTION_ID = 4, OTBM_ATTR_ACTION_ID = 4,
OTBM_ATTR_UNIQUE_ID = 5, OTBM_ATTR_MOVEMENT_ID = 5,
OTBM_ATTR_TEXT = 6, OTBM_ATTR_TEXT = 6,
OTBM_ATTR_DESC = 7, OTBM_ATTR_DESC = 7,
OTBM_ATTR_TELE_DEST = 8, OTBM_ATTR_TELE_DEST = 8,
@@ -51,6 +51,12 @@ enum OTBM_AttrTypes_t {
OTBM_ATTR_SLEEPERGUID = 20, OTBM_ATTR_SLEEPERGUID = 20,
OTBM_ATTR_SLEEPSTART = 21, OTBM_ATTR_SLEEPSTART = 21,
OTBM_ATTR_CHARGES = 22, 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 { enum OTBM_NodeTypes_t {
@@ -76,7 +82,8 @@ enum OTBM_TileFlag_t : uint32_t {
OTBM_TILEFLAG_PROTECTIONZONE = 1 << 0, OTBM_TILEFLAG_PROTECTIONZONE = 1 << 0,
OTBM_TILEFLAG_NOPVPZONE = 1 << 2, OTBM_TILEFLAG_NOPVPZONE = 1 << 2,
OTBM_TILEFLAG_NOLOGOUT = 1 << 3, OTBM_TILEFLAG_NOLOGOUT = 1 << 3,
OTBM_TILEFLAG_PVPZONE = 1 << 4 OTBM_TILEFLAG_PVPZONE = 1 << 4,
OTBM_TILEFLAG_REFRESH = 1 << 5,
}; };
#pragma pack(1) #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); static Tile* createTile(Item*& ground, Item* item, uint16_t x, uint16_t y, uint8_t z);
public: public:
bool loadMap(Map* map, const std::string& fileName); bool loadMap(Map* map, const std::string& identifier);
/* Load the spawns /* Load the spawns
* \param map pointer to the Map class * \param map pointer to the Map class
@@ -147,11 +154,7 @@ class IOMap
errorString = error; errorString = error;
} }
private: protected:
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);
std::string errorString; std::string errorString;
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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(); 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) { if (!result) {
return; return;
} }
@@ -67,7 +67,7 @@ void IOMapSerialize::loadHouseItems(Map* map)
bool IOMapSerialize::saveHouseItems() bool IOMapSerialize::saveHouseItems()
{ {
int64_t start = OTSYS_TIME(); int64_t start = OTSYS_TIME();
Database& db = Database::getInstance(); Database* db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
//Start the transaction //Start the transaction
@@ -77,7 +77,7 @@ bool IOMapSerialize::saveHouseItems()
} }
//clear old tile data //clear old tile data
if (!db.executeQuery("DELETE FROM `tile_store`")) { if (!db->executeQuery("DELETE FROM `tile_store`")) {
return false; return false;
} }
@@ -93,7 +93,7 @@ bool IOMapSerialize::saveHouseItems()
size_t attributesSize; size_t attributesSize;
const char* attributes = stream.getStream(attributesSize); const char* attributes = stream.getStream(attributesSize);
if (attributesSize > 0) { if (attributesSize > 0) {
query << house->getId() << ',' << db.escapeBlob(attributes, attributesSize); query << house->getId() << ',' << db->escapeBlob(attributes, attributesSize);
if (!stmt.addRow(query)) { if (!stmt.addRow(query)) {
return false; return false;
} }
@@ -144,7 +144,7 @@ bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent)
} }
const ItemType& iType = Item::items[id]; const ItemType& iType = Item::items[id];
if (iType.moveable || iType.forceSerialize || !tile) { if (iType.moveable || !tile) {
//create a new item //create a new item
Item* item = Item::CreateItem(id); Item* item = Item::CreateItem(id);
if (item) { if (item) {
@@ -247,7 +247,7 @@ void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile)
const ItemType& it = Item::items[item->getID()]; const ItemType& it = Item::items[item->getID()];
// Note that these are NEGATED, ie. these are the items that will be saved. // 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; continue;
} }
@@ -270,9 +270,9 @@ void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile)
bool IOMapSerialize::loadHouseInfo() 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) { if (!result) {
return false; return false;
} }
@@ -286,7 +286,7 @@ bool IOMapSerialize::loadHouseInfo()
} }
} while (result->next()); } 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) { if (result) {
do { do {
House* house = g_game.map.houses.getHouse(result->getNumber<uint32_t>("house_id")); House* house = g_game.map.houses.getHouse(result->getNumber<uint32_t>("house_id"));
@@ -300,14 +300,14 @@ bool IOMapSerialize::loadHouseInfo()
bool IOMapSerialize::saveHouseInfo() bool IOMapSerialize::saveHouseInfo()
{ {
Database& db = Database::getInstance(); Database* db = Database::getInstance();
DBTransaction transaction; DBTransaction transaction;
if (!transaction.begin()) { if (!transaction.begin()) {
return false; return false;
} }
if (!db.executeQuery("DELETE FROM `house_lists`")) { if (!db->executeQuery("DELETE FROM `house_lists`")) {
return false; return false;
} }
@@ -315,16 +315,16 @@ bool IOMapSerialize::saveHouseInfo()
for (const auto& it : g_game.map.houses.getHouses()) { for (const auto& it : g_game.map.houses.getHouses()) {
House* house = it.second; House* house = it.second;
query << "SELECT `id` FROM `houses` WHERE `id` = " << house->getId(); 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) { if (result) {
query.str(std::string()); 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 { } else {
query.str(std::string()); 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()); query.str(std::string());
} }
@@ -335,7 +335,7 @@ bool IOMapSerialize::saveHouseInfo()
std::string listText; std::string listText;
if (house->getAccessList(GUEST_LIST, listText) && !listText.empty()) { 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)) { if (!stmt.addRow(query)) {
return false; return false;
} }
@@ -344,7 +344,7 @@ bool IOMapSerialize::saveHouseInfo()
} }
if (house->getAccessList(SUBOWNER_LIST, listText) && !listText.empty()) { 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)) { if (!stmt.addRow(query)) {
return false; return false;
} }
@@ -354,7 +354,7 @@ bool IOMapSerialize::saveHouseInfo()
for (Door* door : house->getDoors()) { for (Door* door : house->getDoors()) {
if (door->getAccessList(listText) && !listText.empty()) { 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)) { if (!stmt.addRow(query)) {
return false; return false;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@ class IOMapSerialize
static bool loadHouseInfo(); static bool loadHouseInfo();
static bool saveHouseInfo(); static bool saveHouseInfo();
private: protected:
static void saveItem(PropWriteStream& stream, const Item* item); static void saveItem(PropWriteStream& stream, const Item* item);
static void saveTile(PropWriteStream& stream, const Tile* tile); 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -23,12 +23,7 @@
#include "cylinder.h" #include "cylinder.h"
#include "thing.h" #include "thing.h"
#include "items.h" #include "items.h"
#include "luascript.h"
#include "tools.h"
#include <typeinfo>
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>
#include <deque> #include <deque>
class Creature; class Creature;
@@ -36,8 +31,8 @@ class Player;
class Container; class Container;
class Depot; class Depot;
class Teleport; class Teleport;
class TrashHolder;
class Mailbox; class Mailbox;
class DepotLocker;
class Door; class Door;
class MagicField; class MagicField;
class BedItem; class BedItem;
@@ -55,6 +50,7 @@ enum ITEMPROPERTY {
CONST_PROP_IMMOVABLENOFIELDBLOCKPATH, CONST_PROP_IMMOVABLENOFIELDBLOCKPATH,
CONST_PROP_NOFIELDBLOCKPATH, CONST_PROP_NOFIELDBLOCKPATH,
CONST_PROP_SUPPORTHANGABLE, CONST_PROP_SUPPORTHANGABLE,
CONST_PROP_UNLAY,
}; };
enum TradeEvents_t { enum TradeEvents_t {
@@ -73,7 +69,7 @@ enum AttrTypes_t {
//ATTR_EXT_FILE = 2, //ATTR_EXT_FILE = 2,
ATTR_TILE_FLAGS = 3, ATTR_TILE_FLAGS = 3,
ATTR_ACTION_ID = 4, ATTR_ACTION_ID = 4,
ATTR_UNIQUE_ID = 5, ATTR_MOVEMENT_ID = 5,
ATTR_TEXT = 6, ATTR_TEXT = 6,
ATTR_DESC = 7, ATTR_DESC = 7,
ATTR_TELE_DEST = 8, ATTR_TELE_DEST = 8,
@@ -91,19 +87,22 @@ enum AttrTypes_t {
ATTR_SLEEPERGUID = 20, ATTR_SLEEPERGUID = 20,
ATTR_SLEEPSTART = 21, ATTR_SLEEPSTART = 21,
ATTR_CHARGES = 22, ATTR_CHARGES = 22,
ATTR_CONTAINER_ITEMS = 23, ATTR_KEYNUMBER = 23,
ATTR_NAME = 24, ATTR_KEYHOLENUMBER = 24,
ATTR_ARTICLE = 25, ATTR_DOORQUESTNUMBER = 25,
ATTR_PLURALNAME = 26, ATTR_DOORQUESTVALUE = 26,
ATTR_WEIGHT = 27, ATTR_DOORLEVEL = 27,
ATTR_ATTACK = 28, ATTR_CHESTQUESTNUMBER = 28,
ATTR_DEFENSE = 29, // add non-OTBM attributes after here
ATTR_EXTRADEFENSE = 30, ATTR_CONTAINER_ITEMS = 29,
ATTR_ARMOR = 31, ATTR_NAME = 30,
ATTR_HITCHANCE = 32, ATTR_ARTICLE = 31,
ATTR_SHOOTRANGE = 33, ATTR_PLURALNAME = 32,
ATTR_CUSTOM_ATTRIBUTES = 34, ATTR_WEIGHT = 33,
ATTR_DECAYTO = 35 ATTR_ATTACK = 34,
ATTR_DEFENSE = 35,
ATTR_ARMOR = 36,
ATTR_SHOOTRANGE = 37,
}; };
enum Attr_ReadValue { enum Attr_ReadValue {
@@ -161,11 +160,53 @@ class ItemAttributes
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID));
} }
void setUniqueId(uint16_t n) { void setMovementID(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_UNIQUEID, n); setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n);
} }
uint16_t getUniqueId() const { uint16_t getMovementId() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID)); 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) { void setCharges(uint16_t n) {
@@ -189,13 +230,6 @@ class ItemAttributes
return getIntAttr(ITEM_ATTRIBUTE_OWNER); 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) { void setDuration(int32_t time) {
setIntAttr(ITEM_ATTRIBUTE_DURATION, time); setIntAttr(ITEM_ATTRIBUTE_DURATION, time);
} }
@@ -213,149 +247,19 @@ class ItemAttributes
return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE));
} }
struct CustomAttribute protected:
{ inline bool hasAttribute(itemAttrTypes type) const {
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 {
return (type & attributeBits) != 0; return (type & attributeBits) != 0;
} }
void removeAttribute(itemAttrTypes type); void removeAttribute(itemAttrTypes type);
static std::string emptyString; static std::string emptyString;
static int64_t emptyInt;
static double emptyDouble;
static bool emptyBool;
typedef std::unordered_map<std::string, CustomAttribute> CustomAttributeMap;
struct Attribute struct Attribute
{ {
union { union {
int64_t integer; int64_t integer;
std::string* string; std::string* string;
CustomAttributeMap* custom;
} value; } value;
itemAttrTypes type; itemAttrTypes type;
@@ -368,8 +272,6 @@ class ItemAttributes
value.integer = i.value.integer; value.integer = i.value.integer;
} else if (ItemAttributes::isStrAttrType(type)) { } else if (ItemAttributes::isStrAttrType(type)) {
value.string = new std::string(*i.value.string); value.string = new std::string(*i.value.string);
} else if (ItemAttributes::isCustomAttrType(type)) {
value.custom = new CustomAttributeMap(*i.value.custom);
} else { } else {
memset(&value, 0, sizeof(value)); memset(&value, 0, sizeof(value));
} }
@@ -381,8 +283,6 @@ class ItemAttributes
~Attribute() { ~Attribute() {
if (ItemAttributes::isStrAttrType(type)) { if (ItemAttributes::isStrAttrType(type)) {
delete value.string; delete value.string;
} else if (ItemAttributes::isCustomAttrType(type)) {
delete value.custom;
} }
} }
Attribute& operator=(Attribute other) { Attribute& operator=(Attribute other) {
@@ -393,8 +293,6 @@ class ItemAttributes
if (this != &other) { if (this != &other) {
if (ItemAttributes::isStrAttrType(type)) { if (ItemAttributes::isStrAttrType(type)) {
delete value.string; delete value.string;
} else if (ItemAttributes::isCustomAttrType(type)) {
delete value.custom;
} }
value = other.value; value = other.value;
@@ -425,94 +323,12 @@ class ItemAttributes
const Attribute* getExistingAttr(itemAttrTypes type) const; const Attribute* getExistingAttr(itemAttrTypes type) const;
Attribute& getAttr(itemAttrTypes type); 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: public:
static bool isIntAttrType(itemAttrTypes type) { inline static bool isIntAttrType(itemAttrTypes type) {
return (type & intAttributeTypes) == type; return (type & 0xFFFFE13) != 0;
} }
static bool isStrAttrType(itemAttrTypes type) { inline static bool isStrAttrType(itemAttrTypes type) {
return (type & stringAttributeTypes) == type; return (type & 0x1EC) != 0;
}
inline static bool isCustomAttrType(itemAttrTypes type) {
return (type & ITEM_ATTRIBUTE_CUSTOM) == type;
} }
const std::forward_list<Attribute>& getList() const { const std::forward_list<Attribute>& getList() const {
@@ -543,10 +359,10 @@ class Item : virtual public Thing
bool equals(const Item* otherItem) const; bool equals(const Item* otherItem) const;
Item* getItem() override final { Item* getItem() final {
return this; return this;
} }
const Item* getItem() const override final { const Item* getItem() const final {
return this; return this;
} }
virtual Teleport* getTeleport() { virtual Teleport* getTeleport() {
@@ -555,18 +371,18 @@ class Item : virtual public Thing
virtual const Teleport* getTeleport() const { virtual const Teleport* getTeleport() const {
return nullptr; return nullptr;
} }
virtual TrashHolder* getTrashHolder() {
return nullptr;
}
virtual const TrashHolder* getTrashHolder() const {
return nullptr;
}
virtual Mailbox* getMailbox() { virtual Mailbox* getMailbox() {
return nullptr; return nullptr;
} }
virtual const Mailbox* getMailbox() const { virtual const Mailbox* getMailbox() const {
return nullptr; return nullptr;
} }
virtual DepotLocker* getDepotLocker() {
return nullptr;
}
virtual const DepotLocker* getDepotLocker() const {
return nullptr;
}
virtual Door* getDoor() { virtual Door* getDoor() {
return nullptr; return nullptr;
} }
@@ -621,31 +437,6 @@ class Item : virtual public Thing
return attributes->hasAttribute(type); 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) { void setSpecialDescription(const std::string& desc) {
setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc); setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc);
} }
@@ -684,10 +475,6 @@ class Item : virtual public Thing
} }
void setActionId(uint16_t n) { void setActionId(uint16_t n) {
if (n < 100) {
n = 100;
}
setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n); setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n);
} }
uint16_t getActionId() const { uint16_t getActionId() const {
@@ -697,11 +484,14 @@ class Item : virtual public Thing
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); 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) { if (!attributes) {
return 0; 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) { void setCharges(uint16_t n) {
@@ -767,49 +557,42 @@ class Item : virtual public Thing
return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); 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 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 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); 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 getNameDescription() const;
std::string getWeightDescription() const; std::string getWeightDescription() const;
//serialization //serialization
virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream); virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream);
bool unserializeAttr(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; virtual void serializeAttr(PropWriteStream& propWriteStream) const;
bool isPushable() const override final { bool isPushable() const final {
return isMoveable(); return isMoveable();
} }
int32_t getThrowRange() const override final { int32_t getThrowRange() const final {
return (isPickupable() ? 15 : 2); return (isPickupable() ? 15 : 2);
} }
uint16_t getID() const { uint16_t getID() const {
return id; return id;
} }
uint16_t getClientID() const {
return items[id].clientId;
}
void setID(uint16_t newid); void setID(uint16_t newid);
// Returns the player that is holding this item in his inventory // Returns the player that is holding this item in his inventory
Player* getHoldingPlayer() const; Player* getHoldingPlayer() const;
CombatType_t getDamageType() const {
return items[id].damageType;
}
CombatType_t getCombatType() const {
return items[id].combatType;
}
WeaponType_t getWeaponType() const { WeaponType_t getWeaponType() const {
return items[id].weaponType; return items[id].weaponType;
} }
@@ -822,7 +605,28 @@ class Item : virtual public Thing
} }
return items[id].shootRange; 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; virtual uint32_t getWeight() const;
uint32_t getBaseWeight() const { uint32_t getBaseWeight() const {
if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) { if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) {
@@ -848,24 +652,15 @@ class Item : virtual public Thing
} }
return items[id].defense; 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 { int32_t getSlotPosition() const {
return items[id].slotPosition; return items[id].slotPosition;
} }
int8_t getHitChance() const { uint16_t getDisguiseId() const {
if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) { return items[id].disguiseId;
return getIntAttr(ITEM_ATTRIBUTE_HITCHANCE);
}
return items[id].hitChance;
} }
uint32_t getWorth() const; uint32_t getWorth() const;
LightInfo getLightInfo() const; void getLight(LightInfo& lightInfo) const;
bool hasProperty(ITEMPROPERTY prop) const; bool hasProperty(ITEMPROPERTY prop) const;
bool isBlocking() const { bool isBlocking() const {
@@ -883,15 +678,15 @@ class Item : virtual public Thing
bool isMagicField() const { bool isMagicField() const {
return items[id].isMagicField(); return items[id].isMagicField();
} }
bool isSplash() const {
return items[id].isSplash();
}
bool isMoveable() const { bool isMoveable() const {
return items[id].moveable; return items[id].moveable;
} }
bool isPickupable() const { bool isPickupable() const {
return items[id].pickupable; return items[id].pickupable;
} }
bool isUseable() const {
return items[id].useable;
}
bool isHangable() const { bool isHangable() const {
return items[id].isHangable; return items[id].isHangable;
} }
@@ -899,10 +694,33 @@ class Item : virtual public Thing
const ItemType& it = items[id]; const ItemType& it = items[id];
return it.rotatable && it.rotateTo; return it.rotatable && it.rotateTo;
} }
bool hasWalkStack() const { bool isDisguised() const {
return items[id].walkStack; 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 { const std::string& getName() const {
if (hasAttribute(ITEM_ATTRIBUTE_NAME)) { if (hasAttribute(ITEM_ATTRIBUTE_NAME)) {
return getStrAttr(ITEM_ATTRIBUTE_NAME); return getStrAttr(ITEM_ATTRIBUTE_NAME);
@@ -930,20 +748,12 @@ class Item : virtual public Thing
count = n; count = n;
} }
static uint32_t countByType(const Item* i, int32_t subType) { static uint32_t countByType(const Item* i, int32_t subType);
if (subType == -1 || subType == i->getSubType()) {
return i->getItemCount();
}
return 0;
}
void setDefaultSubtype(); void setDefaultSubtype();
uint16_t getSubType() const; uint16_t getSubType() const;
void setSubType(uint16_t n); void setSubType(uint16_t n);
void setUniqueId(uint16_t n);
void setDefaultDuration() { void setDefaultDuration() {
uint32_t duration = getDefaultDuration(); uint32_t duration = getDefaultDuration();
if (duration != 0) { if (duration != 0) {
@@ -966,18 +776,13 @@ class Item : virtual public Thing
virtual void startDecaying(); virtual void startDecaying();
bool isLoadedFromMap() const {
return loadedFromMap;
}
void setLoadedFromMap(bool value) { void setLoadedFromMap(bool value) {
loadedFromMap = value; loadedFromMap = value;
} }
bool isCleanable() const { 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() { std::unique_ptr<ItemAttributes>& getAttributes() {
if (!attributes) { if (!attributes) {
attributes.reset(new ItemAttributes()); attributes.reset(new ItemAttributes());
@@ -994,32 +799,29 @@ class Item : virtual public Thing
} }
} }
Cylinder* getParent() const override { Cylinder* getParent() const {
return parent; return parent;
} }
void setParent(Cylinder* cylinder) override { void setParent(Cylinder* cylinder) {
parent = cylinder; parent = cylinder;
} }
Cylinder* getTopParent(); Cylinder* getTopParent();
const Cylinder* getTopParent() const; const Cylinder* getTopParent() const;
Tile* getTile() override; Tile* getTile();
const Tile* getTile() const override; const Tile* getTile() const;
bool isRemoved() const override { bool isRemoved() const {
return !parent || parent->isRemoved(); return !parent || parent->isRemoved();
} }
protected: protected:
Cylinder* parent = nullptr;
uint16_t id; // the same id as in ItemType
private:
std::string getWeightDescription(uint32_t weight) const; std::string getWeightDescription(uint32_t weight) const;
Cylinder* parent = nullptr;
std::unique_ptr<ItemAttributes> attributes; std::unique_ptr<ItemAttributes> attributes;
uint32_t referenceCounter = 0; uint32_t referenceCounter = 0;
uint16_t id; // the same id as in ItemType
uint8_t count = 1; // number of stacked items uint8_t count = 1; // number of stacked items
bool loadedFromMap = false; bool loadedFromMap = false;
@@ -1027,7 +829,16 @@ class Item : virtual public Thing
//Don't add variables here, use the ItemAttribute class. //Don't add variables here, use the ItemAttribute class.
}; };
using ItemList = std::list<Item*>; typedef std::list<Item*> ItemList;
using ItemDeque = std::deque<Item*>; 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 #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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -22,8 +22,8 @@
#include "const.h" #include "const.h"
#include "enums.h" #include "enums.h"
#include "itemloader.h"
#include "position.h" #include "position.h"
#include "fileloader.h"
enum SlotPositionBits : uint32_t { enum SlotPositionBits : uint32_t {
SLOTP_WHEREEVER = 0xFFFFFFFF, SLOTP_WHEREEVER = 0xFFFFFFFF,
@@ -46,7 +46,6 @@ enum ItemTypes_t {
ITEM_TYPE_NONE, ITEM_TYPE_NONE,
ITEM_TYPE_DEPOT, ITEM_TYPE_DEPOT,
ITEM_TYPE_MAILBOX, ITEM_TYPE_MAILBOX,
ITEM_TYPE_TRASHHOLDER,
ITEM_TYPE_CONTAINER, ITEM_TYPE_CONTAINER,
ITEM_TYPE_DOOR, ITEM_TYPE_DOOR,
ITEM_TYPE_MAGICFIELD, ITEM_TYPE_MAGICFIELD,
@@ -54,117 +53,28 @@ enum ItemTypes_t {
ITEM_TYPE_BED, ITEM_TYPE_BED,
ITEM_TYPE_KEY, ITEM_TYPE_KEY,
ITEM_TYPE_RUNE, ITEM_TYPE_RUNE,
ITEM_TYPE_CHEST,
ITEM_TYPE_LAST ITEM_TYPE_LAST
}; };
enum ItemParseAttributes_t { enum itemgroup_t {
ITEM_PARSE_TYPE, ITEM_GROUP_NONE,
ITEM_PARSE_DESCRIPTION,
ITEM_PARSE_RUNESPELLNAME, ITEM_GROUP_GROUND,
ITEM_PARSE_WEIGHT, ITEM_GROUP_WEAPON,
ITEM_PARSE_SHOWCOUNT, ITEM_GROUP_AMMUNITION,
ITEM_PARSE_ARMOR, ITEM_GROUP_ARMOR,
ITEM_PARSE_DEFENSE, ITEM_GROUP_CHARGES,
ITEM_PARSE_EXTRADEF, ITEM_GROUP_TELEPORT,
ITEM_PARSE_ATTACK, ITEM_GROUP_MAGICFIELD,
ITEM_PARSE_ROTATETO, ITEM_GROUP_WRITEABLE,
ITEM_PARSE_MOVEABLE, ITEM_GROUP_KEY,
ITEM_PARSE_BLOCKPROJECTILE, ITEM_GROUP_SPLASH,
ITEM_PARSE_PICKUPABLE, ITEM_GROUP_FLUID,
ITEM_PARSE_FORCESERIALIZE, ITEM_GROUP_DOOR,
ITEM_PARSE_FLOORCHANGE, ITEM_GROUP_DEPRECATED,
ITEM_PARSE_CORPSETYPE,
ITEM_PARSE_CONTAINERSIZE, ITEM_GROUP_LAST
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,
}; };
struct Abilities { struct Abilities {
@@ -182,7 +92,6 @@ struct Abilities {
//extra skill modifiers //extra skill modifiers
int32_t skills[SKILL_LAST + 1] = { 0 }; int32_t skills[SKILL_LAST + 1] = { 0 };
int32_t specialSkills[SPECIALSKILL_LAST + 1] = { 0 };
int32_t speed = 0; int32_t speed = 0;
@@ -192,10 +101,6 @@ struct Abilities {
//damage abilities modifiers //damage abilities modifiers
int16_t absorbPercent[COMBAT_COUNT] = { 0 }; int16_t absorbPercent[COMBAT_COUNT] = { 0 };
//elemental damage
uint16_t elementDamage = 0;
CombatType_t elementType = COMBAT_NONE;
bool manaShield = false; bool manaShield = false;
bool invisible = false; bool invisible = false;
bool regeneration = false; bool regeneration = false;
@@ -219,7 +124,10 @@ class ItemType
return group == ITEM_GROUP_GROUND; return group == ITEM_GROUP_GROUND;
} }
bool isContainer() const { bool isContainer() const {
return group == ITEM_GROUP_CONTAINER; return type == ITEM_TYPE_CONTAINER;
}
bool isChest() const {
return type == ITEM_TYPE_CHEST;
} }
bool isSplash() const { bool isSplash() const {
return group == ITEM_GROUP_SPLASH; return group == ITEM_GROUP_SPLASH;
@@ -246,20 +154,11 @@ class ItemType
bool isMailbox() const { bool isMailbox() const {
return (type == ITEM_TYPE_MAILBOX); return (type == ITEM_TYPE_MAILBOX);
} }
bool isTrashHolder() const {
return (type == ITEM_TYPE_TRASHHOLDER);
}
bool isBed() const { bool isBed() const {
return (type == ITEM_TYPE_BED); return (type == ITEM_TYPE_BED);
} }
bool isRune() const { bool isRune() const {
return (type == ITEM_TYPE_RUNE); return type == ITEM_TYPE_RUNE;
}
bool isPickupable() const {
return (allowPickupable || pickupable);
}
bool isUseable() const {
return (useable);
} }
bool hasSubType() const { bool hasSubType() const {
return (isFluidContainer() || isSplash() || stackable || charges != 0); return (isFluidContainer() || isSplash() || stackable || charges != 0);
@@ -291,9 +190,7 @@ class ItemType
itemgroup_t group = ITEM_GROUP_NONE; itemgroup_t group = ITEM_GROUP_NONE;
ItemTypes_t type = ITEM_TYPE_NONE; ItemTypes_t type = ITEM_TYPE_NONE;
uint16_t id = 0; uint16_t id = 0;
uint16_t clientId = 0;
bool stackable = false; bool stackable = false;
bool isAnimation = false;
std::string name; std::string name;
std::string article; std::string article;
@@ -306,35 +203,40 @@ class ItemType
std::unique_ptr<ConditionDamage> conditionDamage; std::unique_ptr<ConditionDamage> conditionDamage;
uint32_t weight = 0; uint32_t weight = 0;
uint32_t levelDoor = 0;
uint32_t decayTime = 0; uint32_t decayTime = 0;
uint32_t wieldInfo = 0; uint32_t wieldInfo = 0;
uint32_t minReqLevel = 0; uint32_t minReqLevel = 0;
uint32_t minReqMagicLevel = 0; uint32_t minReqMagicLevel = 0;
uint32_t charges = 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 decayTo = -1;
int32_t attack = 0; int32_t attack = 0;
int32_t defense = 0; int32_t defense = 0;
int32_t extraDefense = 0; int32_t extraDefense = 0;
int32_t armor = 0; int32_t armor = 0;
uint16_t rotateTo = 0; int32_t rotateTo = 0;
int32_t runeMagLevel = 0; int32_t runeMagLevel = 0;
int32_t runeLevel = 0; int32_t runeLevel = 0;
int32_t nutrition = 0;
int32_t destroyTarget = 0;
CombatType_t combatType = COMBAT_NONE; 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 transformToFree = 0;
uint16_t disguiseId = 0;
uint16_t destroyTo = 0; uint16_t destroyTo = 0;
uint16_t maxTextLen = 0; uint16_t maxTextLen = 0;
uint16_t writeOnceItemId = 0; uint16_t writeOnceItemId = 0;
uint16_t transformEquipTo = 0; uint16_t transformEquipTo = 0;
uint16_t transformDeEquipTo = 0; uint16_t transformDeEquipTo = 0;
uint16_t maxItems = 8; uint16_t maxItems = 8;
uint16_t slotPosition = SLOTP_HAND; uint16_t slotPosition = SLOTP_RIGHT | SLOTP_LEFT | SLOTP_AMMO;
uint16_t speed = 0; uint16_t speed = 0;
uint16_t wareId = 0;
MagicEffectClasses magicEffect = CONST_ME_NONE; MagicEffectClasses magicEffect = CONST_ME_NONE;
Direction bedPartnerDir = DIRECTION_NONE; Direction bedPartnerDir = DIRECTION_NONE;
@@ -344,22 +246,30 @@ class ItemType
RaceType_t corpseType = RACE_NONE; RaceType_t corpseType = RACE_NONE;
FluidTypes_t fluidSource = FLUID_NONE; FluidTypes_t fluidSource = FLUID_NONE;
uint8_t floorChange = 0; uint8_t fragility = 0;
uint8_t alwaysOnTopOrder = 0; uint8_t alwaysOnTopOrder = 0;
uint8_t lightLevel = 0; uint8_t lightLevel = 0;
uint8_t lightColor = 0; uint8_t lightColor = 0;
uint8_t shootRange = 1; 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 forceUse = false;
bool forceSerialize = false; bool changeUse = false;
bool destroy = false;
bool corpse = false;
bool hasHeight = false; bool hasHeight = false;
bool walkStack = true; bool walkStack = true;
bool blockSolid = false; bool blockSolid = false;
bool blockPickupable = false; bool blockPickupable = false;
bool blockProjectile = false; bool blockProjectile = false;
bool blockPathFind = false; bool blockPathFind = false;
bool allowPickupable = false; bool allowPickupable = true;
bool showDuration = false; bool showDuration = false;
bool showCharges = false; bool showCharges = false;
bool showAttributes = false; bool showAttributes = false;
@@ -367,7 +277,7 @@ class ItemType
bool pickupable = false; bool pickupable = false;
bool rotatable = false; bool rotatable = false;
bool useable = false; bool useable = false;
bool moveable = false; bool moveable = true;
bool alwaysOnTop = false; bool alwaysOnTop = false;
bool canReadText = false; bool canReadText = false;
bool canWriteText = false; bool canWriteText = false;
@@ -383,8 +293,7 @@ class ItemType
class Items class Items
{ {
public: public:
using NameMap = std::unordered_multimap<std::string, uint16_t>; using nameMap = std::unordered_multimap<std::string, uint16_t>;
using InventoryVector = std::vector<uint16_t>;
Items(); Items();
@@ -395,38 +304,23 @@ class Items
bool reload(); bool reload();
void clear(); void clear();
bool loadFromOtb(const std::string& file);
const ItemType& operator[](size_t id) const { const ItemType& operator[](size_t id) const {
return getItemType(id); return getItemType(id);
} }
const ItemType& getItemType(size_t id) const; const ItemType& getItemType(size_t id) const;
ItemType& getItemType(size_t id); ItemType& getItemType(size_t id);
const ItemType& getItemIdByClientId(uint16_t spriteId) const;
uint16_t getItemIdByName(const std::string& name); uint16_t getItemIdByName(const std::string& name);
uint32_t majorVersion = 0; bool loadItems();
uint32_t minorVersion = 0;
uint32_t buildNumber = 0;
bool loadFromXml(); inline size_t size() const {
void parseItemNode(const pugi::xml_node& itemNode, uint16_t id);
void buildInventoryList();
const InventoryVector& getInventory() const {
return inventory;
}
size_t size() const {
return items.size(); return items.size();
} }
NameMap nameToItems; nameMap nameToItems;
private: protected:
std::map<uint16_t, uint16_t> reverseItemMap;
std::vector<ItemType> items; std::vector<ItemType> items;
InventoryVector inventory;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -26,52 +26,37 @@
#include <boost/lockfree/stack.hpp> #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> template <typename T, size_t CAPACITY>
class LockfreePoolingAllocator : public std::allocator<T> class LockfreePoolingAllocator : public std::allocator<T>
{ {
public: public:
LockfreePoolingAllocator() = default; template <typename U>
template <typename U, class = typename std::enable_if<!std::is_same<U, T>::value>::type>
explicit constexpr LockfreePoolingAllocator(const U&) {} explicit constexpr LockfreePoolingAllocator(const U&) {}
using value_type = T; typedef T value_type;
T* allocate(size_t) const { T* allocate(size_t) const {
auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::get(); T* p; // NOTE: p doesn't have to be initialized
void* p; // NOTE: p doesn't have to be initialized if (!getFreeList().pop(p)) {
if (!inst.pop(p)) {
//Acquire memory without calling the constructor of T //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 { void deallocate(T* p, size_t) const {
auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::get(); if (!getFreeList().bounded_push(p)) {
if (!inst.bounded_push(p)) {
//Release memory without calling the destructor of T //Release memory without calling the destructor of T
//(it has already been called at this point) //(it has already been called at this point)
operator delete(p); operator delete(p);
} }
} }
private:
typedef boost::lockfree::stack<T*, boost::lockfree::capacity<CAPACITY>> FreeList;
static FreeList& getFreeList() {
static FreeList freeList;
return freeList;
}
}; };
#endif #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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -20,11 +20,7 @@
#ifndef FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8 #ifndef FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8
#define FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8 #define FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8
#if __has_include("luajit/lua.hpp")
#include <luajit/lua.hpp> #include <luajit/lua.hpp>
#else
#include <lua.hpp>
#endif
#if LUA_VERSION_NUM >= 502 #if LUA_VERSION_NUM >= 502
#ifndef LUA_COMPAT_ALL #ifndef LUA_COMPAT_ALL
@@ -39,7 +35,6 @@
#include "database.h" #include "database.h"
#include "enums.h" #include "enums.h"
#include "position.h" #include "position.h"
#include <boost/lexical_cast.hpp>
class Thing; class Thing;
class Creature; class Creature;
@@ -51,7 +46,6 @@ class Combat;
class Condition; class Condition;
class Npc; class Npc;
class Monster; class Monster;
class InstantSpell;
enum { enum {
EVENT_ID_LOADING = 1, EVENT_ID_LOADING = 1,
@@ -99,8 +93,7 @@ struct LuaTimerEventDesc {
class LuaScriptInterface; class LuaScriptInterface;
class Cylinder; class Cylinder;
class Game; class Game;
class Npc;
struct LootBlock;
class ScriptEnvironment class ScriptEnvironment
{ {
@@ -155,9 +148,9 @@ class ScriptEnvironment
void removeItemByUID(uint32_t uid); void removeItemByUID(uint32_t uid);
private: private:
using VariantVector = std::vector<const LuaVariant*>; typedef std::vector<const LuaVariant*> VariantVector;
using StorageMap = std::map<uint32_t, int32_t>; typedef std::map<uint32_t, int32_t> StorageMap;
using DBResultMap = std::map<uint32_t, DBResult_ptr>; typedef std::map<uint32_t, DBResult_ptr> DBResultMap;
LuaScriptInterface* interface; LuaScriptInterface* interface;
@@ -216,7 +209,6 @@ class LuaScriptInterface
const std::string& getFileById(int32_t scriptId); const std::string& getFileById(int32_t scriptId);
int32_t getEvent(const std::string& eventName); int32_t getEvent(const std::string& eventName);
int32_t getEvent();
int32_t getMetaEvent(const std::string& globalName, const std::string& eventName); int32_t getMetaEvent(const std::string& globalName, const std::string& eventName);
static ScriptEnvironment* getScriptEnv() { static ScriptEnvironment* getScriptEnv() {
@@ -279,13 +271,13 @@ class LuaScriptInterface
// Get // Get
template<typename T> 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) getNumber(lua_State* L, int32_t arg)
{ {
return static_cast<T>(static_cast<int64_t>(lua_tonumber(L, arg))); return static_cast<T>(static_cast<int64_t>(lua_tonumber(L, arg)));
} }
template<typename T> 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) getNumber(lua_State* L, int32_t arg)
{ {
return static_cast<T>(lua_tonumber(L, arg)); return static_cast<T>(lua_tonumber(L, arg));
@@ -309,16 +301,16 @@ class LuaScriptInterface
return *userdata; return *userdata;
} }
template<class T> 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)); 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; 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); const auto parameters = lua_gettop(L);
if (parameters == 0 || arg > parameters) { if (parameters == 0 || arg > parameters) {
@@ -328,11 +320,11 @@ class LuaScriptInterface
} }
static std::string getString(lua_State* L, int32_t arg); 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, int32_t& stackpos);
static Position getPosition(lua_State* L, int32_t arg); static Position getPosition(lua_State* L, int32_t arg);
static Outfit_t getOutfit(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 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 Thing* getThing(lua_State* L, int32_t arg);
static Creature* getCreature(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); static LuaDataType getUserdataType(lua_State* L, int32_t arg);
// Is // 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; 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; 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); 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); 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); 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; return lua_isuserdata(L, arg) != 0;
} }
@@ -378,19 +370,17 @@ class LuaScriptInterface
// Push // Push
static void pushBoolean(lua_State* L, bool value); static void pushBoolean(lua_State* L, bool value);
static void pushCombatDamage(lua_State* L, const CombatDamage& damage); 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 pushPosition(lua_State* L, const Position& position, int32_t stackpos = 0);
static void pushOutfit(lua_State* L, const Outfit_t& outfit); 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_pushnumber(L, value);
lua_setfield(L, -2, index); 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); pushString(L, value);
lua_setfield(L, -2, index); lua_setfield(L, -2, index);
@@ -412,21 +402,9 @@ class LuaScriptInterface
void registerFunctions(); 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 registerClass(const std::string& className, const std::string& baseClass, lua_CFunction newFunction = nullptr);
void registerTable(const std::string& tableName); 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 registerMetaMethod(const std::string& className, const std::string& methodName, lua_CFunction func);
void registerGlobalMethod(const std::string& functionName, 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); 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); 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); static bool getArea(lua_State* L, std::list<uint32_t>& list, uint32_t& rows);
//lua functions //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 luaDoPlayerAddItem(lua_State* L);
static int luaDoTileAddItemEx(lua_State* L);
static int luaDoSetCreatureLight(lua_State* L); static int luaDoSetCreatureLight(lua_State* L);
//get item info //get item info
static int luaGetDepotId(lua_State* L); 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 luaGetWorldTime(lua_State* L);
static int luaGetWorldLight(lua_State* L); static int luaGetWorldLight(lua_State* L);
static int luaGetWorldUpTime(lua_State* L); static int luaGetWorldUpTime(lua_State* L);
@@ -475,7 +465,12 @@ class LuaScriptInterface
static int luaDoChallengeCreature(lua_State* L); 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 luaDebugPrint(lua_State* L);
static int luaIsInArray(lua_State* L);
static int luaAddEvent(lua_State* L); static int luaAddEvent(lua_State* L);
static int luaStopEvent(lua_State* L); static int luaStopEvent(lua_State* L);
@@ -486,9 +481,6 @@ class LuaScriptInterface
static int luaGetWaypointPositionByName(lua_State* L); static int luaGetWaypointPositionByName(lua_State* L);
static int luaSendChannelMessage(lua_State* L);
static int luaSendGuildChannelMessage(lua_State* L);
#ifndef LUAJIT_VERSION #ifndef LUAJIT_VERSION
static int luaBitNot(lua_State* L); static int luaBitNot(lua_State* L);
static int luaBitAnd(lua_State* L); static int luaBitAnd(lua_State* L);
@@ -556,12 +548,9 @@ class LuaScriptInterface
static int luaGameCreateMonster(lua_State* L); static int luaGameCreateMonster(lua_State* L);
static int luaGameCreateNpc(lua_State* L); static int luaGameCreateNpc(lua_State* L);
static int luaGameCreateTile(lua_State* L); static int luaGameCreateTile(lua_State* L);
static int luaGameCreateMonsterType(lua_State* L);
static int luaGameStartRaid(lua_State* L); static int luaGameStartRaid(lua_State* L);
static int luaGameGetClientVersion(lua_State* L);
static int luaGameReload(lua_State* L); static int luaGameReload(lua_State* L);
// Variant // Variant
@@ -582,6 +571,7 @@ class LuaScriptInterface
static int luaPositionSendMagicEffect(lua_State* L); static int luaPositionSendMagicEffect(lua_State* L);
static int luaPositionSendDistanceEffect(lua_State* L); static int luaPositionSendDistanceEffect(lua_State* L);
static int luaPositionSendMonsterSay(lua_State* L);
// Tile // Tile
static int luaTileCreate(lua_State* L); static int luaTileCreate(lua_State* L);
@@ -620,8 +610,6 @@ class LuaScriptInterface
static int luaTileGetThingIndex(lua_State* L); static int luaTileGetThingIndex(lua_State* L);
static int luaTileQueryAdd(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); static int luaTileGetHouse(lua_State* L);
@@ -650,34 +638,6 @@ class LuaScriptInterface
static int luaNetworkMessageSkipBytes(lua_State* L); static int luaNetworkMessageSkipBytes(lua_State* L);
static int luaNetworkMessageSendToPlayer(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 // Item
static int luaItemCreate(lua_State* L); static int luaItemCreate(lua_State* L);
@@ -692,9 +652,11 @@ class LuaScriptInterface
static int luaItemSplit(lua_State* L); static int luaItemSplit(lua_State* L);
static int luaItemRemove(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 luaItemGetActionId(lua_State* L);
static int luaItemSetActionId(lua_State* L); static int luaItemSetActionId(lua_State* L);
static int luaItemGetUniqueId(lua_State* L);
static int luaItemGetCount(lua_State* L); static int luaItemGetCount(lua_State* L);
static int luaItemGetCharges(lua_State* L); static int luaItemGetCharges(lua_State* L);
@@ -714,9 +676,6 @@ class LuaScriptInterface
static int luaItemGetAttribute(lua_State* L); static int luaItemGetAttribute(lua_State* L);
static int luaItemSetAttribute(lua_State* L); static int luaItemSetAttribute(lua_State* L);
static int luaItemRemoveAttribute(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 luaItemMoveTo(lua_State* L);
static int luaItemTransform(lua_State* L); static int luaItemTransform(lua_State* L);
@@ -725,7 +684,6 @@ class LuaScriptInterface
static int luaItemGetDescription(lua_State* L); static int luaItemGetDescription(lua_State* L);
static int luaItemHasProperty(lua_State* L); static int luaItemHasProperty(lua_State* L);
static int luaItemIsLoadedFromMap(lua_State* L);
// Container // Container
static int luaContainerCreate(lua_State* L); static int luaContainerCreate(lua_State* L);
@@ -733,7 +691,7 @@ class LuaScriptInterface
static int luaContainerGetSize(lua_State* L); static int luaContainerGetSize(lua_State* L);
static int luaContainerGetCapacity(lua_State* L); static int luaContainerGetCapacity(lua_State* L);
static int luaContainerGetEmptySlots(lua_State* L); static int luaContainerGetEmptySlots(lua_State* L);
static int luaContainerGetContentDescription(lua_State* L);
static int luaContainerGetItemHoldingCount(lua_State* L); static int luaContainerGetItemHoldingCount(lua_State* L);
static int luaContainerGetItemCountById(lua_State* L); static int luaContainerGetItemCountById(lua_State* L);
@@ -741,7 +699,6 @@ class LuaScriptInterface
static int luaContainerHasItem(lua_State* L); static int luaContainerHasItem(lua_State* L);
static int luaContainerAddItem(lua_State* L); static int luaContainerAddItem(lua_State* L);
static int luaContainerAddItemEx(lua_State* L); static int luaContainerAddItemEx(lua_State* L);
static int luaContainerGetCorpseOwner(lua_State* L);
// Teleport // Teleport
static int luaTeleportCreate(lua_State* L); static int luaTeleportCreate(lua_State* L);
@@ -759,8 +716,6 @@ class LuaScriptInterface
static int luaCreatureIsRemoved(lua_State* L); static int luaCreatureIsRemoved(lua_State* L);
static int luaCreatureIsCreature(lua_State* L); static int luaCreatureIsCreature(lua_State* L);
static int luaCreatureIsInGhostMode(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 luaCreatureCanSee(lua_State* L);
static int luaCreatureCanSeeCreature(lua_State* L); static int luaCreatureCanSeeCreature(lua_State* L);
@@ -787,7 +742,6 @@ class LuaScriptInterface
static int luaCreatureChangeSpeed(lua_State* L); static int luaCreatureChangeSpeed(lua_State* L);
static int luaCreatureSetDropLoot(lua_State* L); static int luaCreatureSetDropLoot(lua_State* L);
static int luaCreatureSetSkillLoss(lua_State* L);
static int luaCreatureGetPosition(lua_State* L); static int luaCreatureGetPosition(lua_State* L);
static int luaCreatureGetTile(lua_State* L); static int luaCreatureGetTile(lua_State* L);
@@ -795,7 +749,6 @@ class LuaScriptInterface
static int luaCreatureSetDirection(lua_State* L); static int luaCreatureSetDirection(lua_State* L);
static int luaCreatureGetHealth(lua_State* L); static int luaCreatureGetHealth(lua_State* L);
static int luaCreatureSetHealth(lua_State* L);
static int luaCreatureAddHealth(lua_State* L); static int luaCreatureAddHealth(lua_State* L);
static int luaCreatureGetMaxHealth(lua_State* L); static int luaCreatureGetMaxHealth(lua_State* L);
static int luaCreatureSetMaxHealth(lua_State* L); static int luaCreatureSetMaxHealth(lua_State* L);
@@ -810,7 +763,6 @@ class LuaScriptInterface
static int luaCreatureGetCondition(lua_State* L); static int luaCreatureGetCondition(lua_State* L);
static int luaCreatureAddCondition(lua_State* L); static int luaCreatureAddCondition(lua_State* L);
static int luaCreatureRemoveCondition(lua_State* L); static int luaCreatureRemoveCondition(lua_State* L);
static int luaCreatureHasCondition(lua_State* L);
static int luaCreatureRemove(lua_State* L); static int luaCreatureRemove(lua_State* L);
static int luaCreatureTeleportTo(lua_State* L); static int luaCreatureTeleportTo(lua_State* L);
@@ -823,9 +775,6 @@ class LuaScriptInterface
static int luaCreatureGetDescription(lua_State* L); static int luaCreatureGetDescription(lua_State* L);
static int luaCreatureGetPathTo(lua_State* L); static int luaCreatureGetPathTo(lua_State* L);
static int luaCreatureMove(lua_State* L);
static int luaCreatureGetZone(lua_State* L);
// Player // Player
static int luaPlayerCreate(lua_State* L); static int luaPlayerCreate(lua_State* L);
@@ -837,6 +786,7 @@ class LuaScriptInterface
static int luaPlayerGetAccountId(lua_State* L); static int luaPlayerGetAccountId(lua_State* L);
static int luaPlayerGetLastLoginSaved(lua_State* L); static int luaPlayerGetLastLoginSaved(lua_State* L);
static int luaPlayerGetLastLogout(lua_State* L); static int luaPlayerGetLastLogout(lua_State* L);
static int luaPlayerHasFlag(lua_State* L);
static int luaPlayerGetAccountType(lua_State* L); static int luaPlayerGetAccountType(lua_State* L);
static int luaPlayerSetAccountType(lua_State* L); static int luaPlayerSetAccountType(lua_State* L);
@@ -847,10 +797,10 @@ class LuaScriptInterface
static int luaPlayerGetFreeCapacity(lua_State* L); static int luaPlayerGetFreeCapacity(lua_State* L);
static int luaPlayerGetDepotChest(lua_State* L); static int luaPlayerGetDepotChest(lua_State* L);
static int luaPlayerGetInbox(lua_State* L);
static int luaPlayerGetSkullTime(lua_State* L); static int luaPlayerGetMurderTimestamps(lua_State* L);
static int luaPlayerSetSkullTime(lua_State* L); static int luaPlayerGetPlayerKillerEnd(lua_State* L);
static int luaPlayerSetPlayerKillerEnd(lua_State* L);
static int luaPlayerGetDeathPenalty(lua_State* L); static int luaPlayerGetDeathPenalty(lua_State* L);
static int luaPlayerGetExperience(lua_State* L); static int luaPlayerGetExperience(lua_State* L);
@@ -875,17 +825,6 @@ class LuaScriptInterface
static int luaPlayerGetSkillPercent(lua_State* L); static int luaPlayerGetSkillPercent(lua_State* L);
static int luaPlayerGetSkillTries(lua_State* L); static int luaPlayerGetSkillTries(lua_State* L);
static int luaPlayerAddSkillTries(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 luaPlayerGetItemCount(lua_State* L);
static int luaPlayerGetItemById(lua_State* L); static int luaPlayerGetItemById(lua_State* L);
@@ -935,7 +874,6 @@ class LuaScriptInterface
static int luaPlayerShowTextDialog(lua_State* L); static int luaPlayerShowTextDialog(lua_State* L);
static int luaPlayerSendTextMessage(lua_State* L); static int luaPlayerSendTextMessage(lua_State* L);
static int luaPlayerSendChannelMessage(lua_State* L);
static int luaPlayerSendPrivateMessage(lua_State* L); static int luaPlayerSendPrivateMessage(lua_State* L);
static int luaPlayerChannelSay(lua_State* L); static int luaPlayerChannelSay(lua_State* L);
@@ -952,10 +890,6 @@ class LuaScriptInterface
static int luaPlayerHasOutfit(lua_State* L); static int luaPlayerHasOutfit(lua_State* L);
static int luaPlayerSendOutfitWindow(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 luaPlayerGetPremiumDays(lua_State* L);
static int luaPlayerAddPremiumDays(lua_State* L); static int luaPlayerAddPremiumDays(lua_State* L);
static int luaPlayerRemovePremiumDays(lua_State* L); static int luaPlayerRemovePremiumDays(lua_State* L);
@@ -969,19 +903,12 @@ class LuaScriptInterface
static int luaPlayerForgetSpell(lua_State* L); static int luaPlayerForgetSpell(lua_State* L);
static int luaPlayerHasLearnedSpell(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 luaPlayerSave(lua_State* L);
static int luaPlayerPopupFYI(lua_State* L);
static int luaPlayerIsPzLocked(lua_State* L); static int luaPlayerIsPzLocked(lua_State* L);
static int luaPlayerGetClient(lua_State* L); static int luaPlayerGetClient(lua_State* L);
static int luaPlayerGetHouse(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); static int luaPlayerSetGhostMode(lua_State* L);
@@ -989,12 +916,7 @@ class LuaScriptInterface
static int luaPlayerGetContainerById(lua_State* L); static int luaPlayerGetContainerById(lua_State* L);
static int luaPlayerGetContainerIndex(lua_State* L); static int luaPlayerGetContainerIndex(lua_State* L);
static int luaPlayerGetInstantSpells(lua_State* L); static int luaPlayerGetTotalDamage(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);
// Monster // Monster
static int luaMonsterCreate(lua_State* L); static int luaMonsterCreate(lua_State* L);
@@ -1033,9 +955,6 @@ class LuaScriptInterface
static int luaNpcSetMasterPos(lua_State* L); static int luaNpcSetMasterPos(lua_State* L);
static int luaNpcGetSpeechBubble(lua_State* L);
static int luaNpcSetSpeechBubble(lua_State* L);
// Guild // Guild
static int luaGuildCreate(lua_State* L); static int luaGuildCreate(lua_State* L);
@@ -1047,9 +966,6 @@ class LuaScriptInterface
static int luaGuildGetRankById(lua_State* L); static int luaGuildGetRankById(lua_State* L);
static int luaGuildGetRankByLevel(lua_State* L); static int luaGuildGetRankByLevel(lua_State* L);
static int luaGuildGetMotd(lua_State* L);
static int luaGuildSetMotd(lua_State* L);
// Group // Group
static int luaGroupCreate(lua_State* L); static int luaGroupCreate(lua_State* L);
@@ -1059,13 +975,11 @@ class LuaScriptInterface
static int luaGroupGetAccess(lua_State* L); static int luaGroupGetAccess(lua_State* L);
static int luaGroupGetMaxDepotItems(lua_State* L); static int luaGroupGetMaxDepotItems(lua_State* L);
static int luaGroupGetMaxVipEntries(lua_State* L); static int luaGroupGetMaxVipEntries(lua_State* L);
static int luaGroupHasFlag(lua_State* L);
// Vocation // Vocation
static int luaVocationCreate(lua_State* L); static int luaVocationCreate(lua_State* L);
static int luaVocationGetId(lua_State* L); static int luaVocationGetId(lua_State* L);
static int luaVocationGetClientId(lua_State* L);
static int luaVocationGetName(lua_State* L); static int luaVocationGetName(lua_State* L);
static int luaVocationGetDescription(lua_State* L); static int luaVocationGetDescription(lua_State* L);
@@ -1116,68 +1030,59 @@ class LuaScriptInterface
static int luaHouseGetDoors(lua_State* L); static int luaHouseGetDoors(lua_State* L);
static int luaHouseGetDoorCount(lua_State* L); static int luaHouseGetDoorCount(lua_State* L);
static int luaHouseGetDoorIdByPosition(lua_State* L);
static int luaHouseGetTiles(lua_State* L); static int luaHouseGetTiles(lua_State* L);
static int luaHouseGetItems(lua_State* L);
static int luaHouseGetTileCount(lua_State* L); static int luaHouseGetTileCount(lua_State* L);
static int luaHouseCanEditAccessList(lua_State* L);
static int luaHouseGetAccessList(lua_State* L); static int luaHouseGetAccessList(lua_State* L);
static int luaHouseSetAccessList(lua_State* L); static int luaHouseSetAccessList(lua_State* L);
static int luaHouseKickPlayer(lua_State* L);
// ItemType // ItemType
static int luaItemTypeCreate(lua_State* L); static int luaItemTypeCreate(lua_State* L);
static int luaItemTypeIsCorpse(lua_State* L); static int luaItemTypeIsCorpse(lua_State* L);
static int luaItemTypeIsDoor(lua_State* L); static int luaItemTypeIsDoor(lua_State* L);
static int luaItemTypeIsContainer(lua_State* L); static int luaItemTypeIsContainer(lua_State* L);
static int luaItemTypeIsChest(lua_State* L);
static int luaItemTypeIsFluidContainer(lua_State* L); static int luaItemTypeIsFluidContainer(lua_State* L);
static int luaItemTypeIsMovable(lua_State* L); static int luaItemTypeIsMovable(lua_State* L);
static int luaItemTypeIsRune(lua_State* L); static int luaItemTypeIsRune(lua_State* L);
static int luaItemTypeIsStackable(lua_State* L); static int luaItemTypeIsStackable(lua_State* L);
static int luaItemTypeIsReadable(lua_State* L); static int luaItemTypeIsReadable(lua_State* L);
static int luaItemTypeIsWritable(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 luaItemTypeIsMagicField(lua_State* L);
static int luaItemTypeIsUseable(lua_State* L); static int luaItemTypeIsSplash(lua_State* L);
static int luaItemTypeIsPickupable(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 luaItemTypeGetType(lua_State* L);
static int luaItemTypeGetId(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 luaItemTypeGetName(lua_State* L);
static int luaItemTypeGetPluralName(lua_State* L); static int luaItemTypeGetPluralName(lua_State* L);
static int luaItemTypeGetArticle(lua_State* L); static int luaItemTypeGetArticle(lua_State* L);
static int luaItemTypeGetDescription(lua_State* L); static int luaItemTypeGetDescription(lua_State* L);
static int luaItemTypeGetSlotPosition(lua_State *L); static int luaItemTypeGetSlotPosition(lua_State *L);
static int luaItemTypeGetDestroyTarget(lua_State* L);
static int luaItemTypeGetCharges(lua_State* L); static int luaItemTypeGetCharges(lua_State* L);
static int luaItemTypeGetFluidSource(lua_State* L); static int luaItemTypeGetFluidSource(lua_State* L);
static int luaItemTypeGetCapacity(lua_State* L); static int luaItemTypeGetCapacity(lua_State* L);
static int luaItemTypeGetWeight(lua_State* L); static int luaItemTypeGetWeight(lua_State* L);
static int luaItemTypeGetHitChance(lua_State* L);
static int luaItemTypeGetShootRange(lua_State* L); static int luaItemTypeGetShootRange(lua_State* L);
static int luaItemTypeGetAttack(lua_State* L); static int luaItemTypeGetAttack(lua_State* L);
static int luaItemTypeGetDefense(lua_State* L); static int luaItemTypeGetDefense(lua_State* L);
static int luaItemTypeGetExtraDefense(lua_State* L);
static int luaItemTypeGetArmor(lua_State* L); static int luaItemTypeGetArmor(lua_State* L);
static int luaItemTypeGetWeaponType(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 luaItemTypeGetTransformEquipId(lua_State* L);
static int luaItemTypeGetTransformDeEquipId(lua_State* L); static int luaItemTypeGetTransformDeEquipId(lua_State* L);
static int luaItemTypeGetDestroyId(lua_State* L);
static int luaItemTypeGetDecayId(lua_State* L); static int luaItemTypeGetDecayId(lua_State* L);
static int luaItemTypeGetNutrition(lua_State* L);
static int luaItemTypeGetRequiredLevel(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); static int luaItemTypeHasSubType(lua_State* L);
@@ -1188,8 +1093,7 @@ class LuaScriptInterface
static int luaCombatSetFormula(lua_State* L); static int luaCombatSetFormula(lua_State* L);
static int luaCombatSetArea(lua_State* L); static int luaCombatSetArea(lua_State* L);
static int luaCombatAddCondition(lua_State* L); static int luaCombatSetCondition(lua_State* L);
static int luaCombatClearConditions(lua_State* L);
static int luaCombatSetCallback(lua_State* L); static int luaCombatSetCallback(lua_State* L);
static int luaCombatSetOrigin(lua_State* L); static int luaCombatSetOrigin(lua_State* L);
@@ -1211,10 +1115,10 @@ class LuaScriptInterface
static int luaConditionSetTicks(lua_State* L); static int luaConditionSetTicks(lua_State* L);
static int luaConditionSetParameter(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 luaConditionSetOutfit(lua_State* L);
static int luaConditionAddDamage(lua_State* L); static int luaConditionSetTiming(lua_State* L);
// MonsterType // MonsterType
static int luaMonsterTypeCreate(lua_State* L); static int luaMonsterTypeCreate(lua_State* L);
@@ -1225,100 +1129,47 @@ class LuaScriptInterface
static int luaMonsterTypeIsIllusionable(lua_State* L); static int luaMonsterTypeIsIllusionable(lua_State* L);
static int luaMonsterTypeIsHostile(lua_State* L); static int luaMonsterTypeIsHostile(lua_State* L);
static int luaMonsterTypeIsPushable(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 luaMonsterTypeCanPushItems(lua_State* L);
static int luaMonsterTypeCanPushCreatures(lua_State* L); static int luaMonsterTypeCanPushCreatures(lua_State* L);
static int luaMonsterTypeName(lua_State* L); static int luaMonsterTypeGetName(lua_State* L);
static int luaMonsterTypeNameDescription(lua_State* L); static int luaMonsterTypeGetNameDescription(lua_State* L);
static int luaMonsterTypeHealth(lua_State* L); static int luaMonsterTypeGetHealth(lua_State* L);
static int luaMonsterTypeMaxHealth(lua_State* L); static int luaMonsterTypeGetMaxHealth(lua_State* L);
static int luaMonsterTypeRunHealth(lua_State* L); static int luaMonsterTypeGetRunHealth(lua_State* L);
static int luaMonsterTypeExperience(lua_State* L); static int luaMonsterTypeGetExperience(lua_State* L);
static int luaMonsterTypeCombatImmunities(lua_State* L); static int luaMonsterTypeGetCombatImmunities(lua_State* L);
static int luaMonsterTypeConditionImmunities(lua_State* L); static int luaMonsterTypeGetConditionImmunities(lua_State* L);
static int luaMonsterTypeGetAttackList(lua_State* L); static int luaMonsterTypeGetAttackList(lua_State* L);
static int luaMonsterTypeAddAttack(lua_State* L);
static int luaMonsterTypeGetDefenseList(lua_State* L); static int luaMonsterTypeGetDefenseList(lua_State* L);
static int luaMonsterTypeAddDefense(lua_State* L);
static int luaMonsterTypeGetElementList(lua_State* L); static int luaMonsterTypeGetElementList(lua_State* L);
static int luaMonsterTypeAddElement(lua_State* L);
static int luaMonsterTypeGetVoices(lua_State* L); static int luaMonsterTypeGetVoices(lua_State* L);
static int luaMonsterTypeAddVoice(lua_State* L);
static int luaMonsterTypeGetLoot(lua_State* L); static int luaMonsterTypeGetLoot(lua_State* L);
static int luaMonsterTypeAddLoot(lua_State* L);
static int luaMonsterTypeGetCreatureEvents(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 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 luaMonsterTypeGetTargetDistance(lua_State* L);
static int luaMonsterTypeDefense(lua_State* L); static int luaMonsterTypeGetChangeTargetChance(lua_State* L);
static int luaMonsterTypeOutfit(lua_State* L); static int luaMonsterTypeGetChangeTargetSpeed(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);
// Party // Party
static int luaPartyCreate(lua_State* L);
static int luaPartyDisband(lua_State* L); static int luaPartyDisband(lua_State* L);
static int luaPartyGetLeader(lua_State* L); static int luaPartyGetLeader(lua_State* L);
@@ -1341,142 +1192,21 @@ class LuaScriptInterface
static int luaPartyShareExperience(lua_State* L); static int luaPartyShareExperience(lua_State* L);
static int luaPartySetSharedExperience(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 lastLuaError;
std::string interfaceName; std::string interfaceName;
int32_t eventTableRef = -1;
static ScriptEnvironment scriptEnv[16]; static ScriptEnvironment scriptEnv[16];
static int32_t scriptEnvIndex; static int32_t scriptEnvIndex;
int32_t runningEventId = EVENT_ID_USER;
std::string loadingFile; std::string loadingFile;
//script file cache
std::map<int32_t, std::string> cacheFiles;
}; };
class LuaEnvironment : public LuaScriptInterface class LuaEnvironment : public LuaScriptInterface
@@ -1489,9 +1219,9 @@ class LuaEnvironment : public LuaScriptInterface
LuaEnvironment(const LuaEnvironment&) = delete; LuaEnvironment(const LuaEnvironment&) = delete;
LuaEnvironment& operator=(const LuaEnvironment&) = delete; LuaEnvironment& operator=(const LuaEnvironment&) = delete;
bool initState() override; bool initState();
bool reInitState(); bool reInitState();
bool closeState() override; bool closeState();
LuaScriptInterface* getTestInterface(); LuaScriptInterface* getTestInterface();

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -21,7 +21,9 @@
#include "mailbox.h" #include "mailbox.h"
#include "game.h" #include "game.h"
#include "player.h"
#include "iologindata.h" #include "iologindata.h"
#include "town.h"
extern Game g_game; extern Game g_game;
@@ -91,22 +93,30 @@ void Mailbox::postRemoveNotification(Thing* thing, const Cylinder* newParent, in
bool Mailbox::sendItem(Item* item) const bool Mailbox::sendItem(Item* item) const
{ {
std::string receiver; std::string receiver;
if (!getReceiver(item, receiver)) { std::string townName;
if (!getDestination(item, receiver, townName)) {
return false; return false;
} }
/**No need to continue if its still empty**/ if (receiver.empty() || townName.empty()) {
if (receiver.empty()) { return false;
}
Town* town = g_game.map.towns.getTown(townName);
if (!town) {
return false; return false;
} }
Player* player = g_game.getPlayerByName(receiver); Player* player = g_game.getPlayerByName(receiver);
if (player) { if (player) {
if (g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, DepotLocker* depotLocker = player->getDepotLocker(town->getID(), true);
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { if (depotLocker) {
g_game.transformItem(item, item->getID() + 1); if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER,
player->onReceiveMail(); item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
return true; g_game.transformItem(item, item->getID() + 1);
player->onReceiveMail(town->getID());
return true;
}
} }
} else { } else {
Player tmpPlayer(nullptr); Player tmpPlayer(nullptr);
@@ -114,25 +124,30 @@ bool Mailbox::sendItem(Item* item) const
return false; return false;
} }
if (g_game.internalMoveItem(item->getParent(), tmpPlayer.getInbox(), INDEX_WHEREEVER, DepotLocker* depotLocker = tmpPlayer.getDepotLocker(town->getID(), true);
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { if (depotLocker) {
g_game.transformItem(item, item->getID() + 1); if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER,
IOLoginData::savePlayer(&tmpPlayer); item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
return true; g_game.transformItem(item, item->getID() + 1);
IOLoginData::savePlayer(&tmpPlayer);
return true;
}
} }
} }
return false; 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(); const Container* container = item->getContainer();
if (container) { if (container) {
for (Item* containerItem : container->getItemList()) { 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 true;
} }
} }
return false; return false;
} }
@@ -141,8 +156,24 @@ bool Mailbox::getReceiver(Item* item, std::string& name) const
return false; 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(name);
trimString(town);
return true; return true;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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: public:
explicit Mailbox(uint16_t itemId) : Item(itemId) {} explicit Mailbox(uint16_t itemId) : Item(itemId) {}
Mailbox* getMailbox() override { Mailbox* getMailbox() final {
return this; return this;
} }
const Mailbox* getMailbox() const override { const Mailbox* getMailbox() const final {
return this; return this;
} }
//cylinder implementations //cylinder implementations
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count, 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, ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count,
uint32_t& maxQueryCount, 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 override; ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final;
Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, 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(Thing* thing) final;
void addThing(int32_t index, Thing* thing) override; void addThing(int32_t index, Thing* thing) final;
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override; void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final;
void replaceThing(uint32_t index, Thing* thing) override; 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 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) override; void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
private: 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; bool sendItem(Item* item) const;
static bool canSend(const Item* item); static bool canSend(const Item* item);

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -23,6 +23,7 @@
#include "iomapserialize.h" #include "iomapserialize.h"
#include "combat.h" #include "combat.h"
#include "creature.h" #include "creature.h"
#include "monster.h"
#include "game.h" #include "game.h"
extern Game g_game; extern Game g_game;
@@ -35,6 +36,7 @@ bool Map::loadMap(const std::string& identifier, bool loadHouses)
return false; return false;
} }
Npcs::loadNpcs();
if (!IOMap::loadSpawns(this)) { if (!IOMap::loadSpawns(this)) {
std::cout << "[Warning - Map::loadMap] Failed to load spawn data." << std::endl; 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); Tile* tile = getTile(centerPos.x, centerPos.y, centerPos.z);
if (tile) { if (tile) {
placeInPZ = tile->hasFlag(TILESTATE_PROTECTIONZONE); 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; foundTile = forceLogin || ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_PLAYERISNOTINVITED;
} else { } else {
placeInPZ = false; placeInPZ = false;
@@ -200,7 +209,7 @@ bool Map::placeCreature(const Position& centerPos, Creature* creature, bool exte
continue; 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)) { if (!extendedPos || isSightClear(centerPos, tryPos, false)) {
foundTile = true; foundTile = true;
break; 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); bool teleport = forceTeleport || !newTile.getGround() || !Position::areInRange<1, 1, 0>(oldPos, newPos);
SpectatorVec spectators, newPosSpectators; SpectatorVec list;
getSpectators(spectators, oldPos, true); getSpectators(list, oldPos, true);
getSpectators(newPosSpectators, newPos, true); getSpectators(list, newPos, true);
spectators.addSpectators(newPosSpectators);
std::vector<int32_t> oldStackPosVector; std::vector<int32_t> oldStackPosVector;
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
if (Player* tmpPlayer = spectator->getPlayer()) { if (Player* tmpPlayer = spectator->getPlayer()) {
if (tmpPlayer->canSeeCreature(&creature)) { if (tmpPlayer->canSeeCreature(&creature)) {
oldStackPosVector.push_back(oldTile.getClientIndexOfCreature(tmpPlayer, &creature)); oldStackPosVector.push_back(oldTile.getClientIndexOfCreature(tmpPlayer, &creature));
@@ -281,7 +289,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
//send to client //send to client
size_t i = 0; size_t i = 0;
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
if (Player* tmpPlayer = spectator->getPlayer()) { if (Player* tmpPlayer = spectator->getPlayer()) {
//Use the correct stackpos //Use the correct stackpos
int32_t stackpos = oldStackPosVector[i++]; int32_t stackpos = oldStackPosVector[i++];
@@ -292,7 +300,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
} }
//event method //event method
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
spectator->onCreatureMove(&creature, &newTile, newPos, &oldTile, oldPos, teleport); 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); 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_y = centerPos.y + minRangeY;
int_fast16_t min_x = centerPos.x + minRangeX; int_fast16_t min_x = centerPos.x + minRangeX;
@@ -340,7 +348,7 @@ void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& center
continue; continue;
} }
spectators.emplace_back(creature); list.insert(creature);
} }
leafE = leafE->leafE; leafE = leafE->leafE;
} else { } 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) { if (centerPos.z >= MAP_MAX_LAYERS) {
return; return;
@@ -374,11 +382,11 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo
if (onlyPlayers) { if (onlyPlayers) {
auto it = playersSpectatorCache.find(centerPos); auto it = playersSpectatorCache.find(centerPos);
if (it != playersSpectatorCache.end()) { if (it != playersSpectatorCache.end()) {
if (!spectators.empty()) { if (!list.empty()) {
const SpectatorVec& cachedSpectators = it->second; const SpectatorVec& cachedList = it->second;
spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end()); list.insert(cachedList.begin(), cachedList.end());
} else { } else {
spectators = it->second; list = it->second;
} }
foundCache = true; foundCache = true;
@@ -389,17 +397,17 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo
auto it = spectatorCache.find(centerPos); auto it = spectatorCache.find(centerPos);
if (it != spectatorCache.end()) { if (it != spectatorCache.end()) {
if (!onlyPlayers) { if (!onlyPlayers) {
if (!spectators.empty()) { if (!list.empty()) {
const SpectatorVec& cachedSpectators = it->second; const SpectatorVec& cachedList = it->second;
spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end()); list.insert(cachedList.begin(), cachedList.end());
} else { } else {
spectators = it->second; list = it->second;
} }
} else { } else {
const SpectatorVec& cachedSpectators = it->second; const SpectatorVec& cachedList = it->second;
for (Creature* spectator : cachedSpectators) { for (Creature* spectator : cachedList) {
if (spectator->getPlayer()) { 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; 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 (cacheResult) {
if (onlyPlayers) { if (onlyPlayers) {
playersSpectatorCache[centerPos] = spectators; playersSpectatorCache[centerPos] = list;
} else { } else {
spectatorCache[centerPos] = spectators; spectatorCache[centerPos] = list;
} }
} }
} }
@@ -452,10 +460,6 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo
void Map::clearSpectatorCache() void Map::clearSpectatorCache()
{ {
spectatorCache.clear(); spectatorCache.clear();
}
void Map::clearPlayersSpectatorCache()
{
playersSpectatorCache.clear(); playersSpectatorCache.clear();
} }
@@ -559,7 +563,7 @@ const Tile* Map::canWalkTo(const Creature& creature, const Position& pos) const
//used for non-cached tiles //used for non-cached tiles
Tile* tile = getTile(pos.x, pos.y, pos.z); Tile* tile = getTile(pos.x, pos.y, pos.z);
if (creature.getTile() != tile) { 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; return nullptr;
} }
} }
@@ -842,16 +846,25 @@ int_fast32_t AStarNodes::getTileWalkCost(const Creature& creature, const Tile* t
{ {
int_fast32_t cost = 0; int_fast32_t cost = 0;
if (tile->getTopVisibleCreature(&creature) != nullptr) { if (tile->getTopVisibleCreature(&creature) != nullptr) {
if (const Monster* monster = creature.getMonster()) {
if (monster->canPushCreatures()) {
return cost;
}
}
//destroy creature cost //destroy creature cost
cost += MAP_NORMALWALKCOST * 3; cost += MAP_NORMALWALKCOST * 3;
} }
if (const MagicField* field = tile->getFieldItem()) { if (const MagicField* field = tile->getFieldItem()) {
CombatType_t combatType = field->getCombatType(); CombatType_t combatType = field->getCombatType();
if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) { if (combatType != COMBAT_NONE) {
cost += MAP_NORMALWALKCOST * 18; if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) {
cost += MAP_NORMALWALKCOST * 18;
}
} }
} }
return cost; return cost;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -73,7 +73,7 @@ class AStarNodes
int_fast32_t closedNodes; 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_BITS = 3;
static constexpr int32_t FLOOR_SIZE = (1 << FLOOR_BITS); static constexpr int32_t FLOOR_SIZE = (1 << FLOOR_BITS);
@@ -110,7 +110,7 @@ class QTreeNode
QTreeLeafNode* getLeaf(uint32_t x, uint32_t y); QTreeLeafNode* getLeaf(uint32_t x, uint32_t y);
template<typename Leaf, typename Node> 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 { do {
node = node->child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)]; 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); QTreeLeafNode* createLeaf(uint32_t x, uint32_t y, uint32_t level);
protected: protected:
bool leaf = false;
private:
QTreeNode* child[4] = {}; QTreeNode* child[4] = {};
bool leaf = false;
friend class Map; friend class Map;
}; };
@@ -153,7 +152,7 @@ class QTreeLeafNode final : public QTreeNode
void addCreature(Creature* c); void addCreature(Creature* c);
void removeCreature(Creature* c); void removeCreature(Creature* c);
private: protected:
static bool newLeaf; static bool newLeaf;
QTreeLeafNode* leafS = nullptr; QTreeLeafNode* leafS = nullptr;
QTreeLeafNode* leafE = nullptr; QTreeLeafNode* leafE = nullptr;
@@ -197,7 +196,7 @@ class Map
* \returns A pointer to that tile. * \returns A pointer to that tile.
*/ */
Tile* getTile(uint16_t x, uint16_t y, uint8_t z) const; 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); return getTile(pos.x, pos.y, pos.z);
} }
@@ -220,12 +219,11 @@ class Map
void moveCreature(Creature& creature, Tile& newTile, bool forceTeleport = false); 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 minRangeX = 0, int32_t maxRangeX = 0,
int32_t minRangeY = 0, int32_t maxRangeY = 0); int32_t minRangeY = 0, int32_t maxRangeY = 0);
void clearSpectatorCache(); void clearSpectatorCache();
void clearPlayersSpectatorCache();
/** /**
* Checks if you can throw an object to that position * Checks if you can throw an object to that position
@@ -264,8 +262,7 @@ class Map
Spawns spawns; Spawns spawns;
Towns towns; Towns towns;
Houses houses; Houses houses;
protected:
private:
SpectatorCache spectatorCache; SpectatorCache spectatorCache;
SpectatorCache playersSpectatorCache; SpectatorCache playersSpectatorCache;
@@ -278,7 +275,7 @@ class Map
uint32_t height = 0; uint32_t height = 0;
// Actually scans the map for spectators // 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 minRangeX, int32_t maxRangeX,
int32_t minRangeY, int32_t maxRangeY, int32_t minRangeY, int32_t maxRangeY,
int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -22,11 +22,11 @@
#include "monster.h" #include "monster.h"
#include "game.h" #include "game.h"
#include "spells.h" #include "spells.h"
#include "events.h" #include "configmanager.h"
extern ConfigManager g_config;
extern Game g_game; extern Game g_game;
extern Monsters g_monsters; extern Monsters g_monsters;
extern Events* g_events;
int32_t Monster::despawnRange; int32_t Monster::despawnRange;
int32_t Monster::despawnRadius; int32_t Monster::despawnRadius;
@@ -42,10 +42,10 @@ Monster* Monster::createMonster(const std::string& name)
return new Monster(mType); return new Monster(mType);
} }
Monster::Monster(MonsterType* mType) : Monster::Monster(MonsterType* mtype) :
Creature(), Creature(),
strDescription(mType->nameDescription), strDescription(asLowerCaseString(mtype->nameDescription)),
mType(mType) mType(mtype)
{ {
defaultOutfit = mType->info.outfit; defaultOutfit = mType->info.outfit;
currentOutfit = mType->info.outfit; currentOutfit = mType->info.outfit;
@@ -80,31 +80,45 @@ void Monster::removeList()
g_game.removeMonster(this); 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 bool Monster::canSee(const Position& pos) const
{ {
return Creature::canSee(getPosition(), pos, 9, 9); return Creature::canSee(getPosition(), pos, 9, 9);
} }
bool Monster::canWalkOnFieldType(CombatType_t combatType) const void Monster::onAttackedCreature(Creature* creature)
{ {
switch (combatType) { if (isSummon() && getMaster()) {
case COMBAT_ENERGYDAMAGE: master->onAttackedCreature(creature);
return mType->info.canWalkOnEnergy;
case COMBAT_FIREDAMAGE:
return mType->info.canWalkOnFire;
case COMBAT_EARTHDAMAGE:
return mType->info.canWalkOnPoison;
default:
return true;
} }
} }
void Monster::onAttackedCreatureDisappear(bool)
{
attackTicks = 0;
extraMeleeAttack = true;
}
void Monster::onCreatureAppear(Creature* creature, bool isLogin) void Monster::onCreatureAppear(Creature* creature, bool isLogin)
{ {
Creature::onCreatureAppear(creature, isLogin); Creature::onCreatureAppear(creature, isLogin);
@@ -238,7 +252,7 @@ void Monster::onCreatureMove(Creature* creature, const Tile* newTile, const Posi
} }
if (canSeeNewPos && isSummon() && getMaster() == creature) { if (canSeeNewPos && isSummon() && getMaster() == creature) {
isMasterInRange = true; //Follow master again isMasterInRange = true; //Follow master again
} }
updateIdleStatus(); 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_x = Position::getDistanceX(followPosition, position);
int32_t offset_y = Position::getDistanceY(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); Direction dir = getDirectionTo(position, followPosition);
const Position& checkPosition = getNextPosition(dir, position); const Position& checkPosition = getNextPosition(dir, position);
@@ -365,10 +379,10 @@ void Monster::updateTargetList()
} }
} }
SpectatorVec spectators; SpectatorVec list;
g_game.map.getSpectators(spectators, position, true); g_game.map.getSpectators(list, position, true);
spectators.erase(this); list.erase(this);
for (Creature* spectator : spectators) { for (Creature* spectator : list) {
if (canSee(spectator->getPosition())) { if (canSee(spectator->getPosition())) {
onCreatureFound(spectator); 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; std::list<Creature*> resultList;
const Position& myPos = getPosition(); const Position& myPos = getPosition();
for (Creature* creature : targetList) { for (Creature* creature : targetList) {
if (followCreature != creature && isTarget(creature)) { if (followCreature != creature && isTarget(creature)) {
if (searchType == TARGETSEARCH_RANDOM || canUseAttack(myPos, creature)) { if (searchType == TARGETSEARCH_ANY || canUseAttack(myPos, creature)) {
resultList.push_back(creature); resultList.push_back(creature);
} }
} }
@@ -497,32 +511,31 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
switch (searchType) { switch (searchType) {
case TARGETSEARCH_NEAREST: { case TARGETSEARCH_NEAREST: {
Creature* target = nullptr; 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()) { if (!resultList.empty()) {
auto it = resultList.begin(); for (Creature* creature : resultList) {
target = *it; const Position& targetPosition = creature->getPosition();
if (++it != resultList.end()) { int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition);
const Position& targetPosition = target->getPosition(); if (distance < minRange) {
int32_t minRange = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition); target = creature;
do { minRange = distance;
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());
} }
} else { } else {
int32_t minRange = std::numeric_limits<int32_t>::max();
for (Creature* creature : targetList) { for (Creature* creature : targetList) {
if (!isTarget(creature)) { if (!isTarget(creature)) {
continue; continue;
} }
const Position& pos = creature->getPosition(); const Position& targetPosition = creature->getPosition();
int32_t distance = Position::getDistanceX(myPos, pos) + Position::getDistanceY(myPos, pos); int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition);
if (distance < minRange) { if (distance < minRange) {
target = creature; target = creature;
minRange = distance; minRange = distance;
@@ -535,10 +548,79 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
} }
break; break;
} }
case TARGETSEARCH_WEAKEST: {
Creature* target = nullptr;
case TARGETSEARCH_DEFAULT: int32_t health = 0;
case TARGETSEARCH_ATTACKRANGE: if (attackedCreature) {
case TARGETSEARCH_RANDOM: 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: { default: {
if (!resultList.empty()) { if (!resultList.empty()) {
auto it = resultList.begin(); auto it = resultList.begin();
@@ -546,20 +628,19 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
return selectTarget(*it); return selectTarget(*it);
} }
if (searchType == TARGETSEARCH_ATTACKRANGE) {
return false;
}
break; break;
} }
} }
//lets just pick the first target in the list //lets just pick the first target in the list if we do not have a target
for (Creature* target : targetList) { if (!attackedCreature) {
if (followCreature != target && selectTarget(target)) { for (Creature* target : targetList) {
return true; if (followCreature != target && selectTarget(target)) {
return true;
}
} }
} }
return false; return false;
} }
@@ -637,6 +718,10 @@ bool Monster::selectTarget(Creature* creature)
g_dispatcher.addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game, getID()))); 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); return setFollowCreature(creature);
} }
@@ -673,7 +758,7 @@ void Monster::updateIdleStatus()
void Monster::onAddCondition(ConditionType_t type) 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(); updateMapCache();
} }
@@ -682,8 +767,7 @@ void Monster::onAddCondition(ConditionType_t type)
void Monster::onEndCondition(ConditionType_t type) void Monster::onEndCondition(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) {
ignoreFieldDamage = false;
updateMapCache(); updateMapCache();
} }
@@ -692,6 +776,10 @@ void Monster::onEndCondition(ConditionType_t type)
void Monster::onThink(uint32_t interval) void Monster::onThink(uint32_t interval)
{ {
if (OTSYS_TIME() < earliestWakeUpTime) {
return;
}
Creature::onThink(interval); Creature::onThink(interval);
if (mType->info.thinkEvent != -1) { if (mType->info.thinkEvent != -1) {
@@ -718,9 +806,10 @@ void Monster::onThink(uint32_t interval)
} }
} }
if (!isInSpawnRange(position)) { if (!isInSpawnRange(position) || (lifetime > 0 && (OTSYS_TIME() >= lifetime))) {
g_game.internalTeleport(this, masterPos); // Despawn creatures if they are out of their spawn zone
setIdle(true); g_game.removeCreature(this);
g_game.addMagicEffect(getPosition(), CONST_ME_POFF);
} else { } else {
updateIdleStatus(); updateIdleStatus();
@@ -730,7 +819,6 @@ void Monster::onThink(uint32_t interval)
if (isSummon()) { if (isSummon()) {
if (!attackedCreature) { if (!attackedCreature) {
if (getMaster() && getMaster()->getAttackedCreature()) { if (getMaster() && getMaster()->getAttackedCreature()) {
//This happens if the monster is summoned during combat
selectTarget(getMaster()->getAttackedCreature()); selectTarget(getMaster()->getAttackedCreature());
} else if (getMaster() != followCreature) { } else if (getMaster() != followCreature) {
//Our master has not ordered us to attack anything, lets follow him around instead. //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) { } else if (attackedCreature == this) {
setFollowCreature(nullptr); setFollowCreature(nullptr);
} else if (followCreature != attackedCreature) { } else if (followCreature != attackedCreature) {
//This happens just after a master orders an attack, so lets follow it aswell.
setFollowCreature(attackedCreature); 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()) { } else if (!targetList.empty()) {
if (!followCreature || !hasFollowPath) { if (!followCreature || !hasFollowPath) {
searchTarget(); searchTarget(TARGETSEARCH_ANY);
} else if (isFleeing()) { } else if (isFleeing()) {
if (attackedCreature && !canUseAttack(getPosition(), attackedCreature)) { 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)) { if (!attackedCreature || (isSummon() && attackedCreature == this)) {
return; return;
} }
bool updateLook = true;
bool resetTicks = interval != 0;
attackTicks += interval;
const Position& myPos = getPosition(); const Position& myPos = getPosition();
const Position& targetPos = attackedCreature->getPosition(); const Position& targetPos = attackedCreature->getPosition();
for (const spellBlock_t& spellBlock : mType->info.attackSpells) { bool updateLook = false;
bool inRange = false;
if (attackedCreature == nullptr) { if (OTSYS_TIME() >= earliestAttackTime && !isFleeing()) {
break; 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)) { for (spellBlock_t& spellBlock : mType->info.attackSpells) {
if (spellBlock.chance >= static_cast<uint32_t>(uniform_random(1, 100))) { if (spellBlock.range != 0 && std::max<uint32_t>(Position::getDistanceX(myPos, targetPos), Position::getDistanceY(myPos, targetPos)) <= spellBlock.range) {
if (updateLook) { if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) {
updateLookDirection(); updateLookDirection();
updateLook = false;
}
minCombatValue = spellBlock.minCombatValue; minCombatValue = spellBlock.minCombatValue;
maxCombatValue = spellBlock.maxCombatValue; maxCombatValue = spellBlock.maxCombatValue;
spellBlock.spell->castSpell(this, attackedCreature); spellBlock.spell->castSpell(this, attackedCreature);
egibleToDance = true;
if (spellBlock.isMelee) {
extraMeleeAttack = false;
}
} }
} }
if (!inRange && spellBlock.isMelee) {
//melee swing out of reach
extraMeleeAttack = true;
}
} }
if (updateLook) { if (updateLook) {
updateLookDirection(); updateLookDirection();
} }
if (resetTicks) {
attackTicks = 0;
}
} }
bool Monster::canUseAttack(const Position& pos, const Creature* target) const 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; 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) void Monster::onThinkTarget(uint32_t interval)
{ {
if (!isSummon()) { if (!isSummon()) {
@@ -882,13 +931,42 @@ void Monster::onThinkTarget(uint32_t interval)
if (targetChangeTicks >= mType->info.changeTargetSpeed) { if (targetChangeTicks >= mType->info.changeTargetSpeed) {
targetChangeTicks = 0; targetChangeTicks = 0;
targetChangeCooldown = mType->info.changeTargetSpeed;
if (mType->info.changeTargetChance >= uniform_random(1, 100)) { if (mType->info.changeTargetChance > uniform_random(0, 99)) {
if (mType->info.targetDistance <= 1) { // search target strategies, if no strategy succeeds, target is not switched
searchTarget(TARGETSEARCH_RANDOM); int32_t random = uniform_random(0, 99);
} else { int32_t current_strategy = 0;
searchTarget(TARGETSEARCH_NEAREST);
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) { for (const spellBlock_t& spellBlock : mType->info.defenseSpells) {
if (spellBlock.speed > defenseTicks) { if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) {
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)))) {
minCombatValue = spellBlock.minCombatValue; minCombatValue = spellBlock.minCombatValue;
maxCombatValue = spellBlock.maxCombatValue; maxCombatValue = spellBlock.maxCombatValue;
spellBlock.spell->castSpell(this, this); spellBlock.spell->castSpell(this, this);
@@ -922,20 +987,10 @@ void Monster::onThinkDefense(uint32_t interval)
if (!isSummon() && summons.size() < mType->info.maxSummons && hasFollowPath) { if (!isSummon() && summons.size() < mType->info.maxSummons && hasFollowPath) {
for (const summonBlock_t& summonBlock : mType->info.summons) { for (const summonBlock_t& summonBlock : mType->info.summons) {
if (summonBlock.speed > defenseTicks) {
resetTicks = false;
continue;
}
if (summons.size() >= mType->info.maxSummons) { if (summons.size() >= mType->info.maxSummons) {
continue; continue;
} }
if (defenseTicks % summonBlock.speed >= interval) {
//already used this spell for this round
continue;
}
uint32_t summonCount = 0; uint32_t summonCount = 0;
for (Creature* summon : summons) { for (Creature* summon : summons) {
if (summon->getName() == summonBlock.name) { if (summon->getName() == summonBlock.name) {
@@ -947,49 +1002,41 @@ void Monster::onThinkDefense(uint32_t interval)
continue; continue;
} }
if (summonBlock.chance < static_cast<uint32_t>(uniform_random(1, 100))) { if (normal_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) {
continue; Monster* summon = Monster::createMonster(summonBlock.name);
} if (summon) {
const Position& summonPos = getPosition();
Monster* summon = Monster::createMonster(summonBlock.name); addSummon(summon);
if (summon) {
if (g_game.placeCreature(summon, getPosition(), false, summonBlock.force)) { if (!g_game.placeCreature(summon, summonPos, false, summonBlock.force)) {
summon->setDropLoot(false); removeSummon(summon);
summon->setSkillLoss(false); } else {
summon->setMaster(this); g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE);
g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT);
g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); }
} else {
delete summon;
} }
} }
} }
} }
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; return;
} }
yellTicks += interval; int32_t randomResult = rand();
if (yellTicks >= mType->info.yellSpeedTicks) { if (rand() == 50 * (randomResult / 50)) {
yellTicks = 0; 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)))) { if (voice.yellText) {
uint32_t index = uniform_random(0, mType->info.voiceVector.size() - 1); g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, voice.text, false);
const voiceBlock_t& vb = mType->info.voiceVector[index]; }
else {
if (vb.yellText) { g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, voice.text, false);
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, vb.text, false);
} else {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, vb.text, false);
}
} }
} }
} }
@@ -1112,27 +1159,117 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags)
bool result = false; bool result = false;
if ((!followCreature || !hasFollowPath) && (!isSummon() || !isMasterInRange)) { if ((!followCreature || !hasFollowPath) && (!isSummon() || !isMasterInRange)) {
if (getTimeSinceLastMove() >= 1000) { if (OTSYS_TIME() >= nextDanceStepRound) {
randomStepping = true; updateLookDirection();
nextDanceStepRound = OTSYS_TIME() + getStepDuration();
//choose a random direction //choose a random direction
result = getRandomStep(getPosition(), direction); result = getRandomStep(getPosition(), direction);
} }
} else if ((isSummon() && isMasterInRange) || followCreature) { } else if ((isSummon() && isMasterInRange) || followCreature) {
randomStepping = false;
result = Creature::getNextStep(direction, flags); result = Creature::getNextStep(direction, flags);
if (result) { if (result) {
flags |= FLAG_PATHFINDING; flags |= FLAG_PATHFINDING;
} else { } else {
if (ignoreFieldDamage) {
ignoreFieldDamage = false;
updateMapCache();
}
//target dancing //target dancing
if (attackedCreature && attackedCreature == followCreature) { if (attackedCreature && attackedCreature == followCreature) {
if (isFleeing()) { if (isFleeing()) {
result = getDanceStep(getPosition(), direction, false, false); result = getDanceStep(getPosition(), direction, false, false);
} else if (mType->info.staticAttackChance < static_cast<uint32_t>(uniform_random(1, 100))) { } else if (egibleToDance && OTSYS_TIME() >= earliestDanceTime) {
result = getDanceStep(getPosition(), direction); 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;
}
}
}
} }
} }
} }
@@ -1776,7 +1913,8 @@ void Monster::death(Creature*)
for (Creature* summon : summons) { for (Creature* summon : summons) {
summon->changeHealth(-summon->getHealth()); summon->changeHealth(-summon->getHealth());
summon->removeMaster(); summon->setMaster(nullptr);
summon->decrementReferenceCounter();
} }
summons.clear(); summons.clear();
@@ -1855,61 +1993,86 @@ void Monster::updateLookDirection()
//look EAST/WEST //look EAST/WEST
if (offsetx < 0) { if (offsetx < 0) {
newDir = DIRECTION_WEST; newDir = DIRECTION_WEST;
} else { }
else {
newDir = DIRECTION_EAST; newDir = DIRECTION_EAST;
} }
} else if (dx < dy) { }
else if (dx < dy) {
//look NORTH/SOUTH //look NORTH/SOUTH
if (offsety < 0) { if (offsety < 0) {
newDir = DIRECTION_NORTH; newDir = DIRECTION_NORTH;
} else { }
else {
newDir = DIRECTION_SOUTH; newDir = DIRECTION_SOUTH;
} }
} else { }
else {
Direction dir = getDirection(); Direction dir = getDirection();
if (offsetx < 0 && offsety < 0) { if (offsetx < 0 && offsety < 0) {
if (offsetx == -1 && offsety == -1) {
if (dir == DIRECTION_NORTH) {
newDir = DIRECTION_WEST;
}
}
if (dir == DIRECTION_SOUTH) { if (dir == DIRECTION_SOUTH) {
newDir = DIRECTION_WEST; newDir = DIRECTION_WEST;
} else if (dir == DIRECTION_NORTH) { }
newDir = DIRECTION_WEST; else if (dir == DIRECTION_EAST) {
} else if (dir == DIRECTION_EAST) {
newDir = DIRECTION_NORTH; 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) { if (dir == DIRECTION_NORTH) {
newDir = DIRECTION_WEST; newDir = DIRECTION_WEST;
} else if (dir == DIRECTION_SOUTH) { }
newDir = DIRECTION_WEST; else if (dir == DIRECTION_EAST) {
} else if (dir == DIRECTION_EAST) {
newDir = DIRECTION_SOUTH; 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) { if (dir == DIRECTION_SOUTH) {
newDir = DIRECTION_EAST; newDir = DIRECTION_EAST;
} else if (dir == DIRECTION_NORTH) { }
newDir = DIRECTION_EAST; else if (dir == DIRECTION_WEST) {
} else if (dir == DIRECTION_WEST) {
newDir = DIRECTION_NORTH; newDir = DIRECTION_NORTH;
} }
} else { }
else {
if (offsetx == 1 && offsety == 1) {
if (dir == DIRECTION_SOUTH) {
newDir = DIRECTION_EAST;
}
}
if (dir == DIRECTION_NORTH) { if (dir == DIRECTION_NORTH) {
newDir = DIRECTION_EAST; newDir = DIRECTION_EAST;
} else if (dir == DIRECTION_SOUTH) { }
newDir = DIRECTION_EAST; else if (dir == DIRECTION_WEST) {
} else if (dir == DIRECTION_WEST) {
newDir = DIRECTION_SOUTH; newDir = DIRECTION_SOUTH;
} }
} }
} }
} }
g_game.internalCreatureTurn(this, newDir); if (direction != newDir) {
g_game.internalCreatureTurn(this, newDir);
}
} }
void Monster::dropLoot(Container* corpse, Creature*) void Monster::dropLoot(Container* corpse, Creature*)
{ {
if (corpse && lootDrop) { 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) void Monster::drainHealth(Creature* attacker, int32_t damage)
{ {
Creature::drainHealth(attacker, damage); Creature::drainHealth(attacker, damage);
if (damage > 0 && randomStepping) {
ignoreFieldDamage = true;
updateMapCache();
}
if (isInvisible()) { if (isInvisible()) {
removeCondition(CONDITION_INVISIBLE); removeCondition(CONDITION_INVISIBLE);
} }
@@ -1947,12 +2104,70 @@ bool Monster::challengeCreature(Creature* creature)
bool result = selectTarget(creature); bool result = selectTarget(creature);
if (result) { if (result) {
targetChangeCooldown = 8000; targetChangeCooldown = 1000;
targetChangeTicks = 0; targetChangeTicks = 0;
} }
return result; 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 void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
{ {
Creature::getPathSearchParams(creature, fpp); Creature::getPathSearchParams(creature, fpp);

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -26,15 +26,17 @@
class Creature; class Creature;
class Game; class Game;
class Spawn; class Spawn;
class Combat;
using CreatureHashSet = std::unordered_set<Creature*>; typedef std::unordered_set<Creature*> CreatureHashSet;
using CreatureList = std::list<Creature*>; typedef std::list<Creature*> CreatureList;
enum TargetSearchType_t { enum TargetSearchType_t {
TARGETSEARCH_DEFAULT, TARGETSEARCH_ANY,
TARGETSEARCH_RANDOM, TARGETSEARCH_RANDOM,
TARGETSEARCH_ATTACKRANGE,
TARGETSEARCH_NEAREST, TARGETSEARCH_NEAREST,
TARGETSEARCH_WEAKEST,
TARGETSEARCH_MOSTDAMAGE,
}; };
class Monster final : public Creature class Monster final : public Creature
@@ -44,43 +46,39 @@ class Monster final : public Creature
static int32_t despawnRange; static int32_t despawnRange;
static int32_t despawnRadius; static int32_t despawnRadius;
explicit Monster(MonsterType* mType); explicit Monster(MonsterType* mtype);
~Monster(); ~Monster();
// non-copyable // non-copyable
Monster(const Monster&) = delete; Monster(const Monster&) = delete;
Monster& operator=(const Monster&) = delete; Monster& operator=(const Monster&) = delete;
Monster* getMonster() override { Monster* getMonster() final {
return this; return this;
} }
const Monster* getMonster() const override { const Monster* getMonster() const final {
return this; return this;
} }
void setID() override { void setID() final {
if (id == 0) { if (id == 0) {
id = monsterAutoID++; id = monsterAutoID++;
} }
} }
void removeList() override; void removeList() final;
void addList() override; void addList() final;
const std::string& getName() const override { const std::string& getName() const final {
return mType->name; return mType->name;
} }
const std::string& getNameDescription() const override { const std::string& getNameDescription() const final {
return mType->nameDescription; return mType->nameDescription;
} }
std::string getDescription(int32_t) const override { std::string getDescription(int32_t) const final {
return strDescription + '.'; return strDescription + '.';
} }
CreatureType_t getType() const override {
return CREATURETYPE_MONSTER;
}
const Position& getMasterPos() const { const Position& getMasterPos() const {
return masterPos; return masterPos;
} }
@@ -88,19 +86,22 @@ class Monster final : public Creature
masterPos = pos; masterPos = pos;
} }
RaceType_t getRace() const override { RaceType_t getRace() const final {
return mType->info.race; return mType->info.race;
} }
int32_t getArmor() const override { int32_t getArmor() const final {
return mType->info.armor; 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 { int32_t getDefense() final;
return mType->info.defense; bool isPushable() const final {
}
bool isPushable() const override {
return mType->info.pushable && baseSpeed != 0; return mType->info.pushable && baseSpeed != 0;
} }
bool isAttackable() const override { bool isAttackable() const final {
return mType->info.isAttackable; return mType->info.isAttackable;
} }
@@ -113,8 +114,8 @@ class Monster final : public Creature
bool isHostile() const { bool isHostile() const {
return mType->info.isHostile; return mType->info.isHostile;
} }
bool canSee(const Position& pos) const override; bool canSee(const Position& pos) const final;
bool canSeeInvisibility() const override { bool canSeeInvisibility() const final {
return isImmune(CONDITION_INVISIBLE); return isImmune(CONDITION_INVISIBLE);
} }
uint32_t getManaCost() const { uint32_t getManaCost() const {
@@ -123,35 +124,31 @@ class Monster final : public Creature
void setSpawn(Spawn* spawn) { void setSpawn(Spawn* spawn) {
this->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 drainHealth(Creature* attacker, int32_t damage) final;
void onRemoveCreature(Creature* creature, bool isLogout) override; void changeHealth(int32_t healthChange, bool sendHealthChange = true) final;
void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) override; void onWalk() final;
void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override; bool getNextStep(Direction& direction, uint32_t& flags) final;
void onFollowCreatureComplete(const Creature* creature) final;
void drainHealth(Creature* attacker, int32_t damage) override; void onThink(uint32_t interval) final;
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) 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; void doAttacking(uint32_t interval) final;
bool getCombatValues(int32_t& min, int32_t& max) override;
void doAttacking(uint32_t interval) override; bool searchTarget(TargetSearchType_t searchType);
bool hasExtraSwing() override {
return extraMeleeAttack;
}
bool searchTarget(TargetSearchType_t searchType = TARGETSEARCH_DEFAULT);
bool selectTarget(Creature* creature); bool selectTarget(Creature* creature);
const CreatureList& getTargetList() const { const CreatureList& getTargetList() const {
@@ -170,12 +167,9 @@ class Monster final : public Creature
bool isTargetNearby() const { bool isTargetNearby() const {
return stepDuration >= 1; return stepDuration >= 1;
} }
bool isIgnoringFieldDamage() const {
return ignoreFieldDamage;
}
BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage, 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; static uint32_t monsterAutoID;
@@ -188,13 +182,13 @@ class Monster final : public Creature
MonsterType* mType; MonsterType* mType;
Spawn* spawn = nullptr; 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 targetChangeTicks = 0;
uint32_t defenseTicks = 0;
uint32_t yellTicks = 0;
int32_t minCombatValue = 0; int32_t minCombatValue = 0;
int32_t maxCombatValue = 0; int32_t maxCombatValue = 0;
int32_t targetChangeCooldown = 0; int32_t targetChangeCooldown = 0;
@@ -203,10 +197,8 @@ class Monster final : public Creature
Position masterPos; Position masterPos;
bool isIdle = true; bool isIdle = true;
bool extraMeleeAttack = false;
bool isMasterInRange = false; bool isMasterInRange = false;
bool randomStepping = false; bool egibleToDance = true;
bool ignoreFieldDamage = false;
void onCreatureEnter(Creature* creature); void onCreatureEnter(Creature* creature);
void onCreatureLeave(Creature* creature); void onCreatureLeave(Creature* creature);
@@ -223,8 +215,8 @@ class Monster final : public Creature
void clearTargetList(); void clearTargetList();
void clearFriendList(); void clearFriendList();
void death(Creature* lastHitCreature) override; void death(Creature* lastHitCreature) final;
Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) override; Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) final;
void setIdle(bool idle); void setIdle(bool idle);
void updateIdleStatus(); void updateIdleStatus();
@@ -232,12 +224,11 @@ class Monster final : public Creature
return isIdle; return isIdle;
} }
void onAddCondition(ConditionType_t type) override; void onAddCondition(ConditionType_t type) final;
void onEndCondition(ConditionType_t type) override; 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 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 getRandomStep(const Position& creaturePos, Direction& direction) const;
bool getDanceStep(const Position& creaturePos, Direction& direction, bool getDanceStep(const Position& creaturePos, Direction& direction,
bool keepAttack = true, bool keepDistance = true); bool keepAttack = true, bool keepDistance = true);
@@ -256,25 +247,28 @@ class Monster final : public Creature
bool isFriend(const Creature* creature) const; bool isFriend(const Creature* creature) const;
bool isOpponent(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; return skillLoss ? mType->info.experience : 0;
} }
uint16_t getLookCorpse() const override { uint16_t getLookCorpse() const final {
return mType->info.lookcorpse; return mType->info.lookcorpse;
} }
void dropLoot(Container* corpse, Creature* lastHitCreature) override; void dropLoot(Container* corpse, Creature* lastHitCreature) final;
uint32_t getDamageImmunities() const override { uint32_t getDamageImmunities() const final {
return mType->info.damageImmunities; return mType->info.damageImmunities;
} }
uint32_t getConditionImmunities() const override { uint32_t getConditionImmunities() const final {
return mType->info.conditionImmunities; return mType->info.conditionImmunities;
} }
void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const override; void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const final;
bool useCacheMap() const override { bool useCacheMap() const final {
return !randomStepping; return true;
} }
friend class LuaScriptInterface; friend class LuaScriptInterface;
friend class AreaSpawnEvent;
friend class Combat;
friend class Creature;
}; };
#endif #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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -22,9 +22,8 @@
#include "creature.h" #include "creature.h"
#define MAX_LOOTCHANCE 1000
const uint32_t MAX_LOOTCHANCE = 100000; #define MAX_STATICWALK 100
const uint32_t MAX_STATICWALK = 100;
struct LootBlock { struct LootBlock {
uint16_t id; uint16_t id;
@@ -39,7 +38,7 @@ struct LootBlock {
std::vector<LootBlock> childLoot; std::vector<LootBlock> childLoot;
LootBlock() { LootBlock() {
id = 0; id = 0;
countmax = 1; countmax = 0;
chance = 0; chance = 0;
subType = -1; 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 { struct summonBlock_t {
std::string name; std::string name;
uint32_t chance; uint32_t chance;
uint32_t speed;
uint32_t max; uint32_t max;
bool force = false; bool force = false;
}; };
@@ -75,23 +62,23 @@ struct spellBlock_t {
spellBlock_t(spellBlock_t&& other) : spellBlock_t(spellBlock_t&& other) :
spell(other.spell), spell(other.spell),
chance(other.chance), chance(other.chance),
speed(other.speed),
range(other.range), range(other.range),
minCombatValue(other.minCombatValue), minCombatValue(other.minCombatValue),
maxCombatValue(other.maxCombatValue), maxCombatValue(other.maxCombatValue),
combatSpell(other.combatSpell), attack(other.attack),
isMelee(other.isMelee) { skill(other.skill),
combatSpell(other.combatSpell) {
other.spell = nullptr; other.spell = nullptr;
} }
BaseSpell* spell = nullptr; BaseSpell* spell = nullptr;
uint32_t chance = 100; uint32_t chance = 100;
uint32_t speed = 2000;
uint32_t range = 0; uint32_t range = 0;
int32_t minCombatValue = 0; int32_t minCombatValue = 0;
int32_t maxCombatValue = 0; int32_t maxCombatValue = 0;
int32_t attack = 0;
int32_t skill = 0;
bool combatSpell = false; bool combatSpell = false;
bool isMelee = false;
}; };
struct voiceBlock_t { struct voiceBlock_t {
@@ -124,14 +111,11 @@ class MonsterType
uint64_t experience = 0; uint64_t experience = 0;
uint32_t manaCost = 0; uint32_t manaCost = 0;
uint32_t yellChance = 0;
uint32_t yellSpeedTicks = 0;
uint32_t staticAttackChance = 95;
uint32_t maxSummons = 0; uint32_t maxSummons = 0;
uint32_t changeTargetSpeed = 0; uint32_t changeTargetSpeed = 0;
uint32_t conditionImmunities = 0; uint32_t conditionImmunities = 0;
uint32_t damageImmunities = 0; uint32_t damageImmunities = 0;
uint32_t baseSpeed = 200; uint32_t baseSpeed = 70;
int32_t creatureAppearEvent = -1; int32_t creatureAppearEvent = -1;
int32_t creatureDisappearEvent = -1; int32_t creatureDisappearEvent = -1;
@@ -143,8 +127,15 @@ class MonsterType
int32_t health = 100; int32_t health = 100;
int32_t healthMax = 100; int32_t healthMax = 100;
int32_t changeTargetChance = 0; 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 armor = 0;
int32_t defense = 0;
int32_t attack = 0;
int32_t skill = 0;
int32_t poison = 0;
bool canPushItems = false; bool canPushItems = false;
bool canPushCreatures = false; bool canPushCreatures = false;
@@ -155,11 +146,6 @@ class MonsterType
bool isAttackable = true; bool isAttackable = true;
bool isHostile = true; bool isHostile = true;
bool hiddenHealth = false; bool hiddenHealth = false;
bool canWalkOnEnergy = true;
bool canWalkOnFire = true;
bool canWalkOnPoison = true;
MonstersEvent_t eventType = MONSTERS_EVENT_NONE;
}; };
public: public:
@@ -169,57 +155,14 @@ class MonsterType
MonsterType(const MonsterType&) = delete; MonsterType(const MonsterType&) = delete;
MonsterType& operator=(const MonsterType&) = delete; MonsterType& operator=(const MonsterType&) = delete;
bool loadCallback(LuaScriptInterface* scriptInterface);
std::string name; std::string name;
std::string nameDescription; std::string nameDescription;
MonsterInfo info; MonsterInfo info;
void loadLoot(MonsterType* monsterType, LootBlock lootblock); void createLoot(Container* corpse);
}; bool createLootContainer(Container* parent, const LootBlock& lootblock);
std::vector<Item*> createLootItem(const 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;
}; };
class Monsters class Monsters
@@ -237,23 +180,20 @@ class Monsters
bool reload(); bool reload();
MonsterType* getMonsterType(const std::string& name); 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: private:
ConditionDamage* getDamageCondition(ConditionType_t conditionType, ConditionDamage* getDamageCondition(ConditionType_t conditionType, int32_t cycles, int32_t count, int32_t max_count);
int32_t maxDamage, int32_t minDamage, int32_t startDamage, uint32_t tickInterval);
bool deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, const std::string& description = ""); 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&); void loadLootContainer(const pugi::xml_node& node, LootBlock&);
bool loadLootItem(const pugi::xml_node& node, LootBlock&); bool loadLootItem(const pugi::xml_node& node, LootBlock&);
std::map<std::string, MonsterType> monsters; std::map<std::string, MonsterType> monsters;
std::map<std::string, std::string> unloadedMonsters; std::unique_ptr<LuaScriptInterface> scriptInterface;
bool loaded = false; 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -36,49 +36,43 @@ MoveEvents::MoveEvents() :
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) { std::unordered_set<MoveEvent*> set;
for (int eventType = MOVE_EVENT_STEP_IN; eventType < MOVE_EVENT_LAST; ++eventType) { for (const auto& it : map) {
auto& moveEvents = it->second.moveEvent[eventType]; const MoveEventList& moveEventList = it.second;
for (auto find = moveEvents.begin(); find != moveEvents.end(); ) { for (const auto& i : moveEventList.moveEvent) {
if (fromLua == find->fromLua) { for (MoveEvent* moveEvent : i) {
find = moveEvents.erase(find); set.insert(moveEvent);
} else {
++find;
}
} }
} }
} }
} map.clear();
void MoveEvents::clearPosMap(MovePosListMap& map, bool fromLua) for (MoveEvent* moveEvent : set) {
{ delete moveEvent;
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;
}
}
}
} }
} }
void MoveEvents::clear(bool fromLua) void MoveEvents::clear()
{ {
clearMap(itemIdMap, fromLua); clearMap(itemIdMap);
clearMap(actionIdMap, fromLua); clearMap(movementIdMap);
clearMap(uniqueIdMap, fromLua);
clearPosMap(positionMap, fromLua);
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() LuaScriptInterface& MoveEvents::getScriptInterface()
@@ -91,17 +85,17 @@ std::string MoveEvents::getScriptBaseName() const
return "movements"; return "movements";
} }
Event_ptr MoveEvents::getEvent(const std::string& nodeName) Event* MoveEvents::getEvent(const std::string& nodeName)
{ {
if (strcasecmp(nodeName.c_str(), "movevent") != 0) { if (strcasecmp(nodeName.c_str(), "movevent") != 0) {
return nullptr; 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(); const MoveEvent_t eventType = moveEvent->getEventType();
if (eventType == MOVE_EVENT_ADD_ITEM || eventType == MOVE_EVENT_REMOVE_ITEM) { 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; pugi::xml_attribute attr;
if ((attr = node.attribute("itemid"))) { if ((attr = node.attribute("itemid"))) {
int32_t id = pugi::cast<int32_t>(attr.value()); int32_t id = pugi::cast<int32_t>(attr.value());
addEvent(moveEvent, id, itemIdMap);
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) { if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
ItemType& it = Item::items.getItemType(id); ItemType& it = Item::items.getItemType(id);
it.wieldInfo = moveEvent->getWieldInfo(); it.wieldInfo = moveEvent->getWieldInfo();
@@ -130,12 +125,11 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
it.minReqMagicLevel = moveEvent->getReqMagLv(); it.minReqMagicLevel = moveEvent->getReqMagLv();
it.vocationString = moveEvent->getVocationString(); it.vocationString = moveEvent->getVocationString();
} }
addEvent(std::move(*moveEvent), id, itemIdMap);
} else if ((attr = node.attribute("fromid"))) { } else if ((attr = node.attribute("fromid"))) {
uint32_t id = pugi::cast<uint32_t>(attr.value()); uint32_t id = pugi::cast<uint32_t>(attr.value());
uint32_t endId = pugi::cast<uint32_t>(node.attribute("toid").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) { if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
ItemType& it = Item::items.getItemType(id); 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(); it.vocationString = moveEvent->getVocationString();
while (++id <= endId) { while (++id <= endId) {
addEvent(*moveEvent, id, itemIdMap); addEvent(moveEvent, id, itemIdMap);
ItemType& tit = Item::items.getItemType(id); ItemType& tit = Item::items.getItemType(id);
tit.wieldInfo = moveEvent->getWieldInfo(); tit.wieldInfo = moveEvent->getWieldInfo();
@@ -155,26 +149,17 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
} }
} else { } else {
while (++id <= endId) { while (++id <= endId) {
addEvent(*moveEvent, id, itemIdMap); addEvent(moveEvent, id, itemIdMap);
} }
} }
} else if ((attr = node.attribute("uniqueid"))) { } else if ((attr = node.attribute("movementid"))) {
addEvent(std::move(*moveEvent), pugi::cast<int32_t>(attr.value()), uniqueIdMap); addEvent(moveEvent, pugi::cast<int32_t>(attr.value()), movementIdMap);
} else if ((attr = node.attribute("fromuid"))) { } else if ((attr = node.attribute("frommovementid"))) {
uint32_t id = pugi::cast<uint32_t>(attr.value()); uint32_t id = pugi::cast<uint32_t>(attr.value());
uint32_t endId = pugi::cast<uint32_t>(node.attribute("touid").value()); uint32_t endId = pugi::cast<uint32_t>(node.attribute("tomovementid").value());
addEvent(*moveEvent, id, uniqueIdMap); addEvent(moveEvent, id, movementIdMap);
while (++id <= endId) { while (++id <= endId) {
addEvent(*moveEvent, id, uniqueIdMap); addEvent(moveEvent, id, movementIdMap);
}
} 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);
} }
} else if ((attr = node.attribute("pos"))) { } else if ((attr = node.attribute("pos"))) {
std::vector<int32_t> posList = vectorAtoi(explodeString(attr.as_string(), ";")); 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]); Position pos(posList[0], posList[1], posList[2]);
addEvent(std::move(*moveEvent), pos, positionMap); addEvent(moveEvent, pos, positionMap);
} else { } else {
return false; return false;
} }
return true; return true;
} }
bool MoveEvents::registerLuaFunction(MoveEvent* event) void MoveEvents::addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map)
{
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)
{ {
auto it = map.find(id); auto it = map.find(id);
if (it == map.end()) { if (it == map.end()) {
MoveEventList moveEventList; MoveEventList moveEventList;
moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent)); moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent);
map[id] = moveEventList; map[id] = moveEventList;
} else { } else {
std::list<MoveEvent>& moveEventList = it->second.moveEvent[moveEvent.getEventType()]; std::list<MoveEvent*>& moveEventList = it->second.moveEvent[moveEvent->getEventType()];
for (MoveEvent& existingMoveEvent : moveEventList) { for (MoveEvent* existingMoveEvent : moveEventList) {
if (existingMoveEvent.getSlot() == moveEvent.getSlot()) { if (existingMoveEvent->getSlot() == moveEvent->getSlot()) {
std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << id << std::endl; std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << id << std::endl;
} }
} }
moveEventList.push_back(std::move(moveEvent)); moveEventList.push_back(moveEvent);
} }
} }
@@ -324,10 +212,10 @@ MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType, slots_t slot)
auto it = itemIdMap.find(item->getID()); auto it = itemIdMap.find(item->getID());
if (it != itemIdMap.end()) { if (it != itemIdMap.end()) {
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType]; std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
for (MoveEvent& moveEvent : moveEventList) { for (MoveEvent* moveEvent : moveEventList) {
if ((moveEvent.getSlot() & slotp) != 0) { if ((moveEvent->getSlot() & slotp) != 0) {
return &moveEvent; return moveEvent;
} }
} }
} }
@@ -338,50 +226,45 @@ MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType)
{ {
MoveListMap::iterator it; MoveListMap::iterator it;
if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) { if (item->hasAttribute(ITEM_ATTRIBUTE_MOVEMENTID)) {
it = uniqueIdMap.find(item->getUniqueId()); it = movementIdMap.find(item->getMovementId());
if (it != uniqueIdMap.end()) { if (it != movementIdMap.end()) {
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType]; std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
if (!moveEventList.empty()) { if (!moveEventList.empty()) {
return &(*moveEventList.begin()); return *moveEventList.begin();
} }
} }
} }
if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) { if (!item->hasCollisionEvent() && !item->hasSeparationEvent()) {
it = actionIdMap.find(item->getActionId()); return nullptr;
if (it != actionIdMap.end()) {
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
if (!moveEventList.empty()) {
return &(*moveEventList.begin());
}
}
} }
it = itemIdMap.find(item->getID()); it = itemIdMap.find(item->getID());
if (it != itemIdMap.end()) { if (it != itemIdMap.end()) {
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType]; std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
if (!moveEventList.empty()) { if (!moveEventList.empty()) {
return &(*moveEventList.begin()); return *moveEventList.begin();
} }
} }
return nullptr; 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); auto it = map.find(pos);
if (it == map.end()) { if (it == map.end()) {
MoveEventList moveEventList; MoveEventList moveEventList;
moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent)); moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent);
map[pos] = moveEventList; map[pos] = moveEventList;
} else { } else {
std::list<MoveEvent>& moveEventList = it->second.moveEvent[moveEvent.getEventType()]; std::list<MoveEvent*>& moveEventList = it->second.moveEvent[moveEvent->getEventType()];
if (!moveEventList.empty()) { if (!moveEventList.empty()) {
std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << pos << std::endl; 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()); auto it = positionMap.find(tile->getPosition());
if (it != positionMap.end()) { if (it != positionMap.end()) {
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType]; std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
if (!moveEventList.empty()) { if (!moveEventList.empty()) {
return &(*moveEventList.begin()); return *moveEventList.begin();
} }
} }
return nullptr; return nullptr;
@@ -442,7 +325,7 @@ uint32_t MoveEvents::onPlayerDeEquip(Player* player, Item* item, slots_t slot)
if (!moveEvent) { if (!moveEvent) {
return 1; 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) 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(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 std::string MoveEvent::getScriptEventName() const
{ {
switch (eventType) { switch (eventType) {
@@ -624,6 +521,40 @@ bool MoveEvent::configureEvent(const pugi::xml_node& node)
return true; 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&) uint32_t MoveEvent::StepInField(Creature* creature, Item* item, const Position&)
{ {
MagicField* field = item->getMagicField(); MagicField* field = item->getMagicField();
@@ -715,6 +646,10 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item,
player->sendIcons(); player->sendIcons();
} }
if (it.abilities->absorbPercent[combatTypeToIndex(COMBAT_DROWNDAMAGE)] == 100) {
player->removeCondition(CONDITION_DROWN);
}
if (it.abilities->regeneration) { if (it.abilities->regeneration) {
Condition* condition = Condition::createCondition(static_cast<ConditionId_t>(slot), CONDITION_REGENERATION, -1, 0); 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) { if (needUpdateSkills) {
player->sendSkills(); 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) { if (needUpdateSkills) {
player->sendSkills(); player->sendSkills();
} }
@@ -862,44 +783,6 @@ uint32_t MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots_t
return 1; 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) uint32_t MoveEvent::fireStepEvent(Creature* creature, Item* item, const Position& pos)
{ {
if (scripted) { if (scripted) {
@@ -933,24 +816,19 @@ bool MoveEvent::executeStep(Creature* creature, Item* item, const Position& pos)
return scriptInterface->callFunction(4); 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 (scripted) {
if (!equipFunction || equipFunction(this, player, item, slot, isCheck) == 1) { return executeEquip(player, item, slot);
if (executeEquip(player, item, slot, isCheck)) {
return 1;
}
}
return 0;
} else { } 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) //onEquip(player, item, slot)
//onDeEquip(player, item, slot, isCheck) //onDeEquip(player, item, slot)
if (!scriptInterface->reserveScriptEnv()) { if (!scriptInterface->reserveScriptEnv()) {
std::cout << "[Error - MoveEvent::executeEquip] Call stack overflow" << std::endl; std::cout << "[Error - MoveEvent::executeEquip] Call stack overflow" << std::endl;
return false; return false;
@@ -966,9 +844,8 @@ bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool isCh
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushThing(L, item); LuaScriptInterface::pushThing(L, item);
lua_pushnumber(L, slot); 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) 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -23,9 +23,6 @@
#include "baseevents.h" #include "baseevents.h"
#include "item.h" #include "item.h"
#include "luascript.h" #include "luascript.h"
#include "vocation.h"
extern Vocations g_vocations;
enum MoveEvent_t { enum MoveEvent_t {
MOVE_EVENT_STEP_IN, MOVE_EVENT_STEP_IN,
@@ -42,13 +39,12 @@ enum MoveEvent_t {
}; };
class MoveEvent; class MoveEvent;
using MoveEvent_ptr = std::unique_ptr<MoveEvent>;
struct MoveEventList { 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 class MoveEvents final : public BaseEvents
{ {
@@ -67,54 +63,51 @@ class MoveEvents final : public BaseEvents
MoveEvent* getEvent(Item* item, MoveEvent_t eventType); MoveEvent* getEvent(Item* item, MoveEvent_t eventType);
bool registerLuaEvent(MoveEvent* event); protected:
bool registerLuaFunction(MoveEvent* event); typedef std::map<int32_t, MoveEventList> MoveListMap;
void clear(bool fromLua) override final; void clearMap(MoveListMap& map);
private: typedef std::map<Position, MoveEventList> MovePosListMap;
using MoveListMap = std::map<int32_t, MoveEventList>; void clear() final;
using MovePosListMap = std::map<Position, MoveEventList>; LuaScriptInterface& getScriptInterface() final;
void clearMap(MoveListMap& map, bool fromLua); std::string getScriptBaseName() const final;
void clearPosMap(MovePosListMap& map, bool fromLua); Event* getEvent(const std::string& nodeName) final;
bool registerEvent(Event* event, const pugi::xml_node& node) final;
LuaScriptInterface& getScriptInterface() override; void addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map);
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, const Position& pos, MovePosListMap& map);
void addEvent(MoveEvent moveEvent, const Position& pos, MovePosListMap& map);
MoveEvent* getEvent(const Tile* tile, MoveEvent_t eventType); MoveEvent* getEvent(const Tile* tile, MoveEvent_t eventType);
MoveEvent* getEvent(Item* item, MoveEvent_t eventType, slots_t slot); MoveEvent* getEvent(Item* item, MoveEvent_t eventType, slots_t slot);
MoveListMap uniqueIdMap;
MoveListMap actionIdMap; MoveListMap movementIdMap;
MoveListMap itemIdMap; MoveListMap itemIdMap;
MovePosListMap positionMap; MovePosListMap positionMap;
LuaScriptInterface scriptInterface; LuaScriptInterface scriptInterface;
}; };
using StepFunction = std::function<uint32_t(Creature* creature, Item* item, const Position& pos)>; typedef uint32_t (StepFunction)(Creature* creature, Item* item, const Position& pos);
using MoveFunction = std::function<uint32_t(Item* item, Item* tileItem, const Position& pos)>; typedef uint32_t (MoveFunction)(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 (EquipFunction)(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
class MoveEvent final : public Event class MoveEvent final : public Event
{ {
public: public:
explicit MoveEvent(LuaScriptInterface* interface); explicit MoveEvent(LuaScriptInterface* interface);
explicit MoveEvent(const MoveEvent* copy);
MoveEvent_t getEventType() const; MoveEvent_t getEventType() const;
void setEventType(MoveEvent_t type); void setEventType(MoveEvent_t type);
bool configureEvent(const pugi::xml_node& node) override; bool configureEvent(const pugi::xml_node& node) final;
bool loadFunction(const pugi::xml_attribute& attr, bool isScripted) override; bool loadFunction(const pugi::xml_attribute& attr) final;
uint32_t fireStepEvent(Creature* creature, Item* item, const Position& pos); uint32_t fireStepEvent(Creature* creature, Item* item, const Position& pos);
uint32_t fireAddRemItem(Item* item, Item* tileItem, 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 { uint32_t getSlot() const {
return slot; return slot;
@@ -122,7 +115,7 @@ class MoveEvent final : public Event
//scripting //scripting
bool executeStep(Creature* creature, Item* item, const Position& pos); 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); bool executeAddRemItem(Item* item, Item* tileItem, const Position& pos);
// //
@@ -139,104 +132,29 @@ class MoveEvent final : public Event
const std::string& getVocationString() const { const std::string& getVocationString() const {
return vocationString; return vocationString;
} }
void setVocationString(const std::string& str) {
vocationString = str;
}
uint32_t getWieldInfo() const { uint32_t getWieldInfo() const {
return wieldInfo; return wieldInfo;
} }
const VocEquipMap& getVocEquipMap() const { const VocEquipMap& getVocEquipMap() const {
return vocEquipMap; 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); protected:
static uint32_t StepOutField(Creature* creature, Item* item, const Position& pos); std::string getScriptEventName() const final;
static uint32_t AddItemField(Item* item, Item* tileItem, const Position& pos); static StepFunction StepInField;
static uint32_t RemoveItemField(Item* item, Item* tileItem, const Position& pos); static StepFunction StepOutField;
static uint32_t EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); static MoveFunction AddItemField;
static uint32_t DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); static MoveFunction RemoveItemField;
static EquipFunction EquipItem;
static EquipFunction DeEquipItem;
MoveEvent_t eventType = MOVE_EVENT_NONE; MoveEvent_t eventType = MOVE_EVENT_NONE;
StepFunction stepFunction; StepFunction* stepFunction = nullptr;
MoveFunction moveFunction; MoveFunction* moveFunction = nullptr;
EquipFunction equipFunction; EquipFunction* equipFunction = nullptr;
private:
std::string getScriptEventName() const override;
uint32_t slot = SLOTP_WHEREEVER; uint32_t slot = SLOTP_WHEREEVER;
std::string slotName;
//onEquip information //onEquip information
uint32_t reqLevel = 0; uint32_t reqLevel = 0;
@@ -245,12 +163,6 @@ class MoveEvent final : public Event
std::string vocationString; std::string vocationString;
uint32_t wieldInfo = 0; uint32_t wieldInfo = 0;
VocEquipMap vocEquipMap; 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 #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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]; const ItemType& it = Item::items[id];
add<uint16_t>(it.clientId); if (it.disguise) {
add<uint16_t>(it.disguiseId);
addByte(0xFF); // MARK_UNMARKED } else {
add<uint16_t>(it.id);
}
if (it.stackable) { if (it.stackable) {
addByte(count); addByte(count);
} else if (it.isSplash() || it.isFluidContainer()) { } else if (it.isSplash() || it.isFluidContainer()) {
addByte(fluidMap[count & 7]); addByte(getLiquidColor(count));
}
if (it.isAnimation) {
addByte(0xFE); // random phase (0xFF for async)
} }
} }
@@ -118,21 +116,20 @@ void NetworkMessage::addItem(const Item* item)
{ {
const ItemType& it = Item::items[item->getID()]; const ItemType& it = Item::items[item->getID()];
add<uint16_t>(it.clientId); if (it.disguise) {
addByte(0xFF); // MARK_UNMARKED add<uint16_t>(it.disguiseId);
} else {
add<uint16_t>(it.id);
}
if (it.stackable) { if (it.stackable) {
addByte(std::min<uint16_t>(0xFF, item->getItemCount())); addByte(std::min<uint16_t>(0xFF, item->getItemCount()));
} else if (it.isSplash() || it.isFluidContainer()) { } else if (it.isSplash() || it.isFluidContainer()) {
addByte(fluidMap[item->getFluidType() & 7]); addByte(getLiquidColor(item->getFluidType()));
}
if (it.isAnimation) {
addByte(0xFE); // random phase (0xFF for async)
} }
} }
void NetworkMessage::addItemId(uint16_t itemId) 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -31,16 +31,14 @@ class RSA;
class NetworkMessage class NetworkMessage
{ {
public: public:
using MsgSize_t = uint16_t; typedef uint16_t MsgSize_t;
// Headers: // Headers:
// 2 bytes for unencrypted message size // 2 bytes for unencrypted message size
// 4 bytes for checksum
// 2 bytes for encrypted message size // 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 { HEADER_LENGTH = 2 };
enum { CHECKSUM_LENGTH = 4 };
enum { XTEA_MULTIPLE = 8 }; 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 }; enum { MAX_PROTOCOL_BODY_LENGTH = MAX_BODY_LENGTH - 10 };
NetworkMessage() = default; NetworkMessage() = default;
@@ -150,6 +148,18 @@ class NetworkMessage
} }
protected: 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 { struct NetworkMessageInfo {
MsgSize_t length = 0; MsgSize_t length = 0;
MsgSize_t position = INITIAL_BUFFER_POSITION; MsgSize_t position = INITIAL_BUFFER_POSITION;
@@ -158,19 +168,6 @@ class NetworkMessage
NetworkMessageInfo info; NetworkMessageInfo info;
uint8_t buffer[NETWORKMESSAGE_MAXSIZE]; 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__ #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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -21,86 +21,20 @@
#define FS_NPC_H_B090D0CB549D4435AFA03647195D156F #define FS_NPC_H_B090D0CB549D4435AFA03647195D156F
#include "creature.h" #include "creature.h"
#include "luascript.h"
#include <set> #include <set>
class Npc; class Npc;
class Player; class Player;
class BehaviourDatabase;
class Npcs class Npcs
{ {
public: public:
static void loadNpcs();
static void reload(); 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 class Npc final : public Creature
{ {
public: public:
@@ -110,53 +44,41 @@ class Npc final : public Creature
Npc(const Npc&) = delete; Npc(const Npc&) = delete;
Npc& operator=(const Npc&) = delete; Npc& operator=(const Npc&) = delete;
Npc* getNpc() override { Npc* getNpc() final {
return this; return this;
} }
const Npc* getNpc() const override { const Npc* getNpc() const final {
return this; return this;
} }
bool isPushable() const override { bool isPushable() const final {
return pushable && walkTicks != 0; return baseSpeed > 0;
} }
void setID() override { void setID() final {
if (id == 0) { if (id == 0) {
id = npcAutoID++; id = npcAutoID++;
} }
} }
void removeList() override; void removeList() final;
void addList() override; void addList() final;
static Npc* createNpc(const std::string& name); static Npc* createNpc(const std::string& name);
bool canSee(const Position& pos) const override; bool canSee(const Position& pos) const final;
bool load(); bool load();
void reload(); void reload();
const std::string& getName() const override { const std::string& getName() const final {
return name; return name;
} }
const std::string& getNameDescription() const override { const std::string& getNameDescription() const final {
return name; 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 doSay(const std::string& text);
void doSayToPlayer(Player* player, const std::string& text);
void doMoveTo(const Position& pos); void doMoveTo(const Position& pos);
@@ -168,45 +90,38 @@ class Npc final : public Creature
} }
void setMasterPos(Position pos, int32_t radius = 1) { void setMasterPos(Position pos, int32_t radius = 1) {
masterPos = pos; masterPos = pos;
if (masterRadius == -1) { if (masterRadius == 0) {
masterRadius = radius; 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 turnToCreature(Creature* creature);
void setCreatureFocus(Creature* creature); void setCreatureFocus(Creature* creature);
NpcScriptInterface* getScriptInterface();
static uint32_t npcAutoID; static uint32_t npcAutoID;
private: protected:
explicit Npc(const std::string& name); explicit Npc(const std::string& name);
void onCreatureAppear(Creature* creature, bool isLogin) override; void onCreatureAppear(Creature* creature, bool isLogin) final;
void onRemoveCreature(Creature* creature, bool isLogout) override; void onRemoveCreature(Creature* creature, bool isLogout) final;
void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, 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 onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) final;
void onThink(uint32_t interval) override; void onThink(uint32_t interval) final;
std::string getDescription(int32_t lookDistance) const override; std::string getDescription(int32_t lookDistance) const final;
bool isImmune(CombatType_t) const override { bool isImmune(CombatType_t) const final {
return !attackable; return true;
} }
bool isImmune(ConditionType_t) const override { bool isImmune(ConditionType_t) const final {
return !attackable; return true;
} }
bool isAttackable() const override { bool isAttackable() const final {
return attackable; return false;
} }
bool getNextStep(Direction& dir, uint32_t& flags) override; bool getNextStep(Direction& dir, uint32_t& flags) final;
void setIdle(bool idle); void setIdle(bool idle);
void updateIdleStatus(); void updateIdleStatus();
@@ -215,41 +130,29 @@ class Npc final : public Creature
bool getRandomStep(Direction& dir) const; bool getRandomStep(Direction& dir) const;
void reset(); 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::set<Player*> spectators;
std::string name; std::string name;
std::string filename; std::string filename;
NpcEventsHandler* npcEventHandler;
Position masterPos; Position masterPos;
uint32_t walkTicks; uint32_t lastTalkCreature;
int32_t focusCreature; uint32_t focusCreature;
int32_t masterRadius; uint32_t masterRadius;
uint8_t speechBubble; int64_t conversationStartTime;
int64_t conversationEndTime;
int64_t staticMovementTime;
bool floorChange;
bool attackable;
bool ignoreHeight;
bool loaded; bool loaded;
bool isIdle; bool isIdle;
bool pushable;
static NpcScriptInterface* scriptInterface; BehaviourDatabase* behaviourDatabase;
friend class Npcs; friend class Npcs;
friend class NpcScriptInterface; friend class BehaviourDatabase;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -23,19 +23,18 @@
#include "game.h" #include "game.h"
#include "iomarket.h" #ifndef _WIN32
#include <csignal> // for sigemptyset()
#endif
#include "configmanager.h" #include "configmanager.h"
#include "scriptmanager.h" #include "scriptmanager.h"
#include "rsa.h" #include "rsa.h"
#include "protocolold.h"
#include "protocollogin.h" #include "protocollogin.h"
#include "protocolstatus.h" #include "protocolstatus.h"
#include "databasemanager.h" #include "databasemanager.h"
#include "scheduler.h" #include "scheduler.h"
#include "databasetasks.h" #include "databasetasks.h"
#include "script.h"
#include <fstream>
DatabaseTasks g_databaseTasks; DatabaseTasks g_databaseTasks;
Dispatcher g_dispatcher; Dispatcher g_dispatcher;
@@ -45,7 +44,6 @@ Game g_game;
ConfigManager g_config; ConfigManager g_config;
Monsters g_monsters; Monsters g_monsters;
Vocations g_vocations; Vocations g_vocations;
extern Scripts* g_scripts;
RSA g_RSA; RSA g_RSA;
std::mutex g_loaderLock; std::mutex g_loaderLock;
@@ -58,9 +56,9 @@ void startupErrorMessage(const std::string& errorStr)
g_loaderSignal.notify_all(); 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 // 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"); 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 // Setup bad allocation handler
std::set_new_handler(badAllocationHandler); 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; ServiceManager serviceManager;
g_dispatcher.start(); g_dispatcher.start();
@@ -84,6 +91,19 @@ int main(int argc, char* argv[])
if (serviceManager.is_running()) { if (serviceManager.is_running()) {
std::cout << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " Server Online!" << std::endl << std::endl; 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(); serviceManager.run();
} else { } else {
std::cout << ">> No services running. The server is NOT online." << std::endl; 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 << "Visit our forum for updates, support, and resources: http://otland.net/." << std::endl;
std::cout << 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 // read global config
std::cout << ">> Loading config" << std::endl; std::cout << ">> Loading config" << std::endl;
if (!g_config.load()) { if (!g_config.load()) {
@@ -160,14 +165,16 @@ void mainLoader(int, char*[], ServiceManager* services)
//set RSA key //set RSA key
try { try {
g_RSA.loadPEM("key.pem"); g_RSA.loadPEM("key.pem");
} catch(const std::exception& e) { }
catch (const std::exception& e) {
startupErrorMessage(e.what()); startupErrorMessage(e.what());
return; return;
} }
std::cout << ">> Establishing database connection..." << std::flush; std::cout << ">> Establishing database connection..." << std::flush;
if (!Database::getInstance().connect()) { Database* db = Database::getInstance();
if (!db->connect()) {
startupErrorMessage("Failed to connect to database."); startupErrorMessage("Failed to connect to database.");
return; return;
} }
@@ -183,8 +190,6 @@ void mainLoader(int, char*[], ServiceManager* services)
} }
g_databaseTasks.start(); g_databaseTasks.start();
DatabaseManager::updateDatabase();
if (g_config.getBoolean(ConfigManager::OPTIMIZE_DATABASE) && !DatabaseManager::optimizeTables()) { if (g_config.getBoolean(ConfigManager::OPTIMIZE_DATABASE) && !DatabaseManager::optimizeTables()) {
std::cout << "> No tables were optimized." << std::endl; std::cout << "> No tables were optimized." << std::endl;
} }
@@ -198,42 +203,26 @@ void mainLoader(int, char*[], ServiceManager* services)
// load item data // load item data
std::cout << ">> Loading items" << std::endl; std::cout << ">> Loading items" << std::endl;
if (!Item::items.loadFromOtb("data/items/items.otb")) { if (!Item::items.loadItems()) {
startupErrorMessage("Unable to load items (OTB)!"); startupErrorMessage("Unable to load items (SRV)!");
return;
}
if (!Item::items.loadFromXml()) {
startupErrorMessage("Unable to load items (XML)!");
return; return;
} }
std::cout << ">> Loading script systems" << std::endl; std::cout << ">> Loading script systems" << std::endl;
if (!ScriptingManager::getInstance().loadScriptSystems()) { if (!ScriptingManager::getInstance()->loadScriptSystems()) {
startupErrorMessage("Failed to load script systems"); startupErrorMessage("Failed to load script systems");
return; 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; std::cout << ">> Loading monsters" << std::endl;
if (!g_monsters.loadFromXml()) { if (!g_monsters.loadFromXml()) {
startupErrorMessage("Unable to load monsters!"); startupErrorMessage("Unable to load monsters!");
return; 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; std::cout << ">> Loading outfits" << std::endl;
if (!Outfits::getInstance().loadFromXml()) { auto& outfits = Outfits::getInstance();
if (!outfits.loadFromXml()) {
startupErrorMessage("Unable to load outfits!"); startupErrorMessage("Unable to load outfits!");
return; return;
} }
@@ -266,14 +255,11 @@ void mainLoader(int, char*[], ServiceManager* services)
g_game.setGameState(GAME_STATE_INIT); g_game.setGameState(GAME_STATE_INIT);
// Game client protocols // Game client protocols
services->add<ProtocolGame>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::GAME_PORT))); services->add<ProtocolGame>(g_config.getNumber(ConfigManager::GAME_PORT));
services->add<ProtocolLogin>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT))); services->add<ProtocolLogin>(g_config.getNumber(ConfigManager::LOGIN_PORT));
// OT protocols // OT protocols
services->add<ProtocolStatus>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::STATUS_PORT))); services->add<ProtocolStatus>(g_config.getNumber(ConfigManager::STATUS_PORT));
// Legacy login protocol
services->add<ProtocolOld>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
RentPeriod_t rentPeriod; RentPeriod_t rentPeriod;
std::string strRentPeriod = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD)); 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); g_game.map.houses.payHouses(rentPeriod);
IOMarket::checkExpiredOffers();
IOMarket::getInstance().updateStatistics();
std::cout << ">> Loaded all modules, server starting up..." << std::endl; std::cout << ">> Loaded all modules, server starting up..." << std::endl;
#ifndef _WIN32 #ifndef _WIN32

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * 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 uint16_t OUTPUTMESSAGE_FREE_LIST_CAPACITY = 2048;
const std::chrono::milliseconds OUTPUTMESSAGE_AUTOSEND_DELAY {10}; 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() void OutputMessagePool::scheduleSendAll()
{ {
auto functor = std::bind(&OutputMessagePool::sendAll, this); auto functor = std::bind(&OutputMessagePool::sendAll, this);
@@ -71,7 +79,5 @@ void OutputMessagePool::removeProtocolFromAutosend(const Protocol_ptr& protocol)
OutputMessage_ptr OutputMessagePool::getOutputMessage() OutputMessage_ptr OutputMessagePool::getOutputMessage()
{ {
// LockfreePoolingAllocator<void,...> will leave (void* allocate) ill-formed because return std::allocate_shared<OutputMessage>(OutputMessageAllocator());
// of sizeof(T), so this guaranatees that only one list will be initialized
return std::allocate_shared<OutputMessage>(LockfreePoolingAllocator<void, OUTPUTMESSAGE_FREE_LIST_CAPACITY>());
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -28,80 +28,76 @@ class Protocol;
class OutputMessage : public NetworkMessage class OutputMessage : public NetworkMessage
{ {
public: public:
OutputMessage() = default; OutputMessage() = default;
// non-copyable // non-copyable
OutputMessage(const OutputMessage&) = delete; OutputMessage(const OutputMessage&) = delete;
OutputMessage& operator=(const OutputMessage&) = delete; OutputMessage& operator=(const OutputMessage&) = delete;
uint8_t* getOutputBuffer() { uint8_t* getOutputBuffer() {
return buffer + outputBufferStart; return buffer + outputBufferStart;
} }
void writeMessageLength() { void writeMessageLength() {
add_header(info.length); add_header(info.length);
} }
void addCryptoHeader(bool addChecksum) { void addCryptoHeader() {
if (addChecksum) { writeMessageLength();
add_header(adlerChecksum(buffer + outputBufferStart, info.length)); }
}
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) { inline void append(const OutputMessage_ptr& msg) {
auto msgLen = msg.getLength(); auto msgLen = msg->getLength();
memcpy(buffer + info.position, msg.getBuffer() + 8, msgLen); memcpy(buffer + info.position, msg->getBuffer() + 4, msgLen);
info.length += msgLen; info.length += msgLen;
info.position += msgLen; info.position += msgLen;
} }
void append(const OutputMessage_ptr& msg) { protected:
auto msgLen = msg->getLength(); template <typename T>
memcpy(buffer + info.position, msg->getBuffer() + 8, msgLen); inline void add_header(T add) {
info.length += msgLen; assert(outputBufferStart >= sizeof(T));
info.position += msgLen; outputBufferStart -= sizeof(T);
} memcpy(buffer + outputBufferStart, &add, sizeof(T));
//added header size to the message size
info.length += sizeof(T);
}
private: MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION;
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;
}; };
class OutputMessagePool class OutputMessagePool
{ {
public: public:
// non-copyable // non-copyable
OutputMessagePool(const OutputMessagePool&) = delete; OutputMessagePool(const OutputMessagePool&) = delete;
OutputMessagePool& operator=(const OutputMessagePool&) = delete; OutputMessagePool& operator=(const OutputMessagePool&) = delete;
static OutputMessagePool& getInstance() { static OutputMessagePool& getInstance() {
static OutputMessagePool instance; static OutputMessagePool instance;
return instance; return instance;
} }
void sendAll(); void sendAll();
void scheduleSendAll(); void scheduleSendAll();
static OutputMessage_ptr getOutputMessage(); static OutputMessage_ptr getOutputMessage();
void addProtocolToAutosend(Protocol_ptr protocol); void addProtocolToAutosend(Protocol_ptr protocol);
void removeProtocolFromAutosend(const Protocol_ptr& protocol); void removeProtocolFromAutosend(const Protocol_ptr& protocol);
private: private:
OutputMessagePool() = default; OutputMessagePool() = default;
//NOTE: A vector is used here because this container is mostly read //NOTE: A vector is used here because this container is mostly read
//and relatively rarely modified (only when a client connects/disconnects) //and relatively rarely modified (only when a client connects/disconnects)
std::vector<Protocol_ptr> bufferedProtocols; std::vector<Protocol_ptr> bufferedProtocols;
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -39,13 +39,13 @@ void Party::disband()
return; return;
} }
Player* currentLeader = leader; Player* currentLeader = leader;
leader = nullptr; leader = nullptr;
currentLeader->setParty(nullptr); currentLeader->setParty(nullptr);
currentLeader->sendClosePrivate(CHANNEL_PARTY); currentLeader->sendClosePrivate(CHANNEL_PARTY);
g_game.updatePlayerShield(currentLeader); g_game.updatePlayerShield(currentLeader);
g_game.updatePlayerHelpers(*currentLeader);
currentLeader->sendCreatureSkull(currentLeader); currentLeader->sendCreatureSkull(currentLeader);
currentLeader->sendTextMessage(MESSAGE_INFO_DESCR, "Your party has been disbanded."); currentLeader->sendTextMessage(MESSAGE_INFO_DESCR, "Your party has been disbanded.");
@@ -70,8 +70,8 @@ void Party::disband()
member->sendCreatureSkull(currentLeader); member->sendCreatureSkull(currentLeader);
currentLeader->sendCreatureSkull(member); currentLeader->sendCreatureSkull(member);
g_game.updatePlayerHelpers(*member);
} }
memberList.clear(); memberList.clear();
delete this; delete this;
} }
@@ -112,12 +112,10 @@ bool Party::leaveParty(Player* player)
player->setParty(nullptr); player->setParty(nullptr);
player->sendClosePrivate(CHANNEL_PARTY); player->sendClosePrivate(CHANNEL_PARTY);
g_game.updatePlayerShield(player); g_game.updatePlayerShield(player);
g_game.updatePlayerHelpers(*player);
for (Player* member : memberList) { for (Player* member : memberList) {
member->sendCreatureSkull(player); member->sendCreatureSkull(player);
player->sendPlayerPartyIcons(member); player->sendPlayerPartyIcons(member);
g_game.updatePlayerHelpers(*member);
} }
leader->sendCreatureSkull(player); leader->sendCreatureSkull(player);
@@ -127,6 +125,7 @@ bool Party::leaveParty(Player* player)
player->sendTextMessage(MESSAGE_INFO_DESCR, "You have left the party."); player->sendTextMessage(MESSAGE_INFO_DESCR, "You have left the party.");
updateSharedExperience(); updateSharedExperience();
updateVocationsList();
clearPlayerPoints(player); clearPlayerPoints(player);
@@ -213,10 +212,9 @@ bool Party::joinParty(Player& player)
memberList.push_back(&player); memberList.push_back(&player);
g_game.updatePlayerHelpers(player);
player.removePartyInvitation(this); player.removePartyInvitation(this);
updateSharedExperience(); updateSharedExperience();
updateVocationsList();
const std::string& leaderName = leader->getName(); const std::string& leaderName = leader->getName();
ss.str(std::string()); ss.str(std::string());
@@ -244,12 +242,6 @@ bool Party::removeInvite(Player& player, bool removeFromPlayer/* = true*/)
if (empty()) { if (empty()) {
disband(); disband();
} else {
for (Player* member : memberList) {
g_game.updatePlayerHelpers(*member);
}
g_game.updatePlayerHelpers(*leader);
} }
return true; return true;
@@ -277,7 +269,7 @@ bool Party::invitePlayer(Player& player)
std::ostringstream ss; std::ostringstream ss;
ss << player.getName() << " has been invited."; ss << player.getName() << " has been invited.";
if (empty()) { if (memberList.empty() && inviteList.empty()) {
ss << " Open the party channel to communicate with your members."; ss << " Open the party channel to communicate with your members.";
g_game.updatePlayerShield(leader); g_game.updatePlayerShield(leader);
leader->sendCreatureSkull(leader); leader->sendCreatureSkull(leader);
@@ -287,11 +279,6 @@ bool Party::invitePlayer(Player& player)
inviteList.push_back(&player); inviteList.push_back(&player);
for (Player* member : memberList) {
g_game.updatePlayerHelpers(*member);
}
g_game.updatePlayerHelpers(*leader);
leader->sendCreatureShield(&player); leader->sendCreatureShield(&player);
player.sendCreatureShield(leader); 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() void Party::updateSharedExperience()
{ {
if (sharedExpActive) { 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) bool Party::setSharedExperience(Player* player, bool sharedExpActive)
{ {
if (!player || leader != player) { 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) { if (player->getLevel() < minLevel) {
return false; return false;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* The Forgotten Server - a free and open-source MMORPG server emulator * Tibia GIMUD Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@
class Player; class Player;
class Party; class Party;
using PlayerVector = std::vector<Player*>; typedef std::vector<Player*> PlayerVector;
class Party class Party
{ {
@@ -61,12 +61,13 @@ class Party
bool isPlayerInvited(const Player* player) const; bool isPlayerInvited(const Player* player) const;
void updateAllPartyIcons(); void updateAllPartyIcons();
void broadcastPartyMessage(MessageClasses msgClass, const std::string& msg, bool sendToInvitations = false); void broadcastPartyMessage(MessageClasses msgClass, const std::string& msg, bool sendToInvitations = false);
void broadcastPartyLoot(const std::string& loot);
bool empty() const { bool empty() const {
return memberList.empty() && inviteList.empty(); return memberList.empty() && inviteList.empty();
} }
bool canOpenCorpse(uint32_t ownerId) const; 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 setSharedExperience(Player* player, bool sharedExpActive);
bool isSharedExperienceActive() const { bool isSharedExperienceActive() const {
return sharedExpActive; return sharedExpActive;
@@ -77,10 +78,12 @@ class Party
bool canUseSharedExperience(const Player* player) const; bool canUseSharedExperience(const Player* player) const;
void updateSharedExperience(); void updateSharedExperience();
void updateVocationsList();
void updatePlayerTicks(Player* player, uint32_t points); void updatePlayerTicks(Player* player, uint32_t points);
void clearPlayerPoints(Player* player); void clearPlayerPoints(Player* player);
private: protected:
bool canEnableSharedExperience(); bool canEnableSharedExperience();
std::map<uint32_t, int64_t> ticksMap; std::map<uint32_t, int64_t> ticksMap;
@@ -90,6 +93,8 @@ class Party
Player* leader; Player* leader;
float extraExpRate = 0.20f;
bool sharedExpActive = false; bool sharedExpActive = false;
bool sharedExpEnabled = false; bool sharedExpEnabled = false;
}; };

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