commit newest tfs branch only for compare

This commit is contained in:
ErikasKontenis
2020-01-02 19:39:21 +02:00
parent 0b0624a20b
commit 1f7dcd7347
160 changed files with 24900 additions and 10996 deletions

View File

@@ -4,7 +4,6 @@ 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
@@ -17,6 +16,7 @@ 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,10 +26,12 @@ 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
@@ -37,6 +39,7 @@ 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
@@ -49,24 +52,29 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +24,8 @@
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,29 +40,27 @@ Actions::Actions() :
Actions::~Actions() Actions::~Actions()
{ {
clear(); clear(false);
} }
inline void Actions::clearMap(ActionUseMap& map) void Actions::clearMap(ActionUseMap& map, bool fromLua)
{ {
// Filter out duplicates to avoid double-free for (auto it = map.begin(); it != map.end(); ) {
std::unordered_set<Action*> set; if (fromLua == it->second.fromLua) {
for (const auto& it : map) { it = map.erase(it);
set.insert(it.second); } else {
} ++it;
map.clear(); }
for (Action* action : set) {
delete action;
} }
} }
void Actions::clear() void Actions::clear(bool fromLua)
{ {
clearMap(useItemMap); clearMap(useItemMap, fromLua);
clearMap(actionItemMap); clearMap(uniqueItemMap, fromLua);
clearMap(actionItemMap, fromLua);
scriptInterface.reInitState(); reInitState(fromLua);
} }
LuaScriptInterface& Actions::getScriptInterface() LuaScriptInterface& Actions::getScriptInterface()
@@ -75,23 +73,23 @@ std::string Actions::getScriptBaseName() const
return "actions"; return "actions";
} }
Event* Actions::getEvent(const std::string& nodeName) Event_ptr 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 new Action(&scriptInterface); return Event_ptr(new Action(&scriptInterface));
} }
bool Actions::registerEvent(Event* event, const pugi::xml_node& node) bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
{ {
Action* action = static_cast<Action*>(event); //event is guaranteed to be an Action Action_ptr action{static_cast<Action*>(event.release())}; //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, action); auto result = useItemMap.emplace(id, std::move(*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;
} }
@@ -107,14 +105,14 @@ bool Actions::registerEvent(Event* 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;
@@ -122,10 +120,44 @@ bool Actions::registerEvent(Event* 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, action); auto result = actionItemMap.emplace(aid, std::move(*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;
} }
@@ -141,14 +173,14 @@ bool Actions::registerEvent(Event* 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;
@@ -160,6 +192,69 @@ bool Actions::registerEvent(Event* 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) {
@@ -208,16 +303,23 @@ 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
@@ -256,44 +358,41 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
if (bed->trySleep(player)) { if (bed->trySleep(player)) {
player->setBedItem(bed); player->setBedItem(bed);
if (!bed->sleep(player)) { g_game.sendOfflineTrainingDialog(player);
return RETURNVALUE_CANNOTUSETHISOBJECT;
}
} }
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
if (Container* container = item->getContainer()) { if (Container* container = item->getContainer()) {
if (!item->isChestQuest()) { Container* openContainer;
Container* openContainer;
//depot container //depot container
if (DepotLocker* depot = container->getDepotLocker()) { if (DepotLocker* depot = container->getDepotLocker()) {
DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId(), true); DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId());
myDepotLocker->setParent(depot->getParent()->getTile()); myDepotLocker->setParent(depot->getParent()->getTile());
openContainer = myDepotLocker; openContainer = myDepotLocker;
} else { player->setLastDepotId(depot->getDepotId());
openContainer = container; } else {
} openContainer = container;
uint32_t corpseOwner = container->getCorpseOwner();
if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
return RETURNVALUE_YOUARENOTTHEOWNER;
}
//open/close container
int32_t oldContainerId = player->getContainerID(openContainer);
if (oldContainerId != -1) {
player->onCloseContainer(openContainer);
player->closeContainer(oldContainerId);
} else {
player->addContainer(index, openContainer);
player->onSendContainer(openContainer);
}
return RETURNVALUE_NOERROR;
} }
uint32_t corpseOwner = container->getCorpseOwner();
if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
return RETURNVALUE_YOUARENOTTHEOWNER;
}
//open/close container
int32_t oldContainerId = player->getContainerID(openContainer);
if (oldContainerId != -1) {
player->onCloseContainer(openContainer);
player->closeContainer(oldContainerId);
} else {
player->addContainer(index, openContainer);
player->onSendContainer(openContainer);
}
return RETURNVALUE_NOERROR;
} }
const ItemType& it = Item::items[item->getID()]; const ItemType& it = Item::items[item->getID()];
@@ -307,12 +406,6 @@ 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;
@@ -324,7 +417,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(), -1)); showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), item->getSubType()));
} }
ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey); ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey);
@@ -354,7 +447,7 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position&
} }
if (isHotkey) { if (isHotkey) {
showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1)); showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), item->getSubType()));
} }
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)) {
@@ -373,11 +466,9 @@ 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());
@@ -386,9 +477,6 @@ 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");
@@ -409,6 +497,38 @@ 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";
@@ -418,8 +538,7 @@ 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);
} }
} }
@@ -458,4 +577,4 @@ bool Action::executeUse(Player* player, Item* item, const Position& fromPosition
LuaScriptInterface::pushBoolean(L, isHotkey); LuaScriptInterface::pushBoolean(L, isHotkey);
return scriptInterface->callFunction(6); return scriptInterface->callFunction(6);
} }

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,15 +31,14 @@ 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;
@@ -119,22 +118,25 @@ 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);
void clear() final; LuaScriptInterface& getScriptInterface() override;
LuaScriptInterface& getScriptInterface() final; std::string getScriptBaseName() const override;
std::string getScriptBaseName() const 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;
typedef std::map<uint16_t, Action*> ActionUseMap; using ActionUseMap = std::map<uint16_t, Action>;
ActionUseMap useItemMap; ActionUseMap useItemMap;
ActionUseMap uniqueItemMap;
ActionUseMap actionItemMap; ActionUseMap actionItemMap;
Action* getAction(const Item* item); Action* getAction(const Item* item);
void clearMap(ActionUseMap& map); void clearMap(ActionUseMap& map, bool fromLua);
LuaScriptInterface scriptInterface; LuaScriptInterface scriptInterface;
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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;
}; };
typedef std::map<uint32_t, ConnectBlock> IpConnectMap; using IpConnectMap = std::map<uint32_t, ConnectBlock>;
class Ban class Ban
{ {
public: public:
bool acceptConnection(uint32_t clientip); bool acceptConnection(uint32_t clientIP);
protected: private:
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 ip, BanInfo& banInfo); static bool isIpBanned(uint32_t clientIP, BanInfo& banInfo);
static bool isPlayerNamelocked(uint32_t playerId); static bool isPlayerNamelocked(uint32_t playerId);
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,14 +51,13 @@ 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* event = getEvent(node.name()); Event_ptr 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;
} }
@@ -68,12 +67,15 @@ 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")); success = event->loadFunction(node.attribute("function"), false);
} }
if (!success || !registerEvent(event, node)) { if (success) {
delete event; registerEvent(std::move(event), node);
} }
} }
return true; return true;
@@ -82,14 +84,18 @@ bool BaseEvents::loadFromXml()
bool BaseEvents::reload() bool BaseEvents::reload()
{ {
loaded = false; loaded = false;
clear(); clear(false);
return loadFromXml(); return loadFromXml();
} }
Event::Event(LuaScriptInterface* interface) : scriptInterface(interface) {} void BaseEvents::reInitState(bool fromLua)
{
if (!fromLua) {
getScriptInterface().reInitState();
}
}
Event::Event(const Event* copy) : Event::Event(LuaScriptInterface* interface) : scriptInterface(interface) {}
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
{ {
@@ -143,6 +149,24 @@ 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) {
@@ -158,7 +182,6 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,18 +22,21 @@
#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);
virtual bool loadFunction(const pugi::xml_attribute&) { bool loadCallback();
virtual bool loadFunction(const pugi::xml_attribute&, bool) {
return false; return false;
} }
@@ -41,10 +44,12 @@ 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;
}; };
@@ -60,13 +65,14 @@ class BaseEvents
bool isLoaded() const { bool isLoaded() const {
return loaded; return loaded;
} }
void reInitState(bool fromLua);
protected: private:
virtual LuaScriptInterface& getScriptInterface() = 0; virtual LuaScriptInterface& getScriptInterface() = 0;
virtual std::string getScriptBaseName() const = 0; virtual std::string getScriptBaseName() const = 0;
virtual Event* getEvent(const std::string& nodeName) = 0; virtual Event_ptr getEvent(const std::string& nodeName) = 0;
virtual bool registerEvent(Event* event, const pugi::xml_node& node) = 0; virtual bool registerEvent(Event_ptr event, const pugi::xml_node& node) = 0;
virtual void clear() = 0; virtual void clear(bool) = 0;
bool loaded = false; bool loaded = false;
}; };
@@ -82,9 +88,8 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 poff effect // display 'Zzzz'/sleep effect
g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF); g_game.addMagicEffect(player->getPosition(), CONST_ME_SLEEP);
// 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 != 0) { if (player && it.transformToOnUse[player->getSex()] != 0) {
const ItemType& newType = Item::items[it.transformToOnUse]; const ItemType& newType = Item::items[it.transformToOnUse[player->getSex()]];
if (newType.type == ITEM_TYPE_BED) { if (newType.type == ITEM_TYPE_BED) {
g_game.transformItem(this, it.transformToOnUse); g_game.transformItem(this, it.transformToOnUse[player->getSex()]);
} }
} 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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() final { BedItem* getBed() override {
return this; return this;
} }
const BedItem* getBed() const final { const BedItem* getBed() const override {
return this; return this;
} }
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final; Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
void serializeAttr(PropWriteStream& propWriteStream) const final; void serializeAttr(PropWriteStream& propWriteStream) const override;
bool canRemove() const final { bool canRemove() const override {
return house == nullptr; return house == nullptr;
} }
@@ -48,9 +48,6 @@ 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;
} }
@@ -63,7 +60,7 @@ class BedItem final : public Item
BedItem* getNextBedItem() const; BedItem* getNextBedItem() const;
protected: private:
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +54,10 @@ 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)
@@ -69,6 +73,10 @@ 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
@@ -88,6 +96,20 @@ 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;
} }
@@ -101,10 +123,27 @@ 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()) {
@@ -255,21 +294,39 @@ 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()) {
ChatChannel channel(pugi::cast<uint16_t>(channelNode.attribute("id").value()), channelNode.attribute("name").as_string()); uint16_t channelId = pugi::cast<uint16_t>(channelNode.attribute("id").value());
channel.publicChannel = channelNode.attribute("public").as_bool(); std::string channelName = channelNode.attribute("name").as_string();
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");
@@ -281,13 +338,8 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,23 +26,24 @@
class Party; class Party;
class Player; class Player;
typedef std::map<uint32_t, Player*> UsersMap; using UsersMap = std::map<uint32_t, Player*>;
typedef std::map<uint32_t, const Player*> InvitedMap; using InvitedMap = std::map<uint32_t, const Player*>;
class ChatChannel class ChatChannel
{ {
public: public:
ChatChannel() = default; ChatChannel() = default;
ChatChannel(uint16_t channelId, std::string channelName): ChatChannel(uint16_t channelId, std::string channelName):
name(std::move(channelName)), id{channelId}, 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;
@@ -71,6 +72,9 @@ 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;
@@ -78,7 +82,6 @@ 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;
@@ -89,7 +92,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 final { uint32_t getOwner() const override {
return owner; return owner;
} }
void setOwner(uint32_t owner) { void setOwner(uint32_t owner) {
@@ -105,16 +108,16 @@ class PrivateChatChannel final : public ChatChannel
void closeChannel() const; void closeChannel() const;
const InvitedMap* getInvitedUsers() const final { const InvitedMap* getInvitedUsers() const override {
return &invites; return &invites;
} }
protected: private:
InvitedMap invites; InvitedMap invites;
uint32_t owner = 0; uint32_t owner = 0;
}; };
typedef std::list<ChatChannel*> ChannelList; using ChannelList = std::list<ChatChannel*>;
class Chat class Chat
{ {

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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;
protected: private:
formulaType_t type; formulaType_t type;
}; };
@@ -46,18 +46,12 @@ 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 {
@@ -68,8 +62,6 @@ 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;
@@ -85,39 +77,7 @@ struct CombatParams {
bool useCharges = false; bool useCharges = false;
}; };
struct Impact using CombatFunction = std::function<void(Creature*, Creature*, const CombatParams&, CombatDamage*)>;
{
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
{ {
@@ -185,14 +145,14 @@ class MatrixArea
return cols; return cols;
} }
inline const bool* operator[](uint32_t i) const { const bool* operator[](uint32_t i) const {
return data_[i]; return data_[i];
} }
inline bool* operator[](uint32_t i) { bool* operator[](uint32_t i) {
return data_[i]; return data_[i];
} }
protected: private:
uint32_t centerX; uint32_t centerX;
uint32_t centerY; uint32_t centerY;
@@ -222,7 +182,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();
protected: private:
enum MatrixOperation_t { enum MatrixOperation_t {
MATRIXOPERATION_COPY, MATRIXOPERATION_COPY,
MATRIXOPERATION_MIRROR, MATRIXOPERATION_MIRROR,
@@ -233,7 +193,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);
void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const; static void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op);
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);
@@ -282,18 +242,7 @@ class Combat
Combat(const Combat&) = delete; Combat(const Combat&) = delete;
Combat& operator=(const Combat&) = delete; Combat& operator=(const Combat&) = delete;
static int32_t computeDamage(Creature* creature, int32_t strength, int32_t variation); static void doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params);
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);
@@ -317,10 +266,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(const Position& fromPos, const Position& toPos, uint8_t effect); static void addDistanceEffect(Creature* caster, 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& pos) const; void doCombat(Creature* caster, const Position& position) const;
bool setCallback(CallBackParam_t key); bool setCallback(CallBackParam_t key);
CallBack* getCallback(CallBackParam_t key); CallBack* getCallback(CallBackParam_t key);
@@ -332,9 +281,12 @@ class Combat
bool hasArea() const { bool hasArea() const {
return area != nullptr; return area != nullptr;
} }
void setCondition(const Condition* condition) { void addCondition(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);
@@ -344,13 +296,10 @@ class Combat
params.origin = origin; params.origin = origin;
} }
protected: private:
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, COMBATFUNC func, CombatDamage* data); static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, CombatFunction 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);
@@ -358,8 +307,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& list, Creature* caster, Tile* tile, const CombatParams& params); static void combatTileEffects(const SpectatorVec& spectators, Creature* caster, Tile* tile, const CombatParams& params);
CombatDamage getCombatDamage(Creature* creature) const; CombatDamage getCombatDamage(Creature* creature, Creature* target) const;
//configureable //configureable
CombatParams params; CombatParams params;
@@ -379,10 +328,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() final { MagicField* getMagicField() override {
return this; return this;
} }
const MagicField* getMagicField() const final { const MagicField* getMagicField() const override {
return this; return this;
} }
@@ -393,6 +342,13 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,13 +35,10 @@ enum ConditionAttr_t {
CONDITIONATTR_HEALTHGAIN, CONDITIONATTR_HEALTHGAIN,
CONDITIONATTR_MANATICKS, CONDITIONATTR_MANATICKS,
CONDITIONATTR_MANAGAIN, CONDITIONATTR_MANAGAIN,
CONDITIONATTR_DELAYED,
CONDITIONATTR_OWNER, CONDITIONATTR_OWNER,
CONDITIONATTR_CYCLE, CONDITIONATTR_INTERVALDATA,
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,
@@ -55,6 +52,8 @@ 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
@@ -71,9 +70,9 @@ class Condition
{ {
public: public:
Condition() = default; Condition() = default;
Condition(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) : Condition(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, 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), id(id) {} subId(subId), ticks(ticks), conditionType(type), isBuff(buff), id(id) {}
virtual ~Condition() = default; virtual ~Condition() = default;
virtual bool startCondition(Creature* creature); virtual bool startCondition(Creature* creature);
@@ -101,7 +100,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, uint32_t subId = 0); 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(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);
@@ -114,20 +113,23 @@ 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;
ConditionId_t id; bool isBuff;
virtual bool updateCondition(const Condition* addCondition); private:
ConditionId_t id;
}; };
class ConditionGeneric : public Condition class ConditionGeneric : public Condition
{ {
public: public:
ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0): ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0):
Condition(id, type, ticks, subId) {} Condition(id, type, ticks, buff, 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;
@@ -143,32 +145,35 @@ 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, uint32_t subId = 0) : ConditionAttributes(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
ConditionGeneric(id, type, ticks, subId) {} ConditionGeneric(id, type, ticks, buff, subId) {}
bool startCondition(Creature* creature) final; bool startCondition(Creature* creature) override;
bool executeCondition(Creature* creature, int32_t interval) final; bool executeCondition(Creature* creature, int32_t interval) override;
void endCondition(Creature* creature) final; void endCondition(Creature* creature) override;
void addCondition(Creature* creature, const Condition* condition) final; void addCondition(Creature* creature, const Condition* condition) override;
bool setParam(ConditionParam_t param, int32_t value) final; bool setParam(ConditionParam_t param, int32_t value) override;
ConditionAttributes* clone() const final { ConditionAttributes* clone() const override {
return new ConditionAttributes(*this); return new ConditionAttributes(*this);
} }
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) final; void serialize(PropWriteStream& propWriteStream) override;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
protected: private:
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);
@@ -178,23 +183,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, uint32_t subId = 0): ConditionRegeneration(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0):
ConditionGeneric(id, type, ticks, subId) {} ConditionGeneric(id, type, ticks, buff, subId) {}
void addCondition(Creature* creature, const Condition* addCondition) final; void addCondition(Creature* creature, const Condition* condition) override;
bool executeCondition(Creature* creature, int32_t interval) final; bool executeCondition(Creature* creature, int32_t interval) override;
bool setParam(ConditionParam_t param, int32_t value) final; bool setParam(ConditionParam_t param, int32_t value) override;
ConditionRegeneration* clone() const final { ConditionRegeneration* clone() const override {
return new ConditionRegeneration(*this); return new ConditionRegeneration(*this);
} }
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) final; void serialize(PropWriteStream& propWriteStream) override;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
protected: private:
uint32_t internalHealthTicks = 0; uint32_t internalHealthTicks = 0;
uint32_t internalManaTicks = 0; uint32_t internalManaTicks = 0;
@@ -207,23 +212,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, uint32_t subId = 0) : ConditionSoul(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
ConditionGeneric(id, type, ticks, subId) {} ConditionGeneric(id, type, ticks, buff, subId) {}
void addCondition(Creature* creature, const Condition* addCondition) final; void addCondition(Creature* creature, const Condition* condition) override;
bool executeCondition(Creature* creature, int32_t interval) final; bool executeCondition(Creature* creature, int32_t interval) override;
bool setParam(ConditionParam_t param, int32_t value) final; bool setParam(ConditionParam_t param, int32_t value) override;
ConditionSoul* clone() const final { ConditionSoul* clone() const override {
return new ConditionSoul(*this); return new ConditionSoul(*this);
} }
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) final; void serialize(PropWriteStream& propWriteStream) override;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
protected: private:
uint32_t internalSoulTicks = 0; uint32_t internalSoulTicks = 0;
uint32_t soulTicks = 0; uint32_t soulTicks = 0;
uint32_t soulGain = 0; uint32_t soulGain = 0;
@@ -232,13 +237,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, uint32_t subId = 0) : ConditionInvisible(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
ConditionGeneric(id, type, ticks, subId) {} ConditionGeneric(id, type, ticks, buff, subId) {}
bool startCondition(Creature* creature) final; bool startCondition(Creature* creature) override;
void endCondition(Creature* creature) final; void endCondition(Creature* creature) override;
ConditionInvisible* clone() const final { ConditionInvisible* clone() const override {
return new ConditionInvisible(*this); return new ConditionInvisible(*this);
} }
}; };
@@ -247,133 +252,170 @@ class ConditionDamage final : public Condition
{ {
public: public:
ConditionDamage() = default; ConditionDamage() = default;
ConditionDamage(ConditionId_t id, ConditionType_t type, uint32_t subId = 0) : ConditionDamage(ConditionId_t id, ConditionType_t type, bool buff = false, uint32_t subId = 0) :
Condition(id, type, 0, subId) { Condition(id, type, 0, buff, 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;
}
}
bool startCondition(Creature* creature) final; static void generateDamageList(int32_t amount, int32_t start, std::list<int32_t>& list);
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;
ConditionDamage* clone() const final { bool startCondition(Creature* creature) override;
bool executeCondition(Creature* creature, int32_t interval) override;
void endCondition(Creature* creature) override;
void addCondition(Creature* creature, const Condition* condition) override;
uint32_t getIcons() const override;
ConditionDamage* clone() const override {
return new ConditionDamage(*this); return new ConditionDamage(*this);
} }
bool setParam(ConditionParam_t param, int32_t value) final; bool setParam(ConditionParam_t param, int32_t value) override;
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) final; void serialize(PropWriteStream& propWriteStream) override;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
protected:
int32_t cycle = 0; private:
int32_t count = 0; int32_t maxDamage = 0;
int32_t max_count = 0; int32_t minDamage = 0;
int32_t factor_percent = -1; int32_t startDamage = 0;
int32_t hit_damage = 0; int32_t periodDamage = 0;
bool isFirstCycle = true; int32_t periodDamageTick = 0;
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) final; bool updateCondition(const Condition* addCondition) override;
}; };
class ConditionSpeed final : public Condition class ConditionSpeed final : public Condition
{ {
public: public:
ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId, int32_t changeSpeed) : ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, int32_t changeSpeed) :
Condition(id, type, ticks, subId), speedDelta(changeSpeed) {} Condition(id, type, ticks, buff, subId), speedDelta(changeSpeed) {}
bool startCondition(Creature* creature) final; bool startCondition(Creature* creature) override;
bool executeCondition(Creature* creature, int32_t interval) final; bool executeCondition(Creature* creature, int32_t interval) override;
void endCondition(Creature* creature) final; void endCondition(Creature* creature) override;
void addCondition(Creature* creature, const Condition* condition) final; void addCondition(Creature* creature, const Condition* condition) override;
uint32_t getIcons() const final; uint32_t getIcons() const override;
ConditionSpeed* clone() const final { ConditionSpeed* clone() const override {
return new ConditionSpeed(*this); return new ConditionSpeed(*this);
} }
void setVariation(int32_t newVariation) { bool setParam(ConditionParam_t param, int32_t value) override;
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) final; void serialize(PropWriteStream& propWriteStream) override;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
protected: private:
int32_t appliedSpeedDelta = 0; void getFormulaValues(int32_t var, int32_t& min, int32_t& max) const;
int32_t speedDelta = 0;
int32_t variation = 0; int32_t speedDelta;
//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, uint32_t subId = 0) : ConditionOutfit(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
Condition(id, type, ticks, subId) {} Condition(id, type, ticks, buff, subId) {}
bool startCondition(Creature* creature) final; bool startCondition(Creature* creature) override;
bool executeCondition(Creature* creature, int32_t interval) final; bool executeCondition(Creature* creature, int32_t interval) override;
void endCondition(Creature* creature) final; void endCondition(Creature* creature) override;
void addCondition(Creature* creature, const Condition* condition) final; void addCondition(Creature* creature, const Condition* condition) override;
ConditionOutfit* clone() const final { ConditionOutfit* clone() const override {
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) final; void serialize(PropWriteStream& propWriteStream) override;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
protected: private:
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, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) : ConditionLight(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) :
Condition(id, type, ticks, subId), lightInfo(lightlevel, lightcolor) {} Condition(id, type, ticks, buff, subId), lightInfo(lightlevel, lightcolor) {}
bool startCondition(Creature* creature) final; bool startCondition(Creature* creature) override;
bool executeCondition(Creature* creature, int32_t interval) final; bool executeCondition(Creature* creature, int32_t interval) override;
void endCondition(Creature* creature) final; void endCondition(Creature* creature) override;
void addCondition(Creature* creature, const Condition* addCondition) final; void addCondition(Creature* creature, const Condition* condition) override;
ConditionLight* clone() const final { ConditionLight* clone() const override {
return new ConditionLight(*this); return new ConditionLight(*this);
} }
bool setParam(ConditionParam_t param, int32_t value) final; bool setParam(ConditionParam_t param, int32_t value) override;
//serialization //serialization
void serialize(PropWriteStream& propWriteStream) final; void serialize(PropWriteStream& propWriteStream) override;
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final; bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
protected: private:
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +19,12 @@
#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"
@@ -29,6 +35,57 @@
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();
@@ -63,9 +120,10 @@ 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);
@@ -74,14 +132,19 @@ 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[TELEPORT_NEWBIES] = getGlobalBoolean(L, "teleportNewbies", true); boolean[CLASSIC_EQUIPMENT_SLOTS] = getGlobalBoolean(L, "classicEquipmentSlots", false);
boolean[STACK_CUMULATIVES] = getGlobalBoolean(L, "autoStackCumulatives", false); boolean[CLASSIC_ATTACK_SPEED] = getGlobalBoolean(L, "classicAttackSpeed", false);
boolean[BLOCK_HEIGHT] = getGlobalBoolean(L, "blockHeight", false); boolean[SCRIPTS_CONSOLE_LOGS] = getGlobalBoolean(L, "showScriptsLogInConsole", true);
boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false); boolean[SERVER_SAVE_NOTIFY_MESSAGE] = getGlobalBoolean(L, "serverSaveNotifyMessage", true);
boolean[SERVER_SAVE_CLEAN_MAP] = getGlobalBoolean(L, "serverSaveCleanMap", false);
boolean[SERVER_SAVE_CLOSE] = getGlobalBoolean(L, "serverSaveClose", false);
boolean[SERVER_SAVE_SHUTDOWN] = getGlobalBoolean(L, "serverSaveShutdown", true);
boolean[ONLINE_OFFLINE_CHARLIST] = getGlobalBoolean(L, "showOnlineStatusInCharlist", false);
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", "");
@@ -101,7 +164,9 @@ 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[BAN_LENGTH] = getGlobalNumber(L, "banLength", 30 * 24 * 60 * 60); integer[HOUSE_PRICE] = getGlobalNumber(L, "housePriceEachSQM", 1000);
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);
@@ -109,20 +174,14 @@ 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[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60); integer[FRAG_TIME] = getGlobalNumber(L, "timeToDecreaseFrags", 24 * 60 * 60 * 1000);
integer[RED_SKULL_TIME] = getGlobalNumber(L, "redSkullTime", 30 * 24 * 60 * 60); integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60 * 1000);
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[NEWBIE_TOWN] = getGlobalNumber(L, "newbieTownId", 1); integer[SERVER_SAVE_NOTIFY_DURATION] = getGlobalNumber(L, "serverSaveNotifyDuration", 5);
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);
@@ -138,11 +197,13 @@ 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 string[DUMMY_STR]; return dummyStr;
} }
return string[what]; return string[what];
} }
@@ -164,47 +225,3 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,10 @@
#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,
@@ -37,19 +34,24 @@ 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,
TELEPORT_NEWBIES, CLASSIC_EQUIPMENT_SLOTS,
STACK_CUMULATIVES, CLASSIC_ATTACK_SPEED,
BLOCK_HEIGHT, SCRIPTS_CONSOLE_LOGS,
DROP_ITEMS, SERVER_SAVE_NOTIFY_MESSAGE,
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,
@@ -82,7 +84,9 @@ class ConfigManager
RATE_LOOT, RATE_LOOT,
RATE_MAGIC, RATE_MAGIC,
RATE_SPAWN, RATE_SPAWN,
BAN_LENGTH, HOUSE_PRICE,
KILLS_TO_RED,
KILLS_TO_BLACK,
MAX_MESSAGEBUFFER, MAX_MESSAGEBUFFER,
ACTIONS_DELAY_INTERVAL, ACTIONS_DELAY_INTERVAL,
EX_ACTIONS_DELAY_INTERVAL, EX_ACTIONS_DELAY_INTERVAL,
@@ -90,23 +94,18 @@ 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,
NEWBIE_TOWN, SERVER_SAVE_NOTIFY_DURATION,
NEWBIE_LEVEL_THRESHOLD,
MONEY_RATE,
LAST_INTEGER_CONFIG /* this must be the last one */ LAST_INTEGER_CONFIG /* this must be the last one */
}; };
@@ -119,10 +118,6 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,51 +182,60 @@ 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(msg, shared_from_this()); protocol = service_port->make_protocol(recvChecksum == checksum, 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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;
typedef std::shared_ptr<Protocol> Protocol_ptr; using Protocol_ptr = std::shared_ptr<Protocol>;
class OutputMessage; class OutputMessage;
typedef std::shared_ptr<OutputMessage> OutputMessage_ptr; using OutputMessage_ptr = std::shared_ptr<OutputMessage>;
class Connection; class Connection;
typedef std::shared_ptr<Connection> Connection_ptr; using Connection_ptr = std::shared_ptr<Connection> ;
typedef std::weak_ptr<Connection> ConnectionWeak_ptr; using ConnectionWeak_ptr = std::weak_ptr<Connection>;
class ServiceBase; class ServiceBase;
typedef std::shared_ptr<ServiceBase> Service_ptr; using Service_ptr = std::shared_ptr<ServiceBase>;
class ServicePort; class ServicePort;
typedef std::shared_ptr<ServicePort> ServicePort_ptr; using ServicePort_ptr = std::shared_ptr<ServicePort>;
typedef std::shared_ptr<const ServicePort> ConstServicePort_ptr; using ConstServicePort_ptr = std::shared_ptr<const ServicePort>;
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();
protected: private:
ConnectionManager() = default; ConnectionManager() = default;
std::unordered_set<Connection_ptr> connections; std::unordered_set<Connection_ptr> connections;
@@ -78,12 +78,8 @@ 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),
connectionState = CONNECTION_STATE_OPEN; timeConnected(time(nullptr)) {}
receivedFirst = false;
packetsSent = 0;
timeConnected = time(nullptr);
}
~Connection(); ~Connection();
friend class ConnectionManager; friend class ConnectionManager;
@@ -128,10 +124,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; uint32_t packetsSent = 0;
bool connectionState; bool connectionState = CONNECTION_STATE_OPEN;
bool receivedFirst; bool receivedFirst = false;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +52,67 @@ 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 {
@@ -72,75 +133,192 @@ 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 = 4, TALKTYPE_PRIVATE_FROM = 4,
TALKTYPE_CHANNEL_Y = 5, // Yellow TALKTYPE_PRIVATE_TO = 5,
TALKTYPE_RVR_CHANNEL = 6, TALKTYPE_CHANNEL_Y = 7,
TALKTYPE_RVR_ANSWER = 7, TALKTYPE_CHANNEL_O = 8,
TALKTYPE_RVR_CONTINUE = 8, TALKTYPE_PRIVATE_NP = 10,
TALKTYPE_BROADCAST = 9, TALKTYPE_PRIVATE_PN = 12,
TALKTYPE_CHANNEL_R1 = 10, // Red - #c text TALKTYPE_BROADCAST = 13,
TALKTYPE_PRIVATE_RED = 11, // @name@text TALKTYPE_CHANNEL_R1 = 14, //red - #c text
TALKTYPE_CHANNEL_O = 12, // orange TALKTYPE_PRIVATE_RED_FROM = 15, //@name@text
TALKTYPE_CHANNEL_R2 = 13, // red anonymous - #d text TALKTYPE_PRIVATE_RED_TO = 16, //@name@text
TALKTYPE_MONSTER_YELL = 0x10, TALKTYPE_MONSTER_SAY = 36,
TALKTYPE_MONSTER_SAY = 0x11, TALKTYPE_MONSTER_YELL = 37,
TALKTYPE_CHANNEL_R2 = 0xFF, //#d
}; };
enum MessageClasses : uint8_t { enum MessageClasses : uint8_t {
MESSAGE_STATUS_CONSOLE_YELLOW = 0x01, //Yellow message in the console MESSAGE_STATUS_CONSOLE_BLUE = 4, /*FIXME Blue 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_CLASS_FIRST = MESSAGE_STATUS_CONSOLE_YELLOW, MESSAGE_STATUS_CONSOLE_RED = 13, /*Red message in the console*/
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 FluidTypes_t : uint8_t enum FluidColors_t : uint8_t {
{ FLUID_EMPTY,
FLUID_NONE = 0, FLUID_BLUE,
FLUID_RED,
FLUID_BROWN,
FLUID_GREEN,
FLUID_YELLOW,
FLUID_WHITE,
FLUID_PURPLE,
};
enum FluidTypes_t : uint8_t {
FLUID_NONE = FLUID_EMPTY,
FLUID_WATER = FLUID_BLUE,
FLUID_BLOOD = FLUID_RED,
FLUID_BEER = FLUID_BROWN,
FLUID_SLIME = FLUID_GREEN,
FLUID_LEMONADE = FLUID_YELLOW,
FLUID_MILK = FLUID_WHITE,
FLUID_MANA = FLUID_PURPLE,
FLUID_LIFE = FLUID_RED + 8,
FLUID_OIL = FLUID_BROWN + 8,
FLUID_URINE = FLUID_YELLOW + 8,
FLUID_COCONUTMILK = FLUID_WHITE + 8,
FLUID_WINE = FLUID_PURPLE + 8,
FLUID_MUD = FLUID_BROWN + 16,
FLUID_FRUITJUICE = FLUID_YELLOW + 16,
FLUID_LAVA = FLUID_RED + 24,
FLUID_RUM = FLUID_BROWN + 24,
FLUID_SWAMP = FLUID_GREEN + 24,
FLUID_TEA = FLUID_BROWN + 32,
FLUID_MEAD = FLUID_BROWN + 40,
};
const uint8_t reverseFluidMap[] = {
FLUID_EMPTY,
FLUID_WATER, FLUID_WATER,
FLUID_WINE, FLUID_MANA,
FLUID_BEER,
FLUID_EMPTY,
FLUID_BLOOD,
FLUID_SLIME,
FLUID_EMPTY,
FLUID_LEMONADE,
FLUID_MILK,
};
const uint8_t clientToServerFluidMap[] = {
FLUID_EMPTY,
FLUID_WATER,
FLUID_MANA,
FLUID_BEER, FLUID_BEER,
FLUID_MUD, FLUID_MUD,
FLUID_BLOOD, FLUID_BLOOD,
FLUID_SLIME, FLUID_SLIME,
FLUID_OIL,
FLUID_URINE,
FLUID_MILK,
FLUID_MANAFLUID,
FLUID_LIFEFLUID,
FLUID_LEMONADE,
FLUID_RUM, FLUID_RUM,
FLUID_COCONUTMILK, FLUID_LEMONADE,
FLUID_MILK,
FLUID_WINE,
FLUID_LIFE,
FLUID_URINE,
FLUID_OIL,
FLUID_FRUITJUICE, FLUID_FRUITJUICE,
FLUID_COCONUTMILK,
FLUID_TEA,
FLUID_MEAD,
}; };
enum FluidColor_t : uint8_t enum ClientFluidTypes_t : uint8_t {
{ CLIENTFLUID_EMPTY = 0,
FLUID_COLOR_NONE = 0, CLIENTFLUID_BLUE = 1,
FLUID_COLOR_BLUE = 1, CLIENTFLUID_PURPLE = 2,
FLUID_COLOR_PURPLE = 2, CLIENTFLUID_BROWN_1 = 3,
FLUID_COLOR_BROWN = 3, CLIENTFLUID_BROWN_2 = 4,
FLUID_COLOR_BROWN1 = 4, CLIENTFLUID_RED = 5,
FLUID_COLOR_RED = 5, CLIENTFLUID_GREEN = 6,
FLUID_COLOR_GREEN = 6, CLIENTFLUID_BROWN = 7,
FLUID_COLOR_BROWN2 = 7, CLIENTFLUID_YELLOW = 8,
FLUID_COLOR_YELLOW = 8, CLIENTFLUID_WHITE = 9,
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 {
@@ -155,8 +333,10 @@ 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 = 155, TEXTCOLOR_PURPLE = 154,
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,
@@ -173,6 +353,13 @@ 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 {
@@ -205,6 +392,7 @@ 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,
@@ -217,6 +405,8 @@ 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 {
@@ -224,54 +414,97 @@ 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_FIREFIELD_PVP_FULL = 2118, ITEM_BROWSEFIELD = 460, // for internal use
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_POISONFIELD_PVP = 2121, ITEM_FIREFIELD_PVP_FULL = 1487,
ITEM_POISONFIELD_PERSISTENT = 2127, ITEM_FIREFIELD_PVP_MEDIUM = 1488,
ITEM_POISONFIELD_NOPVP = 2134, ITEM_FIREFIELD_PVP_SMALL = 1489,
ITEM_FIREFIELD_PERSISTENT_FULL = 1492,
ITEM_FIREFIELD_PERSISTENT_MEDIUM = 1493,
ITEM_FIREFIELD_PERSISTENT_SMALL = 1494,
ITEM_FIREFIELD_NOPVP = 1500,
ITEM_ENERGYFIELD_PVP = 2122, ITEM_POISONFIELD_PVP = 1490,
ITEM_ENERGYFIELD_PERSISTENT = 2126, ITEM_POISONFIELD_PERSISTENT = 1496,
ITEM_ENERGYFIELD_NOPVP = 2135, ITEM_POISONFIELD_NOPVP = 1503,
ITEM_MAGICWALL = 2128, ITEM_ENERGYFIELD_PVP = 1491,
ITEM_MAGICWALL_PERSISTENT = 2128, ITEM_ENERGYFIELD_PERSISTENT = 1495,
ITEM_ENERGYFIELD_NOPVP = 1504,
ITEM_WILDGROWTH = 2130, ITEM_MAGICWALL = 1497,
ITEM_WILDGROWTH_PERSISTENT = 2130, ITEM_MAGICWALL_PERSISTENT = 1498,
ITEM_MAGICWALL_SAFE = 11098,
ITEM_GOLD_COIN = 3031, ITEM_WILDGROWTH = 1499,
ITEM_PLATINUM_COIN = 3035, ITEM_WILDGROWTH_PERSISTENT = 2721,
ITEM_CRYSTAL_COIN = 3043, ITEM_WILDGROWTH_SAFE = 11099,
ITEM_DEPOT = 3502, ITEM_BAG = 1987,
ITEM_LOCKER1 = 3497, ITEM_SHOPPING_BAG = 23782,
ITEM_MALE_CORPSE = 4240, ITEM_GOLD_COIN = 2148,
ITEM_FEMALE_CORPSE = 4247, ITEM_PLATINUM_COIN = 2152,
ITEM_CRYSTAL_COIN = 2160,
ITEM_STORE_COIN = 24774, // in-game store currency
ITEM_FULLSPLASH = 2886, ITEM_DEPOT = 2594,
ITEM_SMALLSPLASH = 2889, ITEM_LOCKER1 = 2589,
ITEM_INBOX = 14404,
ITEM_MARKET = 14405,
ITEM_STORE_INBOX = 26052,
ITEM_DEPOT_BOX_I = 25453,
ITEM_DEPOT_BOX_II = 25454,
ITEM_DEPOT_BOX_III = 25455,
ITEM_DEPOT_BOX_IV = 25456,
ITEM_DEPOT_BOX_V = 25457,
ITEM_DEPOT_BOX_VI = 25458,
ITEM_DEPOT_BOX_VII = 25459,
ITEM_DEPOT_BOX_VIII = 25460,
ITEM_DEPOT_BOX_IX = 25461,
ITEM_DEPOT_BOX_X = 25462,
ITEM_DEPOT_BOX_XI = 25463,
ITEM_DEPOT_BOX_XII = 25464,
ITEM_DEPOT_BOX_XIII = 25465,
ITEM_DEPOT_BOX_XIV = 25466,
ITEM_DEPOT_BOX_XV = 25467,
ITEM_DEPOT_BOX_XVI = 25468,
ITEM_DEPOT_BOX_XVII = 25469,
ITEM_PARCEL = 3503, ITEM_MALE_CORPSE = 3058,
ITEM_PARCEL_STAMPED = 3504, ITEM_FEMALE_CORPSE = 3065,
ITEM_LETTER = 3505,
ITEM_LETTER_STAMPED = 3506,
ITEM_LABEL = 3507,
ITEM_AMULETOFLOSS = 3057, ITEM_FULLSPLASH = 2016,
ITEM_SMALLSPLASH = 2019,
ITEM_DOCUMENT_RO = 2819, //read-only ITEM_PARCEL = 2595,
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 {
@@ -313,14 +546,12 @@ 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,
@@ -333,6 +564,7 @@ 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,
@@ -340,7 +572,6 @@ 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;
@@ -350,6 +581,11 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,16 +28,41 @@ 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) : Container::Container(uint16_t type, uint16_t size, bool unlocked /*= true*/, bool pagination /*= false*/) :
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()
{ {
for (Item* item : itemlist) { if (getID() == ITEM_BROWSEFIELD) {
item->setParent(nullptr); g_game.browseFields.erase(getTile());
item->decrementReferenceCounter();
for (Item* item : itemlist) {
item->setParent(parent);
}
} else {
for (Item* item : itemlist) {
item->setParent(nullptr);
item->decrementReferenceCounter();
}
} }
} }
@@ -62,7 +87,7 @@ Container* Container::getParentContainer()
bool Container::hasParent() const bool Container::hasParent() const
{ {
return dynamic_cast<const Container*>(getParent()) != nullptr; return getID() != ITEM_BROWSEFIELD && dynamic_cast<const Player*>(getParent()) == nullptr;
} }
void Container::addItem(Item* item) void Container::addItem(Item* item)
@@ -82,24 +107,22 @@ Attr_ReadValue Container::readAttr(AttrTypes_t attr, PropStream& propStream)
return Item::readAttr(attr, propStream); return Item::readAttr(attr, propStream);
} }
bool Container::unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream) bool Container::unserializeItemNode(OTB::Loader& loader, const OTB::Node& node, PropStream& propStream)
{ {
bool ret = Item::unserializeItemNode(f, node, propStream); bool ret = Item::unserializeItemNode(loader, node, propStream);
if (!ret) { if (!ret) {
return false; return false;
} }
uint32_t type; for (auto& itemNode : node.children) {
NODE nodeItem = f.getChildNode(node, type);
while (nodeItem) {
//load container items //load container items
if (type != OTBM_ITEM) { if (itemNode.type != OTBM_ITEM) {
// unknown type // unknown type
return false; return false;
} }
PropStream itemPropStream; PropStream itemPropStream;
if (!f.getProps(nodeItem, itemPropStream)) { if (!loader.getProps(itemNode, itemPropStream)) {
return false; return false;
} }
@@ -108,14 +131,12 @@ bool Container::unserializeItemNode(FileLoader& f, NODE node, PropStream& propSt
return false; return false;
} }
if (!item->unserializeItemNode(f, nodeItem, itemPropStream)) { if (!item->unserializeItemNode(loader, itemNode, itemPropStream)) {
return false; return false;
} }
addItem(item); addItem(item);
updateItemWeight(item->getWeight()); updateItemWeight(item->getWeight());
nodeItem = f.getNextNode(nodeItem, type);
} }
return true; return true;
} }
@@ -194,48 +215,48 @@ bool Container::isHoldingItem(const Item* item) const
void Container::onAddContainerItem(Item* item) void Container::onAddContainerItem(Item* item)
{ {
SpectatorVec list; SpectatorVec spectators;
g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2); g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2);
//send to client //send to client
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
spectator->getPlayer()->sendAddContainerItem(this, item); spectator->getPlayer()->sendAddContainerItem(this, item);
} }
//event methods //event methods
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
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 list; SpectatorVec spectators;
g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2); g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2);
//send to client //send to client
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
spectator->getPlayer()->sendUpdateContainerItem(this, index, newItem); spectator->getPlayer()->sendUpdateContainerItem(this, index, newItem);
} }
//event methods //event methods
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
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 list; SpectatorVec spectators;
g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2); g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2);
//send change to client //send change to client
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
spectator->getPlayer()->sendRemoveContainerItem(this, index); spectator->getPlayer()->sendRemoveContainerItem(this, index);
} }
//event methods //event methods
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
spectator->getPlayer()->onRemoveContainerItem(this, item); spectator->getPlayer()->onRemoveContainerItem(this, item);
} }
} }
@@ -250,6 +271,10 @@ 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;
@@ -270,6 +295,10 @@ 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();
} }
@@ -326,7 +355,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->isRune() && destItem->getItemCount() < 100) { if (item->equals(destItem) && 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;
@@ -369,9 +398,14 @@ 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;
@@ -386,8 +420,7 @@ 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
@@ -418,22 +451,19 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item**
} }
} }
if (g_config.getBoolean(ConfigManager::STACK_CUMULATIVES)) { bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags);
bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags); if (autoStack && item->isStackable() && item->getParent() != this) {
if (autoStack && item->isStackable() && item->getParent() != this) { //try find a suitable item to stack with
//try find a suitable item to stack with uint32_t n = 0;
uint32_t n = 0; for (Item* listItem : itemlist) {
for (Item* listItem : itemlist) { if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) {
if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) { *destItem = listItem;
*destItem = listItem; index = n;
index = n; return this;
return this;
}
++n;
} }
++n;
} }
} }
return this; return this;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +26,7 @@
#include "item.h" #include "item.h"
class Container; class Container;
class DepotChest;
class DepotLocker; class DepotLocker;
class ContainerIterator class ContainerIterator
@@ -38,7 +39,7 @@ class ContainerIterator
void advance(); void advance();
Item* operator*(); Item* operator*();
protected: private:
std::list<const Container*> over; std::list<const Container*> over;
ItemDeque::const_iterator cur; ItemDeque::const_iterator cur;
@@ -49,31 +50,32 @@ 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); Container(uint16_t type, uint16_t size, bool unlocked = true, bool pagination = false);
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 final; Item* clone() const override final;
Container* getContainer() final { Container* getContainer() override final {
return this; return this;
} }
const Container* getContainer() const final { const Container* getContainer() const override final {
return this; return this;
} }
virtual DepotLocker* getDepotLocker() override { virtual DepotLocker* getDepotLocker() {
return nullptr; return nullptr;
} }
virtual const DepotLocker* getDepotLocker() const override { virtual const DepotLocker* getDepotLocker() const {
return nullptr; return nullptr;
} }
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override; Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream) override; bool unserializeItemNode(OTB::Loader& loader, const OTB::Node& node, PropStream& propStream) override;
std::string getContentDescription() const; std::string getContentDescription() const;
size_t size() const { size_t size() const {
@@ -105,41 +107,60 @@ 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 final; uint32_t getWeight() const override 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 final; uint32_t flags) const override final;
ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override final;
Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem,
uint32_t& flags) final; uint32_t& flags) override final;
void addThing(Thing* thing) final; void addThing(Thing* thing) override final;
void addThing(int32_t index, Thing* thing) final; void addThing(int32_t index, Thing* thing) override final;
void addItemBack(Item* item); void addItemBack(Item* item);
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override final;
void replaceThing(uint32_t index, Thing* thing) final; void replaceThing(uint32_t index, Thing* thing) override final;
void removeThing(Thing* thing, uint32_t count) final; void removeThing(Thing* thing, uint32_t count) override final;
int32_t getThingIndex(const Thing* thing) const final; int32_t getThingIndex(const Thing* thing) const override final;
size_t getFirstIndex() const final; size_t getFirstIndex() const override final;
size_t getLastIndex() const final; size_t getLastIndex() const override final;
uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final; uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override final;
std::map<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const final; std::map<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const override final;
Thing* getThing(size_t index) const final; Thing* getThing(size_t index) const override 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) final; void internalAddThing(Thing* thing) override final;
void internalAddThing(uint32_t index, Thing* thing) final; void internalAddThing(uint32_t index, Thing* thing) override final;
void startDecaying() final; void startDecaying() override 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);
@@ -147,14 +168,6 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +25,10 @@
#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;
@@ -38,8 +42,7 @@ Creature::~Creature()
{ {
for (Creature* summon : summons) { for (Creature* summon : summons) {
summon->setAttackedCreature(nullptr); summon->setAttackedCreature(nullptr);
summon->setMaster(nullptr); summon->removeMaster();
summon->decrementReferenceCounter();
} }
for (Condition* condition : conditions) { for (Condition* condition : conditions) {
@@ -227,7 +230,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_SAY, "Hicks!", false); g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, "Hicks!", false);
} }
} }
} }
@@ -307,7 +310,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) == RETURNVALUE_NOERROR; localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx] = tile && tile->queryAdd(0, *this, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) == RETURNVALUE_NOERROR;
} }
} }
@@ -409,7 +412,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()) {
master->removeSummon(this); setMaster(nullptr);
} }
} else if (isMapLoaded) { } else if (isMapLoaded) {
if (creature->getPosition().z == getPosition().z) { if (creature->getPosition().z == getPosition().z) {
@@ -582,11 +585,6 @@ 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())) {
@@ -619,8 +617,7 @@ 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;
} }
@@ -642,6 +639,7 @@ 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();
@@ -651,8 +649,7 @@ 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;
} }
} }
@@ -676,7 +673,7 @@ void Creature::onDeath()
death(lastHitCreature); death(lastHitCreature);
if (master) { if (master) {
master->removeSummon(this); setMaster(nullptr);
} }
if (droppedCorpse) { if (droppedCorpse) {
@@ -686,41 +683,53 @@ 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)
{ {
Item* splash; if (!lootDrop && getMonster()) {
switch (getRace()) { if (master) {
case RACE_VENOM: //scripting event - onDeath
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME); const CreatureEventList& deathEvents = getCreatureEvents(CREATURE_EVENT_DEATH);
break; for (CreatureEvent* deathEvent : deathEvents) {
deathEvent->executeOnDeath(this, nullptr, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
}
}
case RACE_BLOOD: g_game.addMagicEffect(getPosition(), CONST_ME_POFF);
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD); } else {
break; Item* splash;
switch (getRace()) {
case RACE_VENOM:
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME);
break;
default: case RACE_BLOOD:
splash = nullptr; splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD);
break; break;
}
Tile* tile = getTile(); default:
splash = nullptr;
break;
}
if (splash) { Tile* tile = getTile();
g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
g_game.startDecay(splash);
}
Item* corpse = getCorpse(lastHitCreature, mostDamageCreature); if (splash) {
if (corpse) { g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT); g_game.startDecay(splash);
g_game.startDecay(corpse); }
}
//scripting event - onDeath Item* corpse = getCorpse(lastHitCreature, mostDamageCreature);
for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) { if (corpse) {
deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified); g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT);
} g_game.startDecay(corpse);
}
if (corpse) { //scripting event - onDeath
dropLoot(corpse->getContainer(), lastHitCreature); for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) {
deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
}
if (corpse) {
dropLoot(corpse->getContainer(), lastHitCreature);
}
} }
return true; return true;
@@ -781,28 +790,6 @@ 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) {
@@ -810,6 +797,30 @@ 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();
} }
@@ -833,16 +844,6 @@ 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();
@@ -1026,9 +1027,21 @@ 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;
} }
@@ -1051,15 +1064,8 @@ 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
@@ -1079,28 +1085,43 @@ void Creature::onGainExperience(uint64_t gainExp, Creature* target)
gainExp /= 2; gainExp /= 2;
master->onGainExperience(gainExp, target); master->onGainExperience(gainExp, target);
g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(gainExp)); SpectatorVec spectators;
} g_game.map.getSpectators(spectators, position, false, true);
if (spectators.empty()) {
void Creature::addSummon(Creature* creature) return;
{
creature->setDropLoot(false);
creature->setLossSkill(false);
creature->setMaster(this);
creature->incrementReferenceCounter();
summons.push_back(creature);
}
void Creature::removeSummon(Creature* creature)
{
auto cit = std::find(summons.begin(), summons.end(), creature);
if (cit != summons.end()) {
creature->setDropLoot(true);
creature->setLossSkill(true);
creature->setMaster(nullptr);
creature->decrementReferenceCounter();
summons.erase(cit);
} }
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) {
if (!newMaster && !master) {
return false;
}
if (newMaster) {
incrementReferenceCounter();
newMaster->summons.push_back(this);
}
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*/)
@@ -1259,20 +1280,21 @@ Condition* Creature::getCondition(ConditionType_t type, ConditionId_t conditionI
void Creature::executeConditions(uint32_t interval) void Creature::executeConditions(uint32_t interval)
{ {
auto it = conditions.begin(), end = conditions.end(); ConditionList tempConditions{ conditions };
while (it != end) { for (Condition* condition : tempConditions) {
Condition* condition = *it; auto it = std::find(conditions.begin(), conditions.end(), condition);
if (it == conditions.end()) {
continue;
}
if (!condition->executeCondition(this, interval)) { if (!condition->executeCondition(this, interval)) {
ConditionType_t type = condition->getType(); it = std::find(conditions.begin(), conditions.end(), condition);
if (it != conditions.end()) {
it = conditions.erase(it); conditions.erase(it);
condition->endCondition(this);
condition->endCondition(this); onEndCondition(condition->getType());
delete condition; delete condition;
}
onEndCondition(type);
} else {
++it;
} }
} }
} }
@@ -1289,7 +1311,7 @@ bool Creature::hasCondition(ConditionType_t type, uint32_t subId/* = 0*/) const
continue; continue;
} }
if (condition->getEndTime() >= timeNow) { if (condition->getEndTime() >= timeNow || condition->getTicks() == -1) {
return true; return true;
} }
} }
@@ -1326,8 +1348,18 @@ 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) {
@@ -1339,12 +1371,12 @@ int64_t Creature::getStepDuration() const
groundSpeed = 150; groundSpeed = 150;
} }
double duration = std::floor(1000 * groundSpeed) / stepSpeed; double duration = std::floor(1000 * groundSpeed / calculatedStepSpeed);
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 *= 3; stepDuration *= 2;
} }
return stepDuration; return stepDuration;
@@ -1364,15 +1396,18 @@ int64_t Creature::getEventStepTicks(bool onlyDelay) const
return ret; return ret;
} }
void Creature::getCreatureLight(LightInfo& light) const LightInfo Creature::getCreatureLight() const
{ {
light = internalLight; return internalLight;
}
void Creature::setCreatureLight(LightInfo lightInfo) {
internalLight = std::move(lightInfo);
} }
void Creature::setNormalCreatureLight() void Creature::setNormalCreatureLight()
{ {
internalLight.level = 0; internalLight = {};
internalLight.color = 0;
} }
bool Creature::registerCreatureEvent(const std::string& name) bool Creature::registerCreatureEvent(const std::string& name)
@@ -1440,6 +1475,10 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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"
typedef std::list<Condition*> ConditionList; using ConditionList = std::list<Condition*>;
typedef std::list<CreatureEvent*> CreatureEventList; using CreatureEventList = std::list<CreatureEvent*>;
enum slots_t : uint8_t { enum slots_t : uint8_t {
CONST_SLOT_WHEREEVER = 0, CONST_SLOT_WHEREEVER = 0,
@@ -66,7 +66,6 @@ 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;
@@ -83,7 +82,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;
protected: private:
Position targetPos; Position targetPos;
}; };
@@ -97,16 +96,18 @@ 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() final { Creature* getCreature() override final {
return this; return this;
} }
const Creature* getCreature() const final { const Creature* getCreature() const override final {
return this; return this;
} }
virtual Player* getPlayer() { virtual Player* getPlayer() {
@@ -131,6 +132,8 @@ 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;
@@ -169,13 +172,13 @@ class Creature : virtual public Thing
hiddenHealth = b; hiddenHealth = b;
} }
int32_t getThrowRange() const final { int32_t getThrowRange() const override final {
return 1; return 1;
} }
bool isPushable() const override { bool isPushable() const override {
return getWalkDelay() <= 0; return getWalkDelay() <= 0;
} }
bool isRemoved() const final { bool isRemoved() const override final {
return isInternalRemoved; return isInternalRemoved;
} }
virtual bool canSeeInvisibility() const { virtual bool canSeeInvisibility() const {
@@ -196,15 +199,11 @@ class Creature : virtual public Thing
return getSpeed(); return getSpeed();
} }
int32_t getSpeed() const { int32_t getSpeed() const {
if (baseSpeed == 0) { return baseSpeed + varSpeed;
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();
@@ -271,9 +270,15 @@ 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);
void setMaster(Creature* creature) { bool setMaster(Creature* newMaster);
master = creature;
void removeMaster() {
if (master) {
master = nullptr;
decrementReferenceCounter();
}
} }
bool isSummon() const { bool isSummon() const {
return master != nullptr; return master != nullptr;
} }
@@ -281,8 +286,6 @@ 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;
} }
@@ -290,9 +293,19 @@ class Creature : virtual public Thing
virtual int32_t getArmor() const { virtual int32_t getArmor() const {
return 0; return 0;
} }
virtual int32_t getDefense() { virtual int32_t getDefense() const {
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);
@@ -322,15 +335,12 @@ 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* attacker, int32_t healthGain); void gainHealth(Creature* healer, 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;
@@ -355,11 +365,9 @@ class Creature : virtual public Thing
virtual void onAttackedCreatureChangeZone(ZoneType_t zone); virtual void onAttackedCreatureChangeZone(ZoneType_t zone);
virtual void onIdleStatus(); virtual void onIdleStatus();
virtual void getCreatureLight(LightInfo& light) const; virtual LightInfo getCreatureLight() const;
virtual void setNormalCreatureLight(); virtual void setNormalCreatureLight();
void setCreatureLight(LightInfo light) { void setCreatureLight(LightInfo lightInfo);
internalLight = light;
}
virtual void onThink(uint32_t interval); virtual void onThink(uint32_t interval);
void onAttacking(uint32_t interval); void onAttacking(uint32_t interval);
@@ -382,7 +390,6 @@ 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&) {
@@ -395,30 +402,33 @@ class Creature : virtual public Thing
void setDropLoot(bool lootDrop) { void setDropLoot(bool lootDrop) {
this->lootDrop = lootDrop; this->lootDrop = lootDrop;
} }
void setLossSkill(bool skillLoss) { void setSkillLoss(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 final { Cylinder* getParent() const override final {
return tile; return tile;
} }
void setParent(Cylinder* cylinder) final { void setParent(Cylinder* cylinder) override final {
tile = static_cast<Tile*>(cylinder); tile = static_cast<Tile*>(cylinder);
position = tile->getPosition(); position = tile->getPosition();
} }
inline const Position& getPosition() const final { const Position& getPosition() const override final {
return position; return position;
} }
Tile* getTile() final { Tile* getTile() override final {
return tile; return tile;
} }
const Tile* getTile() const final { const Tile* getTile() const override final {
return tile; return tile;
} }
@@ -464,7 +474,7 @@ class Creature : virtual public Thing
Position position; Position position;
typedef std::map<uint32_t, CountBlock_t> CountMap; using CountMap = std::map<uint32_t, CountBlock_t>;
CountMap damageMap; CountMap damageMap;
std::list<Creature*> summons; std::list<Creature*> summons;
@@ -478,10 +488,6 @@ 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;
@@ -492,8 +498,7 @@ 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 = 70; uint32_t baseSpeed = 220;
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;
@@ -519,6 +524,7 @@ 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 {
@@ -550,7 +556,6 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,22 +29,15 @@ CreatureEvents::CreatureEvents() :
scriptInterface.initState(); scriptInterface.initState();
} }
CreatureEvents::~CreatureEvents() void CreatureEvents::clear(bool fromLua)
{ {
for (const auto& it : creatureEvents) { for (auto it = creatureEvents.begin(); it != creatureEvents.end(); ++it) {
delete it.second; if (fromLua == it->second.fromLua) {
} it->second.clearEvent();
} }
void CreatureEvents::clear()
{
//clear creature events
for (const auto& it : creatureEvents) {
it.second->clearEvent();
} }
//clear lua state reInitState(fromLua);
scriptInterface.reInitState();
} }
LuaScriptInterface& CreatureEvents::getScriptInterface() LuaScriptInterface& CreatureEvents::getScriptInterface()
@@ -57,17 +50,17 @@ std::string CreatureEvents::getScriptBaseName() const
return "creaturescripts"; return "creaturescripts";
} }
Event* CreatureEvents::getEvent(const std::string& nodeName) Event_ptr 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 new CreatureEvent(&scriptInterface); return Event_ptr(new CreatureEvent(&scriptInterface));
} }
bool CreatureEvents::registerEvent(Event* event, const pugi::xml_node&) bool CreatureEvents::registerEvent(Event_ptr event, const pugi::xml_node&)
{ {
CreatureEvent* creatureEvent = static_cast<CreatureEvent*>(event); //event is guaranteed to be a CreatureEvent CreatureEvent_ptr creatureEvent{static_cast<CreatureEvent*>(event.release())}; //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;
@@ -78,13 +71,37 @@ bool CreatureEvents::registerEvent(Event* 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); oldEvent->copyEvent(creatureEvent.get());
} }
return false; return false;
} else { } else {
//if not, register it normally //if not, register it normally
creatureEvents[creatureEvent->getName()] = creatureEvent; creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent));
return true;
}
}
bool CreatureEvents::registerLuaEvent(CreatureEvent* event)
{
CreatureEvent_ptr creatureEvent{ event };
if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) {
std::cout << "Error: [CreatureEvents::registerLuaEvent] Trying to register event without type!" << std::endl;
return false;
}
CreatureEvent* oldEvent = getEventByName(creatureEvent->getName(), false);
if (oldEvent) {
//if there was an event with the same that is not loaded
//(happens when realoading), it is reused
if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) {
oldEvent->copyEvent(creatureEvent.get());
}
return false;
} else {
//if not, register it normally
creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent));
return true; return true;
} }
} }
@@ -93,8 +110,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;
@@ -104,8 +121,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;
} }
} }
@@ -117,8 +134,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;
} }
} }
@@ -129,9 +146,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 (const auto& it : creatureEvents) { for (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;
} }
} }
@@ -177,6 +194,10 @@ 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") {
@@ -217,6 +238,12 @@ 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";
@@ -248,7 +275,7 @@ void CreatureEvent::clearEvent()
loaded = false; loaded = false;
} }
bool CreatureEvent::executeOnLogin(Player* player) bool CreatureEvent::executeOnLogin(Player* player) const
{ {
//onLogin(player) //onLogin(player)
if (!scriptInterface->reserveScriptEnv()) { if (!scriptInterface->reserveScriptEnv()) {
@@ -267,7 +294,7 @@ bool CreatureEvent::executeOnLogin(Player* player)
return scriptInterface->callFunction(1); return scriptInterface->callFunction(1);
} }
bool CreatureEvent::executeOnLogout(Player* player) bool CreatureEvent::executeOnLogout(Player* player) const
{ {
//onLogout(player) //onLogout(player)
if (!scriptInterface->reserveScriptEnv()) { if (!scriptInterface->reserveScriptEnv()) {
@@ -419,9 +446,56 @@ 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, value, type, min, max, origin) //onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, 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;
@@ -438,8 +512,7 @@ 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);
} }
@@ -447,16 +520,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 {
else { damage.primary.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
damage.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4)); damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
damage.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3); damage.secondary.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
damage.min = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2)); damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
damage.max = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
lua_pop(L, 4); lua_pop(L, 4);
if (damage.type != COMBAT_HEALING) { if (damage.primary.type != COMBAT_HEALING) {
damage.value = -damage.value; damage.primary.value = -damage.primary.value;
damage.secondary.value = -damage.secondary.value;
} }
} }
@@ -464,7 +537,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, value, type, min, max, origin) //onManaChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, 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;
@@ -481,8 +554,7 @@ 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);
} }
@@ -490,9 +562,12 @@ 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 {
else { damage.primary.value = LuaScriptInterface::getNumber<int32_t>(L, -4);
damage = LuaScriptInterface::getCombatDamage(L); damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
damage.secondary.value = LuaScriptInterface::getNumber<int32_t>(L, -2);
damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
lua_pop(L, 4);
} }
scriptInterface->resetScriptEnv(); scriptInterface->resetScriptEnv();

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +24,9 @@
#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,
@@ -33,18 +36,69 @@ 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; class CreatureEvent final : public Event
{
public:
explicit CreatureEvent(LuaScriptInterface* interface);
bool configureEvent(const pugi::xml_node& node) override;
CreatureEventType_t getEventType() const {
return type;
}
void setEventType(CreatureEventType_t eventType) {
type = eventType;
}
const std::string& getName() const {
return eventName;
}
void setName(const std::string& name) {
eventName = name;
}
bool isLoaded() const {
return loaded;
}
void setLoaded(bool b) {
loaded = b;
}
void clearEvent();
void copyEvent(CreatureEvent* creatureEvent);
//scripting
bool executeOnLogin(Player* player) const;
bool executeOnLogout(Player* player) const;
bool executeOnThink(Creature* creature, uint32_t interval);
bool executeOnPrepareDeath(Creature* creature, Creature* killer);
bool executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified);
void executeOnKill(Creature* creature, Creature* target);
bool executeAdvance(Player* player, skills_t, uint32_t, uint32_t);
void executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId);
bool executeTextEdit(Player* player, Item* item, const std::string& text);
void executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage);
void executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage);
void executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer);
//
private:
std::string getScriptEventName() const override;
std::string eventName;
CreatureEventType_t type;
bool loaded;
};
class 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;
@@ -57,59 +111,20 @@ class CreatureEvents final : public BaseEvents
CreatureEvent* getEventByName(const std::string& name, bool forceLoaded = true); CreatureEvent* getEventByName(const std::string& name, bool forceLoaded = true);
protected: bool registerLuaEvent(CreatureEvent* event);
LuaScriptInterface& getScriptInterface() final; void clear(bool fromLua) override final;
std::string getScriptBaseName() const final;
Event* getEvent(const std::string& nodeName) final; private:
bool registerEvent(Event* event, const pugi::xml_node& node) final; LuaScriptInterface& getScriptInterface() override;
void clear() final; std::string getScriptBaseName() const override;
Event_ptr getEvent(const std::string& nodeName) override;
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
//creature events //creature events
typedef std::map<std::string, CreatureEvent*> CreatureEventList; using CreatureEventMap = std::map<std::string, CreatureEvent>;
CreatureEventList creatureEvents; CreatureEventMap 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,7 +37,6 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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;
typedef std::shared_ptr<DBResult> DBResult_ptr; using DBResult_ptr = std::shared_ptr<DBResult>;
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;
} }
protected: private:
/** /**
* Transaction related methods. * Transaction related methods.
* *
@@ -129,7 +129,6 @@ 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;
@@ -195,7 +194,7 @@ class DBInsert
bool addRow(std::ostringstream& row); bool addRow(std::ostringstream& row);
bool execute(); bool execute();
protected: private:
std::string query; std::string query;
std::string values; std::string values;
size_t length; size_t length;
@@ -208,7 +207,7 @@ class DBTransaction
~DBTransaction() { ~DBTransaction() {
if (state == STATE_START) { if (state == STATE_START) {
Database::getInstance()->rollback(); Database::getInstance().rollback();
} }
} }
@@ -218,7 +217,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() {
@@ -226,15 +225,15 @@ class DBTransaction
return false; return false;
} }
state = STEATE_COMMIT; state = STATE_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,
STEATE_COMMIT, STATE_COMMIT,
}; };
TransactionStates_t state = STATE_NO_START; TransactionStates_t state = STATE_NO_START;

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,13 +85,68 @@ 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;
} }
@@ -102,16 +157,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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,12 +24,13 @@
class DatabaseManager class DatabaseManager
{ {
public: public:
static bool tableExists(const std::string& table); static bool tableExists(const std::string& tableName);
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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(const std::string& query, const std::function<void(DBResult_ptr, bool)>& callback/* = nullptr*/, bool store/* = false*/) void DatabaseTasks::addTask(std::string query, 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(query, callback, store); tasks.emplace_back(std::move(query), std::move(callback), store);
} }
taskLock.unlock(); taskLock.unlock();
@@ -85,9 +85,13 @@ 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()) {
runTask(tasks.front()); auto task = std::move(tasks.front());
tasks.pop_front(); tasks.pop_front();
guard.unlock();
runTask(task);
guard.lock();
} }
} }
@@ -95,7 +99,7 @@ void DatabaseTasks::shutdown()
{ {
taskLock.lock(); taskLock.lock();
setState(THREAD_STATE_TERMINATED); setState(THREAD_STATE_TERMINATED);
flush();
taskLock.unlock(); taskLock.unlock();
flush();
taskSignal.notify_one(); taskSignal.notify_one();
} }

View File

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

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 = "Sabrehaven"; static constexpr auto STATUS_SERVER_NAME = "The Forgotten Server";
static constexpr auto STATUS_SERVER_VERSION = "1.0"; static constexpr auto STATUS_SERVER_VERSION = "1.3";
static constexpr auto STATUS_SERVER_DEVELOPERS = "OTLand community & Sabrehaven Developers Team"; static constexpr auto STATUS_SERVER_DEVELOPERS = "Mark Samman";
static constexpr auto CLIENT_VERSION_MIN = 781; static constexpr auto CLIENT_VERSION_MIN = 1097;
static constexpr auto CLIENT_VERSION_MAX = 781; static constexpr auto CLIENT_VERSION_MAX = 1098;
static constexpr auto CLIENT_VERSION_STR = "7.81"; static constexpr auto CLIENT_VERSION_STR = "10.98";
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,7 +60,6 @@ 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

82
src/depotchest.cpp Normal file
View File

@@ -0,0 +1,82 @@
/**
* 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;
}

57
src/depotchest.h Normal file
View File

@@ -0,0 +1,57 @@
/**
* 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,12 +20,9 @@
#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, 30), depotId(0) {} Container(type, 3), depotId(0) {}
Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream) Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream)
{ {
@@ -38,40 +35,9 @@ Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream)
return Item::readAttr(attr, propStream); return Item::readAttr(attr, propStream);
} }
ReturnValue DepotLocker::queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor) const ReturnValue DepotLocker::queryAdd(int32_t, const Thing&, uint32_t, uint32_t, Creature*) const
{ {
const Item* item = thing.getItem(); return RETURNVALUE_NOTENOUGHROOM;
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)
@@ -87,3 +53,12 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,21 +21,24 @@
#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() final { DepotLocker* getDepotLocker() override {
return this; return this;
} }
const DepotLocker* getDepotLocker() const final { const DepotLocker* getDepotLocker() const override {
return this; return this;
} }
void removeInbox(Inbox* inbox);
//serialization //serialization
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final; Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
uint16_t getDepotId() const { uint16_t getDepotId() const {
return depotId; return depotId;
@@ -46,12 +49,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 final; uint32_t flags, Creature* actor = nullptr) const override;
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
bool canRemove() const final { bool canRemove() const override {
return false; return false;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +20,43 @@
#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,
@@ -30,7 +67,7 @@ enum itemAttrTypes : uint32_t {
ITEM_ATTRIBUTE_NONE, ITEM_ATTRIBUTE_NONE,
ITEM_ATTRIBUTE_ACTIONID = 1 << 0, ITEM_ATTRIBUTE_ACTIONID = 1 << 0,
ITEM_ATTRIBUTE_MOVEMENTID = 1 << 1, ITEM_ATTRIBUTE_UNIQUEID = 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,
@@ -41,26 +78,60 @@ 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_ARMOR = 1 << 12, ITEM_ATTRIBUTE_EXTRADEFENSE = 1 << 12,
ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 13, ITEM_ATTRIBUTE_ARMOR = 1 << 13,
ITEM_ATTRIBUTE_OWNER = 1 << 14, ITEM_ATTRIBUTE_HITCHANCE = 1 << 14,
ITEM_ATTRIBUTE_DURATION = 1 << 15, ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 15,
ITEM_ATTRIBUTE_DECAYSTATE = 1 << 16, ITEM_ATTRIBUTE_OWNER = 1 << 16,
ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 17, ITEM_ATTRIBUTE_DURATION = 1 << 17,
ITEM_ATTRIBUTE_CHARGES = 1 << 18, ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18,
ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 19, ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 19,
ITEM_ATTRIBUTE_DOORID = 1 << 20, ITEM_ATTRIBUTE_CHARGES = 1 << 20,
ITEM_ATTRIBUTE_KEYNUMBER = 1 << 21, ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 21,
ITEM_ATTRIBUTE_KEYHOLENUMBER = 1 << 22, ITEM_ATTRIBUTE_DOORID = 1 << 22,
ITEM_ATTRIBUTE_DOORQUESTNUMBER = 1 << 23, ITEM_ATTRIBUTE_DECAYTO = 1 << 23,
ITEM_ATTRIBUTE_DOORQUESTVALUE = 1 << 24,
ITEM_ATTRIBUTE_DOORLEVEL = 1 << 25, ITEM_ATTRIBUTE_CUSTOM = 1U << 31
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 {
@@ -75,6 +146,20 @@ 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,
@@ -89,6 +174,7 @@ 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 {
@@ -103,8 +189,11 @@ 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 = 10 COMBAT_COUNT = 12
}; };
enum CombatParam_t { enum CombatParam_t {
@@ -118,14 +207,6 @@ 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 {
@@ -153,6 +234,7 @@ 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,
@@ -178,13 +260,16 @@ 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_CYCLE = 47, CONDITION_PARAM_DISABLE_DEFENSE = 47,
CONDITION_PARAM_HIT_DAMAGE = 48, CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE = 48,
CONDITION_PARAM_COUNT = 49, CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT = 49,
CONDITION_PARAM_MAX_COUNT = 50, CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE = 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 {
@@ -220,6 +305,18 @@ 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,
@@ -233,24 +330,31 @@ 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_HASTE = 1 << 3, CONDITION_BLEEDING = 1 << 3,
CONDITION_PARALYZE = 1 << 4, CONDITION_HASTE = 1 << 4,
CONDITION_OUTFIT = 1 << 5, CONDITION_PARALYZE = 1 << 5,
CONDITION_INVISIBLE = 1 << 6, CONDITION_OUTFIT = 1 << 6,
CONDITION_LIGHT = 1 << 7, CONDITION_INVISIBLE = 1 << 7,
CONDITION_MANASHIELD = 1 << 8, CONDITION_LIGHT = 1 << 8,
CONDITION_INFIGHT = 1 << 9, CONDITION_MANASHIELD = 1 << 9,
CONDITION_DRUNK = 1 << 10, CONDITION_INFIGHT = 1 << 10,
CONDITION_REGENERATION = 1 << 11, CONDITION_DRUNK = 1 << 11,
CONDITION_SOUL = 1 << 12, CONDITION_EXHAUST_WEAPON = 1 << 12, // unused
CONDITION_MUTED = 1 << 13, CONDITION_REGENERATION = 1 << 13,
CONDITION_CHANNELMUTEDTICKS = 1 << 14, CONDITION_SOUL = 1 << 14,
CONDITION_YELLTICKS = 1 << 15, CONDITION_DROWN = 1 << 15,
CONDITION_ATTRIBUTES = 1 << 16, CONDITION_MUTED = 1 << 16,
CONDITION_EXHAUST = 1 << 17, CONDITION_CHANNELMUTEDTICKS = 1 << 17,
CONDITION_PACIFIED = 1 << 18, CONDITION_YELLTICKS = 1 << 18,
CONDITION_AGGRESSIVE = 1 << 19, CONDITION_ATTRIBUTES = 1 << 19,
CONDITION_DROWN = 1 << 20, CONDITION_FREEZING = 1 << 20,
CONDITION_DAZZLED = 1 << 21,
CONDITION_CURSED = 1 << 22,
CONDITION_EXHAUST_COMBAT = 1 << 23, // unused
CONDITION_EXHAUST_HEAL = 1 << 24, // unused
CONDITION_PACIFIED = 1 << 25,
CONDITION_SPELLCOOLDOWN = 1 << 26,
CONDITION_SPELLGROUPCOOLDOWN = 1 << 27,
}; };
enum ConditionId_t : int8_t { enum ConditionId_t : int8_t {
@@ -276,11 +380,7 @@ enum PlayerSex_t : uint8_t {
}; };
enum Vocation_t : uint16_t { enum Vocation_t : uint16_t {
VOCATION_NONE, VOCATION_NONE = 0
VOCATION_SORCERER = 1 << 0,
VOCATION_DRUID = 1 << 1,
VOCATION_PALADIN = 1 << 2,
VOCATION_KNIGHT = 1 << 3,
}; };
enum ReturnValue { enum ReturnValue {
@@ -343,10 +443,12 @@ enum ReturnValue {
RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL, RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL,
RETURNVALUE_CANNOTCONJUREITEMHERE, RETURNVALUE_CANNOTCONJUREITEMHERE,
RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS, RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS,
RETURNVALUE_NAMEISTOOAMBIGIOUS, RETURNVALUE_NAMEISTOOAMBIGUOUS,
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,
@@ -354,9 +456,43 @@ 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;
@@ -371,6 +507,85 @@ 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,
@@ -382,20 +597,31 @@ enum CombatOrigin
struct CombatDamage struct CombatDamage
{ {
CombatType_t type; struct {
int32_t value; CombatType_t type;
int32_t min; int32_t value;
int32_t max; } primary, secondary;
CombatOrigin origin;
CombatOrigin origin;
CombatDamage() CombatDamage()
{ {
origin = ORIGIN_NONE; origin = ORIGIN_NONE;
type = COMBAT_NONE; primary.type = secondary.type = COMBAT_NONE;
value = 0; primary.value = secondary.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) 2016 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,39 +29,9 @@
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;
@@ -71,6 +41,8 @@ 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()) {
@@ -91,80 +63,69 @@ 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") {
creatureOnChangeOutfit = event; info.creatureOnChangeOutfit = event;
} } else if (methodName == "onAreaCombat") {
else if (methodName == "onAreaCombat") { info.creatureOnAreaCombat = event;
creatureOnAreaCombat = event; } else if (methodName == "onTargetCombat") {
} info.creatureOnTargetCombat = event;
else if (methodName == "onTargetCombat") { } else {
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") {
partyOnJoin = event; info.partyOnJoin = event;
} } else if (methodName == "onLeave") {
else if (methodName == "onLeave") { info.partyOnLeave = event;
partyOnLeave = event; } else if (methodName == "onDisband") {
} info.partyOnDisband = event;
else if (methodName == "onDisband") { } else if (methodName == "onShareExperience") {
partyOnDisband = event; info.partyOnShareExperience = 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") {
else if (className == "Player") { if (methodName == "onBrowseField") {
if (methodName == "onLook") { info.playerOnBrowseField = event;
playerOnLook = event; } else if (methodName == "onLook") {
} info.playerOnLook = event;
else if (methodName == "onLookInBattleList") { } else if (methodName == "onLookInBattleList") {
playerOnLookInBattleList = event; info.playerOnLookInBattleList = event;
} } else if (methodName == "onLookInTrade") {
else if (methodName == "onLookInTrade") { info.playerOnLookInTrade = event;
playerOnLookInTrade = event; } else if (methodName == "onLookInShop") {
} info.playerOnLookInShop = event;
else if (methodName == "onTradeRequest") { } else if (methodName == "onTradeRequest") {
playerOnTradeRequest = event; info.playerOnTradeRequest = event;
} } else if (methodName == "onTradeAccept") {
else if (methodName == "onTradeAccept") { info.playerOnTradeAccept = event;
playerOnTradeAccept = event; } else if (methodName == "onMoveItem") {
} info.playerOnMoveItem = event;
else if (methodName == "onMoveItem") { } else if (methodName == "onItemMoved") {
playerOnMoveItem = event; info.playerOnItemMoved = event;
} } else if (methodName == "onMoveCreature") {
else if (methodName == "onItemMoved") { info.playerOnMoveCreature = event;
playerOnItemMoved = event; } else if (methodName == "onReportRuleViolation") {
} info.playerOnReportRuleViolation = event;
else if (methodName == "onMoveCreature") { } else if (methodName == "onReportBug") {
playerOnMoveCreature = event; info.playerOnReportBug = event;
} } else if (methodName == "onTurn") {
else if (methodName == "onReportBug") { info.playerOnTurn = event;
playerOnReportBug = event; } else if (methodName == "onGainExperience") {
} info.playerOnGainExperience = event;
else if (methodName == "onTurn") { } else if (methodName == "onLoseExperience") {
playerOnTurn = event; info.playerOnLoseExperience = event;
} } else if (methodName == "onGainSkillTries") {
else if (methodName == "onGainExperience") { info.playerOnGainSkillTries = event;
playerOnGainExperience = event; } else {
}
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") {
else { if (methodName == "onDropLoot") {
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;
} }
} }
@@ -175,7 +136,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 (creatureOnChangeOutfit == -1) { if (info.creatureOnChangeOutfit == -1) {
return true; return true;
} }
@@ -185,10 +146,10 @@ bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& out
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(creatureOnChangeOutfit, &scriptInterface); env->setScriptId(info.creatureOnChangeOutfit, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(creatureOnChangeOutfit); scriptInterface.pushFunction(info.creatureOnChangeOutfit);
LuaScriptInterface::pushUserdata<Creature>(L, creature); LuaScriptInterface::pushUserdata<Creature>(L, creature);
LuaScriptInterface::setCreatureMetatable(L, -1, creature); LuaScriptInterface::setCreatureMetatable(L, -1, creature);
@@ -201,7 +162,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 (creatureOnAreaCombat == -1) { if (info.creatureOnAreaCombat == -1) {
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
@@ -211,16 +172,15 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(creatureOnAreaCombat, &scriptInterface); env->setScriptId(info.creatureOnAreaCombat, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(creatureOnAreaCombat); scriptInterface.pushFunction(info.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);
} }
@@ -233,8 +193,7 @@ 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);
} }
@@ -246,7 +205,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 (creatureOnTargetCombat == -1) { if (info.creatureOnTargetCombat == -1) {
return RETURNVALUE_NOERROR; return RETURNVALUE_NOERROR;
} }
@@ -256,16 +215,15 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(creatureOnTargetCombat, &scriptInterface); env->setScriptId(info.creatureOnTargetCombat, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(creatureOnTargetCombat); scriptInterface.pushFunction(info.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);
} }
@@ -276,8 +234,7 @@ 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);
} }
@@ -290,7 +247,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 (partyOnJoin == -1) { if (info.partyOnJoin == -1) {
return true; return true;
} }
@@ -300,10 +257,10 @@ bool Events::eventPartyOnJoin(Party* party, Player* player)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnJoin, &scriptInterface); env->setScriptId(info.partyOnJoin, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnJoin); scriptInterface.pushFunction(info.partyOnJoin);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::setMetatable(L, -1, "Party");
@@ -317,7 +274,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 (partyOnLeave == -1) { if (info.partyOnLeave == -1) {
return true; return true;
} }
@@ -327,10 +284,10 @@ bool Events::eventPartyOnLeave(Party* party, Player* player)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnLeave, &scriptInterface); env->setScriptId(info.partyOnLeave, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnLeave); scriptInterface.pushFunction(info.partyOnLeave);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::setMetatable(L, -1, "Party");
@@ -344,7 +301,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 (partyOnDisband == -1) { if (info.partyOnDisband == -1) {
return true; return true;
} }
@@ -354,10 +311,10 @@ bool Events::eventPartyOnDisband(Party* party)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnDisband, &scriptInterface); env->setScriptId(info.partyOnDisband, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnDisband); scriptInterface.pushFunction(info.partyOnDisband);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::setMetatable(L, -1, "Party");
@@ -368,7 +325,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 (partyOnShareExperience == -1) { if (info.partyOnShareExperience == -1) {
return; return;
} }
@@ -378,10 +335,10 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(partyOnShareExperience, &scriptInterface); env->setScriptId(info.partyOnShareExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(partyOnShareExperience); scriptInterface.pushFunction(info.partyOnShareExperience);
LuaScriptInterface::pushUserdata<Party>(L, party); LuaScriptInterface::pushUserdata<Party>(L, party);
LuaScriptInterface::setMetatable(L, -1, "Party"); LuaScriptInterface::setMetatable(L, -1, "Party");
@@ -390,8 +347,7 @@ 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);
} }
@@ -399,10 +355,37 @@ 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 (playerOnLook == -1) { if (info.playerOnLook == -1) {
return; return;
} }
@@ -412,10 +395,10 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing*
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLook, &scriptInterface); env->setScriptId(info.playerOnLook, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLook); scriptInterface.pushFunction(info.playerOnLook);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -423,12 +406,10 @@ 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);
} }
@@ -441,7 +422,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 (playerOnLookInBattleList == -1) { if (info.playerOnLookInBattleList == -1) {
return; return;
} }
@@ -451,10 +432,10 @@ void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, i
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLookInBattleList, &scriptInterface); env->setScriptId(info.playerOnLookInBattleList, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLookInBattleList); scriptInterface.pushFunction(info.playerOnLookInBattleList);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -470,7 +451,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 (playerOnLookInTrade == -1) { if (info.playerOnLookInTrade == -1) {
return; return;
} }
@@ -480,10 +461,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLookInTrade, &scriptInterface); env->setScriptId(info.playerOnLookInTrade, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLookInTrade); scriptInterface.pushFunction(info.playerOnLookInTrade);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -499,10 +480,39 @@ 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 (playerOnMoveItem == -1) { if (info.playerOnMoveItem == -1) {
return true; return true;
} }
@@ -512,10 +522,10 @@ bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, c
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnMoveItem, &scriptInterface); env->setScriptId(info.playerOnMoveItem, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnMoveItem); scriptInterface.pushFunction(info.playerOnMoveItem);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -536,7 +546,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 (playerOnItemMoved == -1) { if (info.playerOnItemMoved == -1) {
return; return;
} }
@@ -546,10 +556,10 @@ void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count,
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnItemMoved, &scriptInterface); env->setScriptId(info.playerOnItemMoved, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnItemMoved); scriptInterface.pushFunction(info.playerOnItemMoved);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -570,7 +580,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 (playerOnMoveCreature == -1) { if (info.playerOnMoveCreature == -1) {
return true; return true;
} }
@@ -580,10 +590,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnMoveCreature, &scriptInterface); env->setScriptId(info.playerOnMoveCreature, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnMoveCreature); scriptInterface.pushFunction(info.playerOnMoveCreature);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -597,10 +607,42 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const
return scriptInterface.callFunction(4); return scriptInterface.callFunction(4);
} }
bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position) void Events::eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation)
{
// Player:onReportRuleViolation(targetName, reportType, reportReason, comment, translation)
if (info.playerOnReportRuleViolation == -1) {
return;
}
if (!scriptInterface.reserveScriptEnv()) {
std::cout << "[Error - Events::eventPlayerOnReportRuleViolation] Call stack overflow" << std::endl;
return;
}
ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(info.playerOnReportRuleViolation, &scriptInterface);
lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(info.playerOnReportRuleViolation);
LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player");
LuaScriptInterface::pushString(L, targetName);
lua_pushnumber(L, reportType);
lua_pushnumber(L, reportReason);
LuaScriptInterface::pushString(L, comment);
LuaScriptInterface::pushString(L, translation);
scriptInterface.callVoidFunction(6);
}
bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position, uint8_t category)
{ {
// Player:onReportBug(message, position, category) // Player:onReportBug(message, position, category)
if (playerOnReportBug == -1) { if (info.playerOnReportBug == -1) {
return true; return true;
} }
@@ -610,24 +652,25 @@ bool Events::eventPlayerOnReportBug(Player* player, const std::string& message,
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnReportBug, &scriptInterface); env->setScriptId(info.playerOnReportBug, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnReportBug); scriptInterface.pushFunction(info.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(3); return scriptInterface.callFunction(4);
} }
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 (playerOnTurn == -1) { if (info.playerOnTurn == -1) {
return true; return true;
} }
@@ -637,10 +680,10 @@ bool Events::eventPlayerOnTurn(Player* player, Direction direction)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnTurn, &scriptInterface); env->setScriptId(info.playerOnTurn, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnTurn); scriptInterface.pushFunction(info.playerOnTurn);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -653,7 +696,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 (playerOnTradeRequest == -1) { if (info.playerOnTradeRequest == -1) {
return true; return true;
} }
@@ -663,10 +706,10 @@ bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* ite
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnTradeRequest, &scriptInterface); env->setScriptId(info.playerOnTradeRequest, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnTradeRequest); scriptInterface.pushFunction(info.playerOnTradeRequest);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -683,7 +726,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 (playerOnTradeAccept == -1) { if (info.playerOnTradeAccept == -1) {
return true; return true;
} }
@@ -693,10 +736,10 @@ bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnTradeAccept, &scriptInterface); env->setScriptId(info.playerOnTradeAccept, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnTradeAccept); scriptInterface.pushFunction(info.playerOnTradeAccept);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -717,7 +760,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 (playerOnGainExperience == -1) { if (info.playerOnGainExperience == -1) {
return; return;
} }
@@ -727,10 +770,10 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnGainExperience, &scriptInterface); env->setScriptId(info.playerOnGainExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnGainExperience); scriptInterface.pushFunction(info.playerOnGainExperience);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -738,8 +781,7 @@ 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);
} }
@@ -748,8 +790,7 @@ 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);
} }
@@ -760,7 +801,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 (playerOnLoseExperience == -1) { if (info.playerOnLoseExperience == -1) {
return; return;
} }
@@ -770,10 +811,10 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnLoseExperience, &scriptInterface); env->setScriptId(info.playerOnLoseExperience, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnLoseExperience); scriptInterface.pushFunction(info.playerOnLoseExperience);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -782,8 +823,7 @@ 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);
} }
@@ -794,7 +834,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 (playerOnGainSkillTries == -1) { if (info.playerOnGainSkillTries == -1) {
return; return;
} }
@@ -804,10 +844,10 @@ void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_
} }
ScriptEnvironment* env = scriptInterface.getScriptEnv(); ScriptEnvironment* env = scriptInterface.getScriptEnv();
env->setScriptId(playerOnGainSkillTries, &scriptInterface); env->setScriptId(info.playerOnGainSkillTries, &scriptInterface);
lua_State* L = scriptInterface.getLuaState(); lua_State* L = scriptInterface.getLuaState();
scriptInterface.pushFunction(playerOnGainSkillTries); scriptInterface.pushFunction(info.playerOnGainSkillTries);
LuaScriptInterface::pushUserdata<Player>(L, player); LuaScriptInterface::pushUserdata<Player>(L, player);
LuaScriptInterface::setMetatable(L, -1, "Player"); LuaScriptInterface::setMetatable(L, -1, "Player");
@@ -817,11 +857,37 @@ 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) 2016 Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,68 +28,80 @@ class Tile;
class Events class Events
{ {
public: struct EventsInfo {
Events(); // Creature
int32_t creatureOnChangeOutfit = -1;
int32_t creatureOnAreaCombat = -1;
int32_t creatureOnTargetCombat = -1;
void clear(); // Party
bool load(); int32_t partyOnJoin = -1;
int32_t partyOnLeave = -1;
int32_t partyOnDisband = -1;
int32_t partyOnShareExperience = -1;
// Creature // Player
bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit); int32_t playerOnBrowseField = -1;
ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive); int32_t playerOnLook = -1;
ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target); int32_t playerOnLookInBattleList = -1;
int32_t playerOnLookInTrade = -1;
int32_t playerOnLookInShop = -1;
int32_t playerOnMoveItem = -1;
int32_t playerOnItemMoved = -1;
int32_t playerOnMoveCreature = -1;
int32_t playerOnReportRuleViolation = -1;
int32_t playerOnReportBug = -1;
int32_t playerOnTurn = -1;
int32_t playerOnTradeRequest = -1;
int32_t playerOnTradeAccept = -1;
int32_t playerOnGainExperience = -1;
int32_t playerOnLoseExperience = -1;
int32_t playerOnGainSkillTries = -1;
// Party // Monster
bool eventPartyOnJoin(Party* party, Player* player); int32_t monsterOnDropLoot = -1;
bool eventPartyOnLeave(Party* party, Player* player); };
bool eventPartyOnDisband(Party* party);
void eventPartyOnShareExperience(Party* party, uint64_t& exp);
// Player public:
void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance); Events();
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);
private: bool load();
LuaScriptInterface scriptInterface;
// Creature // Creature
int32_t creatureOnChangeOutfit; bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit);
int32_t creatureOnAreaCombat; ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive);
int32_t creatureOnTargetCombat; ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target);
// Party // Party
int32_t partyOnJoin; bool eventPartyOnJoin(Party* party, Player* player);
int32_t partyOnLeave; bool eventPartyOnLeave(Party* party, Player* player);
int32_t partyOnDisband; bool eventPartyOnDisband(Party* party);
int32_t partyOnShareExperience; void eventPartyOnShareExperience(Party* party, uint64_t& exp);
// Player // Player
int32_t playerOnLook; bool eventPlayerOnBrowseField(Player* player, const Position& position);
int32_t playerOnLookInBattleList; void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance);
int32_t playerOnLookInTrade; void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance);
int32_t playerOnMoveItem; void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance);
int32_t playerOnItemMoved; bool eventPlayerOnLookInShop(Player* player, const ItemType* itemType, uint8_t count);
int32_t playerOnMoveCreature; bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
int32_t playerOnReportRuleViolation; void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
int32_t playerOnReportBug; bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition);
int32_t playerOnTurn; void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation);
int32_t playerOnTradeRequest; bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position, uint8_t category);
int32_t playerOnTradeAccept; bool eventPlayerOnTurn(Player* player, Direction direction);
int32_t playerOnGainExperience; bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item);
int32_t playerOnLoseExperience; bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem);
int32_t playerOnGainSkillTries; void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp);
void eventPlayerOnLoseExperience(Player* player, uint64_t& exp);
void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries);
// Monster
void eventMonsterOnDropLoot(Monster* monster, Container* corpse);
private:
LuaScriptInterface scriptInterface;
EventsInfo info;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,387 +19,106 @@
#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)
{ {
if (file) { constexpr auto minimalSize = sizeof(Identifier) + sizeof(Node::START) + sizeof(Node::type) + sizeof(Node::END);
fclose(file); if (fileContents.size() <= minimalSize) {
file = nullptr; throw InvalidOTBFormat{};
} }
NodeStruct::clearNet(root); Identifier fileIdentifier;
delete[] buffer; std::copy(fileContents.begin(), fileContents.begin() + fileIdentifier.size(), fileIdentifier.begin());
if (fileIdentifier != acceptedIdentifier && fileIdentifier != wildcard) {
for (auto& i : cached_data) { throw InvalidOTBFormat{};
delete[] i.data;
} }
} }
bool FileLoader::openFile(const char* filename, const char* accept_identifier) using NodeStack = std::stack<Node*, std::vector<Node*>>;
{ static Node& getCurrentNode(const NodeStack& nodeStack) {
file = fopen(filename, "rb"); if (nodeStack.empty()) {
if (!file) { throw InvalidOTBFormat{};
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;
} }
bool FileLoader::parseNode(NODE node) const Node& Loader::parseTree()
{ {
int32_t byte, pos; auto it = fileContents.begin() + sizeof(Identifier);
NODE currentNode = node; if (static_cast<uint8_t>(*it) != Node::START) {
throw InvalidOTBFormat{};
}
root.type = *(++it);
root.propsBegin = ++it;
NodeStack parseStack;
parseStack.push(&root);
while (readByte(byte)) { for (; it != fileContents.end(); ++it) {
currentNode->type = byte; switch(static_cast<uint8_t>(*it)) {
bool setPropsSize = false; case Node::START: {
auto& currentNode = getCurrentNode(parseStack);
while (true) { if (currentNode.children.empty()) {
if (!readByte(byte)) { currentNode.propsEnd = it;
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: {
bool skipNode = false; auto& currentNode = getCurrentNode(parseStack);
if (currentNode.children.empty()) {
switch (byte) { currentNode.propsEnd = it;
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();
case NODE_END: { break;
//current node end
if (!setPropsSize) {
if (!safeTell(pos)) {
return false;
}
currentNode->propsSize = pos - currentNode->start - 2;
}
if (!readByte(byte)) {
return true;
}
switch (byte) {
case NODE_START: {
//starts next node
if (!safeTell(pos)) {
return false;
}
skipNode = true;
NODE nextNode = new NodeStruct();
nextNode->start = pos;
currentNode->next = nextNode;
currentNode = nextNode;
break;
}
case NODE_END:
return safeTell(pos) && safeSeek(pos);
default:
lastError = ERROR_INVALID_FORMAT;
return false;
}
break;
}
case ESCAPE_CHAR: {
if (!readByte(byte)) {
return false;
}
break;
}
default:
break;
} }
case Node::ESCAPE: {
if (skipNode) { if (++it == fileContents.end()) {
throw InvalidOTBFormat{};
}
break;
}
default: {
break; break;
} }
} }
} }
return false; if (!parseStack.empty()) {
} 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;
} }
NODE FileLoader::getNextNode(const NODE prev, uint32_t& type) bool Loader::getProps(const Node& node, PropStream& props)
{ {
if (!prev) { auto size = std::distance(node.propsBegin, node.propsEnd);
return NO_NODE; if (size == 0) {
}
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;
if (cache_offset >= cached_data[cache_index].size) { auto escapedPropEnd = std::copy_if(node.propsBegin, node.propsEnd, propBuffer.begin(), [&lastEscaped](const char& byte) {
int32_t pos = cache_offset + cached_data[cache_index].base; lastEscaped = byte == static_cast<char>(Node::ESCAPE) && !lastEscaped;
int32_t tmp = getCacheBlock(pos); return !lastEscaped;
if (tmp < 0) { });
return false; props.init(&propBuffer[0], std::distance(propBuffer.begin(), escapedPropEnd));
}
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;
} }
inline bool FileLoader::readBytes(uint32_t size, int32_t pos) } //namespace OTB
{
//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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,133 +22,53 @@
#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;
class FileLoader namespace OTB {
using MappedFile = boost::iostreams::mapped_file_source;
using ContentIt = MappedFile::iterator;
using Identifier = std::array<char, 4>;
struct Node
{ {
public: using ChildrenVector = std::vector<Node>;
FileLoader() = default;
~FileLoader();
// non-copyable ChildrenVector children;
FileLoader(const FileLoader&) = delete; ContentIt propsBegin;
FileLoader& operator=(const FileLoader&) = delete; ContentIt propsEnd;
uint8_t type;
bool openFile(const char* filename, const char* identifier); enum NodeChar: uint8_t
const uint8_t* getProps(const NODE, size_t& size); {
bool getProps(const NODE, PropStream& props); ESCAPE = 0xFD,
NODE getChildNode(const NODE parent, uint32_t& type); START = 0xFE,
NODE getNextNode(const NODE prev, uint32_t& type); END = 0xFF,
};
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:
@@ -200,7 +120,7 @@ class PropStream
return true; return true;
} }
protected: private:
const char* p = nullptr; const char* p = nullptr;
const char* end = nullptr; const char* end = nullptr;
}; };
@@ -240,7 +160,7 @@ class PropWriteStream
std::copy(str.begin(), str.end(), std::back_inserter(buffer)); std::copy(str.begin(), str.end(), std::back_inserter(buffer));
} }
protected: private:
std::vector<char> buffer; std::vector<char> buffer;
}; };

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +31,7 @@
#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;
@@ -69,22 +70,6 @@ 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;
@@ -97,7 +82,7 @@ static constexpr int32_t EVENT_DECAY_BUCKETS = 4;
class Game class Game
{ {
public: public:
Game() = default; Game();
~Game(); ~Game();
// non-copyable // non-copyable
@@ -219,7 +204,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 force = false); bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool forced = false);
/** /**
* Remove Creature from the map. * Remove Creature from the map.
@@ -244,7 +229,7 @@ class Game
return playersRecord; return playersRecord;
} }
void getWorldLightInfo(LightInfo& lightInfo) const; LightInfo getWorldLightInfo() 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);
@@ -322,16 +307,19 @@ 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* listPtr = nullptr, const Position* pos = nullptr); bool ghostMode, SpectatorVec* spectatorsPtr = 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); void playerReportBug(uint32_t playerId, const std::string& message, const Position& position, uint8_t category);
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* partner, Item* tradeItem); bool internalStartTrade(Player* player, Player* tradePartner, 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;
@@ -340,10 +328,11 @@ 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* playerId, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile); void playerMoveCreature(Player* player, 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);
@@ -352,6 +341,7 @@ 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);
@@ -365,23 +355,33 @@ 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, chaseMode_t chaseMode, bool secureMode); void playerSetFightModes(uint32_t playerId, fightMode_t fightMode, bool 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,15 +391,18 @@ 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 playerProcessRuleViolationReport(uint32_t playerId, const std::string& name); void playerToggleMount(uint32_t playerId, bool mount);
void playerCloseRuleViolationReport(uint32_t playerId, const std::string& name); void playerLeaveMarket(uint32_t playerId);
void playerCancelRuleViolationReport(uint32_t playerId); void playerBrowseMarket(uint32_t playerId, uint16_t spriteId);
void playerReportRuleViolationReport(Player* player, const std::string& text); void playerBrowseMarketOwnOffers(uint32_t playerId);
void playerContinueRuleViolationReport(Player* player, const std::string& text); void playerBrowseMarketOwnHistory(uint32_t playerId);
void playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t spriteId, uint16_t amount, uint32_t price, bool anonymous);
void playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter);
void playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount);
void parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer); void parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer);
void closeRuleViolationReport(Player* player); std::forward_list<Item*> getMarketItemList(uint16_t wareId, uint16_t sufficientCount, DepotChest* depotChest, Inbox* inbox);
void cancelRuleViolationReport(Player* player);
static void updatePremium(Account& account); static void updatePremium(Account& account);
@@ -410,14 +413,17 @@ 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 sameFloor) const; bool isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck) const;
void changeSpeed(Creature* creature, int32_t varSpeedDelta); void changeSpeed(Creature* creature, int32_t varSpeedDelta);
void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& oufit); void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit);
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* player); void updateCreatureSkull(const Creature* creature);
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);
@@ -439,14 +445,11 @@ class Game
//animation help functions //animation help functions
void addCreatureHealth(const Creature* target); void addCreatureHealth(const Creature* target);
static void addCreatureHealth(const SpectatorVec& list, const Creature* target); static void addCreatureHealth(const SpectatorVec& spectators, const Creature* target);
void addMagicEffect(const Position& pos, uint8_t effect); void addMagicEffect(const Position& pos, uint8_t effect);
static void addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect); static void addMagicEffect(const SpectatorVec& spectators, 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& list, const Position& fromPos, const Position& toPos, uint8_t effect); static void addDistanceEffect(const SpectatorVec& spectators, 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 {
@@ -462,7 +465,8 @@ class Game
uint32_t getMotdNum() const { return motdNum; } uint32_t getMotdNum() const { return motdNum; }
void incrementMotdNum() { motdNum++; } void incrementMotdNum() { motdNum++; }
const std::unordered_map<uint32_t, RuleViolation>& getRuleViolationReports() const { return ruleViolations; } void sendOfflineTrainingDialog(Player* player);
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; }
@@ -472,45 +476,55 @@ class Game
void addNpc(Npc* npc); void addNpc(Npc* npc);
void removeNpc(Npc* npc); void removeNpc(Npc* npc);
void addMonster(Monster* npc); void addMonster(Monster* monster);
void removeMonster(Monster* npc); void removeMonster(Monster* monster);
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;
protected: std::forward_list<Item*> toDecayItems;
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;
@@ -526,6 +540,8 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,44 +35,47 @@ GlobalEvents::GlobalEvents() :
GlobalEvents::~GlobalEvents() GlobalEvents::~GlobalEvents()
{ {
clear(); clear(false);
} }
void GlobalEvents::clearMap(GlobalEventMap& map) void GlobalEvents::clearMap(GlobalEventMap& map, bool fromLua)
{ {
for (const auto& it : map) { for (auto it = map.begin(); it != map.end(); ) {
delete it.second; if (fromLua == it->second.fromLua) {
it = map.erase(it);
} else {
++it;
}
} }
map.clear();
} }
void GlobalEvents::clear() void GlobalEvents::clear(bool fromLua)
{ {
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); clearMap(thinkMap, fromLua);
clearMap(serverMap); clearMap(serverMap, fromLua);
clearMap(timerMap); clearMap(timerMap, fromLua);
scriptInterface.reInitState(); reInitState(fromLua);
} }
Event* GlobalEvents::getEvent(const std::string& nodeName) Event_ptr 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 new GlobalEvent(&scriptInterface); return Event_ptr(new GlobalEvent(&scriptInterface));
} }
bool GlobalEvents::registerEvent(Event* event, const pugi::xml_node&) bool GlobalEvents::registerEvent(Event_ptr event, const pugi::xml_node&)
{ {
GlobalEvent* globalEvent = static_cast<GlobalEvent*>(event); //event is guaranteed to be a GlobalEvent GlobalEvent_ptr globalEvent{static_cast<GlobalEvent*>(event.release())}; //event is guaranteed to be a GlobalEvent
if (globalEvent->getEventType() == GLOBALEVENT_TIMER) { if (globalEvent->getEventType() == GLOBALEVENT_TIMER) {
auto result = timerMap.emplace(globalEvent->getName(), globalEvent); auto result = timerMap.emplace(globalEvent->getName(), std::move(*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)));
@@ -80,12 +83,42 @@ bool GlobalEvents::registerEvent(Event* 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(), globalEvent); auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent));
if (result.second) { if (result.second) {
return true; return true;
} }
} else { // think event } else { // think event
auto result = thinkMap.emplace(globalEvent->getName(), globalEvent); auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent));
if (result.second) {
if (thinkEventId == 0) {
thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this)));
}
return true;
}
}
std::cout << "[Warning - GlobalEvents::configureEvent] Duplicate registered globalevent with name: " << globalEvent->getName() << std::endl;
return false;
}
bool GlobalEvents::registerLuaEvent(GlobalEvent* event)
{
GlobalEvent_ptr globalEvent{ event };
if (globalEvent->getEventType() == GLOBALEVENT_TIMER) {
auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent));
if (result.second) {
if (timerEventId == 0) {
timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this)));
}
return true;
}
} else if (globalEvent->getEventType() != GLOBALEVENT_NONE) {
auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent));
if (result.second) {
return true;
}
} else { // think event
auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent));
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)));
@@ -111,9 +144,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;
@@ -123,7 +156,7 @@ void GlobalEvents::timer()
continue; continue;
} }
if (!globalEvent->executeEvent()) { if (!globalEvent.executeEvent()) {
it = timerMap.erase(it); it = timerMap.erase(it);
continue; continue;
} }
@@ -133,7 +166,7 @@ void GlobalEvents::timer()
nextScheduledTime = nextExecutionTime; nextScheduledTime = nextExecutionTime;
} }
globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime); globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime);
++it; ++it;
} }
@@ -149,10 +182,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 (const auto& it : thinkMap) { for (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;
@@ -160,16 +193,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()) {
@@ -180,15 +213,16 @@ 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) {
GlobalEvent* globalEvent = it.second; const 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;
@@ -197,8 +231,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[it.first] = it.second; retMap.emplace(it.first, it.second);
} }
} }
return retMap; return retMap;
@@ -315,7 +349,7 @@ bool GlobalEvent::executeRecord(uint32_t current, uint32_t old)
return scriptInterface->callFunction(2); return scriptInterface->callFunction(2);
} }
bool GlobalEvent::executeEvent() bool GlobalEvent::executeEvent() const
{ {
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,8 @@ enum GlobalEvent_t {
}; };
class GlobalEvent; class GlobalEvent;
typedef std::map<std::string, GlobalEvent*> GlobalEventMap; using GlobalEvent_ptr = std::unique_ptr<GlobalEvent>;
using GlobalEventMap = std::map<std::string, GlobalEvent>;
class GlobalEvents final : public BaseEvents class GlobalEvents final : public BaseEvents
{ {
@@ -52,18 +53,20 @@ 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); static void clearMap(GlobalEventMap& map, bool fromLua);
protected: bool registerLuaEvent(GlobalEvent* event);
std::string getScriptBaseName() const final { void clear(bool fromLua) override final;
private:
std::string getScriptBaseName() const override {
return "globalevents"; return "globalevents";
} }
void clear() final;
Event* getEvent(const std::string& nodeName) final; Event_ptr getEvent(const std::string& nodeName) override;
bool registerEvent(Event* event, const pugi::xml_node& node) final; bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
LuaScriptInterface& getScriptInterface() final { LuaScriptInterface& getScriptInterface() override {
return scriptInterface; return scriptInterface;
} }
LuaScriptInterface scriptInterface; LuaScriptInterface scriptInterface;
@@ -77,22 +80,31 @@ class GlobalEvent final : public Event
public: public:
explicit GlobalEvent(LuaScriptInterface* interface); explicit GlobalEvent(LuaScriptInterface* interface);
bool configureEvent(const pugi::xml_node& node) final; bool configureEvent(const pugi::xml_node& node) override;
bool executeRecord(uint32_t current, uint32_t old); bool executeRecord(uint32_t current, uint32_t old);
bool executeEvent(); bool executeEvent() const;
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;
@@ -101,10 +113,10 @@ class GlobalEvent final : public Event
nextExecution = time; nextExecution = time;
} }
protected: private:
GlobalEvent_t eventType = GLOBALEVENT_NONE; GlobalEvent_t eventType = GLOBALEVENT_NONE;
std::string getScriptEventName() const final; std::string getScriptEventName() const override;
std::string name; std::string name;
int64_t nextExecution = 0; int64_t nextExecution = 0;

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +24,47 @@
#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;
@@ -37,10 +78,24 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,11 +28,18 @@ 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);
@@ -50,6 +57,16 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,14 +55,26 @@ class Guild
memberCount = count; memberCount = count;
} }
GuildRank* getRankById(uint32_t id); const std::vector<GuildRank>& getRanks() const {
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 id, const std::string& name, uint8_t level); void addRank(uint32_t rankId, const std::string& rankName, uint8_t level);
const std::string& getMotd() const {
return motd;
}
void setMotd(const std::string& motd) {
this->motd = motd;
}
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,12 +85,26 @@ 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;
}
//reset paid date paidUntil = currentTime;
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()) {
@@ -110,9 +124,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 = getRent(); const int32_t housePrice = g_config.getNumber(ConfigManager::HOUSE_PRICE);
if (housePrice != -1) { if (housePrice != -1) {
ss << " It costs " << housePrice * 5 << " gold coins."; ss << " It costs " << (houseTiles.size() * housePrice) << " gold coins.";
} }
} }
@@ -216,7 +230,6 @@ bool House::transferToDepot() const
transferToDepot(&tmpPlayer); transferToDepot(&tmpPlayer);
IOLoginData::savePlayer(&tmpPlayer); IOLoginData::savePlayer(&tmpPlayer);
} }
return true; return true;
} }
@@ -245,9 +258,8 @@ bool House::transferToDepot(Player* player) const
} }
for (Item* item : moveItemList) { for (Item* item : moveItemList) {
g_game.internalMoveItem(item->getParent(), player->getDepotLocker(getTownId(), true), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT); g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT);
} }
return true; return true;
} }
@@ -396,7 +408,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();
guildList.clear(); guildRankList.clear();
expressionList.clear(); expressionList.clear();
regExList.clear(); regExList.clear();
this->list = list; this->list = list;
@@ -421,7 +433,11 @@ 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) {
addGuild(line.substr(at_pos + 1)); if (at_pos == 0) {
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 {
@@ -443,11 +459,43 @@ void AccessList::addPlayer(const std::string& name)
} }
} }
void AccessList::addGuild(const std::string& name) namespace {
const Guild* getGuildByName(const std::string& name)
{ {
uint32_t guildId = IOGuild::getGuildIdByName(name); uint32_t guildId = IOGuild::getGuildIdByName(name);
if (guildId != 0) { if (guildId == 0) {
guildList.insert(guildId); return nullptr;
}
const Guild* guild = g_game.getGuild(guildId);
if (guild) {
return guild;
}
return IOGuild::loadGuild(guildId);
}
}
void AccessList::addGuild(const std::string& name)
{
const Guild* guild = getGuildByName(name);
if (guild) {
for (const auto& rank : guild->getRanks()) {
guildRankList.insert(rank.id);
}
}
}
void AccessList::addGuildRank(const std::string& name, const std::string& rankName)
{
const Guild* guild = getGuildByName(name);
if (guild) {
const GuildRank* rank = guild->getRankByName(rankName);
if (rank) {
guildRankList.insert(rank->id);
}
} }
} }
@@ -502,8 +550,8 @@ bool AccessList::isInList(const Player* player)
return true; return true;
} }
const Guild* guild = player->getGuild(); const GuildRank* rank = player->getGuildRank();
return guild && guildList.find(guild->getId()) != guildList.end(); return rank && guildRankList.find(rank->id) != guildRankList.end();
} }
void AccessList::getList(std::string& list) const void AccessList::getList(std::string& list) const
@@ -667,7 +715,9 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const
continue; continue;
} }
if (g_game.removeMoney(player.getDepotLocker(house->getTownId(), true), house->getRent(), FLAG_NOLIMIT)) { if (player.getBankBalance() >= rent) {
player.setBankBalance(player.getBankBalance() - rent);
time_t paidUntil = currentTime; time_t paidUntil = currentTime;
switch (rentPeriod) { switch (rentPeriod) {
case RENTPERIOD_DAILY: case RENTPERIOD_DAILY:
@@ -718,7 +768,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.getDepotLocker(house->getTownId(), true), letter, INDEX_WHEREEVER, FLAG_NOLIMIT); g_game.internalAddItem(player.getInbox(), 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +22,7 @@
#include <regex> #include <regex>
#include <set> #include <set>
#include <unordered_set>
#include "container.h" #include "container.h"
#include "housetile.h" #include "housetile.h"
@@ -37,6 +38,7 @@ 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);
@@ -46,7 +48,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> guildList; // TODO: include ranks std::unordered_set<uint32_t> guildRankList;
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;
}; };
@@ -60,10 +62,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() final { Door* getDoor() override {
return this; return this;
} }
const Door* getDoor() const final { const Door* getDoor() const override {
return this; return this;
} }
@@ -72,8 +74,8 @@ class Door final : public Item
} }
//serialization //serialization
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final; Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
void serializeAttr(PropWriteStream&) const final {} void serializeAttr(PropWriteStream&) const override {}
void setDoorId(uint32_t doorId) { void setDoorId(uint32_t doorId) {
setIntAttr(ITEM_ATTRIBUTE_DOORID, doorId); setIntAttr(ITEM_ATTRIBUTE_DOORID, doorId);
@@ -87,12 +89,11 @@ 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() final; void onRemoved() override;
protected:
void setHouse(House* house);
private: private:
void setHouse(House* house);
House* house = nullptr; House* house = nullptr;
std::unique_ptr<AccessList> accessList; std::unique_ptr<AccessList> accessList;
friend class House; friend class House;
@@ -110,8 +111,8 @@ enum AccessHouseLevel_t {
HOUSE_OWNER = 3, HOUSE_OWNER = 3,
}; };
typedef std::list<HouseTile*> HouseTileList; using HouseTileList = std::list<HouseTile*>;
typedef std::list<BedItem*> HouseBedItemList; using HouseBedItemList = std::list<BedItem*>;
class HouseTransferItem final : public Item class HouseTransferItem final : public Item
{ {
@@ -120,12 +121,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) final; void onTradeEvent(TradeEvents_t event, Player* owner) override;
bool canTransform() const final { bool canTransform() const override {
return false; return false;
} }
protected: private:
House* house; House* house;
}; };
@@ -207,7 +208,7 @@ class House
HouseTransferItem* getTransferItem(); HouseTransferItem* getTransferItem();
void resetTransferItem(); void resetTransferItem();
bool executeTransfer(HouseTransferItem* item, Player* player); bool executeTransfer(HouseTransferItem* item, Player* newOwner);
const HouseTileList& getTiles() const { const HouseTileList& getTiles() const {
return houseTiles; return houseTiles;
@@ -217,7 +218,6 @@ 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;
}; };
typedef std::map<uint32_t, House*> HouseMap; using HouseMap = std::map<uint32_t, House*>;
enum RentPeriod_t { enum RentPeriod_t {
RENTPERIOD_DAILY, RENTPERIOD_DAILY,

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 final; uint32_t flags, Creature* actor = nullptr) const override;
Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem, Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem,
uint32_t& flags) final; uint32_t& flags) override;
void addThing(int32_t index, Thing* thing) final; void addThing(int32_t index, Thing* thing) override;
void internalAddThing(uint32_t index, Thing* thing) final; void internalAddThing(uint32_t index, Thing* thing) override;
House* getHouse() { House* getHouse() {
return house; return house;

72
src/inbox.cpp Normal file
View File

@@ -0,0 +1,72 @@
/**
* 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;
}

49
src/inbox.h Normal file
View File

@@ -0,0 +1,49 @@
/**
* 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,29 +19,51 @@
#include "otpch.h" #include "otpch.h"
#include "ioguild.h"
#include "database.h" #include "database.h"
#include "guild.h"
#include "ioguild.h"
Guild* IOGuild::loadGuild(uint32_t guildId)
{
Database& db = Database::getInstance();
std::ostringstream query;
query << "SELECT `name` FROM `guilds` WHERE `id` = " << guildId;
if (DBResult_ptr result = db.storeQuery(query.str())) {
Guild* guild = new Guild(guildId, result->getString("name"));
query.str(std::string());
query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = " << guildId;
if ((result = db.storeQuery(query.str()))) {
do {
guild->addRank(result->getNumber<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("level"));
} while (result->next());
}
return guild;
}
return nullptr;
}
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, GuildWarList& guildWarList) void IOGuild::getWarList(uint32_t guildId, GuildWarVector& guildWarVector)
{ {
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;
} }
@@ -49,9 +71,9 @@ void IOGuild::getWarList(uint32_t guildId, GuildWarList& guildWarList)
do { do {
uint32_t guild1 = result->getNumber<uint32_t>("guild1"); uint32_t guild1 = result->getNumber<uint32_t>("guild1");
if (guildId != guild1) { if (guildId != guild1) {
guildWarList.push_back(guild1); guildWarVector.push_back(guild1);
} else { } else {
guildWarList.push_back(result->getNumber<uint32_t>("guild2")); guildWarVector.push_back(result->getNumber<uint32_t>("guild2"));
} }
} while (result->next()); } while (result->next());
} }

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,15 @@
#ifndef FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF #ifndef FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF
#define FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF #define FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF
typedef std::vector<uint32_t> GuildWarList; class Guild;
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, GuildWarList& guildWarList); static void getWarList(uint32_t guildId, GuildWarVector& guildWarVector);
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,14 @@ Account IOLoginData::loadAccount(uint32_t accno)
Account account; Account account;
std::ostringstream query; std::ostringstream query;
query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno; query << "SELECT `id`, `name`, `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");
@@ -48,16 +49,45 @@ 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());
} }
bool IOLoginData::loginserverAuthentication(uint32_t accountNumber, const std::string& password, Account& account) std::string decodeSecret(const std::string& secret)
{ {
Database* db = Database::getInstance(); // simple base32 decoding
std::string key;
key.reserve(10);
uint32_t buffer = 0, left = 0;
for (const auto& ch : secret) {
buffer <<= 5;
if (ch >= 'A' && ch <= 'Z') {
buffer |= (ch & 0x1F) - 1;
} else if (ch >= '2' && ch <= '7') {
buffer |= ch - 24;
} else {
// if a key is broken, return empty and the comparison
// will always be false since the token must not be empty
return {};
}
left += 5;
if (left >= 8) {
left -= 8;
key.push_back(static_cast<char>(buffer >> left));
}
}
return key;
}
bool IOLoginData::loginserverAuthentication(const std::string& name, const std::string& password, Account& account)
{
Database& db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accountNumber; query << "SELECT `id`, `name`, `password`, `secret`, `type`, `premdays`, `lastday` FROM `accounts` 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;
} }
@@ -67,13 +97,15 @@ bool IOLoginData::loginserverAuthentication(uint32_t accountNumber, const std::s
} }
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) {
@@ -85,17 +117,29 @@ bool IOLoginData::loginserverAuthentication(uint32_t accountNumber, const std::s
return true; return true;
} }
uint32_t IOLoginData::gameworldAuthentication(uint32_t accountNumber, const std::string& password, std::string& characterName) uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName, std::string& token, uint32_t tokenTime)
{ {
Database* db = Database::getInstance(); Database& db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "SELECT `id`, `password` FROM `accounts` WHERE `id` = " << accountNumber; query << "SELECT `id`, `password`, `secret` FROM `accounts` WHERE `name` = " << db.escapeString(accountName);
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;
} }
@@ -103,8 +147,8 @@ uint32_t IOLoginData::gameworldAuthentication(uint32_t accountNumber, const std:
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;
} }
@@ -120,7 +164,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;
} }
@@ -131,7 +175,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)
@@ -146,20 +190,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;
} }
@@ -187,17 +231,18 @@ 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`, `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; query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `id` = " << id;
return loadPlayer(player, Database::getInstance()->storeQuery(query.str())); return loadPlayer(player, db.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`, `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); query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `name` = " << db.escapeString(name);
return loadPlayer(player, db->storeQuery(query.str())); return loadPlayer(player, db.storeQuery(query.str()));
} }
bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result) bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
@@ -206,7 +251,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);
@@ -252,7 +297,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 = std::max<uint32_t>(400, result->getNumber<uint32_t>("cap")) * 100; player->capacity = 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;
@@ -298,17 +343,20 @@ 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) {
player->playerKillerEnd = result->getNumber<time_t>("skulltime"); const time_t skullSeconds = result->getNumber<time_t>("skulltime") - time(nullptr);
if (skullSeconds > 0) {
//ensure that we round up the number of ticks
player->skullTicks = (skullSeconds + 2);
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;
} }
} }
@@ -319,6 +367,9 @@ 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;
@@ -351,38 +402,16 @@ 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) {
query.str(std::string()); guild = IOGuild::loadGuild(guildId);
query << "SELECT `name` FROM `guilds` WHERE `id` = " << guildId; g_game.addGuild(guild);
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) {
@@ -392,7 +421,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"));
} }
@@ -404,11 +433,11 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
player->guildRank = rank; player->guildRank = rank;
IOGuild::getWarList(guildId, player->guildWarList); IOGuild::getWarList(guildId, player->guildWarVector);
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"));
} }
} }
@@ -416,7 +445,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());
@@ -427,7 +456,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) {
@@ -455,7 +484,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) {
@@ -464,15 +493,9 @@ 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) {
Container* itemContainer = item->getContainer(); DepotChest* depotChest = player->getDepotChest(pid, true);
if (itemContainer) { if (depotChest) {
DepotLocker* locker = itemContainer->getDepotLocker(); depotChest->internalAddThing(item);
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);
@@ -488,19 +511,49 @@ 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")); player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("value"), true);
} 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());
@@ -516,12 +569,12 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
{ {
std::ostringstream ss; std::ostringstream ss;
typedef std::pair<Container*, int32_t> containerBlock; using ContainerBlock = std::pair<Container*, int32_t>;
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;
@@ -533,7 +586,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;
} }
@@ -544,7 +597,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();
@@ -563,7 +616,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;
} }
@@ -578,11 +631,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;
} }
@@ -590,7 +643,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
@@ -633,7 +686,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` = " << player->sex << ','; query << "`sex` = " << static_cast<uint16_t>(player->sex) << ',';
if (player->lastLoginSaved != 0) { if (player->lastLoginSaved != 0) {
query << "`lastlogin` = " << player->lastLoginSaved << ','; query << "`lastlogin` = " << player->lastLoginSaved << ',';
@@ -643,21 +696,29 @@ 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) {
query << "`skulltime` = " << player->getPlayerKillerEnd() << ','; int64_t skullTime = 0;
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 << ',';
@@ -674,6 +735,7 @@ 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) << ',';
@@ -686,14 +748,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;
} }
@@ -701,7 +763,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;
} }
@@ -711,31 +773,9 @@ 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;
} }
@@ -753,28 +793,51 @@ bool IOLoginData::savePlayer(Player* player)
return false; return false;
} }
//save depot items if (player->lastDepotId != -1) {
query.str(std::string()); //save depot items
query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID(); query.str(std::string());
query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID();
if (!db->executeQuery(query.str())) { 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 << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID();
if (!db.executeQuery(query.str())) {
return false; return false;
} }
DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES "); DBInsert inboxQuery("INSERT INTO `player_inboxitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
itemList.clear(); itemList.clear();
for (const auto& it : player->depotLockerMap) { for (Item* item : player->getInbox()->getItemList()) {
itemList.emplace_back(it.first, it.second); itemList.emplace_back(0, item);
} }
if (!saveItems(player, itemList, depotQuery, propWriteStream)) { if (!saveItems(player, itemList, inboxQuery, 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;
} }
@@ -802,7 +865,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();
} }
@@ -811,11 +874,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;
} }
@@ -824,11 +887,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;
} }
@@ -850,12 +913,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;
} }
@@ -894,16 +957,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)
@@ -911,46 +974,58 @@ 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` FROM `account_viplist` WHERE `account_id` = " << accountId; query << "SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` 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) void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify)
{ {
Database* db = Database::getInstance(); Database& db = Database::getInstance();
std::ostringstream query; std::ostringstream query;
query << "INSERT INTO `account_viplist` (`account_id`, `player_id`) VALUES (" << accountId << ',' << guid << ')'; query << "INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES (" << accountId << ',' << guid << ',' << db.escapeString(description) << ',' << icon << ',' << notify << ')';
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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"
typedef std::list<std::pair<int32_t, Item*>> ItemBlockList; using ItemBlockList = std::list<std::pair<int32_t, Item*>>;
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(uint32_t accountNumber, const std::string& password, Account& account); static bool loginserverAuthentication(const std::string& name, const std::string& password, Account& account);
static uint32_t gameworldAuthentication(uint32_t accountNumber, const std::string& password, std::string& characterName); static uint32_t gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName, std::string& token, uint32_t tokenTime);
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,17 +52,18 @@ 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); static void addVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify);
static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify);
static void 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);
protected: private:
typedef std::map<uint32_t, std::pair<Item*, uint32_t>> ItemMap; using ItemMap = std::map<uint32_t, std::pair<Item*, uint32_t>>;
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& stream); static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream);
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,23 +66,14 @@ 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& identifier) bool IOMap::loadMap(Map* map, const std::string& fileName)
{ {
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;
} }
@@ -94,7 +85,7 @@ bool IOMap::loadMap(Map* map, const std::string& identifier)
} }
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.
@@ -107,17 +98,66 @@ bool IOMap::loadMap(Map* map, const std::string& identifier)
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;
NODE nodeMap = f.getChildNode(root, type); if (root.children.size() != 1 || root.children[0].type != OTBM_MAP_DATA) {
if (type != OTBM_MAP_DATA) {
setLastErrorString("Could not read data node."); setLastErrorString("Could not read data node.");
return false; return false;
} }
if (!f.getProps(nodeMap, propStream)) { auto& mapNode = root.children[0];
if (!parseMapDataAttributes(loader, mapNode, *map, fileName)) {
return false;
}
for (auto& mapDataNode : mapNode.children) {
if (mapDataNode.type == OTBM_TILE_AREA) {
if (!parseTileArea(loader, mapDataNode, *map)) {
return false;
}
} else if (mapDataNode.type == OTBM_TOWNS) {
if (!parseTowns(loader, mapDataNode, *map)) {
return false;
}
} else if (mapDataNode.type == OTBM_WAYPOINTS && headerVersion > 1) {
if (!parseWaypoints(loader, mapDataNode, *map)) {
return false;
}
} else {
setLastErrorString("Unknown map node.");
return false;
}
}
std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl;
return true;
}
bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName)
{
PropStream propStream;
if (!loader.getProps(mapNode, propStream)) {
setLastErrorString("Could not read map data attributes."); setLastErrorString("Could not read map data attributes.");
return false; return false;
} }
@@ -141,8 +181,8 @@ bool IOMap::loadMap(Map* map, const std::string& identifier)
return false; return false;
} }
map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1); map.spawnfile = fileName.substr(0, fileName.rfind('/') + 1);
map->spawnfile += tmp; map.spawnfile += tmp;
break; break;
case OTBM_ATTR_EXT_HOUSE_FILE: case OTBM_ATTR_EXT_HOUSE_FILE:
@@ -151,8 +191,8 @@ bool IOMap::loadMap(Map* map, const std::string& identifier)
return false; return false;
} }
map->housefile = identifier.substr(0, identifier.rfind('/') + 1); map.housefile = fileName.substr(0, fileName.rfind('/') + 1);
map->housefile += tmp; map.housefile += tmp;
break; break;
default: default:
@@ -160,172 +200,104 @@ bool IOMap::loadMap(Map* map, const std::string& identifier)
return false; return false;
} }
} }
return true;
}
NODE nodeMapData = f.getChildNode(nodeMap, type); bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map)
while (nodeMapData != NO_NODE) { {
if (f.getError() != ERROR_NONE) { PropStream propStream;
setLastErrorString("Invalid map node."); if (!loader.getProps(tileAreaNode, propStream)) {
setLastErrorString("Invalid map node.");
return false;
}
OTBM_Destination_coords area_coord;
if (!propStream.read(area_coord)) {
setLastErrorString("Invalid map node.");
return false;
}
uint16_t base_x = area_coord.x;
uint16_t base_y = area_coord.y;
uint16_t z = area_coord.z;
for (auto& tileNode : tileAreaNode.children) {
if (tileNode.type != OTBM_TILE && tileNode.type != OTBM_HOUSETILE) {
setLastErrorString("Unknown tile node.");
return false; return false;
} }
if (type == OTBM_TILE_AREA) { if (!loader.getProps(tileNode, propStream)) {
if (!f.getProps(nodeMapData, propStream)) { setLastErrorString("Could not read node data.");
setLastErrorString("Invalid map node."); return false;
}
OTBM_Tile_coords tile_coord;
if (!propStream.read(tile_coord)) {
setLastErrorString("Could not read tile position.");
return false;
}
uint16_t x = base_x + tile_coord.x;
uint16_t y = base_y + tile_coord.y;
bool isHouseTile = false;
House* house = nullptr;
Tile* tile = nullptr;
Item* ground_item = nullptr;
uint32_t tileflags = TILESTATE_NONE;
if (tileNode.type == OTBM_HOUSETILE) {
uint32_t houseId;
if (!propStream.read<uint32_t>(houseId)) {
std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not read house id.";
setLastErrorString(ss.str());
return false; return false;
} }
OTBM_Destination_coords area_coord; house = map.houses.addHouse(houseId);
if (!propStream.read(area_coord)) { if (!house) {
setLastErrorString("Invalid map node."); std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId;
setLastErrorString(ss.str());
return false; return false;
} }
uint16_t base_x = area_coord.x; tile = new HouseTile(x, y, z, house);
uint16_t base_y = area_coord.y; house->addTile(static_cast<HouseTile*>(tile));
uint16_t z = area_coord.z; isHouseTile = true;
}
NODE nodeTile = f.getChildNode(nodeMapData, type); uint8_t attribute;
while (nodeTile != NO_NODE) { //read tile attributes
if (f.getError() != ERROR_NONE) { while (propStream.read<uint8_t>(attribute)) {
setLastErrorString("Could not read node data."); switch (attribute) {
return false; case OTBM_ATTR_TILE_FLAGS: {
} 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 << "] Could not read house id."; ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags.";
setLastErrorString(ss.str()); setLastErrorString(ss.str());
return false; return false;
} }
house = map->houses.addHouse(houseId); if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) {
if (!house) { tileflags |= TILESTATE_PROTECTIONZONE;
std::ostringstream ss; } else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) {
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId; tileflags |= TILESTATE_NOPVPZONE;
setLastErrorString(ss.str()); } else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) {
return false; tileflags |= TILESTATE_PVPZONE;
} }
tile = new HouseTile(x, y, z, house); if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) {
house->addTile(static_cast<HouseTile*>(tile)); tileflags |= TILESTATE_NOLOGOUT;
isHouseTile = true; }
break;
} }
//read tile attributes case OTBM_ATTR_ITEM: {
while (propStream.read<uint8_t>(attribute)) { Item* item = Item::CreateItem(propStream);
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.";
@@ -333,19 +305,11 @@ bool IOMap::loadMap(Map* map, const std::string& identifier)
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);
} }
@@ -363,100 +327,156 @@ bool IOMap::loadMap(Map* map, const std::string& identifier)
item->setLoadedFromMap(true); item->setLoadedFromMap(true);
} }
} }
break;
nodeItem = f.getNextNode(nodeItem, type);
} }
if (!tile) { default:
tile = createTile(ground_item, nullptr, x, y, z); std::ostringstream ss;
} ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute.";
setLastErrorString(ss.str());
tile->setFlag(static_cast<tileflags_t>(tileflags)); return false;
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;
}
if (!f.getProps(nodeTown, propStream)) { for (auto& itemNode : tileNode.children) {
setLastErrorString("Could not read town data."); if (itemNode.type != OTBM_ITEM) {
return false; std::ostringstream ss;
} ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown node type.";
setLastErrorString(ss.str());
uint32_t townId; return false;
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)) { PropStream stream;
setLastErrorString("Could not read waypoint data."); if (!loader.getProps(itemNode, stream)) {
return false; setLastErrorString("Invalid item node.");
} 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."); Item* item = Item::CreateItem(stream);
if (!item) {
std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item.";
setLastErrorString(ss.str());
return false;
}
if (!item->unserializeItemNode(loader, itemNode, stream)) {
std::ostringstream ss;
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to load item " << item->getID() << '.';
setLastErrorString(ss.str());
delete item;
return false;
}
if (isHouseTile && item->isMoveable()) {
std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl;
delete item;
} else {
if (item->getItemCount() == 0) {
item->setItemCount(1);
}
if (tile) {
tile->internalAddThing(item);
item->startDecaying();
item->setLoadedFromMap(true);
} else if (item->isGroundTile()) {
delete ground_item;
ground_item = item;
} else {
tile = createTile(ground_item, item, x, y, z);
tile->internalAddThing(item);
item->startDecaying();
item->setLoadedFromMap(true);
}
}
}
if (!tile) {
tile = createTile(ground_item, nullptr, x, y, z);
}
tile->setFlag(static_cast<tileflags_t>(tileflags));
map.setTile(x, y, z, tile);
}
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; return false;
} }
nodeMapData = f.getNextNode(nodeMapData, type); if (!loader.getProps(townNode, propStream)) {
} setLastErrorString("Could not read town data.");
return false;
}
std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl; 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; 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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_MOVEMENT_ID = 5, OTBM_ATTR_UNIQUE_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,12 +51,6 @@ 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 {
@@ -82,8 +76,7 @@ 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)
@@ -114,7 +107,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& identifier); bool loadMap(Map* map, const std::string& fileName);
/* Load the spawns /* Load the spawns
* \param map pointer to the Map class * \param map pointer to the Map class
@@ -154,7 +147,11 @@ class IOMap
errorString = error; errorString = error;
} }
protected: private:
bool parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName);
bool parseWaypoints(OTB::Loader& loader, const OTB::Node& waypointsNode, Map& map);
bool parseTowns(OTB::Loader& loader, const OTB::Node& townsNode, Map& map);
bool parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map);
std::string errorString; std::string errorString;
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 || !tile) { if (iType.moveable || iType.forceSerialize || !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 || item->getDoor() || (item->getContainer() && !item->getContainer()->empty()) || it.canWriteText || item->getBed())) { if (!(it.moveable || it.forceSerialize || 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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();
protected: private:
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);

347
src/iomarket.cpp Normal file
View File

@@ -0,0 +1,347 @@
/**
* 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;
}

63
src/iomarket.h Normal file
View File

@@ -0,0 +1,63 @@
/**
* 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,7 +23,12 @@
#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;
@@ -31,8 +36,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;
@@ -50,7 +55,6 @@ 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 {
@@ -69,7 +73,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_MOVEMENT_ID = 5, ATTR_UNIQUE_ID = 5,
ATTR_TEXT = 6, ATTR_TEXT = 6,
ATTR_DESC = 7, ATTR_DESC = 7,
ATTR_TELE_DEST = 8, ATTR_TELE_DEST = 8,
@@ -87,22 +91,19 @@ enum AttrTypes_t {
ATTR_SLEEPERGUID = 20, ATTR_SLEEPERGUID = 20,
ATTR_SLEEPSTART = 21, ATTR_SLEEPSTART = 21,
ATTR_CHARGES = 22, ATTR_CHARGES = 22,
ATTR_KEYNUMBER = 23, ATTR_CONTAINER_ITEMS = 23,
ATTR_KEYHOLENUMBER = 24, ATTR_NAME = 24,
ATTR_DOORQUESTNUMBER = 25, ATTR_ARTICLE = 25,
ATTR_DOORQUESTVALUE = 26, ATTR_PLURALNAME = 26,
ATTR_DOORLEVEL = 27, ATTR_WEIGHT = 27,
ATTR_CHESTQUESTNUMBER = 28, ATTR_ATTACK = 28,
// add non-OTBM attributes after here ATTR_DEFENSE = 29,
ATTR_CONTAINER_ITEMS = 29, ATTR_EXTRADEFENSE = 30,
ATTR_NAME = 30, ATTR_ARMOR = 31,
ATTR_ARTICLE = 31, ATTR_HITCHANCE = 32,
ATTR_PLURALNAME = 32, ATTR_SHOOTRANGE = 33,
ATTR_WEIGHT = 33, ATTR_CUSTOM_ATTRIBUTES = 34,
ATTR_ATTACK = 34, ATTR_DECAYTO = 35
ATTR_DEFENSE = 35,
ATTR_ARMOR = 36,
ATTR_SHOOTRANGE = 37,
}; };
enum Attr_ReadValue { enum Attr_ReadValue {
@@ -160,53 +161,11 @@ class ItemAttributes
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID));
} }
void setMovementID(uint16_t n) { void setUniqueId(uint16_t n) {
setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n); setIntAttr(ITEM_ATTRIBUTE_UNIQUEID, n);
} }
uint16_t getMovementId() const { uint16_t getUniqueId() const {
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID)); return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
}
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) {
@@ -230,6 +189,13 @@ 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);
} }
@@ -247,19 +213,149 @@ class ItemAttributes
return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE));
} }
protected: struct CustomAttribute
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;
@@ -272,6 +368,8 @@ 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));
} }
@@ -283,6 +381,8 @@ 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) {
@@ -293,6 +393,8 @@ 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;
@@ -323,12 +425,94 @@ class ItemAttributes
const Attribute* getExistingAttr(itemAttrTypes type) const; const Attribute* getExistingAttr(itemAttrTypes type) const;
Attribute& getAttr(itemAttrTypes type); Attribute& getAttr(itemAttrTypes type);
public: CustomAttributeMap* getCustomAttributeMap() {
inline static bool isIntAttrType(itemAttrTypes type) { if (!hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
return (type & 0xFFFFE13) != 0; return nullptr;
}
return getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom;
} }
inline static bool isStrAttrType(itemAttrTypes type) {
return (type & 0x1EC) != 0; template<typename R>
void setCustomAttribute(int64_t key, R value) {
std::string tmp = boost::lexical_cast<std::string>(key);
setCustomAttribute(tmp, value);
}
void setCustomAttribute(int64_t key, CustomAttribute& value) {
std::string tmp = boost::lexical_cast<std::string>(key);
setCustomAttribute(tmp, value);
}
template<typename R>
void setCustomAttribute(std::string& key, R value) {
toLowerCaseString(key);
if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
removeCustomAttribute(key);
} else {
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap();
}
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom->emplace(key, value);
}
void setCustomAttribute(std::string& key, CustomAttribute& value) {
toLowerCaseString(key);
if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
removeCustomAttribute(key);
} else {
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap();
}
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom->insert(std::make_pair(std::move(key), std::move(value)));
}
const CustomAttribute* getCustomAttribute(int64_t key) {
std::string tmp = boost::lexical_cast<std::string>(key);
return getCustomAttribute(tmp);
}
const CustomAttribute* getCustomAttribute(const std::string& key) {
if (const CustomAttributeMap* customAttrMap = getCustomAttributeMap()) {
auto it = customAttrMap->find(asLowerCaseString(key));
if (it != customAttrMap->end()) {
return &(it->second);
}
}
return nullptr;
}
bool removeCustomAttribute(int64_t key) {
std::string tmp = boost::lexical_cast<std::string>(key);
return removeCustomAttribute(tmp);
}
bool removeCustomAttribute(const std::string& key) {
if (CustomAttributeMap* customAttrMap = getCustomAttributeMap()) {
auto it = customAttrMap->find(asLowerCaseString(key));
if (it != customAttrMap->end()) {
customAttrMap->erase(it);
return true;
}
}
return false;
}
const static uint32_t intAttributeTypes = ITEM_ATTRIBUTE_ACTIONID | ITEM_ATTRIBUTE_UNIQUEID | ITEM_ATTRIBUTE_DATE
| ITEM_ATTRIBUTE_WEIGHT | ITEM_ATTRIBUTE_ATTACK | ITEM_ATTRIBUTE_DEFENSE | ITEM_ATTRIBUTE_EXTRADEFENSE
| ITEM_ATTRIBUTE_ARMOR | ITEM_ATTRIBUTE_HITCHANCE | ITEM_ATTRIBUTE_SHOOTRANGE | ITEM_ATTRIBUTE_OWNER
| ITEM_ATTRIBUTE_DURATION | ITEM_ATTRIBUTE_DECAYSTATE | ITEM_ATTRIBUTE_CORPSEOWNER | ITEM_ATTRIBUTE_CHARGES
| ITEM_ATTRIBUTE_FLUIDTYPE | ITEM_ATTRIBUTE_DOORID | ITEM_ATTRIBUTE_DECAYTO;
const static uint32_t stringAttributeTypes = ITEM_ATTRIBUTE_DESCRIPTION | ITEM_ATTRIBUTE_TEXT | ITEM_ATTRIBUTE_WRITER
| ITEM_ATTRIBUTE_NAME | ITEM_ATTRIBUTE_ARTICLE | ITEM_ATTRIBUTE_PLURALNAME;
public:
static bool isIntAttrType(itemAttrTypes type) {
return (type & intAttributeTypes) == type;
}
static bool isStrAttrType(itemAttrTypes type) {
return (type & stringAttributeTypes) == type;
}
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 {
@@ -359,10 +543,10 @@ class Item : virtual public Thing
bool equals(const Item* otherItem) const; bool equals(const Item* otherItem) const;
Item* getItem() final { Item* getItem() override final {
return this; return this;
} }
const Item* getItem() const final { const Item* getItem() const override final {
return this; return this;
} }
virtual Teleport* getTeleport() { virtual Teleport* getTeleport() {
@@ -371,18 +555,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;
} }
@@ -437,6 +621,31 @@ 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);
} }
@@ -475,6 +684,10 @@ 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 {
@@ -484,14 +697,11 @@ class Item : virtual public Thing
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID)); return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID));
} }
void setMovementID(uint16_t n) { uint16_t getUniqueId() const {
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_MOVEMENTID)); return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
} }
void setCharges(uint16_t n) { void setCharges(uint16_t n) {
@@ -557,42 +767,49 @@ 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 final; std::string getDescription(int32_t lookDistance) const override 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(FileLoader& f, NODE node, PropStream& propStream); virtual bool unserializeItemNode(OTB::Loader&, const OTB::Node&, PropStream& propStream);
virtual void serializeAttr(PropWriteStream& propWriteStream) const; virtual void serializeAttr(PropWriteStream& propWriteStream) const;
bool isPushable() const final { bool isPushable() const override final {
return isMoveable(); return isMoveable();
} }
int32_t getThrowRange() const final { int32_t getThrowRange() const override 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;
} }
@@ -605,28 +822,7 @@ 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)) {
@@ -652,15 +848,24 @@ 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;
} }
uint16_t getDisguiseId() const { int8_t getHitChance() const {
return items[id].disguiseId; if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) {
return getIntAttr(ITEM_ATTRIBUTE_HITCHANCE);
}
return items[id].hitChance;
} }
uint32_t getWorth() const; uint32_t getWorth() const;
void getLight(LightInfo& lightInfo) const; LightInfo getLightInfo() const;
bool hasProperty(ITEMPROPERTY prop) const; bool hasProperty(ITEMPROPERTY prop) const;
bool isBlocking() const { bool isBlocking() const {
@@ -678,15 +883,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;
} }
@@ -694,33 +899,10 @@ 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 isDisguised() const { bool hasWalkStack() const {
return items[id].disguise; return items[id].walkStack;
}
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);
@@ -748,12 +930,20 @@ 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) {
@@ -776,13 +966,18 @@ 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_ACTIONID); return !loadedFromMap && canRemove() && isPickupable() && !hasAttribute(ITEM_ATTRIBUTE_UNIQUEID) && !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());
@@ -799,29 +994,32 @@ class Item : virtual public Thing
} }
} }
Cylinder* getParent() const { Cylinder* getParent() const override {
return parent; return parent;
} }
void setParent(Cylinder* cylinder) { void setParent(Cylinder* cylinder) override {
parent = cylinder; parent = cylinder;
} }
Cylinder* getTopParent(); Cylinder* getTopParent();
const Cylinder* getTopParent() const; const Cylinder* getTopParent() const;
Tile* getTile(); Tile* getTile() override;
const Tile* getTile() const; const Tile* getTile() const override;
bool isRemoved() const { bool isRemoved() const override {
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;
@@ -829,16 +1027,7 @@ class Item : virtual public Thing
//Don't add variables here, use the ItemAttribute class. //Don't add variables here, use the ItemAttribute class.
}; };
typedef std::list<Item*> ItemList; using ItemList = std::list<Item*>;
typedef std::deque<Item*> ItemDeque; using ItemDeque = std::deque<Item*>;
inline uint32_t Item::countByType(const Item* i, int32_t subType)
{
if (subType == -1 || subType == i->getSubType()) {
return i->getItemCount();
}
return 0;
}
#endif #endif

198
src/itemloader.h Normal file
View File

@@ -0,0 +1,198 @@
/**
* 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,6 +46,7 @@ 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,
@@ -53,28 +54,117 @@ 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 itemgroup_t { enum ItemParseAttributes_t {
ITEM_GROUP_NONE, ITEM_PARSE_TYPE,
ITEM_PARSE_DESCRIPTION,
ITEM_GROUP_GROUND, ITEM_PARSE_RUNESPELLNAME,
ITEM_GROUP_WEAPON, ITEM_PARSE_WEIGHT,
ITEM_GROUP_AMMUNITION, ITEM_PARSE_SHOWCOUNT,
ITEM_GROUP_ARMOR, ITEM_PARSE_ARMOR,
ITEM_GROUP_CHARGES, ITEM_PARSE_DEFENSE,
ITEM_GROUP_TELEPORT, ITEM_PARSE_EXTRADEF,
ITEM_GROUP_MAGICFIELD, ITEM_PARSE_ATTACK,
ITEM_GROUP_WRITEABLE, ITEM_PARSE_ROTATETO,
ITEM_GROUP_KEY, ITEM_PARSE_MOVEABLE,
ITEM_GROUP_SPLASH, ITEM_PARSE_BLOCKPROJECTILE,
ITEM_GROUP_FLUID, ITEM_PARSE_PICKUPABLE,
ITEM_GROUP_DOOR, ITEM_PARSE_FORCESERIALIZE,
ITEM_GROUP_DEPRECATED, ITEM_PARSE_FLOORCHANGE,
ITEM_PARSE_CORPSETYPE,
ITEM_GROUP_LAST ITEM_PARSE_CONTAINERSIZE,
ITEM_PARSE_FLUIDSOURCE,
ITEM_PARSE_READABLE,
ITEM_PARSE_WRITEABLE,
ITEM_PARSE_MAXTEXTLEN,
ITEM_PARSE_WRITEONCEITEMID,
ITEM_PARSE_WEAPONTYPE,
ITEM_PARSE_SLOTTYPE,
ITEM_PARSE_AMMOTYPE,
ITEM_PARSE_SHOOTTYPE,
ITEM_PARSE_EFFECT,
ITEM_PARSE_RANGE,
ITEM_PARSE_STOPDURATION,
ITEM_PARSE_DECAYTO,
ITEM_PARSE_TRANSFORMEQUIPTO,
ITEM_PARSE_TRANSFORMDEEQUIPTO,
ITEM_PARSE_DURATION,
ITEM_PARSE_SHOWDURATION,
ITEM_PARSE_CHARGES,
ITEM_PARSE_SHOWCHARGES,
ITEM_PARSE_SHOWATTRIBUTES,
ITEM_PARSE_HITCHANCE,
ITEM_PARSE_MAXHITCHANCE,
ITEM_PARSE_INVISIBLE,
ITEM_PARSE_SPEED,
ITEM_PARSE_HEALTHGAIN,
ITEM_PARSE_HEALTHTICKS,
ITEM_PARSE_MANAGAIN,
ITEM_PARSE_MANATICKS,
ITEM_PARSE_MANASHIELD,
ITEM_PARSE_SKILLSWORD,
ITEM_PARSE_SKILLAXE,
ITEM_PARSE_SKILLCLUB,
ITEM_PARSE_SKILLDIST,
ITEM_PARSE_SKILLFISH,
ITEM_PARSE_SKILLSHIELD,
ITEM_PARSE_SKILLFIST,
ITEM_PARSE_MAXHITPOINTS,
ITEM_PARSE_MAXHITPOINTSPERCENT,
ITEM_PARSE_MAXMANAPOINTS,
ITEM_PARSE_MAXMANAPOINTSPERCENT,
ITEM_PARSE_MAGICPOINTS,
ITEM_PARSE_MAGICPOINTSPERCENT,
ITEM_PARSE_CRITICALHITCHANCE,
ITEM_PARSE_CRITICALHITAMOUNT,
ITEM_PARSE_LIFELEECHCHANCE,
ITEM_PARSE_LIFELEECHAMOUNT,
ITEM_PARSE_MANALEECHCHANCE,
ITEM_PARSE_MANALEECHAMOUNT,
ITEM_PARSE_FIELDABSORBPERCENTENERGY,
ITEM_PARSE_FIELDABSORBPERCENTFIRE,
ITEM_PARSE_FIELDABSORBPERCENTPOISON,
ITEM_PARSE_ABSORBPERCENTALL,
ITEM_PARSE_ABSORBPERCENTELEMENTS,
ITEM_PARSE_ABSORBPERCENTMAGIC,
ITEM_PARSE_ABSORBPERCENTENERGY,
ITEM_PARSE_ABSORBPERCENTFIRE,
ITEM_PARSE_ABSORBPERCENTPOISON,
ITEM_PARSE_ABSORBPERCENTICE,
ITEM_PARSE_ABSORBPERCENTHOLY,
ITEM_PARSE_ABSORBPERCENTDEATH,
ITEM_PARSE_ABSORBPERCENTLIFEDRAIN,
ITEM_PARSE_ABSORBPERCENTMANADRAIN,
ITEM_PARSE_ABSORBPERCENTDROWN,
ITEM_PARSE_ABSORBPERCENTPHYSICAL,
ITEM_PARSE_ABSORBPERCENTHEALING,
ITEM_PARSE_ABSORBPERCENTUNDEFINED,
ITEM_PARSE_SUPPRESSDRUNK,
ITEM_PARSE_SUPPRESSENERGY,
ITEM_PARSE_SUPPRESSFIRE,
ITEM_PARSE_SUPPRESSPOISON,
ITEM_PARSE_SUPPRESSDROWN,
ITEM_PARSE_SUPPRESSPHYSICAL,
ITEM_PARSE_SUPPRESSFREEZE,
ITEM_PARSE_SUPPRESSDAZZLE,
ITEM_PARSE_SUPPRESSCURSE,
ITEM_PARSE_FIELD,
ITEM_PARSE_REPLACEABLE,
ITEM_PARSE_PARTNERDIRECTION,
ITEM_PARSE_LEVELDOOR,
ITEM_PARSE_MALETRANSFORMTO,
ITEM_PARSE_FEMALETRANSFORMTO,
ITEM_PARSE_TRANSFORMTO,
ITEM_PARSE_DESTROYTO,
ITEM_PARSE_ELEMENTICE,
ITEM_PARSE_ELEMENTEARTH,
ITEM_PARSE_ELEMENTFIRE,
ITEM_PARSE_ELEMENTENERGY,
ITEM_PARSE_WALKSTACK,
ITEM_PARSE_BLOCKING,
ITEM_PARSE_ALLOWDISTREAD,
}; };
struct Abilities { struct Abilities {
@@ -92,6 +182,7 @@ 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;
@@ -101,6 +192,10 @@ 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;
@@ -124,10 +219,7 @@ class ItemType
return group == ITEM_GROUP_GROUND; return group == ITEM_GROUP_GROUND;
} }
bool isContainer() const { bool isContainer() const {
return type == ITEM_TYPE_CONTAINER; return group == ITEM_GROUP_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;
@@ -154,11 +246,20 @@ 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);
@@ -190,7 +291,9 @@ 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;
@@ -203,40 +306,35 @@ 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 attackStrength = 0; int32_t maxHitChance = -1;
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;
int32_t rotateTo = 0; uint16_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 = 0; uint16_t transformToOnUse[2] = {0, 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_RIGHT | SLOTP_LEFT | SLOTP_AMMO; uint16_t slotPosition = SLOTP_HAND;
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;
@@ -246,30 +344,22 @@ class ItemType
RaceType_t corpseType = RACE_NONE; RaceType_t corpseType = RACE_NONE;
FluidTypes_t fluidSource = FLUID_NONE; FluidTypes_t fluidSource = FLUID_NONE;
uint8_t fragility = 0; uint8_t floorChange = 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;
uint8_t weaponSpecialEffect = 0; int8_t hitChance = 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 changeUse = false; bool forceSerialize = 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 = true; bool allowPickupable = false;
bool showDuration = false; bool showDuration = false;
bool showCharges = false; bool showCharges = false;
bool showAttributes = false; bool showAttributes = false;
@@ -277,7 +367,7 @@ class ItemType
bool pickupable = false; bool pickupable = false;
bool rotatable = false; bool rotatable = false;
bool useable = false; bool useable = false;
bool moveable = true; bool moveable = false;
bool alwaysOnTop = false; bool alwaysOnTop = false;
bool canReadText = false; bool canReadText = false;
bool canWriteText = false; bool canWriteText = false;
@@ -293,7 +383,8 @@ 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();
@@ -304,23 +395,38 @@ 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);
bool loadItems(); uint32_t majorVersion = 0;
uint32_t minorVersion = 0;
uint32_t buildNumber = 0;
inline size_t size() const { bool loadFromXml();
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;
protected: private:
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,37 +26,52 @@
#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:
template <typename U> LockfreePoolingAllocator() = default;
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&) {}
typedef T value_type; using value_type = T;
T* allocate(size_t) const { T* allocate(size_t) const {
T* p; // NOTE: p doesn't have to be initialized auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::get();
if (!getFreeList().pop(p)) { void* p; // NOTE: p doesn't have to be initialized
if (!inst.pop(p)) {
//Acquire memory without calling the constructor of T //Acquire memory without calling the constructor of T
p = static_cast<T*>(operator new (sizeof(T))); p = operator new (sizeof(T));
} }
return p; return static_cast<T*>(p);
} }
void deallocate(T* p, size_t) const { void deallocate(T* p, size_t) const {
if (!getFreeList().bounded_push(p)) { auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::get();
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,7 +20,11 @@
#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
@@ -35,6 +39,7 @@
#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;
@@ -46,6 +51,7 @@ 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,
@@ -93,7 +99,8 @@ struct LuaTimerEventDesc {
class LuaScriptInterface; class LuaScriptInterface;
class Cylinder; class Cylinder;
class Game; class Game;
class Npc;
struct LootBlock;
class ScriptEnvironment class ScriptEnvironment
{ {
@@ -148,9 +155,9 @@ class ScriptEnvironment
void removeItemByUID(uint32_t uid); void removeItemByUID(uint32_t uid);
private: private:
typedef std::vector<const LuaVariant*> VariantVector; using VariantVector = std::vector<const LuaVariant*>;
typedef std::map<uint32_t, int32_t> StorageMap; using StorageMap = std::map<uint32_t, int32_t>;
typedef std::map<uint32_t, DBResult_ptr> DBResultMap; using DBResultMap = std::map<uint32_t, DBResult_ptr>;
LuaScriptInterface* interface; LuaScriptInterface* interface;
@@ -209,6 +216,7 @@ 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() {
@@ -271,13 +279,13 @@ class LuaScriptInterface
// Get // Get
template<typename T> template<typename T>
inline static typename std::enable_if<std::is_enum<T>::value, T>::type 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>
inline static typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, T>::type 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));
@@ -301,16 +309,16 @@ class LuaScriptInterface
return *userdata; return *userdata;
} }
template<class T> template<class T>
inline static T** getRawUserdata(lua_State* L, int32_t arg) 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));
} }
inline static bool getBoolean(lua_State* L, int32_t arg) static bool getBoolean(lua_State* L, int32_t arg)
{ {
return lua_toboolean(L, arg) != 0; return lua_toboolean(L, arg) != 0;
} }
inline static bool getBoolean(lua_State* L, int32_t arg, bool defaultValue) 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) {
@@ -320,11 +328,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);
@@ -342,27 +350,27 @@ class LuaScriptInterface
static LuaDataType getUserdataType(lua_State* L, int32_t arg); static LuaDataType getUserdataType(lua_State* L, int32_t arg);
// Is // Is
inline static bool isNumber(lua_State* L, int32_t arg) static bool isNumber(lua_State* L, int32_t arg)
{ {
return lua_type(L, arg) == LUA_TNUMBER; return lua_type(L, arg) == LUA_TNUMBER;
} }
inline static bool isString(lua_State* L, int32_t arg) static bool isString(lua_State* L, int32_t arg)
{ {
return lua_isstring(L, arg) != 0; return lua_isstring(L, arg) != 0;
} }
inline static bool isBoolean(lua_State* L, int32_t arg) static bool isBoolean(lua_State* L, int32_t arg)
{ {
return lua_isboolean(L, arg); return lua_isboolean(L, arg);
} }
inline static bool isTable(lua_State* L, int32_t arg) static bool isTable(lua_State* L, int32_t arg)
{ {
return lua_istable(L, arg); return lua_istable(L, arg);
} }
inline static bool isFunction(lua_State* L, int32_t arg) static bool isFunction(lua_State* L, int32_t arg)
{ {
return lua_isfunction(L, arg); return lua_isfunction(L, arg);
} }
inline static bool isUserdata(lua_State* L, int32_t arg) static bool isUserdata(lua_State* L, int32_t arg)
{ {
return lua_isuserdata(L, arg) != 0; return lua_isuserdata(L, arg) != 0;
} }
@@ -370,17 +378,19 @@ 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);
// //
inline static void setField(lua_State* L, const char* index, lua_Number value) 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);
} }
inline static void setField(lua_State* L, const char* index, const std::string& value) 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);
@@ -402,9 +412,21 @@ 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);
@@ -413,28 +435,16 @@ 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 creature info functions //get world info
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);
@@ -465,12 +475,7 @@ 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);
@@ -481,6 +486,9 @@ 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);
@@ -548,9 +556,12 @@ 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
@@ -571,7 +582,6 @@ 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);
@@ -610,6 +620,8 @@ 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);
@@ -638,6 +650,34 @@ 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);
@@ -652,11 +692,9 @@ 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 luaItemGetMovementId(lua_State* L); static int luaItemGetUniqueId(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);
@@ -676,6 +714,9 @@ 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);
@@ -684,6 +725,7 @@ 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);
@@ -691,7 +733,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);
@@ -699,7 +741,8 @@ 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);
@@ -716,6 +759,8 @@ 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);
@@ -742,6 +787,7 @@ 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);
@@ -749,6 +795,7 @@ 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);
@@ -763,6 +810,7 @@ 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);
@@ -775,6 +823,9 @@ 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);
@@ -786,7 +837,6 @@ 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);
@@ -797,10 +847,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 luaPlayerGetMurderTimestamps(lua_State* L); static int luaPlayerGetSkullTime(lua_State* L);
static int luaPlayerGetPlayerKillerEnd(lua_State* L); static int luaPlayerSetSkullTime(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);
@@ -825,6 +875,17 @@ 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);
@@ -874,6 +935,7 @@ 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);
@@ -890,6 +952,10 @@ 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);
@@ -903,12 +969,19 @@ 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);
@@ -916,7 +989,12 @@ 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 luaPlayerGetTotalDamage(lua_State* L); static int luaPlayerGetInstantSpells(lua_State* L);
static int luaPlayerCanCast(lua_State* L);
static int luaPlayerHasChaseMode(lua_State* L);
static int luaPlayerHasSecureMode(lua_State* L);
static int luaPlayerGetFightMode(lua_State* L);
// Monster // Monster
static int luaMonsterCreate(lua_State* L); static int luaMonsterCreate(lua_State* L);
@@ -955,6 +1033,9 @@ 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);
@@ -966,6 +1047,9 @@ 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);
@@ -975,11 +1059,13 @@ 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);
@@ -1030,59 +1116,68 @@ 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 luaItemTypeIsMagicField(lua_State* L); static int luaItemTypeIsBlocking(lua_State* L);
static int luaItemTypeIsSplash(lua_State* L);
static int luaItemTypeIsKey(lua_State* L);
static int luaItemTypeIsDisguised(lua_State* L);
static int luaItemTypeIsDestroyable(lua_State* L);
static int luaItemTypeIsGroundTile(lua_State* L); static int luaItemTypeIsGroundTile(lua_State* L);
static int luaItemTypeIsMagicField(lua_State* L);
static int luaItemTypeIsUseable(lua_State* L);
static int luaItemTypeIsPickupable(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 luaItemTypeGetDisguiseId(lua_State* L); static int luaItemTypeGetClientId(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);
@@ -1093,7 +1188,8 @@ 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 luaCombatSetCondition(lua_State* L); static int luaCombatAddCondition(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);
@@ -1115,10 +1211,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 luaConditionSetSpeedDelta(lua_State* L); static int luaConditionSetFormula(lua_State* L);
static int luaConditionSetOutfit(lua_State* L); static int luaConditionSetOutfit(lua_State* L);
static int luaConditionSetTiming(lua_State* L); static int luaConditionAddDamage(lua_State* L);
// MonsterType // MonsterType
static int luaMonsterTypeCreate(lua_State* L); static int luaMonsterTypeCreate(lua_State* L);
@@ -1129,47 +1225,100 @@ 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 luaMonsterTypeIsHealthShown(lua_State* L); static int luaMonsterTypeIsHealthHidden(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 luaMonsterTypeGetName(lua_State* L); static int luaMonsterTypeName(lua_State* L);
static int luaMonsterTypeGetNameDescription(lua_State* L); static int luaMonsterTypeNameDescription(lua_State* L);
static int luaMonsterTypeGetHealth(lua_State* L); static int luaMonsterTypeHealth(lua_State* L);
static int luaMonsterTypeGetMaxHealth(lua_State* L); static int luaMonsterTypeMaxHealth(lua_State* L);
static int luaMonsterTypeGetRunHealth(lua_State* L); static int luaMonsterTypeRunHealth(lua_State* L);
static int luaMonsterTypeGetExperience(lua_State* L); static int luaMonsterTypeExperience(lua_State* L);
static int luaMonsterTypeGetCombatImmunities(lua_State* L); static int luaMonsterTypeCombatImmunities(lua_State* L);
static int luaMonsterTypeGetConditionImmunities(lua_State* L); static int luaMonsterTypeConditionImmunities(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 luaMonsterTypeGetMaxSummons(lua_State* L); static int luaMonsterTypeAddSummon(lua_State* L);
static int luaMonsterTypeGetArmor(lua_State* L); static int luaMonsterTypeMaxSummons(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 luaMonsterTypeGetTargetDistance(lua_State* L); static int luaMonsterTypeArmor(lua_State* L);
static int luaMonsterTypeGetChangeTargetChance(lua_State* L); static int luaMonsterTypeDefense(lua_State* L);
static int luaMonsterTypeGetChangeTargetSpeed(lua_State* L); static int luaMonsterTypeOutfit(lua_State* L);
static int luaMonsterTypeRace(lua_State* L);
static int luaMonsterTypeCorpseId(lua_State* L);
static int luaMonsterTypeManaCost(lua_State* L);
static int luaMonsterTypeBaseSpeed(lua_State* L);
static int luaMonsterTypeLight(lua_State* L);
static int luaMonsterTypeStaticAttackChance(lua_State* L);
static int luaMonsterTypeTargetDistance(lua_State* L);
static int luaMonsterTypeYellChance(lua_State* L);
static int luaMonsterTypeYellSpeedTicks(lua_State* L);
static int luaMonsterTypeChangeTargetChance(lua_State* L);
static int luaMonsterTypeChangeTargetSpeed(lua_State* L);
// Loot
static int luaCreateLoot(lua_State* L);
static int luaDeleteLoot(lua_State* L);
static int luaLootSetId(lua_State* L);
static int luaLootSetMaxCount(lua_State* L);
static int luaLootSetSubType(lua_State* L);
static int luaLootSetChance(lua_State* L);
static int luaLootSetActionId(lua_State* L);
static int luaLootSetDescription(lua_State* L);
static int luaLootAddChildLoot(lua_State* L);
// MonsterSpell
static int luaCreateMonsterSpell(lua_State* L);
static int luaDeleteMonsterSpell(lua_State* L);
static int luaMonsterSpellSetType(lua_State* L);
static int luaMonsterSpellSetScriptName(lua_State* L);
static int luaMonsterSpellSetChance(lua_State* L);
static int luaMonsterSpellSetInterval(lua_State* L);
static int luaMonsterSpellSetRange(lua_State* L);
static int luaMonsterSpellSetCombatValue(lua_State* L);
static int luaMonsterSpellSetCombatType(lua_State* L);
static int luaMonsterSpellSetAttackValue(lua_State* L);
static int luaMonsterSpellSetNeedTarget(lua_State* L);
static int luaMonsterSpellSetCombatLength(lua_State* L);
static int luaMonsterSpellSetCombatSpread(lua_State* L);
static int luaMonsterSpellSetCombatRadius(lua_State* L);
static int luaMonsterSpellSetConditionType(lua_State* L);
static int luaMonsterSpellSetConditionDamage(lua_State* L);
static int luaMonsterSpellSetConditionSpeedChange(lua_State* L);
static int luaMonsterSpellSetConditionDuration(lua_State* L);
static int luaMonsterSpellSetConditionTickInterval(lua_State* L);
static int luaMonsterSpellSetCombatShootEffect(lua_State* L);
static int luaMonsterSpellSetCombatEffect(lua_State* L);
// 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);
@@ -1192,21 +1341,142 @@ 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
@@ -1219,9 +1489,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(); bool initState() override;
bool reInitState(); bool reInitState();
bool closeState(); bool closeState() override;
LuaScriptInterface* getTestInterface(); LuaScriptInterface* getTestInterface();

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,9 +21,7 @@
#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;
@@ -93,30 +91,22 @@ 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;
std::string townName; if (!getReceiver(item, receiver)) {
if (!getDestination(item, receiver, townName)) {
return false; return false;
} }
if (receiver.empty() || townName.empty()) { /**No need to continue if its still empty**/
return false; if (receiver.empty()) {
}
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) {
DepotLocker* depotLocker = player->getDepotLocker(town->getID(), true); if (g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER,
if (depotLocker) { item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER, g_game.transformItem(item, item->getID() + 1);
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { player->onReceiveMail();
g_game.transformItem(item, item->getID() + 1); return true;
player->onReceiveMail(town->getID());
return true;
}
} }
} else { } else {
Player tmpPlayer(nullptr); Player tmpPlayer(nullptr);
@@ -124,30 +114,25 @@ bool Mailbox::sendItem(Item* item) const
return false; return false;
} }
DepotLocker* depotLocker = tmpPlayer.getDepotLocker(town->getID(), true); if (g_game.internalMoveItem(item->getParent(), tmpPlayer.getInbox(), INDEX_WHEREEVER,
if (depotLocker) { item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER, g_game.transformItem(item, item->getID() + 1);
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) { IOLoginData::savePlayer(&tmpPlayer);
g_game.transformItem(item, item->getID() + 1); return true;
IOLoginData::savePlayer(&tmpPlayer);
return true;
}
} }
} }
return false; return false;
} }
bool Mailbox::getDestination(Item* item, std::string& name, std::string& town) const bool Mailbox::getReceiver(Item* item, std::string& name) 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 && getDestination(containerItem, name, town)) { if (containerItem->getID() == ITEM_LABEL && getReceiver(containerItem, name)) {
return true; return true;
} }
} }
return false; return false;
} }
@@ -156,24 +141,8 @@ bool Mailbox::getDestination(Item* item, std::string& name, std::string& town) c
return false; return false;
} }
std::istringstream iss(text, std::istringstream::in); name = getFirstLine(text);
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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() final { Mailbox* getMailbox() override {
return this; return this;
} }
const Mailbox* getMailbox() const final { const Mailbox* getMailbox() const override {
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 final; uint32_t flags, Creature* actor = nullptr) const override;
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 final; uint32_t& maxQueryCount, uint32_t flags) const override;
ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final; ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override;
Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem, Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem,
uint32_t& flags) final; uint32_t& flags) override;
void addThing(Thing* thing) final; void addThing(Thing* thing) override;
void addThing(int32_t index, Thing* thing) final; void addThing(int32_t index, Thing* thing) override;
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final; void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override;
void replaceThing(uint32_t index, Thing* thing) final; void replaceThing(uint32_t index, Thing* thing) override;
void removeThing(Thing* thing, uint32_t count) final; void removeThing(Thing* thing, uint32_t count) override;
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final; void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
private: private:
bool getDestination(Item* item, std::string& name, std::string& town) const; bool getReceiver(Item* item, std::string& name) 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,7 +23,6 @@
#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;
@@ -36,7 +35,6 @@ 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;
} }
@@ -163,14 +161,7 @@ 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;
@@ -209,7 +200,7 @@ bool Map::placeCreature(const Position& centerPos, Creature* creature, bool exte
continue; continue;
} }
if (tile->queryAdd(0, *creature, 1, (creature->getMonster() ? FLAG_PLACECHECK : 0)) == RETURNVALUE_NOERROR) { if (tile->queryAdd(0, *creature, 1, 0) == RETURNVALUE_NOERROR) {
if (!extendedPos || isSightClear(centerPos, tryPos, false)) { if (!extendedPos || isSightClear(centerPos, tryPos, false)) {
foundTile = true; foundTile = true;
break; break;
@@ -243,12 +234,13 @@ 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 list; SpectatorVec spectators, newPosSpectators;
getSpectators(list, oldPos, true); getSpectators(spectators, oldPos, true);
getSpectators(list, newPos, true); getSpectators(newPosSpectators, newPos, true);
spectators.addSpectators(newPosSpectators);
std::vector<int32_t> oldStackPosVector; std::vector<int32_t> oldStackPosVector;
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
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));
@@ -289,7 +281,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 : list) { for (Creature* spectator : spectators) {
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++];
@@ -300,7 +292,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
} }
//event method //event method
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
spectator->onCreatureMove(&creature, &newTile, newPos, &oldTile, oldPos, teleport); spectator->onCreatureMove(&creature, &newTile, newPos, &oldTile, oldPos, teleport);
} }
@@ -308,7 +300,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
newTile.postAddNotification(&creature, &oldTile, 0); newTile.postAddNotification(&creature, &oldTile, 0);
} }
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 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
{ {
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;
@@ -348,7 +340,7 @@ void Map::getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, i
continue; continue;
} }
list.insert(creature); spectators.emplace_back(creature);
} }
leafE = leafE->leafE; leafE = leafE->leafE;
} else { } else {
@@ -364,7 +356,7 @@ void Map::getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, i
} }
} }
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*/) 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*/)
{ {
if (centerPos.z >= MAP_MAX_LAYERS) { if (centerPos.z >= MAP_MAX_LAYERS) {
return; return;
@@ -382,11 +374,11 @@ void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool mult
if (onlyPlayers) { if (onlyPlayers) {
auto it = playersSpectatorCache.find(centerPos); auto it = playersSpectatorCache.find(centerPos);
if (it != playersSpectatorCache.end()) { if (it != playersSpectatorCache.end()) {
if (!list.empty()) { if (!spectators.empty()) {
const SpectatorVec& cachedList = it->second; const SpectatorVec& cachedSpectators = it->second;
list.insert(cachedList.begin(), cachedList.end()); spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end());
} else { } else {
list = it->second; spectators = it->second;
} }
foundCache = true; foundCache = true;
@@ -397,17 +389,17 @@ void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool mult
auto it = spectatorCache.find(centerPos); auto it = spectatorCache.find(centerPos);
if (it != spectatorCache.end()) { if (it != spectatorCache.end()) {
if (!onlyPlayers) { if (!onlyPlayers) {
if (!list.empty()) { if (!spectators.empty()) {
const SpectatorVec& cachedList = it->second; const SpectatorVec& cachedSpectators = it->second;
list.insert(cachedList.begin(), cachedList.end()); spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end());
} else { } else {
list = it->second; spectators = it->second;
} }
} else { } else {
const SpectatorVec& cachedList = it->second; const SpectatorVec& cachedSpectators = it->second;
for (Creature* spectator : cachedList) { for (Creature* spectator : cachedSpectators) {
if (spectator->getPlayer()) { if (spectator->getPlayer()) {
list.insert(spectator); spectators.emplace_back(spectator);
} }
} }
} }
@@ -445,13 +437,13 @@ void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool mult
maxRangeZ = centerPos.z; maxRangeZ = centerPos.z;
} }
getSpectatorsInternal(list, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers); getSpectatorsInternal(spectators, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers);
if (cacheResult) { if (cacheResult) {
if (onlyPlayers) { if (onlyPlayers) {
playersSpectatorCache[centerPos] = list; playersSpectatorCache[centerPos] = spectators;
} else { } else {
spectatorCache[centerPos] = list; spectatorCache[centerPos] = spectators;
} }
} }
} }
@@ -460,6 +452,10 @@ void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool mult
void Map::clearSpectatorCache() void Map::clearSpectatorCache()
{ {
spectatorCache.clear(); spectatorCache.clear();
}
void Map::clearPlayersSpectatorCache()
{
playersSpectatorCache.clear(); playersSpectatorCache.clear();
} }
@@ -563,7 +559,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) != RETURNVALUE_NOERROR) { if (!tile || tile->queryAdd(0, creature, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) != RETURNVALUE_NOERROR) {
return nullptr; return nullptr;
} }
} }
@@ -846,25 +842,16 @@ 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 (combatType != COMBAT_NONE) { if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) {
if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) { cost += MAP_NORMALWALKCOST * 18;
cost += MAP_NORMALWALKCOST * 18;
}
} }
} }
return cost; return cost;
} }

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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;
}; };
typedef std::map<Position, SpectatorVec> SpectatorCache; using SpectatorCache = std::map<Position, SpectatorVec>;
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>
inline static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y) 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,10 +127,11 @@ 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:
QTreeNode* child[4] = {};
bool leaf = false; bool leaf = false;
private:
QTreeNode* child[4] = {};
friend class Map; friend class Map;
}; };
@@ -152,7 +153,7 @@ class QTreeLeafNode final : public QTreeNode
void addCreature(Creature* c); void addCreature(Creature* c);
void removeCreature(Creature* c); void removeCreature(Creature* c);
protected: private:
static bool newLeaf; static bool newLeaf;
QTreeLeafNode* leafS = nullptr; QTreeLeafNode* leafS = nullptr;
QTreeLeafNode* leafE = nullptr; QTreeLeafNode* leafE = nullptr;
@@ -196,7 +197,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;
inline Tile* getTile(const Position& pos) const { Tile* getTile(const Position& pos) const {
return getTile(pos.x, pos.y, pos.z); return getTile(pos.x, pos.y, pos.z);
} }
@@ -219,11 +220,12 @@ class Map
void moveCreature(Creature& creature, Tile& newTile, bool forceTeleport = false); void moveCreature(Creature& creature, Tile& newTile, bool forceTeleport = false);
void getSpectators(SpectatorVec& list, const Position& centerPos, bool multifloor = false, bool onlyPlayers = false, void getSpectators(SpectatorVec& spectators, 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
@@ -262,7 +264,8 @@ class Map
Spawns spawns; Spawns spawns;
Towns towns; Towns towns;
Houses houses; Houses houses;
protected:
private:
SpectatorCache spectatorCache; SpectatorCache spectatorCache;
SpectatorCache playersSpectatorCache; SpectatorCache playersSpectatorCache;
@@ -275,7 +278,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& list, const Position& centerPos, void getSpectatorsInternal(SpectatorVec& spectators, 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 "configmanager.h" #include "events.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(asLowerCaseString(mtype->nameDescription)), strDescription(mType->nameDescription),
mType(mtype) mType(mType)
{ {
defaultOutfit = mType->info.outfit; defaultOutfit = mType->info.outfit;
currentOutfit = mType->info.outfit; currentOutfit = mType->info.outfit;
@@ -80,45 +80,31 @@ 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);
} }
void Monster::onAttackedCreature(Creature* creature) bool Monster::canWalkOnFieldType(CombatType_t combatType) const
{ {
if (isSummon() && getMaster()) { switch (combatType) {
master->onAttackedCreature(creature); case COMBAT_ENERGYDAMAGE:
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);
@@ -252,7 +238,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();
@@ -264,7 +250,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 && targetChangeCooldown <= 0) { if ((offset_x > 1 || offset_y > 1) && mType->info.changeTargetChance > 0) {
Direction dir = getDirectionTo(position, followPosition); Direction dir = getDirectionTo(position, followPosition);
const Position& checkPosition = getNextPosition(dir, position); const Position& checkPosition = getNextPosition(dir, position);
@@ -379,10 +365,10 @@ void Monster::updateTargetList()
} }
} }
SpectatorVec list; SpectatorVec spectators;
g_game.map.getSpectators(list, position, true); g_game.map.getSpectators(spectators, position, true);
list.erase(this); spectators.erase(this);
for (Creature* spectator : list) { for (Creature* spectator : spectators) {
if (canSee(spectator->getPosition())) { if (canSee(spectator->getPosition())) {
onCreatureFound(spectator); onCreatureFound(spectator);
} }
@@ -495,14 +481,14 @@ void Monster::onCreatureLeave(Creature* creature)
} }
} }
bool Monster::searchTarget(TargetSearchType_t searchType) bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAULT*/)
{ {
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_ANY || canUseAttack(myPos, creature)) { if (searchType == TARGETSEARCH_RANDOM || canUseAttack(myPos, creature)) {
resultList.push_back(creature); resultList.push_back(creature);
} }
} }
@@ -511,31 +497,32 @@ bool Monster::searchTarget(TargetSearchType_t searchType)
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()) {
for (Creature* creature : resultList) { auto it = resultList.begin();
const Position& targetPosition = creature->getPosition(); target = *it;
int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition); if (++it != resultList.end()) {
if (distance < minRange) { const Position& targetPosition = target->getPosition();
target = creature; int32_t minRange = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition);
minRange = distance; do {
} const Position& pos = (*it)->getPosition();
int32_t distance = Position::getDistanceX(myPos, pos) + Position::getDistanceY(myPos, pos);
if (distance < minRange) {
target = *it;
minRange = distance;
}
} while (++it != resultList.end());
} }
} 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& targetPosition = creature->getPosition(); const Position& pos = creature->getPosition();
int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition); int32_t distance = Position::getDistanceX(myPos, pos) + Position::getDistanceY(myPos, pos);
if (distance < minRange) { if (distance < minRange) {
target = creature; target = creature;
minRange = distance; minRange = distance;
@@ -548,79 +535,10 @@ bool Monster::searchTarget(TargetSearchType_t searchType)
} }
break; break;
} }
case TARGETSEARCH_WEAKEST: {
Creature* target = nullptr;
int32_t health = 0; case TARGETSEARCH_DEFAULT:
if (attackedCreature) { case TARGETSEARCH_ATTACKRANGE:
health = attackedCreature->getMaxHealth(); case TARGETSEARCH_RANDOM:
}
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();
@@ -628,19 +546,20 @@ bool Monster::searchTarget(TargetSearchType_t searchType)
return selectTarget(*it); return selectTarget(*it);
} }
if (searchType == TARGETSEARCH_ATTACKRANGE) {
return false;
}
break; break;
} }
} }
//lets just pick the first target in the list if we do not have a target //lets just pick the first target in the list
if (!attackedCreature) { for (Creature* target : targetList) {
for (Creature* target : targetList) { if (followCreature != target && selectTarget(target)) {
if (followCreature != target && selectTarget(target)) { return true;
return true;
}
} }
} }
return false; return false;
} }
@@ -718,10 +637,6 @@ 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);
} }
@@ -758,7 +673,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 || type == CONDITION_AGGRESSIVE) { if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) {
updateMapCache(); updateMapCache();
} }
@@ -767,7 +682,8 @@ 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 || type == CONDITION_AGGRESSIVE) { if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) {
ignoreFieldDamage = false;
updateMapCache(); updateMapCache();
} }
@@ -776,10 +692,6 @@ 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) {
@@ -806,10 +718,9 @@ void Monster::onThink(uint32_t interval)
} }
} }
if (!isInSpawnRange(position) || (lifetime > 0 && (OTSYS_TIME() >= lifetime))) { if (!isInSpawnRange(position)) {
// Despawn creatures if they are out of their spawn zone g_game.internalTeleport(this, masterPos);
g_game.removeCreature(this); setIdle(true);
g_game.addMagicEffect(getPosition(), CONST_ME_POFF);
} else { } else {
updateIdleStatus(); updateIdleStatus();
@@ -819,6 +730,7 @@ 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.
@@ -827,23 +739,15 @@ 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(TARGETSEARCH_ANY); searchTarget();
} else if (isFleeing()) { } else if (isFleeing()) {
if (attackedCreature && !canUseAttack(getPosition(), attackedCreature)) { if (attackedCreature && !canUseAttack(getPosition(), attackedCreature)) {
searchTarget(TARGETSEARCH_NEAREST); searchTarget(TARGETSEARCH_ATTACKRANGE);
} }
} }
} }
@@ -855,43 +759,56 @@ void Monster::onThink(uint32_t interval)
} }
} }
void Monster::doAttacking(uint32_t) void Monster::doAttacking(uint32_t interval)
{ {
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();
bool updateLook = false; for (const spellBlock_t& spellBlock : mType->info.attackSpells) {
bool inRange = false;
if (OTSYS_TIME() >= earliestAttackTime && !isFleeing()) {
updateLook = true; if (attackedCreature == nullptr) {
if (Combat::closeAttack(this, attackedCreature, FIGHTMODE_BALANCED)) { break;
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
@@ -909,6 +826,40 @@ 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()) {
@@ -931,42 +882,13 @@ 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(0, 99)) { if (mType->info.changeTargetChance >= uniform_random(1, 100)) {
// search target strategies, if no strategy succeeds, target is not switched if (mType->info.targetDistance <= 1) {
int32_t random = uniform_random(0, 99); searchTarget(TARGETSEARCH_RANDOM);
int32_t current_strategy = 0; } else {
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);
} }
} }
} }
@@ -975,10 +897,23 @@ void Monster::onThinkTarget(uint32_t interval)
} }
} }
void Monster::onThinkDefense(uint32_t) void Monster::onThinkDefense(uint32_t interval)
{ {
bool resetTicks = true;
defenseTicks += interval;
for (const spellBlock_t& spellBlock : mType->info.defenseSpells) { for (const spellBlock_t& spellBlock : mType->info.defenseSpells) {
if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) { if (spellBlock.speed > defenseTicks) {
resetTicks = false;
continue;
}
if (defenseTicks % spellBlock.speed >= interval) {
//already used this spell for this round
continue;
}
if ((spellBlock.chance >= static_cast<uint32_t>(uniform_random(1, 100)))) {
minCombatValue = spellBlock.minCombatValue; minCombatValue = spellBlock.minCombatValue;
maxCombatValue = spellBlock.maxCombatValue; maxCombatValue = spellBlock.maxCombatValue;
spellBlock.spell->castSpell(this, this); spellBlock.spell->castSpell(this, this);
@@ -987,10 +922,20 @@ void Monster::onThinkDefense(uint32_t)
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) {
@@ -1002,41 +947,49 @@ void Monster::onThinkDefense(uint32_t)
continue; continue;
} }
if (normal_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) { if (summonBlock.chance < static_cast<uint32_t>(uniform_random(1, 100))) {
Monster* summon = Monster::createMonster(summonBlock.name); continue;
if (summon) { }
const Position& summonPos = getPosition();
addSummon(summon); Monster* summon = Monster::createMonster(summonBlock.name);
if (summon) {
if (!g_game.placeCreature(summon, summonPos, false, summonBlock.force)) { if (g_game.placeCreature(summon, getPosition(), false, summonBlock.force)) {
removeSummon(summon); summon->setDropLoot(false);
} else { summon->setSkillLoss(false);
g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE); summon->setMaster(this);
g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT); g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE);
} g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT);
} else {
delete summon;
} }
} }
} }
} }
if (resetTicks) {
defenseTicks = 0;
}
} }
void Monster::onThinkYell(uint32_t) void Monster::onThinkYell(uint32_t interval)
{ {
if (mType->info.voiceVector.empty()) { if (mType->info.yellSpeedTicks == 0) {
return; return;
} }
int32_t randomResult = rand(); yellTicks += interval;
if (rand() == 50 * (randomResult / 50)) { if (yellTicks >= mType->info.yellSpeedTicks) {
int32_t totalVoices = mType->info.voiceVector.size(); yellTicks = 0;
const voiceBlock_t& voice = mType->info.voiceVector[rand() % totalVoices + 1];
if (voice.yellText) { if (!mType->info.voiceVector.empty() && (mType->info.yellChance >= static_cast<uint32_t>(uniform_random(1, 100)))) {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, voice.text, false); uint32_t index = uniform_random(0, mType->info.voiceVector.size() - 1);
} const voiceBlock_t& vb = mType->info.voiceVector[index];
else {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, voice.text, false); if (vb.yellText) {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, vb.text, false);
} else {
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, vb.text, false);
}
} }
} }
} }
@@ -1159,117 +1112,27 @@ 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 (OTSYS_TIME() >= nextDanceStepRound) { if (getTimeSinceLastMove() >= 1000) {
updateLookDirection(); randomStepping = true;
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 (egibleToDance && OTSYS_TIME() >= earliestDanceTime) { } else if (mType->info.staticAttackChance < static_cast<uint32_t>(uniform_random(1, 100))) {
if (mType->info.targetDistance >= 4) { result = getDanceStep(getPosition(), direction);
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;
}
}
}
} }
} }
} }
@@ -1480,7 +1343,7 @@ bool Monster::getDistanceStep(const Position& targetPos, Direction& direction, b
//escape to NW , W or N [and some extra] //escape to NW , W or N [and some extra]
bool w = canWalkTo(creaturePos, DIRECTION_WEST); bool w = canWalkTo(creaturePos, DIRECTION_WEST);
bool n = canWalkTo(creaturePos, DIRECTION_NORTH); bool n = canWalkTo(creaturePos, DIRECTION_NORTH);
if (w && n) { if (w && n) {
direction = boolean_random() ? DIRECTION_WEST : DIRECTION_NORTH; direction = boolean_random() ? DIRECTION_WEST : DIRECTION_NORTH;
return true; return true;
@@ -1913,8 +1776,7 @@ void Monster::death(Creature*)
for (Creature* summon : summons) { for (Creature* summon : summons) {
summon->changeHealth(-summon->getHealth()); summon->changeHealth(-summon->getHealth());
summon->setMaster(nullptr); summon->removeMaster();
summon->decrementReferenceCounter();
} }
summons.clear(); summons.clear();
@@ -1993,86 +1855,61 @@ 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) {
else if (dir == DIRECTION_EAST) { newDir = DIRECTION_WEST;
} 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) {
else if (dir == DIRECTION_EAST) { newDir = DIRECTION_WEST;
} 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) {
else if (dir == DIRECTION_WEST) { newDir = DIRECTION_EAST;
} 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) {
else if (dir == DIRECTION_WEST) { newDir = DIRECTION_EAST;
} else if (dir == DIRECTION_WEST) {
newDir = DIRECTION_SOUTH; newDir = DIRECTION_SOUTH;
} }
} }
} }
} }
if (direction != newDir) { g_game.internalCreatureTurn(this, newDir);
g_game.internalCreatureTurn(this, newDir);
}
} }
void Monster::dropLoot(Container* corpse, Creature*) void Monster::dropLoot(Container* corpse, Creature*)
{ {
if (corpse && lootDrop) { if (corpse && lootDrop) {
mType->createLoot(corpse); g_events->eventMonsterOnDropLoot(this, corpse);
} }
} }
@@ -2084,6 +1921,12 @@ 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);
} }
@@ -2104,70 +1947,12 @@ bool Monster::challengeCreature(Creature* creature)
bool result = selectTarget(creature); bool result = selectTarget(creature);
if (result) { if (result) {
targetChangeCooldown = 1000; targetChangeCooldown = 8000;
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,17 +26,15 @@
class Creature; class Creature;
class Game; class Game;
class Spawn; class Spawn;
class Combat;
typedef std::unordered_set<Creature*> CreatureHashSet; using CreatureHashSet = std::unordered_set<Creature*>;
typedef std::list<Creature*> CreatureList; using CreatureList = std::list<Creature*>;
enum TargetSearchType_t { enum TargetSearchType_t {
TARGETSEARCH_ANY, TARGETSEARCH_DEFAULT,
TARGETSEARCH_RANDOM, TARGETSEARCH_RANDOM,
TARGETSEARCH_ATTACKRANGE,
TARGETSEARCH_NEAREST, TARGETSEARCH_NEAREST,
TARGETSEARCH_WEAKEST,
TARGETSEARCH_MOSTDAMAGE,
}; };
class Monster final : public Creature class Monster final : public Creature
@@ -46,39 +44,43 @@ 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() final { Monster* getMonster() override {
return this; return this;
} }
const Monster* getMonster() const final { const Monster* getMonster() const override {
return this; return this;
} }
void setID() final { void setID() override {
if (id == 0) { if (id == 0) {
id = monsterAutoID++; id = monsterAutoID++;
} }
} }
void removeList() final; void removeList() override;
void addList() final; void addList() override;
const std::string& getName() const final { const std::string& getName() const override {
return mType->name; return mType->name;
} }
const std::string& getNameDescription() const final { const std::string& getNameDescription() const override {
return mType->nameDescription; return mType->nameDescription;
} }
std::string getDescription(int32_t) const final { std::string getDescription(int32_t) const override {
return strDescription + '.'; return strDescription + '.';
} }
CreatureType_t getType() const override {
return CREATURETYPE_MONSTER;
}
const Position& getMasterPos() const { const Position& getMasterPos() const {
return masterPos; return masterPos;
} }
@@ -86,22 +88,19 @@ class Monster final : public Creature
masterPos = pos; masterPos = pos;
} }
RaceType_t getRace() const final { RaceType_t getRace() const override {
return mType->info.race; return mType->info.race;
} }
int32_t getArmor() const final { int32_t getArmor() const override {
int32_t armor = mType->info.armor; return mType->info.armor;
if (armor > 1) {
return rand() % (mType->info.armor >> 1) + (mType->info.armor >> 1);
}
return armor;
} }
int32_t getDefense() final; int32_t getDefense() const override {
bool isPushable() const final { return mType->info.defense;
}
bool isPushable() const override {
return mType->info.pushable && baseSpeed != 0; return mType->info.pushable && baseSpeed != 0;
} }
bool isAttackable() const final { bool isAttackable() const override {
return mType->info.isAttackable; return mType->info.isAttackable;
} }
@@ -114,8 +113,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 final; bool canSee(const Position& pos) const override;
bool canSeeInvisibility() const final { bool canSeeInvisibility() const override {
return isImmune(CONDITION_INVISIBLE); return isImmune(CONDITION_INVISIBLE);
} }
uint32_t getManaCost() const { uint32_t getManaCost() const {
@@ -124,31 +123,35 @@ 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 onCreatureAppear(Creature* creature, bool isLogin) final; void onAttackedCreatureDisappear(bool isLogout) override;
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 drainHealth(Creature* attacker, int32_t damage) final; void onCreatureAppear(Creature* creature, bool isLogin) override;
void changeHealth(int32_t healthChange, bool sendHealthChange = true) final; void onRemoveCreature(Creature* creature, bool isLogout) override;
void onWalk() final; void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) override;
bool getNextStep(Direction& direction, uint32_t& flags) final; void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override;
void onFollowCreatureComplete(const Creature* creature) final;
void onThink(uint32_t interval) final; void drainHealth(Creature* attacker, int32_t damage) override;
void changeHealth(int32_t healthChange, bool sendHealthChange = true) override;
void onWalk() override;
bool getNextStep(Direction& direction, uint32_t& flags) override;
void onFollowCreatureComplete(const Creature* creature) override;
bool challengeCreature(Creature* creature) final; void onThink(uint32_t interval) override;
bool convinceCreature(Creature* creature) final;
void setNormalCreatureLight() final; bool challengeCreature(Creature* creature) override;
bool getCombatValues(int32_t& min, int32_t& max) final;
void doAttacking(uint32_t interval) final; void setNormalCreatureLight() override;
bool getCombatValues(int32_t& min, int32_t& max) override;
bool searchTarget(TargetSearchType_t searchType); void doAttacking(uint32_t interval) override;
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 {
@@ -167,9 +170,12 @@ 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); bool checkDefense = false, bool checkArmor = false, bool field = false) override;
static uint32_t monsterAutoID; static uint32_t monsterAutoID;
@@ -182,13 +188,13 @@ class Monster final : public Creature
MonsterType* mType; MonsterType* mType;
Spawn* spawn = nullptr; Spawn* spawn = nullptr;
int64_t lifetime = 0; int64_t lastMeleeAttack = 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;
@@ -197,8 +203,10 @@ 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 egibleToDance = true; bool randomStepping = false;
bool ignoreFieldDamage = false;
void onCreatureEnter(Creature* creature); void onCreatureEnter(Creature* creature);
void onCreatureLeave(Creature* creature); void onCreatureLeave(Creature* creature);
@@ -215,8 +223,8 @@ class Monster final : public Creature
void clearTargetList(); void clearTargetList();
void clearFriendList(); void clearFriendList();
void death(Creature* lastHitCreature) final; void death(Creature* lastHitCreature) override;
Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) final; Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) override;
void setIdle(bool idle); void setIdle(bool idle);
void updateIdleStatus(); void updateIdleStatus();
@@ -224,11 +232,12 @@ class Monster final : public Creature
return isIdle; return isIdle;
} }
void onAddCondition(ConditionType_t type) final; void onAddCondition(ConditionType_t type) override;
void onEndCondition(ConditionType_t type) final; void onEndCondition(ConditionType_t type) override;
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);
@@ -247,28 +256,25 @@ 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 final { uint64_t getLostExperience() const override {
return skillLoss ? mType->info.experience : 0; return skillLoss ? mType->info.experience : 0;
} }
uint16_t getLookCorpse() const final { uint16_t getLookCorpse() const override {
return mType->info.lookcorpse; return mType->info.lookcorpse;
} }
void dropLoot(Container* corpse, Creature* lastHitCreature) final; void dropLoot(Container* corpse, Creature* lastHitCreature) override;
uint32_t getDamageImmunities() const final { uint32_t getDamageImmunities() const override {
return mType->info.damageImmunities; return mType->info.damageImmunities;
} }
uint32_t getConditionImmunities() const final { uint32_t getConditionImmunities() const override {
return mType->info.conditionImmunities; return mType->info.conditionImmunities;
} }
void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const final; void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const override;
bool useCacheMap() const final { bool useCacheMap() const override {
return true; return !randomStepping;
} }
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,9 @@
#include "creature.h" #include "creature.h"
#define MAX_LOOTCHANCE 1000
#define MAX_STATICWALK 100 const uint32_t MAX_LOOTCHANCE = 100000;
const uint32_t MAX_STATICWALK = 100;
struct LootBlock { struct LootBlock {
uint16_t id; uint16_t id;
@@ -38,7 +39,7 @@ struct LootBlock {
std::vector<LootBlock> childLoot; std::vector<LootBlock> childLoot;
LootBlock() { LootBlock() {
id = 0; id = 0;
countmax = 0; countmax = 1;
chance = 0; chance = 0;
subType = -1; subType = -1;
@@ -46,9 +47,21 @@ 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;
}; };
@@ -62,23 +75,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),
attack(other.attack), combatSpell(other.combatSpell),
skill(other.skill), isMelee(other.isMelee) {
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 {
@@ -111,11 +124,14 @@ 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 = 70; uint32_t baseSpeed = 200;
int32_t creatureAppearEvent = -1; int32_t creatureAppearEvent = -1;
int32_t creatureDisappearEvent = -1; int32_t creatureDisappearEvent = -1;
@@ -127,15 +143,8 @@ 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 strategyNearestEnemy = 0;
int32_t strategyWeakestEnemy = 0;
int32_t strategyMostDamageEnemy = 0;
int32_t strategyRandomEnemy = 0;
int32_t armor = 0;
int32_t defense = 0; int32_t defense = 0;
int32_t attack = 0; int32_t armor = 0;
int32_t skill = 0;
int32_t poison = 0;
bool canPushItems = false; bool canPushItems = false;
bool canPushCreatures = false; bool canPushCreatures = false;
@@ -146,6 +155,11 @@ 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:
@@ -155,14 +169,57 @@ 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 createLoot(Container* corpse); void loadLoot(MonsterType* monsterType, LootBlock lootblock);
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
@@ -180,20 +237,23 @@ 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 = "");
static uint32_t getLootRandom(); std::unique_ptr<LuaScriptInterface> scriptInterface;
private: private:
ConditionDamage* getDamageCondition(ConditionType_t conditionType, int32_t cycles, int32_t count, int32_t max_count); ConditionDamage* getDamageCondition(ConditionType_t conditionType,
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 = "");
bool loadMonster(const std::string& file, const std::string& monsterName, std::list<std::pair<MonsterType*, std::string>>& monsterScriptList, bool reloading = false); MonsterType* loadMonster(const std::string& file, const std::string& monsterName, 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::unique_ptr<LuaScriptInterface> scriptInterface; std::map<std::string, std::string> unloadedMonsters;
bool loaded = false; bool loaded = false;
}; };

82
src/mounts.cpp Normal file
View File

@@ -0,0 +1,82 @@
/**
* 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;
}

52
src/mounts.h Normal file
View File

@@ -0,0 +1,52 @@
/**
* 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,43 +36,49 @@ MoveEvents::MoveEvents() :
MoveEvents::~MoveEvents() MoveEvents::~MoveEvents()
{ {
clear(); clear(false);
} }
void MoveEvents::clearMap(MoveListMap& map) void MoveEvents::clearMap(MoveListMap& map, bool fromLua)
{ {
std::unordered_set<MoveEvent*> set; for (auto it = map.begin(); it != map.end(); ++it) {
for (const auto& it : map) { for (int eventType = MOVE_EVENT_STEP_IN; eventType < MOVE_EVENT_LAST; ++eventType) {
const MoveEventList& moveEventList = it.second; auto& moveEvents = it->second.moveEvent[eventType];
for (const auto& i : moveEventList.moveEvent) { for (auto find = moveEvents.begin(); find != moveEvents.end(); ) {
for (MoveEvent* moveEvent : i) { if (fromLua == find->fromLua) {
set.insert(moveEvent); find = moveEvents.erase(find);
} else {
++find;
}
} }
} }
} }
map.clear();
for (MoveEvent* moveEvent : set) {
delete moveEvent;
}
} }
void MoveEvents::clear() void MoveEvents::clearPosMap(MovePosListMap& map, bool fromLua)
{ {
clearMap(itemIdMap); for (auto it = map.begin(); it != map.end(); ++it) {
clearMap(movementIdMap); for (int eventType = MOVE_EVENT_STEP_IN; eventType < MOVE_EVENT_LAST; ++eventType) {
auto& moveEvents = it->second.moveEvent[eventType];
for (const auto& it : positionMap) { for (auto find = moveEvents.begin(); find != moveEvents.end(); ) {
const MoveEventList& moveEventList = it.second; if (fromLua == find->fromLua) {
for (const auto& i : moveEventList.moveEvent) { find = moveEvents.erase(find);
for (MoveEvent* moveEvent : i) { } else {
delete moveEvent; ++find;
}
} }
} }
} }
positionMap.clear(); }
scriptInterface.reInitState(); void MoveEvents::clear(bool fromLua)
{
clearMap(itemIdMap, fromLua);
clearMap(actionIdMap, fromLua);
clearMap(uniqueIdMap, fromLua);
clearPosMap(positionMap, fromLua);
reInitState(fromLua);
} }
LuaScriptInterface& MoveEvents::getScriptInterface() LuaScriptInterface& MoveEvents::getScriptInterface()
@@ -85,17 +91,17 @@ std::string MoveEvents::getScriptBaseName() const
return "movements"; return "movements";
} }
Event* MoveEvents::getEvent(const std::string& nodeName) Event_ptr 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 new MoveEvent(&scriptInterface); return Event_ptr(new MoveEvent(&scriptInterface));
} }
bool MoveEvents::registerEvent(Event* event, const pugi::xml_node& node) bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
{ {
MoveEvent* moveEvent = static_cast<MoveEvent*>(event); //event is guaranteed to be a MoveEvent MoveEvent_ptr moveEvent{static_cast<MoveEvent*>(event.release())}; //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) {
@@ -117,7 +123,6 @@ bool MoveEvents::registerEvent(Event* 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();
@@ -125,11 +130,12 @@ bool MoveEvents::registerEvent(Event* 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);
@@ -139,7 +145,7 @@ bool MoveEvents::registerEvent(Event* 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();
@@ -149,17 +155,26 @@ bool MoveEvents::registerEvent(Event* 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("movementid"))) { } else if ((attr = node.attribute("uniqueid"))) {
addEvent(moveEvent, pugi::cast<int32_t>(attr.value()), movementIdMap); addEvent(std::move(*moveEvent), pugi::cast<int32_t>(attr.value()), uniqueIdMap);
} else if ((attr = node.attribute("frommovementid"))) { } else if ((attr = node.attribute("fromuid"))) {
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("tomovementid").value()); uint32_t endId = pugi::cast<uint32_t>(node.attribute("touid").value());
addEvent(moveEvent, id, movementIdMap); addEvent(*moveEvent, id, uniqueIdMap);
while (++id <= endId) { while (++id <= endId) {
addEvent(moveEvent, id, movementIdMap); addEvent(*moveEvent, id, uniqueIdMap);
}
} else if ((attr = node.attribute("actionid"))) {
addEvent(std::move(*moveEvent), pugi::cast<int32_t>(attr.value()), actionIdMap);
} else if ((attr = node.attribute("fromaid"))) {
uint32_t id = pugi::cast<uint32_t>(attr.value());
uint32_t endId = pugi::cast<uint32_t>(node.attribute("toaid").value());
addEvent(*moveEvent, id, actionIdMap);
while (++id <= endId) {
addEvent(*moveEvent, id, actionIdMap);
} }
} 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(), ";"));
@@ -168,28 +183,125 @@ bool MoveEvents::registerEvent(Event* event, const pugi::xml_node& node)
} }
Position pos(posList[0], posList[1], posList[2]); Position pos(posList[0], posList[1], posList[2]);
addEvent(moveEvent, pos, positionMap); addEvent(std::move(*moveEvent), pos, positionMap);
} else { } else {
return false; return false;
} }
return true; return true;
} }
void MoveEvents::addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map) bool MoveEvents::registerLuaFunction(MoveEvent* event)
{
MoveEvent_ptr moveEvent{ event };
if (moveEvent->getItemIdRange().size() > 0) {
if (moveEvent->getItemIdRange().size() == 1) {
uint32_t id = moveEvent->getItemIdRange().at(0);
addEvent(*moveEvent, id, itemIdMap);
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
ItemType& it = Item::items.getItemType(id);
it.wieldInfo = moveEvent->getWieldInfo();
it.minReqLevel = moveEvent->getReqLevel();
it.minReqMagicLevel = moveEvent->getReqMagLv();
it.vocationString = moveEvent->getVocationString();
}
} else {
uint32_t iterId = 0;
while (++iterId < moveEvent->getItemIdRange().size()) {
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
ItemType& it = Item::items.getItemType(moveEvent->getItemIdRange().at(iterId));
it.wieldInfo = moveEvent->getWieldInfo();
it.minReqLevel = moveEvent->getReqLevel();
it.minReqMagicLevel = moveEvent->getReqMagLv();
it.vocationString = moveEvent->getVocationString();
}
addEvent(*moveEvent, moveEvent->getItemIdRange().at(iterId), itemIdMap);
}
}
} else {
return false;
}
return true;
}
bool MoveEvents::registerLuaEvent(MoveEvent* event)
{
MoveEvent_ptr moveEvent{ event };
if (moveEvent->getItemIdRange().size() > 0) {
if (moveEvent->getItemIdRange().size() == 1) {
uint32_t id = moveEvent->getItemIdRange().at(0);
addEvent(*moveEvent, id, itemIdMap);
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
ItemType& it = Item::items.getItemType(id);
it.wieldInfo = moveEvent->getWieldInfo();
it.minReqLevel = moveEvent->getReqLevel();
it.minReqMagicLevel = moveEvent->getReqMagLv();
it.vocationString = moveEvent->getVocationString();
}
} else {
auto v = moveEvent->getItemIdRange();
for (auto i = v.begin(); i != v.end(); i++) {
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
ItemType& it = Item::items.getItemType(*i);
it.wieldInfo = moveEvent->getWieldInfo();
it.minReqLevel = moveEvent->getReqLevel();
it.minReqMagicLevel = moveEvent->getReqMagLv();
it.vocationString = moveEvent->getVocationString();
}
addEvent(*moveEvent, *i, itemIdMap);
}
}
} else if (moveEvent->getActionIdRange().size() > 0) {
if (moveEvent->getActionIdRange().size() == 1) {
int32_t id = moveEvent->getActionIdRange().at(0);
addEvent(*moveEvent, id, actionIdMap);
} else {
auto v = moveEvent->getActionIdRange();
for (auto i = v.begin(); i != v.end(); i++) {
addEvent(*moveEvent, *i, actionIdMap);
}
}
} else if (moveEvent->getUniqueIdRange().size() > 0) {
if (moveEvent->getUniqueIdRange().size() == 1) {
int32_t id = moveEvent->getUniqueIdRange().at(0);
addEvent(*moveEvent, id, uniqueIdMap);
} else {
auto v = moveEvent->getUniqueIdRange();
for (auto i = v.begin(); i != v.end(); i++) {
addEvent(*moveEvent, *i, uniqueIdMap);
}
}
} else if (moveEvent->getPosList().size() > 0) {
if (moveEvent->getPosList().size() == 1) {
Position pos = moveEvent->getPosList().at(0);
addEvent(*moveEvent, pos, positionMap);
} else {
auto v = moveEvent->getPosList();
for (auto i = v.begin(); i != v.end(); i++) {
addEvent(*moveEvent, *i, positionMap);
}
}
} else {
return false;
}
return true;
}
void MoveEvents::addEvent(MoveEvent moveEvent, int32_t id, MoveListMap& map)
{ {
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(moveEvent); moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(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(moveEvent); moveEventList.push_back(std::move(moveEvent));
} }
} }
@@ -212,59 +324,64 @@ 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;
} }
} }
} }
return nullptr; return nullptr;
} }
MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType) MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType)
{ {
MoveListMap::iterator it; MoveListMap::iterator it;
if (item->hasAttribute(ITEM_ATTRIBUTE_MOVEMENTID)) { if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
it = movementIdMap.find(item->getMovementId()); it = uniqueIdMap.find(item->getUniqueId());
if (it != movementIdMap.end()) { if (it != uniqueIdMap.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->hasCollisionEvent() && !item->hasSeparationEvent()) { if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) {
return nullptr; it = actionIdMap.find(item->getActionId());
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(moveEvent); moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(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(moveEvent); moveEventList.push_back(std::move(moveEvent));
} }
} }
@@ -272,9 +389,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;
@@ -325,7 +442,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, true); return moveEvent->fireEquip(player, item, slot, false);
} }
uint32_t MoveEvents::onItemMove(Item* item, Tile* tile, bool isAdd) uint32_t MoveEvents::onItemMove(Item* item, Tile* tile, bool isAdd)
@@ -371,20 +488,6 @@ 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) {
@@ -521,40 +624,6 @@ 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();
@@ -646,10 +715,6 @@ 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);
@@ -682,6 +747,13 @@ 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();
} }
@@ -757,6 +829,13 @@ 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();
} }
@@ -783,6 +862,44 @@ 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) {
@@ -816,19 +933,24 @@ 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 boolean) uint32_t MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool isCheck)
{ {
if (scripted) { if (scripted) {
return executeEquip(player, item, slot); if (!equipFunction || equipFunction(this, player, item, slot, isCheck) == 1) {
if (executeEquip(player, item, slot, isCheck)) {
return 1;
}
}
return 0;
} else { } else {
return equipFunction(this, player, item, slot, boolean); return equipFunction(this, player, item, slot, isCheck);
} }
} }
bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot) bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool isCheck)
{ {
//onEquip(player, item, slot) //onEquip(player, item, slot, isCheck)
//onDeEquip(player, item, slot) //onDeEquip(player, item, slot, isCheck)
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;
@@ -844,8 +966,9 @@ bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot)
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(3); return scriptInterface->callFunction(4);
} }
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,9 @@
#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,
@@ -39,12 +42,13 @@ 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];
}; };
typedef std::map<uint16_t, bool> VocEquipMap; using VocEquipMap = std::map<uint16_t, bool>;
class MoveEvents final : public BaseEvents class MoveEvents final : public BaseEvents
{ {
@@ -63,51 +67,54 @@ class MoveEvents final : public BaseEvents
MoveEvent* getEvent(Item* item, MoveEvent_t eventType); MoveEvent* getEvent(Item* item, MoveEvent_t eventType);
protected: bool registerLuaEvent(MoveEvent* event);
typedef std::map<int32_t, MoveEventList> MoveListMap; bool registerLuaFunction(MoveEvent* event);
void clearMap(MoveListMap& map); void clear(bool fromLua) override final;
typedef std::map<Position, MoveEventList> MovePosListMap; private:
void clear() final; using MoveListMap = std::map<int32_t, MoveEventList>;
LuaScriptInterface& getScriptInterface() final; using MovePosListMap = std::map<Position, MoveEventList>;
std::string getScriptBaseName() const final; void clearMap(MoveListMap& map, bool fromLua);
Event* getEvent(const std::string& nodeName) final; void clearPosMap(MovePosListMap& map, bool fromLua);
bool registerEvent(Event* event, const pugi::xml_node& node) final;
void addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map); LuaScriptInterface& getScriptInterface() override;
std::string getScriptBaseName() const override;
Event_ptr getEvent(const std::string& nodeName) override;
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
void addEvent(MoveEvent* moveEvent, const Position& pos, MovePosListMap& map); void addEvent(MoveEvent moveEvent, int32_t id, MoveListMap& 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 movementIdMap; MoveListMap actionIdMap;
MoveListMap itemIdMap; MoveListMap itemIdMap;
MovePosListMap positionMap; MovePosListMap positionMap;
LuaScriptInterface scriptInterface; LuaScriptInterface scriptInterface;
}; };
typedef uint32_t (StepFunction)(Creature* creature, Item* item, const Position& pos); using StepFunction = std::function<uint32_t(Creature* creature, Item* item, const Position& pos)>;
typedef uint32_t (MoveFunction)(Item* item, Item* tileItem, const Position& pos); using MoveFunction = std::function<uint32_t(Item* item, Item* tileItem, const Position& pos)>;
typedef uint32_t (EquipFunction)(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean); using EquipFunction = std::function<uint32_t(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) final; bool configureEvent(const pugi::xml_node& node) override;
bool loadFunction(const pugi::xml_attribute& attr) final; bool loadFunction(const pugi::xml_attribute& attr, bool isScripted) override;
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 boolean); uint32_t fireEquip(Player* player, Item* item, slots_t slot, bool isCheck);
uint32_t getSlot() const { uint32_t getSlot() const {
return slot; return slot;
@@ -115,7 +122,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 executeEquip(Player* player, Item* item, slots_t slot, bool isCheck);
bool executeAddRemItem(Item* item, Item* tileItem, const Position& pos); bool executeAddRemItem(Item* item, Item* tileItem, const Position& pos);
// //
@@ -132,29 +139,104 @@ 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;
}
protected: static uint32_t StepInField(Creature* creature, Item* item, const Position& pos);
std::string getScriptEventName() const final; static uint32_t StepOutField(Creature* creature, Item* item, const Position& pos);
static StepFunction StepInField; static uint32_t AddItemField(Item* item, Item* tileItem, const Position& pos);
static StepFunction StepOutField; static uint32_t RemoveItemField(Item* item, Item* tileItem, const Position& pos);
static MoveFunction AddItemField; static uint32_t EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
static MoveFunction RemoveItemField; static uint32_t DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
static EquipFunction EquipItem;
static EquipFunction DeEquipItem;
MoveEvent_t eventType = MOVE_EVENT_NONE; MoveEvent_t eventType = MOVE_EVENT_NONE;
StepFunction* stepFunction = nullptr; StepFunction stepFunction;
MoveFunction* moveFunction = nullptr; MoveFunction moveFunction;
EquipFunction* equipFunction = nullptr; EquipFunction equipFunction;
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;
@@ -163,6 +245,12 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,16 +99,18 @@ void NetworkMessage::addItem(uint16_t id, uint8_t count)
{ {
const ItemType& it = Item::items[id]; const ItemType& it = Item::items[id];
if (it.disguise) { add<uint16_t>(it.clientId);
add<uint16_t>(it.disguiseId);
} else { addByte(0xFF); // MARK_UNMARKED
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(getLiquidColor(count)); addByte(fluidMap[count & 7]);
}
if (it.isAnimation) {
addByte(0xFE); // random phase (0xFF for async)
} }
} }
@@ -116,20 +118,21 @@ void NetworkMessage::addItem(const Item* item)
{ {
const ItemType& it = Item::items[item->getID()]; const ItemType& it = Item::items[item->getID()];
if (it.disguise) { add<uint16_t>(it.clientId);
add<uint16_t>(it.disguiseId); addByte(0xFF); // MARK_UNMARKED
} 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(getLiquidColor(item->getFluidType())); addByte(fluidMap[item->getFluidType() & 7]);
}
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>(itemId); add<uint16_t>(Item::items[itemId].clientId);
} }

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,16 @@ class RSA;
class NetworkMessage class NetworkMessage
{ {
public: public:
typedef uint16_t MsgSize_t; using MsgSize_t = uint16_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 = 4; static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 8;
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 - XTEA_MULTIPLE }; enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - CHECKSUM_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;
@@ -148,18 +150,6 @@ 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;
@@ -168,6 +158,19 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,20 +21,86 @@
#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:
@@ -44,41 +110,53 @@ 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() final { Npc* getNpc() override {
return this; return this;
} }
const Npc* getNpc() const final { const Npc* getNpc() const override {
return this; return this;
} }
bool isPushable() const final { bool isPushable() const override {
return baseSpeed > 0; return pushable && walkTicks != 0;
} }
void setID() final { void setID() override {
if (id == 0) { if (id == 0) {
id = npcAutoID++; id = npcAutoID++;
} }
} }
void removeList() final; void removeList() override;
void addList() final; void addList() override;
static Npc* createNpc(const std::string& name); static Npc* createNpc(const std::string& name);
bool canSee(const Position& pos) const final; bool canSee(const Position& pos) const override;
bool load(); bool load();
void reload(); void reload();
const std::string& getName() const final { const std::string& getName() const override {
return name; return name;
} }
const std::string& getNameDescription() const final { const std::string& getNameDescription() const override {
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);
@@ -90,38 +168,45 @@ 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 == 0) { if (masterRadius == -1) {
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;
protected: private:
explicit Npc(const std::string& name); explicit Npc(const std::string& name);
void onCreatureAppear(Creature* creature, bool isLogin) final; void onCreatureAppear(Creature* creature, bool isLogin) override;
void onRemoveCreature(Creature* creature, bool isLogout) final; void onRemoveCreature(Creature* creature, bool isLogout) override;
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) final; const Tile* oldTile, const Position& oldPos, bool teleport) override;
void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) final; void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override;
void onThink(uint32_t interval) final; void onThink(uint32_t interval) override;
std::string getDescription(int32_t lookDistance) const final; std::string getDescription(int32_t lookDistance) const override;
bool isImmune(CombatType_t) const final { bool isImmune(CombatType_t) const override {
return true; return !attackable;
} }
bool isImmune(ConditionType_t) const final { bool isImmune(ConditionType_t) const override {
return true; return !attackable;
} }
bool isAttackable() const final { bool isAttackable() const override {
return false; return attackable;
} }
bool getNextStep(Direction& dir, uint32_t& flags) final; bool getNextStep(Direction& dir, uint32_t& flags) override;
void setIdle(bool idle); void setIdle(bool idle);
void updateIdleStatus(); void updateIdleStatus();
@@ -130,29 +215,41 @@ 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 lastTalkCreature; uint32_t walkTicks;
uint32_t focusCreature; int32_t focusCreature;
uint32_t masterRadius; int32_t masterRadius;
int64_t conversationStartTime; uint8_t speechBubble;
int64_t conversationEndTime;
int64_t staticMovementTime;
bool floorChange;
bool attackable;
bool ignoreHeight;
bool loaded; bool loaded;
bool isIdle; bool isIdle;
bool pushable;
BehaviourDatabase* behaviourDatabase; static NpcScriptInterface* scriptInterface;
friend class Npcs; friend class Npcs;
friend class BehaviourDatabase; friend class NpcScriptInterface;
}; };
#endif #endif

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,18 +23,19 @@
#include "game.h" #include "game.h"
#ifndef _WIN32 #include "iomarket.h"
#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;
@@ -44,6 +45,7 @@ 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;
@@ -56,9 +58,9 @@ void startupErrorMessage(const std::string& errorStr)
g_loaderSignal.notify_all(); g_loaderSignal.notify_all();
} }
void mainLoader(int argc, char* argv[], ServiceManager* servicer); void mainLoader(int argc, char* argv[], ServiceManager* services);
void badAllocationHandler() [[noreturn]] 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");
@@ -71,15 +73,6 @@ 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();
@@ -91,19 +84,6 @@ 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;
@@ -146,6 +126,21 @@ 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()) {
@@ -165,16 +160,14 @@ 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;
Database* db = Database::getInstance(); if (!Database::getInstance().connect()) {
if (!db->connect()) {
startupErrorMessage("Failed to connect to database."); startupErrorMessage("Failed to connect to database.");
return; return;
} }
@@ -190,6 +183,8 @@ 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;
} }
@@ -203,26 +198,42 @@ 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.loadItems()) { if (!Item::items.loadFromOtb("data/items/items.otb")) {
startupErrorMessage("Unable to load items (SRV)!"); startupErrorMessage("Unable to load items (OTB)!");
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;
auto& outfits = Outfits::getInstance(); if (!Outfits::getInstance().loadFromXml()) {
if (!outfits.loadFromXml()) {
startupErrorMessage("Unable to load outfits!"); startupErrorMessage("Unable to load outfits!");
return; return;
} }
@@ -255,11 +266,14 @@ 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>(g_config.getNumber(ConfigManager::GAME_PORT)); services->add<ProtocolGame>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::GAME_PORT)));
services->add<ProtocolLogin>(g_config.getNumber(ConfigManager::LOGIN_PORT)); services->add<ProtocolLogin>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
// OT protocols // OT protocols
services->add<ProtocolStatus>(g_config.getNumber(ConfigManager::STATUS_PORT)); services->add<ProtocolStatus>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::STATUS_PORT)));
// Legacy login protocol
services->add<ProtocolOld>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
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));
@@ -278,6 +292,9 @@ 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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,14 +29,6 @@ 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);
@@ -79,5 +71,7 @@ void OutputMessagePool::removeProtocolFromAutosend(const Protocol_ptr& protocol)
OutputMessage_ptr OutputMessagePool::getOutputMessage() OutputMessage_ptr OutputMessagePool::getOutputMessage()
{ {
return std::allocate_shared<OutputMessage>(OutputMessageAllocator()); // LockfreePoolingAllocator<void,...> will leave (void* allocate) ill-formed because
// of sizeof(T), so this guaranatees that only one list will be initialized
return std::allocate_shared<OutputMessage>(LockfreePoolingAllocator<void, OUTPUTMESSAGE_FREE_LIST_CAPACITY>());
} }

View File

@@ -1,6 +1,6 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,76 +28,80 @@ 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() { void addCryptoHeader(bool addChecksum) {
writeMessageLength(); if (addChecksum) {
} add_header(adlerChecksum(buffer + outputBufferStart, info.length));
}
inline void append(const NetworkMessage& msg) { writeMessageLength();
auto msgLen = msg.getLength(); }
memcpy(buffer + info.position, msg.getBuffer() + 4, msgLen);
info.length += msgLen;
info.position += msgLen;
}
inline void append(const OutputMessage_ptr& msg) { void append(const NetworkMessage& msg) {
auto msgLen = msg->getLength(); auto msgLen = msg.getLength();
memcpy(buffer + info.position, msg->getBuffer() + 4, msgLen); memcpy(buffer + info.position, msg.getBuffer() + 8, msgLen);
info.length += msgLen; info.length += msgLen;
info.position += msgLen; info.position += msgLen;
} }
protected: void append(const OutputMessage_ptr& msg) {
template <typename T> auto msgLen = msg->getLength();
inline void add_header(T add) { memcpy(buffer + info.position, msg->getBuffer() + 8, msgLen);
assert(outputBufferStart >= sizeof(T)); info.length += msgLen;
outputBufferStart -= sizeof(T); info.position += msgLen;
memcpy(buffer + outputBufferStart, &add, sizeof(T)); }
//added header size to the message size
info.length += sizeof(T);
}
MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION; private:
template <typename T>
void add_header(T add) {
assert(outputBufferStart >= sizeof(T));
outputBufferStart -= sizeof(T);
memcpy(buffer + outputBufferStart, &add, sizeof(T));
//added header size to the message size
info.length += sizeof(T);
}
MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION;
}; };
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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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,10 +112,12 @@ 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);
@@ -125,7 +127,6 @@ 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);
@@ -212,9 +213,10 @@ 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());
@@ -242,6 +244,12 @@ 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;
@@ -269,7 +277,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 (memberList.empty() && inviteList.empty()) { if (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);
@@ -279,6 +287,11 @@ 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);
@@ -323,15 +336,6 @@ 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) {
@@ -343,30 +347,6 @@ 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) {
@@ -419,7 +399,7 @@ bool Party::canUseSharedExperience(const Player* player) const
} }
} }
uint32_t minLevel = static_cast<int32_t>(std::ceil((static_cast<float>(highestLevel) * 2) / 3)); uint32_t minLevel = static_cast<uint32_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 @@
/** /**
* Tibia GIMUD Server - a free and open-source MMORPG server emulator * The Forgotten Server - a free and open-source MMORPG server emulator
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com> * Copyright (C) 2019 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;
typedef std::vector<Player*> PlayerVector; using PlayerVector = std::vector<Player*>;
class Party class Party
{ {
@@ -61,13 +61,12 @@ 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;
@@ -78,12 +77,10 @@ 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);
protected: private:
bool canEnableSharedExperience(); bool canEnableSharedExperience();
std::map<uint32_t, int64_t> ticksMap; std::map<uint32_t, int64_t> ticksMap;
@@ -93,8 +90,6 @@ 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