mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-05-11 22:19:21 +02:00
Revert "commit newest tfs branch only for compare"
This reverts commit 1f7dcd73477eae559e0f5915aece1388a54d0b76.
This commit is contained in:
parent
1f7dcd7347
commit
447b858551
@ -4,6 +4,7 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/ban.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/baseevents.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/bed.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/behaviourdatabase.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/chat.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/combat.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/condition.cpp
|
||||
@ -16,7 +17,6 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/database.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/databasemanager.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/databasetasks.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/depotchest.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/depotlocker.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/events.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/fileloader.cpp
|
||||
@ -26,12 +26,10 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/groups.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/house.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/housetile.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/inbox.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/ioguild.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/iologindata.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/iomap.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/iomapserialize.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/iomarket.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/item.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/items.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/luascript.cpp
|
||||
@ -39,7 +37,6 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/map.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/monster.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/monsters.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/mounts.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/movement.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/networkmessage.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/npc.cpp
|
||||
@ -52,29 +49,24 @@ set(tfs_SRC
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocol.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocolgame.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocollogin.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocolold.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/protocolstatus.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/quests.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/raids.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/rsa.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/scheduler.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/scriptmanager.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/script.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/server.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/signals.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/spawn.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/spells.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/script.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/talkaction.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/tasks.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/teleport.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/thing.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/tile.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/tools.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/trashholder.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/vocation.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/waitlist.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/weapons.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/wildcardtree.cpp
|
||||
${CMAKE_CURRENT_LIST_DIR}/xtea.cpp
|
||||
PARENT_SCOPE)
|
||||
)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -24,8 +24,6 @@
|
||||
|
||||
struct Account {
|
||||
std::vector<std::string> characters;
|
||||
std::string name;
|
||||
std::string key;
|
||||
time_t lastDay = 0;
|
||||
uint32_t id = 0;
|
||||
uint16_t premiumDays = 0;
|
||||
|
273
src/actions.cpp
273
src/actions.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -40,27 +40,29 @@ Actions::Actions() :
|
||||
|
||||
Actions::~Actions()
|
||||
{
|
||||
clear(false);
|
||||
clear();
|
||||
}
|
||||
|
||||
void Actions::clearMap(ActionUseMap& map, bool fromLua)
|
||||
inline void Actions::clearMap(ActionUseMap& map)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ) {
|
||||
if (fromLua == it->second.fromLua) {
|
||||
it = map.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
// Filter out duplicates to avoid double-free
|
||||
std::unordered_set<Action*> set;
|
||||
for (const auto& it : map) {
|
||||
set.insert(it.second);
|
||||
}
|
||||
map.clear();
|
||||
|
||||
for (Action* action : set) {
|
||||
delete action;
|
||||
}
|
||||
}
|
||||
|
||||
void Actions::clear(bool fromLua)
|
||||
void Actions::clear()
|
||||
{
|
||||
clearMap(useItemMap, fromLua);
|
||||
clearMap(uniqueItemMap, fromLua);
|
||||
clearMap(actionItemMap, fromLua);
|
||||
clearMap(useItemMap);
|
||||
clearMap(actionItemMap);
|
||||
|
||||
reInitState(fromLua);
|
||||
scriptInterface.reInitState();
|
||||
}
|
||||
|
||||
LuaScriptInterface& Actions::getScriptInterface()
|
||||
@ -73,23 +75,23 @@ std::string Actions::getScriptBaseName() const
|
||||
return "actions";
|
||||
}
|
||||
|
||||
Event_ptr Actions::getEvent(const std::string& nodeName)
|
||||
Event* Actions::getEvent(const std::string& nodeName)
|
||||
{
|
||||
if (strcasecmp(nodeName.c_str(), "action") != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return Event_ptr(new Action(&scriptInterface));
|
||||
return new Action(&scriptInterface);
|
||||
}
|
||||
|
||||
bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
bool Actions::registerEvent(Event* event, const pugi::xml_node& node)
|
||||
{
|
||||
Action_ptr action{static_cast<Action*>(event.release())}; //event is guaranteed to be an Action
|
||||
Action* action = static_cast<Action*>(event); //event is guaranteed to be an Action
|
||||
|
||||
pugi::xml_attribute attr;
|
||||
if ((attr = node.attribute("itemid"))) {
|
||||
uint16_t id = pugi::cast<uint16_t>(attr.value());
|
||||
|
||||
auto result = useItemMap.emplace(id, std::move(*action));
|
||||
auto result = useItemMap.emplace(id, action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << id << std::endl;
|
||||
}
|
||||
@ -105,14 +107,14 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
uint16_t iterId = fromId;
|
||||
uint16_t toId = pugi::cast<uint16_t>(toIdAttribute.value());
|
||||
|
||||
auto result = useItemMap.emplace(iterId, *action);
|
||||
auto result = useItemMap.emplace(iterId, action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
|
||||
}
|
||||
|
||||
bool success = result.second;
|
||||
while (++iterId <= toId) {
|
||||
result = useItemMap.emplace(iterId, *action);
|
||||
result = useItemMap.emplace(iterId, action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with id: " << iterId << " in fromid: " << fromId << ", toid: " << toId << std::endl;
|
||||
continue;
|
||||
@ -120,44 +122,10 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
} else if ((attr = node.attribute("uniqueid"))) {
|
||||
uint16_t uid = pugi::cast<uint16_t>(attr.value());
|
||||
|
||||
auto result = uniqueItemMap.emplace(uid, std::move(*action));
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with uniqueid: " << uid << std::endl;
|
||||
}
|
||||
return result.second;
|
||||
} else if ((attr = node.attribute("fromuid"))) {
|
||||
pugi::xml_attribute toUidAttribute = node.attribute("touid");
|
||||
if (!toUidAttribute) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Missing touid in fromuid: " << attr.as_string() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t fromUid = pugi::cast<uint16_t>(attr.value());
|
||||
uint16_t iterUid = fromUid;
|
||||
uint16_t toUid = pugi::cast<uint16_t>(toUidAttribute.value());
|
||||
|
||||
auto result = uniqueItemMap.emplace(iterUid, *action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl;
|
||||
}
|
||||
|
||||
bool success = result.second;
|
||||
while (++iterUid <= toUid) {
|
||||
result = uniqueItemMap.emplace(iterUid, *action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with unique id: " << iterUid << " in fromuid: " << fromUid << ", touid: " << toUid << std::endl;
|
||||
continue;
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
} else if ((attr = node.attribute("actionid"))) {
|
||||
uint16_t aid = pugi::cast<uint16_t>(attr.value());
|
||||
|
||||
auto result = actionItemMap.emplace(aid, std::move(*action));
|
||||
auto result = actionItemMap.emplace(aid, action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with actionid: " << aid << std::endl;
|
||||
}
|
||||
@ -173,14 +141,14 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
uint16_t iterAid = fromAid;
|
||||
uint16_t toAid = pugi::cast<uint16_t>(toAidAttribute.value());
|
||||
|
||||
auto result = actionItemMap.emplace(iterAid, *action);
|
||||
auto result = actionItemMap.emplace(iterAid, action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
|
||||
}
|
||||
|
||||
bool success = result.second;
|
||||
while (++iterAid <= toAid) {
|
||||
result = actionItemMap.emplace(iterAid, *action);
|
||||
result = actionItemMap.emplace(iterAid, action);
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerEvent] Duplicate registered item with action id: " << iterAid << " in fromaid: " << fromAid << ", toaid: " << toAid << std::endl;
|
||||
continue;
|
||||
@ -192,69 +160,6 @@ bool Actions::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Actions::registerLuaEvent(Action* event)
|
||||
{
|
||||
Action_ptr action{ event };
|
||||
if (action->getItemIdRange().size() > 0) {
|
||||
if (action->getItemIdRange().size() == 1) {
|
||||
auto result = useItemMap.emplace(action->getItemIdRange().at(0), std::move(*action));
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with id: " << action->getItemIdRange().at(0) << std::endl;
|
||||
}
|
||||
return result.second;
|
||||
} else {
|
||||
auto v = action->getItemIdRange();
|
||||
for (auto i = v.begin(); i != v.end(); i++) {
|
||||
auto result = useItemMap.emplace(*i, std::move(*action));
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with id: " << *i << " in range from id: " << v.at(0) << ", to id: " << v.at(v.size() - 1) << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (action->getUniqueIdRange().size() > 0) {
|
||||
if (action->getUniqueIdRange().size() == 1) {
|
||||
auto result = uniqueItemMap.emplace(action->getUniqueIdRange().at(0), std::move(*action));
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with uid: " << action->getUniqueIdRange().at(0) << std::endl;
|
||||
}
|
||||
return result.second;
|
||||
} else {
|
||||
auto v = action->getUniqueIdRange();
|
||||
for (auto i = v.begin(); i != v.end(); i++) {
|
||||
auto result = uniqueItemMap.emplace(*i, std::move(*action));
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with uid: " << *i << " in range from uid: " << v.at(0) << ", to uid: " << v.at(v.size() - 1) << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (action->getActionIdRange().size() > 0) {
|
||||
if (action->getActionIdRange().size() == 1) {
|
||||
auto result = actionItemMap.emplace(action->getActionIdRange().at(0), std::move(*action));
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with aid: " << action->getActionIdRange().at(0) << std::endl;
|
||||
}
|
||||
return result.second;
|
||||
} else {
|
||||
auto v = action->getActionIdRange();
|
||||
for (auto i = v.begin(); i != v.end(); i++) {
|
||||
auto result = actionItemMap.emplace(*i, std::move(*action));
|
||||
if (!result.second) {
|
||||
std::cout << "[Warning - Actions::registerLuaEvent] Duplicate registered item with aid: " << *i << " in range from aid: " << v.at(0) << ", to aid: " << v.at(v.size() - 1) << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
std::cout << "[Warning - Actions::registerLuaEvent] There is no id / aid / uid set for this event" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ReturnValue Actions::canUse(const Player* player, const Position& pos)
|
||||
{
|
||||
if (pos.x != 0xFFFF) {
|
||||
@ -303,23 +208,16 @@ ReturnValue Actions::canUseFar(const Creature* creature, const Position& toPos,
|
||||
|
||||
Action* Actions::getAction(const Item* item)
|
||||
{
|
||||
if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
|
||||
auto it = uniqueItemMap.find(item->getUniqueId());
|
||||
if (it != uniqueItemMap.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) {
|
||||
auto it = actionItemMap.find(item->getActionId());
|
||||
if (it != actionItemMap.end()) {
|
||||
return &it->second;
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
|
||||
auto it = useItemMap.find(item->getID());
|
||||
if (it != useItemMap.end()) {
|
||||
return &it->second;
|
||||
return it->second;
|
||||
}
|
||||
|
||||
//rune items
|
||||
@ -358,41 +256,44 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
|
||||
|
||||
if (bed->trySleep(player)) {
|
||||
player->setBedItem(bed);
|
||||
g_game.sendOfflineTrainingDialog(player);
|
||||
if (!bed->sleep(player)) {
|
||||
return RETURNVALUE_CANNOTUSETHISOBJECT;
|
||||
}
|
||||
}
|
||||
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
if (Container* container = item->getContainer()) {
|
||||
Container* openContainer;
|
||||
if (!item->isChestQuest()) {
|
||||
Container* openContainer;
|
||||
|
||||
//depot container
|
||||
if (DepotLocker* depot = container->getDepotLocker()) {
|
||||
DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId());
|
||||
myDepotLocker->setParent(depot->getParent()->getTile());
|
||||
openContainer = myDepotLocker;
|
||||
player->setLastDepotId(depot->getDepotId());
|
||||
} else {
|
||||
openContainer = container;
|
||||
//depot container
|
||||
if (DepotLocker* depot = container->getDepotLocker()) {
|
||||
DepotLocker* myDepotLocker = player->getDepotLocker(depot->getDepotId(), true);
|
||||
myDepotLocker->setParent(depot->getParent()->getTile());
|
||||
openContainer = myDepotLocker;
|
||||
} else {
|
||||
openContainer = container;
|
||||
}
|
||||
|
||||
uint32_t corpseOwner = container->getCorpseOwner();
|
||||
if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
|
||||
return RETURNVALUE_YOUARENOTTHEOWNER;
|
||||
}
|
||||
|
||||
//open/close container
|
||||
int32_t oldContainerId = player->getContainerID(openContainer);
|
||||
if (oldContainerId != -1) {
|
||||
player->onCloseContainer(openContainer);
|
||||
player->closeContainer(oldContainerId);
|
||||
} else {
|
||||
player->addContainer(index, openContainer);
|
||||
player->onSendContainer(openContainer);
|
||||
}
|
||||
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
uint32_t corpseOwner = container->getCorpseOwner();
|
||||
if (corpseOwner != 0 && !player->canOpenCorpse(corpseOwner)) {
|
||||
return RETURNVALUE_YOUARENOTTHEOWNER;
|
||||
}
|
||||
|
||||
//open/close container
|
||||
int32_t oldContainerId = player->getContainerID(openContainer);
|
||||
if (oldContainerId != -1) {
|
||||
player->onCloseContainer(openContainer);
|
||||
player->closeContainer(oldContainerId);
|
||||
} else {
|
||||
player->addContainer(index, openContainer);
|
||||
player->onSendContainer(openContainer);
|
||||
}
|
||||
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
const ItemType& it = Item::items[item->getID()];
|
||||
@ -406,6 +307,12 @@ ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_
|
||||
}
|
||||
|
||||
return RETURNVALUE_NOERROR;
|
||||
} else if (it.changeUse) {
|
||||
if (it.transformToOnUse) {
|
||||
g_game.transformItem(item, it.transformToOnUse);
|
||||
g_game.startDecay(item);
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return RETURNVALUE_CANNOTUSETHISOBJECT;
|
||||
@ -417,7 +324,7 @@ bool Actions::useItem(Player* player, const Position& pos, uint8_t index, Item*
|
||||
player->stopWalk();
|
||||
|
||||
if (isHotkey) {
|
||||
showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), item->getSubType()));
|
||||
showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1));
|
||||
}
|
||||
|
||||
ReturnValue ret = internalUseItem(player, pos, index, item, isHotkey);
|
||||
@ -447,7 +354,7 @@ bool Actions::useItemEx(Player* player, const Position& fromPos, const Position&
|
||||
}
|
||||
|
||||
if (isHotkey) {
|
||||
showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), item->getSubType()));
|
||||
showUseHotkeyMessage(player, item, player->getItemTypeCount(item->getID(), -1));
|
||||
}
|
||||
|
||||
if (!action->executeUse(player, item, fromPos, action->getTarget(player, creature, toPos, toStackPos), toPos, isHotkey)) {
|
||||
@ -466,9 +373,11 @@ void Actions::showUseHotkeyMessage(Player* player, const Item* item, uint32_t co
|
||||
const ItemType& it = Item::items[item->getID()];
|
||||
if (!it.showCount) {
|
||||
ss << "Using one of " << item->getName() << "...";
|
||||
} else if (count == 1) {
|
||||
}
|
||||
else if (count == 1) {
|
||||
ss << "Using the last " << item->getName() << "...";
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
ss << "Using one of " << count << ' ' << item->getPluralName() << "...";
|
||||
}
|
||||
player->sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
|
||||
@ -477,6 +386,9 @@ void Actions::showUseHotkeyMessage(Player* player, const Item* item, uint32_t co
|
||||
Action::Action(LuaScriptInterface* interface) :
|
||||
Event(interface), function(nullptr), allowFarUse(false), checkFloor(true), checkLineOfSight(true) {}
|
||||
|
||||
Action::Action(const Action* copy) :
|
||||
Event(copy), allowFarUse(copy->allowFarUse), checkFloor(copy->checkFloor), checkLineOfSight(copy->checkLineOfSight) {}
|
||||
|
||||
bool Action::configureEvent(const pugi::xml_node& node)
|
||||
{
|
||||
pugi::xml_attribute allowFarUseAttr = node.attribute("allowfaruse");
|
||||
@ -497,38 +409,6 @@ bool Action::configureEvent(const pugi::xml_node& node)
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool enterMarket(Player* player, Item*, const Position&, Thing*, const Position&, bool)
|
||||
{
|
||||
if (player->getLastDepotId() == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
player->sendMarketEnter(player->getLastDepotId());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool Action::loadFunction(const pugi::xml_attribute& attr, bool isScripted)
|
||||
{
|
||||
const char* functionName = attr.as_string();
|
||||
if (strcasecmp(functionName, "market") == 0) {
|
||||
function = enterMarket;
|
||||
} else {
|
||||
if (!isScripted) {
|
||||
std::cout << "[Warning - Action::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isScripted) {
|
||||
scripted = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Action::getScriptEventName() const
|
||||
{
|
||||
return "onUse";
|
||||
@ -538,7 +418,8 @@ ReturnValue Action::canExecuteAction(const Player* player, const Position& toPos
|
||||
{
|
||||
if (!allowFarUse) {
|
||||
return g_actions->canUse(player, toPos);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return g_actions->canUseFar(player, toPos, checkLineOfSight, checkFloor);
|
||||
}
|
||||
}
|
||||
@ -577,4 +458,4 @@ bool Action::executeUse(Player* player, Item* item, const Position& fromPosition
|
||||
|
||||
LuaScriptInterface::pushBoolean(L, isHotkey);
|
||||
return scriptInterface->callFunction(6);
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -31,14 +31,15 @@ using ActionFunction = std::function<bool(Player* player, Item* item, const Posi
|
||||
class Action : public Event
|
||||
{
|
||||
public:
|
||||
explicit Action(const Action* copy);
|
||||
explicit Action(LuaScriptInterface* interface);
|
||||
|
||||
bool configureEvent(const pugi::xml_node& node) override;
|
||||
bool loadFunction(const pugi::xml_attribute& attr, bool isScripted) override;
|
||||
|
||||
//scripting
|
||||
virtual bool executeUse(Player* player, Item* item, const Position& fromPosition,
|
||||
Thing* target, const Position& toPosition, bool isHotkey);
|
||||
//
|
||||
|
||||
bool getAllowFarUse() const {
|
||||
return allowFarUse;
|
||||
@ -118,25 +119,22 @@ class Actions final : public BaseEvents
|
||||
ReturnValue canUse(const Player* player, const Position& pos, const Item* item);
|
||||
ReturnValue canUseFar(const Creature* creature, const Position& toPos, bool checkLineOfSight, bool checkFloor);
|
||||
|
||||
bool registerLuaEvent(Action* event);
|
||||
void clear(bool fromLua) override final;
|
||||
|
||||
private:
|
||||
ReturnValue internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item, bool isHotkey);
|
||||
static void showUseHotkeyMessage(Player* player, const Item* item, uint32_t count);
|
||||
|
||||
LuaScriptInterface& getScriptInterface() override;
|
||||
std::string getScriptBaseName() const override;
|
||||
Event_ptr getEvent(const std::string& nodeName) override;
|
||||
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
|
||||
void clear() final;
|
||||
LuaScriptInterface& getScriptInterface() final;
|
||||
std::string getScriptBaseName() const final;
|
||||
Event* getEvent(const std::string& nodeName) final;
|
||||
bool registerEvent(Event* event, const pugi::xml_node& node) final;
|
||||
|
||||
using ActionUseMap = std::map<uint16_t, Action>;
|
||||
typedef std::map<uint16_t, Action*> ActionUseMap;
|
||||
ActionUseMap useItemMap;
|
||||
ActionUseMap uniqueItemMap;
|
||||
ActionUseMap actionItemMap;
|
||||
|
||||
Action* getAction(const Item* item);
|
||||
void clearMap(ActionUseMap& map, bool fromLua);
|
||||
void clearMap(ActionUseMap& map);
|
||||
|
||||
LuaScriptInterface scriptInterface;
|
||||
};
|
||||
|
30
src/ban.cpp
30
src/ban.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -24,15 +24,15 @@
|
||||
#include "databasetasks.h"
|
||||
#include "tools.h"
|
||||
|
||||
bool Ban::acceptConnection(uint32_t clientIP)
|
||||
bool Ban::acceptConnection(uint32_t clientip)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lockClass(lock);
|
||||
|
||||
uint64_t currentTime = OTSYS_TIME();
|
||||
|
||||
auto it = ipConnectMap.find(clientIP);
|
||||
auto it = ipConnectMap.find(clientip);
|
||||
if (it == ipConnectMap.end()) {
|
||||
ipConnectMap.emplace(clientIP, ConnectBlock(currentTime, 0, 1));
|
||||
ipConnectMap.emplace(clientip, ConnectBlock(currentTime, 0, 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -60,12 +60,12 @@ bool Ban::acceptConnection(uint32_t clientIP)
|
||||
|
||||
bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `reason`, `expires_at`, `banned_at`, `banned_by`, (SELECT `name` FROM `players` WHERE `id` = `banned_by`) AS `name` FROM `account_bans` WHERE `account_id` = " << accountId;
|
||||
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -74,7 +74,7 @@ bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo)
|
||||
if (expiresAt != 0 && time(nullptr) > expiresAt) {
|
||||
// Move the ban to history if it has expired
|
||||
query.str(std::string());
|
||||
query << "INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, `expired_at`, `banned_by`) VALUES (" << accountId << ',' << db.escapeString(result->getString("reason")) << ',' << result->getNumber<time_t>("banned_at") << ',' << expiresAt << ',' << result->getNumber<uint32_t>("banned_by") << ')';
|
||||
query << "INSERT INTO `account_ban_history` (`account_id`, `reason`, `banned_at`, `expired_at`, `banned_by`) VALUES (" << accountId << ',' << db->escapeString(result->getString("reason")) << ',' << result->getNumber<time_t>("banned_at") << ',' << expiresAt << ',' << result->getNumber<uint32_t>("banned_by") << ')';
|
||||
g_databaseTasks.addTask(query.str());
|
||||
|
||||
query.str(std::string());
|
||||
@ -89,18 +89,18 @@ bool IOBan::isAccountBanned(uint32_t accountId, BanInfo& banInfo)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOBan::isIpBanned(uint32_t clientIP, BanInfo& banInfo)
|
||||
bool IOBan::isIpBanned(uint32_t clientip, BanInfo& banInfo)
|
||||
{
|
||||
if (clientIP == 0) {
|
||||
if (clientip == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `reason`, `expires_at`, (SELECT `name` FROM `players` WHERE `id` = `banned_by`) AS `name` FROM `ip_bans` WHERE `ip` = " << clientIP;
|
||||
query << "SELECT `reason`, `expires_at`, (SELECT `name` FROM `players` WHERE `id` = `banned_by`) AS `name` FROM `ip_bans` WHERE `ip` = " << clientip;
|
||||
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -108,7 +108,7 @@ bool IOBan::isIpBanned(uint32_t clientIP, BanInfo& banInfo)
|
||||
int64_t expiresAt = result->getNumber<int64_t>("expires_at");
|
||||
if (expiresAt != 0 && time(nullptr) > expiresAt) {
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `ip_bans` WHERE `ip` = " << clientIP;
|
||||
query << "DELETE FROM `ip_bans` WHERE `ip` = " << clientip;
|
||||
g_databaseTasks.addTask(query.str());
|
||||
return false;
|
||||
}
|
||||
@ -123,5 +123,5 @@ bool IOBan::isPlayerNamelocked(uint32_t playerId)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "SELECT 1 FROM `player_namelocks` WHERE `player_id` = " << playerId;
|
||||
return Database::getInstance().storeQuery(query.str()).get() != nullptr;
|
||||
return Database::getInstance()->storeQuery(query.str()).get() != nullptr;
|
||||
}
|
||||
|
12
src/ban.h
12
src/ban.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -35,14 +35,14 @@ struct ConnectBlock {
|
||||
uint32_t count;
|
||||
};
|
||||
|
||||
using IpConnectMap = std::map<uint32_t, ConnectBlock>;
|
||||
typedef std::map<uint32_t, ConnectBlock> IpConnectMap;
|
||||
|
||||
class Ban
|
||||
{
|
||||
public:
|
||||
bool acceptConnection(uint32_t clientIP);
|
||||
bool acceptConnection(uint32_t clientip);
|
||||
|
||||
private:
|
||||
protected:
|
||||
IpConnectMap ipConnectMap;
|
||||
std::recursive_mutex lock;
|
||||
};
|
||||
@ -51,7 +51,7 @@ class IOBan
|
||||
{
|
||||
public:
|
||||
static bool isAccountBanned(uint32_t accountId, BanInfo& banInfo);
|
||||
static bool isIpBanned(uint32_t clientIP, BanInfo& banInfo);
|
||||
static bool isIpBanned(uint32_t ip, BanInfo& banInfo);
|
||||
static bool isPlayerNamelocked(uint32_t playerId);
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -51,13 +51,14 @@ bool BaseEvents::loadFromXml()
|
||||
loaded = true;
|
||||
|
||||
for (auto node : doc.child(scriptsName.c_str()).children()) {
|
||||
Event_ptr event = getEvent(node.name());
|
||||
Event* event = getEvent(node.name());
|
||||
if (!event) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!event->configureEvent(node)) {
|
||||
std::cout << "[Warning - BaseEvents::loadFromXml] Failed to configure event" << std::endl;
|
||||
delete event;
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -67,15 +68,12 @@ bool BaseEvents::loadFromXml()
|
||||
if (scriptAttribute) {
|
||||
std::string scriptFile = "scripts/" + std::string(scriptAttribute.as_string());
|
||||
success = event->checkScript(basePath, scriptsName, scriptFile) && event->loadScript(basePath + scriptFile);
|
||||
if (node.attribute("function")) {
|
||||
event->loadFunction(node.attribute("function"), true);
|
||||
}
|
||||
} else {
|
||||
success = event->loadFunction(node.attribute("function"), false);
|
||||
success = event->loadFunction(node.attribute("function"));
|
||||
}
|
||||
|
||||
if (success) {
|
||||
registerEvent(std::move(event), node);
|
||||
if (!success || !registerEvent(event, node)) {
|
||||
delete event;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@ -84,19 +82,15 @@ bool BaseEvents::loadFromXml()
|
||||
bool BaseEvents::reload()
|
||||
{
|
||||
loaded = false;
|
||||
clear(false);
|
||||
clear();
|
||||
return loadFromXml();
|
||||
}
|
||||
|
||||
void BaseEvents::reInitState(bool fromLua)
|
||||
{
|
||||
if (!fromLua) {
|
||||
getScriptInterface().reInitState();
|
||||
}
|
||||
}
|
||||
|
||||
Event::Event(LuaScriptInterface* interface) : scriptInterface(interface) {}
|
||||
|
||||
Event::Event(const Event* copy) :
|
||||
scripted(copy->scripted), scriptId(copy->scriptId), scriptInterface(copy->scriptInterface) {}
|
||||
|
||||
bool Event::checkScript(const std::string& basePath, const std::string& scriptsName, const std::string& scriptFile) const
|
||||
{
|
||||
LuaScriptInterface* testInterface = g_luaEnvironment.getTestInterface();
|
||||
@ -149,24 +143,6 @@ bool Event::loadScript(const std::string& scriptFile)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Event::loadCallback()
|
||||
{
|
||||
if (!scriptInterface || scriptId != 0) {
|
||||
std::cout << "Failure: [Event::loadCallback] scriptInterface == nullptr. scriptid = " << scriptId << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t id = scriptInterface->getEvent();
|
||||
if (id == -1) {
|
||||
std::cout << "[Warning - Event::loadCallback] Event " << getScriptEventName() << " not found. " << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
scripted = true;
|
||||
scriptId = id;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string& name)
|
||||
{
|
||||
if (!interface) {
|
||||
@ -182,6 +158,7 @@ bool CallBack::loadCallBack(LuaScriptInterface* interface, const std::string& na
|
||||
return false;
|
||||
}
|
||||
|
||||
callbackName = name;
|
||||
scriptId = id;
|
||||
loaded = true;
|
||||
return true;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -22,21 +22,18 @@
|
||||
|
||||
#include "luascript.h"
|
||||
|
||||
class Event;
|
||||
using Event_ptr = std::unique_ptr<Event>;
|
||||
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
explicit Event(LuaScriptInterface* interface);
|
||||
explicit Event(const Event* copy);
|
||||
virtual ~Event() = default;
|
||||
|
||||
virtual bool configureEvent(const pugi::xml_node& node) = 0;
|
||||
|
||||
bool checkScript(const std::string& basePath, const std::string& scriptsName, const std::string& scriptFile) const;
|
||||
bool loadScript(const std::string& scriptFile);
|
||||
bool loadCallback();
|
||||
virtual bool loadFunction(const pugi::xml_attribute&, bool) {
|
||||
virtual bool loadFunction(const pugi::xml_attribute&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -44,12 +41,10 @@ class Event
|
||||
return scripted;
|
||||
}
|
||||
|
||||
bool scripted = false;
|
||||
bool fromLua = false;
|
||||
|
||||
protected:
|
||||
virtual std::string getScriptEventName() const = 0;
|
||||
|
||||
bool scripted = false;
|
||||
int32_t scriptId = 0;
|
||||
LuaScriptInterface* scriptInterface = nullptr;
|
||||
};
|
||||
@ -65,14 +60,13 @@ class BaseEvents
|
||||
bool isLoaded() const {
|
||||
return loaded;
|
||||
}
|
||||
void reInitState(bool fromLua);
|
||||
|
||||
private:
|
||||
protected:
|
||||
virtual LuaScriptInterface& getScriptInterface() = 0;
|
||||
virtual std::string getScriptBaseName() const = 0;
|
||||
virtual Event_ptr getEvent(const std::string& nodeName) = 0;
|
||||
virtual bool registerEvent(Event_ptr event, const pugi::xml_node& node) = 0;
|
||||
virtual void clear(bool) = 0;
|
||||
virtual Event* getEvent(const std::string& nodeName) = 0;
|
||||
virtual bool registerEvent(Event* event, const pugi::xml_node& node) = 0;
|
||||
virtual void clear() = 0;
|
||||
|
||||
bool loaded = false;
|
||||
};
|
||||
@ -88,8 +82,9 @@ class CallBack
|
||||
int32_t scriptId = 0;
|
||||
LuaScriptInterface* scriptInterface = nullptr;
|
||||
|
||||
private:
|
||||
bool loaded = false;
|
||||
|
||||
std::string callbackName;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
14
src/bed.cpp
14
src/bed.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -159,8 +159,8 @@ bool BedItem::sleep(Player* player)
|
||||
// make the player walk onto the bed
|
||||
g_game.map.moveCreature(*player, *getTile());
|
||||
|
||||
// display 'Zzzz'/sleep effect
|
||||
g_game.addMagicEffect(player->getPosition(), CONST_ME_SLEEP);
|
||||
// display poff effect
|
||||
g_game.addMagicEffect(player->getPosition(), CONST_ME_POFF);
|
||||
|
||||
// kick player after he sees himself walk onto the bed and it change id
|
||||
uint32_t playerId = player->getID();
|
||||
@ -246,10 +246,10 @@ void BedItem::updateAppearance(const Player* player)
|
||||
{
|
||||
const ItemType& it = Item::items[id];
|
||||
if (it.type == ITEM_TYPE_BED) {
|
||||
if (player && it.transformToOnUse[player->getSex()] != 0) {
|
||||
const ItemType& newType = Item::items[it.transformToOnUse[player->getSex()]];
|
||||
if (player && it.transformToOnUse != 0) {
|
||||
const ItemType& newType = Item::items[it.transformToOnUse];
|
||||
if (newType.type == ITEM_TYPE_BED) {
|
||||
g_game.transformItem(this, it.transformToOnUse[player->getSex()]);
|
||||
g_game.transformItem(this, it.transformToOnUse);
|
||||
}
|
||||
} else if (it.transformToFree != 0) {
|
||||
const ItemType& newType = Item::items[it.transformToFree];
|
||||
|
19
src/bed.h
19
src/bed.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -30,17 +30,17 @@ class BedItem final : public Item
|
||||
public:
|
||||
explicit BedItem(uint16_t id);
|
||||
|
||||
BedItem* getBed() override {
|
||||
BedItem* getBed() final {
|
||||
return this;
|
||||
}
|
||||
const BedItem* getBed() const override {
|
||||
const BedItem* getBed() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
|
||||
void serializeAttr(PropWriteStream& propWriteStream) const override;
|
||||
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final;
|
||||
void serializeAttr(PropWriteStream& propWriteStream) const final;
|
||||
|
||||
bool canRemove() const override {
|
||||
bool canRemove() const final {
|
||||
return house == nullptr;
|
||||
}
|
||||
|
||||
@ -48,6 +48,9 @@ class BedItem final : public Item
|
||||
return sleeperGUID;
|
||||
}
|
||||
|
||||
House* getHouse() const {
|
||||
return house;
|
||||
}
|
||||
void setHouse(House* h) {
|
||||
house = h;
|
||||
}
|
||||
@ -60,7 +63,7 @@ class BedItem final : public Item
|
||||
|
||||
BedItem* getNextBedItem() const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void updateAppearance(const Player* player);
|
||||
void regeneratePlayer(Player* player) const;
|
||||
void internalSetSleeper(const Player* player);
|
||||
|
92
src/chat.cpp
92
src/chat.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -54,10 +54,6 @@ void PrivateChatChannel::invitePlayer(const Player& player, Player& invitePlayer
|
||||
ss.str(std::string());
|
||||
ss << invitePlayer.getName() << " has been invited.";
|
||||
player.sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
|
||||
|
||||
for (const auto& it : users) {
|
||||
it.second->sendChannelEvent(id, invitePlayer.getName(), CHANNELEVENT_INVITE);
|
||||
}
|
||||
}
|
||||
|
||||
void PrivateChatChannel::excludePlayer(const Player& player, Player& excludePlayer)
|
||||
@ -73,10 +69,6 @@ void PrivateChatChannel::excludePlayer(const Player& player, Player& excludePlay
|
||||
player.sendTextMessage(MESSAGE_INFO_DESCR, ss.str());
|
||||
|
||||
excludePlayer.sendClosePrivate(id);
|
||||
|
||||
for (const auto& it : users) {
|
||||
it.second->sendChannelEvent(id, excludePlayer.getName(), CHANNELEVENT_EXCLUDE);
|
||||
}
|
||||
}
|
||||
|
||||
void PrivateChatChannel::closeChannel() const
|
||||
@ -96,20 +88,6 @@ bool ChatChannel::addUser(Player& player)
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Move to script when guild channels can be scripted
|
||||
if (id == CHANNEL_GUILD) {
|
||||
Guild* guild = player.getGuild();
|
||||
if (guild && !guild->getMotd().empty()) {
|
||||
g_scheduler.addEvent(createSchedulerTask(150, std::bind(&Game::sendGuildMotd, &g_game, player.getID())));
|
||||
}
|
||||
}
|
||||
|
||||
if (!publicChannel) {
|
||||
for (const auto& it : users) {
|
||||
it.second->sendChannelEvent(id, player.getName(), CHANNELEVENT_JOIN);
|
||||
}
|
||||
}
|
||||
|
||||
users[player.getID()] = &player;
|
||||
return true;
|
||||
}
|
||||
@ -123,27 +101,10 @@ bool ChatChannel::removeUser(const Player& player)
|
||||
|
||||
users.erase(iter);
|
||||
|
||||
if (!publicChannel) {
|
||||
for (const auto& it : users) {
|
||||
it.second->sendChannelEvent(id, player.getName(), CHANNELEVENT_LEAVE);
|
||||
}
|
||||
}
|
||||
|
||||
executeOnLeaveEvent(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ChatChannel::hasUser(const Player& player) {
|
||||
return users.find(player.getID()) != users.end();
|
||||
}
|
||||
|
||||
void ChatChannel::sendToAll(const std::string& message, SpeakClasses type) const
|
||||
{
|
||||
for (const auto& it : users) {
|
||||
it.second->sendChannelMessage("", message, type, id);
|
||||
}
|
||||
}
|
||||
|
||||
bool ChatChannel::talk(const Player& fromPlayer, SpeakClasses type, const std::string& text)
|
||||
{
|
||||
if (users.find(fromPlayer.getID()) == users.end()) {
|
||||
@ -294,39 +255,21 @@ bool Chat::load()
|
||||
return false;
|
||||
}
|
||||
|
||||
std::forward_list<uint16_t> removedChannels;
|
||||
for (auto& channelEntry : normalChannels) {
|
||||
ChatChannel& channel = channelEntry.second;
|
||||
channel.onSpeakEvent = -1;
|
||||
channel.canJoinEvent = -1;
|
||||
channel.onJoinEvent = -1;
|
||||
channel.onLeaveEvent = -1;
|
||||
removedChannels.push_front(channelEntry.first);
|
||||
}
|
||||
|
||||
for (auto channelNode : doc.child("channels").children()) {
|
||||
uint16_t channelId = pugi::cast<uint16_t>(channelNode.attribute("id").value());
|
||||
std::string channelName = channelNode.attribute("name").as_string();
|
||||
bool isPublic = channelNode.attribute("public").as_bool();
|
||||
ChatChannel channel(pugi::cast<uint16_t>(channelNode.attribute("id").value()), channelNode.attribute("name").as_string());
|
||||
channel.publicChannel = channelNode.attribute("public").as_bool();
|
||||
|
||||
pugi::xml_attribute scriptAttribute = channelNode.attribute("script");
|
||||
|
||||
auto it = normalChannels.find(channelId);
|
||||
if (it != normalChannels.end()) {
|
||||
ChatChannel& channel = it->second;
|
||||
channel.publicChannel = isPublic;
|
||||
channel.name = channelName;
|
||||
|
||||
if (scriptAttribute) {
|
||||
if (scriptInterface.loadFile("data/chatchannels/scripts/" + std::string(scriptAttribute.as_string())) == 0) {
|
||||
channel.onSpeakEvent = scriptInterface.getEvent("onSpeak");
|
||||
channel.canJoinEvent = scriptInterface.getEvent("canJoin");
|
||||
channel.onJoinEvent = scriptInterface.getEvent("onJoin");
|
||||
channel.onLeaveEvent = scriptInterface.getEvent("onLeave");
|
||||
} else {
|
||||
std::cout << "[Warning - Chat::load] Can not load script: " << scriptAttribute.as_string() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
UsersMap tempUserMap = std::move(channel.users);
|
||||
for (const auto& pair : tempUserMap) {
|
||||
channel.addUser(*pair.second);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
ChatChannel channel(channelId, channelName);
|
||||
channel.publicChannel = isPublic;
|
||||
|
||||
if (scriptAttribute) {
|
||||
if (scriptInterface.loadFile("data/chatchannels/scripts/" + std::string(scriptAttribute.as_string())) == 0) {
|
||||
channel.onSpeakEvent = scriptInterface.getEvent("onSpeak");
|
||||
@ -338,8 +281,13 @@ bool Chat::load()
|
||||
}
|
||||
}
|
||||
|
||||
removedChannels.remove(channel.id);
|
||||
normalChannels[channel.id] = channel;
|
||||
}
|
||||
|
||||
for (uint16_t channelId : removedChannels) {
|
||||
normalChannels.erase(channelId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
25
src/chat.h
25
src/chat.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,24 +26,23 @@
|
||||
class Party;
|
||||
class Player;
|
||||
|
||||
using UsersMap = std::map<uint32_t, Player*>;
|
||||
using InvitedMap = std::map<uint32_t, const Player*>;
|
||||
typedef std::map<uint32_t, Player*> UsersMap;
|
||||
typedef std::map<uint32_t, const Player*> InvitedMap;
|
||||
|
||||
class ChatChannel
|
||||
{
|
||||
public:
|
||||
ChatChannel() = default;
|
||||
ChatChannel(uint16_t channelId, std::string channelName):
|
||||
id{channelId}, name{std::move(channelName)} {}
|
||||
name(std::move(channelName)),
|
||||
id(channelId) {}
|
||||
|
||||
virtual ~ChatChannel() = default;
|
||||
|
||||
bool addUser(Player& player);
|
||||
bool removeUser(const Player& player);
|
||||
bool hasUser(const Player& player);
|
||||
|
||||
bool talk(const Player& fromPlayer, SpeakClasses type, const std::string& text);
|
||||
void sendToAll(const std::string& message, SpeakClasses type) const;
|
||||
|
||||
const std::string& getName() const {
|
||||
return name;
|
||||
@ -72,9 +71,6 @@ class ChatChannel
|
||||
protected:
|
||||
UsersMap users;
|
||||
|
||||
uint16_t id;
|
||||
|
||||
private:
|
||||
std::string name;
|
||||
|
||||
int32_t canJoinEvent = -1;
|
||||
@ -82,6 +78,7 @@ class ChatChannel
|
||||
int32_t onLeaveEvent = -1;
|
||||
int32_t onSpeakEvent = -1;
|
||||
|
||||
uint16_t id;
|
||||
bool publicChannel = false;
|
||||
|
||||
friend class Chat;
|
||||
@ -92,7 +89,7 @@ class PrivateChatChannel final : public ChatChannel
|
||||
public:
|
||||
PrivateChatChannel(uint16_t channelId, std::string channelName) : ChatChannel(channelId, channelName) {}
|
||||
|
||||
uint32_t getOwner() const override {
|
||||
uint32_t getOwner() const final {
|
||||
return owner;
|
||||
}
|
||||
void setOwner(uint32_t owner) {
|
||||
@ -108,16 +105,16 @@ class PrivateChatChannel final : public ChatChannel
|
||||
|
||||
void closeChannel() const;
|
||||
|
||||
const InvitedMap* getInvitedUsers() const override {
|
||||
const InvitedMap* getInvitedUsers() const final {
|
||||
return &invites;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
InvitedMap invites;
|
||||
uint32_t owner = 0;
|
||||
};
|
||||
|
||||
using ChannelList = std::list<ChatChannel*>;
|
||||
typedef std::list<ChatChannel*> ChannelList;
|
||||
|
||||
class Chat
|
||||
{
|
||||
|
903
src/combat.cpp
903
src/combat.cpp
File diff suppressed because it is too large
Load Diff
102
src/combat.h
102
src/combat.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -38,7 +38,7 @@ class ValueCallback final : public CallBack
|
||||
explicit ValueCallback(formulaType_t type): type(type) {}
|
||||
void getMinMaxValues(Player* player, CombatDamage& damage, bool useCharges) const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
formulaType_t type;
|
||||
};
|
||||
|
||||
@ -46,12 +46,18 @@ class TileCallback final : public CallBack
|
||||
{
|
||||
public:
|
||||
void onTileCombat(Creature* creature, Tile* tile) const;
|
||||
|
||||
protected:
|
||||
formulaType_t type;
|
||||
};
|
||||
|
||||
class TargetCallback final : public CallBack
|
||||
{
|
||||
public:
|
||||
void onTargetCombat(Creature* creature, Creature* target) const;
|
||||
|
||||
protected:
|
||||
formulaType_t type;
|
||||
};
|
||||
|
||||
struct CombatParams {
|
||||
@ -62,6 +68,8 @@ struct CombatParams {
|
||||
std::unique_ptr<TargetCallback> targetCallback;
|
||||
|
||||
uint16_t itemId = 0;
|
||||
uint16_t decreaseDamage = 0;
|
||||
uint16_t maximumDecreasedDamage = 0;
|
||||
|
||||
ConditionType_t dispelType = CONDITION_NONE;
|
||||
CombatType_t combatType = COMBAT_NONE;
|
||||
@ -77,7 +85,39 @@ struct CombatParams {
|
||||
bool useCharges = false;
|
||||
};
|
||||
|
||||
using CombatFunction = std::function<void(Creature*, Creature*, const CombatParams&, CombatDamage*)>;
|
||||
struct Impact
|
||||
{
|
||||
Creature* actor = nullptr;
|
||||
|
||||
virtual void handleCreature(Creature*) {
|
||||
//
|
||||
}
|
||||
};
|
||||
|
||||
struct DamageImpact : Impact
|
||||
{
|
||||
CombatParams params;
|
||||
CombatDamage damage;
|
||||
|
||||
void handleCreature(Creature* target) final;
|
||||
};
|
||||
|
||||
struct SpeedImpact : Impact
|
||||
{
|
||||
int32_t percent = 0;
|
||||
int32_t duration = 0;
|
||||
|
||||
void handleCreature(Creature* target) final;
|
||||
};
|
||||
|
||||
struct DunkenImpact : Impact
|
||||
{
|
||||
int32_t duration = 0;
|
||||
|
||||
void handleCreature(Creature* target) final;
|
||||
};
|
||||
|
||||
typedef void(*COMBATFUNC)(Creature*, Creature*, const CombatParams&, CombatDamage*);
|
||||
|
||||
class MatrixArea
|
||||
{
|
||||
@ -145,14 +185,14 @@ class MatrixArea
|
||||
return cols;
|
||||
}
|
||||
|
||||
const bool* operator[](uint32_t i) const {
|
||||
inline const bool* operator[](uint32_t i) const {
|
||||
return data_[i];
|
||||
}
|
||||
bool* operator[](uint32_t i) {
|
||||
inline bool* operator[](uint32_t i) {
|
||||
return data_[i];
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
uint32_t centerX;
|
||||
uint32_t centerY;
|
||||
|
||||
@ -182,7 +222,7 @@ class AreaCombat
|
||||
void setupExtArea(const std::list<uint32_t>& list, uint32_t rows);
|
||||
void clear();
|
||||
|
||||
private:
|
||||
protected:
|
||||
enum MatrixOperation_t {
|
||||
MATRIXOPERATION_COPY,
|
||||
MATRIXOPERATION_MIRROR,
|
||||
@ -193,7 +233,7 @@ class AreaCombat
|
||||
};
|
||||
|
||||
MatrixArea* createArea(const std::list<uint32_t>& list, uint32_t rows);
|
||||
static void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op);
|
||||
void copyArea(const MatrixArea* input, MatrixArea* output, MatrixOperation_t op) const;
|
||||
|
||||
MatrixArea* getArea(const Position& centerPos, const Position& targetPos) const {
|
||||
int32_t dx = Position::getOffsetX(targetPos, centerPos);
|
||||
@ -242,7 +282,18 @@ class Combat
|
||||
Combat(const Combat&) = delete;
|
||||
Combat& operator=(const Combat&) = delete;
|
||||
|
||||
static void doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params);
|
||||
static int32_t computeDamage(Creature* creature, int32_t strength, int32_t variation);
|
||||
static int32_t getTotalDamage(int32_t attackSkill, int32_t attackValue, fightMode_t fightMode);
|
||||
|
||||
static bool attack(Creature* attacker, Creature* target);
|
||||
static bool closeAttack(Creature* attacker, Creature* target, fightMode_t fightMode);
|
||||
static bool rangeAttack(Creature* attacker, Creature* target, fightMode_t fightMode);
|
||||
|
||||
static void circleShapeSpell(Creature* attacker, const Position& toPos, int32_t range, int32_t animation, int32_t radius, DamageImpact* impact, int32_t effect);
|
||||
|
||||
static void getAttackValue(Creature* creature, uint32_t& attackValue, uint32_t& skillValue, uint8_t& skill);
|
||||
|
||||
static bool doCombatHealth(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params);
|
||||
static void doCombatHealth(Creature* caster, const Position& position, const AreaCombat* area, CombatDamage& damage, const CombatParams& params);
|
||||
|
||||
static void doCombatMana(Creature* caster, Creature* target, CombatDamage& damage, const CombatParams& params);
|
||||
@ -266,10 +317,10 @@ class Combat
|
||||
static ReturnValue canDoCombat(Creature* attacker, Creature* target);
|
||||
static void postCombatEffects(Creature* caster, const Position& pos, const CombatParams& params);
|
||||
|
||||
static void addDistanceEffect(Creature* caster, const Position& fromPos, const Position& toPos, uint8_t effect);
|
||||
static void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect);
|
||||
|
||||
void doCombat(Creature* caster, Creature* target) const;
|
||||
void doCombat(Creature* caster, const Position& position) const;
|
||||
void doCombat(Creature* caster, const Position& pos) const;
|
||||
|
||||
bool setCallback(CallBackParam_t key);
|
||||
CallBack* getCallback(CallBackParam_t key);
|
||||
@ -281,12 +332,9 @@ class Combat
|
||||
bool hasArea() const {
|
||||
return area != nullptr;
|
||||
}
|
||||
void addCondition(const Condition* condition) {
|
||||
void setCondition(const Condition* condition) {
|
||||
params.conditionList.emplace_front(condition);
|
||||
}
|
||||
void clearConditions() {
|
||||
params.conditionList.clear();
|
||||
}
|
||||
void setPlayerCombatValues(formulaType_t formulaType, double mina, double minb, double maxa, double maxb);
|
||||
void postCombatEffects(Creature* caster, const Position& pos) const {
|
||||
postCombatEffects(caster, pos, params);
|
||||
@ -296,10 +344,13 @@ class Combat
|
||||
params.origin = origin;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
static bool canUseWeapon(Player* player, Item* weapon);
|
||||
static void postWeaponEffects(Player* player, Item* weapon);
|
||||
|
||||
static void doCombatDefault(Creature* caster, Creature* target, const CombatParams& params);
|
||||
|
||||
static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, CombatFunction func, CombatDamage* data);
|
||||
static void CombatFunc(Creature* caster, const Position& pos, const AreaCombat* area, const CombatParams& params, COMBATFUNC func, CombatDamage* data);
|
||||
|
||||
static void CombatHealthFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
|
||||
static void CombatManaFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* damage);
|
||||
@ -307,8 +358,8 @@ class Combat
|
||||
static void CombatDispelFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
|
||||
static void CombatNullFunc(Creature* caster, Creature* target, const CombatParams& params, CombatDamage* data);
|
||||
|
||||
static void combatTileEffects(const SpectatorVec& spectators, Creature* caster, Tile* tile, const CombatParams& params);
|
||||
CombatDamage getCombatDamage(Creature* creature, Creature* target) const;
|
||||
static void combatTileEffects(const SpectatorVec& list, Creature* caster, Tile* tile, const CombatParams& params);
|
||||
CombatDamage getCombatDamage(Creature* creature) const;
|
||||
|
||||
//configureable
|
||||
CombatParams params;
|
||||
@ -328,10 +379,10 @@ class MagicField final : public Item
|
||||
public:
|
||||
explicit MagicField(uint16_t type) : Item(type), createTime(OTSYS_TIME()) {}
|
||||
|
||||
MagicField* getMagicField() override {
|
||||
MagicField* getMagicField() final {
|
||||
return this;
|
||||
}
|
||||
const MagicField* getMagicField() const override {
|
||||
const MagicField* getMagicField() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -342,13 +393,6 @@ class MagicField final : public Item
|
||||
const ItemType& it = items[getID()];
|
||||
return it.combatType;
|
||||
}
|
||||
int32_t getDamage() const {
|
||||
const ItemType& it = items[getID()];
|
||||
if (it.conditionDamage) {
|
||||
return it.conditionDamage->getTotalDamage();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void onStepInField(Creature* creature);
|
||||
|
||||
private:
|
||||
|
File diff suppressed because it is too large
Load Diff
278
src/condition.h
278
src/condition.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -35,10 +35,13 @@ enum ConditionAttr_t {
|
||||
CONDITIONATTR_HEALTHGAIN,
|
||||
CONDITIONATTR_MANATICKS,
|
||||
CONDITIONATTR_MANAGAIN,
|
||||
CONDITIONATTR_DELAYED,
|
||||
CONDITIONATTR_OWNER,
|
||||
CONDITIONATTR_INTERVALDATA,
|
||||
CONDITIONATTR_CYCLE,
|
||||
CONDITIONATTR_COUNT,
|
||||
CONDITIONATTR_MAX_COUNT,
|
||||
CONDITIONATTR_FACTOR_PERCENT,
|
||||
CONDITIONATTR_SPEEDDELTA,
|
||||
CONDITIONATTR_APPLIEDSPEEDDELTA,
|
||||
CONDITIONATTR_FORMULA_MINA,
|
||||
CONDITIONATTR_FORMULA_MINB,
|
||||
CONDITIONATTR_FORMULA_MAXA,
|
||||
@ -52,8 +55,6 @@ enum ConditionAttr_t {
|
||||
CONDITIONATTR_SKILLS,
|
||||
CONDITIONATTR_STATS,
|
||||
CONDITIONATTR_OUTFIT,
|
||||
CONDITIONATTR_PERIODDAMAGE,
|
||||
CONDITIONATTR_ISBUFF,
|
||||
CONDITIONATTR_SUBID,
|
||||
|
||||
//reserved for serialization
|
||||
@ -70,9 +71,9 @@ class Condition
|
||||
{
|
||||
public:
|
||||
Condition() = default;
|
||||
Condition(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
|
||||
Condition(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
|
||||
endTime(ticks == -1 ? std::numeric_limits<int64_t>::max() : 0),
|
||||
subId(subId), ticks(ticks), conditionType(type), isBuff(buff), id(id) {}
|
||||
subId(subId), ticks(ticks), conditionType(type), id(id) {}
|
||||
virtual ~Condition() = default;
|
||||
|
||||
virtual bool startCondition(Creature* creature);
|
||||
@ -100,7 +101,7 @@ class Condition
|
||||
}
|
||||
void setTicks(int32_t newTicks);
|
||||
|
||||
static Condition* createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param = 0, bool buff = false, uint32_t subId = 0);
|
||||
static Condition* createCondition(ConditionId_t id, ConditionType_t type, int32_t ticks, int32_t param = 0, uint32_t subId = 0);
|
||||
static Condition* createCondition(PropStream& propStream);
|
||||
|
||||
virtual bool setParam(ConditionParam_t param, int32_t value);
|
||||
@ -113,23 +114,20 @@ class Condition
|
||||
bool isPersistent() const;
|
||||
|
||||
protected:
|
||||
virtual bool updateCondition(const Condition* addCondition);
|
||||
|
||||
int64_t endTime;
|
||||
uint32_t subId;
|
||||
int32_t ticks;
|
||||
ConditionType_t conditionType;
|
||||
bool isBuff;
|
||||
|
||||
private:
|
||||
ConditionId_t id;
|
||||
|
||||
virtual bool updateCondition(const Condition* addCondition);
|
||||
};
|
||||
|
||||
class ConditionGeneric : public Condition
|
||||
{
|
||||
public:
|
||||
ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0):
|
||||
Condition(id, type, ticks, buff, subId) {}
|
||||
ConditionGeneric(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0):
|
||||
Condition(id, type, ticks, subId) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
@ -145,35 +143,32 @@ class ConditionGeneric : public Condition
|
||||
class ConditionAttributes final : public ConditionGeneric
|
||||
{
|
||||
public:
|
||||
ConditionAttributes(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, buff, subId) {}
|
||||
ConditionAttributes(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, subId) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
void endCondition(Creature* creature) override;
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
bool startCondition(Creature* creature) final;
|
||||
bool executeCondition(Creature* creature, int32_t interval) final;
|
||||
void endCondition(Creature* creature) final;
|
||||
void addCondition(Creature* creature, const Condition* condition) final;
|
||||
|
||||
bool setParam(ConditionParam_t param, int32_t value) override;
|
||||
bool setParam(ConditionParam_t param, int32_t value) final;
|
||||
|
||||
ConditionAttributes* clone() const override {
|
||||
ConditionAttributes* clone() const final {
|
||||
return new ConditionAttributes(*this);
|
||||
}
|
||||
|
||||
//serialization
|
||||
void serialize(PropWriteStream& propWriteStream) override;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
|
||||
void serialize(PropWriteStream& propWriteStream) final;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
|
||||
|
||||
private:
|
||||
protected:
|
||||
int32_t skills[SKILL_LAST + 1] = {};
|
||||
int32_t skillsPercent[SKILL_LAST + 1] = {};
|
||||
int32_t specialSkills[SPECIALSKILL_LAST + 1] = {};
|
||||
int32_t stats[STAT_LAST + 1] = {};
|
||||
int32_t statsPercent[STAT_LAST + 1] = {};
|
||||
int32_t currentSkill = 0;
|
||||
int32_t currentStat = 0;
|
||||
|
||||
bool disableDefense = false;
|
||||
|
||||
void updatePercentStats(Player* player);
|
||||
void updateStats(Player* player);
|
||||
void updatePercentSkills(Player* player);
|
||||
@ -183,23 +178,23 @@ class ConditionAttributes final : public ConditionGeneric
|
||||
class ConditionRegeneration final : public ConditionGeneric
|
||||
{
|
||||
public:
|
||||
ConditionRegeneration(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0):
|
||||
ConditionGeneric(id, type, ticks, buff, subId) {}
|
||||
ConditionRegeneration(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0):
|
||||
ConditionGeneric(id, type, ticks, subId) {}
|
||||
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
void addCondition(Creature* creature, const Condition* addCondition) final;
|
||||
bool executeCondition(Creature* creature, int32_t interval) final;
|
||||
|
||||
bool setParam(ConditionParam_t param, int32_t value) override;
|
||||
bool setParam(ConditionParam_t param, int32_t value) final;
|
||||
|
||||
ConditionRegeneration* clone() const override {
|
||||
ConditionRegeneration* clone() const final {
|
||||
return new ConditionRegeneration(*this);
|
||||
}
|
||||
|
||||
//serialization
|
||||
void serialize(PropWriteStream& propWriteStream) override;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
|
||||
void serialize(PropWriteStream& propWriteStream) final;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
|
||||
|
||||
private:
|
||||
protected:
|
||||
uint32_t internalHealthTicks = 0;
|
||||
uint32_t internalManaTicks = 0;
|
||||
|
||||
@ -212,23 +207,23 @@ class ConditionRegeneration final : public ConditionGeneric
|
||||
class ConditionSoul final : public ConditionGeneric
|
||||
{
|
||||
public:
|
||||
ConditionSoul(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, buff, subId) {}
|
||||
ConditionSoul(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, subId) {}
|
||||
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
void addCondition(Creature* creature, const Condition* addCondition) final;
|
||||
bool executeCondition(Creature* creature, int32_t interval) final;
|
||||
|
||||
bool setParam(ConditionParam_t param, int32_t value) override;
|
||||
bool setParam(ConditionParam_t param, int32_t value) final;
|
||||
|
||||
ConditionSoul* clone() const override {
|
||||
ConditionSoul* clone() const final {
|
||||
return new ConditionSoul(*this);
|
||||
}
|
||||
|
||||
//serialization
|
||||
void serialize(PropWriteStream& propWriteStream) override;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
|
||||
void serialize(PropWriteStream& propWriteStream) final;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
|
||||
|
||||
private:
|
||||
protected:
|
||||
uint32_t internalSoulTicks = 0;
|
||||
uint32_t soulTicks = 0;
|
||||
uint32_t soulGain = 0;
|
||||
@ -237,13 +232,13 @@ class ConditionSoul final : public ConditionGeneric
|
||||
class ConditionInvisible final : public ConditionGeneric
|
||||
{
|
||||
public:
|
||||
ConditionInvisible(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, buff, subId) {}
|
||||
ConditionInvisible(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, subId) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
void endCondition(Creature* creature) override;
|
||||
bool startCondition(Creature* creature) final;
|
||||
void endCondition(Creature* creature) final;
|
||||
|
||||
ConditionInvisible* clone() const override {
|
||||
ConditionInvisible* clone() const final {
|
||||
return new ConditionInvisible(*this);
|
||||
}
|
||||
};
|
||||
@ -252,170 +247,133 @@ class ConditionDamage final : public Condition
|
||||
{
|
||||
public:
|
||||
ConditionDamage() = default;
|
||||
ConditionDamage(ConditionId_t id, ConditionType_t type, bool buff = false, uint32_t subId = 0) :
|
||||
Condition(id, type, 0, buff, subId) {}
|
||||
ConditionDamage(ConditionId_t id, ConditionType_t type, uint32_t subId = 0) :
|
||||
Condition(id, type, 0, subId) {
|
||||
if (type == CONDITION_POISON) {
|
||||
count = max_count = 3;
|
||||
} else if (type == CONDITION_FIRE) {
|
||||
count = max_count = 8;
|
||||
} else if (type == CONDITION_ENERGY) {
|
||||
count = max_count = 10;
|
||||
} else if (type == CONDITION_DROWN) {
|
||||
count = max_count = 3;
|
||||
}
|
||||
}
|
||||
|
||||
static void generateDamageList(int32_t amount, int32_t start, std::list<int32_t>& list);
|
||||
bool startCondition(Creature* creature) final;
|
||||
bool executeCondition(Creature* creature, int32_t interval) final;
|
||||
void endCondition(Creature* creature) final;
|
||||
void addCondition(Creature* creature, const Condition* condition) final;
|
||||
uint32_t getIcons() const final;
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
void endCondition(Creature* creature) override;
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
uint32_t getIcons() const override;
|
||||
|
||||
ConditionDamage* clone() const override {
|
||||
ConditionDamage* clone() const final {
|
||||
return new ConditionDamage(*this);
|
||||
}
|
||||
|
||||
bool setParam(ConditionParam_t param, int32_t value) override;
|
||||
bool setParam(ConditionParam_t param, int32_t value) final;
|
||||
|
||||
bool addDamage(int32_t rounds, int32_t time, int32_t value);
|
||||
bool doForceUpdate() const {
|
||||
return forceUpdate;
|
||||
}
|
||||
int32_t getTotalDamage() const;
|
||||
|
||||
//serialization
|
||||
void serialize(PropWriteStream& propWriteStream) override;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
|
||||
|
||||
private:
|
||||
int32_t maxDamage = 0;
|
||||
int32_t minDamage = 0;
|
||||
int32_t startDamage = 0;
|
||||
int32_t periodDamage = 0;
|
||||
int32_t periodDamageTick = 0;
|
||||
int32_t tickInterval = 2000;
|
||||
|
||||
bool forceUpdate = false;
|
||||
bool delayed = false;
|
||||
bool field = false;
|
||||
void serialize(PropWriteStream& propWriteStream) final;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
|
||||
protected:
|
||||
int32_t cycle = 0;
|
||||
int32_t count = 0;
|
||||
int32_t max_count = 0;
|
||||
int32_t factor_percent = -1;
|
||||
int32_t hit_damage = 0;
|
||||
bool isFirstCycle = true;
|
||||
uint32_t owner = 0;
|
||||
|
||||
bool init();
|
||||
|
||||
std::list<IntervalInfo> damageList;
|
||||
|
||||
bool getNextDamage(int32_t& damage);
|
||||
bool doDamage(Creature* creature, int32_t healthChange);
|
||||
|
||||
bool updateCondition(const Condition* addCondition) override;
|
||||
bool updateCondition(const Condition* addCondition) final;
|
||||
};
|
||||
|
||||
class ConditionSpeed final : public Condition
|
||||
{
|
||||
public:
|
||||
ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, int32_t changeSpeed) :
|
||||
Condition(id, type, ticks, buff, subId), speedDelta(changeSpeed) {}
|
||||
ConditionSpeed(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId, int32_t changeSpeed) :
|
||||
Condition(id, type, ticks, subId), speedDelta(changeSpeed) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
void endCondition(Creature* creature) override;
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
uint32_t getIcons() const override;
|
||||
bool startCondition(Creature* creature) final;
|
||||
bool executeCondition(Creature* creature, int32_t interval) final;
|
||||
void endCondition(Creature* creature) final;
|
||||
void addCondition(Creature* creature, const Condition* condition) final;
|
||||
uint32_t getIcons() const final;
|
||||
|
||||
ConditionSpeed* clone() const override {
|
||||
ConditionSpeed* clone() const final {
|
||||
return new ConditionSpeed(*this);
|
||||
}
|
||||
|
||||
bool setParam(ConditionParam_t param, int32_t value) override;
|
||||
|
||||
void setFormulaVars(float mina, float minb, float maxa, float maxb);
|
||||
void setVariation(int32_t newVariation) {
|
||||
variation = newVariation;
|
||||
}
|
||||
void setSpeedDelta(int32_t newSpeedDelta) {
|
||||
speedDelta = newSpeedDelta;
|
||||
}
|
||||
|
||||
//serialization
|
||||
void serialize(PropWriteStream& propWriteStream) override;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
|
||||
void serialize(PropWriteStream& propWriteStream) final;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
|
||||
|
||||
private:
|
||||
void getFormulaValues(int32_t var, int32_t& min, int32_t& max) const;
|
||||
|
||||
int32_t speedDelta;
|
||||
|
||||
//formula variables
|
||||
float mina = 0.0f;
|
||||
float minb = 0.0f;
|
||||
float maxa = 0.0f;
|
||||
float maxb = 0.0f;
|
||||
protected:
|
||||
int32_t appliedSpeedDelta = 0;
|
||||
int32_t speedDelta = 0;
|
||||
int32_t variation = 0;
|
||||
};
|
||||
|
||||
class ConditionOutfit final : public Condition
|
||||
{
|
||||
public:
|
||||
ConditionOutfit(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
|
||||
Condition(id, type, ticks, buff, subId) {}
|
||||
ConditionOutfit(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId = 0) :
|
||||
Condition(id, type, ticks, subId) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
void endCondition(Creature* creature) override;
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
bool startCondition(Creature* creature) final;
|
||||
bool executeCondition(Creature* creature, int32_t interval) final;
|
||||
void endCondition(Creature* creature) final;
|
||||
void addCondition(Creature* creature, const Condition* condition) final;
|
||||
|
||||
ConditionOutfit* clone() const override {
|
||||
ConditionOutfit* clone() const final {
|
||||
return new ConditionOutfit(*this);
|
||||
}
|
||||
|
||||
void setOutfit(const Outfit_t& outfit);
|
||||
|
||||
//serialization
|
||||
void serialize(PropWriteStream& propWriteStream) override;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
|
||||
void serialize(PropWriteStream& propWriteStream) final;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
|
||||
|
||||
private:
|
||||
protected:
|
||||
Outfit_t outfit;
|
||||
};
|
||||
|
||||
class ConditionLight final : public Condition
|
||||
{
|
||||
public:
|
||||
ConditionLight(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) :
|
||||
Condition(id, type, ticks, buff, subId), lightInfo(lightlevel, lightcolor) {}
|
||||
ConditionLight(ConditionId_t id, ConditionType_t type, int32_t ticks, uint32_t subId, uint8_t lightlevel, uint8_t lightcolor) :
|
||||
Condition(id, type, ticks, subId), lightInfo(lightlevel, lightcolor) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
bool executeCondition(Creature* creature, int32_t interval) override;
|
||||
void endCondition(Creature* creature) override;
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
bool startCondition(Creature* creature) final;
|
||||
bool executeCondition(Creature* creature, int32_t interval) final;
|
||||
void endCondition(Creature* creature) final;
|
||||
void addCondition(Creature* creature, const Condition* addCondition) final;
|
||||
|
||||
ConditionLight* clone() const override {
|
||||
ConditionLight* clone() const final {
|
||||
return new ConditionLight(*this);
|
||||
}
|
||||
|
||||
bool setParam(ConditionParam_t param, int32_t value) override;
|
||||
bool setParam(ConditionParam_t param, int32_t value) final;
|
||||
|
||||
//serialization
|
||||
void serialize(PropWriteStream& propWriteStream) override;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) override;
|
||||
void serialize(PropWriteStream& propWriteStream) final;
|
||||
bool unserializeProp(ConditionAttr_t attr, PropStream& propStream) final;
|
||||
|
||||
private:
|
||||
protected:
|
||||
LightInfo lightInfo;
|
||||
uint32_t internalLightTicks = 0;
|
||||
uint32_t lightChangeInterval = 0;
|
||||
};
|
||||
|
||||
class ConditionSpellCooldown final : public ConditionGeneric
|
||||
{
|
||||
public:
|
||||
ConditionSpellCooldown(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, buff, subId) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
|
||||
ConditionSpellCooldown* clone() const override {
|
||||
return new ConditionSpellCooldown(*this);
|
||||
}
|
||||
};
|
||||
|
||||
class ConditionSpellGroupCooldown final : public ConditionGeneric
|
||||
{
|
||||
public:
|
||||
ConditionSpellGroupCooldown(ConditionId_t id, ConditionType_t type, int32_t ticks, bool buff = false, uint32_t subId = 0) :
|
||||
ConditionGeneric(id, type, ticks, buff, subId) {}
|
||||
|
||||
bool startCondition(Creature* creature) override;
|
||||
void addCondition(Creature* creature, const Condition* condition) override;
|
||||
|
||||
ConditionSpellGroupCooldown* clone() const override {
|
||||
return new ConditionSpellGroupCooldown(*this);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -19,12 +19,6 @@
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#if __has_include("luajit/lua.hpp")
|
||||
#include <luajit/lua.hpp>
|
||||
#else
|
||||
#include <lua.hpp>
|
||||
#endif
|
||||
|
||||
#include "configmanager.h"
|
||||
#include "game.h"
|
||||
|
||||
@ -35,57 +29,6 @@
|
||||
|
||||
extern Game g_game;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string getGlobalString(lua_State* L, const char* identifier, const char* defaultValue)
|
||||
{
|
||||
lua_getglobal(L, identifier);
|
||||
if (!lua_isstring(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
size_t len = lua_strlen(L, -1);
|
||||
std::string ret(lua_tostring(L, -1), len);
|
||||
lua_pop(L, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t getGlobalNumber(lua_State* L, const char* identifier, const int32_t defaultValue = 0)
|
||||
{
|
||||
lua_getglobal(L, identifier);
|
||||
if (!lua_isnumber(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int32_t val = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
bool getGlobalBoolean(lua_State* L, const char* identifier, const bool defaultValue)
|
||||
{
|
||||
lua_getglobal(L, identifier);
|
||||
if (!lua_isboolean(L, -1)) {
|
||||
if (!lua_isstring(L, -1)) {
|
||||
lua_pop(L, 1);
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
size_t len = lua_strlen(L, -1);
|
||||
std::string ret(lua_tostring(L, -1), len);
|
||||
lua_pop(L, 1);
|
||||
return booleanString(ret);
|
||||
}
|
||||
|
||||
int val = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return val != 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool ConfigManager::load()
|
||||
{
|
||||
lua_State* L = luaL_newstate();
|
||||
@ -120,10 +63,9 @@ bool ConfigManager::load()
|
||||
integer[GAME_PORT] = getGlobalNumber(L, "gameProtocolPort", 7172);
|
||||
integer[LOGIN_PORT] = getGlobalNumber(L, "loginProtocolPort", 7171);
|
||||
integer[STATUS_PORT] = getGlobalNumber(L, "statusProtocolPort", 7171);
|
||||
|
||||
integer[MARKET_OFFER_DURATION] = getGlobalNumber(L, "marketOfferDuration", 30 * 24 * 60 * 60);
|
||||
}
|
||||
|
||||
boolean[SHOW_MONSTER_LOOT] = getGlobalBoolean(L, "showMonsterLoot", true);
|
||||
boolean[ALLOW_CHANGEOUTFIT] = getGlobalBoolean(L, "allowChangeOutfit", true);
|
||||
boolean[ONE_PLAYER_ON_ACCOUNT] = getGlobalBoolean(L, "onePlayerOnlinePerAccount", true);
|
||||
boolean[AIMBOT_HOTKEY_ENABLED] = getGlobalBoolean(L, "hotkeyAimbotEnabled", true);
|
||||
@ -132,19 +74,14 @@ bool ConfigManager::load()
|
||||
boolean[FREE_PREMIUM] = getGlobalBoolean(L, "freePremium", false);
|
||||
boolean[REPLACE_KICK_ON_LOGIN] = getGlobalBoolean(L, "replaceKickOnLogin", true);
|
||||
boolean[ALLOW_CLONES] = getGlobalBoolean(L, "allowClones", false);
|
||||
boolean[MARKET_PREMIUM] = getGlobalBoolean(L, "premiumToCreateMarketOffer", true);
|
||||
boolean[EMOTE_SPELLS] = getGlobalBoolean(L, "emoteSpells", false);
|
||||
boolean[STAMINA_SYSTEM] = getGlobalBoolean(L, "staminaSystem", true);
|
||||
boolean[WARN_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "warnUnsafeScripts", true);
|
||||
boolean[CONVERT_UNSAFE_SCRIPTS] = getGlobalBoolean(L, "convertUnsafeScripts", true);
|
||||
boolean[CLASSIC_EQUIPMENT_SLOTS] = getGlobalBoolean(L, "classicEquipmentSlots", false);
|
||||
boolean[CLASSIC_ATTACK_SPEED] = getGlobalBoolean(L, "classicAttackSpeed", false);
|
||||
boolean[SCRIPTS_CONSOLE_LOGS] = getGlobalBoolean(L, "showScriptsLogInConsole", true);
|
||||
boolean[SERVER_SAVE_NOTIFY_MESSAGE] = getGlobalBoolean(L, "serverSaveNotifyMessage", true);
|
||||
boolean[SERVER_SAVE_CLEAN_MAP] = getGlobalBoolean(L, "serverSaveCleanMap", false);
|
||||
boolean[SERVER_SAVE_CLOSE] = getGlobalBoolean(L, "serverSaveClose", false);
|
||||
boolean[SERVER_SAVE_SHUTDOWN] = getGlobalBoolean(L, "serverSaveShutdown", true);
|
||||
boolean[ONLINE_OFFLINE_CHARLIST] = getGlobalBoolean(L, "showOnlineStatusInCharlist", false);
|
||||
boolean[TELEPORT_NEWBIES] = getGlobalBoolean(L, "teleportNewbies", true);
|
||||
boolean[STACK_CUMULATIVES] = getGlobalBoolean(L, "autoStackCumulatives", false);
|
||||
boolean[BLOCK_HEIGHT] = getGlobalBoolean(L, "blockHeight", false);
|
||||
boolean[DROP_ITEMS] = getGlobalBoolean(L, "dropItems", false);
|
||||
|
||||
|
||||
string[DEFAULT_PRIORITY] = getGlobalString(L, "defaultPriority", "high");
|
||||
string[SERVER_NAME] = getGlobalString(L, "serverName", "");
|
||||
@ -164,9 +101,7 @@ bool ConfigManager::load()
|
||||
integer[RATE_LOOT] = getGlobalNumber(L, "rateLoot", 2);
|
||||
integer[RATE_MAGIC] = getGlobalNumber(L, "rateMagic", 3);
|
||||
integer[RATE_SPAWN] = getGlobalNumber(L, "rateSpawn", 1);
|
||||
integer[HOUSE_PRICE] = getGlobalNumber(L, "housePriceEachSQM", 1000);
|
||||
integer[KILLS_TO_RED] = getGlobalNumber(L, "killsToRedSkull", 3);
|
||||
integer[KILLS_TO_BLACK] = getGlobalNumber(L, "killsToBlackSkull", 6);
|
||||
integer[BAN_LENGTH] = getGlobalNumber(L, "banLength", 30 * 24 * 60 * 60);
|
||||
integer[ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenActions", 200);
|
||||
integer[EX_ACTIONS_DELAY_INTERVAL] = getGlobalNumber(L, "timeBetweenExActions", 1000);
|
||||
integer[MAX_MESSAGEBUFFER] = getGlobalNumber(L, "maxMessageBuffer", 4);
|
||||
@ -174,14 +109,20 @@ bool ConfigManager::load()
|
||||
integer[PROTECTION_LEVEL] = getGlobalNumber(L, "protectionLevel", 1);
|
||||
integer[DEATH_LOSE_PERCENT] = getGlobalNumber(L, "deathLosePercent", -1);
|
||||
integer[STATUSQUERY_TIMEOUT] = getGlobalNumber(L, "statusTimeout", 5000);
|
||||
integer[FRAG_TIME] = getGlobalNumber(L, "timeToDecreaseFrags", 24 * 60 * 60 * 1000);
|
||||
integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60 * 1000);
|
||||
integer[WHITE_SKULL_TIME] = getGlobalNumber(L, "whiteSkullTime", 15 * 60);
|
||||
integer[RED_SKULL_TIME] = getGlobalNumber(L, "redSkullTime", 30 * 24 * 60 * 60);
|
||||
integer[KILLS_DAY_RED_SKULL] = getGlobalNumber(L, "killsDayRedSkull", 3);
|
||||
integer[KILLS_WEEK_RED_SKULL] = getGlobalNumber(L, "killsWeekRedSkull", 5);
|
||||
integer[KILLS_MONTH_RED_SKULL] = getGlobalNumber(L, "killsMonthRedSkull", 10);
|
||||
integer[KILLS_DAY_BANISHMENT] = getGlobalNumber(L, "killsDayBanishment", 5);
|
||||
integer[KILLS_WEEK_BANISHMENT] = getGlobalNumber(L, "killsWeekBanishment", 8);
|
||||
integer[KILLS_MONTH_BANISHMENT] = getGlobalNumber(L, "killsMonthBanishment", 10);
|
||||
integer[STAIRHOP_DELAY] = getGlobalNumber(L, "stairJumpExhaustion", 2000);
|
||||
integer[EXP_FROM_PLAYERS_LEVEL_RANGE] = getGlobalNumber(L, "expFromPlayersLevelRange", 75);
|
||||
integer[CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES] = getGlobalNumber(L, "checkExpiredMarketOffersEachMinutes", 60);
|
||||
integer[MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER] = getGlobalNumber(L, "maxMarketOffersAtATimePerPlayer", 100);
|
||||
integer[MAX_PACKETS_PER_SECOND] = getGlobalNumber(L, "maxPacketsPerSecond", 25);
|
||||
integer[SERVER_SAVE_NOTIFY_DURATION] = getGlobalNumber(L, "serverSaveNotifyDuration", 5);
|
||||
integer[NEWBIE_TOWN] = getGlobalNumber(L, "newbieTownId", 1);
|
||||
integer[NEWBIE_LEVEL_THRESHOLD] = getGlobalNumber(L, "newbieLevelThreshold", 5);
|
||||
integer[MONEY_RATE] = getGlobalNumber(L, "moneyRate", 1);
|
||||
|
||||
loaded = true;
|
||||
lua_close(L);
|
||||
@ -197,13 +138,11 @@ bool ConfigManager::reload()
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::string dummyStr;
|
||||
|
||||
const std::string& ConfigManager::getString(string_config_t what) const
|
||||
{
|
||||
if (what >= LAST_STRING_CONFIG) {
|
||||
std::cout << "[Warning - ConfigManager::getString] Accessing invalid index: " << what << std::endl;
|
||||
return dummyStr;
|
||||
return string[DUMMY_STR];
|
||||
}
|
||||
return string[what];
|
||||
}
|
||||
@ -225,3 +164,47 @@ bool ConfigManager::getBoolean(boolean_config_t what) const
|
||||
}
|
||||
return boolean[what];
|
||||
}
|
||||
|
||||
std::string ConfigManager::getGlobalString(lua_State* L, const char* identifier, const char* defaultValue)
|
||||
{
|
||||
lua_getglobal(L, identifier);
|
||||
if (!lua_isstring(L, -1)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
size_t len = lua_strlen(L, -1);
|
||||
std::string ret(lua_tostring(L, -1), len);
|
||||
lua_pop(L, 1);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int32_t ConfigManager::getGlobalNumber(lua_State* L, const char* identifier, const int32_t defaultValue)
|
||||
{
|
||||
lua_getglobal(L, identifier);
|
||||
if (!lua_isnumber(L, -1)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int32_t val = lua_tonumber(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return val;
|
||||
}
|
||||
|
||||
bool ConfigManager::getGlobalBoolean(lua_State* L, const char* identifier, const bool defaultValue)
|
||||
{
|
||||
lua_getglobal(L, identifier);
|
||||
if (!lua_isboolean(L, -1)) {
|
||||
if (!lua_isstring(L, -1)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
size_t len = lua_strlen(L, -1);
|
||||
std::string ret(lua_tostring(L, -1), len);
|
||||
lua_pop(L, 1);
|
||||
return booleanString(ret);
|
||||
}
|
||||
|
||||
int val = lua_toboolean(L, -1);
|
||||
lua_pop(L, 1);
|
||||
return val != 0;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,10 +20,13 @@
|
||||
#ifndef FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39
|
||||
#define FS_CONFIGMANAGER_H_6BDD23BD0B8344F4B7C40E8BE6AF6F39
|
||||
|
||||
#include <luajit/lua.hpp>
|
||||
|
||||
class ConfigManager
|
||||
{
|
||||
public:
|
||||
enum boolean_config_t {
|
||||
SHOW_MONSTER_LOOT,
|
||||
ALLOW_CHANGEOUTFIT,
|
||||
ONE_PLAYER_ON_ACCOUNT,
|
||||
AIMBOT_HOTKEY_ENABLED,
|
||||
@ -34,24 +37,19 @@ class ConfigManager
|
||||
ALLOW_CLONES,
|
||||
BIND_ONLY_GLOBAL_ADDRESS,
|
||||
OPTIMIZE_DATABASE,
|
||||
MARKET_PREMIUM,
|
||||
EMOTE_SPELLS,
|
||||
STAMINA_SYSTEM,
|
||||
WARN_UNSAFE_SCRIPTS,
|
||||
CONVERT_UNSAFE_SCRIPTS,
|
||||
CLASSIC_EQUIPMENT_SLOTS,
|
||||
CLASSIC_ATTACK_SPEED,
|
||||
SCRIPTS_CONSOLE_LOGS,
|
||||
SERVER_SAVE_NOTIFY_MESSAGE,
|
||||
SERVER_SAVE_CLEAN_MAP,
|
||||
SERVER_SAVE_CLOSE,
|
||||
SERVER_SAVE_SHUTDOWN,
|
||||
ONLINE_OFFLINE_CHARLIST,
|
||||
TELEPORT_NEWBIES,
|
||||
STACK_CUMULATIVES,
|
||||
BLOCK_HEIGHT,
|
||||
DROP_ITEMS,
|
||||
|
||||
LAST_BOOLEAN_CONFIG /* this must be the last one */
|
||||
};
|
||||
|
||||
enum string_config_t {
|
||||
DUMMY_STR,
|
||||
MAP_NAME,
|
||||
HOUSE_RENT_PERIOD,
|
||||
SERVER_NAME,
|
||||
@ -84,9 +82,7 @@ class ConfigManager
|
||||
RATE_LOOT,
|
||||
RATE_MAGIC,
|
||||
RATE_SPAWN,
|
||||
HOUSE_PRICE,
|
||||
KILLS_TO_RED,
|
||||
KILLS_TO_BLACK,
|
||||
BAN_LENGTH,
|
||||
MAX_MESSAGEBUFFER,
|
||||
ACTIONS_DELAY_INTERVAL,
|
||||
EX_ACTIONS_DELAY_INTERVAL,
|
||||
@ -94,18 +90,23 @@ class ConfigManager
|
||||
PROTECTION_LEVEL,
|
||||
DEATH_LOSE_PERCENT,
|
||||
STATUSQUERY_TIMEOUT,
|
||||
FRAG_TIME,
|
||||
WHITE_SKULL_TIME,
|
||||
RED_SKULL_TIME,
|
||||
KILLS_DAY_RED_SKULL,
|
||||
KILLS_WEEK_RED_SKULL,
|
||||
KILLS_MONTH_RED_SKULL,
|
||||
KILLS_DAY_BANISHMENT,
|
||||
KILLS_WEEK_BANISHMENT,
|
||||
KILLS_MONTH_BANISHMENT,
|
||||
GAME_PORT,
|
||||
LOGIN_PORT,
|
||||
STATUS_PORT,
|
||||
STAIRHOP_DELAY,
|
||||
MARKET_OFFER_DURATION,
|
||||
CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES,
|
||||
MAX_MARKET_OFFERS_AT_A_TIME_PER_PLAYER,
|
||||
EXP_FROM_PLAYERS_LEVEL_RANGE,
|
||||
MAX_PACKETS_PER_SECOND,
|
||||
SERVER_SAVE_NOTIFY_DURATION,
|
||||
NEWBIE_TOWN,
|
||||
NEWBIE_LEVEL_THRESHOLD,
|
||||
MONEY_RATE,
|
||||
|
||||
LAST_INTEGER_CONFIG /* this must be the last one */
|
||||
};
|
||||
@ -118,6 +119,10 @@ class ConfigManager
|
||||
bool getBoolean(boolean_config_t what) const;
|
||||
|
||||
private:
|
||||
static std::string getGlobalString(lua_State* L, const char* identifier, const char* defaultValue);
|
||||
static int32_t getGlobalNumber(lua_State* L, const char* identifier, const int32_t defaultValue = 0);
|
||||
static bool getGlobalBoolean(lua_State* L, const char* identifier, const bool defaultValue);
|
||||
|
||||
std::string string[LAST_STRING_CONFIG] = {};
|
||||
int32_t integer[LAST_INTEGER_CONFIG] = {};
|
||||
bool boolean[LAST_BOOLEAN_CONFIG] = {};
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -182,60 +182,51 @@ void Connection::parsePacket(const boost::system::error_code& error)
|
||||
if (error) {
|
||||
close(FORCE_CLOSE);
|
||||
return;
|
||||
} else if (connectionState != CONNECTION_STATE_OPEN) {
|
||||
}
|
||||
else if (connectionState != CONNECTION_STATE_OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Check packet checksum
|
||||
uint32_t checksum;
|
||||
int32_t len = msg.getLength() - msg.getBufferPosition() - NetworkMessage::CHECKSUM_LENGTH;
|
||||
if (len > 0) {
|
||||
checksum = adlerChecksum(msg.getBuffer() + msg.getBufferPosition() + NetworkMessage::CHECKSUM_LENGTH, len);
|
||||
} else {
|
||||
checksum = 0;
|
||||
}
|
||||
|
||||
uint32_t recvChecksum = msg.get<uint32_t>();
|
||||
if (recvChecksum != checksum) {
|
||||
// it might not have been the checksum, step back
|
||||
msg.skipBytes(-NetworkMessage::CHECKSUM_LENGTH);
|
||||
}
|
||||
|
||||
if (!receivedFirst) {
|
||||
// First message received
|
||||
receivedFirst = true;
|
||||
|
||||
if (!protocol) {
|
||||
// Game protocol has already been created at this point
|
||||
protocol = service_port->make_protocol(recvChecksum == checksum, msg, shared_from_this());
|
||||
protocol = service_port->make_protocol(msg, shared_from_this());
|
||||
if (!protocol) {
|
||||
close(FORCE_CLOSE);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
msg.skipBytes(1); // Skip protocol ID
|
||||
}
|
||||
|
||||
protocol->onRecvFirstMessage(msg);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
protocol->onRecvMessage(msg); // Send the packet to the current protocol
|
||||
}
|
||||
|
||||
try {
|
||||
readTimer.expires_from_now(boost::posix_time::seconds(CONNECTION_READ_TIMEOUT));
|
||||
readTimer.async_wait(std::bind(&Connection::handleTimeout, std::weak_ptr<Connection>(shared_from_this()),
|
||||
std::placeholders::_1));
|
||||
std::placeholders::_1));
|
||||
|
||||
// Wait to the next packet
|
||||
boost::asio::async_read(socket,
|
||||
boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
|
||||
std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
|
||||
} catch (boost::system::system_error& e) {
|
||||
boost::asio::buffer(msg.getBuffer(), NetworkMessage::HEADER_LENGTH),
|
||||
std::bind(&Connection::parseHeader, shared_from_this(), std::placeholders::_1));
|
||||
}
|
||||
catch (boost::system::system_error& e) {
|
||||
std::cout << "[Network error - Connection::parsePacket] " << e.what() << std::endl;
|
||||
close(FORCE_CLOSE);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Connection::send(const OutputMessage_ptr& msg)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lockClass(connectionLock);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -28,17 +28,17 @@ static constexpr int32_t CONNECTION_WRITE_TIMEOUT = 30;
|
||||
static constexpr int32_t CONNECTION_READ_TIMEOUT = 30;
|
||||
|
||||
class Protocol;
|
||||
using Protocol_ptr = std::shared_ptr<Protocol>;
|
||||
typedef std::shared_ptr<Protocol> Protocol_ptr;
|
||||
class OutputMessage;
|
||||
using OutputMessage_ptr = std::shared_ptr<OutputMessage>;
|
||||
typedef std::shared_ptr<OutputMessage> OutputMessage_ptr;
|
||||
class Connection;
|
||||
using Connection_ptr = std::shared_ptr<Connection> ;
|
||||
using ConnectionWeak_ptr = std::weak_ptr<Connection>;
|
||||
typedef std::shared_ptr<Connection> Connection_ptr;
|
||||
typedef std::weak_ptr<Connection> ConnectionWeak_ptr;
|
||||
class ServiceBase;
|
||||
using Service_ptr = std::shared_ptr<ServiceBase>;
|
||||
typedef std::shared_ptr<ServiceBase> Service_ptr;
|
||||
class ServicePort;
|
||||
using ServicePort_ptr = std::shared_ptr<ServicePort>;
|
||||
using ConstServicePort_ptr = std::shared_ptr<const ServicePort>;
|
||||
typedef std::shared_ptr<ServicePort> ServicePort_ptr;
|
||||
typedef std::shared_ptr<const ServicePort> ConstServicePort_ptr;
|
||||
|
||||
class ConnectionManager
|
||||
{
|
||||
@ -52,7 +52,7 @@ class ConnectionManager
|
||||
void releaseConnection(const Connection_ptr& connection);
|
||||
void closeAll();
|
||||
|
||||
private:
|
||||
protected:
|
||||
ConnectionManager() = default;
|
||||
|
||||
std::unordered_set<Connection_ptr> connections;
|
||||
@ -78,8 +78,12 @@ class Connection : public std::enable_shared_from_this<Connection>
|
||||
readTimer(io_service),
|
||||
writeTimer(io_service),
|
||||
service_port(std::move(service_port)),
|
||||
socket(io_service),
|
||||
timeConnected(time(nullptr)) {}
|
||||
socket(io_service) {
|
||||
connectionState = CONNECTION_STATE_OPEN;
|
||||
receivedFirst = false;
|
||||
packetsSent = 0;
|
||||
timeConnected = time(nullptr);
|
||||
}
|
||||
~Connection();
|
||||
|
||||
friend class ConnectionManager;
|
||||
@ -124,10 +128,10 @@ class Connection : public std::enable_shared_from_this<Connection>
|
||||
boost::asio::ip::tcp::socket socket;
|
||||
|
||||
time_t timeConnected;
|
||||
uint32_t packetsSent = 0;
|
||||
uint32_t packetsSent;
|
||||
|
||||
bool connectionState = CONNECTION_STATE_OPEN;
|
||||
bool receivedFirst = false;
|
||||
bool connectionState;
|
||||
bool receivedFirst;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
414
src/const.h
414
src/const.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -52,67 +52,6 @@ enum MagicEffectClasses : uint8_t {
|
||||
CONST_ME_SOUND_WHITE = 25,
|
||||
CONST_ME_BUBBLES = 26,
|
||||
CONST_ME_CRAPS = 27,
|
||||
CONST_ME_GIFT_WRAPS = 28,
|
||||
CONST_ME_FIREWORK_YELLOW = 29,
|
||||
CONST_ME_FIREWORK_RED = 30,
|
||||
CONST_ME_FIREWORK_BLUE = 31,
|
||||
CONST_ME_STUN = 32,
|
||||
CONST_ME_SLEEP = 33,
|
||||
CONST_ME_WATERCREATURE = 34,
|
||||
CONST_ME_GROUNDSHAKER = 35,
|
||||
CONST_ME_HEARTS = 36,
|
||||
CONST_ME_FIREATTACK = 37,
|
||||
CONST_ME_ENERGYAREA = 38,
|
||||
CONST_ME_SMALLCLOUDS = 39,
|
||||
CONST_ME_HOLYDAMAGE = 40,
|
||||
CONST_ME_BIGCLOUDS = 41,
|
||||
CONST_ME_ICEAREA = 42,
|
||||
CONST_ME_ICETORNADO = 43,
|
||||
CONST_ME_ICEATTACK = 44,
|
||||
CONST_ME_STONES = 45,
|
||||
CONST_ME_SMALLPLANTS = 46,
|
||||
CONST_ME_CARNIPHILA = 47,
|
||||
CONST_ME_PURPLEENERGY = 48,
|
||||
CONST_ME_YELLOWENERGY = 49,
|
||||
CONST_ME_HOLYAREA = 50,
|
||||
CONST_ME_BIGPLANTS = 51,
|
||||
CONST_ME_CAKE = 52,
|
||||
CONST_ME_GIANTICE = 53,
|
||||
CONST_ME_WATERSPLASH = 54,
|
||||
CONST_ME_PLANTATTACK = 55,
|
||||
CONST_ME_TUTORIALARROW = 56,
|
||||
CONST_ME_TUTORIALSQUARE = 57,
|
||||
CONST_ME_MIRRORHORIZONTAL = 58,
|
||||
CONST_ME_MIRRORVERTICAL = 59,
|
||||
CONST_ME_SKULLHORIZONTAL = 60,
|
||||
CONST_ME_SKULLVERTICAL = 61,
|
||||
CONST_ME_ASSASSIN = 62,
|
||||
CONST_ME_STEPSHORIZONTAL = 63,
|
||||
CONST_ME_BLOODYSTEPS = 64,
|
||||
CONST_ME_STEPSVERTICAL = 65,
|
||||
CONST_ME_YALAHARIGHOST = 66,
|
||||
CONST_ME_BATS = 67,
|
||||
CONST_ME_SMOKE = 68,
|
||||
CONST_ME_INSECTS = 69,
|
||||
CONST_ME_DRAGONHEAD = 70,
|
||||
CONST_ME_ORCSHAMAN = 71,
|
||||
CONST_ME_ORCSHAMAN_FIRE = 72,
|
||||
CONST_ME_THUNDER = 73,
|
||||
CONST_ME_FERUMBRAS = 74,
|
||||
CONST_ME_CONFETTI_HORIZONTAL = 75,
|
||||
CONST_ME_CONFETTI_VERTICAL = 76,
|
||||
// 77-157 are empty
|
||||
CONST_ME_BLACKSMOKE = 158,
|
||||
// 159-166 are empty
|
||||
CONST_ME_REDSMOKE = 167,
|
||||
CONST_ME_YELLOWSMOKE = 168,
|
||||
CONST_ME_GREENSMOKE = 169,
|
||||
CONST_ME_PURPLESMOKE = 170,
|
||||
CONST_ME_EARLY_THUNDER = 171,
|
||||
CONST_ME_RAGIAZ_BONECAPSULE = 172,
|
||||
CONST_ME_CRITICAL_DAMAGE = 173,
|
||||
// 174 is empty
|
||||
CONST_ME_PLUNGING_FISH = 175,
|
||||
};
|
||||
|
||||
enum ShootType_t : uint8_t {
|
||||
@ -133,192 +72,75 @@ enum ShootType_t : uint8_t {
|
||||
CONST_ANI_SNOWBALL = 13,
|
||||
CONST_ANI_POWERBOLT = 14,
|
||||
CONST_ANI_POISON = 15,
|
||||
CONST_ANI_INFERNALBOLT = 16,
|
||||
CONST_ANI_HUNTINGSPEAR = 17,
|
||||
CONST_ANI_ENCHANTEDSPEAR = 18,
|
||||
CONST_ANI_REDSTAR = 19,
|
||||
CONST_ANI_GREENSTAR = 20,
|
||||
CONST_ANI_ROYALSPEAR = 21,
|
||||
CONST_ANI_SNIPERARROW = 22,
|
||||
CONST_ANI_ONYXARROW = 23,
|
||||
CONST_ANI_PIERCINGBOLT = 24,
|
||||
CONST_ANI_WHIRLWINDSWORD = 25,
|
||||
CONST_ANI_WHIRLWINDAXE = 26,
|
||||
CONST_ANI_WHIRLWINDCLUB = 27,
|
||||
CONST_ANI_ETHEREALSPEAR = 28,
|
||||
CONST_ANI_ICE = 29,
|
||||
CONST_ANI_EARTH = 30,
|
||||
CONST_ANI_HOLY = 31,
|
||||
CONST_ANI_SUDDENDEATH = 32,
|
||||
CONST_ANI_FLASHARROW = 33,
|
||||
CONST_ANI_FLAMMINGARROW = 34,
|
||||
CONST_ANI_SHIVERARROW = 35,
|
||||
CONST_ANI_ENERGYBALL = 36,
|
||||
CONST_ANI_SMALLICE = 37,
|
||||
CONST_ANI_SMALLHOLY = 38,
|
||||
CONST_ANI_SMALLEARTH = 39,
|
||||
CONST_ANI_EARTHARROW = 40,
|
||||
CONST_ANI_EXPLOSION = 41,
|
||||
CONST_ANI_CAKE = 42,
|
||||
|
||||
CONST_ANI_TARSALARROW = 44,
|
||||
CONST_ANI_VORTEXBOLT = 45,
|
||||
|
||||
CONST_ANI_PRISMATICBOLT = 48,
|
||||
CONST_ANI_CRYSTALLINEARROW = 49,
|
||||
CONST_ANI_DRILLBOLT = 50,
|
||||
CONST_ANI_ENVENOMEDARROW = 51,
|
||||
|
||||
CONST_ANI_GLOOTHSPEAR = 53,
|
||||
CONST_ANI_SIMPLEARROW = 54,
|
||||
|
||||
// for internal use, don't send to client
|
||||
CONST_ANI_WEAPONTYPE = 0xFE, // 254
|
||||
};
|
||||
|
||||
enum SpeakClasses : uint8_t {
|
||||
TALKTYPE_SAY = 1,
|
||||
TALKTYPE_WHISPER = 2,
|
||||
TALKTYPE_YELL = 3,
|
||||
TALKTYPE_PRIVATE_FROM = 4,
|
||||
TALKTYPE_PRIVATE_TO = 5,
|
||||
TALKTYPE_CHANNEL_Y = 7,
|
||||
TALKTYPE_CHANNEL_O = 8,
|
||||
TALKTYPE_PRIVATE_NP = 10,
|
||||
TALKTYPE_PRIVATE_PN = 12,
|
||||
TALKTYPE_BROADCAST = 13,
|
||||
TALKTYPE_CHANNEL_R1 = 14, //red - #c text
|
||||
TALKTYPE_PRIVATE_RED_FROM = 15, //@name@text
|
||||
TALKTYPE_PRIVATE_RED_TO = 16, //@name@text
|
||||
TALKTYPE_MONSTER_SAY = 36,
|
||||
TALKTYPE_MONSTER_YELL = 37,
|
||||
|
||||
TALKTYPE_CHANNEL_R2 = 0xFF, //#d
|
||||
TALKTYPE_PRIVATE = 4,
|
||||
TALKTYPE_CHANNEL_Y = 5, // Yellow
|
||||
TALKTYPE_RVR_CHANNEL = 6,
|
||||
TALKTYPE_RVR_ANSWER = 7,
|
||||
TALKTYPE_RVR_CONTINUE = 8,
|
||||
TALKTYPE_BROADCAST = 9,
|
||||
TALKTYPE_CHANNEL_R1 = 10, // Red - #c text
|
||||
TALKTYPE_PRIVATE_RED = 11, // @name@text
|
||||
TALKTYPE_CHANNEL_O = 12, // orange
|
||||
TALKTYPE_CHANNEL_R2 = 13, // red anonymous - #d text
|
||||
TALKTYPE_MONSTER_YELL = 0x10,
|
||||
TALKTYPE_MONSTER_SAY = 0x11,
|
||||
};
|
||||
|
||||
enum MessageClasses : uint8_t {
|
||||
MESSAGE_STATUS_CONSOLE_BLUE = 4, /*FIXME Blue message in the console*/
|
||||
MESSAGE_STATUS_CONSOLE_YELLOW = 0x01, //Yellow message in the console
|
||||
MESSAGE_STATUS_CONSOLE_LBLUE = 0x04, //Light blue message in the console
|
||||
MESSAGE_STATUS_CONSOLE_ORANGE = 0x11, //Orange message in the console
|
||||
MESSAGE_STATUS_WARNING = 0x12, //Red message in game window and in the console
|
||||
MESSAGE_EVENT_ADVANCE = 0x13, //White message in game window and in the console
|
||||
MESSAGE_EVENT_DEFAULT = 0x14, //White message at the bottom of the game window and in the console
|
||||
MESSAGE_STATUS_DEFAULT = 0x15, //White message at the bottom of the game window and in the console
|
||||
MESSAGE_INFO_DESCR = 0x16, //Green message in game window and in the console
|
||||
MESSAGE_STATUS_SMALL = 0x17, //White message at the bottom of the game window"
|
||||
MESSAGE_STATUS_CONSOLE_BLUE = 0x18, //Blue message in the console
|
||||
MESSAGE_STATUS_CONSOLE_RED = 0x19, //Red message in the console
|
||||
|
||||
MESSAGE_STATUS_CONSOLE_RED = 13, /*Red message in the console*/
|
||||
|
||||
MESSAGE_STATUS_DEFAULT = 17, /*White message at the bottom of the game window and in the console*/
|
||||
MESSAGE_STATUS_WARNING = 18, /*Red message in game window and in the console*/
|
||||
MESSAGE_EVENT_ADVANCE = 19, /*White message in game window and in the console*/
|
||||
|
||||
MESSAGE_STATUS_SMALL = 21, /*White message at the bottom of the game window"*/
|
||||
MESSAGE_INFO_DESCR = 22, /*Green message in game window and in the console*/
|
||||
MESSAGE_DAMAGE_DEALT = 23,
|
||||
MESSAGE_DAMAGE_RECEIVED = 24,
|
||||
MESSAGE_HEALED = 25,
|
||||
MESSAGE_EXPERIENCE = 26,
|
||||
MESSAGE_DAMAGE_OTHERS = 27,
|
||||
MESSAGE_HEALED_OTHERS = 28,
|
||||
MESSAGE_EXPERIENCE_OTHERS = 29,
|
||||
MESSAGE_EVENT_DEFAULT = 30, /*White message at the bottom of the game window and in the console*/
|
||||
MESSAGE_LOOT = 31,
|
||||
|
||||
MESSAGE_GUILD = 33, /*White message in channel (+ channelId)*/
|
||||
MESSAGE_PARTY_MANAGEMENT = 34, /*White message in channel (+ channelId)*/
|
||||
MESSAGE_PARTY = 35, /*White message in channel (+ channelId)*/
|
||||
MESSAGE_EVENT_ORANGE = 36, /*Orange message in the console*/
|
||||
MESSAGE_STATUS_CONSOLE_ORANGE = 37, /*Orange message in the console*/
|
||||
MESSAGE_CLASS_FIRST = MESSAGE_STATUS_CONSOLE_YELLOW,
|
||||
MESSAGE_CLASS_LAST = MESSAGE_STATUS_CONSOLE_RED,
|
||||
};
|
||||
|
||||
enum FluidColors_t : uint8_t {
|
||||
FLUID_EMPTY,
|
||||
FLUID_BLUE,
|
||||
FLUID_RED,
|
||||
FLUID_BROWN,
|
||||
FLUID_GREEN,
|
||||
FLUID_YELLOW,
|
||||
FLUID_WHITE,
|
||||
FLUID_PURPLE,
|
||||
};
|
||||
|
||||
enum FluidTypes_t : uint8_t {
|
||||
FLUID_NONE = FLUID_EMPTY,
|
||||
FLUID_WATER = FLUID_BLUE,
|
||||
FLUID_BLOOD = FLUID_RED,
|
||||
FLUID_BEER = FLUID_BROWN,
|
||||
FLUID_SLIME = FLUID_GREEN,
|
||||
FLUID_LEMONADE = FLUID_YELLOW,
|
||||
FLUID_MILK = FLUID_WHITE,
|
||||
FLUID_MANA = FLUID_PURPLE,
|
||||
|
||||
FLUID_LIFE = FLUID_RED + 8,
|
||||
FLUID_OIL = FLUID_BROWN + 8,
|
||||
FLUID_URINE = FLUID_YELLOW + 8,
|
||||
FLUID_COCONUTMILK = FLUID_WHITE + 8,
|
||||
FLUID_WINE = FLUID_PURPLE + 8,
|
||||
|
||||
FLUID_MUD = FLUID_BROWN + 16,
|
||||
FLUID_FRUITJUICE = FLUID_YELLOW + 16,
|
||||
|
||||
FLUID_LAVA = FLUID_RED + 24,
|
||||
FLUID_RUM = FLUID_BROWN + 24,
|
||||
FLUID_SWAMP = FLUID_GREEN + 24,
|
||||
|
||||
FLUID_TEA = FLUID_BROWN + 32,
|
||||
|
||||
FLUID_MEAD = FLUID_BROWN + 40,
|
||||
};
|
||||
|
||||
const uint8_t reverseFluidMap[] = {
|
||||
FLUID_EMPTY,
|
||||
enum FluidTypes_t : uint8_t
|
||||
{
|
||||
FLUID_NONE = 0,
|
||||
FLUID_WATER,
|
||||
FLUID_MANA,
|
||||
FLUID_BEER,
|
||||
FLUID_EMPTY,
|
||||
FLUID_BLOOD,
|
||||
FLUID_SLIME,
|
||||
FLUID_EMPTY,
|
||||
FLUID_LEMONADE,
|
||||
FLUID_MILK,
|
||||
};
|
||||
|
||||
const uint8_t clientToServerFluidMap[] = {
|
||||
FLUID_EMPTY,
|
||||
FLUID_WATER,
|
||||
FLUID_MANA,
|
||||
FLUID_WINE,
|
||||
FLUID_BEER,
|
||||
FLUID_MUD,
|
||||
FLUID_BLOOD,
|
||||
FLUID_SLIME,
|
||||
FLUID_RUM,
|
||||
FLUID_LEMONADE,
|
||||
FLUID_MILK,
|
||||
FLUID_WINE,
|
||||
FLUID_LIFE,
|
||||
FLUID_URINE,
|
||||
FLUID_OIL,
|
||||
FLUID_FRUITJUICE,
|
||||
FLUID_URINE,
|
||||
FLUID_MILK,
|
||||
FLUID_MANAFLUID,
|
||||
FLUID_LIFEFLUID,
|
||||
FLUID_LEMONADE,
|
||||
FLUID_RUM,
|
||||
FLUID_COCONUTMILK,
|
||||
FLUID_TEA,
|
||||
FLUID_MEAD,
|
||||
FLUID_FRUITJUICE,
|
||||
};
|
||||
|
||||
enum ClientFluidTypes_t : uint8_t {
|
||||
CLIENTFLUID_EMPTY = 0,
|
||||
CLIENTFLUID_BLUE = 1,
|
||||
CLIENTFLUID_PURPLE = 2,
|
||||
CLIENTFLUID_BROWN_1 = 3,
|
||||
CLIENTFLUID_BROWN_2 = 4,
|
||||
CLIENTFLUID_RED = 5,
|
||||
CLIENTFLUID_GREEN = 6,
|
||||
CLIENTFLUID_BROWN = 7,
|
||||
CLIENTFLUID_YELLOW = 8,
|
||||
CLIENTFLUID_WHITE = 9,
|
||||
};
|
||||
|
||||
const uint8_t fluidMap[] = {
|
||||
CLIENTFLUID_EMPTY,
|
||||
CLIENTFLUID_BLUE,
|
||||
CLIENTFLUID_RED,
|
||||
CLIENTFLUID_BROWN_1,
|
||||
CLIENTFLUID_GREEN,
|
||||
CLIENTFLUID_YELLOW,
|
||||
CLIENTFLUID_WHITE,
|
||||
CLIENTFLUID_PURPLE,
|
||||
enum FluidColor_t : uint8_t
|
||||
{
|
||||
FLUID_COLOR_NONE = 0,
|
||||
FLUID_COLOR_BLUE = 1,
|
||||
FLUID_COLOR_PURPLE = 2,
|
||||
FLUID_COLOR_BROWN = 3,
|
||||
FLUID_COLOR_BROWN1 = 4,
|
||||
FLUID_COLOR_RED = 5,
|
||||
FLUID_COLOR_GREEN = 6,
|
||||
FLUID_COLOR_BROWN2 = 7,
|
||||
FLUID_COLOR_YELLOW = 8,
|
||||
FLUID_COLOR_WHITE = 9,
|
||||
};
|
||||
|
||||
enum SquareColor_t : uint8_t {
|
||||
@ -333,10 +155,8 @@ enum TextColor_t : uint8_t {
|
||||
TEXTCOLOR_DARKRED = 108,
|
||||
TEXTCOLOR_LIGHTGREY = 129,
|
||||
TEXTCOLOR_SKYBLUE = 143,
|
||||
TEXTCOLOR_PURPLE = 154,
|
||||
TEXTCOLOR_ELECTRICPURPLE = 155,
|
||||
TEXTCOLOR_PURPLE = 155,
|
||||
TEXTCOLOR_RED = 180,
|
||||
TEXTCOLOR_PASTELRED = 194,
|
||||
TEXTCOLOR_ORANGE = 198,
|
||||
TEXTCOLOR_YELLOW = 210,
|
||||
TEXTCOLOR_WHITE_EXP = 215,
|
||||
@ -353,13 +173,6 @@ enum Icons_t {
|
||||
ICON_HASTE = 1 << 6,
|
||||
ICON_SWORDS = 1 << 7,
|
||||
ICON_DROWNING = 1 << 8,
|
||||
ICON_FREEZING = 1 << 9,
|
||||
ICON_DAZZLED = 1 << 10,
|
||||
ICON_CURSED = 1 << 11,
|
||||
ICON_PARTY_BUFF = 1 << 12,
|
||||
ICON_REDSWORDS = 1 << 13,
|
||||
ICON_PIGEON = 1 << 14,
|
||||
ICON_BLEEDING = 1 << 15,
|
||||
};
|
||||
|
||||
enum WeaponType_t : uint8_t {
|
||||
@ -392,7 +205,6 @@ enum WeaponAction_t : uint8_t {
|
||||
};
|
||||
|
||||
enum WieldInfo_t {
|
||||
WIELDINFO_NONE = 0 << 0,
|
||||
WIELDINFO_LEVEL = 1 << 0,
|
||||
WIELDINFO_MAGLV = 1 << 1,
|
||||
WIELDINFO_VOCREQ = 1 << 2,
|
||||
@ -405,8 +217,6 @@ enum Skulls_t : uint8_t {
|
||||
SKULL_GREEN = 2,
|
||||
SKULL_WHITE = 3,
|
||||
SKULL_RED = 4,
|
||||
SKULL_BLACK = 5,
|
||||
SKULL_ORANGE = 6,
|
||||
};
|
||||
|
||||
enum PartyShields_t : uint8_t {
|
||||
@ -414,97 +224,54 @@ enum PartyShields_t : uint8_t {
|
||||
SHIELD_WHITEYELLOW = 1,
|
||||
SHIELD_WHITEBLUE = 2,
|
||||
SHIELD_BLUE = 3,
|
||||
SHIELD_YELLOW = 4,
|
||||
SHIELD_BLUE_SHAREDEXP = 5,
|
||||
SHIELD_YELLOW_SHAREDEXP = 6,
|
||||
SHIELD_BLUE_NOSHAREDEXP_BLINK = 7,
|
||||
SHIELD_YELLOW_NOSHAREDEXP_BLINK = 8,
|
||||
SHIELD_BLUE_NOSHAREDEXP = 9,
|
||||
SHIELD_YELLOW_NOSHAREDEXP = 10,
|
||||
SHIELD_GRAY = 11,
|
||||
};
|
||||
|
||||
enum GuildEmblems_t : uint8_t {
|
||||
GUILDEMBLEM_NONE = 0,
|
||||
GUILDEMBLEM_ALLY = 1,
|
||||
GUILDEMBLEM_ENEMY = 2,
|
||||
GUILDEMBLEM_NEUTRAL = 3,
|
||||
GUILDEMBLEM_MEMBER = 4,
|
||||
GUILDEMBLEM_OTHER = 5,
|
||||
SHIELD_YELLOW = 4
|
||||
};
|
||||
|
||||
enum item_t : uint16_t {
|
||||
ITEM_BROWSEFIELD = 460, // for internal use
|
||||
ITEM_FIREFIELD_PVP_FULL = 2118,
|
||||
ITEM_FIREFIELD_PVP_MEDIUM = 2119,
|
||||
ITEM_FIREFIELD_PVP_SMALL = 2120,
|
||||
ITEM_FIREFIELD_PERSISTENT_FULL = 2123,
|
||||
ITEM_FIREFIELD_PERSISTENT_MEDIUM = 2124,
|
||||
ITEM_FIREFIELD_PERSISTENT_SMALL = 2125,
|
||||
ITEM_FIREFIELD_NOPVP = 2131,
|
||||
|
||||
ITEM_FIREFIELD_PVP_FULL = 1487,
|
||||
ITEM_FIREFIELD_PVP_MEDIUM = 1488,
|
||||
ITEM_FIREFIELD_PVP_SMALL = 1489,
|
||||
ITEM_FIREFIELD_PERSISTENT_FULL = 1492,
|
||||
ITEM_FIREFIELD_PERSISTENT_MEDIUM = 1493,
|
||||
ITEM_FIREFIELD_PERSISTENT_SMALL = 1494,
|
||||
ITEM_FIREFIELD_NOPVP = 1500,
|
||||
ITEM_POISONFIELD_PVP = 2121,
|
||||
ITEM_POISONFIELD_PERSISTENT = 2127,
|
||||
ITEM_POISONFIELD_NOPVP = 2134,
|
||||
|
||||
ITEM_POISONFIELD_PVP = 1490,
|
||||
ITEM_POISONFIELD_PERSISTENT = 1496,
|
||||
ITEM_POISONFIELD_NOPVP = 1503,
|
||||
ITEM_ENERGYFIELD_PVP = 2122,
|
||||
ITEM_ENERGYFIELD_PERSISTENT = 2126,
|
||||
ITEM_ENERGYFIELD_NOPVP = 2135,
|
||||
|
||||
ITEM_ENERGYFIELD_PVP = 1491,
|
||||
ITEM_ENERGYFIELD_PERSISTENT = 1495,
|
||||
ITEM_ENERGYFIELD_NOPVP = 1504,
|
||||
ITEM_MAGICWALL = 2128,
|
||||
ITEM_MAGICWALL_PERSISTENT = 2128,
|
||||
|
||||
ITEM_MAGICWALL = 1497,
|
||||
ITEM_MAGICWALL_PERSISTENT = 1498,
|
||||
ITEM_MAGICWALL_SAFE = 11098,
|
||||
ITEM_WILDGROWTH = 2130,
|
||||
ITEM_WILDGROWTH_PERSISTENT = 2130,
|
||||
|
||||
ITEM_WILDGROWTH = 1499,
|
||||
ITEM_WILDGROWTH_PERSISTENT = 2721,
|
||||
ITEM_WILDGROWTH_SAFE = 11099,
|
||||
ITEM_GOLD_COIN = 3031,
|
||||
ITEM_PLATINUM_COIN = 3035,
|
||||
ITEM_CRYSTAL_COIN = 3043,
|
||||
|
||||
ITEM_BAG = 1987,
|
||||
ITEM_SHOPPING_BAG = 23782,
|
||||
ITEM_DEPOT = 3502,
|
||||
ITEM_LOCKER1 = 3497,
|
||||
|
||||
ITEM_GOLD_COIN = 2148,
|
||||
ITEM_PLATINUM_COIN = 2152,
|
||||
ITEM_CRYSTAL_COIN = 2160,
|
||||
ITEM_STORE_COIN = 24774, // in-game store currency
|
||||
ITEM_MALE_CORPSE = 4240,
|
||||
ITEM_FEMALE_CORPSE = 4247,
|
||||
|
||||
ITEM_DEPOT = 2594,
|
||||
ITEM_LOCKER1 = 2589,
|
||||
ITEM_INBOX = 14404,
|
||||
ITEM_MARKET = 14405,
|
||||
ITEM_STORE_INBOX = 26052,
|
||||
ITEM_DEPOT_BOX_I = 25453,
|
||||
ITEM_DEPOT_BOX_II = 25454,
|
||||
ITEM_DEPOT_BOX_III = 25455,
|
||||
ITEM_DEPOT_BOX_IV = 25456,
|
||||
ITEM_DEPOT_BOX_V = 25457,
|
||||
ITEM_DEPOT_BOX_VI = 25458,
|
||||
ITEM_DEPOT_BOX_VII = 25459,
|
||||
ITEM_DEPOT_BOX_VIII = 25460,
|
||||
ITEM_DEPOT_BOX_IX = 25461,
|
||||
ITEM_DEPOT_BOX_X = 25462,
|
||||
ITEM_DEPOT_BOX_XI = 25463,
|
||||
ITEM_DEPOT_BOX_XII = 25464,
|
||||
ITEM_DEPOT_BOX_XIII = 25465,
|
||||
ITEM_DEPOT_BOX_XIV = 25466,
|
||||
ITEM_DEPOT_BOX_XV = 25467,
|
||||
ITEM_DEPOT_BOX_XVI = 25468,
|
||||
ITEM_DEPOT_BOX_XVII = 25469,
|
||||
ITEM_FULLSPLASH = 2886,
|
||||
ITEM_SMALLSPLASH = 2889,
|
||||
|
||||
ITEM_MALE_CORPSE = 3058,
|
||||
ITEM_FEMALE_CORPSE = 3065,
|
||||
ITEM_PARCEL = 3503,
|
||||
ITEM_PARCEL_STAMPED = 3504,
|
||||
ITEM_LETTER = 3505,
|
||||
ITEM_LETTER_STAMPED = 3506,
|
||||
ITEM_LABEL = 3507,
|
||||
|
||||
ITEM_FULLSPLASH = 2016,
|
||||
ITEM_SMALLSPLASH = 2019,
|
||||
ITEM_AMULETOFLOSS = 3057,
|
||||
|
||||
ITEM_PARCEL = 2595,
|
||||
ITEM_LETTER = 2597,
|
||||
ITEM_LETTER_STAMPED = 2598,
|
||||
ITEM_LABEL = 2599,
|
||||
|
||||
ITEM_AMULETOFLOSS = 2173,
|
||||
|
||||
ITEM_DOCUMENT_RO = 1968, //read-only
|
||||
ITEM_DOCUMENT_RO = 2819, //read-only
|
||||
};
|
||||
|
||||
enum PlayerFlags : uint64_t {
|
||||
@ -546,12 +313,14 @@ enum PlayerFlags : uint64_t {
|
||||
PlayerFlag_IgnoreWeaponCheck = static_cast<uint64_t>(1) << 35,
|
||||
PlayerFlag_CannotBeMuted = static_cast<uint64_t>(1) << 36,
|
||||
PlayerFlag_IsAlwaysPremium = static_cast<uint64_t>(1) << 37,
|
||||
PlayerFlag_SpecialMoveUse = static_cast<uint64_t>(1) << 38,
|
||||
};
|
||||
|
||||
enum ReloadTypes_t : uint8_t {
|
||||
enum ReloadTypes_t : uint8_t {
|
||||
RELOAD_TYPE_ALL,
|
||||
RELOAD_TYPE_ACTIONS,
|
||||
RELOAD_TYPE_CHAT,
|
||||
RELOAD_TYPE_COMMANDS,
|
||||
RELOAD_TYPE_CONFIG,
|
||||
RELOAD_TYPE_CREATURESCRIPTS,
|
||||
RELOAD_TYPE_EVENTS,
|
||||
@ -564,7 +333,6 @@ enum ReloadTypes_t : uint8_t {
|
||||
RELOAD_TYPE_NPCS,
|
||||
RELOAD_TYPE_QUESTS,
|
||||
RELOAD_TYPE_RAIDS,
|
||||
RELOAD_TYPE_SCRIPTS,
|
||||
RELOAD_TYPE_SPELLS,
|
||||
RELOAD_TYPE_TALKACTIONS,
|
||||
RELOAD_TYPE_WEAPONS,
|
||||
@ -572,6 +340,7 @@ enum ReloadTypes_t : uint8_t {
|
||||
|
||||
static constexpr int32_t CHANNEL_GUILD = 0x00;
|
||||
static constexpr int32_t CHANNEL_PARTY = 0x01;
|
||||
static constexpr int32_t CHANNEL_RULE_REP = 0x02;
|
||||
static constexpr int32_t CHANNEL_PRIVATE = 0xFFFF;
|
||||
|
||||
//Reserved player storage key ranges;
|
||||
@ -581,11 +350,6 @@ static constexpr int32_t PSTRG_RESERVED_RANGE_SIZE = 10000000;
|
||||
//[1000 - 1500];
|
||||
static constexpr int32_t PSTRG_OUTFITS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 1000);
|
||||
static constexpr int32_t PSTRG_OUTFITS_RANGE_SIZE = 500;
|
||||
//[2001 - 2011];
|
||||
static constexpr int32_t PSTRG_MOUNTS_RANGE_START = (PSTRG_RESERVED_RANGE_START + 2001);
|
||||
static constexpr int32_t PSTRG_MOUNTS_RANGE_SIZE = 10;
|
||||
static constexpr int32_t PSTRG_MOUNTS_CURRENTMOUNT = (PSTRG_MOUNTS_RANGE_START + 10);
|
||||
|
||||
|
||||
#define IS_IN_KEYRANGE(key, range) (key >= PSTRG_##range##_START && ((key - PSTRG_##range##_START) <= PSTRG_##range##_SIZE))
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -28,41 +28,16 @@ extern Game g_game;
|
||||
Container::Container(uint16_t type) :
|
||||
Container(type, items[type].maxItems) {}
|
||||
|
||||
Container::Container(uint16_t type, uint16_t size, bool unlocked /*= true*/, bool pagination /*= false*/) :
|
||||
Container::Container(uint16_t type, uint16_t size) :
|
||||
Item(type),
|
||||
maxSize(size),
|
||||
unlocked(unlocked),
|
||||
pagination(pagination)
|
||||
maxSize(size)
|
||||
{}
|
||||
|
||||
Container::Container(Tile* tile) : Container(ITEM_BROWSEFIELD, 30, false, true)
|
||||
{
|
||||
TileItemVector* itemVector = tile->getItemList();
|
||||
if (itemVector) {
|
||||
for (Item* item : *itemVector) {
|
||||
if ((item->getContainer() || item->hasProperty(CONST_PROP_MOVEABLE)) && !item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
|
||||
itemlist.push_front(item);
|
||||
item->setParent(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setParent(tile);
|
||||
}
|
||||
|
||||
Container::~Container()
|
||||
{
|
||||
if (getID() == ITEM_BROWSEFIELD) {
|
||||
g_game.browseFields.erase(getTile());
|
||||
|
||||
for (Item* item : itemlist) {
|
||||
item->setParent(parent);
|
||||
}
|
||||
} else {
|
||||
for (Item* item : itemlist) {
|
||||
item->setParent(nullptr);
|
||||
item->decrementReferenceCounter();
|
||||
}
|
||||
for (Item* item : itemlist) {
|
||||
item->setParent(nullptr);
|
||||
item->decrementReferenceCounter();
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,7 +62,7 @@ Container* Container::getParentContainer()
|
||||
|
||||
bool Container::hasParent() const
|
||||
{
|
||||
return getID() != ITEM_BROWSEFIELD && dynamic_cast<const Player*>(getParent()) == nullptr;
|
||||
return dynamic_cast<const Container*>(getParent()) != nullptr;
|
||||
}
|
||||
|
||||
void Container::addItem(Item* item)
|
||||
@ -107,22 +82,24 @@ Attr_ReadValue Container::readAttr(AttrTypes_t attr, PropStream& propStream)
|
||||
return Item::readAttr(attr, propStream);
|
||||
}
|
||||
|
||||
bool Container::unserializeItemNode(OTB::Loader& loader, const OTB::Node& node, PropStream& propStream)
|
||||
bool Container::unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream)
|
||||
{
|
||||
bool ret = Item::unserializeItemNode(loader, node, propStream);
|
||||
bool ret = Item::unserializeItemNode(f, node, propStream);
|
||||
if (!ret) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& itemNode : node.children) {
|
||||
uint32_t type;
|
||||
NODE nodeItem = f.getChildNode(node, type);
|
||||
while (nodeItem) {
|
||||
//load container items
|
||||
if (itemNode.type != OTBM_ITEM) {
|
||||
if (type != OTBM_ITEM) {
|
||||
// unknown type
|
||||
return false;
|
||||
}
|
||||
|
||||
PropStream itemPropStream;
|
||||
if (!loader.getProps(itemNode, itemPropStream)) {
|
||||
if (!f.getProps(nodeItem, itemPropStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -131,12 +108,14 @@ bool Container::unserializeItemNode(OTB::Loader& loader, const OTB::Node& node,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!item->unserializeItemNode(loader, itemNode, itemPropStream)) {
|
||||
if (!item->unserializeItemNode(f, nodeItem, itemPropStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
addItem(item);
|
||||
updateItemWeight(item->getWeight());
|
||||
|
||||
nodeItem = f.getNextNode(nodeItem, type);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -215,48 +194,48 @@ bool Container::isHoldingItem(const Item* item) const
|
||||
|
||||
void Container::onAddContainerItem(Item* item)
|
||||
{
|
||||
SpectatorVec spectators;
|
||||
g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2);
|
||||
SpectatorVec list;
|
||||
g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2);
|
||||
|
||||
//send to client
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
spectator->getPlayer()->sendAddContainerItem(this, item);
|
||||
}
|
||||
|
||||
//event methods
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
spectator->getPlayer()->onAddContainerItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem)
|
||||
{
|
||||
SpectatorVec spectators;
|
||||
g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2);
|
||||
SpectatorVec list;
|
||||
g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2);
|
||||
|
||||
//send to client
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
spectator->getPlayer()->sendUpdateContainerItem(this, index, newItem);
|
||||
}
|
||||
|
||||
//event methods
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
spectator->getPlayer()->onUpdateContainerItem(this, oldItem, newItem);
|
||||
}
|
||||
}
|
||||
|
||||
void Container::onRemoveContainerItem(uint32_t index, Item* item)
|
||||
{
|
||||
SpectatorVec spectators;
|
||||
g_game.map.getSpectators(spectators, getPosition(), false, true, 2, 2, 2, 2);
|
||||
SpectatorVec list;
|
||||
g_game.map.getSpectators(list, getPosition(), false, true, 2, 2, 2, 2);
|
||||
|
||||
//send change to client
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
spectator->getPlayer()->sendRemoveContainerItem(this, index);
|
||||
}
|
||||
|
||||
//event methods
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
spectator->getPlayer()->onRemoveContainerItem(this, item);
|
||||
}
|
||||
}
|
||||
@ -271,10 +250,6 @@ ReturnValue Container::queryAdd(int32_t index, const Thing& thing, uint32_t coun
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
if (!unlocked) {
|
||||
return RETURNVALUE_NOTPOSSIBLE;
|
||||
}
|
||||
|
||||
const Item* item = thing.getItem();
|
||||
if (item == nullptr) {
|
||||
return RETURNVALUE_NOTPOSSIBLE;
|
||||
@ -295,10 +270,6 @@ ReturnValue Container::queryAdd(int32_t index, const Thing& thing, uint32_t coun
|
||||
return RETURNVALUE_THISISIMPOSSIBLE;
|
||||
}
|
||||
|
||||
if (dynamic_cast<const Inbox*>(cylinder)) {
|
||||
return RETURNVALUE_CONTAINERNOTENOUGHROOM;
|
||||
}
|
||||
|
||||
cylinder = cylinder->getParent();
|
||||
}
|
||||
|
||||
@ -355,7 +326,7 @@ ReturnValue Container::queryMaxCount(int32_t index, const Thing& thing, uint32_t
|
||||
}
|
||||
} else {
|
||||
const Item* destItem = getItemByIndex(index);
|
||||
if (item->equals(destItem) && destItem->getItemCount() < 100) {
|
||||
if (item->equals(destItem) && !destItem->isRune() && destItem->getItemCount() < 100) {
|
||||
uint32_t remainder = 100 - destItem->getItemCount();
|
||||
if (queryAdd(index, *item, remainder, flags) == RETURNVALUE_NOERROR) {
|
||||
n = remainder;
|
||||
@ -398,14 +369,9 @@ ReturnValue Container::queryRemove(const Thing& thing, uint32_t count, uint32_t
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item** destItem,
|
||||
uint32_t& flags)
|
||||
Cylinder* Container::queryDestination(int32_t& index, const Thing &thing, Item** destItem,
|
||||
uint32_t& flags)
|
||||
{
|
||||
if (!unlocked) {
|
||||
*destItem = nullptr;
|
||||
return this;
|
||||
}
|
||||
|
||||
if (index == 254 /*move up*/) {
|
||||
index = INDEX_WHEREEVER;
|
||||
*destItem = nullptr;
|
||||
@ -420,7 +386,8 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item**
|
||||
if (index == 255 /*add wherever*/) {
|
||||
index = INDEX_WHEREEVER;
|
||||
*destItem = nullptr;
|
||||
} else if (index >= static_cast<int32_t>(capacity())) {
|
||||
}
|
||||
else if (index >= static_cast<int32_t>(capacity())) {
|
||||
/*
|
||||
if you have a container, maximize it to show all 20 slots
|
||||
then you open a bag that is inside the container you will have a bag with 8 slots
|
||||
@ -451,19 +418,22 @@ Cylinder* Container::queryDestination(int32_t& index, const Thing& thing, Item**
|
||||
}
|
||||
}
|
||||
|
||||
bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags);
|
||||
if (autoStack && item->isStackable() && item->getParent() != this) {
|
||||
//try find a suitable item to stack with
|
||||
uint32_t n = 0;
|
||||
for (Item* listItem : itemlist) {
|
||||
if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) {
|
||||
*destItem = listItem;
|
||||
index = n;
|
||||
return this;
|
||||
if (g_config.getBoolean(ConfigManager::STACK_CUMULATIVES)) {
|
||||
bool autoStack = !hasBitSet(FLAG_IGNOREAUTOSTACK, flags);
|
||||
if (autoStack && item->isStackable() && item->getParent() != this) {
|
||||
//try find a suitable item to stack with
|
||||
uint32_t n = 0;
|
||||
for (Item* listItem : itemlist) {
|
||||
if (listItem != item && listItem->equals(item) && listItem->getItemCount() < 100) {
|
||||
*destItem = listItem;
|
||||
index = n;
|
||||
return this;
|
||||
}
|
||||
++n;
|
||||
}
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,7 +26,6 @@
|
||||
#include "item.h"
|
||||
|
||||
class Container;
|
||||
class DepotChest;
|
||||
class DepotLocker;
|
||||
|
||||
class ContainerIterator
|
||||
@ -39,7 +38,7 @@ class ContainerIterator
|
||||
void advance();
|
||||
Item* operator*();
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::list<const Container*> over;
|
||||
ItemDeque::const_iterator cur;
|
||||
|
||||
@ -50,32 +49,31 @@ class Container : public Item, public Cylinder
|
||||
{
|
||||
public:
|
||||
explicit Container(uint16_t type);
|
||||
Container(uint16_t type, uint16_t size, bool unlocked = true, bool pagination = false);
|
||||
explicit Container(Tile* tile);
|
||||
Container(uint16_t type, uint16_t size);
|
||||
~Container();
|
||||
|
||||
// non-copyable
|
||||
Container(const Container&) = delete;
|
||||
Container& operator=(const Container&) = delete;
|
||||
|
||||
Item* clone() const override final;
|
||||
Item* clone() const final;
|
||||
|
||||
Container* getContainer() override final {
|
||||
Container* getContainer() final {
|
||||
return this;
|
||||
}
|
||||
const Container* getContainer() const override final {
|
||||
const Container* getContainer() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
virtual DepotLocker* getDepotLocker() {
|
||||
virtual DepotLocker* getDepotLocker() override {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const DepotLocker* getDepotLocker() const {
|
||||
virtual const DepotLocker* getDepotLocker() const override {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
|
||||
bool unserializeItemNode(OTB::Loader& loader, const OTB::Node& node, PropStream& propStream) override;
|
||||
bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream) override;
|
||||
std::string getContentDescription() const;
|
||||
|
||||
size_t size() const {
|
||||
@ -107,60 +105,41 @@ class Container : public Item, public Cylinder
|
||||
bool isHoldingItem(const Item* item) const;
|
||||
|
||||
uint32_t getItemHoldingCount() const;
|
||||
uint32_t getWeight() const override final;
|
||||
|
||||
bool isUnlocked() const {
|
||||
return unlocked;
|
||||
}
|
||||
bool hasPagination() const {
|
||||
return pagination;
|
||||
}
|
||||
uint32_t getWeight() const final;
|
||||
|
||||
//cylinder implementations
|
||||
virtual ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t flags, Creature* actor = nullptr) const override;
|
||||
ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount,
|
||||
uint32_t flags) const override final;
|
||||
ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override final;
|
||||
uint32_t flags) const final;
|
||||
ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final;
|
||||
Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem,
|
||||
uint32_t& flags) override final;
|
||||
uint32_t& flags) final;
|
||||
|
||||
void addThing(Thing* thing) override final;
|
||||
void addThing(int32_t index, Thing* thing) override final;
|
||||
void addThing(Thing* thing) final;
|
||||
void addThing(int32_t index, Thing* thing) final;
|
||||
void addItemBack(Item* item);
|
||||
|
||||
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override final;
|
||||
void replaceThing(uint32_t index, Thing* thing) override final;
|
||||
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final;
|
||||
void replaceThing(uint32_t index, Thing* thing) final;
|
||||
|
||||
void removeThing(Thing* thing, uint32_t count) override final;
|
||||
void removeThing(Thing* thing, uint32_t count) final;
|
||||
|
||||
int32_t getThingIndex(const Thing* thing) const override final;
|
||||
size_t getFirstIndex() const override final;
|
||||
size_t getLastIndex() const override final;
|
||||
uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const override final;
|
||||
std::map<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const override final;
|
||||
Thing* getThing(size_t index) const override final;
|
||||
int32_t getThingIndex(const Thing* thing) const final;
|
||||
size_t getFirstIndex() const final;
|
||||
size_t getLastIndex() const final;
|
||||
uint32_t getItemTypeCount(uint16_t itemId, int32_t subType = -1) const final;
|
||||
std::map<uint32_t, uint32_t>& getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const final;
|
||||
Thing* getThing(size_t index) const final;
|
||||
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
|
||||
void internalAddThing(Thing* thing) override final;
|
||||
void internalAddThing(uint32_t index, Thing* thing) override final;
|
||||
void startDecaying() override final;
|
||||
|
||||
protected:
|
||||
ItemDeque itemlist;
|
||||
void internalAddThing(Thing* thing) final;
|
||||
void internalAddThing(uint32_t index, Thing* thing) final;
|
||||
void startDecaying() final;
|
||||
|
||||
private:
|
||||
std::ostringstream& getContentDescription(std::ostringstream& os) const;
|
||||
|
||||
uint32_t maxSize;
|
||||
uint32_t totalWeight = 0;
|
||||
uint32_t serializationCount = 0;
|
||||
|
||||
bool unlocked;
|
||||
bool pagination;
|
||||
|
||||
void onAddContainerItem(Item* item);
|
||||
void onUpdateContainerItem(uint32_t index, Item* oldItem, Item* newItem);
|
||||
void onRemoveContainerItem(uint32_t index, Item* item);
|
||||
@ -168,6 +147,14 @@ class Container : public Item, public Cylinder
|
||||
Container* getParentContainer();
|
||||
void updateItemWeight(int32_t diff);
|
||||
|
||||
protected:
|
||||
std::ostringstream& getContentDescription(std::ostringstream& os) const;
|
||||
|
||||
uint32_t maxSize;
|
||||
uint32_t totalWeight = 0;
|
||||
ItemDeque itemlist;
|
||||
uint32_t serializationCount = 0;
|
||||
|
||||
friend class ContainerIterator;
|
||||
friend class IOMapSerialize;
|
||||
};
|
||||
|
283
src/creature.cpp
283
src/creature.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -25,10 +25,6 @@
|
||||
#include "configmanager.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
double Creature::speedA = 857.36;
|
||||
double Creature::speedB = 261.29;
|
||||
double Creature::speedC = -4795.01;
|
||||
|
||||
extern Game g_game;
|
||||
extern ConfigManager g_config;
|
||||
extern CreatureEvents* g_creatureEvents;
|
||||
@ -42,7 +38,8 @@ Creature::~Creature()
|
||||
{
|
||||
for (Creature* summon : summons) {
|
||||
summon->setAttackedCreature(nullptr);
|
||||
summon->removeMaster();
|
||||
summon->setMaster(nullptr);
|
||||
summon->decrementReferenceCounter();
|
||||
}
|
||||
|
||||
for (Condition* condition : conditions) {
|
||||
@ -230,7 +227,7 @@ void Creature::onWalk(Direction& dir)
|
||||
if (r < DIRECTION_DIAGONAL_MASK) {
|
||||
dir = static_cast<Direction>(r);
|
||||
}
|
||||
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, "Hicks!", false);
|
||||
g_game.internalCreatureSay(this, TALKTYPE_SAY, "Hicks!", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -310,7 +307,7 @@ void Creature::updateMapCache()
|
||||
void Creature::updateTileCache(const Tile* tile, int32_t dx, int32_t dy)
|
||||
{
|
||||
if (std::abs(dx) <= maxWalkCacheWidth && std::abs(dy) <= maxWalkCacheHeight) {
|
||||
localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx] = tile && tile->queryAdd(0, *this, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) == RETURNVALUE_NOERROR;
|
||||
localMapCache[maxWalkCacheHeight + dy][maxWalkCacheWidth + dx] = tile && tile->queryAdd(0, *this, 1, FLAG_PATHFINDING) == RETURNVALUE_NOERROR;
|
||||
}
|
||||
}
|
||||
|
||||
@ -412,7 +409,7 @@ void Creature::onRemoveCreature(Creature* creature, bool)
|
||||
onCreatureDisappear(creature, true);
|
||||
if (creature == this) {
|
||||
if (master && !master->isRemoved()) {
|
||||
setMaster(nullptr);
|
||||
master->removeSummon(this);
|
||||
}
|
||||
} else if (isMapLoaded) {
|
||||
if (creature->getPosition().z == getPosition().z) {
|
||||
@ -585,6 +582,11 @@ void Creature::onCreatureMove(Creature* creature, const Tile* newTile, const Pos
|
||||
if (creature == followCreature || (creature == this && followCreature)) {
|
||||
if (hasFollowPath) {
|
||||
isUpdatingPath = true;
|
||||
// this updates following walking
|
||||
if (lastWalkUpdate == 0 || OTSYS_TIME() - lastWalkUpdate >= 250) {
|
||||
g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game, getID())));
|
||||
lastWalkUpdate = OTSYS_TIME();
|
||||
}
|
||||
}
|
||||
|
||||
if (newPos.z != oldPos.z || !canSee(followCreature->getPosition())) {
|
||||
@ -617,7 +619,8 @@ void Creature::onDeath()
|
||||
if (lastHitCreature) {
|
||||
lastHitUnjustified = lastHitCreature->onKilledCreature(this);
|
||||
lastHitCreatureMaster = lastHitCreature->getMaster();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
lastHitCreatureMaster = nullptr;
|
||||
}
|
||||
|
||||
@ -639,7 +642,6 @@ void Creature::onDeath()
|
||||
uint64_t gainExp = getGainedExperience(attacker);
|
||||
if (Player* attackerPlayer = attacker->getPlayer()) {
|
||||
attackerPlayer->removeAttacked(getPlayer());
|
||||
|
||||
Party* party = attackerPlayer->getParty();
|
||||
if (party && party->getLeader() && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
|
||||
attacker = party->getLeader();
|
||||
@ -649,7 +651,8 @@ void Creature::onDeath()
|
||||
auto tmpIt = experienceMap.find(attacker);
|
||||
if (tmpIt == experienceMap.end()) {
|
||||
experienceMap[attacker] = gainExp;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
tmpIt->second += gainExp;
|
||||
}
|
||||
}
|
||||
@ -673,7 +676,7 @@ void Creature::onDeath()
|
||||
death(lastHitCreature);
|
||||
|
||||
if (master) {
|
||||
setMaster(nullptr);
|
||||
master->removeSummon(this);
|
||||
}
|
||||
|
||||
if (droppedCorpse) {
|
||||
@ -683,53 +686,41 @@ void Creature::onDeath()
|
||||
|
||||
bool Creature::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified)
|
||||
{
|
||||
if (!lootDrop && getMonster()) {
|
||||
if (master) {
|
||||
//scripting event - onDeath
|
||||
const CreatureEventList& deathEvents = getCreatureEvents(CREATURE_EVENT_DEATH);
|
||||
for (CreatureEvent* deathEvent : deathEvents) {
|
||||
deathEvent->executeOnDeath(this, nullptr, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
|
||||
}
|
||||
}
|
||||
Item* splash;
|
||||
switch (getRace()) {
|
||||
case RACE_VENOM:
|
||||
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME);
|
||||
break;
|
||||
|
||||
g_game.addMagicEffect(getPosition(), CONST_ME_POFF);
|
||||
} else {
|
||||
Item* splash;
|
||||
switch (getRace()) {
|
||||
case RACE_VENOM:
|
||||
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_SLIME);
|
||||
break;
|
||||
case RACE_BLOOD:
|
||||
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD);
|
||||
break;
|
||||
|
||||
case RACE_BLOOD:
|
||||
splash = Item::CreateItem(ITEM_FULLSPLASH, FLUID_BLOOD);
|
||||
break;
|
||||
default:
|
||||
splash = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
splash = nullptr;
|
||||
break;
|
||||
}
|
||||
Tile* tile = getTile();
|
||||
|
||||
Tile* tile = getTile();
|
||||
if (splash) {
|
||||
g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
|
||||
g_game.startDecay(splash);
|
||||
}
|
||||
|
||||
if (splash) {
|
||||
g_game.internalAddItem(tile, splash, INDEX_WHEREEVER, FLAG_NOLIMIT);
|
||||
g_game.startDecay(splash);
|
||||
}
|
||||
Item* corpse = getCorpse(lastHitCreature, mostDamageCreature);
|
||||
if (corpse) {
|
||||
g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT);
|
||||
g_game.startDecay(corpse);
|
||||
}
|
||||
|
||||
Item* corpse = getCorpse(lastHitCreature, mostDamageCreature);
|
||||
if (corpse) {
|
||||
g_game.internalAddItem(tile, corpse, INDEX_WHEREEVER, FLAG_NOLIMIT);
|
||||
g_game.startDecay(corpse);
|
||||
}
|
||||
//scripting event - onDeath
|
||||
for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) {
|
||||
deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
|
||||
}
|
||||
|
||||
//scripting event - onDeath
|
||||
for (CreatureEvent* deathEvent : getCreatureEvents(CREATURE_EVENT_DEATH)) {
|
||||
deathEvent->executeOnDeath(this, corpse, lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
|
||||
}
|
||||
|
||||
if (corpse) {
|
||||
dropLoot(corpse->getContainer(), lastHitCreature);
|
||||
}
|
||||
if (corpse) {
|
||||
dropLoot(corpse->getContainer(), lastHitCreature);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -790,6 +781,28 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3
|
||||
damage = 0;
|
||||
blockType = BLOCK_IMMUNITY;
|
||||
} else if (checkDefense || checkArmor) {
|
||||
if (checkDefense && OTSYS_TIME() >= earliestDefendTime) {
|
||||
damage -= getDefense();
|
||||
|
||||
earliestDefendTime = lastDefendTime + 2000;
|
||||
lastDefendTime = OTSYS_TIME();
|
||||
|
||||
if (damage <= 0) {
|
||||
damage = 0;
|
||||
blockType = BLOCK_DEFENSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkArmor) {
|
||||
if (damage > 0 && combatType == COMBAT_PHYSICALDAMAGE) {
|
||||
damage -= getArmor();
|
||||
if (damage <= 0) {
|
||||
damage = 0;
|
||||
blockType = BLOCK_ARMOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool hasDefense = false;
|
||||
|
||||
if (blockCount > 0) {
|
||||
@ -797,30 +810,6 @@ BlockType_t Creature::blockHit(Creature* attacker, CombatType_t combatType, int3
|
||||
hasDefense = true;
|
||||
}
|
||||
|
||||
if (checkDefense && hasDefense && canUseDefense) {
|
||||
int32_t defense = getDefense();
|
||||
damage -= uniform_random(defense / 2, defense);
|
||||
if (damage <= 0) {
|
||||
damage = 0;
|
||||
blockType = BLOCK_DEFENSE;
|
||||
checkArmor = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkArmor) {
|
||||
int32_t armor = getArmor();
|
||||
if (armor > 3) {
|
||||
damage -= uniform_random(armor / 2, armor - (armor % 2 + 1));
|
||||
} else if (armor > 0) {
|
||||
--damage;
|
||||
}
|
||||
|
||||
if (damage <= 0) {
|
||||
damage = 0;
|
||||
blockType = BLOCK_ARMOR;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDefense && blockType != BLOCK_NONE) {
|
||||
onBlockHit();
|
||||
}
|
||||
@ -844,6 +833,16 @@ bool Creature::setAttackedCreature(Creature* creature)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isSummon() && master) {
|
||||
if (Monster* monster = master->getMonster()) {
|
||||
if (monster->mType->info.targetDistance <= 1) {
|
||||
if (!monster->hasFollowPath && !monster->followCreature) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
attackedCreature = creature;
|
||||
onAttackedCreature(attackedCreature);
|
||||
attackedCreature->onAttacked();
|
||||
@ -1027,21 +1026,9 @@ void Creature::onTickCondition(ConditionType_t type, bool& bRemove)
|
||||
case CONDITION_POISON:
|
||||
bRemove = (field->getCombatType() != COMBAT_EARTHDAMAGE);
|
||||
break;
|
||||
case CONDITION_FREEZING:
|
||||
bRemove = (field->getCombatType() != COMBAT_ICEDAMAGE);
|
||||
break;
|
||||
case CONDITION_DAZZLED:
|
||||
bRemove = (field->getCombatType() != COMBAT_HOLYDAMAGE);
|
||||
break;
|
||||
case CONDITION_CURSED:
|
||||
bRemove = (field->getCombatType() != COMBAT_DEATHDAMAGE);
|
||||
break;
|
||||
case CONDITION_DROWN:
|
||||
bRemove = (field->getCombatType() != COMBAT_DROWNDAMAGE);
|
||||
break;
|
||||
case CONDITION_BLEEDING:
|
||||
bRemove = (field->getCombatType() != COMBAT_PHYSICALDAMAGE);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1064,8 +1051,15 @@ void Creature::onAttackedCreatureDrainHealth(Creature* target, int32_t points)
|
||||
|
||||
bool Creature::onKilledCreature(Creature* target, bool)
|
||||
{
|
||||
if (latestKillEvent == target->getID()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
latestKillEvent = target->getID();
|
||||
|
||||
if (master) {
|
||||
master->onKilledCreature(target);
|
||||
return false;
|
||||
}
|
||||
|
||||
//scripting event - onKill
|
||||
@ -1085,43 +1079,28 @@ void Creature::onGainExperience(uint64_t gainExp, Creature* target)
|
||||
gainExp /= 2;
|
||||
master->onGainExperience(gainExp, target);
|
||||
|
||||
SpectatorVec spectators;
|
||||
g_game.map.getSpectators(spectators, position, false, true);
|
||||
if (spectators.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TextMessage message(MESSAGE_EXPERIENCE_OTHERS, ucfirst(getNameDescription()) + " gained " + std::to_string(gainExp) + (gainExp != 1 ? " experience points." : " experience point."));
|
||||
message.position = position;
|
||||
message.primary.color = TEXTCOLOR_WHITE_EXP;
|
||||
message.primary.value = gainExp;
|
||||
|
||||
for (Creature* spectator : spectators) {
|
||||
spectator->getPlayer()->sendTextMessage(message);
|
||||
}
|
||||
g_game.addAnimatedText(position, TEXTCOLOR_WHITE_EXP, std::to_string(gainExp));
|
||||
}
|
||||
|
||||
bool Creature::setMaster(Creature* newMaster) {
|
||||
if (!newMaster && !master) {
|
||||
return false;
|
||||
}
|
||||
void Creature::addSummon(Creature* creature)
|
||||
{
|
||||
creature->setDropLoot(false);
|
||||
creature->setLossSkill(false);
|
||||
creature->setMaster(this);
|
||||
creature->incrementReferenceCounter();
|
||||
summons.push_back(creature);
|
||||
}
|
||||
|
||||
if (newMaster) {
|
||||
incrementReferenceCounter();
|
||||
newMaster->summons.push_back(this);
|
||||
void Creature::removeSummon(Creature* creature)
|
||||
{
|
||||
auto cit = std::find(summons.begin(), summons.end(), creature);
|
||||
if (cit != summons.end()) {
|
||||
creature->setDropLoot(true);
|
||||
creature->setLossSkill(true);
|
||||
creature->setMaster(nullptr);
|
||||
creature->decrementReferenceCounter();
|
||||
summons.erase(cit);
|
||||
}
|
||||
|
||||
Creature* oldMaster = master;
|
||||
master = newMaster;
|
||||
|
||||
if (oldMaster) {
|
||||
auto summon = std::find(oldMaster->summons.begin(), oldMaster->summons.end(), this);
|
||||
if (summon != oldMaster->summons.end()) {
|
||||
oldMaster->summons.erase(summon);
|
||||
decrementReferenceCounter();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Creature::addCondition(Condition* condition, bool force/* = false*/)
|
||||
@ -1280,21 +1259,20 @@ Condition* Creature::getCondition(ConditionType_t type, ConditionId_t conditionI
|
||||
|
||||
void Creature::executeConditions(uint32_t interval)
|
||||
{
|
||||
ConditionList tempConditions{ conditions };
|
||||
for (Condition* condition : tempConditions) {
|
||||
auto it = std::find(conditions.begin(), conditions.end(), condition);
|
||||
if (it == conditions.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto it = conditions.begin(), end = conditions.end();
|
||||
while (it != end) {
|
||||
Condition* condition = *it;
|
||||
if (!condition->executeCondition(this, interval)) {
|
||||
it = std::find(conditions.begin(), conditions.end(), condition);
|
||||
if (it != conditions.end()) {
|
||||
conditions.erase(it);
|
||||
condition->endCondition(this);
|
||||
onEndCondition(condition->getType());
|
||||
delete condition;
|
||||
}
|
||||
ConditionType_t type = condition->getType();
|
||||
|
||||
it = conditions.erase(it);
|
||||
|
||||
condition->endCondition(this);
|
||||
delete condition;
|
||||
|
||||
onEndCondition(type);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1311,7 +1289,7 @@ bool Creature::hasCondition(ConditionType_t type, uint32_t subId/* = 0*/) const
|
||||
continue;
|
||||
}
|
||||
|
||||
if (condition->getEndTime() >= timeNow || condition->getTicks() == -1) {
|
||||
if (condition->getEndTime() >= timeNow) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1348,18 +1326,8 @@ int64_t Creature::getStepDuration() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t calculatedStepSpeed;
|
||||
uint32_t groundSpeed;
|
||||
|
||||
int32_t stepSpeed = getStepSpeed();
|
||||
if (stepSpeed > -Creature::speedB) {
|
||||
calculatedStepSpeed = floor((Creature::speedA * log((stepSpeed / 2) + Creature::speedB) + Creature::speedC) + 0.5);
|
||||
if (calculatedStepSpeed == 0) {
|
||||
calculatedStepSpeed = 1;
|
||||
}
|
||||
} else {
|
||||
calculatedStepSpeed = 1;
|
||||
}
|
||||
|
||||
Item* ground = tile->getGround();
|
||||
if (ground) {
|
||||
@ -1371,12 +1339,12 @@ int64_t Creature::getStepDuration() const
|
||||
groundSpeed = 150;
|
||||
}
|
||||
|
||||
double duration = std::floor(1000 * groundSpeed / calculatedStepSpeed);
|
||||
double duration = std::floor(1000 * groundSpeed) / stepSpeed;
|
||||
int64_t stepDuration = std::ceil(duration / 50) * 50;
|
||||
|
||||
const Monster* monster = getMonster();
|
||||
if (monster && monster->isTargetNearby() && !monster->isFleeing() && !monster->getMaster()) {
|
||||
stepDuration *= 2;
|
||||
stepDuration *= 3;
|
||||
}
|
||||
|
||||
return stepDuration;
|
||||
@ -1396,18 +1364,15 @@ int64_t Creature::getEventStepTicks(bool onlyDelay) const
|
||||
return ret;
|
||||
}
|
||||
|
||||
LightInfo Creature::getCreatureLight() const
|
||||
void Creature::getCreatureLight(LightInfo& light) const
|
||||
{
|
||||
return internalLight;
|
||||
}
|
||||
|
||||
void Creature::setCreatureLight(LightInfo lightInfo) {
|
||||
internalLight = std::move(lightInfo);
|
||||
light = internalLight;
|
||||
}
|
||||
|
||||
void Creature::setNormalCreatureLight()
|
||||
{
|
||||
internalLight = {};
|
||||
internalLight.level = 0;
|
||||
internalLight.color = 0;
|
||||
}
|
||||
|
||||
bool Creature::registerCreatureEvent(const std::string& name)
|
||||
@ -1475,10 +1440,6 @@ CreatureEventList Creature::getCreatureEvents(CreatureEventType_t type)
|
||||
}
|
||||
|
||||
for (CreatureEvent* creatureEvent : eventsList) {
|
||||
if (!creatureEvent->isLoaded()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (creatureEvent->getEventType() == type) {
|
||||
tmpEventList.push_back(creatureEvent);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -28,8 +28,8 @@
|
||||
#include "enums.h"
|
||||
#include "creatureevent.h"
|
||||
|
||||
using ConditionList = std::list<Condition*>;
|
||||
using CreatureEventList = std::list<CreatureEvent*>;
|
||||
typedef std::list<Condition*> ConditionList;
|
||||
typedef std::list<CreatureEvent*> CreatureEventList;
|
||||
|
||||
enum slots_t : uint8_t {
|
||||
CONST_SLOT_WHEREEVER = 0,
|
||||
@ -66,6 +66,7 @@ class Monster;
|
||||
class Npc;
|
||||
class Item;
|
||||
class Tile;
|
||||
class Combat;
|
||||
|
||||
static constexpr int32_t EVENT_CREATURECOUNT = 10;
|
||||
static constexpr int32_t EVENT_CREATURE_THINK_INTERVAL = 1000;
|
||||
@ -82,7 +83,7 @@ class FrozenPathingConditionCall
|
||||
bool isInRange(const Position& startPos, const Position& testPos,
|
||||
const FindPathParams& fpp) const;
|
||||
|
||||
private:
|
||||
protected:
|
||||
Position targetPos;
|
||||
};
|
||||
|
||||
@ -96,18 +97,16 @@ class Creature : virtual public Thing
|
||||
Creature();
|
||||
|
||||
public:
|
||||
static double speedA, speedB, speedC;
|
||||
|
||||
virtual ~Creature();
|
||||
|
||||
// non-copyable
|
||||
Creature(const Creature&) = delete;
|
||||
Creature& operator=(const Creature&) = delete;
|
||||
|
||||
Creature* getCreature() override final {
|
||||
Creature* getCreature() final {
|
||||
return this;
|
||||
}
|
||||
const Creature* getCreature() const override final {
|
||||
const Creature* getCreature() const final {
|
||||
return this;
|
||||
}
|
||||
virtual Player* getPlayer() {
|
||||
@ -132,8 +131,6 @@ class Creature : virtual public Thing
|
||||
virtual const std::string& getName() const = 0;
|
||||
virtual const std::string& getNameDescription() const = 0;
|
||||
|
||||
virtual CreatureType_t getType() const = 0;
|
||||
|
||||
virtual void setID() = 0;
|
||||
void setRemoved() {
|
||||
isInternalRemoved = true;
|
||||
@ -172,13 +169,13 @@ class Creature : virtual public Thing
|
||||
hiddenHealth = b;
|
||||
}
|
||||
|
||||
int32_t getThrowRange() const override final {
|
||||
int32_t getThrowRange() const final {
|
||||
return 1;
|
||||
}
|
||||
bool isPushable() const override {
|
||||
return getWalkDelay() <= 0;
|
||||
}
|
||||
bool isRemoved() const override final {
|
||||
bool isRemoved() const final {
|
||||
return isInternalRemoved;
|
||||
}
|
||||
virtual bool canSeeInvisibility() const {
|
||||
@ -199,11 +196,15 @@ class Creature : virtual public Thing
|
||||
return getSpeed();
|
||||
}
|
||||
int32_t getSpeed() const {
|
||||
return baseSpeed + varSpeed;
|
||||
if (baseSpeed == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (2 * (varSpeed + baseSpeed)) + 80;
|
||||
}
|
||||
void setSpeed(int32_t varSpeedDelta) {
|
||||
int32_t oldSpeed = getSpeed();
|
||||
varSpeed = varSpeedDelta;
|
||||
varSpeed += varSpeedDelta;
|
||||
|
||||
if (getSpeed() <= 0) {
|
||||
stopEventWalk();
|
||||
@ -270,15 +271,9 @@ class Creature : virtual public Thing
|
||||
virtual BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
|
||||
bool checkDefense = false, bool checkArmor = false, bool field = false);
|
||||
|
||||
bool setMaster(Creature* newMaster);
|
||||
|
||||
void removeMaster() {
|
||||
if (master) {
|
||||
master = nullptr;
|
||||
decrementReferenceCounter();
|
||||
}
|
||||
void setMaster(Creature* creature) {
|
||||
master = creature;
|
||||
}
|
||||
|
||||
bool isSummon() const {
|
||||
return master != nullptr;
|
||||
}
|
||||
@ -286,6 +281,8 @@ class Creature : virtual public Thing
|
||||
return master;
|
||||
}
|
||||
|
||||
void addSummon(Creature* creature);
|
||||
void removeSummon(Creature* creature);
|
||||
const std::list<Creature*>& getSummons() const {
|
||||
return summons;
|
||||
}
|
||||
@ -293,19 +290,9 @@ class Creature : virtual public Thing
|
||||
virtual int32_t getArmor() const {
|
||||
return 0;
|
||||
}
|
||||
virtual int32_t getDefense() const {
|
||||
virtual int32_t getDefense() {
|
||||
return 0;
|
||||
}
|
||||
virtual float getAttackFactor() const {
|
||||
return 1.0f;
|
||||
}
|
||||
virtual float getDefenseFactor() const {
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
virtual uint8_t getSpeechBubble() const {
|
||||
return SPEECHBUBBLE_NONE;
|
||||
}
|
||||
|
||||
bool addCondition(Condition* condition, bool force = false);
|
||||
bool addCombatCondition(Condition* condition);
|
||||
@ -335,12 +322,15 @@ class Creature : virtual public Thing
|
||||
|
||||
virtual void changeHealth(int32_t healthChange, bool sendHealthChange = true);
|
||||
|
||||
void gainHealth(Creature* healer, int32_t healthGain);
|
||||
void gainHealth(Creature* attacker, int32_t healthGain);
|
||||
virtual void drainHealth(Creature* attacker, int32_t damage);
|
||||
|
||||
virtual bool challengeCreature(Creature*) {
|
||||
return false;
|
||||
}
|
||||
virtual bool convinceCreature(Creature*) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void onDeath();
|
||||
virtual uint64_t getGainedExperience(Creature* attacker) const;
|
||||
@ -365,9 +355,11 @@ class Creature : virtual public Thing
|
||||
virtual void onAttackedCreatureChangeZone(ZoneType_t zone);
|
||||
virtual void onIdleStatus();
|
||||
|
||||
virtual LightInfo getCreatureLight() const;
|
||||
virtual void getCreatureLight(LightInfo& light) const;
|
||||
virtual void setNormalCreatureLight();
|
||||
void setCreatureLight(LightInfo lightInfo);
|
||||
void setCreatureLight(LightInfo light) {
|
||||
internalLight = light;
|
||||
}
|
||||
|
||||
virtual void onThink(uint32_t interval);
|
||||
void onAttacking(uint32_t interval);
|
||||
@ -390,6 +382,7 @@ class Creature : virtual public Thing
|
||||
|
||||
virtual void onCreatureSay(Creature*, SpeakClasses, const std::string&) {}
|
||||
|
||||
virtual void onCreatureConvinced(const Creature*, const Creature*) {}
|
||||
virtual void onPlacedCreature() {}
|
||||
|
||||
virtual bool getCombatValues(int32_t&, int32_t&) {
|
||||
@ -402,33 +395,30 @@ class Creature : virtual public Thing
|
||||
void setDropLoot(bool lootDrop) {
|
||||
this->lootDrop = lootDrop;
|
||||
}
|
||||
void setSkillLoss(bool skillLoss) {
|
||||
void setLossSkill(bool skillLoss) {
|
||||
this->skillLoss = skillLoss;
|
||||
}
|
||||
void setUseDefense(bool useDefense) {
|
||||
canUseDefense = useDefense;
|
||||
}
|
||||
|
||||
//creature script events
|
||||
bool registerCreatureEvent(const std::string& name);
|
||||
bool unregisterCreatureEvent(const std::string& name);
|
||||
|
||||
Cylinder* getParent() const override final {
|
||||
Cylinder* getParent() const final {
|
||||
return tile;
|
||||
}
|
||||
void setParent(Cylinder* cylinder) override final {
|
||||
void setParent(Cylinder* cylinder) final {
|
||||
tile = static_cast<Tile*>(cylinder);
|
||||
position = tile->getPosition();
|
||||
}
|
||||
|
||||
const Position& getPosition() const override final {
|
||||
inline const Position& getPosition() const final {
|
||||
return position;
|
||||
}
|
||||
|
||||
Tile* getTile() override final {
|
||||
Tile* getTile() final {
|
||||
return tile;
|
||||
}
|
||||
const Tile* getTile() const override final {
|
||||
const Tile* getTile() const final {
|
||||
return tile;
|
||||
}
|
||||
|
||||
@ -474,7 +464,7 @@ class Creature : virtual public Thing
|
||||
|
||||
Position position;
|
||||
|
||||
using CountMap = std::map<uint32_t, CountBlock_t>;
|
||||
typedef std::map<uint32_t, CountBlock_t> CountMap;
|
||||
CountMap damageMap;
|
||||
|
||||
std::list<Creature*> summons;
|
||||
@ -488,6 +478,10 @@ class Creature : virtual public Thing
|
||||
Creature* master = nullptr;
|
||||
Creature* followCreature = nullptr;
|
||||
|
||||
int64_t earliestDefendTime = 0;
|
||||
int64_t lastDefendTime = 0;
|
||||
|
||||
uint64_t lastWalkUpdate = 0;
|
||||
uint64_t lastStep = 0;
|
||||
uint32_t referenceCounter = 0;
|
||||
uint32_t id = 0;
|
||||
@ -498,7 +492,8 @@ class Creature : virtual public Thing
|
||||
uint32_t blockCount = 0;
|
||||
uint32_t blockTicks = 0;
|
||||
uint32_t lastStepCost = 1;
|
||||
uint32_t baseSpeed = 220;
|
||||
uint32_t baseSpeed = 70;
|
||||
uint32_t latestKillEvent = 0;
|
||||
int32_t varSpeed = 0;
|
||||
int32_t health = 1000;
|
||||
int32_t healthMax = 1000;
|
||||
@ -524,7 +519,6 @@ class Creature : virtual public Thing
|
||||
bool hasFollowPath = false;
|
||||
bool forceUpdateFollowPath = false;
|
||||
bool hiddenHealth = false;
|
||||
bool canUseDefense = true;
|
||||
|
||||
//creature script events
|
||||
bool hasEventRegistered(CreatureEventType_t event) const {
|
||||
@ -556,6 +550,7 @@ class Creature : virtual public Thing
|
||||
friend class Game;
|
||||
friend class Map;
|
||||
friend class LuaScriptInterface;
|
||||
friend class Combat;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -29,15 +29,22 @@ CreatureEvents::CreatureEvents() :
|
||||
scriptInterface.initState();
|
||||
}
|
||||
|
||||
void CreatureEvents::clear(bool fromLua)
|
||||
CreatureEvents::~CreatureEvents()
|
||||
{
|
||||
for (auto it = creatureEvents.begin(); it != creatureEvents.end(); ++it) {
|
||||
if (fromLua == it->second.fromLua) {
|
||||
it->second.clearEvent();
|
||||
}
|
||||
for (const auto& it : creatureEvents) {
|
||||
delete it.second;
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureEvents::clear()
|
||||
{
|
||||
//clear creature events
|
||||
for (const auto& it : creatureEvents) {
|
||||
it.second->clearEvent();
|
||||
}
|
||||
|
||||
reInitState(fromLua);
|
||||
//clear lua state
|
||||
scriptInterface.reInitState();
|
||||
}
|
||||
|
||||
LuaScriptInterface& CreatureEvents::getScriptInterface()
|
||||
@ -50,17 +57,17 @@ std::string CreatureEvents::getScriptBaseName() const
|
||||
return "creaturescripts";
|
||||
}
|
||||
|
||||
Event_ptr CreatureEvents::getEvent(const std::string& nodeName)
|
||||
Event* CreatureEvents::getEvent(const std::string& nodeName)
|
||||
{
|
||||
if (strcasecmp(nodeName.c_str(), "event") != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return Event_ptr(new CreatureEvent(&scriptInterface));
|
||||
return new CreatureEvent(&scriptInterface);
|
||||
}
|
||||
|
||||
bool CreatureEvents::registerEvent(Event_ptr event, const pugi::xml_node&)
|
||||
bool CreatureEvents::registerEvent(Event* event, const pugi::xml_node&)
|
||||
{
|
||||
CreatureEvent_ptr creatureEvent{static_cast<CreatureEvent*>(event.release())}; //event is guaranteed to be a CreatureEvent
|
||||
CreatureEvent* creatureEvent = static_cast<CreatureEvent*>(event); //event is guaranteed to be a CreatureEvent
|
||||
if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) {
|
||||
std::cout << "Error: [CreatureEvents::registerEvent] Trying to register event without type!" << std::endl;
|
||||
return false;
|
||||
@ -71,37 +78,13 @@ bool CreatureEvents::registerEvent(Event_ptr event, const pugi::xml_node&)
|
||||
//if there was an event with the same that is not loaded
|
||||
//(happens when realoading), it is reused
|
||||
if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) {
|
||||
oldEvent->copyEvent(creatureEvent.get());
|
||||
oldEvent->copyEvent(creatureEvent);
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
//if not, register it normally
|
||||
creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool CreatureEvents::registerLuaEvent(CreatureEvent* event)
|
||||
{
|
||||
CreatureEvent_ptr creatureEvent{ event };
|
||||
if (creatureEvent->getEventType() == CREATURE_EVENT_NONE) {
|
||||
std::cout << "Error: [CreatureEvents::registerLuaEvent] Trying to register event without type!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
CreatureEvent* oldEvent = getEventByName(creatureEvent->getName(), false);
|
||||
if (oldEvent) {
|
||||
//if there was an event with the same that is not loaded
|
||||
//(happens when realoading), it is reused
|
||||
if (!oldEvent->isLoaded() && oldEvent->getEventType() == creatureEvent->getEventType()) {
|
||||
oldEvent->copyEvent(creatureEvent.get());
|
||||
}
|
||||
|
||||
return false;
|
||||
} else {
|
||||
//if not, register it normally
|
||||
creatureEvents.emplace(creatureEvent->getName(), std::move(*creatureEvent));
|
||||
creatureEvents[creatureEvent->getName()] = creatureEvent;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -110,8 +93,8 @@ CreatureEvent* CreatureEvents::getEventByName(const std::string& name, bool forc
|
||||
{
|
||||
auto it = creatureEvents.find(name);
|
||||
if (it != creatureEvents.end()) {
|
||||
if (!forceLoaded || it->second.isLoaded()) {
|
||||
return &it->second;
|
||||
if (!forceLoaded || it->second->isLoaded()) {
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@ -121,8 +104,8 @@ bool CreatureEvents::playerLogin(Player* player) const
|
||||
{
|
||||
//fire global event if is registered
|
||||
for (const auto& it : creatureEvents) {
|
||||
if (it.second.getEventType() == CREATURE_EVENT_LOGIN) {
|
||||
if (!it.second.executeOnLogin(player)) {
|
||||
if (it.second->getEventType() == CREATURE_EVENT_LOGIN) {
|
||||
if (!it.second->executeOnLogin(player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -134,8 +117,8 @@ bool CreatureEvents::playerLogout(Player* player) const
|
||||
{
|
||||
//fire global event if is registered
|
||||
for (const auto& it : creatureEvents) {
|
||||
if (it.second.getEventType() == CREATURE_EVENT_LOGOUT) {
|
||||
if (!it.second.executeOnLogout(player)) {
|
||||
if (it.second->getEventType() == CREATURE_EVENT_LOGOUT) {
|
||||
if (!it.second->executeOnLogout(player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -146,9 +129,9 @@ bool CreatureEvents::playerLogout(Player* player) const
|
||||
bool CreatureEvents::playerAdvance(Player* player, skills_t skill, uint32_t oldLevel,
|
||||
uint32_t newLevel)
|
||||
{
|
||||
for (auto& it : creatureEvents) {
|
||||
if (it.second.getEventType() == CREATURE_EVENT_ADVANCE) {
|
||||
if (!it.second.executeAdvance(player, skill, oldLevel, newLevel)) {
|
||||
for (const auto& it : creatureEvents) {
|
||||
if (it.second->getEventType() == CREATURE_EVENT_ADVANCE) {
|
||||
if (!it.second->executeAdvance(player, skill, oldLevel, newLevel)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -194,10 +177,6 @@ bool CreatureEvent::configureEvent(const pugi::xml_node& node)
|
||||
type = CREATURE_EVENT_KILL;
|
||||
} else if (tmpStr == "advance") {
|
||||
type = CREATURE_EVENT_ADVANCE;
|
||||
} else if (tmpStr == "modalwindow") {
|
||||
type = CREATURE_EVENT_MODALWINDOW;
|
||||
} else if (tmpStr == "textedit") {
|
||||
type = CREATURE_EVENT_TEXTEDIT;
|
||||
} else if (tmpStr == "healthchange") {
|
||||
type = CREATURE_EVENT_HEALTHCHANGE;
|
||||
} else if (tmpStr == "manachange") {
|
||||
@ -238,12 +217,6 @@ std::string CreatureEvent::getScriptEventName() const
|
||||
case CREATURE_EVENT_ADVANCE:
|
||||
return "onAdvance";
|
||||
|
||||
case CREATURE_EVENT_MODALWINDOW:
|
||||
return "onModalWindow";
|
||||
|
||||
case CREATURE_EVENT_TEXTEDIT:
|
||||
return "onTextEdit";
|
||||
|
||||
case CREATURE_EVENT_HEALTHCHANGE:
|
||||
return "onHealthChange";
|
||||
|
||||
@ -275,7 +248,7 @@ void CreatureEvent::clearEvent()
|
||||
loaded = false;
|
||||
}
|
||||
|
||||
bool CreatureEvent::executeOnLogin(Player* player) const
|
||||
bool CreatureEvent::executeOnLogin(Player* player)
|
||||
{
|
||||
//onLogin(player)
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
@ -294,7 +267,7 @@ bool CreatureEvent::executeOnLogin(Player* player) const
|
||||
return scriptInterface->callFunction(1);
|
||||
}
|
||||
|
||||
bool CreatureEvent::executeOnLogout(Player* player) const
|
||||
bool CreatureEvent::executeOnLogout(Player* player)
|
||||
{
|
||||
//onLogout(player)
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
@ -446,56 +419,9 @@ void CreatureEvent::executeOnKill(Creature* creature, Creature* target)
|
||||
scriptInterface->callVoidFunction(2);
|
||||
}
|
||||
|
||||
void CreatureEvent::executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId)
|
||||
{
|
||||
//onModalWindow(player, modalWindowId, buttonId, choiceId)
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
std::cout << "[Error - CreatureEvent::executeModalWindow] Call stack overflow" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
||||
env->setScriptId(scriptId, scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface->getLuaState();
|
||||
scriptInterface->pushFunction(scriptId);
|
||||
|
||||
LuaScriptInterface::pushUserdata(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
|
||||
lua_pushnumber(L, modalWindowId);
|
||||
lua_pushnumber(L, buttonId);
|
||||
lua_pushnumber(L, choiceId);
|
||||
|
||||
scriptInterface->callVoidFunction(4);
|
||||
}
|
||||
|
||||
bool CreatureEvent::executeTextEdit(Player* player, Item* item, const std::string& text)
|
||||
{
|
||||
//onTextEdit(player, item, text)
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
std::cout << "[Error - CreatureEvent::executeTextEdit] Call stack overflow" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface->getScriptEnv();
|
||||
env->setScriptId(scriptId, scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface->getLuaState();
|
||||
scriptInterface->pushFunction(scriptId);
|
||||
|
||||
LuaScriptInterface::pushUserdata(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
|
||||
LuaScriptInterface::pushThing(L, item);
|
||||
LuaScriptInterface::pushString(L, text);
|
||||
|
||||
return scriptInterface->callFunction(3);
|
||||
}
|
||||
|
||||
void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage)
|
||||
{
|
||||
//onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
|
||||
//onHealthChange(creature, attacker, value, type, min, max, origin)
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
std::cout << "[Error - CreatureEvent::executeHealthChange] Call stack overflow" << std::endl;
|
||||
return;
|
||||
@ -512,7 +438,8 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker,
|
||||
if (attacker) {
|
||||
LuaScriptInterface::pushUserdata(L, attacker);
|
||||
LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
@ -520,16 +447,16 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker,
|
||||
|
||||
if (scriptInterface->protectedCall(L, 7, 4) != 0) {
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
damage.primary.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
|
||||
damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
|
||||
damage.secondary.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
|
||||
damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
|
||||
}
|
||||
else {
|
||||
damage.value = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -4));
|
||||
damage.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
|
||||
damage.min = std::abs(LuaScriptInterface::getNumber<int32_t>(L, -2));
|
||||
damage.max = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
|
||||
|
||||
lua_pop(L, 4);
|
||||
if (damage.primary.type != COMBAT_HEALING) {
|
||||
damage.primary.value = -damage.primary.value;
|
||||
damage.secondary.value = -damage.secondary.value;
|
||||
if (damage.type != COMBAT_HEALING) {
|
||||
damage.value = -damage.value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -537,7 +464,7 @@ void CreatureEvent::executeHealthChange(Creature* creature, Creature* attacker,
|
||||
}
|
||||
|
||||
void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage) {
|
||||
//onManaChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
|
||||
//onManaChange(creature, attacker, value, type, min, max, origin)
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
std::cout << "[Error - CreatureEvent::executeManaChange] Call stack overflow" << std::endl;
|
||||
return;
|
||||
@ -554,7 +481,8 @@ void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, Co
|
||||
if (attacker) {
|
||||
LuaScriptInterface::pushUserdata(L, attacker);
|
||||
LuaScriptInterface::setCreatureMetatable(L, -1, attacker);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
@ -562,12 +490,9 @@ void CreatureEvent::executeManaChange(Creature* creature, Creature* attacker, Co
|
||||
|
||||
if (scriptInterface->protectedCall(L, 7, 4) != 0) {
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
damage.primary.value = LuaScriptInterface::getNumber<int32_t>(L, -4);
|
||||
damage.primary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -3);
|
||||
damage.secondary.value = LuaScriptInterface::getNumber<int32_t>(L, -2);
|
||||
damage.secondary.type = LuaScriptInterface::getNumber<CombatType_t>(L, -1);
|
||||
lua_pop(L, 4);
|
||||
}
|
||||
else {
|
||||
damage = LuaScriptInterface::getCombatDamage(L);
|
||||
}
|
||||
|
||||
scriptInterface->resetScriptEnv();
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -24,9 +24,6 @@
|
||||
#include "baseevents.h"
|
||||
#include "enums.h"
|
||||
|
||||
class CreatureEvent;
|
||||
using CreatureEvent_ptr = std::unique_ptr<CreatureEvent>;
|
||||
|
||||
enum CreatureEventType_t {
|
||||
CREATURE_EVENT_NONE,
|
||||
CREATURE_EVENT_LOGIN,
|
||||
@ -36,69 +33,18 @@ enum CreatureEventType_t {
|
||||
CREATURE_EVENT_DEATH,
|
||||
CREATURE_EVENT_KILL,
|
||||
CREATURE_EVENT_ADVANCE,
|
||||
CREATURE_EVENT_MODALWINDOW,
|
||||
CREATURE_EVENT_TEXTEDIT,
|
||||
CREATURE_EVENT_HEALTHCHANGE,
|
||||
CREATURE_EVENT_MANACHANGE,
|
||||
CREATURE_EVENT_EXTENDED_OPCODE, // otclient additional network opcodes
|
||||
};
|
||||
|
||||
class CreatureEvent final : public Event
|
||||
{
|
||||
public:
|
||||
explicit CreatureEvent(LuaScriptInterface* interface);
|
||||
|
||||
bool configureEvent(const pugi::xml_node& node) override;
|
||||
|
||||
CreatureEventType_t getEventType() const {
|
||||
return type;
|
||||
}
|
||||
void setEventType(CreatureEventType_t eventType) {
|
||||
type = eventType;
|
||||
}
|
||||
const std::string& getName() const {
|
||||
return eventName;
|
||||
}
|
||||
void setName(const std::string& name) {
|
||||
eventName = name;
|
||||
}
|
||||
bool isLoaded() const {
|
||||
return loaded;
|
||||
}
|
||||
void setLoaded(bool b) {
|
||||
loaded = b;
|
||||
}
|
||||
|
||||
void clearEvent();
|
||||
void copyEvent(CreatureEvent* creatureEvent);
|
||||
|
||||
//scripting
|
||||
bool executeOnLogin(Player* player) const;
|
||||
bool executeOnLogout(Player* player) const;
|
||||
bool executeOnThink(Creature* creature, uint32_t interval);
|
||||
bool executeOnPrepareDeath(Creature* creature, Creature* killer);
|
||||
bool executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified);
|
||||
void executeOnKill(Creature* creature, Creature* target);
|
||||
bool executeAdvance(Player* player, skills_t, uint32_t, uint32_t);
|
||||
void executeModalWindow(Player* player, uint32_t modalWindowId, uint8_t buttonId, uint8_t choiceId);
|
||||
bool executeTextEdit(Player* player, Item* item, const std::string& text);
|
||||
void executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage);
|
||||
void executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage);
|
||||
void executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer);
|
||||
//
|
||||
|
||||
private:
|
||||
std::string getScriptEventName() const override;
|
||||
|
||||
std::string eventName;
|
||||
CreatureEventType_t type;
|
||||
bool loaded;
|
||||
};
|
||||
class CreatureEvent;
|
||||
|
||||
class CreatureEvents final : public BaseEvents
|
||||
{
|
||||
public:
|
||||
CreatureEvents();
|
||||
~CreatureEvents();
|
||||
|
||||
// non-copyable
|
||||
CreatureEvents(const CreatureEvents&) = delete;
|
||||
@ -111,20 +57,59 @@ class CreatureEvents final : public BaseEvents
|
||||
|
||||
CreatureEvent* getEventByName(const std::string& name, bool forceLoaded = true);
|
||||
|
||||
bool registerLuaEvent(CreatureEvent* event);
|
||||
void clear(bool fromLua) override final;
|
||||
|
||||
private:
|
||||
LuaScriptInterface& getScriptInterface() override;
|
||||
std::string getScriptBaseName() const override;
|
||||
Event_ptr getEvent(const std::string& nodeName) override;
|
||||
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
|
||||
protected:
|
||||
LuaScriptInterface& getScriptInterface() final;
|
||||
std::string getScriptBaseName() const final;
|
||||
Event* getEvent(const std::string& nodeName) final;
|
||||
bool registerEvent(Event* event, const pugi::xml_node& node) final;
|
||||
void clear() final;
|
||||
|
||||
//creature events
|
||||
using CreatureEventMap = std::map<std::string, CreatureEvent>;
|
||||
CreatureEventMap creatureEvents;
|
||||
typedef std::map<std::string, CreatureEvent*> CreatureEventList;
|
||||
CreatureEventList creatureEvents;
|
||||
|
||||
LuaScriptInterface scriptInterface;
|
||||
};
|
||||
|
||||
class CreatureEvent final : public Event
|
||||
{
|
||||
public:
|
||||
explicit CreatureEvent(LuaScriptInterface* interface);
|
||||
|
||||
bool configureEvent(const pugi::xml_node& node) final;
|
||||
|
||||
CreatureEventType_t getEventType() const {
|
||||
return type;
|
||||
}
|
||||
const std::string& getName() const {
|
||||
return eventName;
|
||||
}
|
||||
bool isLoaded() const {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
void clearEvent();
|
||||
void copyEvent(CreatureEvent* creatureEvent);
|
||||
|
||||
//scripting
|
||||
bool executeOnLogin(Player* player);
|
||||
bool executeOnLogout(Player* player);
|
||||
bool executeOnThink(Creature* creature, uint32_t interval);
|
||||
bool executeOnPrepareDeath(Creature* creature, Creature* killer);
|
||||
bool executeOnDeath(Creature* creature, Item* corpse, Creature* killer, Creature* mostDamageKiller, bool lastHitUnjustified, bool mostDamageUnjustified);
|
||||
void executeOnKill(Creature* creature, Creature* target);
|
||||
bool executeAdvance(Player* player, skills_t, uint32_t, uint32_t);
|
||||
void executeHealthChange(Creature* creature, Creature* attacker, CombatDamage& damage);
|
||||
void executeManaChange(Creature* creature, Creature* attacker, CombatDamage& damage);
|
||||
void executeExtendedOpcode(Player* player, uint8_t opcode, const std::string& buffer);
|
||||
//
|
||||
|
||||
protected:
|
||||
std::string getScriptEventName() const final;
|
||||
|
||||
std::string eventName;
|
||||
CreatureEventType_t type;
|
||||
bool loaded;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -37,6 +37,7 @@ enum cylinderflags_t {
|
||||
FLAG_IGNOREFIELDDAMAGE = 1 << 5, //Bypass field damage checks
|
||||
FLAG_IGNORENOTMOVEABLE = 1 << 6, //Bypass check for mobility
|
||||
FLAG_IGNOREAUTOSTACK = 1 << 7, //queryDestination will not try to stack items together
|
||||
FLAG_PLACECHECK = 1 << 8, //Special check for placing the monster
|
||||
};
|
||||
|
||||
enum cylinderlink_t {
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -255,7 +255,7 @@ bool DBInsert::addRow(const std::string& row)
|
||||
// adds new row to buffer
|
||||
const size_t rowLength = row.length();
|
||||
length += rowLength;
|
||||
if (length > Database::getInstance().getMaxPacketSize() && !execute()) {
|
||||
if (length > Database::getInstance()->getMaxPacketSize() && !execute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -288,7 +288,7 @@ bool DBInsert::execute()
|
||||
}
|
||||
|
||||
// executes buffer
|
||||
bool res = Database::getInstance().executeQuery(query + values);
|
||||
bool res = Database::getInstance()->executeQuery(query + values);
|
||||
values.clear();
|
||||
length = query.length();
|
||||
return res;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -25,7 +25,7 @@
|
||||
#include <mysql/mysql.h>
|
||||
|
||||
class DBResult;
|
||||
using DBResult_ptr = std::shared_ptr<DBResult>;
|
||||
typedef std::shared_ptr<DBResult> DBResult_ptr;
|
||||
|
||||
class Database
|
||||
{
|
||||
@ -42,10 +42,10 @@ class Database
|
||||
*
|
||||
* @return database connection handler singleton
|
||||
*/
|
||||
static Database& getInstance()
|
||||
static Database* getInstance()
|
||||
{
|
||||
static Database instance;
|
||||
return instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -117,7 +117,7 @@ class Database
|
||||
return maxPacketSize;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
/**
|
||||
* Transaction related methods.
|
||||
*
|
||||
@ -129,6 +129,7 @@ class Database
|
||||
bool rollback();
|
||||
bool commit();
|
||||
|
||||
private:
|
||||
MYSQL* handle = nullptr;
|
||||
std::recursive_mutex databaseLock;
|
||||
uint64_t maxPacketSize = 1048576;
|
||||
@ -194,7 +195,7 @@ class DBInsert
|
||||
bool addRow(std::ostringstream& row);
|
||||
bool execute();
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::string query;
|
||||
std::string values;
|
||||
size_t length;
|
||||
@ -207,7 +208,7 @@ class DBTransaction
|
||||
|
||||
~DBTransaction() {
|
||||
if (state == STATE_START) {
|
||||
Database::getInstance().rollback();
|
||||
Database::getInstance()->rollback();
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,7 +218,7 @@ class DBTransaction
|
||||
|
||||
bool begin() {
|
||||
state = STATE_START;
|
||||
return Database::getInstance().beginTransaction();
|
||||
return Database::getInstance()->beginTransaction();
|
||||
}
|
||||
|
||||
bool commit() {
|
||||
@ -225,15 +226,15 @@ class DBTransaction
|
||||
return false;
|
||||
}
|
||||
|
||||
state = STATE_COMMIT;
|
||||
return Database::getInstance().commit();
|
||||
state = STEATE_COMMIT;
|
||||
return Database::getInstance()->commit();
|
||||
}
|
||||
|
||||
private:
|
||||
enum TransactionStates_t {
|
||||
STATE_NO_START,
|
||||
STATE_START,
|
||||
STATE_COMMIT,
|
||||
STEATE_COMMIT,
|
||||
};
|
||||
|
||||
TransactionStates_t state = STATE_NO_START;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -27,11 +27,11 @@ extern ConfigManager g_config;
|
||||
|
||||
bool DatabaseManager::optimizeTables()
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
|
||||
query << "SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `DATA_FREE` > 0";
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
query << "SELECT `TABLE_NAME` FROM `information_schema`.`TABLES` WHERE `TABLE_SCHEMA` = " << db->escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `DATA_FREE` > 0";
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -43,7 +43,7 @@ bool DatabaseManager::optimizeTables()
|
||||
query.str(std::string());
|
||||
query << "OPTIMIZE TABLE `" << tableName << '`';
|
||||
|
||||
if (db.executeQuery(query.str())) {
|
||||
if (db->executeQuery(query.str())) {
|
||||
std::cout << " [success]" << std::endl;
|
||||
} else {
|
||||
std::cout << " [failed]" << std::endl;
|
||||
@ -54,27 +54,27 @@ bool DatabaseManager::optimizeTables()
|
||||
|
||||
bool DatabaseManager::tableExists(const std::string& tableName)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `TABLE_NAME` = " << db.escapeString(tableName) << " LIMIT 1";
|
||||
return db.storeQuery(query.str()).get() != nullptr;
|
||||
query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db->escapeString(g_config.getString(ConfigManager::MYSQL_DB)) << " AND `TABLE_NAME` = " << db->escapeString(tableName) << " LIMIT 1";
|
||||
return db->storeQuery(query.str()).get() != nullptr;
|
||||
}
|
||||
|
||||
bool DatabaseManager::isDatabaseSetup()
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db.escapeString(g_config.getString(ConfigManager::MYSQL_DB));
|
||||
return db.storeQuery(query.str()).get() != nullptr;
|
||||
query << "SELECT `TABLE_NAME` FROM `information_schema`.`tables` WHERE `TABLE_SCHEMA` = " << db->escapeString(g_config.getString(ConfigManager::MYSQL_DB));
|
||||
return db->storeQuery(query.str()).get() != nullptr;
|
||||
}
|
||||
|
||||
int32_t DatabaseManager::getDatabaseVersion()
|
||||
{
|
||||
if (!tableExists("server_config")) {
|
||||
Database& db = Database::getInstance();
|
||||
db.executeQuery("CREATE TABLE `server_config` (`config` VARCHAR(50) NOT NULL, `value` VARCHAR(256) NOT NULL DEFAULT '', UNIQUE(`config`)) ENGINE = InnoDB");
|
||||
db.executeQuery("INSERT INTO `server_config` VALUES ('db_version', 0)");
|
||||
Database* db = Database::getInstance();
|
||||
db->executeQuery("CREATE TABLE `server_config` (`config` VARCHAR(50) NOT NULL, `value` VARCHAR(256) NOT NULL DEFAULT '', UNIQUE(`config`)) ENGINE = InnoDB");
|
||||
db->executeQuery("INSERT INTO `server_config` VALUES ('db_version', 0)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -85,68 +85,13 @@ int32_t DatabaseManager::getDatabaseVersion()
|
||||
return -1;
|
||||
}
|
||||
|
||||
void DatabaseManager::updateDatabase()
|
||||
{
|
||||
lua_State* L = luaL_newstate();
|
||||
if (!L) {
|
||||
return;
|
||||
}
|
||||
|
||||
luaL_openlibs(L);
|
||||
|
||||
#ifndef LUAJIT_VERSION
|
||||
//bit operations for Lua, based on bitlib project release 24
|
||||
//bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshift, bit.rshift
|
||||
luaL_register(L, "bit", LuaScriptInterface::luaBitReg);
|
||||
#endif
|
||||
|
||||
//db table
|
||||
luaL_register(L, "db", LuaScriptInterface::luaDatabaseTable);
|
||||
|
||||
//result table
|
||||
luaL_register(L, "result", LuaScriptInterface::luaResultTable);
|
||||
|
||||
int32_t version = getDatabaseVersion();
|
||||
do {
|
||||
std::ostringstream ss;
|
||||
ss << "data/migrations/" << version << ".lua";
|
||||
if (luaL_dofile(L, ss.str().c_str()) != 0) {
|
||||
std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!LuaScriptInterface::reserveScriptEnv()) {
|
||||
break;
|
||||
}
|
||||
|
||||
lua_getglobal(L, "onUpdateDatabase");
|
||||
if (lua_pcall(L, 0, 1, 0) != 0) {
|
||||
LuaScriptInterface::resetScriptEnv();
|
||||
std::cout << "[Error - DatabaseManager::updateDatabase - Version: " << version << "] " << lua_tostring(L, -1) << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!LuaScriptInterface::getBoolean(L, -1, false)) {
|
||||
LuaScriptInterface::resetScriptEnv();
|
||||
break;
|
||||
}
|
||||
|
||||
version++;
|
||||
std::cout << "> Database has been updated to version " << version << '.' << std::endl;
|
||||
registerDatabaseConfig("db_version", version);
|
||||
|
||||
LuaScriptInterface::resetScriptEnv();
|
||||
} while (true);
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
bool DatabaseManager::getDatabaseConfig(const std::string& config, int32_t& value)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
query << "SELECT `value` FROM `server_config` WHERE `config` = " << db.escapeString(config);
|
||||
query << "SELECT `value` FROM `server_config` WHERE `config` = " << db->escapeString(config);
|
||||
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -157,16 +102,16 @@ bool DatabaseManager::getDatabaseConfig(const std::string& config, int32_t& valu
|
||||
|
||||
void DatabaseManager::registerDatabaseConfig(const std::string& config, int32_t value)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
|
||||
int32_t tmp;
|
||||
|
||||
if (!getDatabaseConfig(config, tmp)) {
|
||||
query << "INSERT INTO `server_config` VALUES (" << db.escapeString(config) << ", '" << value << "')";
|
||||
query << "INSERT INTO `server_config` VALUES (" << db->escapeString(config) << ", '" << value << "')";
|
||||
} else {
|
||||
query << "UPDATE `server_config` SET `value` = '" << value << "' WHERE `config` = " << db.escapeString(config);
|
||||
query << "UPDATE `server_config` SET `value` = '" << value << "' WHERE `config` = " << db->escapeString(config);
|
||||
}
|
||||
|
||||
db.executeQuery(query.str());
|
||||
db->executeQuery(query.str());
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -24,13 +24,12 @@
|
||||
class DatabaseManager
|
||||
{
|
||||
public:
|
||||
static bool tableExists(const std::string& tableName);
|
||||
static bool tableExists(const std::string& table);
|
||||
|
||||
static int32_t getDatabaseVersion();
|
||||
static bool isDatabaseSetup();
|
||||
|
||||
static bool optimizeTables();
|
||||
static void updateDatabase();
|
||||
|
||||
static bool getDatabaseConfig(const std::string& config, int32_t& value);
|
||||
static void registerDatabaseConfig(const std::string& config, int32_t value);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -51,13 +51,13 @@ void DatabaseTasks::threadMain()
|
||||
}
|
||||
}
|
||||
|
||||
void DatabaseTasks::addTask(std::string query, std::function<void(DBResult_ptr, bool)> callback/* = nullptr*/, bool store/* = false*/)
|
||||
void DatabaseTasks::addTask(const std::string& query, const std::function<void(DBResult_ptr, bool)>& callback/* = nullptr*/, bool store/* = false*/)
|
||||
{
|
||||
bool signal = false;
|
||||
taskLock.lock();
|
||||
if (getState() == THREAD_STATE_RUNNING) {
|
||||
signal = tasks.empty();
|
||||
tasks.emplace_back(std::move(query), std::move(callback), store);
|
||||
tasks.emplace_back(query, callback, store);
|
||||
}
|
||||
taskLock.unlock();
|
||||
|
||||
@ -85,13 +85,9 @@ void DatabaseTasks::runTask(const DatabaseTask& task)
|
||||
|
||||
void DatabaseTasks::flush()
|
||||
{
|
||||
std::unique_lock<std::mutex> guard{ taskLock };
|
||||
while (!tasks.empty()) {
|
||||
auto task = std::move(tasks.front());
|
||||
runTask(tasks.front());
|
||||
tasks.pop_front();
|
||||
guard.unlock();
|
||||
runTask(task);
|
||||
guard.lock();
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +95,7 @@ void DatabaseTasks::shutdown()
|
||||
{
|
||||
taskLock.lock();
|
||||
setState(THREAD_STATE_TERMINATED);
|
||||
taskLock.unlock();
|
||||
flush();
|
||||
taskLock.unlock();
|
||||
taskSignal.notify_one();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,7 +26,7 @@
|
||||
#include "enums.h"
|
||||
|
||||
struct DatabaseTask {
|
||||
DatabaseTask(std::string&& query, std::function<void(DBResult_ptr, bool)>&& callback, bool store) :
|
||||
DatabaseTask(std::string query, std::function<void(DBResult_ptr, bool)> callback, bool store) :
|
||||
query(std::move(query)), callback(std::move(callback)), store(store) {}
|
||||
|
||||
std::string query;
|
||||
@ -42,7 +42,7 @@ class DatabaseTasks : public ThreadHolder<DatabaseTasks>
|
||||
void flush();
|
||||
void shutdown();
|
||||
|
||||
void addTask(std::string query, std::function<void(DBResult_ptr, bool)> callback = nullptr, bool store = false);
|
||||
void addTask(const std::string& query, const std::function<void(DBResult_ptr, bool)>& callback = nullptr, bool store = false);
|
||||
|
||||
void threadMain();
|
||||
private:
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,13 +20,13 @@
|
||||
#ifndef FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
|
||||
#define FS_DEFINITIONS_H_877452FEC245450C9F96B8FD268D8963
|
||||
|
||||
static constexpr auto STATUS_SERVER_NAME = "The Forgotten Server";
|
||||
static constexpr auto STATUS_SERVER_VERSION = "1.3";
|
||||
static constexpr auto STATUS_SERVER_DEVELOPERS = "Mark Samman";
|
||||
static constexpr auto STATUS_SERVER_NAME = "Sabrehaven";
|
||||
static constexpr auto STATUS_SERVER_VERSION = "1.0";
|
||||
static constexpr auto STATUS_SERVER_DEVELOPERS = "OTLand community & Sabrehaven Developers Team";
|
||||
|
||||
static constexpr auto CLIENT_VERSION_MIN = 1097;
|
||||
static constexpr auto CLIENT_VERSION_MAX = 1098;
|
||||
static constexpr auto CLIENT_VERSION_STR = "10.98";
|
||||
static constexpr auto CLIENT_VERSION_MIN = 781;
|
||||
static constexpr auto CLIENT_VERSION_MAX = 781;
|
||||
static constexpr auto CLIENT_VERSION_STR = "7.81";
|
||||
|
||||
static constexpr auto AUTHENTICATOR_DIGITS = 6U;
|
||||
static constexpr auto AUTHENTICATOR_PERIOD = 30U;
|
||||
@ -60,6 +60,7 @@ static constexpr auto AUTHENTICATOR_PERIOD = 30U;
|
||||
#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
#pragma warning(disable:4351) // new behavior: elements of array will be default initialized
|
||||
#pragma warning(disable:4458) // declaration hides class member
|
||||
#pragma warning(disable:4996) // inetpton warning
|
||||
#endif
|
||||
|
||||
#define strcasecmp _stricmp
|
||||
|
@ -1,82 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#include "depotchest.h"
|
||||
#include "tools.h"
|
||||
|
||||
DepotChest::DepotChest(uint16_t type) :
|
||||
Container(type), maxDepotItems(1500) {}
|
||||
|
||||
ReturnValue DepotChest::queryAdd(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t flags, Creature* actor/* = nullptr*/) const
|
||||
{
|
||||
const Item* item = thing.getItem();
|
||||
if (item == nullptr) {
|
||||
return RETURNVALUE_NOTPOSSIBLE;
|
||||
}
|
||||
|
||||
bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags);
|
||||
if (!skipLimit) {
|
||||
int32_t addCount = 0;
|
||||
|
||||
if ((item->isStackable() && item->getItemCount() != count)) {
|
||||
addCount = 1;
|
||||
}
|
||||
|
||||
if (item->getTopParent() != this) {
|
||||
if (const Container* container = item->getContainer()) {
|
||||
addCount = container->getItemHoldingCount() + 1;
|
||||
} else {
|
||||
addCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (getItemHoldingCount() + addCount > maxDepotItems) {
|
||||
return RETURNVALUE_DEPOTISFULL;
|
||||
}
|
||||
}
|
||||
|
||||
return Container::queryAdd(index, thing, count, flags, actor);
|
||||
}
|
||||
|
||||
void DepotChest::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t)
|
||||
{
|
||||
Cylinder* parent = getParent();
|
||||
if (parent != nullptr) {
|
||||
parent->postAddNotification(thing, oldParent, index, LINK_PARENT);
|
||||
}
|
||||
}
|
||||
|
||||
void DepotChest::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t)
|
||||
{
|
||||
Cylinder* parent = getParent();
|
||||
if (parent != nullptr) {
|
||||
parent->postRemoveNotification(thing, newParent, index, LINK_PARENT);
|
||||
}
|
||||
}
|
||||
|
||||
Cylinder* DepotChest::getParent() const
|
||||
{
|
||||
if (parent) {
|
||||
return parent->getParent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef FS_DEPOTCHEST_H_6538526014684E3DBC92CC12815B6766
|
||||
#define FS_DEPOTCHEST_H_6538526014684E3DBC92CC12815B6766
|
||||
|
||||
#include "container.h"
|
||||
|
||||
class DepotChest final : public Container
|
||||
{
|
||||
public:
|
||||
explicit DepotChest(uint16_t type);
|
||||
|
||||
//serialization
|
||||
void setMaxDepotItems(uint32_t maxitems) {
|
||||
maxDepotItems = maxitems;
|
||||
}
|
||||
|
||||
//cylinder implementations
|
||||
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t flags, Creature* actor = nullptr) const override;
|
||||
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
|
||||
//overrides
|
||||
bool canRemove() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cylinder* getParent() const override;
|
||||
Cylinder* getRealParent() const override {
|
||||
return parent;
|
||||
}
|
||||
|
||||
private:
|
||||
uint32_t maxDepotItems;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,9 +20,12 @@
|
||||
#include "otpch.h"
|
||||
|
||||
#include "depotlocker.h"
|
||||
#include "creature.h"
|
||||
#include "player.h"
|
||||
#include "tools.h"
|
||||
|
||||
DepotLocker::DepotLocker(uint16_t type) :
|
||||
Container(type, 3), depotId(0) {}
|
||||
Container(type, 30), depotId(0) {}
|
||||
|
||||
Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream)
|
||||
{
|
||||
@ -35,9 +38,40 @@ Attr_ReadValue DepotLocker::readAttr(AttrTypes_t attr, PropStream& propStream)
|
||||
return Item::readAttr(attr, propStream);
|
||||
}
|
||||
|
||||
ReturnValue DepotLocker::queryAdd(int32_t, const Thing&, uint32_t, uint32_t, Creature*) const
|
||||
ReturnValue DepotLocker::queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature* actor) const
|
||||
{
|
||||
return RETURNVALUE_NOTENOUGHROOM;
|
||||
const Item* item = thing.getItem();
|
||||
if (item == nullptr) {
|
||||
return RETURNVALUE_NOTPOSSIBLE;
|
||||
}
|
||||
|
||||
bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags);
|
||||
if (!skipLimit) {
|
||||
int32_t addCount = 0;
|
||||
|
||||
if ((item->isStackable() && item->getItemCount() != count)) {
|
||||
addCount = 1;
|
||||
}
|
||||
|
||||
if (item->getTopParent() != this) {
|
||||
if (const Container* container = item->getContainer()) {
|
||||
addCount = container->getItemHoldingCount() + 1;
|
||||
} else {
|
||||
addCount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (actor) {
|
||||
Player* player = actor->getPlayer();
|
||||
if (player) {
|
||||
if (getItemHoldingCount() + addCount > player->getMaxDepotItems()) {
|
||||
return RETURNVALUE_DEPOTISFULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Container::queryAdd(index, thing, count, flags, actor);
|
||||
}
|
||||
|
||||
void DepotLocker::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t)
|
||||
@ -53,12 +87,3 @@ void DepotLocker::postRemoveNotification(Thing* thing, const Cylinder* newParent
|
||||
parent->postRemoveNotification(thing, newParent, index, LINK_PARENT);
|
||||
}
|
||||
}
|
||||
|
||||
void DepotLocker::removeInbox(Inbox* inbox)
|
||||
{
|
||||
auto cit = std::find(itemlist.begin(), itemlist.end(), inbox);
|
||||
if (cit == itemlist.end()) {
|
||||
return;
|
||||
}
|
||||
itemlist.erase(cit);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -21,24 +21,21 @@
|
||||
#define FS_DEPOTLOCKER_H_53AD8E0606A34070B87F792611F4F3F8
|
||||
|
||||
#include "container.h"
|
||||
#include "inbox.h"
|
||||
|
||||
class DepotLocker final : public Container
|
||||
{
|
||||
public:
|
||||
explicit DepotLocker(uint16_t type);
|
||||
|
||||
DepotLocker* getDepotLocker() override {
|
||||
DepotLocker* getDepotLocker() final {
|
||||
return this;
|
||||
}
|
||||
const DepotLocker* getDepotLocker() const override {
|
||||
const DepotLocker* getDepotLocker() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
void removeInbox(Inbox* inbox);
|
||||
|
||||
//serialization
|
||||
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
|
||||
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final;
|
||||
|
||||
uint16_t getDepotId() const {
|
||||
return depotId;
|
||||
@ -49,12 +46,12 @@ class DepotLocker final : public Container
|
||||
|
||||
//cylinder implementations
|
||||
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t flags, Creature* actor = nullptr) const override;
|
||||
uint32_t flags, Creature* actor = nullptr) const final;
|
||||
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
|
||||
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
|
||||
|
||||
bool canRemove() const override {
|
||||
bool canRemove() const final {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
356
src/enums.h
356
src/enums.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,43 +20,6 @@
|
||||
#ifndef FS_ENUMS_H_003445999FEE4A67BCECBE918B0124CE
|
||||
#define FS_ENUMS_H_003445999FEE4A67BCECBE918B0124CE
|
||||
|
||||
enum RuleViolationType_t : uint8_t {
|
||||
REPORT_TYPE_NAME = 0,
|
||||
REPORT_TYPE_STATEMENT = 1,
|
||||
REPORT_TYPE_BOT = 2
|
||||
};
|
||||
|
||||
enum RuleViolationReasons_t : uint8_t {
|
||||
REPORT_REASON_NAMEINAPPROPRIATE = 0,
|
||||
REPORT_REASON_NAMEPOORFORMATTED = 1,
|
||||
REPORT_REASON_NAMEADVERTISING = 2,
|
||||
REPORT_REASON_NAMEUNFITTING = 3,
|
||||
REPORT_REASON_NAMERULEVIOLATION = 4,
|
||||
REPORT_REASON_INSULTINGSTATEMENT = 5,
|
||||
REPORT_REASON_SPAMMING = 6,
|
||||
REPORT_REASON_ADVERTISINGSTATEMENT = 7,
|
||||
REPORT_REASON_UNFITTINGSTATEMENT = 8,
|
||||
REPORT_REASON_LANGUAGESTATEMENT = 9,
|
||||
REPORT_REASON_DISCLOSURE = 10,
|
||||
REPORT_REASON_RULEVIOLATION = 11,
|
||||
REPORT_REASON_STATEMENT_BUGABUSE = 12,
|
||||
REPORT_REASON_UNOFFICIALSOFTWARE = 13,
|
||||
REPORT_REASON_PRETENDING = 14,
|
||||
REPORT_REASON_HARASSINGOWNERS = 15,
|
||||
REPORT_REASON_FALSEINFO = 16,
|
||||
REPORT_REASON_ACCOUNTSHARING = 17,
|
||||
REPORT_REASON_STEALINGDATA = 18,
|
||||
REPORT_REASON_SERVICEATTACKING = 19,
|
||||
REPORT_REASON_SERVICEAGREEMENT = 20
|
||||
};
|
||||
|
||||
enum BugReportType_t : uint8_t {
|
||||
BUG_CATEGORY_MAP = 0,
|
||||
BUG_CATEGORY_TYPO = 1,
|
||||
BUG_CATEGORY_TECHNICAL = 2,
|
||||
BUG_CATEGORY_OTHER = 3
|
||||
};
|
||||
|
||||
enum ThreadState {
|
||||
THREAD_STATE_RUNNING,
|
||||
THREAD_STATE_CLOSING,
|
||||
@ -67,7 +30,7 @@ enum itemAttrTypes : uint32_t {
|
||||
ITEM_ATTRIBUTE_NONE,
|
||||
|
||||
ITEM_ATTRIBUTE_ACTIONID = 1 << 0,
|
||||
ITEM_ATTRIBUTE_UNIQUEID = 1 << 1,
|
||||
ITEM_ATTRIBUTE_MOVEMENTID = 1 << 1,
|
||||
ITEM_ATTRIBUTE_DESCRIPTION = 1 << 2,
|
||||
ITEM_ATTRIBUTE_TEXT = 1 << 3,
|
||||
ITEM_ATTRIBUTE_DATE = 1 << 4,
|
||||
@ -78,60 +41,26 @@ enum itemAttrTypes : uint32_t {
|
||||
ITEM_ATTRIBUTE_WEIGHT = 1 << 9,
|
||||
ITEM_ATTRIBUTE_ATTACK = 1 << 10,
|
||||
ITEM_ATTRIBUTE_DEFENSE = 1 << 11,
|
||||
ITEM_ATTRIBUTE_EXTRADEFENSE = 1 << 12,
|
||||
ITEM_ATTRIBUTE_ARMOR = 1 << 13,
|
||||
ITEM_ATTRIBUTE_HITCHANCE = 1 << 14,
|
||||
ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 15,
|
||||
ITEM_ATTRIBUTE_OWNER = 1 << 16,
|
||||
ITEM_ATTRIBUTE_DURATION = 1 << 17,
|
||||
ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18,
|
||||
ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 19,
|
||||
ITEM_ATTRIBUTE_CHARGES = 1 << 20,
|
||||
ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 21,
|
||||
ITEM_ATTRIBUTE_DOORID = 1 << 22,
|
||||
ITEM_ATTRIBUTE_DECAYTO = 1 << 23,
|
||||
|
||||
ITEM_ATTRIBUTE_CUSTOM = 1U << 31
|
||||
ITEM_ATTRIBUTE_ARMOR = 1 << 12,
|
||||
ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 13,
|
||||
ITEM_ATTRIBUTE_OWNER = 1 << 14,
|
||||
ITEM_ATTRIBUTE_DURATION = 1 << 15,
|
||||
ITEM_ATTRIBUTE_DECAYSTATE = 1 << 16,
|
||||
ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 17,
|
||||
ITEM_ATTRIBUTE_CHARGES = 1 << 18,
|
||||
ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 19,
|
||||
ITEM_ATTRIBUTE_DOORID = 1 << 20,
|
||||
ITEM_ATTRIBUTE_KEYNUMBER = 1 << 21,
|
||||
ITEM_ATTRIBUTE_KEYHOLENUMBER = 1 << 22,
|
||||
ITEM_ATTRIBUTE_DOORQUESTNUMBER = 1 << 23,
|
||||
ITEM_ATTRIBUTE_DOORQUESTVALUE = 1 << 24,
|
||||
ITEM_ATTRIBUTE_DOORLEVEL = 1 << 25,
|
||||
ITEM_ATTRIBUTE_CHESTQUESTNUMBER = 1 << 26,
|
||||
};
|
||||
|
||||
enum VipStatus_t : uint8_t {
|
||||
VIPSTATUS_OFFLINE = 0,
|
||||
VIPSTATUS_ONLINE = 1,
|
||||
VIPSTATUS_PENDING = 2
|
||||
};
|
||||
|
||||
enum MarketAction_t {
|
||||
MARKETACTION_BUY = 0,
|
||||
MARKETACTION_SELL = 1,
|
||||
};
|
||||
|
||||
enum MarketRequest_t {
|
||||
MARKETREQUEST_OWN_OFFERS = 0xFFFE,
|
||||
MARKETREQUEST_OWN_HISTORY = 0xFFFF,
|
||||
};
|
||||
|
||||
enum MarketOfferState_t {
|
||||
OFFERSTATE_ACTIVE = 0,
|
||||
OFFERSTATE_CANCELLED = 1,
|
||||
OFFERSTATE_EXPIRED = 2,
|
||||
OFFERSTATE_ACCEPTED = 3,
|
||||
|
||||
OFFERSTATE_ACCEPTEDEX = 255,
|
||||
};
|
||||
|
||||
enum ChannelEvent_t : uint8_t {
|
||||
CHANNELEVENT_JOIN = 0,
|
||||
CHANNELEVENT_LEAVE = 1,
|
||||
CHANNELEVENT_INVITE = 2,
|
||||
CHANNELEVENT_EXCLUDE = 3,
|
||||
};
|
||||
|
||||
enum CreatureType_t : uint8_t {
|
||||
CREATURETYPE_PLAYER = 0,
|
||||
CREATURETYPE_MONSTER = 1,
|
||||
CREATURETYPE_NPC = 2,
|
||||
CREATURETYPE_SUMMON_OWN = 3,
|
||||
CREATURETYPE_SUMMON_OTHERS = 4,
|
||||
};
|
||||
|
||||
enum OperatingSystem_t : uint8_t {
|
||||
@ -146,20 +75,6 @@ enum OperatingSystem_t : uint8_t {
|
||||
CLIENTOS_OTCLIENT_MAC = 12,
|
||||
};
|
||||
|
||||
enum SpellGroup_t : uint8_t {
|
||||
SPELLGROUP_NONE = 0,
|
||||
SPELLGROUP_ATTACK = 1,
|
||||
SPELLGROUP_HEALING = 2,
|
||||
SPELLGROUP_SUPPORT = 3,
|
||||
SPELLGROUP_SPECIAL = 4,
|
||||
};
|
||||
|
||||
enum SpellType_t : uint8_t {
|
||||
SPELL_UNDEFINED = 0,
|
||||
SPELL_INSTANT = 1,
|
||||
SPELL_RUNE = 2,
|
||||
};
|
||||
|
||||
enum AccountType_t : uint8_t {
|
||||
ACCOUNT_TYPE_NORMAL = 1,
|
||||
ACCOUNT_TYPE_TUTOR = 2,
|
||||
@ -174,7 +89,6 @@ enum RaceType_t : uint8_t {
|
||||
RACE_BLOOD,
|
||||
RACE_UNDEAD,
|
||||
RACE_FIRE,
|
||||
RACE_ENERGY,
|
||||
};
|
||||
|
||||
enum CombatType_t : uint16_t {
|
||||
@ -189,11 +103,8 @@ enum CombatType_t : uint16_t {
|
||||
COMBAT_MANADRAIN = 1 << 6,
|
||||
COMBAT_HEALING = 1 << 7,
|
||||
COMBAT_DROWNDAMAGE = 1 << 8,
|
||||
COMBAT_ICEDAMAGE = 1 << 9,
|
||||
COMBAT_HOLYDAMAGE = 1 << 10,
|
||||
COMBAT_DEATHDAMAGE = 1 << 11,
|
||||
|
||||
COMBAT_COUNT = 12
|
||||
COMBAT_COUNT = 10
|
||||
};
|
||||
|
||||
enum CombatParam_t {
|
||||
@ -207,6 +118,14 @@ enum CombatParam_t {
|
||||
COMBAT_PARAM_AGGRESSIVE,
|
||||
COMBAT_PARAM_DISPEL,
|
||||
COMBAT_PARAM_USECHARGES,
|
||||
COMBAT_PARAM_DECREASEDAMAGE,
|
||||
COMBAT_PARAM_MAXIMUMDECREASEDDAMAGE,
|
||||
};
|
||||
|
||||
enum fightMode_t : uint8_t {
|
||||
FIGHTMODE_ATTACK = 1,
|
||||
FIGHTMODE_BALANCED = 2,
|
||||
FIGHTMODE_DEFENSE = 3,
|
||||
};
|
||||
|
||||
enum CallBackParam_t {
|
||||
@ -234,7 +153,6 @@ enum ConditionParam_t {
|
||||
CONDITION_PARAM_MAXVALUE = 15,
|
||||
CONDITION_PARAM_STARTVALUE = 16,
|
||||
CONDITION_PARAM_TICKINTERVAL = 17,
|
||||
CONDITION_PARAM_FORCEUPDATE = 18,
|
||||
CONDITION_PARAM_SKILL_MELEE = 19,
|
||||
CONDITION_PARAM_SKILL_FIST = 20,
|
||||
CONDITION_PARAM_SKILL_CLUB = 21,
|
||||
@ -260,16 +178,13 @@ enum ConditionParam_t {
|
||||
CONDITION_PARAM_SKILL_DISTANCEPERCENT = 41,
|
||||
CONDITION_PARAM_SKILL_SHIELDPERCENT = 42,
|
||||
CONDITION_PARAM_SKILL_FISHINGPERCENT = 43,
|
||||
CONDITION_PARAM_BUFF_SPELL = 44,
|
||||
// CONDITION_PARAM_BUFF_SPELL = 44,
|
||||
CONDITION_PARAM_SUBID = 45,
|
||||
CONDITION_PARAM_FIELD = 46,
|
||||
CONDITION_PARAM_DISABLE_DEFENSE = 47,
|
||||
CONDITION_PARAM_SPECIALSKILL_CRITICALHITCHANCE = 48,
|
||||
CONDITION_PARAM_SPECIALSKILL_CRITICALHITAMOUNT = 49,
|
||||
CONDITION_PARAM_SPECIALSKILL_LIFELEECHCHANCE = 50,
|
||||
CONDITION_PARAM_SPECIALSKILL_LIFELEECHAMOUNT = 51,
|
||||
CONDITION_PARAM_SPECIALSKILL_MANALEECHCHANCE = 52,
|
||||
CONDITION_PARAM_SPECIALSKILL_MANALEECHAMOUNT = 53,
|
||||
CONDITION_PARAM_CYCLE = 47,
|
||||
CONDITION_PARAM_HIT_DAMAGE = 48,
|
||||
CONDITION_PARAM_COUNT = 49,
|
||||
CONDITION_PARAM_MAX_COUNT = 50,
|
||||
};
|
||||
|
||||
enum BlockType_t : uint8_t {
|
||||
@ -305,18 +220,6 @@ enum stats_t {
|
||||
STAT_LAST = STAT_MAGICPOINTS
|
||||
};
|
||||
|
||||
enum SpecialSkills_t {
|
||||
SPECIALSKILL_CRITICALHITCHANCE,
|
||||
SPECIALSKILL_CRITICALHITAMOUNT,
|
||||
SPECIALSKILL_LIFELEECHCHANCE,
|
||||
SPECIALSKILL_LIFELEECHAMOUNT,
|
||||
SPECIALSKILL_MANALEECHCHANCE,
|
||||
SPECIALSKILL_MANALEECHAMOUNT,
|
||||
|
||||
SPECIALSKILL_FIRST = SPECIALSKILL_CRITICALHITCHANCE,
|
||||
SPECIALSKILL_LAST = SPECIALSKILL_MANALEECHAMOUNT
|
||||
};
|
||||
|
||||
enum formulaType_t {
|
||||
COMBAT_FORMULA_UNDEFINED,
|
||||
COMBAT_FORMULA_LEVELMAGIC,
|
||||
@ -330,31 +233,24 @@ enum ConditionType_t {
|
||||
CONDITION_POISON = 1 << 0,
|
||||
CONDITION_FIRE = 1 << 1,
|
||||
CONDITION_ENERGY = 1 << 2,
|
||||
CONDITION_BLEEDING = 1 << 3,
|
||||
CONDITION_HASTE = 1 << 4,
|
||||
CONDITION_PARALYZE = 1 << 5,
|
||||
CONDITION_OUTFIT = 1 << 6,
|
||||
CONDITION_INVISIBLE = 1 << 7,
|
||||
CONDITION_LIGHT = 1 << 8,
|
||||
CONDITION_MANASHIELD = 1 << 9,
|
||||
CONDITION_INFIGHT = 1 << 10,
|
||||
CONDITION_DRUNK = 1 << 11,
|
||||
CONDITION_EXHAUST_WEAPON = 1 << 12, // unused
|
||||
CONDITION_REGENERATION = 1 << 13,
|
||||
CONDITION_SOUL = 1 << 14,
|
||||
CONDITION_DROWN = 1 << 15,
|
||||
CONDITION_MUTED = 1 << 16,
|
||||
CONDITION_CHANNELMUTEDTICKS = 1 << 17,
|
||||
CONDITION_YELLTICKS = 1 << 18,
|
||||
CONDITION_ATTRIBUTES = 1 << 19,
|
||||
CONDITION_FREEZING = 1 << 20,
|
||||
CONDITION_DAZZLED = 1 << 21,
|
||||
CONDITION_CURSED = 1 << 22,
|
||||
CONDITION_EXHAUST_COMBAT = 1 << 23, // unused
|
||||
CONDITION_EXHAUST_HEAL = 1 << 24, // unused
|
||||
CONDITION_PACIFIED = 1 << 25,
|
||||
CONDITION_SPELLCOOLDOWN = 1 << 26,
|
||||
CONDITION_SPELLGROUPCOOLDOWN = 1 << 27,
|
||||
CONDITION_HASTE = 1 << 3,
|
||||
CONDITION_PARALYZE = 1 << 4,
|
||||
CONDITION_OUTFIT = 1 << 5,
|
||||
CONDITION_INVISIBLE = 1 << 6,
|
||||
CONDITION_LIGHT = 1 << 7,
|
||||
CONDITION_MANASHIELD = 1 << 8,
|
||||
CONDITION_INFIGHT = 1 << 9,
|
||||
CONDITION_DRUNK = 1 << 10,
|
||||
CONDITION_REGENERATION = 1 << 11,
|
||||
CONDITION_SOUL = 1 << 12,
|
||||
CONDITION_MUTED = 1 << 13,
|
||||
CONDITION_CHANNELMUTEDTICKS = 1 << 14,
|
||||
CONDITION_YELLTICKS = 1 << 15,
|
||||
CONDITION_ATTRIBUTES = 1 << 16,
|
||||
CONDITION_EXHAUST = 1 << 17,
|
||||
CONDITION_PACIFIED = 1 << 18,
|
||||
CONDITION_AGGRESSIVE = 1 << 19,
|
||||
CONDITION_DROWN = 1 << 20,
|
||||
};
|
||||
|
||||
enum ConditionId_t : int8_t {
|
||||
@ -380,7 +276,11 @@ enum PlayerSex_t : uint8_t {
|
||||
};
|
||||
|
||||
enum Vocation_t : uint16_t {
|
||||
VOCATION_NONE = 0
|
||||
VOCATION_NONE,
|
||||
VOCATION_SORCERER = 1 << 0,
|
||||
VOCATION_DRUID = 1 << 1,
|
||||
VOCATION_PALADIN = 1 << 2,
|
||||
VOCATION_KNIGHT = 1 << 3,
|
||||
};
|
||||
|
||||
enum ReturnValue {
|
||||
@ -443,12 +343,10 @@ enum ReturnValue {
|
||||
RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL,
|
||||
RETURNVALUE_CANNOTCONJUREITEMHERE,
|
||||
RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS,
|
||||
RETURNVALUE_NAMEISTOOAMBIGUOUS,
|
||||
RETURNVALUE_NAMEISTOOAMBIGIOUS,
|
||||
RETURNVALUE_CANONLYUSEONESHIELD,
|
||||
RETURNVALUE_NOPARTYMEMBERSINRANGE,
|
||||
RETURNVALUE_YOUARENOTTHEOWNER,
|
||||
RETURNVALUE_NOSUCHRAIDEXISTS,
|
||||
RETURNVALUE_ANOTHERRAIDISALREADYEXECUTING,
|
||||
RETURNVALUE_TRADEPLAYERFARAWAY,
|
||||
RETURNVALUE_YOUDONTOWNTHISHOUSE,
|
||||
RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE,
|
||||
@ -456,43 +354,9 @@ enum ReturnValue {
|
||||
RETURNVALUE_YOUCANNOTTRADETHISHOUSE,
|
||||
};
|
||||
|
||||
enum SpeechBubble_t
|
||||
{
|
||||
SPEECHBUBBLE_NONE = 0,
|
||||
SPEECHBUBBLE_NORMAL = 1,
|
||||
SPEECHBUBBLE_TRADE = 2,
|
||||
SPEECHBUBBLE_QUEST = 3,
|
||||
SPEECHBUBBLE_QUESTTRADER = 4,
|
||||
};
|
||||
|
||||
enum MapMark_t
|
||||
{
|
||||
MAPMARK_TICK = 0,
|
||||
MAPMARK_QUESTION = 1,
|
||||
MAPMARK_EXCLAMATION = 2,
|
||||
MAPMARK_STAR = 3,
|
||||
MAPMARK_CROSS = 4,
|
||||
MAPMARK_TEMPLE = 5,
|
||||
MAPMARK_KISS = 6,
|
||||
MAPMARK_SHOVEL = 7,
|
||||
MAPMARK_SWORD = 8,
|
||||
MAPMARK_FLAG = 9,
|
||||
MAPMARK_LOCK = 10,
|
||||
MAPMARK_BAG = 11,
|
||||
MAPMARK_SKULL = 12,
|
||||
MAPMARK_DOLLAR = 13,
|
||||
MAPMARK_REDNORTH = 14,
|
||||
MAPMARK_REDSOUTH = 15,
|
||||
MAPMARK_REDEAST = 16,
|
||||
MAPMARK_REDWEST = 17,
|
||||
MAPMARK_GREENNORTH = 18,
|
||||
MAPMARK_GREENSOUTH = 19,
|
||||
};
|
||||
|
||||
struct Outfit_t {
|
||||
uint16_t lookType = 0;
|
||||
uint16_t lookTypeEx = 0;
|
||||
uint16_t lookMount = 0;
|
||||
uint8_t lookHead = 0;
|
||||
uint8_t lookBody = 0;
|
||||
uint8_t lookLegs = 0;
|
||||
@ -507,85 +371,6 @@ struct LightInfo {
|
||||
constexpr LightInfo(uint8_t level, uint8_t color) : level(level), color(color) {}
|
||||
};
|
||||
|
||||
struct ShopInfo {
|
||||
uint16_t itemId;
|
||||
int32_t subType;
|
||||
uint32_t buyPrice;
|
||||
uint32_t sellPrice;
|
||||
std::string realName;
|
||||
|
||||
ShopInfo() {
|
||||
itemId = 0;
|
||||
subType = 1;
|
||||
buyPrice = 0;
|
||||
sellPrice = 0;
|
||||
}
|
||||
|
||||
ShopInfo(uint16_t itemId, int32_t subType = 0, uint32_t buyPrice = 0, uint32_t sellPrice = 0, std::string realName = "")
|
||||
: itemId(itemId), subType(subType), buyPrice(buyPrice), sellPrice(sellPrice), realName(std::move(realName)) {}
|
||||
};
|
||||
|
||||
struct MarketOffer {
|
||||
uint32_t price;
|
||||
uint32_t timestamp;
|
||||
uint16_t amount;
|
||||
uint16_t counter;
|
||||
uint16_t itemId;
|
||||
std::string playerName;
|
||||
};
|
||||
|
||||
struct MarketOfferEx {
|
||||
MarketOfferEx() = default;
|
||||
MarketOfferEx(MarketOfferEx&& other) :
|
||||
id(other.id), playerId(other.playerId), timestamp(other.timestamp), price(other.price),
|
||||
amount(other.amount), counter(other.counter), itemId(other.itemId), type(other.type),
|
||||
playerName(std::move(other.playerName)) {}
|
||||
|
||||
uint32_t id;
|
||||
uint32_t playerId;
|
||||
uint32_t timestamp;
|
||||
uint32_t price;
|
||||
uint16_t amount;
|
||||
uint16_t counter;
|
||||
uint16_t itemId;
|
||||
MarketAction_t type;
|
||||
std::string playerName;
|
||||
};
|
||||
|
||||
struct HistoryMarketOffer {
|
||||
uint32_t timestamp;
|
||||
uint32_t price;
|
||||
uint16_t itemId;
|
||||
uint16_t amount;
|
||||
MarketOfferState_t state;
|
||||
};
|
||||
|
||||
struct MarketStatistics {
|
||||
MarketStatistics() {
|
||||
numTransactions = 0;
|
||||
highestPrice = 0;
|
||||
totalPrice = 0;
|
||||
lowestPrice = 0;
|
||||
}
|
||||
|
||||
uint32_t numTransactions;
|
||||
uint32_t highestPrice;
|
||||
uint64_t totalPrice;
|
||||
uint32_t lowestPrice;
|
||||
};
|
||||
|
||||
struct ModalWindow
|
||||
{
|
||||
std::list<std::pair<std::string, uint8_t>> buttons, choices;
|
||||
std::string title, message;
|
||||
uint32_t id;
|
||||
uint8_t defaultEnterButton, defaultEscapeButton;
|
||||
bool priority;
|
||||
|
||||
ModalWindow(uint32_t id, std::string title, std::string message)
|
||||
: title(std::move(title)), message(std::move(message)), id(id), defaultEnterButton(0xFF), defaultEscapeButton(0xFF), priority(false) {}
|
||||
};
|
||||
|
||||
enum CombatOrigin
|
||||
{
|
||||
ORIGIN_NONE,
|
||||
@ -597,31 +382,20 @@ enum CombatOrigin
|
||||
|
||||
struct CombatDamage
|
||||
{
|
||||
struct {
|
||||
CombatType_t type;
|
||||
int32_t value;
|
||||
} primary, secondary;
|
||||
|
||||
CombatType_t type;
|
||||
int32_t value;
|
||||
int32_t min;
|
||||
int32_t max;
|
||||
CombatOrigin origin;
|
||||
|
||||
CombatDamage()
|
||||
{
|
||||
origin = ORIGIN_NONE;
|
||||
primary.type = secondary.type = COMBAT_NONE;
|
||||
primary.value = secondary.value = 0;
|
||||
type = COMBAT_NONE;
|
||||
value = 0;
|
||||
min = 0;
|
||||
max = 0;
|
||||
}
|
||||
};
|
||||
|
||||
using MarketOfferList = std::list<MarketOffer>;
|
||||
using HistoryMarketOfferList = std::list<HistoryMarketOffer>;
|
||||
using ShopInfoList = std::list<ShopInfo>;
|
||||
|
||||
enum MonstersEvent_t : uint8_t {
|
||||
MONSTERS_EVENT_NONE = 0,
|
||||
MONSTERS_EVENT_THINK = 1,
|
||||
MONSTERS_EVENT_APPEAR = 2,
|
||||
MONSTERS_EVENT_DISAPPEAR = 3,
|
||||
MONSTERS_EVENT_MOVE = 4,
|
||||
MONSTERS_EVENT_SAY = 5,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
432
src/events.cpp
432
src/events.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -29,9 +29,39 @@
|
||||
Events::Events() :
|
||||
scriptInterface("Event Interface")
|
||||
{
|
||||
clear();
|
||||
scriptInterface.initState();
|
||||
}
|
||||
|
||||
void Events::clear()
|
||||
{
|
||||
// Creature
|
||||
creatureOnChangeOutfit = -1;
|
||||
creatureOnAreaCombat = -1;
|
||||
creatureOnTargetCombat = -1;
|
||||
|
||||
// Party
|
||||
partyOnJoin = -1;
|
||||
partyOnLeave = -1;
|
||||
partyOnDisband = -1;
|
||||
|
||||
// Player
|
||||
playerOnLook = -1;
|
||||
playerOnLookInBattleList = -1;
|
||||
playerOnLookInTrade = -1;
|
||||
playerOnMoveItem = -1;
|
||||
playerOnItemMoved = -1;
|
||||
playerOnMoveCreature = -1;
|
||||
playerOnTurn = -1;
|
||||
playerOnTradeRequest = -1;
|
||||
playerOnTradeAccept = -1;
|
||||
playerOnGainExperience = -1;
|
||||
playerOnLoseExperience = -1;
|
||||
playerOnGainSkillTries = -1;
|
||||
playerOnReportBug = -1;
|
||||
|
||||
}
|
||||
|
||||
bool Events::load()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
@ -41,8 +71,6 @@ bool Events::load()
|
||||
return false;
|
||||
}
|
||||
|
||||
info = {};
|
||||
|
||||
std::set<std::string> classes;
|
||||
for (auto eventNode : doc.child("events").children()) {
|
||||
if (!eventNode.attribute("enabled").as_bool()) {
|
||||
@ -63,69 +91,80 @@ bool Events::load()
|
||||
const int32_t event = scriptInterface.getMetaEvent(className, methodName);
|
||||
if (className == "Creature") {
|
||||
if (methodName == "onChangeOutfit") {
|
||||
info.creatureOnChangeOutfit = event;
|
||||
} else if (methodName == "onAreaCombat") {
|
||||
info.creatureOnAreaCombat = event;
|
||||
} else if (methodName == "onTargetCombat") {
|
||||
info.creatureOnTargetCombat = event;
|
||||
} else {
|
||||
creatureOnChangeOutfit = event;
|
||||
}
|
||||
else if (methodName == "onAreaCombat") {
|
||||
creatureOnAreaCombat = event;
|
||||
}
|
||||
else if (methodName == "onTargetCombat") {
|
||||
creatureOnTargetCombat = event;
|
||||
}
|
||||
else {
|
||||
std::cout << "[Warning - Events::load] Unknown creature method: " << methodName << std::endl;
|
||||
}
|
||||
} else if (className == "Party") {
|
||||
}
|
||||
else if (className == "Party") {
|
||||
if (methodName == "onJoin") {
|
||||
info.partyOnJoin = event;
|
||||
} else if (methodName == "onLeave") {
|
||||
info.partyOnLeave = event;
|
||||
} else if (methodName == "onDisband") {
|
||||
info.partyOnDisband = event;
|
||||
} else if (methodName == "onShareExperience") {
|
||||
info.partyOnShareExperience = event;
|
||||
} else {
|
||||
partyOnJoin = event;
|
||||
}
|
||||
else if (methodName == "onLeave") {
|
||||
partyOnLeave = event;
|
||||
}
|
||||
else if (methodName == "onDisband") {
|
||||
partyOnDisband = event;
|
||||
}
|
||||
else if (methodName == "onShareExperience") {
|
||||
partyOnShareExperience = event;
|
||||
}
|
||||
else {
|
||||
std::cout << "[Warning - Events::load] Unknown party method: " << methodName << std::endl;
|
||||
}
|
||||
} else if (className == "Player") {
|
||||
if (methodName == "onBrowseField") {
|
||||
info.playerOnBrowseField = event;
|
||||
} else if (methodName == "onLook") {
|
||||
info.playerOnLook = event;
|
||||
} else if (methodName == "onLookInBattleList") {
|
||||
info.playerOnLookInBattleList = event;
|
||||
} else if (methodName == "onLookInTrade") {
|
||||
info.playerOnLookInTrade = event;
|
||||
} else if (methodName == "onLookInShop") {
|
||||
info.playerOnLookInShop = event;
|
||||
} else if (methodName == "onTradeRequest") {
|
||||
info.playerOnTradeRequest = event;
|
||||
} else if (methodName == "onTradeAccept") {
|
||||
info.playerOnTradeAccept = event;
|
||||
} else if (methodName == "onMoveItem") {
|
||||
info.playerOnMoveItem = event;
|
||||
} else if (methodName == "onItemMoved") {
|
||||
info.playerOnItemMoved = event;
|
||||
} else if (methodName == "onMoveCreature") {
|
||||
info.playerOnMoveCreature = event;
|
||||
} else if (methodName == "onReportRuleViolation") {
|
||||
info.playerOnReportRuleViolation = event;
|
||||
} else if (methodName == "onReportBug") {
|
||||
info.playerOnReportBug = event;
|
||||
} else if (methodName == "onTurn") {
|
||||
info.playerOnTurn = event;
|
||||
} else if (methodName == "onGainExperience") {
|
||||
info.playerOnGainExperience = event;
|
||||
} else if (methodName == "onLoseExperience") {
|
||||
info.playerOnLoseExperience = event;
|
||||
} else if (methodName == "onGainSkillTries") {
|
||||
info.playerOnGainSkillTries = event;
|
||||
} else {
|
||||
}
|
||||
else if (className == "Player") {
|
||||
if (methodName == "onLook") {
|
||||
playerOnLook = event;
|
||||
}
|
||||
else if (methodName == "onLookInBattleList") {
|
||||
playerOnLookInBattleList = event;
|
||||
}
|
||||
else if (methodName == "onLookInTrade") {
|
||||
playerOnLookInTrade = event;
|
||||
}
|
||||
else if (methodName == "onTradeRequest") {
|
||||
playerOnTradeRequest = event;
|
||||
}
|
||||
else if (methodName == "onTradeAccept") {
|
||||
playerOnTradeAccept = event;
|
||||
}
|
||||
else if (methodName == "onMoveItem") {
|
||||
playerOnMoveItem = event;
|
||||
}
|
||||
else if (methodName == "onItemMoved") {
|
||||
playerOnItemMoved = event;
|
||||
}
|
||||
else if (methodName == "onMoveCreature") {
|
||||
playerOnMoveCreature = event;
|
||||
}
|
||||
else if (methodName == "onReportBug") {
|
||||
playerOnReportBug = event;
|
||||
}
|
||||
else if (methodName == "onTurn") {
|
||||
playerOnTurn = event;
|
||||
}
|
||||
else if (methodName == "onGainExperience") {
|
||||
playerOnGainExperience = event;
|
||||
}
|
||||
else if (methodName == "onLoseExperience") {
|
||||
playerOnLoseExperience = event;
|
||||
}
|
||||
else if (methodName == "onGainSkillTries") {
|
||||
playerOnGainSkillTries = event;
|
||||
}
|
||||
else {
|
||||
std::cout << "[Warning - Events::load] Unknown player method: " << methodName << std::endl;
|
||||
}
|
||||
} else if (className == "Monster") {
|
||||
if (methodName == "onDropLoot") {
|
||||
info.monsterOnDropLoot = event;
|
||||
} else {
|
||||
std::cout << "[Warning - Events::load] Unknown monster method: " << methodName << std::endl;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
std::cout << "[Warning - Events::load] Unknown class: " << className << std::endl;
|
||||
}
|
||||
}
|
||||
@ -136,7 +175,7 @@ bool Events::load()
|
||||
bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit)
|
||||
{
|
||||
// Creature:onChangeOutfit(outfit) or Creature.onChangeOutfit(self, outfit)
|
||||
if (info.creatureOnChangeOutfit == -1) {
|
||||
if (creatureOnChangeOutfit == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -146,10 +185,10 @@ bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& out
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.creatureOnChangeOutfit, &scriptInterface);
|
||||
env->setScriptId(creatureOnChangeOutfit, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.creatureOnChangeOutfit);
|
||||
scriptInterface.pushFunction(creatureOnChangeOutfit);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
||||
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
||||
@ -162,7 +201,7 @@ bool Events::eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& out
|
||||
ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive)
|
||||
{
|
||||
// Creature:onAreaCombat(tile, aggressive) or Creature.onAreaCombat(self, tile, aggressive)
|
||||
if (info.creatureOnAreaCombat == -1) {
|
||||
if (creatureOnAreaCombat == -1) {
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
@ -172,15 +211,16 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.creatureOnAreaCombat, &scriptInterface);
|
||||
env->setScriptId(creatureOnAreaCombat, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.creatureOnAreaCombat);
|
||||
scriptInterface.pushFunction(creatureOnAreaCombat);
|
||||
|
||||
if (creature) {
|
||||
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
||||
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
@ -193,7 +233,8 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo
|
||||
if (scriptInterface.protectedCall(L, 3, 1) != 0) {
|
||||
returnValue = RETURNVALUE_NOTPOSSIBLE;
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
@ -205,7 +246,7 @@ ReturnValue Events::eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bo
|
||||
ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* target)
|
||||
{
|
||||
// Creature:onTargetCombat(target) or Creature.onTargetCombat(self, target)
|
||||
if (info.creatureOnTargetCombat == -1) {
|
||||
if (creatureOnTargetCombat == -1) {
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
@ -215,15 +256,16 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.creatureOnTargetCombat, &scriptInterface);
|
||||
env->setScriptId(creatureOnTargetCombat, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.creatureOnTargetCombat);
|
||||
scriptInterface.pushFunction(creatureOnTargetCombat);
|
||||
|
||||
if (creature) {
|
||||
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
||||
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
@ -234,7 +276,8 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta
|
||||
if (scriptInterface.protectedCall(L, 2, 1) != 0) {
|
||||
returnValue = RETURNVALUE_NOTPOSSIBLE;
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
returnValue = LuaScriptInterface::getNumber<ReturnValue>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
@ -247,7 +290,7 @@ ReturnValue Events::eventCreatureOnTargetCombat(Creature* creature, Creature* ta
|
||||
bool Events::eventPartyOnJoin(Party* party, Player* player)
|
||||
{
|
||||
// Party:onJoin(player) or Party.onJoin(self, player)
|
||||
if (info.partyOnJoin == -1) {
|
||||
if (partyOnJoin == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -257,10 +300,10 @@ bool Events::eventPartyOnJoin(Party* party, Player* player)
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.partyOnJoin, &scriptInterface);
|
||||
env->setScriptId(partyOnJoin, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.partyOnJoin);
|
||||
scriptInterface.pushFunction(partyOnJoin);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Party>(L, party);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Party");
|
||||
@ -274,7 +317,7 @@ bool Events::eventPartyOnJoin(Party* party, Player* player)
|
||||
bool Events::eventPartyOnLeave(Party* party, Player* player)
|
||||
{
|
||||
// Party:onLeave(player) or Party.onLeave(self, player)
|
||||
if (info.partyOnLeave == -1) {
|
||||
if (partyOnLeave == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -284,10 +327,10 @@ bool Events::eventPartyOnLeave(Party* party, Player* player)
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.partyOnLeave, &scriptInterface);
|
||||
env->setScriptId(partyOnLeave, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.partyOnLeave);
|
||||
scriptInterface.pushFunction(partyOnLeave);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Party>(L, party);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Party");
|
||||
@ -301,7 +344,7 @@ bool Events::eventPartyOnLeave(Party* party, Player* player)
|
||||
bool Events::eventPartyOnDisband(Party* party)
|
||||
{
|
||||
// Party:onDisband() or Party.onDisband(self)
|
||||
if (info.partyOnDisband == -1) {
|
||||
if (partyOnDisband == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -311,10 +354,10 @@ bool Events::eventPartyOnDisband(Party* party)
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.partyOnDisband, &scriptInterface);
|
||||
env->setScriptId(partyOnDisband, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.partyOnDisband);
|
||||
scriptInterface.pushFunction(partyOnDisband);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Party>(L, party);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Party");
|
||||
@ -325,7 +368,7 @@ bool Events::eventPartyOnDisband(Party* party)
|
||||
void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
|
||||
{
|
||||
// Party:onShareExperience(exp) or Party.onShareExperience(self, exp)
|
||||
if (info.partyOnShareExperience == -1) {
|
||||
if (partyOnShareExperience == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -335,10 +378,10 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.partyOnShareExperience, &scriptInterface);
|
||||
env->setScriptId(partyOnShareExperience, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.partyOnShareExperience);
|
||||
scriptInterface.pushFunction(partyOnShareExperience);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Party>(L, party);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Party");
|
||||
@ -347,7 +390,8 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
|
||||
|
||||
if (scriptInterface.protectedCall(L, 2, 1) != 0) {
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
@ -355,37 +399,10 @@ void Events::eventPartyOnShareExperience(Party* party, uint64_t& exp)
|
||||
scriptInterface.resetScriptEnv();
|
||||
}
|
||||
|
||||
// Player
|
||||
bool Events::eventPlayerOnBrowseField(Player* player, const Position& position)
|
||||
{
|
||||
// Player:onBrowseField(position) or Player.onBrowseField(self, position)
|
||||
if (info.playerOnBrowseField == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!scriptInterface.reserveScriptEnv()) {
|
||||
std::cout << "[Error - Events::eventPlayerOnBrowseField] Call stack overflow" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnBrowseField, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnBrowseField);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
|
||||
LuaScriptInterface::pushPosition(L, position);
|
||||
|
||||
return scriptInterface.callFunction(2);
|
||||
}
|
||||
|
||||
void Events::eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance)
|
||||
{
|
||||
// Player:onLook(thing, position, distance) or Player.onLook(self, thing, position, distance)
|
||||
if (info.playerOnLook == -1) {
|
||||
if (playerOnLook == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -395,10 +412,10 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing*
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnLook, &scriptInterface);
|
||||
env->setScriptId(playerOnLook, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnLook);
|
||||
scriptInterface.pushFunction(playerOnLook);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -406,10 +423,12 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing*
|
||||
if (Creature* creature = thing->getCreature()) {
|
||||
LuaScriptInterface::pushUserdata<Creature>(L, creature);
|
||||
LuaScriptInterface::setCreatureMetatable(L, -1, creature);
|
||||
} else if (Item* item = thing->getItem()) {
|
||||
}
|
||||
else if (Item* item = thing->getItem()) {
|
||||
LuaScriptInterface::pushUserdata<Item>(L, item);
|
||||
LuaScriptInterface::setItemMetatable(L, -1, item);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
@ -422,7 +441,7 @@ void Events::eventPlayerOnLook(Player* player, const Position& position, Thing*
|
||||
void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance)
|
||||
{
|
||||
// Player:onLookInBattleList(creature, position, distance) or Player.onLookInBattleList(self, creature, position, distance)
|
||||
if (info.playerOnLookInBattleList == -1) {
|
||||
if (playerOnLookInBattleList == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -432,10 +451,10 @@ void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, i
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnLookInBattleList, &scriptInterface);
|
||||
env->setScriptId(playerOnLookInBattleList, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnLookInBattleList);
|
||||
scriptInterface.pushFunction(playerOnLookInBattleList);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -451,7 +470,7 @@ void Events::eventPlayerOnLookInBattleList(Player* player, Creature* creature, i
|
||||
void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance)
|
||||
{
|
||||
// Player:onLookInTrade(partner, item, distance) or Player.onLookInTrade(self, partner, item, distance)
|
||||
if (info.playerOnLookInTrade == -1) {
|
||||
if (playerOnLookInTrade == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -461,10 +480,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnLookInTrade, &scriptInterface);
|
||||
env->setScriptId(playerOnLookInTrade, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnLookInTrade);
|
||||
scriptInterface.pushFunction(playerOnLookInTrade);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -480,39 +499,10 @@ void Events::eventPlayerOnLookInTrade(Player* player, Player* partner, Item* ite
|
||||
scriptInterface.callVoidFunction(4);
|
||||
}
|
||||
|
||||
bool Events::eventPlayerOnLookInShop(Player* player, const ItemType* itemType, uint8_t count)
|
||||
{
|
||||
// Player:onLookInShop(itemType, count) or Player.onLookInShop(self, itemType, count)
|
||||
if (info.playerOnLookInShop == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!scriptInterface.reserveScriptEnv()) {
|
||||
std::cout << "[Error - Events::eventPlayerOnLookInShop] Call stack overflow" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnLookInShop, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnLookInShop);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
|
||||
LuaScriptInterface::pushUserdata<const ItemType>(L, itemType);
|
||||
LuaScriptInterface::setMetatable(L, -1, "ItemType");
|
||||
|
||||
lua_pushnumber(L, count);
|
||||
|
||||
return scriptInterface.callFunction(3);
|
||||
}
|
||||
|
||||
bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder)
|
||||
{
|
||||
// Player:onMoveItem(item, count, fromPosition, toPosition) or Player.onMoveItem(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder)
|
||||
if (info.playerOnMoveItem == -1) {
|
||||
if (playerOnMoveItem == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -522,10 +512,10 @@ bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, c
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnMoveItem, &scriptInterface);
|
||||
env->setScriptId(playerOnMoveItem, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnMoveItem);
|
||||
scriptInterface.pushFunction(playerOnMoveItem);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -546,7 +536,7 @@ bool Events::eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, c
|
||||
void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder)
|
||||
{
|
||||
// Player:onItemMoved(item, count, fromPosition, toPosition) or Player.onItemMoved(self, item, count, fromPosition, toPosition, fromCylinder, toCylinder)
|
||||
if (info.playerOnItemMoved == -1) {
|
||||
if (playerOnItemMoved == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -556,10 +546,10 @@ void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count,
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnItemMoved, &scriptInterface);
|
||||
env->setScriptId(playerOnItemMoved, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnItemMoved);
|
||||
scriptInterface.pushFunction(playerOnItemMoved);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -580,7 +570,7 @@ void Events::eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count,
|
||||
bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition)
|
||||
{
|
||||
// Player:onMoveCreature(creature, fromPosition, toPosition) or Player.onMoveCreature(self, creature, fromPosition, toPosition)
|
||||
if (info.playerOnMoveCreature == -1) {
|
||||
if (playerOnMoveCreature == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -590,10 +580,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnMoveCreature, &scriptInterface);
|
||||
env->setScriptId(playerOnMoveCreature, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnMoveCreature);
|
||||
scriptInterface.pushFunction(playerOnMoveCreature);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -607,42 +597,10 @@ bool Events::eventPlayerOnMoveCreature(Player* player, Creature* creature, const
|
||||
return scriptInterface.callFunction(4);
|
||||
}
|
||||
|
||||
void Events::eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation)
|
||||
{
|
||||
// Player:onReportRuleViolation(targetName, reportType, reportReason, comment, translation)
|
||||
if (info.playerOnReportRuleViolation == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scriptInterface.reserveScriptEnv()) {
|
||||
std::cout << "[Error - Events::eventPlayerOnReportRuleViolation] Call stack overflow" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnReportRuleViolation, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnReportRuleViolation);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
|
||||
LuaScriptInterface::pushString(L, targetName);
|
||||
|
||||
lua_pushnumber(L, reportType);
|
||||
lua_pushnumber(L, reportReason);
|
||||
|
||||
LuaScriptInterface::pushString(L, comment);
|
||||
LuaScriptInterface::pushString(L, translation);
|
||||
|
||||
scriptInterface.callVoidFunction(6);
|
||||
}
|
||||
|
||||
bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position, uint8_t category)
|
||||
bool Events::eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position)
|
||||
{
|
||||
// Player:onReportBug(message, position, category)
|
||||
if (info.playerOnReportBug == -1) {
|
||||
if (playerOnReportBug == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -652,25 +610,24 @@ bool Events::eventPlayerOnReportBug(Player* player, const std::string& message,
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnReportBug, &scriptInterface);
|
||||
env->setScriptId(playerOnReportBug, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnReportBug);
|
||||
scriptInterface.pushFunction(playerOnReportBug);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
|
||||
LuaScriptInterface::pushString(L, message);
|
||||
LuaScriptInterface::pushPosition(L, position);
|
||||
lua_pushnumber(L, category);
|
||||
|
||||
return scriptInterface.callFunction(4);
|
||||
return scriptInterface.callFunction(3);
|
||||
}
|
||||
|
||||
bool Events::eventPlayerOnTurn(Player* player, Direction direction)
|
||||
{
|
||||
// Player:onTurn(direction) or Player.onTurn(self, direction)
|
||||
if (info.playerOnTurn == -1) {
|
||||
if (playerOnTurn == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -680,10 +637,10 @@ bool Events::eventPlayerOnTurn(Player* player, Direction direction)
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnTurn, &scriptInterface);
|
||||
env->setScriptId(playerOnTurn, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnTurn);
|
||||
scriptInterface.pushFunction(playerOnTurn);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -696,7 +653,7 @@ bool Events::eventPlayerOnTurn(Player* player, Direction direction)
|
||||
bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* item)
|
||||
{
|
||||
// Player:onTradeRequest(target, item)
|
||||
if (info.playerOnTradeRequest == -1) {
|
||||
if (playerOnTradeRequest == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -706,10 +663,10 @@ bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* ite
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnTradeRequest, &scriptInterface);
|
||||
env->setScriptId(playerOnTradeRequest, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnTradeRequest);
|
||||
scriptInterface.pushFunction(playerOnTradeRequest);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -726,7 +683,7 @@ bool Events::eventPlayerOnTradeRequest(Player* player, Player* target, Item* ite
|
||||
bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem)
|
||||
{
|
||||
// Player:onTradeAccept(target, item, targetItem)
|
||||
if (info.playerOnTradeAccept == -1) {
|
||||
if (playerOnTradeAccept == -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -736,10 +693,10 @@ bool Events::eventPlayerOnTradeAccept(Player* player, Player* target, Item* item
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnTradeAccept, &scriptInterface);
|
||||
env->setScriptId(playerOnTradeAccept, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnTradeAccept);
|
||||
scriptInterface.pushFunction(playerOnTradeAccept);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -760,7 +717,7 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
|
||||
{
|
||||
// Player:onGainExperience(source, exp, rawExp)
|
||||
// rawExp gives the original exp which is not multiplied
|
||||
if (info.playerOnGainExperience == -1) {
|
||||
if (playerOnGainExperience == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -770,10 +727,10 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnGainExperience, &scriptInterface);
|
||||
env->setScriptId(playerOnGainExperience, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnGainExperience);
|
||||
scriptInterface.pushFunction(playerOnGainExperience);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -781,7 +738,8 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
|
||||
if (source) {
|
||||
LuaScriptInterface::pushUserdata<Creature>(L, source);
|
||||
LuaScriptInterface::setCreatureMetatable(L, -1, source);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
@ -790,7 +748,8 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
|
||||
|
||||
if (scriptInterface.protectedCall(L, 4, 1) != 0) {
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
@ -801,7 +760,7 @@ void Events::eventPlayerOnGainExperience(Player* player, Creature* source, uint6
|
||||
void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
|
||||
{
|
||||
// Player:onLoseExperience(exp)
|
||||
if (info.playerOnLoseExperience == -1) {
|
||||
if (playerOnLoseExperience == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -811,10 +770,10 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnLoseExperience, &scriptInterface);
|
||||
env->setScriptId(playerOnLoseExperience, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnLoseExperience);
|
||||
scriptInterface.pushFunction(playerOnLoseExperience);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -823,7 +782,8 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
|
||||
|
||||
if (scriptInterface.protectedCall(L, 2, 1) != 0) {
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
exp = LuaScriptInterface::getNumber<uint64_t>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
@ -834,7 +794,7 @@ void Events::eventPlayerOnLoseExperience(Player* player, uint64_t& exp)
|
||||
void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries)
|
||||
{
|
||||
// Player:onGainSkillTries(skill, tries)
|
||||
if (info.playerOnGainSkillTries == -1) {
|
||||
if (playerOnGainSkillTries == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -844,10 +804,10 @@ void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.playerOnGainSkillTries, &scriptInterface);
|
||||
env->setScriptId(playerOnGainSkillTries, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.playerOnGainSkillTries);
|
||||
scriptInterface.pushFunction(playerOnGainSkillTries);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Player>(L, player);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
@ -857,37 +817,11 @@ void Events::eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_
|
||||
|
||||
if (scriptInterface.protectedCall(L, 3, 1) != 0) {
|
||||
LuaScriptInterface::reportError(nullptr, LuaScriptInterface::popString(L));
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
tries = LuaScriptInterface::getNumber<uint64_t>(L, -1);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
scriptInterface.resetScriptEnv();
|
||||
}
|
||||
|
||||
void Events::eventMonsterOnDropLoot(Monster* monster, Container* corpse)
|
||||
{
|
||||
// Monster:onDropLoot(corpse)
|
||||
if (info.monsterOnDropLoot == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scriptInterface.reserveScriptEnv()) {
|
||||
std::cout << "[Error - Events::eventMonsterOnDropLoot] Call stack overflow" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
ScriptEnvironment* env = scriptInterface.getScriptEnv();
|
||||
env->setScriptId(info.monsterOnDropLoot, &scriptInterface);
|
||||
|
||||
lua_State* L = scriptInterface.getLuaState();
|
||||
scriptInterface.pushFunction(info.monsterOnDropLoot);
|
||||
|
||||
LuaScriptInterface::pushUserdata<Monster>(L, monster);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Monster");
|
||||
|
||||
LuaScriptInterface::pushUserdata<Container>(L, corpse);
|
||||
LuaScriptInterface::setMetatable(L, -1, "Container");
|
||||
|
||||
return scriptInterface.callVoidFunction(2);
|
||||
}
|
||||
}
|
124
src/events.h
124
src/events.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Copyright (C) 2016 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -28,80 +28,68 @@ class Tile;
|
||||
|
||||
class Events
|
||||
{
|
||||
struct EventsInfo {
|
||||
// Creature
|
||||
int32_t creatureOnChangeOutfit = -1;
|
||||
int32_t creatureOnAreaCombat = -1;
|
||||
int32_t creatureOnTargetCombat = -1;
|
||||
public:
|
||||
Events();
|
||||
|
||||
// Party
|
||||
int32_t partyOnJoin = -1;
|
||||
int32_t partyOnLeave = -1;
|
||||
int32_t partyOnDisband = -1;
|
||||
int32_t partyOnShareExperience = -1;
|
||||
void clear();
|
||||
bool load();
|
||||
|
||||
// Player
|
||||
int32_t playerOnBrowseField = -1;
|
||||
int32_t playerOnLook = -1;
|
||||
int32_t playerOnLookInBattleList = -1;
|
||||
int32_t playerOnLookInTrade = -1;
|
||||
int32_t playerOnLookInShop = -1;
|
||||
int32_t playerOnMoveItem = -1;
|
||||
int32_t playerOnItemMoved = -1;
|
||||
int32_t playerOnMoveCreature = -1;
|
||||
int32_t playerOnReportRuleViolation = -1;
|
||||
int32_t playerOnReportBug = -1;
|
||||
int32_t playerOnTurn = -1;
|
||||
int32_t playerOnTradeRequest = -1;
|
||||
int32_t playerOnTradeAccept = -1;
|
||||
int32_t playerOnGainExperience = -1;
|
||||
int32_t playerOnLoseExperience = -1;
|
||||
int32_t playerOnGainSkillTries = -1;
|
||||
// Creature
|
||||
bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit);
|
||||
ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive);
|
||||
ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target);
|
||||
|
||||
// Monster
|
||||
int32_t monsterOnDropLoot = -1;
|
||||
};
|
||||
// Party
|
||||
bool eventPartyOnJoin(Party* party, Player* player);
|
||||
bool eventPartyOnLeave(Party* party, Player* player);
|
||||
bool eventPartyOnDisband(Party* party);
|
||||
void eventPartyOnShareExperience(Party* party, uint64_t& exp);
|
||||
|
||||
public:
|
||||
Events();
|
||||
// Player
|
||||
void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance);
|
||||
void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance);
|
||||
void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance);
|
||||
bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
|
||||
void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
|
||||
bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition);
|
||||
void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation);
|
||||
bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position);
|
||||
bool eventPlayerOnTurn(Player* player, Direction direction);
|
||||
bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item);
|
||||
bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem);
|
||||
void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp);
|
||||
void eventPlayerOnLoseExperience(Player* player, uint64_t& exp);
|
||||
void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries);
|
||||
|
||||
bool load();
|
||||
private:
|
||||
LuaScriptInterface scriptInterface;
|
||||
|
||||
// Creature
|
||||
bool eventCreatureOnChangeOutfit(Creature* creature, const Outfit_t& outfit);
|
||||
ReturnValue eventCreatureOnAreaCombat(Creature* creature, Tile* tile, bool aggressive);
|
||||
ReturnValue eventCreatureOnTargetCombat(Creature* creature, Creature* target);
|
||||
// Creature
|
||||
int32_t creatureOnChangeOutfit;
|
||||
int32_t creatureOnAreaCombat;
|
||||
int32_t creatureOnTargetCombat;
|
||||
|
||||
// Party
|
||||
bool eventPartyOnJoin(Party* party, Player* player);
|
||||
bool eventPartyOnLeave(Party* party, Player* player);
|
||||
bool eventPartyOnDisband(Party* party);
|
||||
void eventPartyOnShareExperience(Party* party, uint64_t& exp);
|
||||
// Party
|
||||
int32_t partyOnJoin;
|
||||
int32_t partyOnLeave;
|
||||
int32_t partyOnDisband;
|
||||
int32_t partyOnShareExperience;
|
||||
|
||||
// Player
|
||||
bool eventPlayerOnBrowseField(Player* player, const Position& position);
|
||||
void eventPlayerOnLook(Player* player, const Position& position, Thing* thing, uint8_t stackpos, int32_t lookDistance);
|
||||
void eventPlayerOnLookInBattleList(Player* player, Creature* creature, int32_t lookDistance);
|
||||
void eventPlayerOnLookInTrade(Player* player, Player* partner, Item* item, int32_t lookDistance);
|
||||
bool eventPlayerOnLookInShop(Player* player, const ItemType* itemType, uint8_t count);
|
||||
bool eventPlayerOnMoveItem(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
|
||||
void eventPlayerOnItemMoved(Player* player, Item* item, uint16_t count, const Position& fromPosition, const Position& toPosition, Cylinder* fromCylinder, Cylinder* toCylinder);
|
||||
bool eventPlayerOnMoveCreature(Player* player, Creature* creature, const Position& fromPosition, const Position& toPosition);
|
||||
void eventPlayerOnReportRuleViolation(Player* player, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation);
|
||||
bool eventPlayerOnReportBug(Player* player, const std::string& message, const Position& position, uint8_t category);
|
||||
bool eventPlayerOnTurn(Player* player, Direction direction);
|
||||
bool eventPlayerOnTradeRequest(Player* player, Player* target, Item* item);
|
||||
bool eventPlayerOnTradeAccept(Player* player, Player* target, Item* item, Item* targetItem);
|
||||
void eventPlayerOnGainExperience(Player* player, Creature* source, uint64_t& exp, uint64_t rawExp);
|
||||
void eventPlayerOnLoseExperience(Player* player, uint64_t& exp);
|
||||
void eventPlayerOnGainSkillTries(Player* player, skills_t skill, uint64_t& tries);
|
||||
|
||||
// Monster
|
||||
void eventMonsterOnDropLoot(Monster* monster, Container* corpse);
|
||||
|
||||
private:
|
||||
LuaScriptInterface scriptInterface;
|
||||
EventsInfo info;
|
||||
// Player
|
||||
int32_t playerOnLook;
|
||||
int32_t playerOnLookInBattleList;
|
||||
int32_t playerOnLookInTrade;
|
||||
int32_t playerOnMoveItem;
|
||||
int32_t playerOnItemMoved;
|
||||
int32_t playerOnMoveCreature;
|
||||
int32_t playerOnReportRuleViolation;
|
||||
int32_t playerOnReportBug;
|
||||
int32_t playerOnTurn;
|
||||
int32_t playerOnTradeRequest;
|
||||
int32_t playerOnTradeAccept;
|
||||
int32_t playerOnGainExperience;
|
||||
int32_t playerOnLoseExperience;
|
||||
int32_t playerOnGainSkillTries;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -19,106 +19,387 @@
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#include <stack>
|
||||
#include "fileloader.h"
|
||||
|
||||
|
||||
namespace OTB {
|
||||
|
||||
constexpr Identifier wildcard = {{'\0', '\0', '\0', '\0'}};
|
||||
|
||||
Loader::Loader(const std::string& fileName, const Identifier& acceptedIdentifier):
|
||||
fileContents(fileName)
|
||||
FileLoader::~FileLoader()
|
||||
{
|
||||
constexpr auto minimalSize = sizeof(Identifier) + sizeof(Node::START) + sizeof(Node::type) + sizeof(Node::END);
|
||||
if (fileContents.size() <= minimalSize) {
|
||||
throw InvalidOTBFormat{};
|
||||
if (file) {
|
||||
fclose(file);
|
||||
file = nullptr;
|
||||
}
|
||||
|
||||
Identifier fileIdentifier;
|
||||
std::copy(fileContents.begin(), fileContents.begin() + fileIdentifier.size(), fileIdentifier.begin());
|
||||
if (fileIdentifier != acceptedIdentifier && fileIdentifier != wildcard) {
|
||||
throw InvalidOTBFormat{};
|
||||
NodeStruct::clearNet(root);
|
||||
delete[] buffer;
|
||||
|
||||
for (auto& i : cached_data) {
|
||||
delete[] i.data;
|
||||
}
|
||||
}
|
||||
|
||||
using NodeStack = std::stack<Node*, std::vector<Node*>>;
|
||||
static Node& getCurrentNode(const NodeStack& nodeStack) {
|
||||
if (nodeStack.empty()) {
|
||||
throw InvalidOTBFormat{};
|
||||
bool FileLoader::openFile(const char* filename, const char* accept_identifier)
|
||||
{
|
||||
file = fopen(filename, "rb");
|
||||
if (!file) {
|
||||
lastError = ERROR_CAN_NOT_OPEN;
|
||||
return false;
|
||||
}
|
||||
return *nodeStack.top();
|
||||
|
||||
char identifier[4];
|
||||
if (fread(identifier, 1, 4, file) < 4) {
|
||||
fclose(file);
|
||||
file = nullptr;
|
||||
lastError = ERROR_EOF;
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first four bytes must either match the accept identifier or be 0x00000000 (wildcard)
|
||||
if (memcmp(identifier, accept_identifier, 4) != 0 && memcmp(identifier, "\0\0\0\0", 4) != 0) {
|
||||
fclose(file);
|
||||
file = nullptr;
|
||||
lastError = ERROR_INVALID_FILE_VERSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
fseek(file, 0, SEEK_END);
|
||||
int32_t file_size = ftell(file);
|
||||
cache_size = std::min<uint32_t>(32768, std::max<uint32_t>(file_size / 20, 8192)) & ~0x1FFF;
|
||||
|
||||
if (!safeSeek(4)) {
|
||||
lastError = ERROR_INVALID_FORMAT;
|
||||
return false;
|
||||
}
|
||||
|
||||
delete root;
|
||||
root = new NodeStruct();
|
||||
root->start = 4;
|
||||
|
||||
int32_t byte;
|
||||
if (safeSeek(4) && readByte(byte) && byte == NODE_START) {
|
||||
return parseNode(root);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const Node& Loader::parseTree()
|
||||
bool FileLoader::parseNode(NODE node)
|
||||
{
|
||||
auto it = fileContents.begin() + sizeof(Identifier);
|
||||
if (static_cast<uint8_t>(*it) != Node::START) {
|
||||
throw InvalidOTBFormat{};
|
||||
}
|
||||
root.type = *(++it);
|
||||
root.propsBegin = ++it;
|
||||
NodeStack parseStack;
|
||||
parseStack.push(&root);
|
||||
int32_t byte, pos;
|
||||
NODE currentNode = node;
|
||||
|
||||
for (; it != fileContents.end(); ++it) {
|
||||
switch(static_cast<uint8_t>(*it)) {
|
||||
case Node::START: {
|
||||
auto& currentNode = getCurrentNode(parseStack);
|
||||
if (currentNode.children.empty()) {
|
||||
currentNode.propsEnd = it;
|
||||
}
|
||||
currentNode.children.emplace_back();
|
||||
auto& child = currentNode.children.back();
|
||||
if (++it == fileContents.end()) {
|
||||
throw InvalidOTBFormat{};
|
||||
}
|
||||
child.type = *it;
|
||||
child.propsBegin = it + sizeof(Node::type);
|
||||
parseStack.push(&child);
|
||||
break;
|
||||
while (readByte(byte)) {
|
||||
currentNode->type = byte;
|
||||
bool setPropsSize = false;
|
||||
|
||||
while (true) {
|
||||
if (!readByte(byte)) {
|
||||
return false;
|
||||
}
|
||||
case Node::END: {
|
||||
auto& currentNode = getCurrentNode(parseStack);
|
||||
if (currentNode.children.empty()) {
|
||||
currentNode.propsEnd = it;
|
||||
|
||||
bool skipNode = false;
|
||||
|
||||
switch (byte) {
|
||||
case NODE_START: {
|
||||
//child node start
|
||||
if (!safeTell(pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
NODE childNode = new NodeStruct();
|
||||
childNode->start = pos;
|
||||
currentNode->propsSize = pos - currentNode->start - 2;
|
||||
currentNode->child = childNode;
|
||||
|
||||
setPropsSize = true;
|
||||
|
||||
if (!parseNode(childNode)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
parseStack.pop();
|
||||
break;
|
||||
}
|
||||
case Node::ESCAPE: {
|
||||
if (++it == fileContents.end()) {
|
||||
throw InvalidOTBFormat{};
|
||||
|
||||
case NODE_END: {
|
||||
//current node end
|
||||
if (!setPropsSize) {
|
||||
if (!safeTell(pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
currentNode->propsSize = pos - currentNode->start - 2;
|
||||
}
|
||||
|
||||
if (!readByte(byte)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (byte) {
|
||||
case NODE_START: {
|
||||
//starts next node
|
||||
if (!safeTell(pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
skipNode = true;
|
||||
NODE nextNode = new NodeStruct();
|
||||
nextNode->start = pos;
|
||||
currentNode->next = nextNode;
|
||||
currentNode = nextNode;
|
||||
break;
|
||||
}
|
||||
|
||||
case NODE_END:
|
||||
return safeTell(pos) && safeSeek(pos);
|
||||
|
||||
default:
|
||||
lastError = ERROR_INVALID_FORMAT;
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case ESCAPE_CHAR: {
|
||||
if (!readByte(byte)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
|
||||
if (skipNode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!parseStack.empty()) {
|
||||
throw InvalidOTBFormat{};
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint8_t* FileLoader::getProps(const NODE node, size_t& size)
|
||||
{
|
||||
if (!node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (node->propsSize >= buffer_size) {
|
||||
delete[] buffer;
|
||||
|
||||
while (node->propsSize >= buffer_size) {
|
||||
buffer_size *= 2;
|
||||
}
|
||||
|
||||
buffer = new uint8_t[buffer_size];
|
||||
}
|
||||
|
||||
//get buffer
|
||||
if (!readBytes(node->propsSize, node->start + 2)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//unscape buffer
|
||||
size_t j = 0;
|
||||
bool escaped = false;
|
||||
for (uint32_t i = 0; i < node->propsSize; ++i, ++j) {
|
||||
if (buffer[i] == ESCAPE_CHAR) {
|
||||
//escape char found, skip it and write next
|
||||
buffer[j] = buffer[++i];
|
||||
//is neede a displacement for next bytes
|
||||
escaped = true;
|
||||
} else if (escaped) {
|
||||
//perform that displacement
|
||||
buffer[j] = buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
size = j;
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool FileLoader::getProps(const NODE node, PropStream& props)
|
||||
{
|
||||
size_t size;
|
||||
if (const uint8_t* a = getProps(node, size)) {
|
||||
props.init(reinterpret_cast<const char*>(a), size); // does not break strict aliasing
|
||||
return true;
|
||||
}
|
||||
|
||||
props.init(nullptr, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
NODE FileLoader::getChildNode(const NODE parent, uint32_t& type)
|
||||
{
|
||||
if (parent) {
|
||||
NODE child = parent->child;
|
||||
if (child) {
|
||||
type = child->type;
|
||||
}
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
type = root->type;
|
||||
return root;
|
||||
}
|
||||
|
||||
bool Loader::getProps(const Node& node, PropStream& props)
|
||||
NODE FileLoader::getNextNode(const NODE prev, uint32_t& type)
|
||||
{
|
||||
auto size = std::distance(node.propsBegin, node.propsEnd);
|
||||
if (size == 0) {
|
||||
if (!prev) {
|
||||
return NO_NODE;
|
||||
}
|
||||
|
||||
NODE next = prev->next;
|
||||
if (next) {
|
||||
type = next->type;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
inline bool FileLoader::readByte(int32_t& value)
|
||||
{
|
||||
if (cache_index == NO_VALID_CACHE) {
|
||||
lastError = ERROR_CACHE_ERROR;
|
||||
return false;
|
||||
}
|
||||
propBuffer.resize(size);
|
||||
bool lastEscaped = false;
|
||||
|
||||
auto escapedPropEnd = std::copy_if(node.propsBegin, node.propsEnd, propBuffer.begin(), [&lastEscaped](const char& byte) {
|
||||
lastEscaped = byte == static_cast<char>(Node::ESCAPE) && !lastEscaped;
|
||||
return !lastEscaped;
|
||||
});
|
||||
props.init(&propBuffer[0], std::distance(propBuffer.begin(), escapedPropEnd));
|
||||
if (cache_offset >= cached_data[cache_index].size) {
|
||||
int32_t pos = cache_offset + cached_data[cache_index].base;
|
||||
int32_t tmp = getCacheBlock(pos);
|
||||
if (tmp < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cache_index = tmp;
|
||||
cache_offset = pos - cached_data[cache_index].base;
|
||||
if (cache_offset >= cached_data[cache_index].size) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
value = cached_data[cache_index].data[cache_offset++];
|
||||
return true;
|
||||
}
|
||||
|
||||
} //namespace OTB
|
||||
inline bool FileLoader::readBytes(uint32_t size, int32_t pos)
|
||||
{
|
||||
//seek at pos
|
||||
uint32_t remain = size;
|
||||
uint8_t* buf = this->buffer;
|
||||
do {
|
||||
//prepare cache
|
||||
uint32_t i = getCacheBlock(pos);
|
||||
if (i == NO_VALID_CACHE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cache_index = i;
|
||||
cache_offset = pos - cached_data[i].base;
|
||||
|
||||
//get maximum read block size and calculate remaining bytes
|
||||
uint32_t reading = std::min<int32_t>(remain, cached_data[i].size - cache_offset);
|
||||
remain -= reading;
|
||||
|
||||
//read it
|
||||
memcpy(buf, cached_data[cache_index].data + cache_offset, reading);
|
||||
|
||||
//update variables
|
||||
cache_offset += reading;
|
||||
buf += reading;
|
||||
pos += reading;
|
||||
} while (remain > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool FileLoader::safeSeek(uint32_t pos)
|
||||
{
|
||||
uint32_t i = getCacheBlock(pos);
|
||||
if (i == NO_VALID_CACHE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
cache_index = i;
|
||||
cache_offset = pos - cached_data[i].base;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool FileLoader::safeTell(int32_t& pos)
|
||||
{
|
||||
if (cache_index == NO_VALID_CACHE) {
|
||||
lastError = ERROR_CACHE_ERROR;
|
||||
return false;
|
||||
}
|
||||
|
||||
pos = cached_data[cache_index].base + cache_offset - 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline uint32_t FileLoader::getCacheBlock(uint32_t pos)
|
||||
{
|
||||
bool found = false;
|
||||
uint32_t i, base_pos = pos & ~(cache_size - 1);
|
||||
|
||||
for (i = 0; i < CACHE_BLOCKS; i++) {
|
||||
if (cached_data[i].loaded) {
|
||||
if (cached_data[i].base == base_pos) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
i = loadCacheBlock(pos);
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
int32_t FileLoader::loadCacheBlock(uint32_t pos)
|
||||
{
|
||||
int32_t i, loading_cache = -1, base_pos = pos & ~(cache_size - 1);
|
||||
|
||||
for (i = 0; i < CACHE_BLOCKS; i++) {
|
||||
if (!cached_data[i].loaded) {
|
||||
loading_cache = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loading_cache == -1) {
|
||||
for (i = 0; i < CACHE_BLOCKS; i++) {
|
||||
if (std::abs(static_cast<long>(cached_data[i].base) - base_pos) > static_cast<long>(2 * cache_size)) {
|
||||
loading_cache = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (loading_cache == -1) {
|
||||
loading_cache = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (cached_data[loading_cache].data == nullptr) {
|
||||
cached_data[loading_cache].data = new uint8_t[cache_size];
|
||||
}
|
||||
|
||||
cached_data[loading_cache].base = base_pos;
|
||||
|
||||
if (fseek(file, cached_data[loading_cache].base, SEEK_SET) != 0) {
|
||||
lastError = ERROR_SEEK_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint32_t size = fread(cached_data[loading_cache].data, 1, cache_size, file);
|
||||
cached_data[loading_cache].size = size;
|
||||
|
||||
if (size < (pos - cached_data[loading_cache].base)) {
|
||||
lastError = ERROR_SEEK_ERROR;
|
||||
return -1;
|
||||
}
|
||||
|
||||
cached_data[loading_cache].loaded = 1;
|
||||
return loading_cache;
|
||||
}
|
||||
|
168
src/fileloader.h
168
src/fileloader.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -22,53 +22,133 @@
|
||||
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
|
||||
struct NodeStruct;
|
||||
|
||||
typedef NodeStruct* NODE;
|
||||
|
||||
struct NodeStruct {
|
||||
uint32_t start = 0;
|
||||
uint32_t propsSize = 0;
|
||||
uint32_t type = 0;
|
||||
NodeStruct* next = nullptr;
|
||||
NodeStruct* child = nullptr;
|
||||
|
||||
static void clearNet(NodeStruct* root) {
|
||||
if (root) {
|
||||
clearChild(root);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static void clearNext(NodeStruct* node) {
|
||||
NodeStruct* deleteNode = node;
|
||||
NodeStruct* nextNode;
|
||||
|
||||
while (deleteNode) {
|
||||
if (deleteNode->child) {
|
||||
clearChild(deleteNode->child);
|
||||
}
|
||||
|
||||
nextNode = deleteNode->next;
|
||||
delete deleteNode;
|
||||
deleteNode = nextNode;
|
||||
}
|
||||
}
|
||||
|
||||
static void clearChild(NodeStruct* node) {
|
||||
if (node->child) {
|
||||
clearChild(node->child);
|
||||
}
|
||||
|
||||
if (node->next) {
|
||||
clearNext(node->next);
|
||||
}
|
||||
|
||||
delete node;
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr auto NO_NODE = nullptr;
|
||||
|
||||
enum FILELOADER_ERRORS {
|
||||
ERROR_NONE,
|
||||
ERROR_INVALID_FILE_VERSION,
|
||||
ERROR_CAN_NOT_OPEN,
|
||||
ERROR_CAN_NOT_CREATE,
|
||||
ERROR_EOF,
|
||||
ERROR_SEEK_ERROR,
|
||||
ERROR_NOT_OPEN,
|
||||
ERROR_INVALID_NODE,
|
||||
ERROR_INVALID_FORMAT,
|
||||
ERROR_TELL_ERROR,
|
||||
ERROR_COULDNOTWRITE,
|
||||
ERROR_CACHE_ERROR,
|
||||
};
|
||||
|
||||
class PropStream;
|
||||
|
||||
namespace OTB {
|
||||
using MappedFile = boost::iostreams::mapped_file_source;
|
||||
using ContentIt = MappedFile::iterator;
|
||||
using Identifier = std::array<char, 4>;
|
||||
|
||||
struct Node
|
||||
class FileLoader
|
||||
{
|
||||
using ChildrenVector = std::vector<Node>;
|
||||
public:
|
||||
FileLoader() = default;
|
||||
~FileLoader();
|
||||
|
||||
ChildrenVector children;
|
||||
ContentIt propsBegin;
|
||||
ContentIt propsEnd;
|
||||
uint8_t type;
|
||||
enum NodeChar: uint8_t
|
||||
{
|
||||
ESCAPE = 0xFD,
|
||||
START = 0xFE,
|
||||
END = 0xFF,
|
||||
};
|
||||
// non-copyable
|
||||
FileLoader(const FileLoader&) = delete;
|
||||
FileLoader& operator=(const FileLoader&) = delete;
|
||||
|
||||
bool openFile(const char* filename, const char* identifier);
|
||||
const uint8_t* getProps(const NODE, size_t& size);
|
||||
bool getProps(const NODE, PropStream& props);
|
||||
NODE getChildNode(const NODE parent, uint32_t& type);
|
||||
NODE getNextNode(const NODE prev, uint32_t& type);
|
||||
|
||||
FILELOADER_ERRORS getError() const {
|
||||
return lastError;
|
||||
}
|
||||
|
||||
protected:
|
||||
enum SPECIAL_BYTES {
|
||||
ESCAPE_CHAR = 0xFD,
|
||||
NODE_START = 0xFE,
|
||||
NODE_END = 0xFF,
|
||||
};
|
||||
|
||||
bool parseNode(NODE node);
|
||||
|
||||
inline bool readByte(int32_t& value);
|
||||
inline bool readBytes(uint32_t size, int32_t pos);
|
||||
inline bool safeSeek(uint32_t pos);
|
||||
inline bool safeTell(int32_t& pos);
|
||||
|
||||
protected:
|
||||
struct cache {
|
||||
uint8_t* data;
|
||||
uint32_t loaded;
|
||||
uint32_t base;
|
||||
uint32_t size;
|
||||
};
|
||||
|
||||
static constexpr int32_t CACHE_BLOCKS = 3;
|
||||
cache cached_data[CACHE_BLOCKS] = {};
|
||||
|
||||
uint8_t* buffer = new uint8_t[1024];
|
||||
NODE root = nullptr;
|
||||
FILE* file = nullptr;
|
||||
|
||||
FILELOADER_ERRORS lastError = ERROR_NONE;
|
||||
uint32_t buffer_size = 1024;
|
||||
|
||||
uint32_t cache_size = 0;
|
||||
static constexpr uint32_t NO_VALID_CACHE = std::numeric_limits<uint32_t>::max();
|
||||
uint32_t cache_index = NO_VALID_CACHE;
|
||||
uint32_t cache_offset = NO_VALID_CACHE;
|
||||
|
||||
inline uint32_t getCacheBlock(uint32_t pos);
|
||||
int32_t loadCacheBlock(uint32_t pos);
|
||||
};
|
||||
|
||||
struct LoadError : std::exception {
|
||||
const char* what() const noexcept override = 0;
|
||||
};
|
||||
|
||||
struct InvalidOTBFormat final : LoadError {
|
||||
const char* what() const noexcept override {
|
||||
return "Invalid OTBM file format";
|
||||
}
|
||||
};
|
||||
|
||||
class Loader {
|
||||
MappedFile fileContents;
|
||||
Node root;
|
||||
std::vector<char> propBuffer;
|
||||
public:
|
||||
Loader(const std::string& fileName, const Identifier& acceptedIdentifier);
|
||||
bool getProps(const Node& node, PropStream& props);
|
||||
const Node& parseTree();
|
||||
};
|
||||
|
||||
} //namespace OTB
|
||||
|
||||
class PropStream
|
||||
{
|
||||
public:
|
||||
@ -120,7 +200,7 @@ class PropStream
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
const char* p = nullptr;
|
||||
const char* end = nullptr;
|
||||
};
|
||||
@ -160,7 +240,7 @@ class PropWriteStream
|
||||
std::copy(str.begin(), str.end(), std::back_inserter(buffer));
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
std::vector<char> buffer;
|
||||
};
|
||||
|
||||
|
2305
src/game.cpp
2305
src/game.cpp
File diff suppressed because it is too large
Load Diff
118
src/game.h
118
src/game.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -31,7 +31,6 @@
|
||||
#include "raids.h"
|
||||
#include "npc.h"
|
||||
#include "wildcardtree.h"
|
||||
#include "quests.h"
|
||||
|
||||
class ServiceManager;
|
||||
class Creature;
|
||||
@ -70,6 +69,22 @@ enum LightState_t {
|
||||
LIGHT_STATE_SUNRISE,
|
||||
};
|
||||
|
||||
struct RuleViolation {
|
||||
RuleViolation() = default;
|
||||
RuleViolation(uint32_t _reporterId, const std::string& _text) :
|
||||
reporterId(_reporterId),
|
||||
gamemasterId(0),
|
||||
text(_text),
|
||||
pending(true)
|
||||
{
|
||||
}
|
||||
|
||||
uint32_t reporterId;
|
||||
uint32_t gamemasterId;
|
||||
std::string text;
|
||||
bool pending;
|
||||
};
|
||||
|
||||
static constexpr int32_t EVENT_LIGHTINTERVAL = 10000;
|
||||
static constexpr int32_t EVENT_DECAYINTERVAL = 250;
|
||||
static constexpr int32_t EVENT_DECAY_BUCKETS = 4;
|
||||
@ -82,7 +97,7 @@ static constexpr int32_t EVENT_DECAY_BUCKETS = 4;
|
||||
class Game
|
||||
{
|
||||
public:
|
||||
Game();
|
||||
Game() = default;
|
||||
~Game();
|
||||
|
||||
// non-copyable
|
||||
@ -204,7 +219,7 @@ class Game
|
||||
* \param extendedPos If true, the creature will in first-hand be placed 2 tiles away
|
||||
* \param force If true, placing the creature will not fail because of obstacles (creatures/items)
|
||||
*/
|
||||
bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool forced = false);
|
||||
bool placeCreature(Creature* creature, const Position& pos, bool extendedPos = false, bool force = false);
|
||||
|
||||
/**
|
||||
* Remove Creature from the map.
|
||||
@ -229,7 +244,7 @@ class Game
|
||||
return playersRecord;
|
||||
}
|
||||
|
||||
LightInfo getWorldLightInfo() const;
|
||||
void getWorldLightInfo(LightInfo& lightInfo) const;
|
||||
|
||||
ReturnValue internalMoveCreature(Creature* creature, Direction direction, uint32_t flags = 0);
|
||||
ReturnValue internalMoveCreature(Creature& creature, Tile& toTile, uint32_t flags = 0);
|
||||
@ -307,19 +322,16 @@ class Game
|
||||
* \param text The text to say
|
||||
*/
|
||||
bool internalCreatureSay(Creature* creature, SpeakClasses type, const std::string& text,
|
||||
bool ghostMode, SpectatorVec* spectatorsPtr = nullptr, const Position* pos = nullptr);
|
||||
bool ghostMode, SpectatorVec* listPtr = nullptr, const Position* pos = nullptr);
|
||||
|
||||
void loadPlayersRecord();
|
||||
void checkPlayersRecord();
|
||||
|
||||
void sendGuildMotd(uint32_t playerId);
|
||||
void kickPlayer(uint32_t playerId, bool displayEffect);
|
||||
void playerReportBug(uint32_t playerId, const std::string& message, const Position& position, uint8_t category);
|
||||
void playerReportBug(uint32_t playerId, const std::string& message);
|
||||
void playerDebugAssert(uint32_t playerId, const std::string& assertLine, const std::string& date, const std::string& description, const std::string& comment);
|
||||
void playerAnswerModalWindow(uint32_t playerId, uint32_t modalWindowId, uint8_t button, uint8_t choice);
|
||||
void playerReportRuleViolation(uint32_t playerId, const std::string& targetName, uint8_t reportType, uint8_t reportReason, const std::string& comment, const std::string& translation);
|
||||
|
||||
bool internalStartTrade(Player* player, Player* tradePartner, Item* tradeItem);
|
||||
bool internalStartTrade(Player* player, Player* partner, Item* tradeItem);
|
||||
void internalCloseTrade(Player* player);
|
||||
bool playerBroadcastMessage(Player* player, const std::string& text) const;
|
||||
void broadcastMessage(const std::string& text, MessageClasses type) const;
|
||||
@ -328,11 +340,10 @@ class Game
|
||||
void playerMoveThing(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos,
|
||||
const Position& toPos, uint8_t count);
|
||||
void playerMoveCreatureByID(uint32_t playerId, uint32_t movingCreatureId, const Position& movingCreatureOrigPos, const Position& toPos);
|
||||
void playerMoveCreature(Player* player, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile);
|
||||
void playerMoveCreature(Player* playerId, Creature* movingCreature, const Position& movingCreatureOrigPos, Tile* toTile);
|
||||
void playerMoveItemByPlayerID(uint32_t playerId, const Position& fromPos, uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count);
|
||||
void playerMoveItem(Player* player, const Position& fromPos,
|
||||
uint16_t spriteId, uint8_t fromStackPos, const Position& toPos, uint8_t count, Item* item, Cylinder* toCylinder);
|
||||
void playerEquipItem(uint32_t playerId, uint16_t spriteId);
|
||||
void playerMove(uint32_t playerId, Direction direction);
|
||||
void playerCreatePrivateChannel(uint32_t playerId);
|
||||
void playerChannelInvite(uint32_t playerId, const std::string& name);
|
||||
@ -341,7 +352,6 @@ class Game
|
||||
void playerOpenChannel(uint32_t playerId, uint16_t channelId);
|
||||
void playerCloseChannel(uint32_t playerId, uint16_t channelId);
|
||||
void playerOpenPrivateChannel(uint32_t playerId, std::string& receiver);
|
||||
void playerCloseNpcChannel(uint32_t playerId);
|
||||
void playerReceivePing(uint32_t playerId);
|
||||
void playerReceivePingBack(uint32_t playerId);
|
||||
void playerAutoWalk(uint32_t playerId, const std::forward_list<Direction>& listDir);
|
||||
@ -355,33 +365,23 @@ class Game
|
||||
void playerUpdateContainer(uint32_t playerId, uint8_t cid);
|
||||
void playerRotateItem(uint32_t playerId, const Position& pos, uint8_t stackPos, const uint16_t spriteId);
|
||||
void playerWriteItem(uint32_t playerId, uint32_t windowTextId, const std::string& text);
|
||||
void playerBrowseField(uint32_t playerId, const Position& pos);
|
||||
void playerSeekInContainer(uint32_t playerId, uint8_t containerId, uint16_t index);
|
||||
void playerUpdateHouseWindow(uint32_t playerId, uint8_t listId, uint32_t windowTextId, const std::string& text);
|
||||
void playerRequestTrade(uint32_t playerId, const Position& pos, uint8_t stackPos,
|
||||
uint32_t tradePlayerId, uint16_t spriteId);
|
||||
void playerAcceptTrade(uint32_t playerId);
|
||||
void playerLookInTrade(uint32_t playerId, bool lookAtCounterOffer, uint8_t index);
|
||||
void playerPurchaseItem(uint32_t playerId, uint16_t spriteId, uint8_t count, uint8_t amount,
|
||||
bool ignoreCap = false, bool inBackpacks = false);
|
||||
void playerSellItem(uint32_t playerId, uint16_t spriteId, uint8_t count,
|
||||
uint8_t amount, bool ignoreEquipped = false);
|
||||
void playerCloseShop(uint32_t playerId);
|
||||
void playerLookInShop(uint32_t playerId, uint16_t spriteId, uint8_t count);
|
||||
void playerCloseTrade(uint32_t playerId);
|
||||
void playerSetAttackedCreature(uint32_t playerId, uint32_t creatureId);
|
||||
void playerFollowCreature(uint32_t playerId, uint32_t creatureId);
|
||||
void playerCancelAttackAndFollow(uint32_t playerId);
|
||||
void playerSetFightModes(uint32_t playerId, fightMode_t fightMode, bool chaseMode, bool secureMode);
|
||||
void playerSetFightModes(uint32_t playerId, fightMode_t fightMode, chaseMode_t chaseMode, bool secureMode);
|
||||
void playerLookAt(uint32_t playerId, const Position& pos, uint8_t stackPos);
|
||||
void playerLookInBattleList(uint32_t playerId, uint32_t creatureId);
|
||||
void playerRequestAddVip(uint32_t playerId, const std::string& name);
|
||||
void playerRequestRemoveVip(uint32_t playerId, uint32_t guid);
|
||||
void playerRequestEditVip(uint32_t playerId, uint32_t guid, const std::string& description, uint32_t icon, bool notify);
|
||||
void playerTurn(uint32_t playerId, Direction dir);
|
||||
void playerRequestOutfit(uint32_t playerId);
|
||||
void playerShowQuestLog(uint32_t playerId);
|
||||
void playerShowQuestLine(uint32_t playerId, uint16_t questId);
|
||||
void playerSay(uint32_t playerId, uint16_t channelId, SpeakClasses type,
|
||||
const std::string& receiver, const std::string& text);
|
||||
void playerChangeOutfit(uint32_t playerId, Outfit_t outfit);
|
||||
@ -391,18 +391,15 @@ class Game
|
||||
void playerPassPartyLeadership(uint32_t playerId, uint32_t newLeaderId);
|
||||
void playerLeaveParty(uint32_t playerId);
|
||||
void playerEnableSharedPartyExperience(uint32_t playerId, bool sharedExpActive);
|
||||
void playerToggleMount(uint32_t playerId, bool mount);
|
||||
void playerLeaveMarket(uint32_t playerId);
|
||||
void playerBrowseMarket(uint32_t playerId, uint16_t spriteId);
|
||||
void playerBrowseMarketOwnOffers(uint32_t playerId);
|
||||
void playerBrowseMarketOwnHistory(uint32_t playerId);
|
||||
void playerCreateMarketOffer(uint32_t playerId, uint8_t type, uint16_t spriteId, uint16_t amount, uint32_t price, bool anonymous);
|
||||
void playerCancelMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter);
|
||||
void playerAcceptMarketOffer(uint32_t playerId, uint32_t timestamp, uint16_t counter, uint16_t amount);
|
||||
|
||||
void playerProcessRuleViolationReport(uint32_t playerId, const std::string& name);
|
||||
void playerCloseRuleViolationReport(uint32_t playerId, const std::string& name);
|
||||
void playerCancelRuleViolationReport(uint32_t playerId);
|
||||
void playerReportRuleViolationReport(Player* player, const std::string& text);
|
||||
void playerContinueRuleViolationReport(Player* player, const std::string& text);
|
||||
void parsePlayerExtendedOpcode(uint32_t playerId, uint8_t opcode, const std::string& buffer);
|
||||
|
||||
std::forward_list<Item*> getMarketItemList(uint16_t wareId, uint16_t sufficientCount, DepotChest* depotChest, Inbox* inbox);
|
||||
void closeRuleViolationReport(Player* player);
|
||||
void cancelRuleViolationReport(Player* player);
|
||||
|
||||
static void updatePremium(Account& account);
|
||||
|
||||
@ -413,17 +410,14 @@ class Game
|
||||
|
||||
bool canThrowObjectTo(const Position& fromPos, const Position& toPos, bool checkLineOfSight = true,
|
||||
int32_t rangex = Map::maxClientViewportX, int32_t rangey = Map::maxClientViewportY) const;
|
||||
bool isSightClear(const Position& fromPos, const Position& toPos, bool floorCheck) const;
|
||||
bool isSightClear(const Position& fromPos, const Position& toPos, bool sameFloor) const;
|
||||
|
||||
void changeSpeed(Creature* creature, int32_t varSpeedDelta);
|
||||
void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& outfit);
|
||||
void internalCreatureChangeOutfit(Creature* creature, const Outfit_t& oufit);
|
||||
void internalCreatureChangeVisible(Creature* creature, bool visible);
|
||||
void changeLight(const Creature* creature);
|
||||
void updateCreatureSkull(const Creature* creature);
|
||||
void updateCreatureSkull(const Creature* player);
|
||||
void updatePlayerShield(Player* player);
|
||||
void updatePlayerHelpers(const Player& player);
|
||||
void updateCreatureType(Creature* creature);
|
||||
void updateCreatureWalkthrough(const Creature* creature);
|
||||
|
||||
GameState_t getGameState() const;
|
||||
void setGameState(GameState_t newState);
|
||||
@ -445,11 +439,14 @@ class Game
|
||||
|
||||
//animation help functions
|
||||
void addCreatureHealth(const Creature* target);
|
||||
static void addCreatureHealth(const SpectatorVec& spectators, const Creature* target);
|
||||
static void addCreatureHealth(const SpectatorVec& list, const Creature* target);
|
||||
void addMagicEffect(const Position& pos, uint8_t effect);
|
||||
static void addMagicEffect(const SpectatorVec& spectators, const Position& pos, uint8_t effect);
|
||||
static void addMagicEffect(const SpectatorVec& list, const Position& pos, uint8_t effect);
|
||||
void addDistanceEffect(const Position& fromPos, const Position& toPos, uint8_t effect);
|
||||
static void addDistanceEffect(const SpectatorVec& spectators, const Position& fromPos, const Position& toPos, uint8_t effect);
|
||||
static void addDistanceEffect(const SpectatorVec& list, const Position& fromPos, const Position& toPos, uint8_t effect);
|
||||
void addAnimatedText(const Position& pos, uint8_t color, const std::string& text);
|
||||
static void addAnimatedText(const SpectatorVec& list, const Position& pos, uint8_t color, const std::string& text);
|
||||
void addMonsterSayText(const Position& pos, const std::string& text);
|
||||
|
||||
void startDecay(Item* item);
|
||||
int32_t getLightHour() const {
|
||||
@ -465,8 +462,7 @@ class Game
|
||||
uint32_t getMotdNum() const { return motdNum; }
|
||||
void incrementMotdNum() { motdNum++; }
|
||||
|
||||
void sendOfflineTrainingDialog(Player* player);
|
||||
|
||||
const std::unordered_map<uint32_t, RuleViolation>& getRuleViolationReports() const { return ruleViolations; }
|
||||
const std::unordered_map<uint32_t, Player*>& getPlayers() const { return players; }
|
||||
const std::map<uint32_t, Npc*>& getNpcs() const { return npcs; }
|
||||
|
||||
@ -476,55 +472,45 @@ class Game
|
||||
void addNpc(Npc* npc);
|
||||
void removeNpc(Npc* npc);
|
||||
|
||||
void addMonster(Monster* monster);
|
||||
void removeMonster(Monster* monster);
|
||||
void addMonster(Monster* npc);
|
||||
void removeMonster(Monster* npc);
|
||||
|
||||
Guild* getGuild(uint32_t id) const;
|
||||
void addGuild(Guild* guild);
|
||||
void removeGuild(uint32_t guildId);
|
||||
void decreaseBrowseFieldRef(const Position& pos);
|
||||
|
||||
std::unordered_map<Tile*, Container*> browseFields;
|
||||
|
||||
void internalRemoveItems(std::vector<Item*> itemList, uint32_t amount, bool stackable);
|
||||
|
||||
BedItem* getBedBySleeper(uint32_t guid) const;
|
||||
void setBedSleeper(BedItem* bed, uint32_t guid);
|
||||
void removeBedSleeper(uint32_t guid);
|
||||
|
||||
Item* getUniqueItem(uint16_t uniqueId);
|
||||
bool addUniqueItem(uint16_t uniqueId, Item* item);
|
||||
void removeUniqueItem(uint16_t uniqueId);
|
||||
|
||||
bool reload(ReloadTypes_t reloadType);
|
||||
|
||||
Groups groups;
|
||||
Map map;
|
||||
Mounts mounts;
|
||||
Raids raids;
|
||||
Quests quests;
|
||||
|
||||
std::forward_list<Item*> toDecayItems;
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text);
|
||||
void playerWhisper(Player* player, const std::string& text);
|
||||
bool playerYell(Player* player, const std::string& text);
|
||||
bool playerSpeakTo(Player* player, SpeakClasses type, const std::string& receiver, const std::string& text);
|
||||
void playerSpeakToNpc(Player* player, const std::string& text);
|
||||
|
||||
void checkDecay();
|
||||
void internalDecayItem(Item* item);
|
||||
|
||||
//list of reported rule violations, for correct channel listing
|
||||
std::unordered_map<uint32_t, RuleViolation> ruleViolations;
|
||||
|
||||
std::unordered_map<uint32_t, Player*> players;
|
||||
std::unordered_map<std::string, Player*> mappedPlayerNames;
|
||||
std::unordered_map<uint32_t, Guild*> guilds;
|
||||
std::unordered_map<uint16_t, Item*> uniqueItems;
|
||||
std::map<uint32_t, uint32_t> stages;
|
||||
|
||||
std::list<Item*> decayItems[EVENT_DECAY_BUCKETS];
|
||||
std::list<Creature*> checkCreatureLists[EVENT_CREATURECOUNT];
|
||||
|
||||
std::forward_list<Item*> toDecayItems;
|
||||
|
||||
std::vector<Creature*> ToReleaseCreatures;
|
||||
std::vector<Item*> ToReleaseItems;
|
||||
|
||||
@ -540,8 +526,6 @@ class Game
|
||||
|
||||
std::map<uint32_t, BedItem*> bedSleepersMap;
|
||||
|
||||
ModalWindow offlineTrainingWindow { std::numeric_limits<uint32_t>::max(), "Choose a Skill", "Please choose a skill:" };
|
||||
|
||||
static constexpr int32_t LIGHT_LEVEL_DAY = 250;
|
||||
static constexpr int32_t LIGHT_LEVEL_NIGHT = 40;
|
||||
static constexpr int32_t SUNSET = 1305;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -35,47 +35,44 @@ GlobalEvents::GlobalEvents() :
|
||||
|
||||
GlobalEvents::~GlobalEvents()
|
||||
{
|
||||
clear(false);
|
||||
clear();
|
||||
}
|
||||
|
||||
void GlobalEvents::clearMap(GlobalEventMap& map, bool fromLua)
|
||||
void GlobalEvents::clearMap(GlobalEventMap& map)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ) {
|
||||
if (fromLua == it->second.fromLua) {
|
||||
it = map.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
for (const auto& it : map) {
|
||||
delete it.second;
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
|
||||
void GlobalEvents::clear(bool fromLua)
|
||||
void GlobalEvents::clear()
|
||||
{
|
||||
g_scheduler.stopEvent(thinkEventId);
|
||||
thinkEventId = 0;
|
||||
g_scheduler.stopEvent(timerEventId);
|
||||
timerEventId = 0;
|
||||
|
||||
clearMap(thinkMap, fromLua);
|
||||
clearMap(serverMap, fromLua);
|
||||
clearMap(timerMap, fromLua);
|
||||
clearMap(thinkMap);
|
||||
clearMap(serverMap);
|
||||
clearMap(timerMap);
|
||||
|
||||
reInitState(fromLua);
|
||||
scriptInterface.reInitState();
|
||||
}
|
||||
|
||||
Event_ptr GlobalEvents::getEvent(const std::string& nodeName)
|
||||
Event* GlobalEvents::getEvent(const std::string& nodeName)
|
||||
{
|
||||
if (strcasecmp(nodeName.c_str(), "globalevent") != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return Event_ptr(new GlobalEvent(&scriptInterface));
|
||||
return new GlobalEvent(&scriptInterface);
|
||||
}
|
||||
|
||||
bool GlobalEvents::registerEvent(Event_ptr event, const pugi::xml_node&)
|
||||
bool GlobalEvents::registerEvent(Event* event, const pugi::xml_node&)
|
||||
{
|
||||
GlobalEvent_ptr globalEvent{static_cast<GlobalEvent*>(event.release())}; //event is guaranteed to be a GlobalEvent
|
||||
GlobalEvent* globalEvent = static_cast<GlobalEvent*>(event); //event is guaranteed to be a GlobalEvent
|
||||
if (globalEvent->getEventType() == GLOBALEVENT_TIMER) {
|
||||
auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent));
|
||||
auto result = timerMap.emplace(globalEvent->getName(), globalEvent);
|
||||
if (result.second) {
|
||||
if (timerEventId == 0) {
|
||||
timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this)));
|
||||
@ -83,42 +80,12 @@ bool GlobalEvents::registerEvent(Event_ptr event, const pugi::xml_node&)
|
||||
return true;
|
||||
}
|
||||
} else if (globalEvent->getEventType() != GLOBALEVENT_NONE) {
|
||||
auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent));
|
||||
auto result = serverMap.emplace(globalEvent->getName(), globalEvent);
|
||||
if (result.second) {
|
||||
return true;
|
||||
}
|
||||
} else { // think event
|
||||
auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent));
|
||||
if (result.second) {
|
||||
if (thinkEventId == 0) {
|
||||
thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "[Warning - GlobalEvents::configureEvent] Duplicate registered globalevent with name: " << globalEvent->getName() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GlobalEvents::registerLuaEvent(GlobalEvent* event)
|
||||
{
|
||||
GlobalEvent_ptr globalEvent{ event };
|
||||
if (globalEvent->getEventType() == GLOBALEVENT_TIMER) {
|
||||
auto result = timerMap.emplace(globalEvent->getName(), std::move(*globalEvent));
|
||||
if (result.second) {
|
||||
if (timerEventId == 0) {
|
||||
timerEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::timer, this)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} else if (globalEvent->getEventType() != GLOBALEVENT_NONE) {
|
||||
auto result = serverMap.emplace(globalEvent->getName(), std::move(*globalEvent));
|
||||
if (result.second) {
|
||||
return true;
|
||||
}
|
||||
} else { // think event
|
||||
auto result = thinkMap.emplace(globalEvent->getName(), std::move(*globalEvent));
|
||||
auto result = thinkMap.emplace(globalEvent->getName(), globalEvent);
|
||||
if (result.second) {
|
||||
if (thinkEventId == 0) {
|
||||
thinkEventId = g_scheduler.addEvent(createSchedulerTask(SCHEDULER_MINTICKS, std::bind(&GlobalEvents::think, this)));
|
||||
@ -144,9 +111,9 @@ void GlobalEvents::timer()
|
||||
|
||||
auto it = timerMap.begin();
|
||||
while (it != timerMap.end()) {
|
||||
GlobalEvent& globalEvent = it->second;
|
||||
GlobalEvent* globalEvent = it->second;
|
||||
|
||||
int64_t nextExecutionTime = globalEvent.getNextExecution() - now;
|
||||
int64_t nextExecutionTime = globalEvent->getNextExecution() - now;
|
||||
if (nextExecutionTime > 0) {
|
||||
if (nextExecutionTime < nextScheduledTime) {
|
||||
nextScheduledTime = nextExecutionTime;
|
||||
@ -156,7 +123,7 @@ void GlobalEvents::timer()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!globalEvent.executeEvent()) {
|
||||
if (!globalEvent->executeEvent()) {
|
||||
it = timerMap.erase(it);
|
||||
continue;
|
||||
}
|
||||
@ -166,7 +133,7 @@ void GlobalEvents::timer()
|
||||
nextScheduledTime = nextExecutionTime;
|
||||
}
|
||||
|
||||
globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime);
|
||||
globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime);
|
||||
|
||||
++it;
|
||||
}
|
||||
@ -182,10 +149,10 @@ void GlobalEvents::think()
|
||||
int64_t now = OTSYS_TIME();
|
||||
|
||||
int64_t nextScheduledTime = std::numeric_limits<int64_t>::max();
|
||||
for (auto& it : thinkMap) {
|
||||
GlobalEvent& globalEvent = it.second;
|
||||
for (const auto& it : thinkMap) {
|
||||
GlobalEvent* globalEvent = it.second;
|
||||
|
||||
int64_t nextExecutionTime = globalEvent.getNextExecution() - now;
|
||||
int64_t nextExecutionTime = globalEvent->getNextExecution() - now;
|
||||
if (nextExecutionTime > 0) {
|
||||
if (nextExecutionTime < nextScheduledTime) {
|
||||
nextScheduledTime = nextExecutionTime;
|
||||
@ -193,16 +160,16 @@ void GlobalEvents::think()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!globalEvent.executeEvent()) {
|
||||
std::cout << "[Error - GlobalEvents::think] Failed to execute event: " << globalEvent.getName() << std::endl;
|
||||
if (!globalEvent->executeEvent()) {
|
||||
std::cout << "[Error - GlobalEvents::think] Failed to execute event: " << globalEvent->getName() << std::endl;
|
||||
}
|
||||
|
||||
nextExecutionTime = globalEvent.getInterval();
|
||||
nextExecutionTime = globalEvent->getInterval();
|
||||
if (nextExecutionTime < nextScheduledTime) {
|
||||
nextScheduledTime = nextExecutionTime;
|
||||
}
|
||||
|
||||
globalEvent.setNextExecution(globalEvent.getNextExecution() + nextExecutionTime);
|
||||
globalEvent->setNextExecution(globalEvent->getNextExecution() + nextExecutionTime);
|
||||
}
|
||||
|
||||
if (nextScheduledTime != std::numeric_limits<int64_t>::max()) {
|
||||
@ -213,16 +180,15 @@ void GlobalEvents::think()
|
||||
void GlobalEvents::execute(GlobalEvent_t type) const
|
||||
{
|
||||
for (const auto& it : serverMap) {
|
||||
const GlobalEvent& globalEvent = it.second;
|
||||
if (globalEvent.getEventType() == type) {
|
||||
globalEvent.executeEvent();
|
||||
GlobalEvent* globalEvent = it.second;
|
||||
if (globalEvent->getEventType() == type) {
|
||||
globalEvent->executeEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type)
|
||||
{
|
||||
// TODO: This should be better implemented. Maybe have a map for every type.
|
||||
switch (type) {
|
||||
case GLOBALEVENT_NONE: return thinkMap;
|
||||
case GLOBALEVENT_TIMER: return timerMap;
|
||||
@ -231,8 +197,8 @@ GlobalEventMap GlobalEvents::getEventMap(GlobalEvent_t type)
|
||||
case GLOBALEVENT_RECORD: {
|
||||
GlobalEventMap retMap;
|
||||
for (const auto& it : serverMap) {
|
||||
if (it.second.getEventType() == type) {
|
||||
retMap.emplace(it.first, it.second);
|
||||
if (it.second->getEventType() == type) {
|
||||
retMap[it.first] = it.second;
|
||||
}
|
||||
}
|
||||
return retMap;
|
||||
@ -349,7 +315,7 @@ bool GlobalEvent::executeRecord(uint32_t current, uint32_t old)
|
||||
return scriptInterface->callFunction(2);
|
||||
}
|
||||
|
||||
bool GlobalEvent::executeEvent() const
|
||||
bool GlobalEvent::executeEvent()
|
||||
{
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
std::cout << "[Error - GlobalEvent::executeEvent] Call stack overflow" << std::endl;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -33,8 +33,7 @@ enum GlobalEvent_t {
|
||||
};
|
||||
|
||||
class GlobalEvent;
|
||||
using GlobalEvent_ptr = std::unique_ptr<GlobalEvent>;
|
||||
using GlobalEventMap = std::map<std::string, GlobalEvent>;
|
||||
typedef std::map<std::string, GlobalEvent*> GlobalEventMap;
|
||||
|
||||
class GlobalEvents final : public BaseEvents
|
||||
{
|
||||
@ -53,20 +52,18 @@ class GlobalEvents final : public BaseEvents
|
||||
void execute(GlobalEvent_t type) const;
|
||||
|
||||
GlobalEventMap getEventMap(GlobalEvent_t type);
|
||||
static void clearMap(GlobalEventMap& map, bool fromLua);
|
||||
static void clearMap(GlobalEventMap& map);
|
||||
|
||||
bool registerLuaEvent(GlobalEvent* event);
|
||||
void clear(bool fromLua) override final;
|
||||
|
||||
private:
|
||||
std::string getScriptBaseName() const override {
|
||||
protected:
|
||||
std::string getScriptBaseName() const final {
|
||||
return "globalevents";
|
||||
}
|
||||
void clear() final;
|
||||
|
||||
Event_ptr getEvent(const std::string& nodeName) override;
|
||||
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
|
||||
Event* getEvent(const std::string& nodeName) final;
|
||||
bool registerEvent(Event* event, const pugi::xml_node& node) final;
|
||||
|
||||
LuaScriptInterface& getScriptInterface() override {
|
||||
LuaScriptInterface& getScriptInterface() final {
|
||||
return scriptInterface;
|
||||
}
|
||||
LuaScriptInterface scriptInterface;
|
||||
@ -80,31 +77,22 @@ class GlobalEvent final : public Event
|
||||
public:
|
||||
explicit GlobalEvent(LuaScriptInterface* interface);
|
||||
|
||||
bool configureEvent(const pugi::xml_node& node) override;
|
||||
bool configureEvent(const pugi::xml_node& node) final;
|
||||
|
||||
bool executeRecord(uint32_t current, uint32_t old);
|
||||
bool executeEvent() const;
|
||||
bool executeEvent();
|
||||
|
||||
GlobalEvent_t getEventType() const {
|
||||
return eventType;
|
||||
}
|
||||
void setEventType(GlobalEvent_t type) {
|
||||
eventType = type;
|
||||
}
|
||||
|
||||
const std::string& getName() const {
|
||||
return name;
|
||||
}
|
||||
void setName(std::string eventName) {
|
||||
name = eventName;
|
||||
}
|
||||
|
||||
uint32_t getInterval() const {
|
||||
return interval;
|
||||
}
|
||||
void setInterval(uint32_t eventInterval) {
|
||||
interval |= eventInterval;
|
||||
}
|
||||
|
||||
int64_t getNextExecution() const {
|
||||
return nextExecution;
|
||||
@ -113,10 +101,10 @@ class GlobalEvent final : public Event
|
||||
nextExecution = time;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
GlobalEvent_t eventType = GLOBALEVENT_NONE;
|
||||
|
||||
std::string getScriptEventName() const override;
|
||||
std::string getScriptEventName() const final;
|
||||
|
||||
std::string name;
|
||||
int64_t nextExecution = 0;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -24,47 +24,6 @@
|
||||
#include "pugicast.h"
|
||||
#include "tools.h"
|
||||
|
||||
const std::unordered_map<std::string, PlayerFlags> ParsePlayerFlagMap = {
|
||||
{"cannotusecombat", PlayerFlag_CannotUseCombat},
|
||||
{"cannotattackplayer", PlayerFlag_CannotAttackPlayer},
|
||||
{"cannotattackmonster", PlayerFlag_CannotAttackMonster},
|
||||
{"cannotbeattacked", PlayerFlag_CannotBeAttacked},
|
||||
{"canconvinceall", PlayerFlag_CanConvinceAll},
|
||||
{"cansummonall", PlayerFlag_CanSummonAll},
|
||||
{"canillusionall", PlayerFlag_CanIllusionAll},
|
||||
{"cansenseinvisibility", PlayerFlag_CanSenseInvisibility},
|
||||
{"ignoredbymonsters", PlayerFlag_IgnoredByMonsters},
|
||||
{"notgaininfight", PlayerFlag_NotGainInFight},
|
||||
{"hasinfinitemana", PlayerFlag_HasInfiniteMana},
|
||||
{"hasinfinitesoul", PlayerFlag_HasInfiniteSoul},
|
||||
{"hasnoexhaustion", PlayerFlag_HasNoExhaustion},
|
||||
{"cannotusespells", PlayerFlag_CannotUseSpells},
|
||||
{"cannotpickupitem", PlayerFlag_CannotPickupItem},
|
||||
{"canalwayslogin", PlayerFlag_CanAlwaysLogin},
|
||||
{"canbroadcast", PlayerFlag_CanBroadcast},
|
||||
{"canedithouses", PlayerFlag_CanEditHouses},
|
||||
{"cannotbebanned", PlayerFlag_CannotBeBanned},
|
||||
{"cannotbepushed", PlayerFlag_CannotBePushed},
|
||||
{"hasinfinitecapacity", PlayerFlag_HasInfiniteCapacity},
|
||||
{"cannotpushallcreatures", PlayerFlag_CanPushAllCreatures},
|
||||
{"cantalkredprivate", PlayerFlag_CanTalkRedPrivate},
|
||||
{"cantalkredchannel", PlayerFlag_CanTalkRedChannel},
|
||||
{"talkorangehelpchannel", PlayerFlag_TalkOrangeHelpChannel},
|
||||
{"notgainexperience", PlayerFlag_NotGainExperience},
|
||||
{"notgainmana", PlayerFlag_NotGainMana},
|
||||
{"notgainhealth", PlayerFlag_NotGainHealth},
|
||||
{"notgainskill", PlayerFlag_NotGainSkill},
|
||||
{"setmaxspeed", PlayerFlag_SetMaxSpeed},
|
||||
{"specialvip", PlayerFlag_SpecialVIP},
|
||||
{"notgenerateloot", PlayerFlag_NotGenerateLoot},
|
||||
{"cantalkredchannelanonymous", PlayerFlag_CanTalkRedChannelAnonymous},
|
||||
{"ignoreprotectionzone", PlayerFlag_IgnoreProtectionZone},
|
||||
{"ignorespellcheck", PlayerFlag_IgnoreSpellCheck},
|
||||
{"ignoreweaponcheck", PlayerFlag_IgnoreWeaponCheck},
|
||||
{"cannotbemuted", PlayerFlag_CannotBeMuted},
|
||||
{"isalwayspremium", PlayerFlag_IsAlwaysPremium}
|
||||
};
|
||||
|
||||
bool Groups::load()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
@ -78,24 +37,10 @@ bool Groups::load()
|
||||
Group group;
|
||||
group.id = pugi::cast<uint32_t>(groupNode.attribute("id").value());
|
||||
group.name = groupNode.attribute("name").as_string();
|
||||
group.flags = pugi::cast<uint64_t>(groupNode.attribute("flags").value());
|
||||
group.access = groupNode.attribute("access").as_bool();
|
||||
group.maxDepotItems = pugi::cast<uint32_t>(groupNode.attribute("maxdepotitems").value());
|
||||
group.maxVipEntries = pugi::cast<uint32_t>(groupNode.attribute("maxvipentries").value());
|
||||
group.flags = pugi::cast<uint64_t>(groupNode.attribute("flags").value());
|
||||
if (pugi::xml_node node = groupNode.child("flags")) {
|
||||
for (auto flagNode : node.children()) {
|
||||
pugi::xml_attribute attr = flagNode.first_attribute();
|
||||
if (!attr || (attr && !attr.as_bool())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto parseFlag = ParsePlayerFlagMap.find(attr.name());
|
||||
if (parseFlag != ParsePlayerFlagMap.end()) {
|
||||
group.flags |= parseFlag->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
groups.push_back(group);
|
||||
}
|
||||
return true;
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -28,18 +28,11 @@ extern Game g_game;
|
||||
void Guild::addMember(Player* player)
|
||||
{
|
||||
membersOnline.push_back(player);
|
||||
for (Player* member : membersOnline) {
|
||||
g_game.updatePlayerHelpers(*member);
|
||||
}
|
||||
}
|
||||
|
||||
void Guild::removeMember(Player* player)
|
||||
{
|
||||
membersOnline.remove(player);
|
||||
for (Player* member : membersOnline) {
|
||||
g_game.updatePlayerHelpers(*member);
|
||||
}
|
||||
g_game.updatePlayerHelpers(*player);
|
||||
|
||||
if (membersOnline.empty()) {
|
||||
g_game.removeGuild(id);
|
||||
@ -57,16 +50,6 @@ GuildRank* Guild::getRankById(uint32_t rankId)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GuildRank* Guild::getRankByName(const std::string& name) const
|
||||
{
|
||||
for (const auto& rank : ranks) {
|
||||
if (rank.name == name) {
|
||||
return &rank;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GuildRank* Guild::getRankByLevel(uint8_t level) const
|
||||
{
|
||||
for (const auto& rank : ranks) {
|
||||
|
20
src/guild.h
20
src/guild.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -55,26 +55,14 @@ class Guild
|
||||
memberCount = count;
|
||||
}
|
||||
|
||||
const std::vector<GuildRank>& getRanks() const {
|
||||
return ranks;
|
||||
}
|
||||
GuildRank* getRankById(uint32_t rankId);
|
||||
const GuildRank* getRankByName(const std::string& name) const;
|
||||
GuildRank* getRankById(uint32_t id);
|
||||
const GuildRank* getRankByLevel(uint8_t level) const;
|
||||
void addRank(uint32_t rankId, const std::string& rankName, uint8_t level);
|
||||
|
||||
const std::string& getMotd() const {
|
||||
return motd;
|
||||
}
|
||||
void setMotd(const std::string& motd) {
|
||||
this->motd = motd;
|
||||
}
|
||||
void addRank(uint32_t id, const std::string& name, uint8_t level);
|
||||
|
||||
private:
|
||||
std::list<Player*> membersOnline;
|
||||
std::vector<GuildRank> ranks;
|
||||
std::string name;
|
||||
std::string motd;
|
||||
uint32_t id;
|
||||
uint32_t memberCount = 0;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -41,11 +41,11 @@ void House::addTile(HouseTile* tile)
|
||||
void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* player/* = nullptr*/)
|
||||
{
|
||||
if (updateDatabase && owner != guid) {
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `houses` SET `owner` = " << guid << ", `bid` = 0, `bid_end` = 0, `last_bid` = 0, `highest_bidder` = 0 WHERE `id` = " << id;
|
||||
db.executeQuery(query.str());
|
||||
db->executeQuery(query.str());
|
||||
}
|
||||
|
||||
if (isLoaded && owner == guid) {
|
||||
@ -85,26 +85,12 @@ void House::setOwner(uint32_t guid, bool updateDatabase/* = true*/, Player* play
|
||||
for (Door* door : doorSet) {
|
||||
door->setAccessList("");
|
||||
}
|
||||
} else {
|
||||
std::string strRentPeriod = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD));
|
||||
time_t currentTime = time(nullptr);
|
||||
if (strRentPeriod == "yearly") {
|
||||
currentTime += 24 * 60 * 60 * 365;
|
||||
} else if (strRentPeriod == "monthly") {
|
||||
currentTime += 24 * 60 * 60 * 30;
|
||||
} else if (strRentPeriod == "weekly") {
|
||||
currentTime += 24 * 60 * 60 * 7;
|
||||
} else if (strRentPeriod == "daily") {
|
||||
currentTime += 24 * 60 * 60;
|
||||
} else {
|
||||
currentTime = 0;
|
||||
}
|
||||
|
||||
paidUntil = currentTime;
|
||||
//reset paid date
|
||||
paidUntil = 0;
|
||||
rentWarnings = 0;
|
||||
}
|
||||
|
||||
rentWarnings = 0;
|
||||
|
||||
if (guid != 0) {
|
||||
std::string name = IOLoginData::getNameByGuid(guid);
|
||||
if (!name.empty()) {
|
||||
@ -124,9 +110,9 @@ void House::updateDoorDescription() const
|
||||
} else {
|
||||
ss << "It belongs to house '" << houseName << "'. Nobody owns this house.";
|
||||
|
||||
const int32_t housePrice = g_config.getNumber(ConfigManager::HOUSE_PRICE);
|
||||
const int32_t housePrice = getRent();
|
||||
if (housePrice != -1) {
|
||||
ss << " It costs " << (houseTiles.size() * housePrice) << " gold coins.";
|
||||
ss << " It costs " << housePrice * 5 << " gold coins.";
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,6 +216,7 @@ bool House::transferToDepot() const
|
||||
transferToDepot(&tmpPlayer);
|
||||
IOLoginData::savePlayer(&tmpPlayer);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -258,8 +245,9 @@ bool House::transferToDepot(Player* player) const
|
||||
}
|
||||
|
||||
for (Item* item : moveItemList) {
|
||||
g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT);
|
||||
g_game.internalMoveItem(item->getParent(), player->getDepotLocker(getTownId(), true), INDEX_WHEREEVER, item, item->getItemCount(), nullptr, FLAG_NOLIMIT);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -408,7 +396,7 @@ bool House::executeTransfer(HouseTransferItem* item, Player* newOwner)
|
||||
void AccessList::parseList(const std::string& list)
|
||||
{
|
||||
playerList.clear();
|
||||
guildRankList.clear();
|
||||
guildList.clear();
|
||||
expressionList.clear();
|
||||
regExList.clear();
|
||||
this->list = list;
|
||||
@ -433,11 +421,7 @@ void AccessList::parseList(const std::string& list)
|
||||
|
||||
std::string::size_type at_pos = line.find("@");
|
||||
if (at_pos != std::string::npos) {
|
||||
if (at_pos == 0) {
|
||||
addGuild(line.substr(1));
|
||||
} else {
|
||||
addGuildRank(line.substr(0, at_pos - 1), line.substr(at_pos + 1));
|
||||
}
|
||||
addGuild(line.substr(at_pos + 1));
|
||||
} else if (line.find("!") != std::string::npos || line.find("*") != std::string::npos || line.find("?") != std::string::npos) {
|
||||
addExpression(line);
|
||||
} else {
|
||||
@ -459,43 +443,11 @@ void AccessList::addPlayer(const std::string& name)
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const Guild* getGuildByName(const std::string& name)
|
||||
{
|
||||
uint32_t guildId = IOGuild::getGuildIdByName(name);
|
||||
if (guildId == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Guild* guild = g_game.getGuild(guildId);
|
||||
if (guild) {
|
||||
return guild;
|
||||
}
|
||||
|
||||
return IOGuild::loadGuild(guildId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AccessList::addGuild(const std::string& name)
|
||||
{
|
||||
const Guild* guild = getGuildByName(name);
|
||||
if (guild) {
|
||||
for (const auto& rank : guild->getRanks()) {
|
||||
guildRankList.insert(rank.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AccessList::addGuildRank(const std::string& name, const std::string& rankName)
|
||||
{
|
||||
const Guild* guild = getGuildByName(name);
|
||||
if (guild) {
|
||||
const GuildRank* rank = guild->getRankByName(rankName);
|
||||
if (rank) {
|
||||
guildRankList.insert(rank->id);
|
||||
}
|
||||
uint32_t guildId = IOGuild::getGuildIdByName(name);
|
||||
if (guildId != 0) {
|
||||
guildList.insert(guildId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,8 +502,8 @@ bool AccessList::isInList(const Player* player)
|
||||
return true;
|
||||
}
|
||||
|
||||
const GuildRank* rank = player->getGuildRank();
|
||||
return rank && guildRankList.find(rank->id) != guildRankList.end();
|
||||
const Guild* guild = player->getGuild();
|
||||
return guild && guildList.find(guild->getId()) != guildList.end();
|
||||
}
|
||||
|
||||
void AccessList::getList(std::string& list) const
|
||||
@ -715,9 +667,7 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.getBankBalance() >= rent) {
|
||||
player.setBankBalance(player.getBankBalance() - rent);
|
||||
|
||||
if (g_game.removeMoney(player.getDepotLocker(house->getTownId(), true), house->getRent(), FLAG_NOLIMIT)) {
|
||||
time_t paidUntil = currentTime;
|
||||
switch (rentPeriod) {
|
||||
case RENTPERIOD_DAILY:
|
||||
@ -768,7 +718,7 @@ void Houses::payHouses(RentPeriod_t rentPeriod) const
|
||||
std::ostringstream ss;
|
||||
ss << "Warning! \nThe " << period << " rent of " << house->getRent() << " gold for your house \"" << house->getName() << "\" is payable. Have it within " << daysLeft << " days or you will lose this house.";
|
||||
letter->setText(ss.str());
|
||||
g_game.internalAddItem(player.getInbox(), letter, INDEX_WHEREEVER, FLAG_NOLIMIT);
|
||||
g_game.internalAddItem(player.getDepotLocker(house->getTownId(), true), letter, INDEX_WHEREEVER, FLAG_NOLIMIT);
|
||||
house->setPayRentWarnings(house->getPayRentWarnings() + 1);
|
||||
} else {
|
||||
house->setOwner(0, true, &player);
|
||||
|
36
src/house.h
36
src/house.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -22,7 +22,6 @@
|
||||
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "container.h"
|
||||
#include "housetile.h"
|
||||
@ -38,7 +37,6 @@ class AccessList
|
||||
void parseList(const std::string& list);
|
||||
void addPlayer(const std::string& name);
|
||||
void addGuild(const std::string& name);
|
||||
void addGuildRank(const std::string& name, const std::string& rankName);
|
||||
void addExpression(const std::string& expression);
|
||||
|
||||
bool isInList(const Player* player);
|
||||
@ -48,7 +46,7 @@ class AccessList
|
||||
private:
|
||||
std::string list;
|
||||
std::unordered_set<uint32_t> playerList;
|
||||
std::unordered_set<uint32_t> guildRankList;
|
||||
std::unordered_set<uint32_t> guildList; // TODO: include ranks
|
||||
std::list<std::string> expressionList;
|
||||
std::list<std::pair<std::regex, bool>> regExList;
|
||||
};
|
||||
@ -62,10 +60,10 @@ class Door final : public Item
|
||||
Door(const Door&) = delete;
|
||||
Door& operator=(const Door&) = delete;
|
||||
|
||||
Door* getDoor() override {
|
||||
Door* getDoor() final {
|
||||
return this;
|
||||
}
|
||||
const Door* getDoor() const override {
|
||||
const Door* getDoor() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -74,8 +72,8 @@ class Door final : public Item
|
||||
}
|
||||
|
||||
//serialization
|
||||
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) override;
|
||||
void serializeAttr(PropWriteStream&) const override {}
|
||||
Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream) final;
|
||||
void serializeAttr(PropWriteStream&) const final {}
|
||||
|
||||
void setDoorId(uint32_t doorId) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORID, doorId);
|
||||
@ -89,11 +87,12 @@ class Door final : public Item
|
||||
void setAccessList(const std::string& textlist);
|
||||
bool getAccessList(std::string& list) const;
|
||||
|
||||
void onRemoved() override;
|
||||
void onRemoved() final;
|
||||
|
||||
private:
|
||||
protected:
|
||||
void setHouse(House* house);
|
||||
|
||||
private:
|
||||
House* house = nullptr;
|
||||
std::unique_ptr<AccessList> accessList;
|
||||
friend class House;
|
||||
@ -111,8 +110,8 @@ enum AccessHouseLevel_t {
|
||||
HOUSE_OWNER = 3,
|
||||
};
|
||||
|
||||
using HouseTileList = std::list<HouseTile*>;
|
||||
using HouseBedItemList = std::list<BedItem*>;
|
||||
typedef std::list<HouseTile*> HouseTileList;
|
||||
typedef std::list<BedItem*> HouseBedItemList;
|
||||
|
||||
class HouseTransferItem final : public Item
|
||||
{
|
||||
@ -121,12 +120,12 @@ class HouseTransferItem final : public Item
|
||||
|
||||
explicit HouseTransferItem(House* house) : Item(0), house(house) {}
|
||||
|
||||
void onTradeEvent(TradeEvents_t event, Player* owner) override;
|
||||
bool canTransform() const override {
|
||||
void onTradeEvent(TradeEvents_t event, Player* owner) final;
|
||||
bool canTransform() const final {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
protected:
|
||||
House* house;
|
||||
};
|
||||
|
||||
@ -208,7 +207,7 @@ class House
|
||||
|
||||
HouseTransferItem* getTransferItem();
|
||||
void resetTransferItem();
|
||||
bool executeTransfer(HouseTransferItem* item, Player* newOwner);
|
||||
bool executeTransfer(HouseTransferItem* item, Player* player);
|
||||
|
||||
const HouseTileList& getTiles() const {
|
||||
return houseTiles;
|
||||
@ -218,6 +217,7 @@ class House
|
||||
return doorSet;
|
||||
}
|
||||
|
||||
|
||||
void addBed(BedItem* bed);
|
||||
const HouseBedItemList& getBeds() const {
|
||||
return bedsList;
|
||||
@ -257,7 +257,7 @@ class House
|
||||
bool isLoaded = false;
|
||||
};
|
||||
|
||||
using HouseMap = std::map<uint32_t, House*>;
|
||||
typedef std::map<uint32_t, House*> HouseMap;
|
||||
|
||||
enum RentPeriod_t {
|
||||
RENTPERIOD_DAILY,
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -31,13 +31,13 @@ class HouseTile final : public DynamicTile
|
||||
|
||||
//cylinder implementations
|
||||
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t flags, Creature* actor = nullptr) const override;
|
||||
uint32_t flags, Creature* actor = nullptr) const final;
|
||||
|
||||
Tile* queryDestination(int32_t& index, const Thing& thing, Item** destItem,
|
||||
uint32_t& flags) override;
|
||||
uint32_t& flags) final;
|
||||
|
||||
void addThing(int32_t index, Thing* thing) override;
|
||||
void internalAddThing(uint32_t index, Thing* thing) override;
|
||||
void addThing(int32_t index, Thing* thing) final;
|
||||
void internalAddThing(uint32_t index, Thing* thing) final;
|
||||
|
||||
House* getHouse() {
|
||||
return house;
|
||||
|
@ -1,72 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#include "inbox.h"
|
||||
#include "tools.h"
|
||||
|
||||
Inbox::Inbox(uint16_t type) : Container(type, 30, false, true) {}
|
||||
|
||||
ReturnValue Inbox::queryAdd(int32_t, const Thing& thing, uint32_t,
|
||||
uint32_t flags, Creature*) const
|
||||
{
|
||||
if (!hasBitSet(FLAG_NOLIMIT, flags)) {
|
||||
return RETURNVALUE_CONTAINERNOTENOUGHROOM;
|
||||
}
|
||||
|
||||
const Item* item = thing.getItem();
|
||||
if (!item) {
|
||||
return RETURNVALUE_NOTPOSSIBLE;
|
||||
}
|
||||
|
||||
if (item == this) {
|
||||
return RETURNVALUE_THISISIMPOSSIBLE;
|
||||
}
|
||||
|
||||
if (!item->isPickupable()) {
|
||||
return RETURNVALUE_CANNOTPICKUP;
|
||||
}
|
||||
|
||||
return RETURNVALUE_NOERROR;
|
||||
}
|
||||
|
||||
void Inbox::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t)
|
||||
{
|
||||
Cylinder* parent = getParent();
|
||||
if (parent != nullptr) {
|
||||
parent->postAddNotification(thing, oldParent, index, LINK_PARENT);
|
||||
}
|
||||
}
|
||||
|
||||
void Inbox::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t)
|
||||
{
|
||||
Cylinder* parent = getParent();
|
||||
if (parent != nullptr) {
|
||||
parent->postRemoveNotification(thing, newParent, index, LINK_PARENT);
|
||||
}
|
||||
}
|
||||
|
||||
Cylinder* Inbox::getParent() const
|
||||
{
|
||||
if (parent) {
|
||||
return parent->getParent();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
49
src/inbox.h
49
src/inbox.h
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef FS_INBOX_H_C3EF10190329447883B9C3479234EE5C
|
||||
#define FS_INBOX_H_C3EF10190329447883B9C3479234EE5C
|
||||
|
||||
#include "container.h"
|
||||
|
||||
class Inbox final : public Container
|
||||
{
|
||||
public:
|
||||
explicit Inbox(uint16_t type);
|
||||
|
||||
//cylinder implementations
|
||||
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t flags, Creature* actor = nullptr) const override;
|
||||
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
|
||||
//overrides
|
||||
bool canRemove() const override {
|
||||
return false;
|
||||
}
|
||||
|
||||
Cylinder* getParent() const override;
|
||||
Cylinder* getRealParent() const override {
|
||||
return parent;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -19,51 +19,29 @@
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#include "database.h"
|
||||
#include "guild.h"
|
||||
#include "ioguild.h"
|
||||
|
||||
Guild* IOGuild::loadGuild(uint32_t guildId)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
query << "SELECT `name` FROM `guilds` WHERE `id` = " << guildId;
|
||||
if (DBResult_ptr result = db.storeQuery(query.str())) {
|
||||
Guild* guild = new Guild(guildId, result->getString("name"));
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = " << guildId;
|
||||
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
do {
|
||||
guild->addRank(result->getNumber<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("level"));
|
||||
} while (result->next());
|
||||
}
|
||||
return guild;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
#include "database.h"
|
||||
|
||||
uint32_t IOGuild::getGuildIdByName(const std::string& name)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id` FROM `guilds` WHERE `name` = " << db.escapeString(name);
|
||||
query << "SELECT `id` FROM `guilds` WHERE `name` = " << db->escapeString(name);
|
||||
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
return result->getNumber<uint32_t>("id");
|
||||
}
|
||||
|
||||
void IOGuild::getWarList(uint32_t guildId, GuildWarVector& guildWarVector)
|
||||
void IOGuild::getWarList(uint32_t guildId, GuildWarList& guildWarList)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "SELECT `guild1`, `guild2` FROM `guild_wars` WHERE (`guild1` = " << guildId << " OR `guild2` = " << guildId << ") AND `ended` = 0 AND `status` = 1";
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
@ -71,9 +49,9 @@ void IOGuild::getWarList(uint32_t guildId, GuildWarVector& guildWarVector)
|
||||
do {
|
||||
uint32_t guild1 = result->getNumber<uint32_t>("guild1");
|
||||
if (guildId != guild1) {
|
||||
guildWarVector.push_back(guild1);
|
||||
guildWarList.push_back(guild1);
|
||||
} else {
|
||||
guildWarVector.push_back(result->getNumber<uint32_t>("guild2"));
|
||||
guildWarList.push_back(result->getNumber<uint32_t>("guild2"));
|
||||
}
|
||||
} while (result->next());
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,15 +20,13 @@
|
||||
#ifndef FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF
|
||||
#define FS_IOGUILD_H_EF9ACEBA0B844C388B70FF52E69F1AFF
|
||||
|
||||
class Guild;
|
||||
using GuildWarVector = std::vector<uint32_t>;
|
||||
typedef std::vector<uint32_t> GuildWarList;
|
||||
|
||||
class IOGuild
|
||||
{
|
||||
public:
|
||||
static Guild* loadGuild(uint32_t guildId);
|
||||
static uint32_t getGuildIdByName(const std::string& name);
|
||||
static void getWarList(uint32_t guildId, GuildWarVector& guildWarVector);
|
||||
static void getWarList(uint32_t guildId, GuildWarList& guildWarList);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -31,14 +31,13 @@ Account IOLoginData::loadAccount(uint32_t accno)
|
||||
Account account;
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `name`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno;
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accno;
|
||||
DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return account;
|
||||
}
|
||||
|
||||
account.id = result->getNumber<uint32_t>("id");
|
||||
account.name = result->getString("name");
|
||||
account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
|
||||
account.premiumDays = result->getNumber<uint16_t>("premdays");
|
||||
account.lastDay = result->getNumber<time_t>("lastday");
|
||||
@ -49,45 +48,16 @@ bool IOLoginData::saveAccount(const Account& acc)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `accounts` SET `premdays` = " << acc.premiumDays << ", `lastday` = " << acc.lastDay << " WHERE `id` = " << acc.id;
|
||||
return Database::getInstance().executeQuery(query.str());
|
||||
return Database::getInstance()->executeQuery(query.str());
|
||||
}
|
||||
|
||||
std::string decodeSecret(const std::string& secret)
|
||||
bool IOLoginData::loginserverAuthentication(uint32_t accountNumber, const std::string& password, Account& account)
|
||||
{
|
||||
// simple base32 decoding
|
||||
std::string key;
|
||||
key.reserve(10);
|
||||
|
||||
uint32_t buffer = 0, left = 0;
|
||||
for (const auto& ch : secret) {
|
||||
buffer <<= 5;
|
||||
if (ch >= 'A' && ch <= 'Z') {
|
||||
buffer |= (ch & 0x1F) - 1;
|
||||
} else if (ch >= '2' && ch <= '7') {
|
||||
buffer |= ch - 24;
|
||||
} else {
|
||||
// if a key is broken, return empty and the comparison
|
||||
// will always be false since the token must not be empty
|
||||
return {};
|
||||
}
|
||||
|
||||
left += 5;
|
||||
if (left >= 8) {
|
||||
left -= 8;
|
||||
key.push_back(static_cast<char>(buffer >> left));
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
bool IOLoginData::loginserverAuthentication(const std::string& name, const std::string& password, Account& account)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `name`, `password`, `secret`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `name` = " << db.escapeString(name);
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
query << "SELECT `id`, `password`, `type`, `premdays`, `lastday` FROM `accounts` WHERE `id` = " << accountNumber;
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -97,15 +67,13 @@ bool IOLoginData::loginserverAuthentication(const std::string& name, const std::
|
||||
}
|
||||
|
||||
account.id = result->getNumber<uint32_t>("id");
|
||||
account.name = result->getString("name");
|
||||
account.key = decodeSecret(result->getString("secret"));
|
||||
account.accountType = static_cast<AccountType_t>(result->getNumber<int32_t>("type"));
|
||||
account.premiumDays = result->getNumber<uint16_t>("premdays");
|
||||
account.lastDay = result->getNumber<time_t>("lastday");
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `name`, `deletion` FROM `players` WHERE `account_id` = " << account.id;
|
||||
result = db.storeQuery(query.str());
|
||||
result = db->storeQuery(query.str());
|
||||
if (result) {
|
||||
do {
|
||||
if (result->getNumber<uint64_t>("deletion") == 0) {
|
||||
@ -117,29 +85,17 @@ bool IOLoginData::loginserverAuthentication(const std::string& name, const std::
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName, std::string& token, uint32_t tokenTime)
|
||||
uint32_t IOLoginData::gameworldAuthentication(uint32_t accountNumber, const std::string& password, std::string& characterName)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `password`, `secret` FROM `accounts` WHERE `name` = " << db.escapeString(accountName);
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
query << "SELECT `id`, `password` FROM `accounts` WHERE `id` = " << accountNumber;
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string secret = decodeSecret(result->getString("secret"));
|
||||
if (!secret.empty()) {
|
||||
if (token.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool tokenValid = token == generateToken(secret, tokenTime) || token == generateToken(secret, tokenTime - 1) || token == generateToken(secret, tokenTime + 1);
|
||||
if (!tokenValid) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (transformToSHA1(password) != result->getString("password")) {
|
||||
return 0;
|
||||
}
|
||||
@ -147,8 +103,8 @@ uint32_t IOLoginData::gameworldAuthentication(const std::string& accountName, co
|
||||
uint32_t accountId = result->getNumber<uint32_t>("id");
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `account_id`, `name`, `deletion` FROM `players` WHERE `name` = " << db.escapeString(characterName);
|
||||
result = db.storeQuery(query.str());
|
||||
query << "SELECT `account_id`, `name`, `deletion` FROM `players` WHERE `name` = " << db->escapeString(characterName);
|
||||
result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
@ -164,7 +120,7 @@ AccountType_t IOLoginData::getAccountType(uint32_t accountId)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "SELECT `type` FROM `accounts` WHERE `id` = " << accountId;
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return ACCOUNT_TYPE_NORMAL;
|
||||
}
|
||||
@ -175,7 +131,7 @@ void IOLoginData::setAccountType(uint32_t accountId, AccountType_t accountType)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `accounts` SET `type` = " << static_cast<uint16_t>(accountType) << " WHERE `id` = " << accountId;
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
Database::getInstance()->executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOLoginData::updateOnlineStatus(uint32_t guid, bool login)
|
||||
@ -190,20 +146,20 @@ void IOLoginData::updateOnlineStatus(uint32_t guid, bool login)
|
||||
} else {
|
||||
query << "DELETE FROM `players_online` WHERE `player_id` = " << guid;
|
||||
}
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
Database::getInstance()->executeQuery(query.str());
|
||||
}
|
||||
|
||||
bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `account_id`, `group_id`, `deletion`, (SELECT `type` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `account_type`";
|
||||
if (!g_config.getBoolean(ConfigManager::FREE_PREMIUM)) {
|
||||
query << ", (SELECT `premdays` FROM `accounts` WHERE `accounts`.`id` = `account_id`) AS `premium_days`";
|
||||
}
|
||||
query << " FROM `players` WHERE `name` = " << db.escapeString(name);
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
query << " FROM `players` WHERE `name` = " << db->escapeString(name);
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -231,18 +187,17 @@ bool IOLoginData::preloadPlayer(Player* player, const std::string& name)
|
||||
|
||||
bool IOLoginData::loadPlayerById(Player* player, uint32_t id)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `id` = " << id;
|
||||
return loadPlayer(player, db.storeQuery(query.str()));
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `id` = " << id;
|
||||
return loadPlayer(player, Database::getInstance()->storeQuery(query.str()));
|
||||
}
|
||||
|
||||
bool IOLoginData::loadPlayerByName(Player* player, const std::string& name)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `offlinetraining_time`, `offlinetraining_skill`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries`, `direction` FROM `players` WHERE `name` = " << db.escapeString(name);
|
||||
return loadPlayer(player, db.storeQuery(query.str()));
|
||||
query << "SELECT `id`, `name`, `account_id`, `group_id`, `sex`, `vocation`, `experience`, `level`, `maglevel`, `health`, `healthmax`, `blessings`, `mana`, `manamax`, `manaspent`, `soul`, `lookbody`, `lookfeet`, `lookhead`, `looklegs`, `looktype`, `lookaddons`, `posx`, `posy`, `posz`, `cap`, `lastlogin`, `lastlogout`, `lastip`, `conditions`, `skulltime`, `skull`, `town_id`, `balance`, `stamina`, `skill_fist`, `skill_fist_tries`, `skill_club`, `skill_club_tries`, `skill_sword`, `skill_sword_tries`, `skill_axe`, `skill_axe_tries`, `skill_dist`, `skill_dist_tries`, `skill_shielding`, `skill_shielding_tries`, `skill_fishing`, `skill_fishing_tries` FROM `players` WHERE `name` = " << db->escapeString(name);
|
||||
return loadPlayer(player, db->storeQuery(query.str()));
|
||||
}
|
||||
|
||||
bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
@ -251,7 +206,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
return false;
|
||||
}
|
||||
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
uint32_t accno = result->getNumber<uint32_t>("account_id");
|
||||
Account acc = loadAccount(accno);
|
||||
@ -297,7 +252,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
}
|
||||
|
||||
player->soul = result->getNumber<uint16_t>("soul");
|
||||
player->capacity = result->getNumber<uint32_t>("cap") * 100;
|
||||
player->capacity = std::max<uint32_t>(400, result->getNumber<uint32_t>("cap")) * 100;
|
||||
player->blessings = result->getNumber<uint16_t>("blessings");
|
||||
|
||||
unsigned long conditionsSize;
|
||||
@ -343,20 +298,17 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
player->defaultOutfit.lookFeet = result->getNumber<uint16_t>("lookfeet");
|
||||
player->defaultOutfit.lookAddons = result->getNumber<uint16_t>("lookaddons");
|
||||
player->currentOutfit = player->defaultOutfit;
|
||||
player->direction = static_cast<Direction> (result->getNumber<uint16_t>("direction"));
|
||||
|
||||
if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
|
||||
const time_t skullSeconds = result->getNumber<time_t>("skulltime") - time(nullptr);
|
||||
if (skullSeconds > 0) {
|
||||
//ensure that we round up the number of ticks
|
||||
player->skullTicks = (skullSeconds + 2);
|
||||
player->playerKillerEnd = result->getNumber<time_t>("skulltime");
|
||||
|
||||
uint16_t skull = result->getNumber<uint16_t>("skull");
|
||||
if (skull == SKULL_RED) {
|
||||
player->skull = SKULL_RED;
|
||||
} else if (skull == SKULL_BLACK) {
|
||||
player->skull = SKULL_BLACK;
|
||||
}
|
||||
uint16_t skull = result->getNumber<uint16_t>("skull");
|
||||
if (skull == SKULL_RED) {
|
||||
player->skull = SKULL_RED;
|
||||
}
|
||||
|
||||
if (player->playerKillerEnd == 0) {
|
||||
player->skull = SKULL_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -367,9 +319,6 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
player->lastLoginSaved = result->getNumber<time_t>("lastlogin");
|
||||
player->lastLogout = result->getNumber<time_t>("lastlogout");
|
||||
|
||||
player->offlineTrainingTime = result->getNumber<int32_t>("offlinetraining_time") * 1000;
|
||||
player->offlineTrainingSkill = result->getNumber<int32_t>("offlinetraining_skill");
|
||||
|
||||
Town* town = g_game.map.towns.getTown(result->getNumber<uint32_t>("town_id"));
|
||||
if (!town) {
|
||||
std::cout << "[Error - IOLoginData::loadPlayer] " << player->name << " has Town ID " << result->getNumber<uint32_t>("town_id") << " which doesn't exist" << std::endl;
|
||||
@ -402,16 +351,38 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
}
|
||||
|
||||
std::ostringstream query;
|
||||
|
||||
query << "SELECT `date` FROM `player_murders` WHERE `player_id` = " << player->getGUID() << " ORDER BY `date` ASC";
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
do {
|
||||
player->murderTimeStamps.push_back(result->getNumber<time_t>("date"));
|
||||
} while (result->next());
|
||||
}
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `guild_id`, `rank_id`, `nick` FROM `guild_membership` WHERE `player_id` = " << player->getGUID();
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
uint32_t guildId = result->getNumber<uint32_t>("guild_id");
|
||||
uint32_t playerRankId = result->getNumber<uint32_t>("rank_id");
|
||||
player->guildNick = result->getString("nick");
|
||||
|
||||
Guild* guild = g_game.getGuild(guildId);
|
||||
if (!guild) {
|
||||
guild = IOGuild::loadGuild(guildId);
|
||||
g_game.addGuild(guild);
|
||||
query.str(std::string());
|
||||
query << "SELECT `name` FROM `guilds` WHERE `id` = " << guildId;
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
guild = new Guild(guildId, result->getString("name"));
|
||||
g_game.addGuild(guild);
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `guild_id` = " << guildId;
|
||||
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
do {
|
||||
guild->addRank(result->getNumber<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("level"));
|
||||
} while (result->next());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (guild) {
|
||||
@ -421,7 +392,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
query.str(std::string());
|
||||
query << "SELECT `id`, `name`, `level` FROM `guild_ranks` WHERE `id` = " << playerRankId;
|
||||
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
guild->addRank(result->getNumber<uint32_t>("id"), result->getString("name"), result->getNumber<uint16_t>("level"));
|
||||
}
|
||||
|
||||
@ -433,11 +404,11 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
|
||||
player->guildRank = rank;
|
||||
|
||||
IOGuild::getWarList(guildId, player->guildWarVector);
|
||||
IOGuild::getWarList(guildId, player->guildWarList);
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT COUNT(*) AS `members` FROM `guild_membership` WHERE `guild_id` = " << guildId;
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
guild->setMemberCount(result->getNumber<uint32_t>("members"));
|
||||
}
|
||||
}
|
||||
@ -445,7 +416,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `player_id`, `name` FROM `player_spells` WHERE `player_id` = " << player->getGUID();
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
do {
|
||||
player->learnedInstantSpellList.emplace_front(result->getString("name"));
|
||||
} while (result->next());
|
||||
@ -456,7 +427,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_items` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
loadItems(itemMap, result);
|
||||
|
||||
for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
|
||||
@ -484,7 +455,7 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_depotitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
loadItems(itemMap, result);
|
||||
|
||||
for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
|
||||
@ -493,9 +464,15 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
|
||||
int32_t pid = pair.second;
|
||||
if (pid >= 0 && pid < 100) {
|
||||
DepotChest* depotChest = player->getDepotChest(pid, true);
|
||||
if (depotChest) {
|
||||
depotChest->internalAddThing(item);
|
||||
Container* itemContainer = item->getContainer();
|
||||
if (itemContainer) {
|
||||
DepotLocker* locker = itemContainer->getDepotLocker();
|
||||
if (locker) {
|
||||
DepotLocker* existingLocker = player->getDepotLocker(pid, false);
|
||||
if (!existingLocker) {
|
||||
player->depotLockerMap[pid] = locker;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ItemMap::const_iterator it2 = itemMap.find(pid);
|
||||
@ -511,49 +488,19 @@ bool IOLoginData::loadPlayer(Player* player, DBResult_ptr result)
|
||||
}
|
||||
}
|
||||
|
||||
//load inbox items
|
||||
itemMap.clear();
|
||||
|
||||
query.str(std::string());
|
||||
query << "SELECT `pid`, `sid`, `itemtype`, `count`, `attributes` FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID() << " ORDER BY `sid` DESC";
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
loadItems(itemMap, result);
|
||||
|
||||
for (ItemMap::const_reverse_iterator it = itemMap.rbegin(), end = itemMap.rend(); it != end; ++it) {
|
||||
const std::pair<Item*, int32_t>& pair = it->second;
|
||||
Item* item = pair.first;
|
||||
int32_t pid = pair.second;
|
||||
|
||||
if (pid >= 0 && pid < 100) {
|
||||
player->getInbox()->internalAddThing(item);
|
||||
} else {
|
||||
ItemMap::const_iterator it2 = itemMap.find(pid);
|
||||
|
||||
if (it2 == itemMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Container* container = it2->second.first->getContainer();
|
||||
if (container) {
|
||||
container->internalAddThing(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//load storage map
|
||||
query.str(std::string());
|
||||
query << "SELECT `key`, `value` FROM `player_storage` WHERE `player_id` = " << player->getGUID();
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
do {
|
||||
player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("value"), true);
|
||||
player->addStorageValue(result->getNumber<uint32_t>("key"), result->getNumber<int32_t>("value"));
|
||||
} while (result->next());
|
||||
}
|
||||
|
||||
//load vip
|
||||
query.str(std::string());
|
||||
query << "SELECT `player_id` FROM `account_viplist` WHERE `account_id` = " << player->getAccount();
|
||||
if ((result = db.storeQuery(query.str()))) {
|
||||
if ((result = db->storeQuery(query.str()))) {
|
||||
do {
|
||||
player->addVIPInternal(result->getNumber<uint32_t>("player_id"));
|
||||
} while (result->next());
|
||||
@ -569,12 +516,12 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
||||
{
|
||||
std::ostringstream ss;
|
||||
|
||||
using ContainerBlock = std::pair<Container*, int32_t>;
|
||||
std::list<ContainerBlock> queue;
|
||||
typedef std::pair<Container*, int32_t> containerBlock;
|
||||
std::list<containerBlock> queue;
|
||||
|
||||
int32_t runningId = 100;
|
||||
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
for (const auto& it : itemList) {
|
||||
int32_t pid = it.first;
|
||||
Item* item = it.second;
|
||||
@ -586,7 +533,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
||||
size_t attributesSize;
|
||||
const char* attributes = propWriteStream.getStream(attributesSize);
|
||||
|
||||
ss << player->getGUID() << ',' << pid << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize);
|
||||
ss << player->getGUID() << ',' << pid << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db->escapeBlob(attributes, attributesSize);
|
||||
if (!query_insert.addRow(ss)) {
|
||||
return false;
|
||||
}
|
||||
@ -597,7 +544,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
||||
}
|
||||
|
||||
while (!queue.empty()) {
|
||||
const ContainerBlock& cb = queue.front();
|
||||
const containerBlock& cb = queue.front();
|
||||
Container* container = cb.first;
|
||||
int32_t parentId = cb.second;
|
||||
queue.pop_front();
|
||||
@ -616,7 +563,7 @@ bool IOLoginData::saveItems(const Player* player, const ItemBlockList& itemList,
|
||||
size_t attributesSize;
|
||||
const char* attributes = propWriteStream.getStream(attributesSize);
|
||||
|
||||
ss << player->getGUID() << ',' << parentId << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db.escapeBlob(attributes, attributesSize);
|
||||
ss << player->getGUID() << ',' << parentId << ',' << runningId << ',' << item->getID() << ',' << item->getSubType() << ',' << db->escapeBlob(attributes, attributesSize);
|
||||
if (!query_insert.addRow(ss)) {
|
||||
return false;
|
||||
}
|
||||
@ -631,11 +578,11 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
player->changeHealth(1);
|
||||
}
|
||||
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `save` FROM `players` WHERE `id` = " << player->getGUID();
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -643,7 +590,7 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
if (result->getNumber<uint16_t>("save") == 0) {
|
||||
query.str(std::string());
|
||||
query << "UPDATE `players` SET `lastlogin` = " << player->lastLoginSaved << ", `lastip` = " << player->lastIP << " WHERE `id` = " << player->getGUID();
|
||||
return db.executeQuery(query.str());
|
||||
return db->executeQuery(query.str());
|
||||
}
|
||||
|
||||
//serialize conditions
|
||||
@ -686,7 +633,7 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
query << "`posz` = " << loginPosition.getZ() << ',';
|
||||
|
||||
query << "`cap` = " << (player->capacity / 100) << ',';
|
||||
query << "`sex` = " << static_cast<uint16_t>(player->sex) << ',';
|
||||
query << "`sex` = " << player->sex << ',';
|
||||
|
||||
if (player->lastLoginSaved != 0) {
|
||||
query << "`lastlogin` = " << player->lastLoginSaved << ',';
|
||||
@ -696,29 +643,21 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
query << "`lastip` = " << player->lastIP << ',';
|
||||
}
|
||||
|
||||
query << "`conditions` = " << db.escapeBlob(conditions, conditionsSize) << ',';
|
||||
query << "`conditions` = " << db->escapeBlob(conditions, conditionsSize) << ',';
|
||||
|
||||
if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
|
||||
int64_t skullTime = 0;
|
||||
|
||||
if (player->skullTicks > 0) {
|
||||
skullTime = time(nullptr) + player->skullTicks;
|
||||
}
|
||||
query << "`skulltime` = " << skullTime << ',';
|
||||
query << "`skulltime` = " << player->getPlayerKillerEnd() << ',';
|
||||
|
||||
Skulls_t skull = SKULL_NONE;
|
||||
if (player->skull == SKULL_RED) {
|
||||
skull = SKULL_RED;
|
||||
} else if (player->skull == SKULL_BLACK) {
|
||||
skull = SKULL_BLACK;
|
||||
}
|
||||
query << "`skull` = " << static_cast<int64_t>(skull) << ',';
|
||||
|
||||
query << "`skull` = " << static_cast<uint32_t>(skull) << ',';
|
||||
}
|
||||
|
||||
query << "`lastlogout` = " << player->getLastLogout() << ',';
|
||||
query << "`balance` = " << player->bankBalance << ',';
|
||||
query << "`offlinetraining_time` = " << player->getOfflineTrainingTime() / 1000 << ',';
|
||||
query << "`offlinetraining_skill` = " << player->getOfflineTrainingSkill() << ',';
|
||||
query << "`stamina` = " << player->getStaminaMinutes() << ',';
|
||||
|
||||
query << "`skill_fist` = " << player->skills[SKILL_FIST].level << ',';
|
||||
@ -735,7 +674,6 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
query << "`skill_shielding_tries` = " << player->skills[SKILL_SHIELD].tries << ',';
|
||||
query << "`skill_fishing` = " << player->skills[SKILL_FISHING].level << ',';
|
||||
query << "`skill_fishing_tries` = " << player->skills[SKILL_FISHING].tries << ',';
|
||||
query << "`direction` = " << static_cast<uint16_t> (player->getDirection()) << ',';
|
||||
|
||||
if (!player->isOffline()) {
|
||||
query << "`onlinetime` = `onlinetime` + " << (time(nullptr) - player->lastLoginSaved) << ',';
|
||||
@ -748,14 +686,14 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!db.executeQuery(query.str())) {
|
||||
if (!db->executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// learned spells
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `player_spells` WHERE `player_id` = " << player->getGUID();
|
||||
if (!db.executeQuery(query.str())) {
|
||||
if (!db->executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -763,7 +701,7 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
|
||||
DBInsert spellsQuery("INSERT INTO `player_spells` (`player_id`, `name` ) VALUES ");
|
||||
for (const std::string& spellName : player->learnedInstantSpellList) {
|
||||
query << player->getGUID() << ',' << db.escapeString(spellName);
|
||||
query << player->getGUID() << ',' << db->escapeString(spellName);
|
||||
if (!spellsQuery.addRow(query)) {
|
||||
return false;
|
||||
}
|
||||
@ -773,9 +711,31 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
return false;
|
||||
}
|
||||
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `player_murders` WHERE `player_id` = " << player->getGUID();
|
||||
|
||||
if (!db->executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
query.str(std::string());
|
||||
|
||||
DBInsert murdersQuery("INSERT INTO `player_murders`(`id`, `player_id`, `date`) VALUES ");
|
||||
for (time_t timestamp : player->murderTimeStamps) {
|
||||
query << "NULL," << player->getGUID() << ',' << timestamp;
|
||||
if (!murdersQuery.addRow(query)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!murdersQuery.execute()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//item saving
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `player_items` WHERE `player_id` = " << player->getGUID();
|
||||
if (!db.executeQuery(query.str())) {
|
||||
if (!db->executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -793,51 +753,28 @@ bool IOLoginData::savePlayer(Player* player)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player->lastDepotId != -1) {
|
||||
//save depot items
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID();
|
||||
|
||||
if (!db.executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
|
||||
itemList.clear();
|
||||
|
||||
for (const auto& it : player->depotChests) {
|
||||
DepotChest* depotChest = it.second;
|
||||
for (Item* item : depotChest->getItemList()) {
|
||||
itemList.emplace_back(it.first, item);
|
||||
}
|
||||
}
|
||||
|
||||
if (!saveItems(player, itemList, depotQuery, propWriteStream)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//save inbox items
|
||||
//save depot items
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `player_inboxitems` WHERE `player_id` = " << player->getGUID();
|
||||
if (!db.executeQuery(query.str())) {
|
||||
query << "DELETE FROM `player_depotitems` WHERE `player_id` = " << player->getGUID();
|
||||
|
||||
if (!db->executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBInsert inboxQuery("INSERT INTO `player_inboxitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
|
||||
DBInsert depotQuery("INSERT INTO `player_depotitems` (`player_id`, `pid`, `sid`, `itemtype`, `count`, `attributes`) VALUES ");
|
||||
itemList.clear();
|
||||
|
||||
for (Item* item : player->getInbox()->getItemList()) {
|
||||
itemList.emplace_back(0, item);
|
||||
for (const auto& it : player->depotLockerMap) {
|
||||
itemList.emplace_back(it.first, it.second);
|
||||
}
|
||||
|
||||
if (!saveItems(player, itemList, inboxQuery, propWriteStream)) {
|
||||
if (!saveItems(player, itemList, depotQuery, propWriteStream)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `player_storage` WHERE `player_id` = " << player->getGUID();
|
||||
if (!db.executeQuery(query.str())) {
|
||||
if (!db->executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -865,7 +802,7 @@ std::string IOLoginData::getNameByGuid(uint32_t guid)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "SELECT `name` FROM `players` WHERE `id` = " << guid;
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return std::string();
|
||||
}
|
||||
@ -874,11 +811,11 @@ std::string IOLoginData::getNameByGuid(uint32_t guid)
|
||||
|
||||
uint32_t IOLoginData::getGuidByName(const std::string& name)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id` FROM `players` WHERE `name` = " << db.escapeString(name);
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
query << "SELECT `id` FROM `players` WHERE `name` = " << db->escapeString(name);
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
@ -887,11 +824,11 @@ uint32_t IOLoginData::getGuidByName(const std::string& name)
|
||||
|
||||
bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string& name)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = " << db.escapeString(name);
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
query << "SELECT `name`, `id`, `group_id`, `account_id` FROM `players` WHERE `name` = " << db->escapeString(name);
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -913,12 +850,12 @@ bool IOLoginData::getGuidByNameEx(uint32_t& guid, bool& specialVip, std::string&
|
||||
|
||||
bool IOLoginData::formatPlayerName(std::string& name)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `name` FROM `players` WHERE `name` = " << db.escapeString(name);
|
||||
query << "SELECT `name` FROM `players` WHERE `name` = " << db->escapeString(name);
|
||||
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -957,16 +894,16 @@ void IOLoginData::increaseBankBalance(uint32_t guid, uint64_t bankBalance)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `players` SET `balance` = `balance` + " << bankBalance << " WHERE `id` = " << guid;
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
Database::getInstance()->executeQuery(query.str());
|
||||
}
|
||||
|
||||
bool IOLoginData::hasBiddedOnHouse(uint32_t guid)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id` FROM `houses` WHERE `highest_bidder` = " << guid << " LIMIT 1";
|
||||
return db.storeQuery(query.str()).get() != nullptr;
|
||||
return db->storeQuery(query.str()).get() != nullptr;
|
||||
}
|
||||
|
||||
std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId)
|
||||
@ -974,58 +911,46 @@ std::forward_list<VIPEntry> IOLoginData::getVIPEntries(uint32_t accountId)
|
||||
std::forward_list<VIPEntry> entries;
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name`, `description`, `icon`, `notify` FROM `account_viplist` WHERE `account_id` = " << accountId;
|
||||
query << "SELECT `player_id`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `name` FROM `account_viplist` WHERE `account_id` = " << accountId;
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
DBResult_ptr result = Database::getInstance()->storeQuery(query.str());
|
||||
if (result) {
|
||||
do {
|
||||
entries.emplace_front(
|
||||
result->getNumber<uint32_t>("player_id"),
|
||||
result->getString("name"),
|
||||
result->getString("description"),
|
||||
result->getNumber<uint32_t>("icon"),
|
||||
result->getNumber<uint16_t>("notify") != 0
|
||||
result->getString("name")
|
||||
);
|
||||
} while (result->next());
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify)
|
||||
void IOLoginData::addVIPEntry(uint32_t accountId, uint32_t guid)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "INSERT INTO `account_viplist` (`account_id`, `player_id`, `description`, `icon`, `notify`) VALUES (" << accountId << ',' << guid << ',' << db.escapeString(description) << ',' << icon << ',' << notify << ')';
|
||||
db.executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOLoginData::editVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify)
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `account_viplist` SET `description` = " << db.escapeString(description) << ", `icon` = " << icon << ", `notify` = " << notify << " WHERE `account_id` = " << accountId << " AND `player_id` = " << guid;
|
||||
db.executeQuery(query.str());
|
||||
query << "INSERT INTO `account_viplist` (`account_id`, `player_id`) VALUES (" << accountId << ',' << guid << ')';
|
||||
db->executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOLoginData::removeVIPEntry(uint32_t accountId, uint32_t guid)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "DELETE FROM `account_viplist` WHERE `account_id` = " << accountId << " AND `player_id` = " << guid;
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
Database::getInstance()->executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOLoginData::addPremiumDays(uint32_t accountId, int32_t addDays)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `accounts` SET `premdays` = `premdays` + " << addDays << " WHERE `id` = " << accountId;
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
Database::getInstance()->executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOLoginData::removePremiumDays(uint32_t accountId, int32_t removeDays)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `accounts` SET `premdays` = `premdays` - " << removeDays << " WHERE `id` = " << accountId;
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
Database::getInstance()->executeQuery(query.str());
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -24,7 +24,7 @@
|
||||
#include "player.h"
|
||||
#include "database.h"
|
||||
|
||||
using ItemBlockList = std::list<std::pair<int32_t, Item*>>;
|
||||
typedef std::list<std::pair<int32_t, Item*>> ItemBlockList;
|
||||
|
||||
class IOLoginData
|
||||
{
|
||||
@ -32,8 +32,8 @@ class IOLoginData
|
||||
static Account loadAccount(uint32_t accno);
|
||||
static bool saveAccount(const Account& acc);
|
||||
|
||||
static bool loginserverAuthentication(const std::string& name, const std::string& password, Account& account);
|
||||
static uint32_t gameworldAuthentication(const std::string& accountName, const std::string& password, std::string& characterName, std::string& token, uint32_t tokenTime);
|
||||
static bool loginserverAuthentication(uint32_t accountNumber, const std::string& password, Account& account);
|
||||
static uint32_t gameworldAuthentication(uint32_t accountNumber, const std::string& password, std::string& characterName);
|
||||
|
||||
static AccountType_t getAccountType(uint32_t accountId);
|
||||
static void setAccountType(uint32_t accountId, AccountType_t accountType);
|
||||
@ -52,18 +52,17 @@ class IOLoginData
|
||||
static bool hasBiddedOnHouse(uint32_t guid);
|
||||
|
||||
static std::forward_list<VIPEntry> getVIPEntries(uint32_t accountId);
|
||||
static void addVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify);
|
||||
static void editVIPEntry(uint32_t accountId, uint32_t guid, const std::string& description, uint32_t icon, bool notify);
|
||||
static void addVIPEntry(uint32_t accountId, uint32_t guid);
|
||||
static void removeVIPEntry(uint32_t accountId, uint32_t guid);
|
||||
|
||||
static void addPremiumDays(uint32_t accountId, int32_t addDays);
|
||||
static void removePremiumDays(uint32_t accountId, int32_t removeDays);
|
||||
|
||||
private:
|
||||
using ItemMap = std::map<uint32_t, std::pair<Item*, uint32_t>>;
|
||||
protected:
|
||||
typedef std::map<uint32_t, std::pair<Item*, uint32_t>> ItemMap;
|
||||
|
||||
static void loadItems(ItemMap& itemMap, DBResult_ptr result);
|
||||
static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& propWriteStream);
|
||||
static bool saveItems(const Player* player, const ItemBlockList& itemList, DBInsert& query_insert, PropWriteStream& stream);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
548
src/iomap.cpp
548
src/iomap.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -66,14 +66,23 @@ Tile* IOMap::createTile(Item*& ground, Item* item, uint16_t x, uint16_t y, uint8
|
||||
return tile;
|
||||
}
|
||||
|
||||
bool IOMap::loadMap(Map* map, const std::string& fileName)
|
||||
bool IOMap::loadMap(Map* map, const std::string& identifier)
|
||||
{
|
||||
int64_t start = OTSYS_TIME();
|
||||
OTB::Loader loader{fileName, OTB::Identifier{{'O', 'T', 'B', 'M'}}};
|
||||
auto& root = loader.parseTree();
|
||||
|
||||
FileLoader f;
|
||||
if (!f.openFile(identifier.c_str(), "OTBM")) {
|
||||
std::ostringstream ss;
|
||||
ss << "Could not open the file " << identifier << '.';
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t type;
|
||||
PropStream propStream;
|
||||
if (!loader.getProps(root, propStream)) {
|
||||
|
||||
NODE root = f.getChildNode(nullptr, type);
|
||||
if (!f.getProps(root, propStream)) {
|
||||
setLastErrorString("Could not read root property.");
|
||||
return false;
|
||||
}
|
||||
@ -85,7 +94,7 @@ bool IOMap::loadMap(Map* map, const std::string& fileName)
|
||||
}
|
||||
|
||||
uint32_t headerVersion = root_header.version;
|
||||
if (headerVersion == 0) {
|
||||
if (headerVersion <= 0) {
|
||||
//In otbm version 1 the count variable after splashes/fluidcontainers and stackables
|
||||
//are saved as attributes instead, this solves alot of problems with items
|
||||
//that is changed (stackable/charges/fluidcontainer/splash) during an update.
|
||||
@ -98,66 +107,17 @@ bool IOMap::loadMap(Map* map, const std::string& fileName)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root_header.majorVersionItems < 3) {
|
||||
setLastErrorString("This map need to be upgraded by using the latest map editor version to be able to load correctly.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root_header.majorVersionItems > Item::items.majorVersion) {
|
||||
setLastErrorString("The map was saved with a different items.otb version, an upgraded items.otb is required.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root_header.minorVersionItems < CLIENT_VERSION_810) {
|
||||
setLastErrorString("This map needs to be updated.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (root_header.minorVersionItems > Item::items.minorVersion) {
|
||||
std::cout << "[Warning - IOMap::loadMap] This map needs an updated items.otb." << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "> Map size: " << root_header.width << "x" << root_header.height << '.' << std::endl;
|
||||
map->width = root_header.width;
|
||||
map->height = root_header.height;
|
||||
|
||||
if (root.children.size() != 1 || root.children[0].type != OTBM_MAP_DATA) {
|
||||
NODE nodeMap = f.getChildNode(root, type);
|
||||
if (type != OTBM_MAP_DATA) {
|
||||
setLastErrorString("Could not read data node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& mapNode = root.children[0];
|
||||
if (!parseMapDataAttributes(loader, mapNode, *map, fileName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& mapDataNode : mapNode.children) {
|
||||
if (mapDataNode.type == OTBM_TILE_AREA) {
|
||||
if (!parseTileArea(loader, mapDataNode, *map)) {
|
||||
return false;
|
||||
}
|
||||
} else if (mapDataNode.type == OTBM_TOWNS) {
|
||||
if (!parseTowns(loader, mapDataNode, *map)) {
|
||||
return false;
|
||||
}
|
||||
} else if (mapDataNode.type == OTBM_WAYPOINTS && headerVersion > 1) {
|
||||
if (!parseWaypoints(loader, mapDataNode, *map)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
setLastErrorString("Unknown map node.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName)
|
||||
{
|
||||
PropStream propStream;
|
||||
if (!loader.getProps(mapNode, propStream)) {
|
||||
if (!f.getProps(nodeMap, propStream)) {
|
||||
setLastErrorString("Could not read map data attributes.");
|
||||
return false;
|
||||
}
|
||||
@ -181,8 +141,8 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode
|
||||
return false;
|
||||
}
|
||||
|
||||
map.spawnfile = fileName.substr(0, fileName.rfind('/') + 1);
|
||||
map.spawnfile += tmp;
|
||||
map->spawnfile = identifier.substr(0, identifier.rfind('/') + 1);
|
||||
map->spawnfile += tmp;
|
||||
break;
|
||||
|
||||
case OTBM_ATTR_EXT_HOUSE_FILE:
|
||||
@ -191,8 +151,8 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode
|
||||
return false;
|
||||
}
|
||||
|
||||
map.housefile = fileName.substr(0, fileName.rfind('/') + 1);
|
||||
map.housefile += tmp;
|
||||
map->housefile = identifier.substr(0, identifier.rfind('/') + 1);
|
||||
map->housefile += tmp;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -200,104 +160,172 @@ bool IOMap::parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map)
|
||||
{
|
||||
PropStream propStream;
|
||||
if (!loader.getProps(tileAreaNode, propStream)) {
|
||||
setLastErrorString("Invalid map node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
OTBM_Destination_coords area_coord;
|
||||
if (!propStream.read(area_coord)) {
|
||||
setLastErrorString("Invalid map node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t base_x = area_coord.x;
|
||||
uint16_t base_y = area_coord.y;
|
||||
uint16_t z = area_coord.z;
|
||||
|
||||
for (auto& tileNode : tileAreaNode.children) {
|
||||
if (tileNode.type != OTBM_TILE && tileNode.type != OTBM_HOUSETILE) {
|
||||
setLastErrorString("Unknown tile node.");
|
||||
NODE nodeMapData = f.getChildNode(nodeMap, type);
|
||||
while (nodeMapData != NO_NODE) {
|
||||
if (f.getError() != ERROR_NONE) {
|
||||
setLastErrorString("Invalid map node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loader.getProps(tileNode, propStream)) {
|
||||
setLastErrorString("Could not read node data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
OTBM_Tile_coords tile_coord;
|
||||
if (!propStream.read(tile_coord)) {
|
||||
setLastErrorString("Could not read tile position.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t x = base_x + tile_coord.x;
|
||||
uint16_t y = base_y + tile_coord.y;
|
||||
|
||||
bool isHouseTile = false;
|
||||
House* house = nullptr;
|
||||
Tile* tile = nullptr;
|
||||
Item* ground_item = nullptr;
|
||||
uint32_t tileflags = TILESTATE_NONE;
|
||||
|
||||
if (tileNode.type == OTBM_HOUSETILE) {
|
||||
uint32_t houseId;
|
||||
if (!propStream.read<uint32_t>(houseId)) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not read house id.";
|
||||
setLastErrorString(ss.str());
|
||||
if (type == OTBM_TILE_AREA) {
|
||||
if (!f.getProps(nodeMapData, propStream)) {
|
||||
setLastErrorString("Invalid map node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
house = map.houses.addHouse(houseId);
|
||||
if (!house) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId;
|
||||
setLastErrorString(ss.str());
|
||||
OTBM_Destination_coords area_coord;
|
||||
if (!propStream.read(area_coord)) {
|
||||
setLastErrorString("Invalid map node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
tile = new HouseTile(x, y, z, house);
|
||||
house->addTile(static_cast<HouseTile*>(tile));
|
||||
isHouseTile = true;
|
||||
}
|
||||
uint16_t base_x = area_coord.x;
|
||||
uint16_t base_y = area_coord.y;
|
||||
uint16_t z = area_coord.z;
|
||||
|
||||
uint8_t attribute;
|
||||
//read tile attributes
|
||||
while (propStream.read<uint8_t>(attribute)) {
|
||||
switch (attribute) {
|
||||
case OTBM_ATTR_TILE_FLAGS: {
|
||||
uint32_t flags;
|
||||
if (!propStream.read<uint32_t>(flags)) {
|
||||
NODE nodeTile = f.getChildNode(nodeMapData, type);
|
||||
while (nodeTile != NO_NODE) {
|
||||
if (f.getError() != ERROR_NONE) {
|
||||
setLastErrorString("Could not read node data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (type != OTBM_TILE && type != OTBM_HOUSETILE) {
|
||||
setLastErrorString("Unknown tile node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.getProps(nodeTile, propStream)) {
|
||||
setLastErrorString("Could not read node data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
OTBM_Tile_coords tile_coord;
|
||||
if (!propStream.read(tile_coord)) {
|
||||
setLastErrorString("Could not read tile position.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint16_t x = base_x + tile_coord.x;
|
||||
uint16_t y = base_y + tile_coord.y;
|
||||
|
||||
bool isHouseTile = false;
|
||||
House* house = nullptr;
|
||||
Tile* tile = nullptr;
|
||||
Item* ground_item = nullptr;
|
||||
uint32_t tileflags = TILESTATE_NONE;
|
||||
|
||||
if (type == OTBM_HOUSETILE) {
|
||||
uint32_t houseId;
|
||||
if (!propStream.read<uint32_t>(houseId)) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags.";
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not read house id.";
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) {
|
||||
tileflags |= TILESTATE_PROTECTIONZONE;
|
||||
} else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) {
|
||||
tileflags |= TILESTATE_NOPVPZONE;
|
||||
} else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) {
|
||||
tileflags |= TILESTATE_PVPZONE;
|
||||
house = map->houses.addHouse(houseId);
|
||||
if (!house) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Could not create house id: " << houseId;
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) {
|
||||
tileflags |= TILESTATE_NOLOGOUT;
|
||||
}
|
||||
break;
|
||||
tile = new HouseTile(x, y, z, house);
|
||||
house->addTile(static_cast<HouseTile*>(tile));
|
||||
isHouseTile = true;
|
||||
}
|
||||
|
||||
case OTBM_ATTR_ITEM: {
|
||||
Item* item = Item::CreateItem(propStream);
|
||||
//read tile attributes
|
||||
while (propStream.read<uint8_t>(attribute)) {
|
||||
switch (attribute) {
|
||||
case OTBM_ATTR_TILE_FLAGS: {
|
||||
uint32_t flags;
|
||||
if (!propStream.read<uint32_t>(flags)) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to read tile flags.";
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((flags & OTBM_TILEFLAG_PROTECTIONZONE) != 0) {
|
||||
tileflags |= TILESTATE_PROTECTIONZONE;
|
||||
} else if ((flags & OTBM_TILEFLAG_NOPVPZONE) != 0) {
|
||||
tileflags |= TILESTATE_NOPVPZONE;
|
||||
} else if ((flags & OTBM_TILEFLAG_PVPZONE) != 0) {
|
||||
tileflags |= TILESTATE_PVPZONE;
|
||||
}
|
||||
|
||||
if ((flags & OTBM_TILEFLAG_REFRESH) != 0) {
|
||||
tileflags |= TILESTATE_REFRESH;
|
||||
}
|
||||
|
||||
if ((flags & OTBM_TILEFLAG_NOLOGOUT) != 0) {
|
||||
tileflags |= TILESTATE_NOLOGOUT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OTBM_ATTR_ITEM: {
|
||||
Item* item = Item::CreateItem(propStream);
|
||||
if (!item) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item.";
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isHouseTile && item->isMoveable()) {
|
||||
//std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl;
|
||||
delete item;
|
||||
} else {
|
||||
if (item->getItemCount() <= 0) {
|
||||
item->setItemCount(1);
|
||||
}
|
||||
|
||||
if (tile) {
|
||||
tile->internalAddThing(item);
|
||||
item->startDecaying();
|
||||
item->setLoadedFromMap(true);
|
||||
} else if (item->isGroundTile()) {
|
||||
delete ground_item;
|
||||
ground_item = item;
|
||||
} else {
|
||||
tile = createTile(ground_item, item, x, y, z);
|
||||
tile->internalAddThing(item);
|
||||
item->startDecaying();
|
||||
item->setLoadedFromMap(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute.";
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
NODE nodeItem = f.getChildNode(nodeTile, type);
|
||||
while (nodeItem) {
|
||||
if (type != OTBM_ITEM) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown node type.";
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
PropStream stream;
|
||||
if (!f.getProps(nodeItem, stream)) {
|
||||
setLastErrorString("Invalid item node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Item* item = Item::CreateItem(stream);
|
||||
if (!item) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item.";
|
||||
@ -305,11 +333,19 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!item->unserializeItemNode(f, nodeItem, stream)) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to load item " << item->getID() << '.';
|
||||
setLastErrorString(ss.str());
|
||||
delete item;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isHouseTile && item->isMoveable()) {
|
||||
std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl;
|
||||
//std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl;
|
||||
delete item;
|
||||
} else {
|
||||
if (item->getItemCount() == 0) {
|
||||
if (item->getItemCount() <= 0) {
|
||||
item->setItemCount(1);
|
||||
}
|
||||
|
||||
@ -327,156 +363,100 @@ bool IOMap::parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Ma
|
||||
item->setLoadedFromMap(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
nodeItem = f.getNextNode(nodeItem, type);
|
||||
}
|
||||
|
||||
default:
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown tile attribute.";
|
||||
setLastErrorString(ss.str());
|
||||
if (!tile) {
|
||||
tile = createTile(ground_item, nullptr, x, y, z);
|
||||
}
|
||||
|
||||
tile->setFlag(static_cast<tileflags_t>(tileflags));
|
||||
|
||||
map->setTile(x, y, z, tile);
|
||||
|
||||
nodeTile = f.getNextNode(nodeTile, type);
|
||||
}
|
||||
} else if (type == OTBM_TOWNS) {
|
||||
NODE nodeTown = f.getChildNode(nodeMapData, type);
|
||||
while (nodeTown != NO_NODE) {
|
||||
if (type != OTBM_TOWN) {
|
||||
setLastErrorString("Unknown town node.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& itemNode : tileNode.children) {
|
||||
if (itemNode.type != OTBM_ITEM) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Unknown node type.";
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
PropStream stream;
|
||||
if (!loader.getProps(itemNode, stream)) {
|
||||
setLastErrorString("Invalid item node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Item* item = Item::CreateItem(stream);
|
||||
if (!item) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to create item.";
|
||||
setLastErrorString(ss.str());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!item->unserializeItemNode(loader, itemNode, stream)) {
|
||||
std::ostringstream ss;
|
||||
ss << "[x:" << x << ", y:" << y << ", z:" << z << "] Failed to load item " << item->getID() << '.';
|
||||
setLastErrorString(ss.str());
|
||||
delete item;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isHouseTile && item->isMoveable()) {
|
||||
std::cout << "[Warning - IOMap::loadMap] Moveable item with ID: " << item->getID() << ", in house: " << house->getId() << ", at position [x: " << x << ", y: " << y << ", z: " << z << "]." << std::endl;
|
||||
delete item;
|
||||
} else {
|
||||
if (item->getItemCount() == 0) {
|
||||
item->setItemCount(1);
|
||||
}
|
||||
|
||||
if (tile) {
|
||||
tile->internalAddThing(item);
|
||||
item->startDecaying();
|
||||
item->setLoadedFromMap(true);
|
||||
} else if (item->isGroundTile()) {
|
||||
delete ground_item;
|
||||
ground_item = item;
|
||||
} else {
|
||||
tile = createTile(ground_item, item, x, y, z);
|
||||
tile->internalAddThing(item);
|
||||
item->startDecaying();
|
||||
item->setLoadedFromMap(true);
|
||||
if (!f.getProps(nodeTown, propStream)) {
|
||||
setLastErrorString("Could not read town data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t townId;
|
||||
if (!propStream.read<uint32_t>(townId)) {
|
||||
setLastErrorString("Could not read town id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Town* town = map->towns.getTown(townId);
|
||||
if (!town) {
|
||||
town = new Town(townId);
|
||||
map->towns.addTown(townId, town);
|
||||
}
|
||||
|
||||
std::string townName;
|
||||
if (!propStream.readString(townName)) {
|
||||
setLastErrorString("Could not read town name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
town->setName(townName);
|
||||
|
||||
OTBM_Destination_coords town_coords;
|
||||
if (!propStream.read(town_coords)) {
|
||||
setLastErrorString("Could not read town coordinates.");
|
||||
return false;
|
||||
}
|
||||
|
||||
town->setTemplePos(Position(town_coords.x, town_coords.y, town_coords.z));
|
||||
|
||||
nodeTown = f.getNextNode(nodeTown, type);
|
||||
}
|
||||
} else if (type == OTBM_WAYPOINTS) {
|
||||
NODE nodeWaypoint = f.getChildNode(nodeMapData, type);
|
||||
while (nodeWaypoint != NO_NODE) {
|
||||
if (type != OTBM_WAYPOINT) {
|
||||
setLastErrorString("Unknown waypoint node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!f.getProps(nodeWaypoint, propStream)) {
|
||||
setLastErrorString("Could not read waypoint data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
if (!propStream.readString(name)) {
|
||||
setLastErrorString("Could not read waypoint name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
OTBM_Destination_coords waypoint_coords;
|
||||
if (!propStream.read(waypoint_coords)) {
|
||||
setLastErrorString("Could not read waypoint coordinates.");
|
||||
return false;
|
||||
}
|
||||
|
||||
map->waypoints[name] = Position(waypoint_coords.x, waypoint_coords.y, waypoint_coords.z);
|
||||
|
||||
nodeWaypoint = f.getNextNode(nodeWaypoint, type);
|
||||
}
|
||||
} else {
|
||||
setLastErrorString("Unknown map node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tile) {
|
||||
tile = createTile(ground_item, nullptr, x, y, z);
|
||||
}
|
||||
|
||||
tile->setFlag(static_cast<tileflags_t>(tileflags));
|
||||
|
||||
map.setTile(x, y, z, tile);
|
||||
nodeMapData = f.getNextNode(nodeMapData, type);
|
||||
}
|
||||
|
||||
std::cout << "> Map loading time: " << (OTSYS_TIME() - start) / (1000.) << " seconds." << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IOMap::parseTowns(OTB::Loader& loader, const OTB::Node& townsNode, Map& map)
|
||||
{
|
||||
for (auto& townNode : townsNode.children) {
|
||||
PropStream propStream;
|
||||
if (townNode.type != OTBM_TOWN) {
|
||||
setLastErrorString("Unknown town node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loader.getProps(townNode, propStream)) {
|
||||
setLastErrorString("Could not read town data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t townId;
|
||||
if (!propStream.read<uint32_t>(townId)) {
|
||||
setLastErrorString("Could not read town id.");
|
||||
return false;
|
||||
}
|
||||
|
||||
Town* town = map.towns.getTown(townId);
|
||||
if (!town) {
|
||||
town = new Town(townId);
|
||||
map.towns.addTown(townId, town);
|
||||
}
|
||||
|
||||
std::string townName;
|
||||
if (!propStream.readString(townName)) {
|
||||
setLastErrorString("Could not read town name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
town->setName(townName);
|
||||
|
||||
OTBM_Destination_coords town_coords;
|
||||
if (!propStream.read(town_coords)) {
|
||||
setLastErrorString("Could not read town coordinates.");
|
||||
return false;
|
||||
}
|
||||
|
||||
town->setTemplePos(Position(town_coords.x, town_coords.y, town_coords.z));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool IOMap::parseWaypoints(OTB::Loader& loader, const OTB::Node& waypointsNode, Map& map)
|
||||
{
|
||||
PropStream propStream;
|
||||
for (auto& node : waypointsNode.children) {
|
||||
if (node.type != OTBM_WAYPOINT) {
|
||||
setLastErrorString("Unknown waypoint node.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loader.getProps(node, propStream)) {
|
||||
setLastErrorString("Could not read waypoint data.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string name;
|
||||
if (!propStream.readString(name)) {
|
||||
setLastErrorString("Could not read waypoint name.");
|
||||
return false;
|
||||
}
|
||||
|
||||
OTBM_Destination_coords waypoint_coords;
|
||||
if (!propStream.read(waypoint_coords)) {
|
||||
setLastErrorString("Could not read waypoint coordinates.");
|
||||
return false;
|
||||
}
|
||||
|
||||
map.waypoints[name] = Position(waypoint_coords.x, waypoint_coords.y, waypoint_coords.z);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
23
src/iomap.h
23
src/iomap.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -33,7 +33,7 @@ enum OTBM_AttrTypes_t {
|
||||
OTBM_ATTR_EXT_FILE = 2,
|
||||
OTBM_ATTR_TILE_FLAGS = 3,
|
||||
OTBM_ATTR_ACTION_ID = 4,
|
||||
OTBM_ATTR_UNIQUE_ID = 5,
|
||||
OTBM_ATTR_MOVEMENT_ID = 5,
|
||||
OTBM_ATTR_TEXT = 6,
|
||||
OTBM_ATTR_DESC = 7,
|
||||
OTBM_ATTR_TELE_DEST = 8,
|
||||
@ -51,6 +51,12 @@ enum OTBM_AttrTypes_t {
|
||||
OTBM_ATTR_SLEEPERGUID = 20,
|
||||
OTBM_ATTR_SLEEPSTART = 21,
|
||||
OTBM_ATTR_CHARGES = 22,
|
||||
OTBM_ATTR_KEYNUMBER = 23,
|
||||
OTBM_ATTR_KEYHOLENUMBER = 24,
|
||||
OTBM_ATTR_DOORQUESTNUMBER = 25,
|
||||
OTBM_ATTR_DOORQUESTVALUE = 26,
|
||||
OTBM_ATTR_DOORLEVEL = 27,
|
||||
OTBM_ATTR_CHESTQUESTNUMBER = 28,
|
||||
};
|
||||
|
||||
enum OTBM_NodeTypes_t {
|
||||
@ -76,7 +82,8 @@ enum OTBM_TileFlag_t : uint32_t {
|
||||
OTBM_TILEFLAG_PROTECTIONZONE = 1 << 0,
|
||||
OTBM_TILEFLAG_NOPVPZONE = 1 << 2,
|
||||
OTBM_TILEFLAG_NOLOGOUT = 1 << 3,
|
||||
OTBM_TILEFLAG_PVPZONE = 1 << 4
|
||||
OTBM_TILEFLAG_PVPZONE = 1 << 4,
|
||||
OTBM_TILEFLAG_REFRESH = 1 << 5,
|
||||
};
|
||||
|
||||
#pragma pack(1)
|
||||
@ -107,7 +114,7 @@ class IOMap
|
||||
static Tile* createTile(Item*& ground, Item* item, uint16_t x, uint16_t y, uint8_t z);
|
||||
|
||||
public:
|
||||
bool loadMap(Map* map, const std::string& fileName);
|
||||
bool loadMap(Map* map, const std::string& identifier);
|
||||
|
||||
/* Load the spawns
|
||||
* \param map pointer to the Map class
|
||||
@ -147,11 +154,7 @@ class IOMap
|
||||
errorString = error;
|
||||
}
|
||||
|
||||
private:
|
||||
bool parseMapDataAttributes(OTB::Loader& loader, const OTB::Node& mapNode, Map& map, const std::string& fileName);
|
||||
bool parseWaypoints(OTB::Loader& loader, const OTB::Node& waypointsNode, Map& map);
|
||||
bool parseTowns(OTB::Loader& loader, const OTB::Node& townsNode, Map& map);
|
||||
bool parseTileArea(OTB::Loader& loader, const OTB::Node& tileAreaNode, Map& map);
|
||||
protected:
|
||||
std::string errorString;
|
||||
};
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -29,7 +29,7 @@ void IOMapSerialize::loadHouseItems(Map* map)
|
||||
{
|
||||
int64_t start = OTSYS_TIME();
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery("SELECT `data` FROM `tile_store`");
|
||||
DBResult_ptr result = Database::getInstance()->storeQuery("SELECT `data` FROM `tile_store`");
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
@ -67,7 +67,7 @@ void IOMapSerialize::loadHouseItems(Map* map)
|
||||
bool IOMapSerialize::saveHouseItems()
|
||||
{
|
||||
int64_t start = OTSYS_TIME();
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
std::ostringstream query;
|
||||
|
||||
//Start the transaction
|
||||
@ -77,7 +77,7 @@ bool IOMapSerialize::saveHouseItems()
|
||||
}
|
||||
|
||||
//clear old tile data
|
||||
if (!db.executeQuery("DELETE FROM `tile_store`")) {
|
||||
if (!db->executeQuery("DELETE FROM `tile_store`")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ bool IOMapSerialize::saveHouseItems()
|
||||
size_t attributesSize;
|
||||
const char* attributes = stream.getStream(attributesSize);
|
||||
if (attributesSize > 0) {
|
||||
query << house->getId() << ',' << db.escapeBlob(attributes, attributesSize);
|
||||
query << house->getId() << ',' << db->escapeBlob(attributes, attributesSize);
|
||||
if (!stmt.addRow(query)) {
|
||||
return false;
|
||||
}
|
||||
@ -144,7 +144,7 @@ bool IOMapSerialize::loadItem(PropStream& propStream, Cylinder* parent)
|
||||
}
|
||||
|
||||
const ItemType& iType = Item::items[id];
|
||||
if (iType.moveable || iType.forceSerialize || !tile) {
|
||||
if (iType.moveable || !tile) {
|
||||
//create a new item
|
||||
Item* item = Item::CreateItem(id);
|
||||
if (item) {
|
||||
@ -247,7 +247,7 @@ void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile)
|
||||
const ItemType& it = Item::items[item->getID()];
|
||||
|
||||
// Note that these are NEGATED, ie. these are the items that will be saved.
|
||||
if (!(it.moveable || it.forceSerialize || item->getDoor() || (item->getContainer() && !item->getContainer()->empty()) || it.canWriteText || item->getBed())) {
|
||||
if (!(it.moveable || item->getDoor() || (item->getContainer() && !item->getContainer()->empty()) || it.canWriteText || item->getBed())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -270,9 +270,9 @@ void IOMapSerialize::saveTile(PropWriteStream& stream, const Tile* tile)
|
||||
|
||||
bool IOMapSerialize::loadHouseInfo()
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
DBResult_ptr result = db.storeQuery("SELECT `id`, `owner`, `paid`, `warnings` FROM `houses`");
|
||||
DBResult_ptr result = db->storeQuery("SELECT `id`, `owner`, `paid`, `warnings` FROM `houses`");
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
@ -286,7 +286,7 @@ bool IOMapSerialize::loadHouseInfo()
|
||||
}
|
||||
} while (result->next());
|
||||
|
||||
result = db.storeQuery("SELECT `house_id`, `listid`, `list` FROM `house_lists`");
|
||||
result = db->storeQuery("SELECT `house_id`, `listid`, `list` FROM `house_lists`");
|
||||
if (result) {
|
||||
do {
|
||||
House* house = g_game.map.houses.getHouse(result->getNumber<uint32_t>("house_id"));
|
||||
@ -300,14 +300,14 @@ bool IOMapSerialize::loadHouseInfo()
|
||||
|
||||
bool IOMapSerialize::saveHouseInfo()
|
||||
{
|
||||
Database& db = Database::getInstance();
|
||||
Database* db = Database::getInstance();
|
||||
|
||||
DBTransaction transaction;
|
||||
if (!transaction.begin()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!db.executeQuery("DELETE FROM `house_lists`")) {
|
||||
if (!db->executeQuery("DELETE FROM `house_lists`")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -315,16 +315,16 @@ bool IOMapSerialize::saveHouseInfo()
|
||||
for (const auto& it : g_game.map.houses.getHouses()) {
|
||||
House* house = it.second;
|
||||
query << "SELECT `id` FROM `houses` WHERE `id` = " << house->getId();
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
DBResult_ptr result = db->storeQuery(query.str());
|
||||
if (result) {
|
||||
query.str(std::string());
|
||||
query << "UPDATE `houses` SET `owner` = " << house->getOwner() << ", `paid` = " << house->getPaidUntil() << ", `warnings` = " << house->getPayRentWarnings() << ", `name` = " << db.escapeString(house->getName()) << ", `town_id` = " << house->getTownId() << ", `rent` = " << house->getRent() << ", `size` = " << house->getTiles().size() << ", `beds` = " << house->getBedCount() << " WHERE `id` = " << house->getId();
|
||||
query << "UPDATE `houses` SET `owner` = " << house->getOwner() << ", `paid` = " << house->getPaidUntil() << ", `warnings` = " << house->getPayRentWarnings() << ", `name` = " << db->escapeString(house->getName()) << ", `town_id` = " << house->getTownId() << ", `rent` = " << house->getRent() << ", `size` = " << house->getTiles().size() << ", `beds` = " << house->getBedCount() << " WHERE `id` = " << house->getId();
|
||||
} else {
|
||||
query.str(std::string());
|
||||
query << "INSERT INTO `houses` (`id`, `owner`, `paid`, `warnings`, `name`, `town_id`, `rent`, `size`, `beds`) VALUES (" << house->getId() << ',' << house->getOwner() << ',' << house->getPaidUntil() << ',' << house->getPayRentWarnings() << ',' << db.escapeString(house->getName()) << ',' << house->getTownId() << ',' << house->getRent() << ',' << house->getTiles().size() << ',' << house->getBedCount() << ')';
|
||||
query << "INSERT INTO `houses` (`id`, `owner`, `paid`, `warnings`, `name`, `town_id`, `rent`, `size`, `beds`) VALUES (" << house->getId() << ',' << house->getOwner() << ',' << house->getPaidUntil() << ',' << house->getPayRentWarnings() << ',' << db->escapeString(house->getName()) << ',' << house->getTownId() << ',' << house->getRent() << ',' << house->getTiles().size() << ',' << house->getBedCount() << ')';
|
||||
}
|
||||
|
||||
db.executeQuery(query.str());
|
||||
db->executeQuery(query.str());
|
||||
query.str(std::string());
|
||||
}
|
||||
|
||||
@ -335,7 +335,7 @@ bool IOMapSerialize::saveHouseInfo()
|
||||
|
||||
std::string listText;
|
||||
if (house->getAccessList(GUEST_LIST, listText) && !listText.empty()) {
|
||||
query << house->getId() << ',' << GUEST_LIST << ',' << db.escapeString(listText);
|
||||
query << house->getId() << ',' << GUEST_LIST << ',' << db->escapeString(listText);
|
||||
if (!stmt.addRow(query)) {
|
||||
return false;
|
||||
}
|
||||
@ -344,7 +344,7 @@ bool IOMapSerialize::saveHouseInfo()
|
||||
}
|
||||
|
||||
if (house->getAccessList(SUBOWNER_LIST, listText) && !listText.empty()) {
|
||||
query << house->getId() << ',' << SUBOWNER_LIST << ',' << db.escapeString(listText);
|
||||
query << house->getId() << ',' << SUBOWNER_LIST << ',' << db->escapeString(listText);
|
||||
if (!stmt.addRow(query)) {
|
||||
return false;
|
||||
}
|
||||
@ -354,7 +354,7 @@ bool IOMapSerialize::saveHouseInfo()
|
||||
|
||||
for (Door* door : house->getDoors()) {
|
||||
if (door->getAccessList(listText) && !listText.empty()) {
|
||||
query << house->getId() << ',' << door->getDoorId() << ',' << db.escapeString(listText);
|
||||
query << house->getId() << ',' << door->getDoorId() << ',' << db->escapeString(listText);
|
||||
if (!stmt.addRow(query)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -31,7 +31,7 @@ class IOMapSerialize
|
||||
static bool loadHouseInfo();
|
||||
static bool saveHouseInfo();
|
||||
|
||||
private:
|
||||
protected:
|
||||
static void saveItem(PropWriteStream& stream, const Item* item);
|
||||
static void saveTile(PropWriteStream& stream, const Tile* tile);
|
||||
|
||||
|
347
src/iomarket.cpp
347
src/iomarket.cpp
@ -1,347 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#include "iomarket.h"
|
||||
|
||||
#include "configmanager.h"
|
||||
#include "databasetasks.h"
|
||||
#include "iologindata.h"
|
||||
#include "game.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
extern ConfigManager g_config;
|
||||
extern Game g_game;
|
||||
|
||||
MarketOfferList IOMarket::getActiveOffers(MarketAction_t action, uint16_t itemId)
|
||||
{
|
||||
MarketOfferList offerList;
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `amount`, `price`, `created`, `anonymous`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `player_name` FROM `market_offers` WHERE `sale` = " << action << " AND `itemtype` = " << itemId;
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
if (!result) {
|
||||
return offerList;
|
||||
}
|
||||
|
||||
const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);
|
||||
|
||||
do {
|
||||
MarketOffer offer;
|
||||
offer.amount = result->getNumber<uint16_t>("amount");
|
||||
offer.price = result->getNumber<uint32_t>("price");
|
||||
offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration;
|
||||
offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
|
||||
if (result->getNumber<uint16_t>("anonymous") == 0) {
|
||||
offer.playerName = result->getString("player_name");
|
||||
} else {
|
||||
offer.playerName = "Anonymous";
|
||||
}
|
||||
offerList.push_back(offer);
|
||||
} while (result->next());
|
||||
return offerList;
|
||||
}
|
||||
|
||||
MarketOfferList IOMarket::getOwnOffers(MarketAction_t action, uint32_t playerId)
|
||||
{
|
||||
MarketOfferList offerList;
|
||||
|
||||
const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `amount`, `price`, `created`, `itemtype` FROM `market_offers` WHERE `player_id` = " << playerId << " AND `sale` = " << action;
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
if (!result) {
|
||||
return offerList;
|
||||
}
|
||||
|
||||
do {
|
||||
MarketOffer offer;
|
||||
offer.amount = result->getNumber<uint16_t>("amount");
|
||||
offer.price = result->getNumber<uint32_t>("price");
|
||||
offer.timestamp = result->getNumber<uint32_t>("created") + marketOfferDuration;
|
||||
offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
|
||||
offer.itemId = result->getNumber<uint16_t>("itemtype");
|
||||
offerList.push_back(offer);
|
||||
} while (result->next());
|
||||
return offerList;
|
||||
}
|
||||
|
||||
HistoryMarketOfferList IOMarket::getOwnHistory(MarketAction_t action, uint32_t playerId)
|
||||
{
|
||||
HistoryMarketOfferList offerList;
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `itemtype`, `amount`, `price`, `expires_at`, `state` FROM `market_history` WHERE `player_id` = " << playerId << " AND `sale` = " << action;
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
if (!result) {
|
||||
return offerList;
|
||||
}
|
||||
|
||||
do {
|
||||
HistoryMarketOffer offer;
|
||||
offer.itemId = result->getNumber<uint16_t>("itemtype");
|
||||
offer.amount = result->getNumber<uint16_t>("amount");
|
||||
offer.price = result->getNumber<uint32_t>("price");
|
||||
offer.timestamp = result->getNumber<uint32_t>("expires_at");
|
||||
|
||||
MarketOfferState_t offerState = static_cast<MarketOfferState_t>(result->getNumber<uint16_t>("state"));
|
||||
if (offerState == OFFERSTATE_ACCEPTEDEX) {
|
||||
offerState = OFFERSTATE_ACCEPTED;
|
||||
}
|
||||
|
||||
offer.state = offerState;
|
||||
|
||||
offerList.push_back(offer);
|
||||
} while (result->next());
|
||||
return offerList;
|
||||
}
|
||||
|
||||
void IOMarket::processExpiredOffers(DBResult_ptr result, bool)
|
||||
{
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
if (!IOMarket::moveOfferToHistory(result->getNumber<uint32_t>("id"), OFFERSTATE_EXPIRED)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint32_t playerId = result->getNumber<uint32_t>("player_id");
|
||||
const uint16_t amount = result->getNumber<uint16_t>("amount");
|
||||
if (result->getNumber<uint16_t>("sale") == 1) {
|
||||
const ItemType& itemType = Item::items[result->getNumber<uint16_t>("itemtype")];
|
||||
if (itemType.id == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Player* player = g_game.getPlayerByGUID(playerId);
|
||||
if (!player) {
|
||||
player = new Player(nullptr);
|
||||
if (!IOLoginData::loadPlayerById(player, playerId)) {
|
||||
delete player;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemType.stackable) {
|
||||
uint16_t tmpAmount = amount;
|
||||
while (tmpAmount > 0) {
|
||||
uint16_t stackCount = std::min<uint16_t>(100, tmpAmount);
|
||||
Item* item = Item::CreateItem(itemType.id, stackCount);
|
||||
if (g_game.internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) {
|
||||
delete item;
|
||||
break;
|
||||
}
|
||||
|
||||
tmpAmount -= stackCount;
|
||||
}
|
||||
} else {
|
||||
int32_t subType;
|
||||
if (itemType.charges != 0) {
|
||||
subType = itemType.charges;
|
||||
} else {
|
||||
subType = -1;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < amount; ++i) {
|
||||
Item* item = Item::CreateItem(itemType.id, subType);
|
||||
if (g_game.internalAddItem(player->getInbox(), item, INDEX_WHEREEVER, FLAG_NOLIMIT) != RETURNVALUE_NOERROR) {
|
||||
delete item;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (player->isOffline()) {
|
||||
IOLoginData::savePlayer(player);
|
||||
delete player;
|
||||
}
|
||||
} else {
|
||||
uint64_t totalPrice = result->getNumber<uint64_t>("price") * amount;
|
||||
|
||||
Player* player = g_game.getPlayerByGUID(playerId);
|
||||
if (player) {
|
||||
player->setBankBalance(player->getBankBalance() + totalPrice);
|
||||
} else {
|
||||
IOLoginData::increaseBankBalance(playerId, totalPrice);
|
||||
}
|
||||
}
|
||||
} while (result->next());
|
||||
}
|
||||
|
||||
void IOMarket::checkExpiredOffers()
|
||||
{
|
||||
const time_t lastExpireDate = time(nullptr) - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `amount`, `price`, `itemtype`, `player_id`, `sale` FROM `market_offers` WHERE `created` <= " << lastExpireDate;
|
||||
g_databaseTasks.addTask(query.str(), IOMarket::processExpiredOffers, true);
|
||||
|
||||
int32_t checkExpiredMarketOffersEachMinutes = g_config.getNumber(ConfigManager::CHECK_EXPIRED_MARKET_OFFERS_EACH_MINUTES);
|
||||
if (checkExpiredMarketOffersEachMinutes <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
g_scheduler.addEvent(createSchedulerTask(checkExpiredMarketOffersEachMinutes * 60 * 1000, IOMarket::checkExpiredOffers));
|
||||
}
|
||||
|
||||
uint32_t IOMarket::getPlayerOfferCount(uint32_t playerId)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "SELECT COUNT(*) AS `count` FROM `market_offers` WHERE `player_id` = " << playerId;
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
if (!result) {
|
||||
return 0;
|
||||
}
|
||||
return result->getNumber<int32_t>("count");
|
||||
}
|
||||
|
||||
MarketOfferEx IOMarket::getOfferByCounter(uint32_t timestamp, uint16_t counter)
|
||||
{
|
||||
MarketOfferEx offer;
|
||||
|
||||
const int32_t created = timestamp - g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `id`, `sale`, `itemtype`, `amount`, `created`, `price`, `player_id`, `anonymous`, (SELECT `name` FROM `players` WHERE `id` = `player_id`) AS `player_name` FROM `market_offers` WHERE `created` = " << created << " AND (`id` & 65535) = " << counter << " LIMIT 1";
|
||||
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
if (!result) {
|
||||
offer.id = 0;
|
||||
return offer;
|
||||
}
|
||||
|
||||
offer.id = result->getNumber<uint32_t>("id");
|
||||
offer.type = static_cast<MarketAction_t>(result->getNumber<uint16_t>("sale"));
|
||||
offer.amount = result->getNumber<uint16_t>("amount");
|
||||
offer.counter = result->getNumber<uint32_t>("id") & 0xFFFF;
|
||||
offer.timestamp = result->getNumber<uint32_t>("created");
|
||||
offer.price = result->getNumber<uint32_t>("price");
|
||||
offer.itemId = result->getNumber<uint16_t>("itemtype");
|
||||
offer.playerId = result->getNumber<uint32_t>("player_id");
|
||||
if (result->getNumber<uint16_t>("anonymous") == 0) {
|
||||
offer.playerName = result->getString("player_name");
|
||||
} else {
|
||||
offer.playerName = "Anonymous";
|
||||
}
|
||||
return offer;
|
||||
}
|
||||
|
||||
void IOMarket::createOffer(uint32_t playerId, MarketAction_t action, uint32_t itemId, uint16_t amount, uint32_t price, bool anonymous)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "INSERT INTO `market_offers` (`player_id`, `sale`, `itemtype`, `amount`, `price`, `created`, `anonymous`) VALUES (" << playerId << ',' << action << ',' << itemId << ',' << amount << ',' << price << ',' << time(nullptr) << ',' << anonymous << ')';
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOMarket::acceptOffer(uint32_t offerId, uint16_t amount)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "UPDATE `market_offers` SET `amount` = `amount` - " << amount << " WHERE `id` = " << offerId;
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOMarket::deleteOffer(uint32_t offerId)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "DELETE FROM `market_offers` WHERE `id` = " << offerId;
|
||||
Database::getInstance().executeQuery(query.str());
|
||||
}
|
||||
|
||||
void IOMarket::appendHistory(uint32_t playerId, MarketAction_t type, uint16_t itemId, uint16_t amount, uint32_t price, time_t timestamp, MarketOfferState_t state)
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "INSERT INTO `market_history` (`player_id`, `sale`, `itemtype`, `amount`, `price`, `expires_at`, `inserted`, `state`) VALUES ("
|
||||
<< playerId << ',' << type << ',' << itemId << ',' << amount << ',' << price << ','
|
||||
<< timestamp << ',' << time(nullptr) << ',' << state << ')';
|
||||
g_databaseTasks.addTask(query.str());
|
||||
}
|
||||
|
||||
bool IOMarket::moveOfferToHistory(uint32_t offerId, MarketOfferState_t state)
|
||||
{
|
||||
const int32_t marketOfferDuration = g_config.getNumber(ConfigManager::MARKET_OFFER_DURATION);
|
||||
|
||||
Database& db = Database::getInstance();
|
||||
|
||||
std::ostringstream query;
|
||||
query << "SELECT `player_id`, `sale`, `itemtype`, `amount`, `price`, `created` FROM `market_offers` WHERE `id` = " << offerId;
|
||||
|
||||
DBResult_ptr result = db.storeQuery(query.str());
|
||||
if (!result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
query.str(std::string());
|
||||
query << "DELETE FROM `market_offers` WHERE `id` = " << offerId;
|
||||
if (!db.executeQuery(query.str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
appendHistory(result->getNumber<uint32_t>("player_id"), static_cast<MarketAction_t>(result->getNumber<uint16_t>("sale")), result->getNumber<uint16_t>("itemtype"), result->getNumber<uint16_t>("amount"), result->getNumber<uint32_t>("price"), result->getNumber<uint32_t>("created") + marketOfferDuration, state);
|
||||
return true;
|
||||
}
|
||||
|
||||
void IOMarket::updateStatistics()
|
||||
{
|
||||
std::ostringstream query;
|
||||
query << "SELECT `sale` AS `sale`, `itemtype` AS `itemtype`, COUNT(`price`) AS `num`, MIN(`price`) AS `min`, MAX(`price`) AS `max`, SUM(`price`) AS `sum` FROM `market_history` WHERE `state` = " << OFFERSTATE_ACCEPTED << " GROUP BY `itemtype`, `sale`";
|
||||
DBResult_ptr result = Database::getInstance().storeQuery(query.str());
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
MarketStatistics* statistics;
|
||||
if (result->getNumber<uint16_t>("sale") == MARKETACTION_BUY) {
|
||||
statistics = &purchaseStatistics[result->getNumber<uint16_t>("itemtype")];
|
||||
} else {
|
||||
statistics = &saleStatistics[result->getNumber<uint16_t>("itemtype")];
|
||||
}
|
||||
|
||||
statistics->numTransactions = result->getNumber<uint32_t>("num");
|
||||
statistics->lowestPrice = result->getNumber<uint32_t>("min");
|
||||
statistics->totalPrice = result->getNumber<uint64_t>("sum");
|
||||
statistics->highestPrice = result->getNumber<uint32_t>("max");
|
||||
} while (result->next());
|
||||
}
|
||||
|
||||
MarketStatistics* IOMarket::getPurchaseStatistics(uint16_t itemId)
|
||||
{
|
||||
auto it = purchaseStatistics.find(itemId);
|
||||
if (it == purchaseStatistics.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
MarketStatistics* IOMarket::getSaleStatistics(uint16_t itemId)
|
||||
{
|
||||
auto it = saleStatistics.find(itemId);
|
||||
if (it == saleStatistics.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return &it->second;
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef FS_IOMARKET_H_B981E52C218C42D3B9EF726EBF0E92C9
|
||||
#define FS_IOMARKET_H_B981E52C218C42D3B9EF726EBF0E92C9
|
||||
|
||||
#include "enums.h"
|
||||
#include "database.h"
|
||||
|
||||
class IOMarket
|
||||
{
|
||||
public:
|
||||
static IOMarket& getInstance() {
|
||||
static IOMarket instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
static MarketOfferList getActiveOffers(MarketAction_t action, uint16_t itemId);
|
||||
static MarketOfferList getOwnOffers(MarketAction_t action, uint32_t playerId);
|
||||
static HistoryMarketOfferList getOwnHistory(MarketAction_t action, uint32_t playerId);
|
||||
|
||||
static void processExpiredOffers(DBResult_ptr result, bool);
|
||||
static void checkExpiredOffers();
|
||||
|
||||
static uint32_t getPlayerOfferCount(uint32_t playerId);
|
||||
static MarketOfferEx getOfferByCounter(uint32_t timestamp, uint16_t counter);
|
||||
|
||||
static void createOffer(uint32_t playerId, MarketAction_t action, uint32_t itemId, uint16_t amount, uint32_t price, bool anonymous);
|
||||
static void acceptOffer(uint32_t offerId, uint16_t amount);
|
||||
static void deleteOffer(uint32_t offerId);
|
||||
|
||||
static void appendHistory(uint32_t playerId, MarketAction_t type, uint16_t itemId, uint16_t amount, uint32_t price, time_t timestamp, MarketOfferState_t state);
|
||||
static bool moveOfferToHistory(uint32_t offerId, MarketOfferState_t state);
|
||||
|
||||
void updateStatistics();
|
||||
|
||||
MarketStatistics* getPurchaseStatistics(uint16_t itemId);
|
||||
MarketStatistics* getSaleStatistics(uint16_t itemId);
|
||||
|
||||
private:
|
||||
IOMarket() = default;
|
||||
|
||||
std::map<uint16_t, MarketStatistics> purchaseStatistics;
|
||||
std::map<uint16_t, MarketStatistics> saleStatistics;
|
||||
};
|
||||
|
||||
#endif
|
953
src/item.cpp
953
src/item.cpp
File diff suppressed because it is too large
Load Diff
527
src/item.h
527
src/item.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -23,12 +23,7 @@
|
||||
#include "cylinder.h"
|
||||
#include "thing.h"
|
||||
#include "items.h"
|
||||
#include "luascript.h"
|
||||
#include "tools.h"
|
||||
#include <typeinfo>
|
||||
|
||||
#include <boost/variant.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <deque>
|
||||
|
||||
class Creature;
|
||||
@ -36,8 +31,8 @@ class Player;
|
||||
class Container;
|
||||
class Depot;
|
||||
class Teleport;
|
||||
class TrashHolder;
|
||||
class Mailbox;
|
||||
class DepotLocker;
|
||||
class Door;
|
||||
class MagicField;
|
||||
class BedItem;
|
||||
@ -55,6 +50,7 @@ enum ITEMPROPERTY {
|
||||
CONST_PROP_IMMOVABLENOFIELDBLOCKPATH,
|
||||
CONST_PROP_NOFIELDBLOCKPATH,
|
||||
CONST_PROP_SUPPORTHANGABLE,
|
||||
CONST_PROP_UNLAY,
|
||||
};
|
||||
|
||||
enum TradeEvents_t {
|
||||
@ -73,7 +69,7 @@ enum AttrTypes_t {
|
||||
//ATTR_EXT_FILE = 2,
|
||||
ATTR_TILE_FLAGS = 3,
|
||||
ATTR_ACTION_ID = 4,
|
||||
ATTR_UNIQUE_ID = 5,
|
||||
ATTR_MOVEMENT_ID = 5,
|
||||
ATTR_TEXT = 6,
|
||||
ATTR_DESC = 7,
|
||||
ATTR_TELE_DEST = 8,
|
||||
@ -91,19 +87,22 @@ enum AttrTypes_t {
|
||||
ATTR_SLEEPERGUID = 20,
|
||||
ATTR_SLEEPSTART = 21,
|
||||
ATTR_CHARGES = 22,
|
||||
ATTR_CONTAINER_ITEMS = 23,
|
||||
ATTR_NAME = 24,
|
||||
ATTR_ARTICLE = 25,
|
||||
ATTR_PLURALNAME = 26,
|
||||
ATTR_WEIGHT = 27,
|
||||
ATTR_ATTACK = 28,
|
||||
ATTR_DEFENSE = 29,
|
||||
ATTR_EXTRADEFENSE = 30,
|
||||
ATTR_ARMOR = 31,
|
||||
ATTR_HITCHANCE = 32,
|
||||
ATTR_SHOOTRANGE = 33,
|
||||
ATTR_CUSTOM_ATTRIBUTES = 34,
|
||||
ATTR_DECAYTO = 35
|
||||
ATTR_KEYNUMBER = 23,
|
||||
ATTR_KEYHOLENUMBER = 24,
|
||||
ATTR_DOORQUESTNUMBER = 25,
|
||||
ATTR_DOORQUESTVALUE = 26,
|
||||
ATTR_DOORLEVEL = 27,
|
||||
ATTR_CHESTQUESTNUMBER = 28,
|
||||
// add non-OTBM attributes after here
|
||||
ATTR_CONTAINER_ITEMS = 29,
|
||||
ATTR_NAME = 30,
|
||||
ATTR_ARTICLE = 31,
|
||||
ATTR_PLURALNAME = 32,
|
||||
ATTR_WEIGHT = 33,
|
||||
ATTR_ATTACK = 34,
|
||||
ATTR_DEFENSE = 35,
|
||||
ATTR_ARMOR = 36,
|
||||
ATTR_SHOOTRANGE = 37,
|
||||
};
|
||||
|
||||
enum Attr_ReadValue {
|
||||
@ -161,11 +160,53 @@ class ItemAttributes
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID));
|
||||
}
|
||||
|
||||
void setUniqueId(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_UNIQUEID, n);
|
||||
void setMovementID(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n);
|
||||
}
|
||||
uint16_t getUniqueId() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
|
||||
uint16_t getMovementId() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID));
|
||||
}
|
||||
|
||||
void setKeyNumber(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_KEYNUMBER, n);
|
||||
}
|
||||
uint16_t getKeyNumber() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_KEYNUMBER));
|
||||
}
|
||||
|
||||
void setKeyHoleNumber(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER, n);
|
||||
}
|
||||
uint16_t getKeyHoleNumber() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_KEYHOLENUMBER));
|
||||
}
|
||||
|
||||
void setDoorQuestNumber(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER, n);
|
||||
}
|
||||
uint16_t getDoorQuestNumber() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTNUMBER));
|
||||
}
|
||||
|
||||
void setDoorQuestValue(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE, n);
|
||||
}
|
||||
uint16_t getDoorQuestValue() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_DOORQUESTVALUE));
|
||||
}
|
||||
|
||||
void setDoorLevel(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_DOORLEVEL, n);
|
||||
}
|
||||
uint16_t getDoorLevel() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_DOORLEVEL));
|
||||
}
|
||||
|
||||
void setChestQuestNumber(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER, n);
|
||||
}
|
||||
uint16_t getChestQuestNumber() const {
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_CHESTQUESTNUMBER));
|
||||
}
|
||||
|
||||
void setCharges(uint16_t n) {
|
||||
@ -189,13 +230,6 @@ class ItemAttributes
|
||||
return getIntAttr(ITEM_ATTRIBUTE_OWNER);
|
||||
}
|
||||
|
||||
void setCorpseOwner(uint32_t corpseOwner) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER, corpseOwner);
|
||||
}
|
||||
uint32_t getCorpseOwner() const {
|
||||
return getIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER);
|
||||
}
|
||||
|
||||
void setDuration(int32_t time) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_DURATION, time);
|
||||
}
|
||||
@ -213,149 +247,19 @@ class ItemAttributes
|
||||
return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE));
|
||||
}
|
||||
|
||||
struct CustomAttribute
|
||||
{
|
||||
typedef boost::variant<boost::blank, std::string, int64_t, double, bool> VariantAttribute;
|
||||
VariantAttribute value;
|
||||
|
||||
CustomAttribute() : value(boost::blank()) {}
|
||||
|
||||
template<typename T>
|
||||
explicit CustomAttribute(const T& v) : value(v) {}
|
||||
|
||||
template<typename T>
|
||||
void set(const T& v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
const T& get();
|
||||
|
||||
struct PushLuaVisitor : public boost::static_visitor<> {
|
||||
lua_State* L;
|
||||
|
||||
explicit PushLuaVisitor(lua_State* L) : boost::static_visitor<>(), L(L) {}
|
||||
|
||||
void operator()(const boost::blank&) const {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
|
||||
void operator()(const std::string& v) const {
|
||||
LuaScriptInterface::pushString(L, v);
|
||||
}
|
||||
|
||||
void operator()(bool v) const {
|
||||
LuaScriptInterface::pushBoolean(L, v);
|
||||
}
|
||||
|
||||
void operator()(const int64_t& v) const {
|
||||
lua_pushnumber(L, v);
|
||||
}
|
||||
|
||||
void operator()(const double& v) const {
|
||||
lua_pushnumber(L, v);
|
||||
}
|
||||
};
|
||||
|
||||
void pushToLua(lua_State* L) const {
|
||||
boost::apply_visitor(PushLuaVisitor(L), value);
|
||||
}
|
||||
|
||||
struct SerializeVisitor : public boost::static_visitor<> {
|
||||
PropWriteStream& propWriteStream;
|
||||
|
||||
explicit SerializeVisitor(PropWriteStream& propWriteStream) : boost::static_visitor<>(), propWriteStream(propWriteStream) {}
|
||||
|
||||
void operator()(const boost::blank&) const {
|
||||
}
|
||||
|
||||
void operator()(const std::string& v) const {
|
||||
propWriteStream.writeString(v);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void operator()(const T& v) const {
|
||||
propWriteStream.write<T>(v);
|
||||
}
|
||||
};
|
||||
|
||||
void serialize(PropWriteStream& propWriteStream) const {
|
||||
propWriteStream.write<uint8_t>(static_cast<uint8_t>(value.which()));
|
||||
boost::apply_visitor(SerializeVisitor(propWriteStream), value);
|
||||
}
|
||||
|
||||
bool unserialize(PropStream& propStream) {
|
||||
// This is hard coded so it's not general, depends on the position of the variants.
|
||||
uint8_t pos;
|
||||
if (!propStream.read<uint8_t>(pos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (pos) {
|
||||
case 1: { // std::string
|
||||
std::string tmp;
|
||||
if (!propStream.readString(tmp)) {
|
||||
return false;
|
||||
}
|
||||
value = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: { // int64_t
|
||||
int64_t tmp;
|
||||
if (!propStream.read<int64_t>(tmp)) {
|
||||
return false;
|
||||
}
|
||||
value = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: { // double
|
||||
double tmp;
|
||||
if (!propStream.read<double>(tmp)) {
|
||||
return false;
|
||||
}
|
||||
value = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
case 4: { // bool
|
||||
bool tmp;
|
||||
if (!propStream.read<bool>(tmp)) {
|
||||
return false;
|
||||
}
|
||||
value = tmp;
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
value = boost::blank();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
bool hasAttribute(itemAttrTypes type) const {
|
||||
protected:
|
||||
inline bool hasAttribute(itemAttrTypes type) const {
|
||||
return (type & attributeBits) != 0;
|
||||
}
|
||||
void removeAttribute(itemAttrTypes type);
|
||||
|
||||
static std::string emptyString;
|
||||
static int64_t emptyInt;
|
||||
static double emptyDouble;
|
||||
static bool emptyBool;
|
||||
|
||||
typedef std::unordered_map<std::string, CustomAttribute> CustomAttributeMap;
|
||||
|
||||
struct Attribute
|
||||
{
|
||||
union {
|
||||
int64_t integer;
|
||||
std::string* string;
|
||||
CustomAttributeMap* custom;
|
||||
} value;
|
||||
itemAttrTypes type;
|
||||
|
||||
@ -368,8 +272,6 @@ class ItemAttributes
|
||||
value.integer = i.value.integer;
|
||||
} else if (ItemAttributes::isStrAttrType(type)) {
|
||||
value.string = new std::string(*i.value.string);
|
||||
} else if (ItemAttributes::isCustomAttrType(type)) {
|
||||
value.custom = new CustomAttributeMap(*i.value.custom);
|
||||
} else {
|
||||
memset(&value, 0, sizeof(value));
|
||||
}
|
||||
@ -381,8 +283,6 @@ class ItemAttributes
|
||||
~Attribute() {
|
||||
if (ItemAttributes::isStrAttrType(type)) {
|
||||
delete value.string;
|
||||
} else if (ItemAttributes::isCustomAttrType(type)) {
|
||||
delete value.custom;
|
||||
}
|
||||
}
|
||||
Attribute& operator=(Attribute other) {
|
||||
@ -393,8 +293,6 @@ class ItemAttributes
|
||||
if (this != &other) {
|
||||
if (ItemAttributes::isStrAttrType(type)) {
|
||||
delete value.string;
|
||||
} else if (ItemAttributes::isCustomAttrType(type)) {
|
||||
delete value.custom;
|
||||
}
|
||||
|
||||
value = other.value;
|
||||
@ -425,94 +323,12 @@ class ItemAttributes
|
||||
const Attribute* getExistingAttr(itemAttrTypes type) const;
|
||||
Attribute& getAttr(itemAttrTypes type);
|
||||
|
||||
CustomAttributeMap* getCustomAttributeMap() {
|
||||
if (!hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom;
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
void setCustomAttribute(int64_t key, R value) {
|
||||
std::string tmp = boost::lexical_cast<std::string>(key);
|
||||
setCustomAttribute(tmp, value);
|
||||
}
|
||||
|
||||
void setCustomAttribute(int64_t key, CustomAttribute& value) {
|
||||
std::string tmp = boost::lexical_cast<std::string>(key);
|
||||
setCustomAttribute(tmp, value);
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
void setCustomAttribute(std::string& key, R value) {
|
||||
toLowerCaseString(key);
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
|
||||
removeCustomAttribute(key);
|
||||
} else {
|
||||
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap();
|
||||
}
|
||||
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom->emplace(key, value);
|
||||
}
|
||||
|
||||
void setCustomAttribute(std::string& key, CustomAttribute& value) {
|
||||
toLowerCaseString(key);
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_CUSTOM)) {
|
||||
removeCustomAttribute(key);
|
||||
} else {
|
||||
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom = new CustomAttributeMap();
|
||||
}
|
||||
getAttr(ITEM_ATTRIBUTE_CUSTOM).value.custom->insert(std::make_pair(std::move(key), std::move(value)));
|
||||
}
|
||||
|
||||
const CustomAttribute* getCustomAttribute(int64_t key) {
|
||||
std::string tmp = boost::lexical_cast<std::string>(key);
|
||||
return getCustomAttribute(tmp);
|
||||
}
|
||||
|
||||
const CustomAttribute* getCustomAttribute(const std::string& key) {
|
||||
if (const CustomAttributeMap* customAttrMap = getCustomAttributeMap()) {
|
||||
auto it = customAttrMap->find(asLowerCaseString(key));
|
||||
if (it != customAttrMap->end()) {
|
||||
return &(it->second);
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool removeCustomAttribute(int64_t key) {
|
||||
std::string tmp = boost::lexical_cast<std::string>(key);
|
||||
return removeCustomAttribute(tmp);
|
||||
}
|
||||
|
||||
bool removeCustomAttribute(const std::string& key) {
|
||||
if (CustomAttributeMap* customAttrMap = getCustomAttributeMap()) {
|
||||
auto it = customAttrMap->find(asLowerCaseString(key));
|
||||
if (it != customAttrMap->end()) {
|
||||
customAttrMap->erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const static uint32_t intAttributeTypes = ITEM_ATTRIBUTE_ACTIONID | ITEM_ATTRIBUTE_UNIQUEID | ITEM_ATTRIBUTE_DATE
|
||||
| ITEM_ATTRIBUTE_WEIGHT | ITEM_ATTRIBUTE_ATTACK | ITEM_ATTRIBUTE_DEFENSE | ITEM_ATTRIBUTE_EXTRADEFENSE
|
||||
| ITEM_ATTRIBUTE_ARMOR | ITEM_ATTRIBUTE_HITCHANCE | ITEM_ATTRIBUTE_SHOOTRANGE | ITEM_ATTRIBUTE_OWNER
|
||||
| ITEM_ATTRIBUTE_DURATION | ITEM_ATTRIBUTE_DECAYSTATE | ITEM_ATTRIBUTE_CORPSEOWNER | ITEM_ATTRIBUTE_CHARGES
|
||||
| ITEM_ATTRIBUTE_FLUIDTYPE | ITEM_ATTRIBUTE_DOORID | ITEM_ATTRIBUTE_DECAYTO;
|
||||
const static uint32_t stringAttributeTypes = ITEM_ATTRIBUTE_DESCRIPTION | ITEM_ATTRIBUTE_TEXT | ITEM_ATTRIBUTE_WRITER
|
||||
| ITEM_ATTRIBUTE_NAME | ITEM_ATTRIBUTE_ARTICLE | ITEM_ATTRIBUTE_PLURALNAME;
|
||||
|
||||
public:
|
||||
static bool isIntAttrType(itemAttrTypes type) {
|
||||
return (type & intAttributeTypes) == type;
|
||||
inline static bool isIntAttrType(itemAttrTypes type) {
|
||||
return (type & 0xFFFFE13) != 0;
|
||||
}
|
||||
static bool isStrAttrType(itemAttrTypes type) {
|
||||
return (type & stringAttributeTypes) == type;
|
||||
}
|
||||
inline static bool isCustomAttrType(itemAttrTypes type) {
|
||||
return (type & ITEM_ATTRIBUTE_CUSTOM) == type;
|
||||
inline static bool isStrAttrType(itemAttrTypes type) {
|
||||
return (type & 0x1EC) != 0;
|
||||
}
|
||||
|
||||
const std::forward_list<Attribute>& getList() const {
|
||||
@ -543,10 +359,10 @@ class Item : virtual public Thing
|
||||
|
||||
bool equals(const Item* otherItem) const;
|
||||
|
||||
Item* getItem() override final {
|
||||
Item* getItem() final {
|
||||
return this;
|
||||
}
|
||||
const Item* getItem() const override final {
|
||||
const Item* getItem() const final {
|
||||
return this;
|
||||
}
|
||||
virtual Teleport* getTeleport() {
|
||||
@ -555,18 +371,18 @@ class Item : virtual public Thing
|
||||
virtual const Teleport* getTeleport() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual TrashHolder* getTrashHolder() {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const TrashHolder* getTrashHolder() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual Mailbox* getMailbox() {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const Mailbox* getMailbox() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual DepotLocker* getDepotLocker() {
|
||||
return nullptr;
|
||||
}
|
||||
virtual const DepotLocker* getDepotLocker() const {
|
||||
return nullptr;
|
||||
}
|
||||
virtual Door* getDoor() {
|
||||
return nullptr;
|
||||
}
|
||||
@ -621,31 +437,6 @@ class Item : virtual public Thing
|
||||
return attributes->hasAttribute(type);
|
||||
}
|
||||
|
||||
template<typename R>
|
||||
void setCustomAttribute(std::string& key, R value) {
|
||||
getAttributes()->setCustomAttribute(key, value);
|
||||
}
|
||||
|
||||
void setCustomAttribute(std::string& key, ItemAttributes::CustomAttribute& value) {
|
||||
getAttributes()->setCustomAttribute(key, value);
|
||||
}
|
||||
|
||||
const ItemAttributes::CustomAttribute* getCustomAttribute(int64_t key) {
|
||||
return getAttributes()->getCustomAttribute(key);
|
||||
}
|
||||
|
||||
const ItemAttributes::CustomAttribute* getCustomAttribute(const std::string& key) {
|
||||
return getAttributes()->getCustomAttribute(key);
|
||||
}
|
||||
|
||||
bool removeCustomAttribute(int64_t key) {
|
||||
return getAttributes()->removeCustomAttribute(key);
|
||||
}
|
||||
|
||||
bool removeCustomAttribute(const std::string& key) {
|
||||
return getAttributes()->removeCustomAttribute(key);
|
||||
}
|
||||
|
||||
void setSpecialDescription(const std::string& desc) {
|
||||
setStrAttr(ITEM_ATTRIBUTE_DESCRIPTION, desc);
|
||||
}
|
||||
@ -684,10 +475,6 @@ class Item : virtual public Thing
|
||||
}
|
||||
|
||||
void setActionId(uint16_t n) {
|
||||
if (n < 100) {
|
||||
n = 100;
|
||||
}
|
||||
|
||||
setIntAttr(ITEM_ATTRIBUTE_ACTIONID, n);
|
||||
}
|
||||
uint16_t getActionId() const {
|
||||
@ -697,11 +484,14 @@ class Item : virtual public Thing
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_ACTIONID));
|
||||
}
|
||||
|
||||
uint16_t getUniqueId() const {
|
||||
void setMovementID(uint16_t n) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_MOVEMENTID, n);
|
||||
}
|
||||
uint16_t getMovementId() const {
|
||||
if (!attributes) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_UNIQUEID));
|
||||
return static_cast<uint16_t>(getIntAttr(ITEM_ATTRIBUTE_MOVEMENTID));
|
||||
}
|
||||
|
||||
void setCharges(uint16_t n) {
|
||||
@ -767,49 +557,42 @@ class Item : virtual public Thing
|
||||
return static_cast<ItemDecayState_t>(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE));
|
||||
}
|
||||
|
||||
void setDecayTo(int32_t decayTo) {
|
||||
setIntAttr(ITEM_ATTRIBUTE_DECAYTO, decayTo);
|
||||
}
|
||||
int32_t getDecayTo() const {
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_DECAYTO)) {
|
||||
return getIntAttr(ITEM_ATTRIBUTE_DECAYTO);
|
||||
}
|
||||
return items[id].decayTo;
|
||||
}
|
||||
|
||||
static std::string getDescription(const ItemType& it, int32_t lookDistance, const Item* item = nullptr, int32_t subType = -1, bool addArticle = true);
|
||||
static std::string getNameDescription(const ItemType& it, const Item* item = nullptr, int32_t subType = -1, bool addArticle = true);
|
||||
static std::string getWeightDescription(const ItemType& it, uint32_t weight, uint32_t count = 1);
|
||||
|
||||
std::string getDescription(int32_t lookDistance) const override final;
|
||||
std::string getDescription(int32_t lookDistance) const final;
|
||||
std::string getNameDescription() const;
|
||||
std::string getWeightDescription() const;
|
||||
|
||||
//serialization
|
||||
virtual Attr_ReadValue readAttr(AttrTypes_t attr, PropStream& propStream);
|
||||
bool unserializeAttr(PropStream& propStream);
|
||||
virtual bool unserializeItemNode(OTB::Loader&, const OTB::Node&, PropStream& propStream);
|
||||
virtual bool unserializeItemNode(FileLoader& f, NODE node, PropStream& propStream);
|
||||
|
||||
virtual void serializeAttr(PropWriteStream& propWriteStream) const;
|
||||
|
||||
bool isPushable() const override final {
|
||||
bool isPushable() const final {
|
||||
return isMoveable();
|
||||
}
|
||||
int32_t getThrowRange() const override final {
|
||||
int32_t getThrowRange() const final {
|
||||
return (isPickupable() ? 15 : 2);
|
||||
}
|
||||
|
||||
uint16_t getID() const {
|
||||
return id;
|
||||
}
|
||||
uint16_t getClientID() const {
|
||||
return items[id].clientId;
|
||||
}
|
||||
void setID(uint16_t newid);
|
||||
|
||||
// Returns the player that is holding this item in his inventory
|
||||
Player* getHoldingPlayer() const;
|
||||
|
||||
CombatType_t getDamageType() const {
|
||||
return items[id].damageType;
|
||||
}
|
||||
CombatType_t getCombatType() const {
|
||||
return items[id].combatType;
|
||||
}
|
||||
WeaponType_t getWeaponType() const {
|
||||
return items[id].weaponType;
|
||||
}
|
||||
@ -822,7 +605,28 @@ class Item : virtual public Thing
|
||||
}
|
||||
return items[id].shootRange;
|
||||
}
|
||||
uint8_t getMissileType() const {
|
||||
return items[id].shootType;
|
||||
}
|
||||
uint8_t getFragility() const {
|
||||
return items[id].fragility;
|
||||
}
|
||||
|
||||
int32_t getAttackStrength() const {
|
||||
return items[id].attackStrength;
|
||||
}
|
||||
int32_t getAttackVariation() const {
|
||||
return items[id].attackVariation;
|
||||
}
|
||||
int32_t getManaConsumption() const {
|
||||
return items[id].manaConsumption;
|
||||
}
|
||||
uint32_t getMinimumLevel() const {
|
||||
return items[id].minReqLevel;
|
||||
}
|
||||
int32_t getWeaponSpecialEffect() const {
|
||||
return items[id].weaponSpecialEffect;
|
||||
}
|
||||
virtual uint32_t getWeight() const;
|
||||
uint32_t getBaseWeight() const {
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_WEIGHT)) {
|
||||
@ -848,24 +652,15 @@ class Item : virtual public Thing
|
||||
}
|
||||
return items[id].defense;
|
||||
}
|
||||
int32_t getExtraDefense() const {
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_EXTRADEFENSE)) {
|
||||
return getIntAttr(ITEM_ATTRIBUTE_EXTRADEFENSE);
|
||||
}
|
||||
return items[id].extraDefense;
|
||||
}
|
||||
int32_t getSlotPosition() const {
|
||||
return items[id].slotPosition;
|
||||
}
|
||||
int8_t getHitChance() const {
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_HITCHANCE)) {
|
||||
return getIntAttr(ITEM_ATTRIBUTE_HITCHANCE);
|
||||
}
|
||||
return items[id].hitChance;
|
||||
uint16_t getDisguiseId() const {
|
||||
return items[id].disguiseId;
|
||||
}
|
||||
|
||||
uint32_t getWorth() const;
|
||||
LightInfo getLightInfo() const;
|
||||
void getLight(LightInfo& lightInfo) const;
|
||||
|
||||
bool hasProperty(ITEMPROPERTY prop) const;
|
||||
bool isBlocking() const {
|
||||
@ -883,15 +678,15 @@ class Item : virtual public Thing
|
||||
bool isMagicField() const {
|
||||
return items[id].isMagicField();
|
||||
}
|
||||
bool isSplash() const {
|
||||
return items[id].isSplash();
|
||||
}
|
||||
bool isMoveable() const {
|
||||
return items[id].moveable;
|
||||
}
|
||||
bool isPickupable() const {
|
||||
return items[id].pickupable;
|
||||
}
|
||||
bool isUseable() const {
|
||||
return items[id].useable;
|
||||
}
|
||||
bool isHangable() const {
|
||||
return items[id].isHangable;
|
||||
}
|
||||
@ -899,10 +694,33 @@ class Item : virtual public Thing
|
||||
const ItemType& it = items[id];
|
||||
return it.rotatable && it.rotateTo;
|
||||
}
|
||||
bool hasWalkStack() const {
|
||||
return items[id].walkStack;
|
||||
bool isDisguised() const {
|
||||
return items[id].disguise;
|
||||
}
|
||||
bool isChangeUse() const {
|
||||
return items[id].changeUse;
|
||||
}
|
||||
bool isChestQuest() const {
|
||||
return items[id].isChest();
|
||||
}
|
||||
bool hasCollisionEvent() const {
|
||||
return items[id].collisionEvent;
|
||||
}
|
||||
bool hasSeparationEvent() const {
|
||||
return items[id].separationEvent;
|
||||
}
|
||||
bool hasUseEvent() const {
|
||||
return items[id].useEvent;
|
||||
}
|
||||
bool hasMultiUseEvent() const {
|
||||
return items[id].multiUseEvent;
|
||||
}
|
||||
bool canDistUse() const {
|
||||
return items[id].distUse;
|
||||
}
|
||||
bool isRune() const {
|
||||
return items[id].isRune();
|
||||
}
|
||||
|
||||
const std::string& getName() const {
|
||||
if (hasAttribute(ITEM_ATTRIBUTE_NAME)) {
|
||||
return getStrAttr(ITEM_ATTRIBUTE_NAME);
|
||||
@ -930,20 +748,12 @@ class Item : virtual public Thing
|
||||
count = n;
|
||||
}
|
||||
|
||||
static uint32_t countByType(const Item* i, int32_t subType) {
|
||||
if (subType == -1 || subType == i->getSubType()) {
|
||||
return i->getItemCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
static uint32_t countByType(const Item* i, int32_t subType);
|
||||
|
||||
void setDefaultSubtype();
|
||||
uint16_t getSubType() const;
|
||||
void setSubType(uint16_t n);
|
||||
|
||||
void setUniqueId(uint16_t n);
|
||||
|
||||
void setDefaultDuration() {
|
||||
uint32_t duration = getDefaultDuration();
|
||||
if (duration != 0) {
|
||||
@ -966,18 +776,13 @@ class Item : virtual public Thing
|
||||
|
||||
virtual void startDecaying();
|
||||
|
||||
bool isLoadedFromMap() const {
|
||||
return loadedFromMap;
|
||||
}
|
||||
void setLoadedFromMap(bool value) {
|
||||
loadedFromMap = value;
|
||||
}
|
||||
bool isCleanable() const {
|
||||
return !loadedFromMap && canRemove() && isPickupable() && !hasAttribute(ITEM_ATTRIBUTE_UNIQUEID) && !hasAttribute(ITEM_ATTRIBUTE_ACTIONID);
|
||||
return !loadedFromMap && canRemove() && isPickupable() && !hasAttribute(ITEM_ATTRIBUTE_ACTIONID);
|
||||
}
|
||||
|
||||
bool hasMarketAttributes() const;
|
||||
|
||||
std::unique_ptr<ItemAttributes>& getAttributes() {
|
||||
if (!attributes) {
|
||||
attributes.reset(new ItemAttributes());
|
||||
@ -994,32 +799,29 @@ class Item : virtual public Thing
|
||||
}
|
||||
}
|
||||
|
||||
Cylinder* getParent() const override {
|
||||
Cylinder* getParent() const {
|
||||
return parent;
|
||||
}
|
||||
void setParent(Cylinder* cylinder) override {
|
||||
void setParent(Cylinder* cylinder) {
|
||||
parent = cylinder;
|
||||
}
|
||||
Cylinder* getTopParent();
|
||||
const Cylinder* getTopParent() const;
|
||||
Tile* getTile() override;
|
||||
const Tile* getTile() const override;
|
||||
bool isRemoved() const override {
|
||||
Tile* getTile();
|
||||
const Tile* getTile() const;
|
||||
bool isRemoved() const {
|
||||
return !parent || parent->isRemoved();
|
||||
}
|
||||
|
||||
protected:
|
||||
Cylinder* parent = nullptr;
|
||||
|
||||
uint16_t id; // the same id as in ItemType
|
||||
|
||||
private:
|
||||
std::string getWeightDescription(uint32_t weight) const;
|
||||
|
||||
Cylinder* parent = nullptr;
|
||||
std::unique_ptr<ItemAttributes> attributes;
|
||||
|
||||
uint32_t referenceCounter = 0;
|
||||
|
||||
uint16_t id; // the same id as in ItemType
|
||||
uint8_t count = 1; // number of stacked items
|
||||
|
||||
bool loadedFromMap = false;
|
||||
@ -1027,7 +829,16 @@ class Item : virtual public Thing
|
||||
//Don't add variables here, use the ItemAttribute class.
|
||||
};
|
||||
|
||||
using ItemList = std::list<Item*>;
|
||||
using ItemDeque = std::deque<Item*>;
|
||||
typedef std::list<Item*> ItemList;
|
||||
typedef std::deque<Item*> ItemDeque;
|
||||
|
||||
inline uint32_t Item::countByType(const Item* i, int32_t subType)
|
||||
{
|
||||
if (subType == -1 || subType == i->getSubType()) {
|
||||
return i->getItemCount();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
198
src/itemloader.h
198
src/itemloader.h
@ -1,198 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef FS_ITEMLOADER_H_107F1D3EECC94CD0A0F528843010D5D4
|
||||
#define FS_ITEMLOADER_H_107F1D3EECC94CD0A0F528843010D5D4
|
||||
|
||||
#include "fileloader.h"
|
||||
|
||||
enum itemgroup_t {
|
||||
ITEM_GROUP_NONE,
|
||||
|
||||
ITEM_GROUP_GROUND,
|
||||
ITEM_GROUP_CONTAINER,
|
||||
ITEM_GROUP_WEAPON, //deprecated
|
||||
ITEM_GROUP_AMMUNITION, //deprecated
|
||||
ITEM_GROUP_ARMOR, //deprecated
|
||||
ITEM_GROUP_CHARGES,
|
||||
ITEM_GROUP_TELEPORT, //deprecated
|
||||
ITEM_GROUP_MAGICFIELD, //deprecated
|
||||
ITEM_GROUP_WRITEABLE, //deprecated
|
||||
ITEM_GROUP_KEY, //deprecated
|
||||
ITEM_GROUP_SPLASH,
|
||||
ITEM_GROUP_FLUID,
|
||||
ITEM_GROUP_DOOR, //deprecated
|
||||
ITEM_GROUP_DEPRECATED,
|
||||
|
||||
ITEM_GROUP_LAST
|
||||
};
|
||||
|
||||
/////////OTB specific//////////////
|
||||
enum clientVersion_t {
|
||||
CLIENT_VERSION_750 = 1,
|
||||
CLIENT_VERSION_755 = 2,
|
||||
CLIENT_VERSION_760 = 3,
|
||||
CLIENT_VERSION_770 = 3,
|
||||
CLIENT_VERSION_780 = 4,
|
||||
CLIENT_VERSION_790 = 5,
|
||||
CLIENT_VERSION_792 = 6,
|
||||
CLIENT_VERSION_800 = 7,
|
||||
CLIENT_VERSION_810 = 8,
|
||||
CLIENT_VERSION_811 = 9,
|
||||
CLIENT_VERSION_820 = 10,
|
||||
CLIENT_VERSION_830 = 11,
|
||||
CLIENT_VERSION_840 = 12,
|
||||
CLIENT_VERSION_841 = 13,
|
||||
CLIENT_VERSION_842 = 14,
|
||||
CLIENT_VERSION_850 = 15,
|
||||
CLIENT_VERSION_854_BAD = 16,
|
||||
CLIENT_VERSION_854 = 17,
|
||||
CLIENT_VERSION_855 = 18,
|
||||
CLIENT_VERSION_860_OLD = 19,
|
||||
CLIENT_VERSION_860 = 20,
|
||||
CLIENT_VERSION_861 = 21,
|
||||
CLIENT_VERSION_862 = 22,
|
||||
CLIENT_VERSION_870 = 23,
|
||||
CLIENT_VERSION_871 = 24,
|
||||
CLIENT_VERSION_872 = 25,
|
||||
CLIENT_VERSION_873 = 26,
|
||||
CLIENT_VERSION_900 = 27,
|
||||
CLIENT_VERSION_910 = 28,
|
||||
CLIENT_VERSION_920 = 29,
|
||||
CLIENT_VERSION_940 = 30,
|
||||
CLIENT_VERSION_944_V1 = 31,
|
||||
CLIENT_VERSION_944_V2 = 32,
|
||||
CLIENT_VERSION_944_V3 = 33,
|
||||
CLIENT_VERSION_944_V4 = 34,
|
||||
CLIENT_VERSION_946 = 35,
|
||||
CLIENT_VERSION_950 = 36,
|
||||
CLIENT_VERSION_952 = 37,
|
||||
CLIENT_VERSION_953 = 38,
|
||||
CLIENT_VERSION_954 = 39,
|
||||
CLIENT_VERSION_960 = 40,
|
||||
CLIENT_VERSION_961 = 41,
|
||||
CLIENT_VERSION_963 = 42,
|
||||
CLIENT_VERSION_970 = 43,
|
||||
CLIENT_VERSION_980 = 44,
|
||||
CLIENT_VERSION_981 = 45,
|
||||
CLIENT_VERSION_982 = 46,
|
||||
CLIENT_VERSION_983 = 47,
|
||||
CLIENT_VERSION_985 = 48,
|
||||
CLIENT_VERSION_986 = 49,
|
||||
CLIENT_VERSION_1010 = 50,
|
||||
CLIENT_VERSION_1020 = 51,
|
||||
CLIENT_VERSION_1021 = 52,
|
||||
CLIENT_VERSION_1030 = 53,
|
||||
CLIENT_VERSION_1031 = 54,
|
||||
CLIENT_VERSION_1035 = 55,
|
||||
CLIENT_VERSION_1076 = 56,
|
||||
CLIENT_VERSION_1098 = 57,
|
||||
};
|
||||
|
||||
enum rootattrib_ {
|
||||
ROOT_ATTR_VERSION = 0x01,
|
||||
};
|
||||
|
||||
enum itemattrib_t {
|
||||
ITEM_ATTR_FIRST = 0x10,
|
||||
ITEM_ATTR_SERVERID = ITEM_ATTR_FIRST,
|
||||
ITEM_ATTR_CLIENTID,
|
||||
ITEM_ATTR_NAME,
|
||||
ITEM_ATTR_DESCR,
|
||||
ITEM_ATTR_SPEED,
|
||||
ITEM_ATTR_SLOT,
|
||||
ITEM_ATTR_MAXITEMS,
|
||||
ITEM_ATTR_WEIGHT,
|
||||
ITEM_ATTR_WEAPON,
|
||||
ITEM_ATTR_AMU,
|
||||
ITEM_ATTR_ARMOR,
|
||||
ITEM_ATTR_MAGLEVEL,
|
||||
ITEM_ATTR_MAGFIELDTYPE,
|
||||
ITEM_ATTR_WRITEABLE,
|
||||
ITEM_ATTR_ROTATETO,
|
||||
ITEM_ATTR_DECAY,
|
||||
ITEM_ATTR_SPRITEHASH,
|
||||
ITEM_ATTR_MINIMAPCOLOR,
|
||||
ITEM_ATTR_07,
|
||||
ITEM_ATTR_08,
|
||||
ITEM_ATTR_LIGHT,
|
||||
|
||||
//1-byte aligned
|
||||
ITEM_ATTR_DECAY2, //deprecated
|
||||
ITEM_ATTR_WEAPON2, //deprecated
|
||||
ITEM_ATTR_AMU2, //deprecated
|
||||
ITEM_ATTR_ARMOR2, //deprecated
|
||||
ITEM_ATTR_WRITEABLE2, //deprecated
|
||||
ITEM_ATTR_LIGHT2,
|
||||
ITEM_ATTR_TOPORDER,
|
||||
ITEM_ATTR_WRITEABLE3, //deprecated
|
||||
|
||||
ITEM_ATTR_WAREID,
|
||||
|
||||
ITEM_ATTR_LAST
|
||||
};
|
||||
|
||||
enum itemflags_t {
|
||||
FLAG_BLOCK_SOLID = 1 << 0,
|
||||
FLAG_BLOCK_PROJECTILE = 1 << 1,
|
||||
FLAG_BLOCK_PATHFIND = 1 << 2,
|
||||
FLAG_HAS_HEIGHT = 1 << 3,
|
||||
FLAG_USEABLE = 1 << 4,
|
||||
FLAG_PICKUPABLE = 1 << 5,
|
||||
FLAG_MOVEABLE = 1 << 6,
|
||||
FLAG_STACKABLE = 1 << 7,
|
||||
FLAG_FLOORCHANGEDOWN = 1 << 8, // unused
|
||||
FLAG_FLOORCHANGENORTH = 1 << 9, // unused
|
||||
FLAG_FLOORCHANGEEAST = 1 << 10, // unused
|
||||
FLAG_FLOORCHANGESOUTH = 1 << 11, // unused
|
||||
FLAG_FLOORCHANGEWEST = 1 << 12, // unused
|
||||
FLAG_ALWAYSONTOP = 1 << 13,
|
||||
FLAG_READABLE = 1 << 14,
|
||||
FLAG_ROTATABLE = 1 << 15,
|
||||
FLAG_HANGABLE = 1 << 16,
|
||||
FLAG_VERTICAL = 1 << 17,
|
||||
FLAG_HORIZONTAL = 1 << 18,
|
||||
FLAG_CANNOTDECAY = 1 << 19, // unused
|
||||
FLAG_ALLOWDISTREAD = 1 << 20,
|
||||
FLAG_UNUSED = 1 << 21, // unused
|
||||
FLAG_CLIENTCHARGES = 1 << 22, /* deprecated */
|
||||
FLAG_LOOKTHROUGH = 1 << 23,
|
||||
FLAG_ANIMATION = 1 << 24,
|
||||
FLAG_FULLTILE = 1 << 25, // unused
|
||||
FLAG_FORCEUSE = 1 << 26,
|
||||
};
|
||||
|
||||
//1-byte aligned structs
|
||||
#pragma pack(1)
|
||||
|
||||
struct VERSIONINFO {
|
||||
uint32_t dwMajorVersion;
|
||||
uint32_t dwMinorVersion;
|
||||
uint32_t dwBuildNumber;
|
||||
uint8_t CSDVersion[128];
|
||||
};
|
||||
|
||||
struct lightBlock2 {
|
||||
uint16_t lightLevel;
|
||||
uint16_t lightColor;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
/////////OTB specific//////////////
|
||||
#endif
|
1742
src/items.cpp
1742
src/items.cpp
File diff suppressed because it is too large
Load Diff
218
src/items.h
218
src/items.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -22,8 +22,8 @@
|
||||
|
||||
#include "const.h"
|
||||
#include "enums.h"
|
||||
#include "itemloader.h"
|
||||
#include "position.h"
|
||||
#include "fileloader.h"
|
||||
|
||||
enum SlotPositionBits : uint32_t {
|
||||
SLOTP_WHEREEVER = 0xFFFFFFFF,
|
||||
@ -46,7 +46,6 @@ enum ItemTypes_t {
|
||||
ITEM_TYPE_NONE,
|
||||
ITEM_TYPE_DEPOT,
|
||||
ITEM_TYPE_MAILBOX,
|
||||
ITEM_TYPE_TRASHHOLDER,
|
||||
ITEM_TYPE_CONTAINER,
|
||||
ITEM_TYPE_DOOR,
|
||||
ITEM_TYPE_MAGICFIELD,
|
||||
@ -54,117 +53,28 @@ enum ItemTypes_t {
|
||||
ITEM_TYPE_BED,
|
||||
ITEM_TYPE_KEY,
|
||||
ITEM_TYPE_RUNE,
|
||||
ITEM_TYPE_CHEST,
|
||||
ITEM_TYPE_LAST
|
||||
};
|
||||
|
||||
enum ItemParseAttributes_t {
|
||||
ITEM_PARSE_TYPE,
|
||||
ITEM_PARSE_DESCRIPTION,
|
||||
ITEM_PARSE_RUNESPELLNAME,
|
||||
ITEM_PARSE_WEIGHT,
|
||||
ITEM_PARSE_SHOWCOUNT,
|
||||
ITEM_PARSE_ARMOR,
|
||||
ITEM_PARSE_DEFENSE,
|
||||
ITEM_PARSE_EXTRADEF,
|
||||
ITEM_PARSE_ATTACK,
|
||||
ITEM_PARSE_ROTATETO,
|
||||
ITEM_PARSE_MOVEABLE,
|
||||
ITEM_PARSE_BLOCKPROJECTILE,
|
||||
ITEM_PARSE_PICKUPABLE,
|
||||
ITEM_PARSE_FORCESERIALIZE,
|
||||
ITEM_PARSE_FLOORCHANGE,
|
||||
ITEM_PARSE_CORPSETYPE,
|
||||
ITEM_PARSE_CONTAINERSIZE,
|
||||
ITEM_PARSE_FLUIDSOURCE,
|
||||
ITEM_PARSE_READABLE,
|
||||
ITEM_PARSE_WRITEABLE,
|
||||
ITEM_PARSE_MAXTEXTLEN,
|
||||
ITEM_PARSE_WRITEONCEITEMID,
|
||||
ITEM_PARSE_WEAPONTYPE,
|
||||
ITEM_PARSE_SLOTTYPE,
|
||||
ITEM_PARSE_AMMOTYPE,
|
||||
ITEM_PARSE_SHOOTTYPE,
|
||||
ITEM_PARSE_EFFECT,
|
||||
ITEM_PARSE_RANGE,
|
||||
ITEM_PARSE_STOPDURATION,
|
||||
ITEM_PARSE_DECAYTO,
|
||||
ITEM_PARSE_TRANSFORMEQUIPTO,
|
||||
ITEM_PARSE_TRANSFORMDEEQUIPTO,
|
||||
ITEM_PARSE_DURATION,
|
||||
ITEM_PARSE_SHOWDURATION,
|
||||
ITEM_PARSE_CHARGES,
|
||||
ITEM_PARSE_SHOWCHARGES,
|
||||
ITEM_PARSE_SHOWATTRIBUTES,
|
||||
ITEM_PARSE_HITCHANCE,
|
||||
ITEM_PARSE_MAXHITCHANCE,
|
||||
ITEM_PARSE_INVISIBLE,
|
||||
ITEM_PARSE_SPEED,
|
||||
ITEM_PARSE_HEALTHGAIN,
|
||||
ITEM_PARSE_HEALTHTICKS,
|
||||
ITEM_PARSE_MANAGAIN,
|
||||
ITEM_PARSE_MANATICKS,
|
||||
ITEM_PARSE_MANASHIELD,
|
||||
ITEM_PARSE_SKILLSWORD,
|
||||
ITEM_PARSE_SKILLAXE,
|
||||
ITEM_PARSE_SKILLCLUB,
|
||||
ITEM_PARSE_SKILLDIST,
|
||||
ITEM_PARSE_SKILLFISH,
|
||||
ITEM_PARSE_SKILLSHIELD,
|
||||
ITEM_PARSE_SKILLFIST,
|
||||
ITEM_PARSE_MAXHITPOINTS,
|
||||
ITEM_PARSE_MAXHITPOINTSPERCENT,
|
||||
ITEM_PARSE_MAXMANAPOINTS,
|
||||
ITEM_PARSE_MAXMANAPOINTSPERCENT,
|
||||
ITEM_PARSE_MAGICPOINTS,
|
||||
ITEM_PARSE_MAGICPOINTSPERCENT,
|
||||
ITEM_PARSE_CRITICALHITCHANCE,
|
||||
ITEM_PARSE_CRITICALHITAMOUNT,
|
||||
ITEM_PARSE_LIFELEECHCHANCE,
|
||||
ITEM_PARSE_LIFELEECHAMOUNT,
|
||||
ITEM_PARSE_MANALEECHCHANCE,
|
||||
ITEM_PARSE_MANALEECHAMOUNT,
|
||||
ITEM_PARSE_FIELDABSORBPERCENTENERGY,
|
||||
ITEM_PARSE_FIELDABSORBPERCENTFIRE,
|
||||
ITEM_PARSE_FIELDABSORBPERCENTPOISON,
|
||||
ITEM_PARSE_ABSORBPERCENTALL,
|
||||
ITEM_PARSE_ABSORBPERCENTELEMENTS,
|
||||
ITEM_PARSE_ABSORBPERCENTMAGIC,
|
||||
ITEM_PARSE_ABSORBPERCENTENERGY,
|
||||
ITEM_PARSE_ABSORBPERCENTFIRE,
|
||||
ITEM_PARSE_ABSORBPERCENTPOISON,
|
||||
ITEM_PARSE_ABSORBPERCENTICE,
|
||||
ITEM_PARSE_ABSORBPERCENTHOLY,
|
||||
ITEM_PARSE_ABSORBPERCENTDEATH,
|
||||
ITEM_PARSE_ABSORBPERCENTLIFEDRAIN,
|
||||
ITEM_PARSE_ABSORBPERCENTMANADRAIN,
|
||||
ITEM_PARSE_ABSORBPERCENTDROWN,
|
||||
ITEM_PARSE_ABSORBPERCENTPHYSICAL,
|
||||
ITEM_PARSE_ABSORBPERCENTHEALING,
|
||||
ITEM_PARSE_ABSORBPERCENTUNDEFINED,
|
||||
ITEM_PARSE_SUPPRESSDRUNK,
|
||||
ITEM_PARSE_SUPPRESSENERGY,
|
||||
ITEM_PARSE_SUPPRESSFIRE,
|
||||
ITEM_PARSE_SUPPRESSPOISON,
|
||||
ITEM_PARSE_SUPPRESSDROWN,
|
||||
ITEM_PARSE_SUPPRESSPHYSICAL,
|
||||
ITEM_PARSE_SUPPRESSFREEZE,
|
||||
ITEM_PARSE_SUPPRESSDAZZLE,
|
||||
ITEM_PARSE_SUPPRESSCURSE,
|
||||
ITEM_PARSE_FIELD,
|
||||
ITEM_PARSE_REPLACEABLE,
|
||||
ITEM_PARSE_PARTNERDIRECTION,
|
||||
ITEM_PARSE_LEVELDOOR,
|
||||
ITEM_PARSE_MALETRANSFORMTO,
|
||||
ITEM_PARSE_FEMALETRANSFORMTO,
|
||||
ITEM_PARSE_TRANSFORMTO,
|
||||
ITEM_PARSE_DESTROYTO,
|
||||
ITEM_PARSE_ELEMENTICE,
|
||||
ITEM_PARSE_ELEMENTEARTH,
|
||||
ITEM_PARSE_ELEMENTFIRE,
|
||||
ITEM_PARSE_ELEMENTENERGY,
|
||||
ITEM_PARSE_WALKSTACK,
|
||||
ITEM_PARSE_BLOCKING,
|
||||
ITEM_PARSE_ALLOWDISTREAD,
|
||||
enum itemgroup_t {
|
||||
ITEM_GROUP_NONE,
|
||||
|
||||
ITEM_GROUP_GROUND,
|
||||
ITEM_GROUP_WEAPON,
|
||||
ITEM_GROUP_AMMUNITION,
|
||||
ITEM_GROUP_ARMOR,
|
||||
ITEM_GROUP_CHARGES,
|
||||
ITEM_GROUP_TELEPORT,
|
||||
ITEM_GROUP_MAGICFIELD,
|
||||
ITEM_GROUP_WRITEABLE,
|
||||
ITEM_GROUP_KEY,
|
||||
ITEM_GROUP_SPLASH,
|
||||
ITEM_GROUP_FLUID,
|
||||
ITEM_GROUP_DOOR,
|
||||
ITEM_GROUP_DEPRECATED,
|
||||
|
||||
ITEM_GROUP_LAST
|
||||
};
|
||||
|
||||
struct Abilities {
|
||||
@ -182,7 +92,6 @@ struct Abilities {
|
||||
|
||||
//extra skill modifiers
|
||||
int32_t skills[SKILL_LAST + 1] = { 0 };
|
||||
int32_t specialSkills[SPECIALSKILL_LAST + 1] = { 0 };
|
||||
|
||||
int32_t speed = 0;
|
||||
|
||||
@ -192,10 +101,6 @@ struct Abilities {
|
||||
//damage abilities modifiers
|
||||
int16_t absorbPercent[COMBAT_COUNT] = { 0 };
|
||||
|
||||
//elemental damage
|
||||
uint16_t elementDamage = 0;
|
||||
CombatType_t elementType = COMBAT_NONE;
|
||||
|
||||
bool manaShield = false;
|
||||
bool invisible = false;
|
||||
bool regeneration = false;
|
||||
@ -219,7 +124,10 @@ class ItemType
|
||||
return group == ITEM_GROUP_GROUND;
|
||||
}
|
||||
bool isContainer() const {
|
||||
return group == ITEM_GROUP_CONTAINER;
|
||||
return type == ITEM_TYPE_CONTAINER;
|
||||
}
|
||||
bool isChest() const {
|
||||
return type == ITEM_TYPE_CHEST;
|
||||
}
|
||||
bool isSplash() const {
|
||||
return group == ITEM_GROUP_SPLASH;
|
||||
@ -246,20 +154,11 @@ class ItemType
|
||||
bool isMailbox() const {
|
||||
return (type == ITEM_TYPE_MAILBOX);
|
||||
}
|
||||
bool isTrashHolder() const {
|
||||
return (type == ITEM_TYPE_TRASHHOLDER);
|
||||
}
|
||||
bool isBed() const {
|
||||
return (type == ITEM_TYPE_BED);
|
||||
}
|
||||
bool isRune() const {
|
||||
return (type == ITEM_TYPE_RUNE);
|
||||
}
|
||||
bool isPickupable() const {
|
||||
return (allowPickupable || pickupable);
|
||||
}
|
||||
bool isUseable() const {
|
||||
return (useable);
|
||||
return type == ITEM_TYPE_RUNE;
|
||||
}
|
||||
bool hasSubType() const {
|
||||
return (isFluidContainer() || isSplash() || stackable || charges != 0);
|
||||
@ -291,9 +190,7 @@ class ItemType
|
||||
itemgroup_t group = ITEM_GROUP_NONE;
|
||||
ItemTypes_t type = ITEM_TYPE_NONE;
|
||||
uint16_t id = 0;
|
||||
uint16_t clientId = 0;
|
||||
bool stackable = false;
|
||||
bool isAnimation = false;
|
||||
|
||||
std::string name;
|
||||
std::string article;
|
||||
@ -306,35 +203,40 @@ class ItemType
|
||||
std::unique_ptr<ConditionDamage> conditionDamage;
|
||||
|
||||
uint32_t weight = 0;
|
||||
uint32_t levelDoor = 0;
|
||||
uint32_t decayTime = 0;
|
||||
uint32_t wieldInfo = 0;
|
||||
uint32_t minReqLevel = 0;
|
||||
uint32_t minReqMagicLevel = 0;
|
||||
uint32_t charges = 0;
|
||||
int32_t maxHitChance = -1;
|
||||
int32_t attackStrength = 0;
|
||||
int32_t attackVariation = 0;
|
||||
int32_t manaConsumption = 0;
|
||||
int32_t vocations = 0;
|
||||
int32_t decayTo = -1;
|
||||
int32_t attack = 0;
|
||||
int32_t defense = 0;
|
||||
int32_t extraDefense = 0;
|
||||
int32_t armor = 0;
|
||||
uint16_t rotateTo = 0;
|
||||
int32_t rotateTo = 0;
|
||||
int32_t runeMagLevel = 0;
|
||||
int32_t runeLevel = 0;
|
||||
int32_t nutrition = 0;
|
||||
int32_t destroyTarget = 0;
|
||||
|
||||
CombatType_t combatType = COMBAT_NONE;
|
||||
CombatType_t damageType = COMBAT_NONE;
|
||||
|
||||
uint16_t transformToOnUse[2] = {0, 0};
|
||||
uint16_t transformToOnUse = 0;
|
||||
uint16_t transformToFree = 0;
|
||||
uint16_t disguiseId = 0;
|
||||
uint16_t destroyTo = 0;
|
||||
uint16_t maxTextLen = 0;
|
||||
uint16_t writeOnceItemId = 0;
|
||||
uint16_t transformEquipTo = 0;
|
||||
uint16_t transformDeEquipTo = 0;
|
||||
uint16_t maxItems = 8;
|
||||
uint16_t slotPosition = SLOTP_HAND;
|
||||
uint16_t slotPosition = SLOTP_RIGHT | SLOTP_LEFT | SLOTP_AMMO;
|
||||
uint16_t speed = 0;
|
||||
uint16_t wareId = 0;
|
||||
|
||||
MagicEffectClasses magicEffect = CONST_ME_NONE;
|
||||
Direction bedPartnerDir = DIRECTION_NONE;
|
||||
@ -344,22 +246,30 @@ class ItemType
|
||||
RaceType_t corpseType = RACE_NONE;
|
||||
FluidTypes_t fluidSource = FLUID_NONE;
|
||||
|
||||
uint8_t floorChange = 0;
|
||||
uint8_t fragility = 0;
|
||||
uint8_t alwaysOnTopOrder = 0;
|
||||
uint8_t lightLevel = 0;
|
||||
uint8_t lightColor = 0;
|
||||
uint8_t shootRange = 1;
|
||||
int8_t hitChance = 0;
|
||||
uint8_t weaponSpecialEffect = 0;
|
||||
|
||||
bool collisionEvent = false;
|
||||
bool separationEvent = false;
|
||||
bool useEvent = false;
|
||||
bool multiUseEvent = false;
|
||||
bool distUse = false;
|
||||
bool disguise = false;
|
||||
bool forceUse = false;
|
||||
bool forceSerialize = false;
|
||||
bool changeUse = false;
|
||||
bool destroy = false;
|
||||
bool corpse = false;
|
||||
bool hasHeight = false;
|
||||
bool walkStack = true;
|
||||
bool blockSolid = false;
|
||||
bool blockPickupable = false;
|
||||
bool blockProjectile = false;
|
||||
bool blockPathFind = false;
|
||||
bool allowPickupable = false;
|
||||
bool allowPickupable = true;
|
||||
bool showDuration = false;
|
||||
bool showCharges = false;
|
||||
bool showAttributes = false;
|
||||
@ -367,7 +277,7 @@ class ItemType
|
||||
bool pickupable = false;
|
||||
bool rotatable = false;
|
||||
bool useable = false;
|
||||
bool moveable = false;
|
||||
bool moveable = true;
|
||||
bool alwaysOnTop = false;
|
||||
bool canReadText = false;
|
||||
bool canWriteText = false;
|
||||
@ -383,8 +293,7 @@ class ItemType
|
||||
class Items
|
||||
{
|
||||
public:
|
||||
using NameMap = std::unordered_multimap<std::string, uint16_t>;
|
||||
using InventoryVector = std::vector<uint16_t>;
|
||||
using nameMap = std::unordered_multimap<std::string, uint16_t>;
|
||||
|
||||
Items();
|
||||
|
||||
@ -395,38 +304,23 @@ class Items
|
||||
bool reload();
|
||||
void clear();
|
||||
|
||||
bool loadFromOtb(const std::string& file);
|
||||
|
||||
const ItemType& operator[](size_t id) const {
|
||||
return getItemType(id);
|
||||
}
|
||||
const ItemType& getItemType(size_t id) const;
|
||||
ItemType& getItemType(size_t id);
|
||||
const ItemType& getItemIdByClientId(uint16_t spriteId) const;
|
||||
|
||||
uint16_t getItemIdByName(const std::string& name);
|
||||
|
||||
uint32_t majorVersion = 0;
|
||||
uint32_t minorVersion = 0;
|
||||
uint32_t buildNumber = 0;
|
||||
bool loadItems();
|
||||
|
||||
bool loadFromXml();
|
||||
void parseItemNode(const pugi::xml_node& itemNode, uint16_t id);
|
||||
|
||||
void buildInventoryList();
|
||||
const InventoryVector& getInventory() const {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
inline size_t size() const {
|
||||
return items.size();
|
||||
}
|
||||
|
||||
NameMap nameToItems;
|
||||
nameMap nameToItems;
|
||||
|
||||
private:
|
||||
std::map<uint16_t, uint16_t> reverseItemMap;
|
||||
protected:
|
||||
std::vector<ItemType> items;
|
||||
InventoryVector inventory;
|
||||
};
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,52 +26,37 @@
|
||||
|
||||
#include <boost/lockfree/stack.hpp>
|
||||
|
||||
/*
|
||||
* we use this to avoid instantiating multiple free lists for objects of the
|
||||
* same size and it can be replaced by a variable template in C++14
|
||||
*
|
||||
* template <std::size_t TSize, size_t CAPACITY>
|
||||
* boost::lockfree::stack<void*, boost::lockfree::capacity<CAPACITY> lockfreeFreeList;
|
||||
*/
|
||||
template <std::size_t TSize, size_t CAPACITY>
|
||||
struct LockfreeFreeList
|
||||
{
|
||||
using FreeList = boost::lockfree::stack<void*, boost::lockfree::capacity<CAPACITY>>;
|
||||
static FreeList& get()
|
||||
{
|
||||
static FreeList freeList;
|
||||
return freeList;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, size_t CAPACITY>
|
||||
class LockfreePoolingAllocator : public std::allocator<T>
|
||||
{
|
||||
public:
|
||||
LockfreePoolingAllocator() = default;
|
||||
|
||||
template <typename U, class = typename std::enable_if<!std::is_same<U, T>::value>::type>
|
||||
template <typename U>
|
||||
explicit constexpr LockfreePoolingAllocator(const U&) {}
|
||||
using value_type = T;
|
||||
typedef T value_type;
|
||||
|
||||
T* allocate(size_t) const {
|
||||
auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::get();
|
||||
void* p; // NOTE: p doesn't have to be initialized
|
||||
if (!inst.pop(p)) {
|
||||
T* p; // NOTE: p doesn't have to be initialized
|
||||
if (!getFreeList().pop(p)) {
|
||||
//Acquire memory without calling the constructor of T
|
||||
p = operator new (sizeof(T));
|
||||
p = static_cast<T*>(operator new (sizeof(T)));
|
||||
}
|
||||
return static_cast<T*>(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
void deallocate(T* p, size_t) const {
|
||||
auto& inst = LockfreeFreeList<sizeof(T), CAPACITY>::get();
|
||||
if (!inst.bounded_push(p)) {
|
||||
if (!getFreeList().bounded_push(p)) {
|
||||
//Release memory without calling the destructor of T
|
||||
//(it has already been called at this point)
|
||||
operator delete(p);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
typedef boost::lockfree::stack<T*, boost::lockfree::capacity<CAPACITY>> FreeList;
|
||||
static FreeList& getFreeList() {
|
||||
static FreeList freeList;
|
||||
return freeList;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
6289
src/luascript.cpp
6289
src/luascript.cpp
File diff suppressed because it is too large
Load Diff
452
src/luascript.h
452
src/luascript.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -20,11 +20,7 @@
|
||||
#ifndef FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8
|
||||
#define FS_LUASCRIPT_H_5344B2BC907E46E3943EA78574A212D8
|
||||
|
||||
#if __has_include("luajit/lua.hpp")
|
||||
#include <luajit/lua.hpp>
|
||||
#else
|
||||
#include <lua.hpp>
|
||||
#endif
|
||||
|
||||
#if LUA_VERSION_NUM >= 502
|
||||
#ifndef LUA_COMPAT_ALL
|
||||
@ -39,7 +35,6 @@
|
||||
#include "database.h"
|
||||
#include "enums.h"
|
||||
#include "position.h"
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
class Thing;
|
||||
class Creature;
|
||||
@ -51,7 +46,6 @@ class Combat;
|
||||
class Condition;
|
||||
class Npc;
|
||||
class Monster;
|
||||
class InstantSpell;
|
||||
|
||||
enum {
|
||||
EVENT_ID_LOADING = 1,
|
||||
@ -99,8 +93,7 @@ struct LuaTimerEventDesc {
|
||||
class LuaScriptInterface;
|
||||
class Cylinder;
|
||||
class Game;
|
||||
|
||||
struct LootBlock;
|
||||
class Npc;
|
||||
|
||||
class ScriptEnvironment
|
||||
{
|
||||
@ -155,9 +148,9 @@ class ScriptEnvironment
|
||||
void removeItemByUID(uint32_t uid);
|
||||
|
||||
private:
|
||||
using VariantVector = std::vector<const LuaVariant*>;
|
||||
using StorageMap = std::map<uint32_t, int32_t>;
|
||||
using DBResultMap = std::map<uint32_t, DBResult_ptr>;
|
||||
typedef std::vector<const LuaVariant*> VariantVector;
|
||||
typedef std::map<uint32_t, int32_t> StorageMap;
|
||||
typedef std::map<uint32_t, DBResult_ptr> DBResultMap;
|
||||
|
||||
LuaScriptInterface* interface;
|
||||
|
||||
@ -216,7 +209,6 @@ class LuaScriptInterface
|
||||
|
||||
const std::string& getFileById(int32_t scriptId);
|
||||
int32_t getEvent(const std::string& eventName);
|
||||
int32_t getEvent();
|
||||
int32_t getMetaEvent(const std::string& globalName, const std::string& eventName);
|
||||
|
||||
static ScriptEnvironment* getScriptEnv() {
|
||||
@ -279,13 +271,13 @@ class LuaScriptInterface
|
||||
|
||||
// Get
|
||||
template<typename T>
|
||||
static typename std::enable_if<std::is_enum<T>::value, T>::type
|
||||
inline static typename std::enable_if<std::is_enum<T>::value, T>::type
|
||||
getNumber(lua_State* L, int32_t arg)
|
||||
{
|
||||
return static_cast<T>(static_cast<int64_t>(lua_tonumber(L, arg)));
|
||||
}
|
||||
template<typename T>
|
||||
static typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, T>::type
|
||||
inline static typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, T>::type
|
||||
getNumber(lua_State* L, int32_t arg)
|
||||
{
|
||||
return static_cast<T>(lua_tonumber(L, arg));
|
||||
@ -309,16 +301,16 @@ class LuaScriptInterface
|
||||
return *userdata;
|
||||
}
|
||||
template<class T>
|
||||
static T** getRawUserdata(lua_State* L, int32_t arg)
|
||||
inline static T** getRawUserdata(lua_State* L, int32_t arg)
|
||||
{
|
||||
return static_cast<T**>(lua_touserdata(L, arg));
|
||||
}
|
||||
|
||||
static bool getBoolean(lua_State* L, int32_t arg)
|
||||
inline static bool getBoolean(lua_State* L, int32_t arg)
|
||||
{
|
||||
return lua_toboolean(L, arg) != 0;
|
||||
}
|
||||
static bool getBoolean(lua_State* L, int32_t arg, bool defaultValue)
|
||||
inline static bool getBoolean(lua_State* L, int32_t arg, bool defaultValue)
|
||||
{
|
||||
const auto parameters = lua_gettop(L);
|
||||
if (parameters == 0 || arg > parameters) {
|
||||
@ -328,11 +320,11 @@ class LuaScriptInterface
|
||||
}
|
||||
|
||||
static std::string getString(lua_State* L, int32_t arg);
|
||||
static CombatDamage getCombatDamage(lua_State* L);
|
||||
static Position getPosition(lua_State* L, int32_t arg, int32_t& stackpos);
|
||||
static Position getPosition(lua_State* L, int32_t arg);
|
||||
static Outfit_t getOutfit(lua_State* L, int32_t arg);
|
||||
static LuaVariant getVariant(lua_State* L, int32_t arg);
|
||||
static InstantSpell* getInstantSpell(lua_State* L, int32_t arg);
|
||||
|
||||
static Thing* getThing(lua_State* L, int32_t arg);
|
||||
static Creature* getCreature(lua_State* L, int32_t arg);
|
||||
@ -350,27 +342,27 @@ class LuaScriptInterface
|
||||
static LuaDataType getUserdataType(lua_State* L, int32_t arg);
|
||||
|
||||
// Is
|
||||
static bool isNumber(lua_State* L, int32_t arg)
|
||||
inline static bool isNumber(lua_State* L, int32_t arg)
|
||||
{
|
||||
return lua_type(L, arg) == LUA_TNUMBER;
|
||||
}
|
||||
static bool isString(lua_State* L, int32_t arg)
|
||||
inline static bool isString(lua_State* L, int32_t arg)
|
||||
{
|
||||
return lua_isstring(L, arg) != 0;
|
||||
}
|
||||
static bool isBoolean(lua_State* L, int32_t arg)
|
||||
inline static bool isBoolean(lua_State* L, int32_t arg)
|
||||
{
|
||||
return lua_isboolean(L, arg);
|
||||
}
|
||||
static bool isTable(lua_State* L, int32_t arg)
|
||||
inline static bool isTable(lua_State* L, int32_t arg)
|
||||
{
|
||||
return lua_istable(L, arg);
|
||||
}
|
||||
static bool isFunction(lua_State* L, int32_t arg)
|
||||
inline static bool isFunction(lua_State* L, int32_t arg)
|
||||
{
|
||||
return lua_isfunction(L, arg);
|
||||
}
|
||||
static bool isUserdata(lua_State* L, int32_t arg)
|
||||
inline static bool isUserdata(lua_State* L, int32_t arg)
|
||||
{
|
||||
return lua_isuserdata(L, arg) != 0;
|
||||
}
|
||||
@ -378,19 +370,17 @@ class LuaScriptInterface
|
||||
// Push
|
||||
static void pushBoolean(lua_State* L, bool value);
|
||||
static void pushCombatDamage(lua_State* L, const CombatDamage& damage);
|
||||
static void pushInstantSpell(lua_State* L, const InstantSpell& spell);
|
||||
static void pushPosition(lua_State* L, const Position& position, int32_t stackpos = 0);
|
||||
static void pushOutfit(lua_State* L, const Outfit_t& outfit);
|
||||
static void pushLoot(lua_State* L, const std::vector<LootBlock>& lootList);
|
||||
|
||||
//
|
||||
static void setField(lua_State* L, const char* index, lua_Number value)
|
||||
inline static void setField(lua_State* L, const char* index, lua_Number value)
|
||||
{
|
||||
lua_pushnumber(L, value);
|
||||
lua_setfield(L, -2, index);
|
||||
}
|
||||
|
||||
static void setField(lua_State* L, const char* index, const std::string& value)
|
||||
inline static void setField(lua_State* L, const char* index, const std::string& value)
|
||||
{
|
||||
pushString(L, value);
|
||||
lua_setfield(L, -2, index);
|
||||
@ -412,21 +402,9 @@ class LuaScriptInterface
|
||||
|
||||
void registerFunctions();
|
||||
|
||||
void registerMethod(const std::string& globalName, const std::string& methodName, lua_CFunction func);
|
||||
|
||||
static std::string getErrorDesc(ErrorCode_t code);
|
||||
|
||||
lua_State* luaState = nullptr;
|
||||
|
||||
int32_t eventTableRef = -1;
|
||||
int32_t runningEventId = EVENT_ID_USER;
|
||||
|
||||
//script file cache
|
||||
std::map<int32_t, std::string> cacheFiles;
|
||||
|
||||
private:
|
||||
void registerClass(const std::string& className, const std::string& baseClass, lua_CFunction newFunction = nullptr);
|
||||
void registerTable(const std::string& tableName);
|
||||
void registerMethod(const std::string& className, const std::string& methodName, lua_CFunction func);
|
||||
void registerMetaMethod(const std::string& className, const std::string& methodName, lua_CFunction func);
|
||||
void registerGlobalMethod(const std::string& functionName, lua_CFunction func);
|
||||
void registerVariable(const std::string& tableName, const std::string& name, lua_Number value);
|
||||
@ -435,16 +413,28 @@ class LuaScriptInterface
|
||||
|
||||
std::string getStackTrace(const std::string& error_desc);
|
||||
|
||||
static std::string getErrorDesc(ErrorCode_t code);
|
||||
static bool getArea(lua_State* L, std::list<uint32_t>& list, uint32_t& rows);
|
||||
|
||||
//lua functions
|
||||
static int luaDoCreateItem(lua_State* L);
|
||||
static int luaDoCreateItemEx(lua_State* L);
|
||||
static int luaDoMoveCreature(lua_State* L);
|
||||
|
||||
static int luaDoPlayerAddItem(lua_State* L);
|
||||
static int luaDoTileAddItemEx(lua_State* L);
|
||||
static int luaDoSetCreatureLight(lua_State* L);
|
||||
|
||||
//get item info
|
||||
static int luaGetDepotId(lua_State* L);
|
||||
|
||||
//get world info
|
||||
//get creature info functions
|
||||
static int luaGetPlayerFlagValue(lua_State* L);
|
||||
static int luaGetCreatureCondition(lua_State* L);
|
||||
|
||||
static int luaGetPlayerInstantSpellInfo(lua_State* L);
|
||||
static int luaGetPlayerInstantSpellCount(lua_State* L);
|
||||
|
||||
static int luaGetWorldTime(lua_State* L);
|
||||
static int luaGetWorldLight(lua_State* L);
|
||||
static int luaGetWorldUpTime(lua_State* L);
|
||||
@ -475,7 +465,12 @@ class LuaScriptInterface
|
||||
|
||||
static int luaDoChallengeCreature(lua_State* L);
|
||||
|
||||
static int luaSetCreatureOutfit(lua_State* L);
|
||||
static int luaSetMonsterOutfit(lua_State* L);
|
||||
static int luaSetItemOutfit(lua_State* L);
|
||||
|
||||
static int luaDebugPrint(lua_State* L);
|
||||
static int luaIsInArray(lua_State* L);
|
||||
static int luaAddEvent(lua_State* L);
|
||||
static int luaStopEvent(lua_State* L);
|
||||
|
||||
@ -486,9 +481,6 @@ class LuaScriptInterface
|
||||
|
||||
static int luaGetWaypointPositionByName(lua_State* L);
|
||||
|
||||
static int luaSendChannelMessage(lua_State* L);
|
||||
static int luaSendGuildChannelMessage(lua_State* L);
|
||||
|
||||
#ifndef LUAJIT_VERSION
|
||||
static int luaBitNot(lua_State* L);
|
||||
static int luaBitAnd(lua_State* L);
|
||||
@ -556,12 +548,9 @@ class LuaScriptInterface
|
||||
static int luaGameCreateMonster(lua_State* L);
|
||||
static int luaGameCreateNpc(lua_State* L);
|
||||
static int luaGameCreateTile(lua_State* L);
|
||||
static int luaGameCreateMonsterType(lua_State* L);
|
||||
|
||||
static int luaGameStartRaid(lua_State* L);
|
||||
|
||||
static int luaGameGetClientVersion(lua_State* L);
|
||||
|
||||
static int luaGameReload(lua_State* L);
|
||||
|
||||
// Variant
|
||||
@ -582,6 +571,7 @@ class LuaScriptInterface
|
||||
|
||||
static int luaPositionSendMagicEffect(lua_State* L);
|
||||
static int luaPositionSendDistanceEffect(lua_State* L);
|
||||
static int luaPositionSendMonsterSay(lua_State* L);
|
||||
|
||||
// Tile
|
||||
static int luaTileCreate(lua_State* L);
|
||||
@ -620,8 +610,6 @@ class LuaScriptInterface
|
||||
static int luaTileGetThingIndex(lua_State* L);
|
||||
|
||||
static int luaTileQueryAdd(lua_State* L);
|
||||
static int luaTileAddItem(lua_State* L);
|
||||
static int luaTileAddItemEx(lua_State* L);
|
||||
|
||||
static int luaTileGetHouse(lua_State* L);
|
||||
|
||||
@ -650,34 +638,6 @@ class LuaScriptInterface
|
||||
static int luaNetworkMessageSkipBytes(lua_State* L);
|
||||
static int luaNetworkMessageSendToPlayer(lua_State* L);
|
||||
|
||||
// ModalWindow
|
||||
static int luaModalWindowCreate(lua_State* L);
|
||||
static int luaModalWindowDelete(lua_State* L);
|
||||
|
||||
static int luaModalWindowGetId(lua_State* L);
|
||||
static int luaModalWindowGetTitle(lua_State* L);
|
||||
static int luaModalWindowGetMessage(lua_State* L);
|
||||
|
||||
static int luaModalWindowSetTitle(lua_State* L);
|
||||
static int luaModalWindowSetMessage(lua_State* L);
|
||||
|
||||
static int luaModalWindowGetButtonCount(lua_State* L);
|
||||
static int luaModalWindowGetChoiceCount(lua_State* L);
|
||||
|
||||
static int luaModalWindowAddButton(lua_State* L);
|
||||
static int luaModalWindowAddChoice(lua_State* L);
|
||||
|
||||
static int luaModalWindowGetDefaultEnterButton(lua_State* L);
|
||||
static int luaModalWindowSetDefaultEnterButton(lua_State* L);
|
||||
|
||||
static int luaModalWindowGetDefaultEscapeButton(lua_State* L);
|
||||
static int luaModalWindowSetDefaultEscapeButton(lua_State* L);
|
||||
|
||||
static int luaModalWindowHasPriority(lua_State* L);
|
||||
static int luaModalWindowSetPriority(lua_State* L);
|
||||
|
||||
static int luaModalWindowSendToPlayer(lua_State* L);
|
||||
|
||||
// Item
|
||||
static int luaItemCreate(lua_State* L);
|
||||
|
||||
@ -692,9 +652,11 @@ class LuaScriptInterface
|
||||
static int luaItemSplit(lua_State* L);
|
||||
static int luaItemRemove(lua_State* L);
|
||||
|
||||
static int luaItemGetUniqueId(lua_State* L);
|
||||
static int luaItemGetMovementId(lua_State* L);
|
||||
static int luaItemSetMovementId(lua_State* L);
|
||||
static int luaItemGetActionId(lua_State* L);
|
||||
static int luaItemSetActionId(lua_State* L);
|
||||
static int luaItemGetUniqueId(lua_State* L);
|
||||
|
||||
static int luaItemGetCount(lua_State* L);
|
||||
static int luaItemGetCharges(lua_State* L);
|
||||
@ -714,9 +676,6 @@ class LuaScriptInterface
|
||||
static int luaItemGetAttribute(lua_State* L);
|
||||
static int luaItemSetAttribute(lua_State* L);
|
||||
static int luaItemRemoveAttribute(lua_State* L);
|
||||
static int luaItemGetCustomAttribute(lua_State* L);
|
||||
static int luaItemSetCustomAttribute(lua_State* L);
|
||||
static int luaItemRemoveCustomAttribute(lua_State* L);
|
||||
|
||||
static int luaItemMoveTo(lua_State* L);
|
||||
static int luaItemTransform(lua_State* L);
|
||||
@ -725,7 +684,6 @@ class LuaScriptInterface
|
||||
static int luaItemGetDescription(lua_State* L);
|
||||
|
||||
static int luaItemHasProperty(lua_State* L);
|
||||
static int luaItemIsLoadedFromMap(lua_State* L);
|
||||
|
||||
// Container
|
||||
static int luaContainerCreate(lua_State* L);
|
||||
@ -733,7 +691,7 @@ class LuaScriptInterface
|
||||
static int luaContainerGetSize(lua_State* L);
|
||||
static int luaContainerGetCapacity(lua_State* L);
|
||||
static int luaContainerGetEmptySlots(lua_State* L);
|
||||
static int luaContainerGetContentDescription(lua_State* L);
|
||||
|
||||
static int luaContainerGetItemHoldingCount(lua_State* L);
|
||||
static int luaContainerGetItemCountById(lua_State* L);
|
||||
|
||||
@ -741,8 +699,7 @@ class LuaScriptInterface
|
||||
static int luaContainerHasItem(lua_State* L);
|
||||
static int luaContainerAddItem(lua_State* L);
|
||||
static int luaContainerAddItemEx(lua_State* L);
|
||||
static int luaContainerGetCorpseOwner(lua_State* L);
|
||||
|
||||
|
||||
// Teleport
|
||||
static int luaTeleportCreate(lua_State* L);
|
||||
|
||||
@ -759,8 +716,6 @@ class LuaScriptInterface
|
||||
static int luaCreatureIsRemoved(lua_State* L);
|
||||
static int luaCreatureIsCreature(lua_State* L);
|
||||
static int luaCreatureIsInGhostMode(lua_State* L);
|
||||
static int luaCreatureIsHealthHidden(lua_State* L);
|
||||
static int luaCreatureIsImmune(lua_State* L);
|
||||
|
||||
static int luaCreatureCanSee(lua_State* L);
|
||||
static int luaCreatureCanSeeCreature(lua_State* L);
|
||||
@ -787,7 +742,6 @@ class LuaScriptInterface
|
||||
static int luaCreatureChangeSpeed(lua_State* L);
|
||||
|
||||
static int luaCreatureSetDropLoot(lua_State* L);
|
||||
static int luaCreatureSetSkillLoss(lua_State* L);
|
||||
|
||||
static int luaCreatureGetPosition(lua_State* L);
|
||||
static int luaCreatureGetTile(lua_State* L);
|
||||
@ -795,7 +749,6 @@ class LuaScriptInterface
|
||||
static int luaCreatureSetDirection(lua_State* L);
|
||||
|
||||
static int luaCreatureGetHealth(lua_State* L);
|
||||
static int luaCreatureSetHealth(lua_State* L);
|
||||
static int luaCreatureAddHealth(lua_State* L);
|
||||
static int luaCreatureGetMaxHealth(lua_State* L);
|
||||
static int luaCreatureSetMaxHealth(lua_State* L);
|
||||
@ -810,7 +763,6 @@ class LuaScriptInterface
|
||||
static int luaCreatureGetCondition(lua_State* L);
|
||||
static int luaCreatureAddCondition(lua_State* L);
|
||||
static int luaCreatureRemoveCondition(lua_State* L);
|
||||
static int luaCreatureHasCondition(lua_State* L);
|
||||
|
||||
static int luaCreatureRemove(lua_State* L);
|
||||
static int luaCreatureTeleportTo(lua_State* L);
|
||||
@ -823,9 +775,6 @@ class LuaScriptInterface
|
||||
static int luaCreatureGetDescription(lua_State* L);
|
||||
|
||||
static int luaCreatureGetPathTo(lua_State* L);
|
||||
static int luaCreatureMove(lua_State* L);
|
||||
|
||||
static int luaCreatureGetZone(lua_State* L);
|
||||
|
||||
// Player
|
||||
static int luaPlayerCreate(lua_State* L);
|
||||
@ -837,6 +786,7 @@ class LuaScriptInterface
|
||||
static int luaPlayerGetAccountId(lua_State* L);
|
||||
static int luaPlayerGetLastLoginSaved(lua_State* L);
|
||||
static int luaPlayerGetLastLogout(lua_State* L);
|
||||
static int luaPlayerHasFlag(lua_State* L);
|
||||
|
||||
static int luaPlayerGetAccountType(lua_State* L);
|
||||
static int luaPlayerSetAccountType(lua_State* L);
|
||||
@ -847,10 +797,10 @@ class LuaScriptInterface
|
||||
static int luaPlayerGetFreeCapacity(lua_State* L);
|
||||
|
||||
static int luaPlayerGetDepotChest(lua_State* L);
|
||||
static int luaPlayerGetInbox(lua_State* L);
|
||||
|
||||
static int luaPlayerGetSkullTime(lua_State* L);
|
||||
static int luaPlayerSetSkullTime(lua_State* L);
|
||||
static int luaPlayerGetMurderTimestamps(lua_State* L);
|
||||
static int luaPlayerGetPlayerKillerEnd(lua_State* L);
|
||||
static int luaPlayerSetPlayerKillerEnd(lua_State* L);
|
||||
static int luaPlayerGetDeathPenalty(lua_State* L);
|
||||
|
||||
static int luaPlayerGetExperience(lua_State* L);
|
||||
@ -875,17 +825,6 @@ class LuaScriptInterface
|
||||
static int luaPlayerGetSkillPercent(lua_State* L);
|
||||
static int luaPlayerGetSkillTries(lua_State* L);
|
||||
static int luaPlayerAddSkillTries(lua_State* L);
|
||||
static int luaPlayerGetSpecialSkill(lua_State* L);
|
||||
static int luaPlayerAddSpecialSkill(lua_State* L);
|
||||
|
||||
static int luaPlayerAddOfflineTrainingTime(lua_State* L);
|
||||
static int luaPlayerGetOfflineTrainingTime(lua_State* L);
|
||||
static int luaPlayerRemoveOfflineTrainingTime(lua_State* L);
|
||||
|
||||
static int luaPlayerAddOfflineTrainingTries(lua_State* L);
|
||||
|
||||
static int luaPlayerGetOfflineTrainingSkill(lua_State* L);
|
||||
static int luaPlayerSetOfflineTrainingSkill(lua_State* L);
|
||||
|
||||
static int luaPlayerGetItemCount(lua_State* L);
|
||||
static int luaPlayerGetItemById(lua_State* L);
|
||||
@ -935,7 +874,6 @@ class LuaScriptInterface
|
||||
static int luaPlayerShowTextDialog(lua_State* L);
|
||||
|
||||
static int luaPlayerSendTextMessage(lua_State* L);
|
||||
static int luaPlayerSendChannelMessage(lua_State* L);
|
||||
static int luaPlayerSendPrivateMessage(lua_State* L);
|
||||
|
||||
static int luaPlayerChannelSay(lua_State* L);
|
||||
@ -952,10 +890,6 @@ class LuaScriptInterface
|
||||
static int luaPlayerHasOutfit(lua_State* L);
|
||||
static int luaPlayerSendOutfitWindow(lua_State* L);
|
||||
|
||||
static int luaPlayerAddMount(lua_State* L);
|
||||
static int luaPlayerRemoveMount(lua_State* L);
|
||||
static int luaPlayerHasMount(lua_State* L);
|
||||
|
||||
static int luaPlayerGetPremiumDays(lua_State* L);
|
||||
static int luaPlayerAddPremiumDays(lua_State* L);
|
||||
static int luaPlayerRemovePremiumDays(lua_State* L);
|
||||
@ -969,19 +903,12 @@ class LuaScriptInterface
|
||||
static int luaPlayerForgetSpell(lua_State* L);
|
||||
static int luaPlayerHasLearnedSpell(lua_State* L);
|
||||
|
||||
static int luaPlayerSendTutorial(lua_State* L);
|
||||
static int luaPlayerAddMapMark(lua_State* L);
|
||||
|
||||
static int luaPlayerSave(lua_State* L);
|
||||
static int luaPlayerPopupFYI(lua_State* L);
|
||||
|
||||
static int luaPlayerIsPzLocked(lua_State* L);
|
||||
|
||||
static int luaPlayerGetClient(lua_State* L);
|
||||
|
||||
static int luaPlayerGetHouse(lua_State* L);
|
||||
static int luaPlayerSendHouseWindow(lua_State* L);
|
||||
static int luaPlayerSetEditHouse(lua_State* L);
|
||||
|
||||
static int luaPlayerSetGhostMode(lua_State* L);
|
||||
|
||||
@ -989,12 +916,7 @@ class LuaScriptInterface
|
||||
static int luaPlayerGetContainerById(lua_State* L);
|
||||
static int luaPlayerGetContainerIndex(lua_State* L);
|
||||
|
||||
static int luaPlayerGetInstantSpells(lua_State* L);
|
||||
static int luaPlayerCanCast(lua_State* L);
|
||||
|
||||
static int luaPlayerHasChaseMode(lua_State* L);
|
||||
static int luaPlayerHasSecureMode(lua_State* L);
|
||||
static int luaPlayerGetFightMode(lua_State* L);
|
||||
static int luaPlayerGetTotalDamage(lua_State* L);
|
||||
|
||||
// Monster
|
||||
static int luaMonsterCreate(lua_State* L);
|
||||
@ -1033,9 +955,6 @@ class LuaScriptInterface
|
||||
|
||||
static int luaNpcSetMasterPos(lua_State* L);
|
||||
|
||||
static int luaNpcGetSpeechBubble(lua_State* L);
|
||||
static int luaNpcSetSpeechBubble(lua_State* L);
|
||||
|
||||
// Guild
|
||||
static int luaGuildCreate(lua_State* L);
|
||||
|
||||
@ -1047,9 +966,6 @@ class LuaScriptInterface
|
||||
static int luaGuildGetRankById(lua_State* L);
|
||||
static int luaGuildGetRankByLevel(lua_State* L);
|
||||
|
||||
static int luaGuildGetMotd(lua_State* L);
|
||||
static int luaGuildSetMotd(lua_State* L);
|
||||
|
||||
// Group
|
||||
static int luaGroupCreate(lua_State* L);
|
||||
|
||||
@ -1059,13 +975,11 @@ class LuaScriptInterface
|
||||
static int luaGroupGetAccess(lua_State* L);
|
||||
static int luaGroupGetMaxDepotItems(lua_State* L);
|
||||
static int luaGroupGetMaxVipEntries(lua_State* L);
|
||||
static int luaGroupHasFlag(lua_State* L);
|
||||
|
||||
// Vocation
|
||||
static int luaVocationCreate(lua_State* L);
|
||||
|
||||
static int luaVocationGetId(lua_State* L);
|
||||
static int luaVocationGetClientId(lua_State* L);
|
||||
static int luaVocationGetName(lua_State* L);
|
||||
static int luaVocationGetDescription(lua_State* L);
|
||||
|
||||
@ -1116,68 +1030,59 @@ class LuaScriptInterface
|
||||
|
||||
static int luaHouseGetDoors(lua_State* L);
|
||||
static int luaHouseGetDoorCount(lua_State* L);
|
||||
static int luaHouseGetDoorIdByPosition(lua_State* L);
|
||||
|
||||
static int luaHouseGetTiles(lua_State* L);
|
||||
static int luaHouseGetItems(lua_State* L);
|
||||
static int luaHouseGetTileCount(lua_State* L);
|
||||
|
||||
static int luaHouseCanEditAccessList(lua_State* L);
|
||||
static int luaHouseGetAccessList(lua_State* L);
|
||||
static int luaHouseSetAccessList(lua_State* L);
|
||||
|
||||
static int luaHouseKickPlayer(lua_State* L);
|
||||
|
||||
// ItemType
|
||||
static int luaItemTypeCreate(lua_State* L);
|
||||
|
||||
static int luaItemTypeIsCorpse(lua_State* L);
|
||||
static int luaItemTypeIsDoor(lua_State* L);
|
||||
static int luaItemTypeIsContainer(lua_State* L);
|
||||
static int luaItemTypeIsChest(lua_State* L);
|
||||
static int luaItemTypeIsFluidContainer(lua_State* L);
|
||||
static int luaItemTypeIsMovable(lua_State* L);
|
||||
static int luaItemTypeIsRune(lua_State* L);
|
||||
static int luaItemTypeIsStackable(lua_State* L);
|
||||
static int luaItemTypeIsReadable(lua_State* L);
|
||||
static int luaItemTypeIsWritable(lua_State* L);
|
||||
static int luaItemTypeIsBlocking(lua_State* L);
|
||||
static int luaItemTypeIsGroundTile(lua_State* L);
|
||||
static int luaItemTypeIsMagicField(lua_State* L);
|
||||
static int luaItemTypeIsUseable(lua_State* L);
|
||||
static int luaItemTypeIsPickupable(lua_State* L);
|
||||
static int luaItemTypeIsSplash(lua_State* L);
|
||||
static int luaItemTypeIsKey(lua_State* L);
|
||||
static int luaItemTypeIsDisguised(lua_State* L);
|
||||
static int luaItemTypeIsDestroyable(lua_State* L);
|
||||
static int luaItemTypeIsGroundTile(lua_State* L);
|
||||
|
||||
static int luaItemTypeGetType(lua_State* L);
|
||||
static int luaItemTypeGetId(lua_State* L);
|
||||
static int luaItemTypeGetClientId(lua_State* L);
|
||||
static int luaItemTypeGetDisguiseId(lua_State* L);
|
||||
static int luaItemTypeGetName(lua_State* L);
|
||||
static int luaItemTypeGetPluralName(lua_State* L);
|
||||
static int luaItemTypeGetArticle(lua_State* L);
|
||||
static int luaItemTypeGetDescription(lua_State* L);
|
||||
static int luaItemTypeGetSlotPosition(lua_State *L);
|
||||
static int luaItemTypeGetDestroyTarget(lua_State* L);
|
||||
|
||||
static int luaItemTypeGetCharges(lua_State* L);
|
||||
static int luaItemTypeGetFluidSource(lua_State* L);
|
||||
static int luaItemTypeGetCapacity(lua_State* L);
|
||||
static int luaItemTypeGetWeight(lua_State* L);
|
||||
|
||||
static int luaItemTypeGetHitChance(lua_State* L);
|
||||
static int luaItemTypeGetShootRange(lua_State* L);
|
||||
static int luaItemTypeGetAttack(lua_State* L);
|
||||
static int luaItemTypeGetDefense(lua_State* L);
|
||||
static int luaItemTypeGetExtraDefense(lua_State* L);
|
||||
static int luaItemTypeGetArmor(lua_State* L);
|
||||
static int luaItemTypeGetWeaponType(lua_State* L);
|
||||
|
||||
static int luaItemTypeGetElementType(lua_State* L);
|
||||
static int luaItemTypeGetElementDamage(lua_State* L);
|
||||
|
||||
static int luaItemTypeGetTransformEquipId(lua_State* L);
|
||||
static int luaItemTypeGetTransformDeEquipId(lua_State* L);
|
||||
static int luaItemTypeGetDestroyId(lua_State* L);
|
||||
static int luaItemTypeGetDecayId(lua_State* L);
|
||||
static int luaItemTypeGetNutrition(lua_State* L);
|
||||
static int luaItemTypeGetRequiredLevel(lua_State* L);
|
||||
static int luaItemTypeGetAmmoType(lua_State* L);
|
||||
static int luaItemTypeGetCorpseType(lua_State* L);
|
||||
|
||||
static int luaItemTypeHasSubType(lua_State* L);
|
||||
|
||||
@ -1188,8 +1093,7 @@ class LuaScriptInterface
|
||||
static int luaCombatSetFormula(lua_State* L);
|
||||
|
||||
static int luaCombatSetArea(lua_State* L);
|
||||
static int luaCombatAddCondition(lua_State* L);
|
||||
static int luaCombatClearConditions(lua_State* L);
|
||||
static int luaCombatSetCondition(lua_State* L);
|
||||
static int luaCombatSetCallback(lua_State* L);
|
||||
static int luaCombatSetOrigin(lua_State* L);
|
||||
|
||||
@ -1211,10 +1115,10 @@ class LuaScriptInterface
|
||||
static int luaConditionSetTicks(lua_State* L);
|
||||
|
||||
static int luaConditionSetParameter(lua_State* L);
|
||||
static int luaConditionSetFormula(lua_State* L);
|
||||
static int luaConditionSetSpeedDelta(lua_State* L);
|
||||
static int luaConditionSetOutfit(lua_State* L);
|
||||
|
||||
static int luaConditionAddDamage(lua_State* L);
|
||||
static int luaConditionSetTiming(lua_State* L);
|
||||
|
||||
// MonsterType
|
||||
static int luaMonsterTypeCreate(lua_State* L);
|
||||
@ -1225,100 +1129,47 @@ class LuaScriptInterface
|
||||
static int luaMonsterTypeIsIllusionable(lua_State* L);
|
||||
static int luaMonsterTypeIsHostile(lua_State* L);
|
||||
static int luaMonsterTypeIsPushable(lua_State* L);
|
||||
static int luaMonsterTypeIsHealthHidden(lua_State* L);
|
||||
static int luaMonsterTypeIsHealthShown(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeCanPushItems(lua_State* L);
|
||||
static int luaMonsterTypeCanPushCreatures(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeName(lua_State* L);
|
||||
static int luaMonsterTypeNameDescription(lua_State* L);
|
||||
static int luaMonsterTypeGetName(lua_State* L);
|
||||
static int luaMonsterTypeGetNameDescription(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeHealth(lua_State* L);
|
||||
static int luaMonsterTypeMaxHealth(lua_State* L);
|
||||
static int luaMonsterTypeRunHealth(lua_State* L);
|
||||
static int luaMonsterTypeExperience(lua_State* L);
|
||||
static int luaMonsterTypeGetHealth(lua_State* L);
|
||||
static int luaMonsterTypeGetMaxHealth(lua_State* L);
|
||||
static int luaMonsterTypeGetRunHealth(lua_State* L);
|
||||
static int luaMonsterTypeGetExperience(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeCombatImmunities(lua_State* L);
|
||||
static int luaMonsterTypeConditionImmunities(lua_State* L);
|
||||
static int luaMonsterTypeGetCombatImmunities(lua_State* L);
|
||||
static int luaMonsterTypeGetConditionImmunities(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeGetAttackList(lua_State* L);
|
||||
static int luaMonsterTypeAddAttack(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeGetDefenseList(lua_State* L);
|
||||
static int luaMonsterTypeAddDefense(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeGetElementList(lua_State* L);
|
||||
static int luaMonsterTypeAddElement(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeGetVoices(lua_State* L);
|
||||
static int luaMonsterTypeAddVoice(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeGetLoot(lua_State* L);
|
||||
static int luaMonsterTypeAddLoot(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeGetCreatureEvents(lua_State* L);
|
||||
static int luaMonsterTypeRegisterEvent(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeEventOnCallback(lua_State* L);
|
||||
static int luaMonsterTypeEventType(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeGetSummonList(lua_State* L);
|
||||
static int luaMonsterTypeAddSummon(lua_State* L);
|
||||
static int luaMonsterTypeGetMaxSummons(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeMaxSummons(lua_State* L);
|
||||
static int luaMonsterTypeGetArmor(lua_State* L);
|
||||
static int luaMonsterTypeGetDefense(lua_State* L);
|
||||
static int luaMonsterTypeGetOutfit(lua_State* L);
|
||||
static int luaMonsterTypeGetRace(lua_State* L);
|
||||
static int luaMonsterTypeGetCorpseId(lua_State* L);
|
||||
static int luaMonsterTypeGetManaCost(lua_State* L);
|
||||
static int luaMonsterTypeGetBaseSpeed(lua_State* L);
|
||||
static int luaMonsterTypeGetLight(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeArmor(lua_State* L);
|
||||
static int luaMonsterTypeDefense(lua_State* L);
|
||||
static int luaMonsterTypeOutfit(lua_State* L);
|
||||
static int luaMonsterTypeRace(lua_State* L);
|
||||
static int luaMonsterTypeCorpseId(lua_State* L);
|
||||
static int luaMonsterTypeManaCost(lua_State* L);
|
||||
static int luaMonsterTypeBaseSpeed(lua_State* L);
|
||||
static int luaMonsterTypeLight(lua_State* L);
|
||||
|
||||
static int luaMonsterTypeStaticAttackChance(lua_State* L);
|
||||
static int luaMonsterTypeTargetDistance(lua_State* L);
|
||||
static int luaMonsterTypeYellChance(lua_State* L);
|
||||
static int luaMonsterTypeYellSpeedTicks(lua_State* L);
|
||||
static int luaMonsterTypeChangeTargetChance(lua_State* L);
|
||||
static int luaMonsterTypeChangeTargetSpeed(lua_State* L);
|
||||
|
||||
// Loot
|
||||
static int luaCreateLoot(lua_State* L);
|
||||
static int luaDeleteLoot(lua_State* L);
|
||||
static int luaLootSetId(lua_State* L);
|
||||
static int luaLootSetMaxCount(lua_State* L);
|
||||
static int luaLootSetSubType(lua_State* L);
|
||||
static int luaLootSetChance(lua_State* L);
|
||||
static int luaLootSetActionId(lua_State* L);
|
||||
static int luaLootSetDescription(lua_State* L);
|
||||
static int luaLootAddChildLoot(lua_State* L);
|
||||
|
||||
// MonsterSpell
|
||||
static int luaCreateMonsterSpell(lua_State* L);
|
||||
static int luaDeleteMonsterSpell(lua_State* L);
|
||||
static int luaMonsterSpellSetType(lua_State* L);
|
||||
static int luaMonsterSpellSetScriptName(lua_State* L);
|
||||
static int luaMonsterSpellSetChance(lua_State* L);
|
||||
static int luaMonsterSpellSetInterval(lua_State* L);
|
||||
static int luaMonsterSpellSetRange(lua_State* L);
|
||||
static int luaMonsterSpellSetCombatValue(lua_State* L);
|
||||
static int luaMonsterSpellSetCombatType(lua_State* L);
|
||||
static int luaMonsterSpellSetAttackValue(lua_State* L);
|
||||
static int luaMonsterSpellSetNeedTarget(lua_State* L);
|
||||
static int luaMonsterSpellSetCombatLength(lua_State* L);
|
||||
static int luaMonsterSpellSetCombatSpread(lua_State* L);
|
||||
static int luaMonsterSpellSetCombatRadius(lua_State* L);
|
||||
static int luaMonsterSpellSetConditionType(lua_State* L);
|
||||
static int luaMonsterSpellSetConditionDamage(lua_State* L);
|
||||
static int luaMonsterSpellSetConditionSpeedChange(lua_State* L);
|
||||
static int luaMonsterSpellSetConditionDuration(lua_State* L);
|
||||
static int luaMonsterSpellSetConditionTickInterval(lua_State* L);
|
||||
static int luaMonsterSpellSetCombatShootEffect(lua_State* L);
|
||||
static int luaMonsterSpellSetCombatEffect(lua_State* L);
|
||||
static int luaMonsterTypeGetTargetDistance(lua_State* L);
|
||||
static int luaMonsterTypeGetChangeTargetChance(lua_State* L);
|
||||
static int luaMonsterTypeGetChangeTargetSpeed(lua_State* L);
|
||||
|
||||
// Party
|
||||
static int luaPartyCreate(lua_State* L);
|
||||
static int luaPartyDisband(lua_State* L);
|
||||
|
||||
static int luaPartyGetLeader(lua_State* L);
|
||||
@ -1341,142 +1192,21 @@ class LuaScriptInterface
|
||||
static int luaPartyShareExperience(lua_State* L);
|
||||
static int luaPartySetSharedExperience(lua_State* L);
|
||||
|
||||
// Spells
|
||||
static int luaSpellCreate(lua_State* L);
|
||||
|
||||
static int luaSpellOnCastSpell(lua_State* L);
|
||||
static int luaSpellRegister(lua_State* L);
|
||||
static int luaSpellName(lua_State* L);
|
||||
static int luaSpellId(lua_State* L);
|
||||
static int luaSpellGroup(lua_State* L);
|
||||
static int luaSpellCooldown(lua_State* L);
|
||||
static int luaSpellGroupCooldown(lua_State* L);
|
||||
static int luaSpellLevel(lua_State* L);
|
||||
static int luaSpellMagicLevel(lua_State* L);
|
||||
static int luaSpellMana(lua_State* L);
|
||||
static int luaSpellManaPercent(lua_State* L);
|
||||
static int luaSpellSoul(lua_State* L);
|
||||
static int luaSpellRange(lua_State* L);
|
||||
static int luaSpellPremium(lua_State* L);
|
||||
static int luaSpellEnabled(lua_State* L);
|
||||
static int luaSpellNeedTarget(lua_State* L);
|
||||
static int luaSpellNeedWeapon(lua_State* L);
|
||||
static int luaSpellNeedLearn(lua_State* L);
|
||||
static int luaSpellSelfTarget(lua_State* L);
|
||||
static int luaSpellBlocking(lua_State* L);
|
||||
static int luaSpellAggressive(lua_State* L);
|
||||
static int luaSpellVocation(lua_State* L);
|
||||
|
||||
// only for InstantSpells
|
||||
static int luaSpellWords(lua_State* L);
|
||||
static int luaSpellNeedDirection(lua_State* L);
|
||||
static int luaSpellHasParams(lua_State* L);
|
||||
static int luaSpellHasPlayerNameParam(lua_State* L);
|
||||
static int luaSpellNeedCasterTargetOrDirection(lua_State* L);
|
||||
static int luaSpellIsBlockingWalls(lua_State* L);
|
||||
|
||||
// only for RuneSpells
|
||||
static int luaSpellRuneId(lua_State* L);
|
||||
static int luaSpellCharges(lua_State* L);
|
||||
static int luaSpellAllowFarUse(lua_State* L);
|
||||
static int luaSpellBlockWalls(lua_State* L);
|
||||
static int luaSpellCheckFloor(lua_State* L);
|
||||
|
||||
// Actions
|
||||
static int luaCreateAction(lua_State* L);
|
||||
static int luaActionOnUse(lua_State* L);
|
||||
static int luaActionRegister(lua_State* L);
|
||||
static int luaActionItemId(lua_State* L);
|
||||
static int luaActionActionId(lua_State* L);
|
||||
static int luaActionUniqueId(lua_State* L);
|
||||
static int luaActionAllowFarUse(lua_State* L);
|
||||
static int luaActionBlockWalls(lua_State* L);
|
||||
static int luaActionCheckFloor(lua_State* L);
|
||||
|
||||
// Talkactions
|
||||
static int luaCreateTalkaction(lua_State* L);
|
||||
static int luaTalkactionOnSay(lua_State* L);
|
||||
static int luaTalkactionRegister(lua_State* L);
|
||||
static int luaTalkactionSeparator(lua_State* L);
|
||||
|
||||
// CreatureEvents
|
||||
static int luaCreateCreatureEvent(lua_State* L);
|
||||
static int luaCreatureEventType(lua_State* L);
|
||||
static int luaCreatureEventRegister(lua_State* L);
|
||||
static int luaCreatureEventOnCallback(lua_State* L);
|
||||
|
||||
// MoveEvents
|
||||
static int luaCreateMoveEvent(lua_State* L);
|
||||
static int luaMoveEventType(lua_State* L);
|
||||
static int luaMoveEventRegister(lua_State* L);
|
||||
static int luaMoveEventOnCallback(lua_State* L);
|
||||
static int luaMoveEventLevel(lua_State* L);
|
||||
static int luaMoveEventSlot(lua_State* L);
|
||||
static int luaMoveEventMagLevel(lua_State* L);
|
||||
static int luaMoveEventPremium(lua_State* L);
|
||||
static int luaMoveEventVocation(lua_State* L);
|
||||
static int luaMoveEventItemId(lua_State* L);
|
||||
static int luaMoveEventActionId(lua_State* L);
|
||||
static int luaMoveEventUniqueId(lua_State* L);
|
||||
static int luaMoveEventPosition(lua_State* L);
|
||||
|
||||
// GlobalEvents
|
||||
static int luaCreateGlobalEvent(lua_State* L);
|
||||
static int luaGlobalEventType(lua_State* L);
|
||||
static int luaGlobalEventRegister(lua_State* L);
|
||||
static int luaGlobalEventOnCallback(lua_State* L);
|
||||
static int luaGlobalEventTime(lua_State* L);
|
||||
static int luaGlobalEventInterval(lua_State* L);
|
||||
|
||||
// Weapon
|
||||
static int luaCreateWeapon(lua_State* L);
|
||||
static int luaWeaponId(lua_State* L);
|
||||
static int luaWeaponLevel(lua_State* L);
|
||||
static int luaWeaponMagicLevel(lua_State* L);
|
||||
static int luaWeaponMana(lua_State* L);
|
||||
static int luaWeaponManaPercent(lua_State* L);
|
||||
static int luaWeaponHealth(lua_State* L);
|
||||
static int luaWeaponHealthPercent(lua_State* L);
|
||||
static int luaWeaponSoul(lua_State* L);
|
||||
static int luaWeaponPremium(lua_State* L);
|
||||
static int luaWeaponBreakChance(lua_State* L);
|
||||
static int luaWeaponAction(lua_State* L);
|
||||
static int luaWeaponUnproperly(lua_State* L);
|
||||
static int luaWeaponVocation(lua_State* L);
|
||||
static int luaWeaponOnUseWeapon(lua_State* L);
|
||||
static int luaWeaponRegister(lua_State* L);
|
||||
static int luaWeaponElement(lua_State* L);
|
||||
static int luaWeaponAttack(lua_State* L);
|
||||
static int luaWeaponDefense(lua_State* L);
|
||||
static int luaWeaponRange(lua_State* L);
|
||||
static int luaWeaponCharges(lua_State* L);
|
||||
static int luaWeaponDuration(lua_State* L);
|
||||
static int luaWeaponDecayTo(lua_State* L);
|
||||
static int luaWeaponTransformEquipTo(lua_State* L);
|
||||
static int luaWeaponTransformDeEquipTo(lua_State* L);
|
||||
static int luaWeaponSlotType(lua_State* L);
|
||||
static int luaWeaponHitChance(lua_State* L);
|
||||
static int luaWeaponExtraElement(lua_State* L);
|
||||
|
||||
// exclusively for distance weapons
|
||||
static int luaWeaponMaxHitChance(lua_State* L);
|
||||
static int luaWeaponAmmoType(lua_State* L);
|
||||
|
||||
// exclusively for wands
|
||||
static int luaWeaponWandDamage(lua_State* L);
|
||||
|
||||
// exclusively for wands & distance weapons
|
||||
static int luaWeaponShootType(lua_State* L);
|
||||
|
||||
//
|
||||
lua_State* luaState = nullptr;
|
||||
std::string lastLuaError;
|
||||
|
||||
std::string interfaceName;
|
||||
int32_t eventTableRef = -1;
|
||||
|
||||
static ScriptEnvironment scriptEnv[16];
|
||||
static int32_t scriptEnvIndex;
|
||||
|
||||
int32_t runningEventId = EVENT_ID_USER;
|
||||
std::string loadingFile;
|
||||
|
||||
//script file cache
|
||||
std::map<int32_t, std::string> cacheFiles;
|
||||
};
|
||||
|
||||
class LuaEnvironment : public LuaScriptInterface
|
||||
@ -1489,9 +1219,9 @@ class LuaEnvironment : public LuaScriptInterface
|
||||
LuaEnvironment(const LuaEnvironment&) = delete;
|
||||
LuaEnvironment& operator=(const LuaEnvironment&) = delete;
|
||||
|
||||
bool initState() override;
|
||||
bool initState();
|
||||
bool reInitState();
|
||||
bool closeState() override;
|
||||
bool closeState();
|
||||
|
||||
LuaScriptInterface* getTestInterface();
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -21,7 +21,9 @@
|
||||
|
||||
#include "mailbox.h"
|
||||
#include "game.h"
|
||||
#include "player.h"
|
||||
#include "iologindata.h"
|
||||
#include "town.h"
|
||||
|
||||
extern Game g_game;
|
||||
|
||||
@ -91,22 +93,30 @@ void Mailbox::postRemoveNotification(Thing* thing, const Cylinder* newParent, in
|
||||
bool Mailbox::sendItem(Item* item) const
|
||||
{
|
||||
std::string receiver;
|
||||
if (!getReceiver(item, receiver)) {
|
||||
std::string townName;
|
||||
if (!getDestination(item, receiver, townName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**No need to continue if its still empty**/
|
||||
if (receiver.empty()) {
|
||||
if (receiver.empty() || townName.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Town* town = g_game.map.towns.getTown(townName);
|
||||
if (!town) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Player* player = g_game.getPlayerByName(receiver);
|
||||
if (player) {
|
||||
if (g_game.internalMoveItem(item->getParent(), player->getInbox(), INDEX_WHEREEVER,
|
||||
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
|
||||
g_game.transformItem(item, item->getID() + 1);
|
||||
player->onReceiveMail();
|
||||
return true;
|
||||
DepotLocker* depotLocker = player->getDepotLocker(town->getID(), true);
|
||||
if (depotLocker) {
|
||||
if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER,
|
||||
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
|
||||
g_game.transformItem(item, item->getID() + 1);
|
||||
player->onReceiveMail(town->getID());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Player tmpPlayer(nullptr);
|
||||
@ -114,25 +124,30 @@ bool Mailbox::sendItem(Item* item) const
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_game.internalMoveItem(item->getParent(), tmpPlayer.getInbox(), INDEX_WHEREEVER,
|
||||
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
|
||||
g_game.transformItem(item, item->getID() + 1);
|
||||
IOLoginData::savePlayer(&tmpPlayer);
|
||||
return true;
|
||||
DepotLocker* depotLocker = tmpPlayer.getDepotLocker(town->getID(), true);
|
||||
if (depotLocker) {
|
||||
if (g_game.internalMoveItem(item->getParent(), depotLocker, INDEX_WHEREEVER,
|
||||
item, item->getItemCount(), nullptr, FLAG_NOLIMIT) == RETURNVALUE_NOERROR) {
|
||||
g_game.transformItem(item, item->getID() + 1);
|
||||
IOLoginData::savePlayer(&tmpPlayer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mailbox::getReceiver(Item* item, std::string& name) const
|
||||
bool Mailbox::getDestination(Item* item, std::string& name, std::string& town) const
|
||||
{
|
||||
const Container* container = item->getContainer();
|
||||
if (container) {
|
||||
for (Item* containerItem : container->getItemList()) {
|
||||
if (containerItem->getID() == ITEM_LABEL && getReceiver(containerItem, name)) {
|
||||
if (containerItem->getID() == ITEM_LABEL && getDestination(containerItem, name, town)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -141,8 +156,24 @@ bool Mailbox::getReceiver(Item* item, std::string& name) const
|
||||
return false;
|
||||
}
|
||||
|
||||
name = getFirstLine(text);
|
||||
std::istringstream iss(text, std::istringstream::in);
|
||||
std::string temp;
|
||||
uint32_t currentLine = 1;
|
||||
|
||||
while (getline(iss, temp, '\n')) {
|
||||
if (currentLine == 1) {
|
||||
name = temp;
|
||||
} else if (currentLine == 2) {
|
||||
town = temp;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
|
||||
++currentLine;
|
||||
}
|
||||
|
||||
trimString(name);
|
||||
trimString(town);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -29,35 +29,35 @@ class Mailbox final : public Item, public Cylinder
|
||||
public:
|
||||
explicit Mailbox(uint16_t itemId) : Item(itemId) {}
|
||||
|
||||
Mailbox* getMailbox() override {
|
||||
Mailbox* getMailbox() final {
|
||||
return this;
|
||||
}
|
||||
const Mailbox* getMailbox() const override {
|
||||
const Mailbox* getMailbox() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
//cylinder implementations
|
||||
ReturnValue queryAdd(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t flags, Creature* actor = nullptr) const override;
|
||||
uint32_t flags, Creature* actor = nullptr) const final;
|
||||
ReturnValue queryMaxCount(int32_t index, const Thing& thing, uint32_t count,
|
||||
uint32_t& maxQueryCount, uint32_t flags) const override;
|
||||
ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const override;
|
||||
uint32_t& maxQueryCount, uint32_t flags) const final;
|
||||
ReturnValue queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const final;
|
||||
Cylinder* queryDestination(int32_t& index, const Thing& thing, Item** destItem,
|
||||
uint32_t& flags) override;
|
||||
uint32_t& flags) final;
|
||||
|
||||
void addThing(Thing* thing) override;
|
||||
void addThing(int32_t index, Thing* thing) override;
|
||||
void addThing(Thing* thing) final;
|
||||
void addThing(int32_t index, Thing* thing) final;
|
||||
|
||||
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) override;
|
||||
void replaceThing(uint32_t index, Thing* thing) override;
|
||||
void updateThing(Thing* thing, uint16_t itemId, uint32_t count) final;
|
||||
void replaceThing(uint32_t index, Thing* thing) final;
|
||||
|
||||
void removeThing(Thing* thing, uint32_t count) override;
|
||||
void removeThing(Thing* thing, uint32_t count) final;
|
||||
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) override;
|
||||
void postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
|
||||
void postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link = LINK_OWNER) final;
|
||||
|
||||
private:
|
||||
bool getReceiver(Item* item, std::string& name) const;
|
||||
bool getDestination(Item* item, std::string& name, std::string& town) const;
|
||||
bool sendItem(Item* item) const;
|
||||
|
||||
static bool canSend(const Item* item);
|
||||
|
83
src/map.cpp
83
src/map.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -23,6 +23,7 @@
|
||||
#include "iomapserialize.h"
|
||||
#include "combat.h"
|
||||
#include "creature.h"
|
||||
#include "monster.h"
|
||||
#include "game.h"
|
||||
|
||||
extern Game g_game;
|
||||
@ -35,6 +36,7 @@ bool Map::loadMap(const std::string& identifier, bool loadHouses)
|
||||
return false;
|
||||
}
|
||||
|
||||
Npcs::loadNpcs();
|
||||
if (!IOMap::loadSpawns(this)) {
|
||||
std::cout << "[Warning - Map::loadMap] Failed to load spawn data." << std::endl;
|
||||
}
|
||||
@ -161,7 +163,14 @@ bool Map::placeCreature(const Position& centerPos, Creature* creature, bool exte
|
||||
Tile* tile = getTile(centerPos.x, centerPos.y, centerPos.z);
|
||||
if (tile) {
|
||||
placeInPZ = tile->hasFlag(TILESTATE_PROTECTIONZONE);
|
||||
ReturnValue ret = tile->queryAdd(0, *creature, 1, FLAG_IGNOREBLOCKITEM);
|
||||
|
||||
ReturnValue ret;
|
||||
if (creature->getPlayer()) {
|
||||
ret = tile->queryAdd(0, *creature, 1, 0);
|
||||
} else {
|
||||
ret = tile->queryAdd(0, *creature, 1, (creature->getMonster() ? FLAG_PLACECHECK : FLAG_IGNOREBLOCKITEM));
|
||||
}
|
||||
|
||||
foundTile = forceLogin || ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_PLAYERISNOTINVITED;
|
||||
} else {
|
||||
placeInPZ = false;
|
||||
@ -200,7 +209,7 @@ bool Map::placeCreature(const Position& centerPos, Creature* creature, bool exte
|
||||
continue;
|
||||
}
|
||||
|
||||
if (tile->queryAdd(0, *creature, 1, 0) == RETURNVALUE_NOERROR) {
|
||||
if (tile->queryAdd(0, *creature, 1, (creature->getMonster() ? FLAG_PLACECHECK : 0)) == RETURNVALUE_NOERROR) {
|
||||
if (!extendedPos || isSightClear(centerPos, tryPos, false)) {
|
||||
foundTile = true;
|
||||
break;
|
||||
@ -234,13 +243,12 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
|
||||
|
||||
bool teleport = forceTeleport || !newTile.getGround() || !Position::areInRange<1, 1, 0>(oldPos, newPos);
|
||||
|
||||
SpectatorVec spectators, newPosSpectators;
|
||||
getSpectators(spectators, oldPos, true);
|
||||
getSpectators(newPosSpectators, newPos, true);
|
||||
spectators.addSpectators(newPosSpectators);
|
||||
SpectatorVec list;
|
||||
getSpectators(list, oldPos, true);
|
||||
getSpectators(list, newPos, true);
|
||||
|
||||
std::vector<int32_t> oldStackPosVector;
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
if (Player* tmpPlayer = spectator->getPlayer()) {
|
||||
if (tmpPlayer->canSeeCreature(&creature)) {
|
||||
oldStackPosVector.push_back(oldTile.getClientIndexOfCreature(tmpPlayer, &creature));
|
||||
@ -281,7 +289,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
|
||||
|
||||
//send to client
|
||||
size_t i = 0;
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
if (Player* tmpPlayer = spectator->getPlayer()) {
|
||||
//Use the correct stackpos
|
||||
int32_t stackpos = oldStackPosVector[i++];
|
||||
@ -292,7 +300,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
|
||||
}
|
||||
|
||||
//event method
|
||||
for (Creature* spectator : spectators) {
|
||||
for (Creature* spectator : list) {
|
||||
spectator->onCreatureMove(&creature, &newTile, newPos, &oldTile, oldPos, teleport);
|
||||
}
|
||||
|
||||
@ -300,7 +308,7 @@ void Map::moveCreature(Creature& creature, Tile& newTile, bool forceTeleport/* =
|
||||
newTile.postAddNotification(&creature, &oldTile, 0);
|
||||
}
|
||||
|
||||
void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& centerPos, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const
|
||||
void Map::getSpectatorsInternal(SpectatorVec& list, const Position& centerPos, int32_t minRangeX, int32_t maxRangeX, int32_t minRangeY, int32_t maxRangeY, int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const
|
||||
{
|
||||
int_fast16_t min_y = centerPos.y + minRangeY;
|
||||
int_fast16_t min_x = centerPos.x + minRangeX;
|
||||
@ -340,7 +348,7 @@ void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& center
|
||||
continue;
|
||||
}
|
||||
|
||||
spectators.emplace_back(creature);
|
||||
list.insert(creature);
|
||||
}
|
||||
leafE = leafE->leafE;
|
||||
} else {
|
||||
@ -356,7 +364,7 @@ void Map::getSpectatorsInternal(SpectatorVec& spectators, const Position& center
|
||||
}
|
||||
}
|
||||
|
||||
void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, bool multifloor /*= false*/, bool onlyPlayers /*= false*/, int32_t minRangeX /*= 0*/, int32_t maxRangeX /*= 0*/, int32_t minRangeY /*= 0*/, int32_t maxRangeY /*= 0*/)
|
||||
void Map::getSpectators(SpectatorVec& list, const Position& centerPos, bool multifloor /*= false*/, bool onlyPlayers /*= false*/, int32_t minRangeX /*= 0*/, int32_t maxRangeX /*= 0*/, int32_t minRangeY /*= 0*/, int32_t maxRangeY /*= 0*/)
|
||||
{
|
||||
if (centerPos.z >= MAP_MAX_LAYERS) {
|
||||
return;
|
||||
@ -374,11 +382,11 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo
|
||||
if (onlyPlayers) {
|
||||
auto it = playersSpectatorCache.find(centerPos);
|
||||
if (it != playersSpectatorCache.end()) {
|
||||
if (!spectators.empty()) {
|
||||
const SpectatorVec& cachedSpectators = it->second;
|
||||
spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end());
|
||||
if (!list.empty()) {
|
||||
const SpectatorVec& cachedList = it->second;
|
||||
list.insert(cachedList.begin(), cachedList.end());
|
||||
} else {
|
||||
spectators = it->second;
|
||||
list = it->second;
|
||||
}
|
||||
|
||||
foundCache = true;
|
||||
@ -389,17 +397,17 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo
|
||||
auto it = spectatorCache.find(centerPos);
|
||||
if (it != spectatorCache.end()) {
|
||||
if (!onlyPlayers) {
|
||||
if (!spectators.empty()) {
|
||||
const SpectatorVec& cachedSpectators = it->second;
|
||||
spectators.insert(spectators.end(), cachedSpectators.begin(), cachedSpectators.end());
|
||||
if (!list.empty()) {
|
||||
const SpectatorVec& cachedList = it->second;
|
||||
list.insert(cachedList.begin(), cachedList.end());
|
||||
} else {
|
||||
spectators = it->second;
|
||||
list = it->second;
|
||||
}
|
||||
} else {
|
||||
const SpectatorVec& cachedSpectators = it->second;
|
||||
for (Creature* spectator : cachedSpectators) {
|
||||
const SpectatorVec& cachedList = it->second;
|
||||
for (Creature* spectator : cachedList) {
|
||||
if (spectator->getPlayer()) {
|
||||
spectators.emplace_back(spectator);
|
||||
list.insert(spectator);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -437,13 +445,13 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo
|
||||
maxRangeZ = centerPos.z;
|
||||
}
|
||||
|
||||
getSpectatorsInternal(spectators, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers);
|
||||
getSpectatorsInternal(list, centerPos, minRangeX, maxRangeX, minRangeY, maxRangeY, minRangeZ, maxRangeZ, onlyPlayers);
|
||||
|
||||
if (cacheResult) {
|
||||
if (onlyPlayers) {
|
||||
playersSpectatorCache[centerPos] = spectators;
|
||||
playersSpectatorCache[centerPos] = list;
|
||||
} else {
|
||||
spectatorCache[centerPos] = spectators;
|
||||
spectatorCache[centerPos] = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -452,10 +460,6 @@ void Map::getSpectators(SpectatorVec& spectators, const Position& centerPos, boo
|
||||
void Map::clearSpectatorCache()
|
||||
{
|
||||
spectatorCache.clear();
|
||||
}
|
||||
|
||||
void Map::clearPlayersSpectatorCache()
|
||||
{
|
||||
playersSpectatorCache.clear();
|
||||
}
|
||||
|
||||
@ -559,7 +563,7 @@ const Tile* Map::canWalkTo(const Creature& creature, const Position& pos) const
|
||||
//used for non-cached tiles
|
||||
Tile* tile = getTile(pos.x, pos.y, pos.z);
|
||||
if (creature.getTile() != tile) {
|
||||
if (!tile || tile->queryAdd(0, creature, 1, FLAG_PATHFINDING | FLAG_IGNOREFIELDDAMAGE) != RETURNVALUE_NOERROR) {
|
||||
if (!tile || tile->queryAdd(0, creature, 1, FLAG_PATHFINDING) != RETURNVALUE_NOERROR) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -842,16 +846,25 @@ int_fast32_t AStarNodes::getTileWalkCost(const Creature& creature, const Tile* t
|
||||
{
|
||||
int_fast32_t cost = 0;
|
||||
if (tile->getTopVisibleCreature(&creature) != nullptr) {
|
||||
if (const Monster* monster = creature.getMonster()) {
|
||||
if (monster->canPushCreatures()) {
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
//destroy creature cost
|
||||
cost += MAP_NORMALWALKCOST * 3;
|
||||
}
|
||||
|
||||
if (const MagicField* field = tile->getFieldItem()) {
|
||||
CombatType_t combatType = field->getCombatType();
|
||||
if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) {
|
||||
cost += MAP_NORMALWALKCOST * 18;
|
||||
if (combatType != COMBAT_NONE) {
|
||||
if (!creature.isImmune(combatType) && !creature.hasCondition(Combat::DamageToConditionType(combatType))) {
|
||||
cost += MAP_NORMALWALKCOST * 18;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
|
25
src/map.h
25
src/map.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -73,7 +73,7 @@ class AStarNodes
|
||||
int_fast32_t closedNodes;
|
||||
};
|
||||
|
||||
using SpectatorCache = std::map<Position, SpectatorVec>;
|
||||
typedef std::map<Position, SpectatorVec> SpectatorCache;
|
||||
|
||||
static constexpr int32_t FLOOR_BITS = 3;
|
||||
static constexpr int32_t FLOOR_SIZE = (1 << FLOOR_BITS);
|
||||
@ -110,7 +110,7 @@ class QTreeNode
|
||||
QTreeLeafNode* getLeaf(uint32_t x, uint32_t y);
|
||||
|
||||
template<typename Leaf, typename Node>
|
||||
static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y)
|
||||
inline static Leaf getLeafStatic(Node node, uint32_t x, uint32_t y)
|
||||
{
|
||||
do {
|
||||
node = node->child[((x & 0x8000) >> 15) | ((y & 0x8000) >> 14)];
|
||||
@ -127,11 +127,10 @@ class QTreeNode
|
||||
QTreeLeafNode* createLeaf(uint32_t x, uint32_t y, uint32_t level);
|
||||
|
||||
protected:
|
||||
bool leaf = false;
|
||||
|
||||
private:
|
||||
QTreeNode* child[4] = {};
|
||||
|
||||
bool leaf = false;
|
||||
|
||||
friend class Map;
|
||||
};
|
||||
|
||||
@ -153,7 +152,7 @@ class QTreeLeafNode final : public QTreeNode
|
||||
void addCreature(Creature* c);
|
||||
void removeCreature(Creature* c);
|
||||
|
||||
private:
|
||||
protected:
|
||||
static bool newLeaf;
|
||||
QTreeLeafNode* leafS = nullptr;
|
||||
QTreeLeafNode* leafE = nullptr;
|
||||
@ -197,7 +196,7 @@ class Map
|
||||
* \returns A pointer to that tile.
|
||||
*/
|
||||
Tile* getTile(uint16_t x, uint16_t y, uint8_t z) const;
|
||||
Tile* getTile(const Position& pos) const {
|
||||
inline Tile* getTile(const Position& pos) const {
|
||||
return getTile(pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
@ -220,12 +219,11 @@ class Map
|
||||
|
||||
void moveCreature(Creature& creature, Tile& newTile, bool forceTeleport = false);
|
||||
|
||||
void getSpectators(SpectatorVec& spectators, const Position& centerPos, bool multifloor = false, bool onlyPlayers = false,
|
||||
void getSpectators(SpectatorVec& list, const Position& centerPos, bool multifloor = false, bool onlyPlayers = false,
|
||||
int32_t minRangeX = 0, int32_t maxRangeX = 0,
|
||||
int32_t minRangeY = 0, int32_t maxRangeY = 0);
|
||||
|
||||
void clearSpectatorCache();
|
||||
void clearPlayersSpectatorCache();
|
||||
|
||||
/**
|
||||
* Checks if you can throw an object to that position
|
||||
@ -264,8 +262,7 @@ class Map
|
||||
Spawns spawns;
|
||||
Towns towns;
|
||||
Houses houses;
|
||||
|
||||
private:
|
||||
protected:
|
||||
SpectatorCache spectatorCache;
|
||||
SpectatorCache playersSpectatorCache;
|
||||
|
||||
@ -278,7 +275,7 @@ class Map
|
||||
uint32_t height = 0;
|
||||
|
||||
// Actually scans the map for spectators
|
||||
void getSpectatorsInternal(SpectatorVec& spectators, const Position& centerPos,
|
||||
void getSpectatorsInternal(SpectatorVec& list, const Position& centerPos,
|
||||
int32_t minRangeX, int32_t maxRangeX,
|
||||
int32_t minRangeY, int32_t maxRangeY,
|
||||
int32_t minRangeZ, int32_t maxRangeZ, bool onlyPlayers) const;
|
||||
|
683
src/monster.cpp
683
src/monster.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -22,11 +22,11 @@
|
||||
#include "monster.h"
|
||||
#include "game.h"
|
||||
#include "spells.h"
|
||||
#include "events.h"
|
||||
#include "configmanager.h"
|
||||
|
||||
extern ConfigManager g_config;
|
||||
extern Game g_game;
|
||||
extern Monsters g_monsters;
|
||||
extern Events* g_events;
|
||||
|
||||
int32_t Monster::despawnRange;
|
||||
int32_t Monster::despawnRadius;
|
||||
@ -42,10 +42,10 @@ Monster* Monster::createMonster(const std::string& name)
|
||||
return new Monster(mType);
|
||||
}
|
||||
|
||||
Monster::Monster(MonsterType* mType) :
|
||||
Monster::Monster(MonsterType* mtype) :
|
||||
Creature(),
|
||||
strDescription(mType->nameDescription),
|
||||
mType(mType)
|
||||
strDescription(asLowerCaseString(mtype->nameDescription)),
|
||||
mType(mtype)
|
||||
{
|
||||
defaultOutfit = mType->info.outfit;
|
||||
currentOutfit = mType->info.outfit;
|
||||
@ -80,31 +80,45 @@ void Monster::removeList()
|
||||
g_game.removeMonster(this);
|
||||
}
|
||||
|
||||
int32_t Monster::getDefense()
|
||||
{
|
||||
int32_t totalDefense = mType->info.defense + 1;
|
||||
int32_t defenseSkill = mType->info.skill;
|
||||
|
||||
fightMode_t attackMode = FIGHTMODE_BALANCED;
|
||||
|
||||
if ((followCreature || !attackedCreature) && earliestAttackTime <= OTSYS_TIME()) {
|
||||
attackMode = FIGHTMODE_DEFENSE;
|
||||
}
|
||||
|
||||
if (attackMode == FIGHTMODE_ATTACK) {
|
||||
totalDefense -= 4 * totalDefense / 10;
|
||||
} else if (attackMode == FIGHTMODE_DEFENSE) {
|
||||
totalDefense += 8 * totalDefense / 10;
|
||||
}
|
||||
|
||||
if (totalDefense) {
|
||||
int32_t formula = (5 * (defenseSkill) + 50) * totalDefense;
|
||||
int32_t randresult = rand() % 100;
|
||||
|
||||
totalDefense = formula * ((rand() % 100 + randresult) / 2) / 10000.;
|
||||
}
|
||||
|
||||
return totalDefense;
|
||||
}
|
||||
|
||||
bool Monster::canSee(const Position& pos) const
|
||||
{
|
||||
return Creature::canSee(getPosition(), pos, 9, 9);
|
||||
}
|
||||
|
||||
bool Monster::canWalkOnFieldType(CombatType_t combatType) const
|
||||
void Monster::onAttackedCreature(Creature* creature)
|
||||
{
|
||||
switch (combatType) {
|
||||
case COMBAT_ENERGYDAMAGE:
|
||||
return mType->info.canWalkOnEnergy;
|
||||
case COMBAT_FIREDAMAGE:
|
||||
return mType->info.canWalkOnFire;
|
||||
case COMBAT_EARTHDAMAGE:
|
||||
return mType->info.canWalkOnPoison;
|
||||
default:
|
||||
return true;
|
||||
if (isSummon() && getMaster()) {
|
||||
master->onAttackedCreature(creature);
|
||||
}
|
||||
}
|
||||
|
||||
void Monster::onAttackedCreatureDisappear(bool)
|
||||
{
|
||||
attackTicks = 0;
|
||||
extraMeleeAttack = true;
|
||||
}
|
||||
|
||||
void Monster::onCreatureAppear(Creature* creature, bool isLogin)
|
||||
{
|
||||
Creature::onCreatureAppear(creature, isLogin);
|
||||
@ -238,7 +252,7 @@ void Monster::onCreatureMove(Creature* creature, const Tile* newTile, const Posi
|
||||
}
|
||||
|
||||
if (canSeeNewPos && isSummon() && getMaster() == creature) {
|
||||
isMasterInRange = true; //Follow master again
|
||||
isMasterInRange = true; //Follow master again
|
||||
}
|
||||
|
||||
updateIdleStatus();
|
||||
@ -250,7 +264,7 @@ void Monster::onCreatureMove(Creature* creature, const Tile* newTile, const Posi
|
||||
|
||||
int32_t offset_x = Position::getDistanceX(followPosition, position);
|
||||
int32_t offset_y = Position::getDistanceY(followPosition, position);
|
||||
if ((offset_x > 1 || offset_y > 1) && mType->info.changeTargetChance > 0) {
|
||||
if ((offset_x > 1 || offset_y > 1) && mType->info.changeTargetChance > 0 && targetChangeCooldown <= 0) {
|
||||
Direction dir = getDirectionTo(position, followPosition);
|
||||
const Position& checkPosition = getNextPosition(dir, position);
|
||||
|
||||
@ -365,10 +379,10 @@ void Monster::updateTargetList()
|
||||
}
|
||||
}
|
||||
|
||||
SpectatorVec spectators;
|
||||
g_game.map.getSpectators(spectators, position, true);
|
||||
spectators.erase(this);
|
||||
for (Creature* spectator : spectators) {
|
||||
SpectatorVec list;
|
||||
g_game.map.getSpectators(list, position, true);
|
||||
list.erase(this);
|
||||
for (Creature* spectator : list) {
|
||||
if (canSee(spectator->getPosition())) {
|
||||
onCreatureFound(spectator);
|
||||
}
|
||||
@ -481,14 +495,14 @@ void Monster::onCreatureLeave(Creature* creature)
|
||||
}
|
||||
}
|
||||
|
||||
bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAULT*/)
|
||||
bool Monster::searchTarget(TargetSearchType_t searchType)
|
||||
{
|
||||
std::list<Creature*> resultList;
|
||||
const Position& myPos = getPosition();
|
||||
|
||||
for (Creature* creature : targetList) {
|
||||
if (followCreature != creature && isTarget(creature)) {
|
||||
if (searchType == TARGETSEARCH_RANDOM || canUseAttack(myPos, creature)) {
|
||||
if (searchType == TARGETSEARCH_ANY || canUseAttack(myPos, creature)) {
|
||||
resultList.push_back(creature);
|
||||
}
|
||||
}
|
||||
@ -497,32 +511,31 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
|
||||
switch (searchType) {
|
||||
case TARGETSEARCH_NEAREST: {
|
||||
Creature* target = nullptr;
|
||||
|
||||
int32_t minRange = 0;
|
||||
if (attackedCreature) {
|
||||
const Position& targetPosition = attackedCreature->getPosition();
|
||||
minRange = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition);
|
||||
}
|
||||
|
||||
if (!resultList.empty()) {
|
||||
auto it = resultList.begin();
|
||||
target = *it;
|
||||
for (Creature* creature : resultList) {
|
||||
const Position& targetPosition = creature->getPosition();
|
||||
|
||||
if (++it != resultList.end()) {
|
||||
const Position& targetPosition = target->getPosition();
|
||||
int32_t minRange = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition);
|
||||
do {
|
||||
const Position& pos = (*it)->getPosition();
|
||||
|
||||
int32_t distance = Position::getDistanceX(myPos, pos) + Position::getDistanceY(myPos, pos);
|
||||
if (distance < minRange) {
|
||||
target = *it;
|
||||
minRange = distance;
|
||||
}
|
||||
} while (++it != resultList.end());
|
||||
int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition);
|
||||
if (distance < minRange) {
|
||||
target = creature;
|
||||
minRange = distance;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int32_t minRange = std::numeric_limits<int32_t>::max();
|
||||
for (Creature* creature : targetList) {
|
||||
if (!isTarget(creature)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Position& pos = creature->getPosition();
|
||||
int32_t distance = Position::getDistanceX(myPos, pos) + Position::getDistanceY(myPos, pos);
|
||||
const Position& targetPosition = creature->getPosition();
|
||||
int32_t distance = Position::getDistanceX(myPos, targetPosition) + Position::getDistanceY(myPos, targetPosition);
|
||||
if (distance < minRange) {
|
||||
target = creature;
|
||||
minRange = distance;
|
||||
@ -535,10 +548,79 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TARGETSEARCH_WEAKEST: {
|
||||
Creature* target = nullptr;
|
||||
|
||||
case TARGETSEARCH_DEFAULT:
|
||||
case TARGETSEARCH_ATTACKRANGE:
|
||||
case TARGETSEARCH_RANDOM:
|
||||
int32_t health = 0;
|
||||
if (attackedCreature) {
|
||||
health = attackedCreature->getMaxHealth();
|
||||
}
|
||||
|
||||
if (!resultList.empty()) {
|
||||
for (Creature* creature : resultList) {
|
||||
if (creature->getMaxHealth() < health) {
|
||||
target = creature;
|
||||
health = creature->getMaxHealth();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Creature* creature : targetList) {
|
||||
if (creature->getMaxHealth() < health) {
|
||||
target = creature;
|
||||
health = creature->getMaxHealth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target && selectTarget(target)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TARGETSEARCH_MOSTDAMAGE: {
|
||||
Creature* target = nullptr;
|
||||
|
||||
int32_t maxDamage = 0;
|
||||
|
||||
if (!resultList.empty()) {
|
||||
for (Creature* creature : resultList) {
|
||||
auto it = damageMap.find(creature->getID());
|
||||
if (it == damageMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t damage = it->second.total;
|
||||
|
||||
if (OTSYS_TIME() - it->second.ticks <= g_config.getNumber(ConfigManager::PZ_LOCKED)) {
|
||||
if (damage > maxDamage) {
|
||||
target = creature;
|
||||
maxDamage = damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (Creature* creature : targetList) {
|
||||
auto it = damageMap.find(creature->getID());
|
||||
if (it == damageMap.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int32_t damage = it->second.total;
|
||||
|
||||
if (OTSYS_TIME() - it->second.ticks <= g_config.getNumber(ConfigManager::PZ_LOCKED)) {
|
||||
if (damage > maxDamage) {
|
||||
target = creature;
|
||||
maxDamage = damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (target && selectTarget(target)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (!resultList.empty()) {
|
||||
auto it = resultList.begin();
|
||||
@ -546,20 +628,19 @@ bool Monster::searchTarget(TargetSearchType_t searchType /*= TARGETSEARCH_DEFAUL
|
||||
return selectTarget(*it);
|
||||
}
|
||||
|
||||
if (searchType == TARGETSEARCH_ATTACKRANGE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//lets just pick the first target in the list
|
||||
for (Creature* target : targetList) {
|
||||
if (followCreature != target && selectTarget(target)) {
|
||||
return true;
|
||||
//lets just pick the first target in the list if we do not have a target
|
||||
if (!attackedCreature) {
|
||||
for (Creature* target : targetList) {
|
||||
if (followCreature != target && selectTarget(target)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -637,6 +718,10 @@ bool Monster::selectTarget(Creature* creature)
|
||||
g_dispatcher.addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game, getID())));
|
||||
}
|
||||
}
|
||||
|
||||
// without this task, monster would randomly start dancing until next game round
|
||||
g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game, getID())));
|
||||
targetChangeCooldown += 3000;
|
||||
return setFollowCreature(creature);
|
||||
}
|
||||
|
||||
@ -673,7 +758,7 @@ void Monster::updateIdleStatus()
|
||||
|
||||
void Monster::onAddCondition(ConditionType_t type)
|
||||
{
|
||||
if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) {
|
||||
if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON || type == CONDITION_AGGRESSIVE) {
|
||||
updateMapCache();
|
||||
}
|
||||
|
||||
@ -682,8 +767,7 @@ void Monster::onAddCondition(ConditionType_t type)
|
||||
|
||||
void Monster::onEndCondition(ConditionType_t type)
|
||||
{
|
||||
if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON) {
|
||||
ignoreFieldDamage = false;
|
||||
if (type == CONDITION_FIRE || type == CONDITION_ENERGY || type == CONDITION_POISON || type == CONDITION_AGGRESSIVE) {
|
||||
updateMapCache();
|
||||
}
|
||||
|
||||
@ -692,6 +776,10 @@ void Monster::onEndCondition(ConditionType_t type)
|
||||
|
||||
void Monster::onThink(uint32_t interval)
|
||||
{
|
||||
if (OTSYS_TIME() < earliestWakeUpTime) {
|
||||
return;
|
||||
}
|
||||
|
||||
Creature::onThink(interval);
|
||||
|
||||
if (mType->info.thinkEvent != -1) {
|
||||
@ -718,9 +806,10 @@ void Monster::onThink(uint32_t interval)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isInSpawnRange(position)) {
|
||||
g_game.internalTeleport(this, masterPos);
|
||||
setIdle(true);
|
||||
if (!isInSpawnRange(position) || (lifetime > 0 && (OTSYS_TIME() >= lifetime))) {
|
||||
// Despawn creatures if they are out of their spawn zone
|
||||
g_game.removeCreature(this);
|
||||
g_game.addMagicEffect(getPosition(), CONST_ME_POFF);
|
||||
} else {
|
||||
updateIdleStatus();
|
||||
|
||||
@ -730,7 +819,6 @@ void Monster::onThink(uint32_t interval)
|
||||
if (isSummon()) {
|
||||
if (!attackedCreature) {
|
||||
if (getMaster() && getMaster()->getAttackedCreature()) {
|
||||
//This happens if the monster is summoned during combat
|
||||
selectTarget(getMaster()->getAttackedCreature());
|
||||
} else if (getMaster() != followCreature) {
|
||||
//Our master has not ordered us to attack anything, lets follow him around instead.
|
||||
@ -739,15 +827,23 @@ void Monster::onThink(uint32_t interval)
|
||||
} else if (attackedCreature == this) {
|
||||
setFollowCreature(nullptr);
|
||||
} else if (followCreature != attackedCreature) {
|
||||
//This happens just after a master orders an attack, so lets follow it aswell.
|
||||
setFollowCreature(attackedCreature);
|
||||
}
|
||||
|
||||
if (master) {
|
||||
if (Monster* monster = master->getMonster()) {
|
||||
if (monster->mType->info.targetDistance <= 1 && !monster->hasFollowPath) {
|
||||
setFollowCreature(master);
|
||||
setAttackedCreature(nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!targetList.empty()) {
|
||||
if (!followCreature || !hasFollowPath) {
|
||||
searchTarget();
|
||||
searchTarget(TARGETSEARCH_ANY);
|
||||
} else if (isFleeing()) {
|
||||
if (attackedCreature && !canUseAttack(getPosition(), attackedCreature)) {
|
||||
searchTarget(TARGETSEARCH_ATTACKRANGE);
|
||||
searchTarget(TARGETSEARCH_NEAREST);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -759,56 +855,43 @@ void Monster::onThink(uint32_t interval)
|
||||
}
|
||||
}
|
||||
|
||||
void Monster::doAttacking(uint32_t interval)
|
||||
void Monster::doAttacking(uint32_t)
|
||||
{
|
||||
if (!attackedCreature || (isSummon() && attackedCreature == this)) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool updateLook = true;
|
||||
bool resetTicks = interval != 0;
|
||||
attackTicks += interval;
|
||||
|
||||
const Position& myPos = getPosition();
|
||||
const Position& targetPos = attackedCreature->getPosition();
|
||||
|
||||
for (const spellBlock_t& spellBlock : mType->info.attackSpells) {
|
||||
bool inRange = false;
|
||||
|
||||
if (attackedCreature == nullptr) {
|
||||
break;
|
||||
bool updateLook = false;
|
||||
|
||||
if (OTSYS_TIME() >= earliestAttackTime && !isFleeing()) {
|
||||
updateLook = true;
|
||||
if (Combat::closeAttack(this, attackedCreature, FIGHTMODE_BALANCED)) {
|
||||
egibleToDance = true;
|
||||
earliestAttackTime = OTSYS_TIME() + 2000;
|
||||
removeCondition(CONDITION_AGGRESSIVE, true);
|
||||
}
|
||||
|
||||
if (canUseSpell(myPos, targetPos, spellBlock, interval, inRange, resetTicks)) {
|
||||
if (spellBlock.chance >= static_cast<uint32_t>(uniform_random(1, 100))) {
|
||||
if (updateLook) {
|
||||
updateLookDirection();
|
||||
updateLook = false;
|
||||
}
|
||||
}
|
||||
|
||||
for (spellBlock_t& spellBlock : mType->info.attackSpells) {
|
||||
if (spellBlock.range != 0 && std::max<uint32_t>(Position::getDistanceX(myPos, targetPos), Position::getDistanceY(myPos, targetPos)) <= spellBlock.range) {
|
||||
if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) {
|
||||
updateLookDirection();
|
||||
|
||||
minCombatValue = spellBlock.minCombatValue;
|
||||
maxCombatValue = spellBlock.maxCombatValue;
|
||||
|
||||
spellBlock.spell->castSpell(this, attackedCreature);
|
||||
|
||||
if (spellBlock.isMelee) {
|
||||
extraMeleeAttack = false;
|
||||
}
|
||||
egibleToDance = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!inRange && spellBlock.isMelee) {
|
||||
//melee swing out of reach
|
||||
extraMeleeAttack = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (updateLook) {
|
||||
updateLookDirection();
|
||||
}
|
||||
|
||||
if (resetTicks) {
|
||||
attackTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool Monster::canUseAttack(const Position& pos, const Creature* target) const
|
||||
@ -826,40 +909,6 @@ bool Monster::canUseAttack(const Position& pos, const Creature* target) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Monster::canUseSpell(const Position& pos, const Position& targetPos,
|
||||
const spellBlock_t& sb, uint32_t interval, bool& inRange, bool& resetTicks)
|
||||
{
|
||||
inRange = true;
|
||||
|
||||
if (sb.isMelee && isFleeing()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (extraMeleeAttack) {
|
||||
lastMeleeAttack = OTSYS_TIME();
|
||||
} else if (sb.isMelee && (OTSYS_TIME() - lastMeleeAttack) < 1500) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!sb.isMelee || !extraMeleeAttack) {
|
||||
if (sb.speed > attackTicks) {
|
||||
resetTicks = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (attackTicks % sb.speed >= interval) {
|
||||
//already used this spell for this round
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (sb.range != 0 && std::max<uint32_t>(Position::getDistanceX(pos, targetPos), Position::getDistanceY(pos, targetPos)) > sb.range) {
|
||||
inRange = false;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Monster::onThinkTarget(uint32_t interval)
|
||||
{
|
||||
if (!isSummon()) {
|
||||
@ -882,13 +931,42 @@ void Monster::onThinkTarget(uint32_t interval)
|
||||
|
||||
if (targetChangeTicks >= mType->info.changeTargetSpeed) {
|
||||
targetChangeTicks = 0;
|
||||
targetChangeCooldown = mType->info.changeTargetSpeed;
|
||||
|
||||
if (mType->info.changeTargetChance >= uniform_random(1, 100)) {
|
||||
if (mType->info.targetDistance <= 1) {
|
||||
searchTarget(TARGETSEARCH_RANDOM);
|
||||
} else {
|
||||
searchTarget(TARGETSEARCH_NEAREST);
|
||||
if (mType->info.changeTargetChance > uniform_random(0, 99)) {
|
||||
// search target strategies, if no strategy succeeds, target is not switched
|
||||
int32_t random = uniform_random(0, 99);
|
||||
int32_t current_strategy = 0;
|
||||
|
||||
TargetSearchType_t searchType = TARGETSEARCH_ANY;
|
||||
|
||||
do
|
||||
{
|
||||
int32_t strategy = 0;
|
||||
|
||||
if (current_strategy == 0) {
|
||||
strategy = mType->info.strategyNearestEnemy;
|
||||
searchType = TARGETSEARCH_NEAREST;
|
||||
} else if (current_strategy == 1) {
|
||||
strategy = mType->info.strategyWeakestEnemy;
|
||||
searchType = TARGETSEARCH_WEAKEST;
|
||||
} else if (current_strategy == 2) {
|
||||
strategy = mType->info.strategyMostDamageEnemy;
|
||||
searchType = TARGETSEARCH_MOSTDAMAGE;
|
||||
} else if (current_strategy == 3) {
|
||||
strategy = mType->info.strategyRandomEnemy;
|
||||
searchType = TARGETSEARCH_RANDOM;
|
||||
}
|
||||
|
||||
if (random < strategy) {
|
||||
break;
|
||||
}
|
||||
|
||||
current_strategy++;
|
||||
random -= strategy;
|
||||
} while (current_strategy <= 3);
|
||||
|
||||
if (searchType != TARGETSEARCH_ANY) {
|
||||
searchTarget(searchType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -897,23 +975,10 @@ void Monster::onThinkTarget(uint32_t interval)
|
||||
}
|
||||
}
|
||||
|
||||
void Monster::onThinkDefense(uint32_t interval)
|
||||
void Monster::onThinkDefense(uint32_t)
|
||||
{
|
||||
bool resetTicks = true;
|
||||
defenseTicks += interval;
|
||||
|
||||
for (const spellBlock_t& spellBlock : mType->info.defenseSpells) {
|
||||
if (spellBlock.speed > defenseTicks) {
|
||||
resetTicks = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (defenseTicks % spellBlock.speed >= interval) {
|
||||
//already used this spell for this round
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((spellBlock.chance >= static_cast<uint32_t>(uniform_random(1, 100)))) {
|
||||
if (uniform_random(0, spellBlock.chance) == 0 && (master || health > mType->info.runAwayHealth || uniform_random(1, 3) == 1)) {
|
||||
minCombatValue = spellBlock.minCombatValue;
|
||||
maxCombatValue = spellBlock.maxCombatValue;
|
||||
spellBlock.spell->castSpell(this, this);
|
||||
@ -922,20 +987,10 @@ void Monster::onThinkDefense(uint32_t interval)
|
||||
|
||||
if (!isSummon() && summons.size() < mType->info.maxSummons && hasFollowPath) {
|
||||
for (const summonBlock_t& summonBlock : mType->info.summons) {
|
||||
if (summonBlock.speed > defenseTicks) {
|
||||
resetTicks = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (summons.size() >= mType->info.maxSummons) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (defenseTicks % summonBlock.speed >= interval) {
|
||||
//already used this spell for this round
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t summonCount = 0;
|
||||
for (Creature* summon : summons) {
|
||||
if (summon->getName() == summonBlock.name) {
|
||||
@ -947,49 +1002,41 @@ void Monster::onThinkDefense(uint32_t interval)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (summonBlock.chance < static_cast<uint32_t>(uniform_random(1, 100))) {
|
||||
continue;
|
||||
}
|
||||
if (normal_random(0, summonBlock.chance) == 0 && (health > mType->info.runAwayHealth || normal_random(1, 3) == 1)) {
|
||||
Monster* summon = Monster::createMonster(summonBlock.name);
|
||||
if (summon) {
|
||||
const Position& summonPos = getPosition();
|
||||
|
||||
Monster* summon = Monster::createMonster(summonBlock.name);
|
||||
if (summon) {
|
||||
if (g_game.placeCreature(summon, getPosition(), false, summonBlock.force)) {
|
||||
summon->setDropLoot(false);
|
||||
summon->setSkillLoss(false);
|
||||
summon->setMaster(this);
|
||||
g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE);
|
||||
g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT);
|
||||
} else {
|
||||
delete summon;
|
||||
addSummon(summon);
|
||||
|
||||
if (!g_game.placeCreature(summon, summonPos, false, summonBlock.force)) {
|
||||
removeSummon(summon);
|
||||
} else {
|
||||
g_game.addMagicEffect(getPosition(), CONST_ME_MAGIC_BLUE);
|
||||
g_game.addMagicEffect(summon->getPosition(), CONST_ME_TELEPORT);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resetTicks) {
|
||||
defenseTicks = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Monster::onThinkYell(uint32_t interval)
|
||||
void Monster::onThinkYell(uint32_t)
|
||||
{
|
||||
if (mType->info.yellSpeedTicks == 0) {
|
||||
if (mType->info.voiceVector.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
yellTicks += interval;
|
||||
if (yellTicks >= mType->info.yellSpeedTicks) {
|
||||
yellTicks = 0;
|
||||
int32_t randomResult = rand();
|
||||
if (rand() == 50 * (randomResult / 50)) {
|
||||
int32_t totalVoices = mType->info.voiceVector.size();
|
||||
const voiceBlock_t& voice = mType->info.voiceVector[rand() % totalVoices + 1];
|
||||
|
||||
if (!mType->info.voiceVector.empty() && (mType->info.yellChance >= static_cast<uint32_t>(uniform_random(1, 100)))) {
|
||||
uint32_t index = uniform_random(0, mType->info.voiceVector.size() - 1);
|
||||
const voiceBlock_t& vb = mType->info.voiceVector[index];
|
||||
|
||||
if (vb.yellText) {
|
||||
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, vb.text, false);
|
||||
} else {
|
||||
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, vb.text, false);
|
||||
}
|
||||
if (voice.yellText) {
|
||||
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_YELL, voice.text, false);
|
||||
}
|
||||
else {
|
||||
g_game.internalCreatureSay(this, TALKTYPE_MONSTER_SAY, voice.text, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1112,27 +1159,117 @@ bool Monster::getNextStep(Direction& direction, uint32_t& flags)
|
||||
|
||||
bool result = false;
|
||||
if ((!followCreature || !hasFollowPath) && (!isSummon() || !isMasterInRange)) {
|
||||
if (getTimeSinceLastMove() >= 1000) {
|
||||
randomStepping = true;
|
||||
if (OTSYS_TIME() >= nextDanceStepRound) {
|
||||
updateLookDirection();
|
||||
nextDanceStepRound = OTSYS_TIME() + getStepDuration();
|
||||
|
||||
//choose a random direction
|
||||
result = getRandomStep(getPosition(), direction);
|
||||
}
|
||||
} else if ((isSummon() && isMasterInRange) || followCreature) {
|
||||
randomStepping = false;
|
||||
result = Creature::getNextStep(direction, flags);
|
||||
if (result) {
|
||||
flags |= FLAG_PATHFINDING;
|
||||
} else {
|
||||
if (ignoreFieldDamage) {
|
||||
ignoreFieldDamage = false;
|
||||
updateMapCache();
|
||||
}
|
||||
//target dancing
|
||||
if (attackedCreature && attackedCreature == followCreature) {
|
||||
if (isFleeing()) {
|
||||
result = getDanceStep(getPosition(), direction, false, false);
|
||||
} else if (mType->info.staticAttackChance < static_cast<uint32_t>(uniform_random(1, 100))) {
|
||||
result = getDanceStep(getPosition(), direction);
|
||||
} else if (egibleToDance && OTSYS_TIME() >= earliestDanceTime) {
|
||||
if (mType->info.targetDistance >= 4) {
|
||||
const Position& myPos = getPosition();
|
||||
const Position targetPos = attackedCreature->getPosition();
|
||||
|
||||
if (Position::getDistanceX(myPos, targetPos) == 4 || Position::getDistanceY(myPos, targetPos) == 4) {
|
||||
int32_t currentX = myPos.x;
|
||||
int32_t currentY = myPos.y;
|
||||
int32_t danceRandom = rand();
|
||||
int32_t danceRandomResult = danceRandom % 5;
|
||||
|
||||
if (danceRandom % 5 == 1) {
|
||||
direction = DIRECTION_EAST;
|
||||
currentX++;
|
||||
} else if (danceRandomResult <= 1) {
|
||||
if (danceRandom == 5 * (danceRandom / 5)) {
|
||||
direction = DIRECTION_WEST;
|
||||
currentX--;
|
||||
}
|
||||
} else if (danceRandomResult == 2) {
|
||||
direction = DIRECTION_NORTH;
|
||||
currentY--;
|
||||
} else if (danceRandomResult == 3) {
|
||||
direction = DIRECTION_SOUTH;
|
||||
currentY++;
|
||||
}
|
||||
|
||||
if (danceRandomResult <= 3 && canWalkTo(myPos, direction)) {
|
||||
int32_t xTest = targetPos.x - currentX;
|
||||
if (currentX - targetPos.x > -1) {
|
||||
xTest = currentX - targetPos.x;
|
||||
}
|
||||
|
||||
int32_t yTest = targetPos.y - currentY;
|
||||
if (currentY - targetPos.y > -1) {
|
||||
yTest = currentY - targetPos.y;
|
||||
}
|
||||
|
||||
int32_t realTest = yTest;
|
||||
|
||||
if (xTest >= yTest) {
|
||||
realTest = xTest;
|
||||
}
|
||||
|
||||
if (realTest == 4) {
|
||||
result = true;
|
||||
egibleToDance = false;
|
||||
earliestWakeUpTime = OTSYS_TIME() + 1000;
|
||||
earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration();
|
||||
earliestAttackTime += 200;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const Position& myPos = getPosition();
|
||||
const Position targetPos = attackedCreature->getPosition();
|
||||
|
||||
if (Position::areInRange<1, 1>(myPos, targetPos)) {
|
||||
int32_t danceRandom = rand();
|
||||
int32_t danceRandomResult = danceRandom % 5;
|
||||
|
||||
int32_t currentX = myPos.x;
|
||||
int32_t currentY = myPos.y;
|
||||
|
||||
if (danceRandom % 5 == 1) {
|
||||
direction = DIRECTION_EAST;
|
||||
currentX++;
|
||||
} else if (danceRandomResult <= 1) {
|
||||
if (danceRandom == 5 * (danceRandom / 5)) {
|
||||
direction = DIRECTION_WEST;
|
||||
currentX--;
|
||||
}
|
||||
} else if (danceRandomResult == 2) {
|
||||
direction = DIRECTION_NORTH;
|
||||
currentY--;
|
||||
} else if (danceRandomResult == 3) {
|
||||
direction = DIRECTION_SOUTH;
|
||||
currentY++;
|
||||
}
|
||||
|
||||
Position position = myPos;
|
||||
position.x = currentX;
|
||||
position.y = currentY;
|
||||
|
||||
if (danceRandomResult <= 3 &&
|
||||
canWalkTo(myPos, direction) &&
|
||||
Position::areInRange<1, 1>(position, targetPos)) {
|
||||
result = true;
|
||||
egibleToDance = false;
|
||||
earliestWakeUpTime = OTSYS_TIME() + 1000;
|
||||
earliestDanceTime = OTSYS_TIME() + 1000 + getStepDuration();
|
||||
earliestAttackTime += 200;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1343,7 +1480,7 @@ bool Monster::getDistanceStep(const Position& targetPos, Direction& direction, b
|
||||
//escape to NW , W or N [and some extra]
|
||||
bool w = canWalkTo(creaturePos, DIRECTION_WEST);
|
||||
bool n = canWalkTo(creaturePos, DIRECTION_NORTH);
|
||||
|
||||
|
||||
if (w && n) {
|
||||
direction = boolean_random() ? DIRECTION_WEST : DIRECTION_NORTH;
|
||||
return true;
|
||||
@ -1776,7 +1913,8 @@ void Monster::death(Creature*)
|
||||
|
||||
for (Creature* summon : summons) {
|
||||
summon->changeHealth(-summon->getHealth());
|
||||
summon->removeMaster();
|
||||
summon->setMaster(nullptr);
|
||||
summon->decrementReferenceCounter();
|
||||
}
|
||||
summons.clear();
|
||||
|
||||
@ -1855,61 +1993,86 @@ void Monster::updateLookDirection()
|
||||
//look EAST/WEST
|
||||
if (offsetx < 0) {
|
||||
newDir = DIRECTION_WEST;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
newDir = DIRECTION_EAST;
|
||||
}
|
||||
} else if (dx < dy) {
|
||||
}
|
||||
else if (dx < dy) {
|
||||
//look NORTH/SOUTH
|
||||
if (offsety < 0) {
|
||||
newDir = DIRECTION_NORTH;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
newDir = DIRECTION_SOUTH;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
Direction dir = getDirection();
|
||||
if (offsetx < 0 && offsety < 0) {
|
||||
if (offsetx == -1 && offsety == -1) {
|
||||
if (dir == DIRECTION_NORTH) {
|
||||
newDir = DIRECTION_WEST;
|
||||
}
|
||||
}
|
||||
if (dir == DIRECTION_SOUTH) {
|
||||
newDir = DIRECTION_WEST;
|
||||
} else if (dir == DIRECTION_NORTH) {
|
||||
newDir = DIRECTION_WEST;
|
||||
} else if (dir == DIRECTION_EAST) {
|
||||
}
|
||||
else if (dir == DIRECTION_EAST) {
|
||||
newDir = DIRECTION_NORTH;
|
||||
}
|
||||
} else if (offsetx < 0 && offsety > 0) {
|
||||
}
|
||||
else if (offsetx < 0 && offsety > 0) {
|
||||
if (offsetx == -1 && offsety == 1) {
|
||||
if (dir == DIRECTION_SOUTH) {
|
||||
newDir = DIRECTION_WEST;
|
||||
}
|
||||
}
|
||||
if (dir == DIRECTION_NORTH) {
|
||||
newDir = DIRECTION_WEST;
|
||||
} else if (dir == DIRECTION_SOUTH) {
|
||||
newDir = DIRECTION_WEST;
|
||||
} else if (dir == DIRECTION_EAST) {
|
||||
}
|
||||
else if (dir == DIRECTION_EAST) {
|
||||
newDir = DIRECTION_SOUTH;
|
||||
}
|
||||
} else if (offsetx > 0 && offsety < 0) {
|
||||
}
|
||||
else if (offsetx > 0 && offsety < 0) {
|
||||
if (offsetx == 1 && offsety == -1) {
|
||||
if (dir == DIRECTION_NORTH) {
|
||||
newDir = DIRECTION_EAST;
|
||||
}
|
||||
}
|
||||
if (dir == DIRECTION_SOUTH) {
|
||||
newDir = DIRECTION_EAST;
|
||||
} else if (dir == DIRECTION_NORTH) {
|
||||
newDir = DIRECTION_EAST;
|
||||
} else if (dir == DIRECTION_WEST) {
|
||||
}
|
||||
else if (dir == DIRECTION_WEST) {
|
||||
newDir = DIRECTION_NORTH;
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (offsetx == 1 && offsety == 1) {
|
||||
if (dir == DIRECTION_SOUTH) {
|
||||
newDir = DIRECTION_EAST;
|
||||
}
|
||||
}
|
||||
if (dir == DIRECTION_NORTH) {
|
||||
newDir = DIRECTION_EAST;
|
||||
} else if (dir == DIRECTION_SOUTH) {
|
||||
newDir = DIRECTION_EAST;
|
||||
} else if (dir == DIRECTION_WEST) {
|
||||
}
|
||||
else if (dir == DIRECTION_WEST) {
|
||||
newDir = DIRECTION_SOUTH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_game.internalCreatureTurn(this, newDir);
|
||||
if (direction != newDir) {
|
||||
g_game.internalCreatureTurn(this, newDir);
|
||||
}
|
||||
}
|
||||
|
||||
void Monster::dropLoot(Container* corpse, Creature*)
|
||||
{
|
||||
if (corpse && lootDrop) {
|
||||
g_events->eventMonsterOnDropLoot(this, corpse);
|
||||
mType->createLoot(corpse);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1921,12 +2084,6 @@ void Monster::setNormalCreatureLight()
|
||||
void Monster::drainHealth(Creature* attacker, int32_t damage)
|
||||
{
|
||||
Creature::drainHealth(attacker, damage);
|
||||
|
||||
if (damage > 0 && randomStepping) {
|
||||
ignoreFieldDamage = true;
|
||||
updateMapCache();
|
||||
}
|
||||
|
||||
if (isInvisible()) {
|
||||
removeCondition(CONDITION_INVISIBLE);
|
||||
}
|
||||
@ -1947,12 +2104,70 @@ bool Monster::challengeCreature(Creature* creature)
|
||||
|
||||
bool result = selectTarget(creature);
|
||||
if (result) {
|
||||
targetChangeCooldown = 8000;
|
||||
targetChangeCooldown = 1000;
|
||||
targetChangeTicks = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Monster::convinceCreature(Creature* creature)
|
||||
{
|
||||
Player* player = creature->getPlayer();
|
||||
if (player && !player->hasFlag(PlayerFlag_CanConvinceAll)) {
|
||||
if (!mType->info.isConvinceable) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (isSummon()) {
|
||||
if (getMaster() == creature) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Creature* oldMaster = getMaster();
|
||||
oldMaster->removeSummon(this);
|
||||
}
|
||||
|
||||
creature->addSummon(this);
|
||||
|
||||
setFollowCreature(nullptr);
|
||||
setAttackedCreature(nullptr);
|
||||
|
||||
//destroy summons
|
||||
for (Creature* summon : summons) {
|
||||
summon->changeHealth(-summon->getHealth());
|
||||
summon->setMaster(nullptr);
|
||||
summon->decrementReferenceCounter();
|
||||
}
|
||||
summons.clear();
|
||||
|
||||
isMasterInRange = true;
|
||||
updateTargetList();
|
||||
updateIdleStatus();
|
||||
|
||||
//Notify surrounding about the change
|
||||
SpectatorVec list;
|
||||
g_game.map.getSpectators(list, getPosition(), true);
|
||||
g_game.map.getSpectators(list, creature->getPosition(), true);
|
||||
for (Creature* spectator : list) {
|
||||
spectator->onCreatureConvinced(creature, this);
|
||||
}
|
||||
|
||||
if (spawn) {
|
||||
spawn->removeMonster(this);
|
||||
spawn = nullptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Monster::onCreatureConvinced(const Creature* convincer, const Creature* creature)
|
||||
{
|
||||
if (convincer != this && (isFriend(creature) || isOpponent(creature))) {
|
||||
updateTargetList();
|
||||
updateIdleStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void Monster::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
|
||||
{
|
||||
Creature::getPathSearchParams(creature, fpp);
|
||||
|
146
src/monster.h
146
src/monster.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,15 +26,17 @@
|
||||
class Creature;
|
||||
class Game;
|
||||
class Spawn;
|
||||
class Combat;
|
||||
|
||||
using CreatureHashSet = std::unordered_set<Creature*>;
|
||||
using CreatureList = std::list<Creature*>;
|
||||
typedef std::unordered_set<Creature*> CreatureHashSet;
|
||||
typedef std::list<Creature*> CreatureList;
|
||||
|
||||
enum TargetSearchType_t {
|
||||
TARGETSEARCH_DEFAULT,
|
||||
TARGETSEARCH_ANY,
|
||||
TARGETSEARCH_RANDOM,
|
||||
TARGETSEARCH_ATTACKRANGE,
|
||||
TARGETSEARCH_NEAREST,
|
||||
TARGETSEARCH_WEAKEST,
|
||||
TARGETSEARCH_MOSTDAMAGE,
|
||||
};
|
||||
|
||||
class Monster final : public Creature
|
||||
@ -44,43 +46,39 @@ class Monster final : public Creature
|
||||
static int32_t despawnRange;
|
||||
static int32_t despawnRadius;
|
||||
|
||||
explicit Monster(MonsterType* mType);
|
||||
explicit Monster(MonsterType* mtype);
|
||||
~Monster();
|
||||
|
||||
// non-copyable
|
||||
Monster(const Monster&) = delete;
|
||||
Monster& operator=(const Monster&) = delete;
|
||||
|
||||
Monster* getMonster() override {
|
||||
Monster* getMonster() final {
|
||||
return this;
|
||||
}
|
||||
const Monster* getMonster() const override {
|
||||
const Monster* getMonster() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
void setID() override {
|
||||
void setID() final {
|
||||
if (id == 0) {
|
||||
id = monsterAutoID++;
|
||||
}
|
||||
}
|
||||
|
||||
void removeList() override;
|
||||
void addList() override;
|
||||
void removeList() final;
|
||||
void addList() final;
|
||||
|
||||
const std::string& getName() const override {
|
||||
const std::string& getName() const final {
|
||||
return mType->name;
|
||||
}
|
||||
const std::string& getNameDescription() const override {
|
||||
const std::string& getNameDescription() const final {
|
||||
return mType->nameDescription;
|
||||
}
|
||||
std::string getDescription(int32_t) const override {
|
||||
std::string getDescription(int32_t) const final {
|
||||
return strDescription + '.';
|
||||
}
|
||||
|
||||
CreatureType_t getType() const override {
|
||||
return CREATURETYPE_MONSTER;
|
||||
}
|
||||
|
||||
const Position& getMasterPos() const {
|
||||
return masterPos;
|
||||
}
|
||||
@ -88,19 +86,22 @@ class Monster final : public Creature
|
||||
masterPos = pos;
|
||||
}
|
||||
|
||||
RaceType_t getRace() const override {
|
||||
RaceType_t getRace() const final {
|
||||
return mType->info.race;
|
||||
}
|
||||
int32_t getArmor() const override {
|
||||
return mType->info.armor;
|
||||
int32_t getArmor() const final {
|
||||
int32_t armor = mType->info.armor;
|
||||
if (armor > 1) {
|
||||
return rand() % (mType->info.armor >> 1) + (mType->info.armor >> 1);
|
||||
}
|
||||
|
||||
return armor;
|
||||
}
|
||||
int32_t getDefense() const override {
|
||||
return mType->info.defense;
|
||||
}
|
||||
bool isPushable() const override {
|
||||
int32_t getDefense() final;
|
||||
bool isPushable() const final {
|
||||
return mType->info.pushable && baseSpeed != 0;
|
||||
}
|
||||
bool isAttackable() const override {
|
||||
bool isAttackable() const final {
|
||||
return mType->info.isAttackable;
|
||||
}
|
||||
|
||||
@ -113,8 +114,8 @@ class Monster final : public Creature
|
||||
bool isHostile() const {
|
||||
return mType->info.isHostile;
|
||||
}
|
||||
bool canSee(const Position& pos) const override;
|
||||
bool canSeeInvisibility() const override {
|
||||
bool canSee(const Position& pos) const final;
|
||||
bool canSeeInvisibility() const final {
|
||||
return isImmune(CONDITION_INVISIBLE);
|
||||
}
|
||||
uint32_t getManaCost() const {
|
||||
@ -123,35 +124,31 @@ class Monster final : public Creature
|
||||
void setSpawn(Spawn* spawn) {
|
||||
this->spawn = spawn;
|
||||
}
|
||||
bool canWalkOnFieldType(CombatType_t combatType) const;
|
||||
|
||||
void onAttackedCreature(Creature* creature) final;
|
||||
|
||||
void onAttackedCreatureDisappear(bool isLogout) override;
|
||||
void onCreatureAppear(Creature* creature, bool isLogin) final;
|
||||
void onRemoveCreature(Creature* creature, bool isLogout) final;
|
||||
void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) final;
|
||||
void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) final;
|
||||
|
||||
void onCreatureAppear(Creature* creature, bool isLogin) override;
|
||||
void onRemoveCreature(Creature* creature, bool isLogout) override;
|
||||
void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos, const Tile* oldTile, const Position& oldPos, bool teleport) override;
|
||||
void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override;
|
||||
void drainHealth(Creature* attacker, int32_t damage) final;
|
||||
void changeHealth(int32_t healthChange, bool sendHealthChange = true) final;
|
||||
void onWalk() final;
|
||||
bool getNextStep(Direction& direction, uint32_t& flags) final;
|
||||
void onFollowCreatureComplete(const Creature* creature) final;
|
||||
|
||||
void drainHealth(Creature* attacker, int32_t damage) override;
|
||||
void changeHealth(int32_t healthChange, bool sendHealthChange = true) override;
|
||||
void onWalk() override;
|
||||
bool getNextStep(Direction& direction, uint32_t& flags) override;
|
||||
void onFollowCreatureComplete(const Creature* creature) override;
|
||||
void onThink(uint32_t interval) final;
|
||||
|
||||
void onThink(uint32_t interval) override;
|
||||
bool challengeCreature(Creature* creature) final;
|
||||
bool convinceCreature(Creature* creature) final;
|
||||
|
||||
bool challengeCreature(Creature* creature) override;
|
||||
void setNormalCreatureLight() final;
|
||||
bool getCombatValues(int32_t& min, int32_t& max) final;
|
||||
|
||||
void setNormalCreatureLight() override;
|
||||
bool getCombatValues(int32_t& min, int32_t& max) override;
|
||||
void doAttacking(uint32_t interval) final;
|
||||
|
||||
void doAttacking(uint32_t interval) override;
|
||||
bool hasExtraSwing() override {
|
||||
return extraMeleeAttack;
|
||||
}
|
||||
|
||||
bool searchTarget(TargetSearchType_t searchType = TARGETSEARCH_DEFAULT);
|
||||
bool searchTarget(TargetSearchType_t searchType);
|
||||
bool selectTarget(Creature* creature);
|
||||
|
||||
const CreatureList& getTargetList() const {
|
||||
@ -170,12 +167,9 @@ class Monster final : public Creature
|
||||
bool isTargetNearby() const {
|
||||
return stepDuration >= 1;
|
||||
}
|
||||
bool isIgnoringFieldDamage() const {
|
||||
return ignoreFieldDamage;
|
||||
}
|
||||
|
||||
BlockType_t blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
|
||||
bool checkDefense = false, bool checkArmor = false, bool field = false) override;
|
||||
bool checkDefense = false, bool checkArmor = false, bool field = false);
|
||||
|
||||
static uint32_t monsterAutoID;
|
||||
|
||||
@ -188,13 +182,13 @@ class Monster final : public Creature
|
||||
MonsterType* mType;
|
||||
Spawn* spawn = nullptr;
|
||||
|
||||
int64_t lastMeleeAttack = 0;
|
||||
int64_t lifetime = 0;
|
||||
int64_t nextDanceStepRound = 0;
|
||||
int64_t earliestAttackTime = 0;
|
||||
int64_t earliestWakeUpTime = 0;
|
||||
int64_t earliestDanceTime = 0;
|
||||
|
||||
uint32_t attackTicks = 0;
|
||||
uint32_t targetTicks = 0;
|
||||
uint32_t targetChangeTicks = 0;
|
||||
uint32_t defenseTicks = 0;
|
||||
uint32_t yellTicks = 0;
|
||||
int32_t minCombatValue = 0;
|
||||
int32_t maxCombatValue = 0;
|
||||
int32_t targetChangeCooldown = 0;
|
||||
@ -203,10 +197,8 @@ class Monster final : public Creature
|
||||
Position masterPos;
|
||||
|
||||
bool isIdle = true;
|
||||
bool extraMeleeAttack = false;
|
||||
bool isMasterInRange = false;
|
||||
bool randomStepping = false;
|
||||
bool ignoreFieldDamage = false;
|
||||
bool egibleToDance = true;
|
||||
|
||||
void onCreatureEnter(Creature* creature);
|
||||
void onCreatureLeave(Creature* creature);
|
||||
@ -223,8 +215,8 @@ class Monster final : public Creature
|
||||
void clearTargetList();
|
||||
void clearFriendList();
|
||||
|
||||
void death(Creature* lastHitCreature) override;
|
||||
Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) override;
|
||||
void death(Creature* lastHitCreature) final;
|
||||
Item* getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature) final;
|
||||
|
||||
void setIdle(bool idle);
|
||||
void updateIdleStatus();
|
||||
@ -232,12 +224,11 @@ class Monster final : public Creature
|
||||
return isIdle;
|
||||
}
|
||||
|
||||
void onAddCondition(ConditionType_t type) override;
|
||||
void onEndCondition(ConditionType_t type) override;
|
||||
void onAddCondition(ConditionType_t type) final;
|
||||
void onEndCondition(ConditionType_t type) final;
|
||||
void onCreatureConvinced(const Creature* convincer, const Creature* creature) final;
|
||||
|
||||
bool canUseAttack(const Position& pos, const Creature* target) const;
|
||||
bool canUseSpell(const Position& pos, const Position& targetPos,
|
||||
const spellBlock_t& sb, uint32_t interval, bool& inRange, bool& resetTicks);
|
||||
bool getRandomStep(const Position& creaturePos, Direction& direction) const;
|
||||
bool getDanceStep(const Position& creaturePos, Direction& direction,
|
||||
bool keepAttack = true, bool keepDistance = true);
|
||||
@ -256,25 +247,28 @@ class Monster final : public Creature
|
||||
bool isFriend(const Creature* creature) const;
|
||||
bool isOpponent(const Creature* creature) const;
|
||||
|
||||
uint64_t getLostExperience() const override {
|
||||
uint64_t getLostExperience() const final {
|
||||
return skillLoss ? mType->info.experience : 0;
|
||||
}
|
||||
uint16_t getLookCorpse() const override {
|
||||
uint16_t getLookCorpse() const final {
|
||||
return mType->info.lookcorpse;
|
||||
}
|
||||
void dropLoot(Container* corpse, Creature* lastHitCreature) override;
|
||||
uint32_t getDamageImmunities() const override {
|
||||
void dropLoot(Container* corpse, Creature* lastHitCreature) final;
|
||||
uint32_t getDamageImmunities() const final {
|
||||
return mType->info.damageImmunities;
|
||||
}
|
||||
uint32_t getConditionImmunities() const override {
|
||||
uint32_t getConditionImmunities() const final {
|
||||
return mType->info.conditionImmunities;
|
||||
}
|
||||
void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const override;
|
||||
bool useCacheMap() const override {
|
||||
return !randomStepping;
|
||||
void getPathSearchParams(const Creature* creature, FindPathParams& fpp) const final;
|
||||
bool useCacheMap() const final {
|
||||
return true;
|
||||
}
|
||||
|
||||
friend class LuaScriptInterface;
|
||||
friend class AreaSpawnEvent;
|
||||
friend class Combat;
|
||||
friend class Creature;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
830
src/monsters.cpp
830
src/monsters.cpp
File diff suppressed because it is too large
Load Diff
112
src/monsters.h
112
src/monsters.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -22,9 +22,8 @@
|
||||
|
||||
#include "creature.h"
|
||||
|
||||
|
||||
const uint32_t MAX_LOOTCHANCE = 100000;
|
||||
const uint32_t MAX_STATICWALK = 100;
|
||||
#define MAX_LOOTCHANCE 1000
|
||||
#define MAX_STATICWALK 100
|
||||
|
||||
struct LootBlock {
|
||||
uint16_t id;
|
||||
@ -39,7 +38,7 @@ struct LootBlock {
|
||||
std::vector<LootBlock> childLoot;
|
||||
LootBlock() {
|
||||
id = 0;
|
||||
countmax = 1;
|
||||
countmax = 0;
|
||||
chance = 0;
|
||||
|
||||
subType = -1;
|
||||
@ -47,21 +46,9 @@ struct LootBlock {
|
||||
}
|
||||
};
|
||||
|
||||
class Loot {
|
||||
public:
|
||||
Loot() = default;
|
||||
|
||||
// non-copyable
|
||||
Loot(const Loot&) = delete;
|
||||
Loot& operator=(const Loot&) = delete;
|
||||
|
||||
LootBlock lootBlock;
|
||||
};
|
||||
|
||||
struct summonBlock_t {
|
||||
std::string name;
|
||||
uint32_t chance;
|
||||
uint32_t speed;
|
||||
uint32_t max;
|
||||
bool force = false;
|
||||
};
|
||||
@ -75,23 +62,23 @@ struct spellBlock_t {
|
||||
spellBlock_t(spellBlock_t&& other) :
|
||||
spell(other.spell),
|
||||
chance(other.chance),
|
||||
speed(other.speed),
|
||||
range(other.range),
|
||||
minCombatValue(other.minCombatValue),
|
||||
maxCombatValue(other.maxCombatValue),
|
||||
combatSpell(other.combatSpell),
|
||||
isMelee(other.isMelee) {
|
||||
attack(other.attack),
|
||||
skill(other.skill),
|
||||
combatSpell(other.combatSpell) {
|
||||
other.spell = nullptr;
|
||||
}
|
||||
|
||||
BaseSpell* spell = nullptr;
|
||||
uint32_t chance = 100;
|
||||
uint32_t speed = 2000;
|
||||
uint32_t range = 0;
|
||||
int32_t minCombatValue = 0;
|
||||
int32_t maxCombatValue = 0;
|
||||
int32_t attack = 0;
|
||||
int32_t skill = 0;
|
||||
bool combatSpell = false;
|
||||
bool isMelee = false;
|
||||
};
|
||||
|
||||
struct voiceBlock_t {
|
||||
@ -124,14 +111,11 @@ class MonsterType
|
||||
uint64_t experience = 0;
|
||||
|
||||
uint32_t manaCost = 0;
|
||||
uint32_t yellChance = 0;
|
||||
uint32_t yellSpeedTicks = 0;
|
||||
uint32_t staticAttackChance = 95;
|
||||
uint32_t maxSummons = 0;
|
||||
uint32_t changeTargetSpeed = 0;
|
||||
uint32_t conditionImmunities = 0;
|
||||
uint32_t damageImmunities = 0;
|
||||
uint32_t baseSpeed = 200;
|
||||
uint32_t baseSpeed = 70;
|
||||
|
||||
int32_t creatureAppearEvent = -1;
|
||||
int32_t creatureDisappearEvent = -1;
|
||||
@ -143,8 +127,15 @@ class MonsterType
|
||||
int32_t health = 100;
|
||||
int32_t healthMax = 100;
|
||||
int32_t changeTargetChance = 0;
|
||||
int32_t defense = 0;
|
||||
int32_t strategyNearestEnemy = 0;
|
||||
int32_t strategyWeakestEnemy = 0;
|
||||
int32_t strategyMostDamageEnemy = 0;
|
||||
int32_t strategyRandomEnemy = 0;
|
||||
int32_t armor = 0;
|
||||
int32_t defense = 0;
|
||||
int32_t attack = 0;
|
||||
int32_t skill = 0;
|
||||
int32_t poison = 0;
|
||||
|
||||
bool canPushItems = false;
|
||||
bool canPushCreatures = false;
|
||||
@ -155,11 +146,6 @@ class MonsterType
|
||||
bool isAttackable = true;
|
||||
bool isHostile = true;
|
||||
bool hiddenHealth = false;
|
||||
bool canWalkOnEnergy = true;
|
||||
bool canWalkOnFire = true;
|
||||
bool canWalkOnPoison = true;
|
||||
|
||||
MonstersEvent_t eventType = MONSTERS_EVENT_NONE;
|
||||
};
|
||||
|
||||
public:
|
||||
@ -169,57 +155,14 @@ class MonsterType
|
||||
MonsterType(const MonsterType&) = delete;
|
||||
MonsterType& operator=(const MonsterType&) = delete;
|
||||
|
||||
bool loadCallback(LuaScriptInterface* scriptInterface);
|
||||
|
||||
std::string name;
|
||||
std::string nameDescription;
|
||||
|
||||
MonsterInfo info;
|
||||
|
||||
void loadLoot(MonsterType* monsterType, LootBlock lootblock);
|
||||
};
|
||||
|
||||
class MonsterSpell
|
||||
{
|
||||
public:
|
||||
MonsterSpell() = default;
|
||||
|
||||
MonsterSpell(const MonsterSpell&) = delete;
|
||||
MonsterSpell& operator=(const MonsterSpell&) = delete;
|
||||
|
||||
std::string name = "";
|
||||
std::string scriptName = "";
|
||||
|
||||
uint8_t chance = 100;
|
||||
uint8_t range = 0;
|
||||
|
||||
uint16_t interval = 2000;
|
||||
|
||||
int32_t minCombatValue = 0;
|
||||
int32_t maxCombatValue = 0;
|
||||
int32_t attack = 0;
|
||||
int32_t skill = 0;
|
||||
int32_t length = 0;
|
||||
int32_t spread = 0;
|
||||
int32_t radius = 0;
|
||||
int32_t conditionMinDamage = 0;
|
||||
int32_t conditionMaxDamage = 0;
|
||||
int32_t conditionStartDamage = 0;
|
||||
int32_t tickInterval = 0;
|
||||
int32_t speedChange = 0;
|
||||
int32_t duration = 0;
|
||||
|
||||
bool isScripted = false;
|
||||
bool needTarget = false;
|
||||
bool needDirection = false;
|
||||
bool combatSpell = false;
|
||||
bool isMelee = false;
|
||||
|
||||
Outfit_t outfit = {};
|
||||
ShootType_t shoot = CONST_ANI_NONE;
|
||||
MagicEffectClasses effect = CONST_ME_NONE;
|
||||
ConditionType_t conditionType = CONDITION_NONE;
|
||||
CombatType_t combatType = COMBAT_UNDEFINEDDAMAGE;
|
||||
void createLoot(Container* corpse);
|
||||
bool createLootContainer(Container* parent, const LootBlock& lootblock);
|
||||
std::vector<Item*> createLootItem(const LootBlock& lootBlock);
|
||||
};
|
||||
|
||||
class Monsters
|
||||
@ -237,23 +180,20 @@ class Monsters
|
||||
bool reload();
|
||||
|
||||
MonsterType* getMonsterType(const std::string& name);
|
||||
void addMonsterType(const std::string& name, MonsterType* mType);
|
||||
bool deserializeSpell(MonsterSpell* spell, spellBlock_t& sb, const std::string& description = "");
|
||||
|
||||
std::unique_ptr<LuaScriptInterface> scriptInterface;
|
||||
static uint32_t getLootRandom();
|
||||
|
||||
private:
|
||||
ConditionDamage* getDamageCondition(ConditionType_t conditionType,
|
||||
int32_t maxDamage, int32_t minDamage, int32_t startDamage, uint32_t tickInterval);
|
||||
ConditionDamage* getDamageCondition(ConditionType_t conditionType, int32_t cycles, int32_t count, int32_t max_count);
|
||||
bool deserializeSpell(const pugi::xml_node& node, spellBlock_t& sb, const std::string& description = "");
|
||||
|
||||
MonsterType* loadMonster(const std::string& file, const std::string& monsterName, bool reloading = false);
|
||||
bool loadMonster(const std::string& file, const std::string& monsterName, std::list<std::pair<MonsterType*, std::string>>& monsterScriptList, bool reloading = false);
|
||||
|
||||
void loadLootContainer(const pugi::xml_node& node, LootBlock&);
|
||||
bool loadLootItem(const pugi::xml_node& node, LootBlock&);
|
||||
|
||||
std::map<std::string, MonsterType> monsters;
|
||||
std::map<std::string, std::string> unloadedMonsters;
|
||||
std::unique_ptr<LuaScriptInterface> scriptInterface;
|
||||
|
||||
bool loaded = false;
|
||||
};
|
||||
|
@ -1,82 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "otpch.h"
|
||||
|
||||
#include "mounts.h"
|
||||
|
||||
#include "pugicast.h"
|
||||
#include "tools.h"
|
||||
|
||||
bool Mounts::reload()
|
||||
{
|
||||
mounts.clear();
|
||||
return loadFromXml();
|
||||
}
|
||||
|
||||
bool Mounts::loadFromXml()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file("data/XML/mounts.xml");
|
||||
if (!result) {
|
||||
printXMLError("Error - Mounts::loadFromXml", "data/XML/mounts.xml", result);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto mountNode : doc.child("mounts").children()) {
|
||||
mounts.emplace_back(
|
||||
static_cast<uint8_t>(pugi::cast<uint16_t>(mountNode.attribute("id").value())),
|
||||
pugi::cast<uint16_t>(mountNode.attribute("clientid").value()),
|
||||
mountNode.attribute("name").as_string(),
|
||||
pugi::cast<int32_t>(mountNode.attribute("speed").value()),
|
||||
mountNode.attribute("premium").as_bool()
|
||||
);
|
||||
}
|
||||
mounts.shrink_to_fit();
|
||||
return true;
|
||||
}
|
||||
|
||||
Mount* Mounts::getMountByID(uint8_t id)
|
||||
{
|
||||
auto it = std::find_if(mounts.begin(), mounts.end(), [id](const Mount& mount) {
|
||||
return mount.id == id;
|
||||
});
|
||||
|
||||
return it != mounts.end() ? &*it : nullptr;
|
||||
}
|
||||
|
||||
Mount* Mounts::getMountByName(const std::string& name) {
|
||||
auto mountName = name.c_str();
|
||||
for (auto& it : mounts) {
|
||||
if (strcasecmp(mountName, it.name.c_str()) == 0) {
|
||||
return ⁢
|
||||
}
|
||||
}
|
||||
|
||||
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
52
src/mounts.h
@ -1,52 +0,0 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#ifndef FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6
|
||||
#define FS_MOUNTS_H_73716D11906A4C5C9F4A7B68D34C9BA6
|
||||
|
||||
struct Mount
|
||||
{
|
||||
Mount(uint8_t id, uint16_t clientId, std::string name, int32_t speed, bool premium) :
|
||||
name(std::move(name)), speed(speed), clientId(clientId), id(id), premium(premium) {}
|
||||
|
||||
std::string name;
|
||||
int32_t speed;
|
||||
uint16_t clientId;
|
||||
uint8_t id;
|
||||
bool premium;
|
||||
};
|
||||
|
||||
class Mounts
|
||||
{
|
||||
public:
|
||||
bool reload();
|
||||
bool loadFromXml();
|
||||
Mount* getMountByID(uint8_t id);
|
||||
Mount* getMountByName(const std::string& name);
|
||||
Mount* getMountByClientID(uint16_t clientId);
|
||||
|
||||
const std::vector<Mount>& getMounts() const {
|
||||
return mounts;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<Mount> mounts;
|
||||
};
|
||||
|
||||
#endif
|
381
src/movement.cpp
381
src/movement.cpp
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -36,49 +36,43 @@ MoveEvents::MoveEvents() :
|
||||
|
||||
MoveEvents::~MoveEvents()
|
||||
{
|
||||
clear(false);
|
||||
clear();
|
||||
}
|
||||
|
||||
void MoveEvents::clearMap(MoveListMap& map, bool fromLua)
|
||||
void MoveEvents::clearMap(MoveListMap& map)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
for (int eventType = MOVE_EVENT_STEP_IN; eventType < MOVE_EVENT_LAST; ++eventType) {
|
||||
auto& moveEvents = it->second.moveEvent[eventType];
|
||||
for (auto find = moveEvents.begin(); find != moveEvents.end(); ) {
|
||||
if (fromLua == find->fromLua) {
|
||||
find = moveEvents.erase(find);
|
||||
} else {
|
||||
++find;
|
||||
}
|
||||
std::unordered_set<MoveEvent*> set;
|
||||
for (const auto& it : map) {
|
||||
const MoveEventList& moveEventList = it.second;
|
||||
for (const auto& i : moveEventList.moveEvent) {
|
||||
for (MoveEvent* moveEvent : i) {
|
||||
set.insert(moveEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
map.clear();
|
||||
|
||||
void MoveEvents::clearPosMap(MovePosListMap& map, bool fromLua)
|
||||
{
|
||||
for (auto it = map.begin(); it != map.end(); ++it) {
|
||||
for (int eventType = MOVE_EVENT_STEP_IN; eventType < MOVE_EVENT_LAST; ++eventType) {
|
||||
auto& moveEvents = it->second.moveEvent[eventType];
|
||||
for (auto find = moveEvents.begin(); find != moveEvents.end(); ) {
|
||||
if (fromLua == find->fromLua) {
|
||||
find = moveEvents.erase(find);
|
||||
} else {
|
||||
++find;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (MoveEvent* moveEvent : set) {
|
||||
delete moveEvent;
|
||||
}
|
||||
}
|
||||
|
||||
void MoveEvents::clear(bool fromLua)
|
||||
void MoveEvents::clear()
|
||||
{
|
||||
clearMap(itemIdMap, fromLua);
|
||||
clearMap(actionIdMap, fromLua);
|
||||
clearMap(uniqueIdMap, fromLua);
|
||||
clearPosMap(positionMap, fromLua);
|
||||
clearMap(itemIdMap);
|
||||
clearMap(movementIdMap);
|
||||
|
||||
reInitState(fromLua);
|
||||
for (const auto& it : positionMap) {
|
||||
const MoveEventList& moveEventList = it.second;
|
||||
for (const auto& i : moveEventList.moveEvent) {
|
||||
for (MoveEvent* moveEvent : i) {
|
||||
delete moveEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
positionMap.clear();
|
||||
|
||||
scriptInterface.reInitState();
|
||||
}
|
||||
|
||||
LuaScriptInterface& MoveEvents::getScriptInterface()
|
||||
@ -91,17 +85,17 @@ std::string MoveEvents::getScriptBaseName() const
|
||||
return "movements";
|
||||
}
|
||||
|
||||
Event_ptr MoveEvents::getEvent(const std::string& nodeName)
|
||||
Event* MoveEvents::getEvent(const std::string& nodeName)
|
||||
{
|
||||
if (strcasecmp(nodeName.c_str(), "movevent") != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
return Event_ptr(new MoveEvent(&scriptInterface));
|
||||
return new MoveEvent(&scriptInterface);
|
||||
}
|
||||
|
||||
bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
bool MoveEvents::registerEvent(Event* event, const pugi::xml_node& node)
|
||||
{
|
||||
MoveEvent_ptr moveEvent{static_cast<MoveEvent*>(event.release())}; //event is guaranteed to be a MoveEvent
|
||||
MoveEvent* moveEvent = static_cast<MoveEvent*>(event); //event is guaranteed to be a MoveEvent
|
||||
|
||||
const MoveEvent_t eventType = moveEvent->getEventType();
|
||||
if (eventType == MOVE_EVENT_ADD_ITEM || eventType == MOVE_EVENT_REMOVE_ITEM) {
|
||||
@ -123,6 +117,7 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
pugi::xml_attribute attr;
|
||||
if ((attr = node.attribute("itemid"))) {
|
||||
int32_t id = pugi::cast<int32_t>(attr.value());
|
||||
addEvent(moveEvent, id, itemIdMap);
|
||||
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
|
||||
ItemType& it = Item::items.getItemType(id);
|
||||
it.wieldInfo = moveEvent->getWieldInfo();
|
||||
@ -130,12 +125,11 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
it.minReqMagicLevel = moveEvent->getReqMagLv();
|
||||
it.vocationString = moveEvent->getVocationString();
|
||||
}
|
||||
addEvent(std::move(*moveEvent), id, itemIdMap);
|
||||
} else if ((attr = node.attribute("fromid"))) {
|
||||
uint32_t id = pugi::cast<uint32_t>(attr.value());
|
||||
uint32_t endId = pugi::cast<uint32_t>(node.attribute("toid").value());
|
||||
|
||||
addEvent(*moveEvent, id, itemIdMap);
|
||||
addEvent(moveEvent, id, itemIdMap);
|
||||
|
||||
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
|
||||
ItemType& it = Item::items.getItemType(id);
|
||||
@ -145,7 +139,7 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
it.vocationString = moveEvent->getVocationString();
|
||||
|
||||
while (++id <= endId) {
|
||||
addEvent(*moveEvent, id, itemIdMap);
|
||||
addEvent(moveEvent, id, itemIdMap);
|
||||
|
||||
ItemType& tit = Item::items.getItemType(id);
|
||||
tit.wieldInfo = moveEvent->getWieldInfo();
|
||||
@ -155,26 +149,17 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
}
|
||||
} else {
|
||||
while (++id <= endId) {
|
||||
addEvent(*moveEvent, id, itemIdMap);
|
||||
addEvent(moveEvent, id, itemIdMap);
|
||||
}
|
||||
}
|
||||
} else if ((attr = node.attribute("uniqueid"))) {
|
||||
addEvent(std::move(*moveEvent), pugi::cast<int32_t>(attr.value()), uniqueIdMap);
|
||||
} else if ((attr = node.attribute("fromuid"))) {
|
||||
} else if ((attr = node.attribute("movementid"))) {
|
||||
addEvent(moveEvent, pugi::cast<int32_t>(attr.value()), movementIdMap);
|
||||
} else if ((attr = node.attribute("frommovementid"))) {
|
||||
uint32_t id = pugi::cast<uint32_t>(attr.value());
|
||||
uint32_t endId = pugi::cast<uint32_t>(node.attribute("touid").value());
|
||||
addEvent(*moveEvent, id, uniqueIdMap);
|
||||
uint32_t endId = pugi::cast<uint32_t>(node.attribute("tomovementid").value());
|
||||
addEvent(moveEvent, id, movementIdMap);
|
||||
while (++id <= endId) {
|
||||
addEvent(*moveEvent, id, uniqueIdMap);
|
||||
}
|
||||
} else if ((attr = node.attribute("actionid"))) {
|
||||
addEvent(std::move(*moveEvent), pugi::cast<int32_t>(attr.value()), actionIdMap);
|
||||
} else if ((attr = node.attribute("fromaid"))) {
|
||||
uint32_t id = pugi::cast<uint32_t>(attr.value());
|
||||
uint32_t endId = pugi::cast<uint32_t>(node.attribute("toaid").value());
|
||||
addEvent(*moveEvent, id, actionIdMap);
|
||||
while (++id <= endId) {
|
||||
addEvent(*moveEvent, id, actionIdMap);
|
||||
addEvent(moveEvent, id, movementIdMap);
|
||||
}
|
||||
} else if ((attr = node.attribute("pos"))) {
|
||||
std::vector<int32_t> posList = vectorAtoi(explodeString(attr.as_string(), ";"));
|
||||
@ -183,125 +168,28 @@ bool MoveEvents::registerEvent(Event_ptr event, const pugi::xml_node& node)
|
||||
}
|
||||
|
||||
Position pos(posList[0], posList[1], posList[2]);
|
||||
addEvent(std::move(*moveEvent), pos, positionMap);
|
||||
addEvent(moveEvent, pos, positionMap);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoveEvents::registerLuaFunction(MoveEvent* event)
|
||||
{
|
||||
MoveEvent_ptr moveEvent{ event };
|
||||
if (moveEvent->getItemIdRange().size() > 0) {
|
||||
if (moveEvent->getItemIdRange().size() == 1) {
|
||||
uint32_t id = moveEvent->getItemIdRange().at(0);
|
||||
addEvent(*moveEvent, id, itemIdMap);
|
||||
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
|
||||
ItemType& it = Item::items.getItemType(id);
|
||||
it.wieldInfo = moveEvent->getWieldInfo();
|
||||
it.minReqLevel = moveEvent->getReqLevel();
|
||||
it.minReqMagicLevel = moveEvent->getReqMagLv();
|
||||
it.vocationString = moveEvent->getVocationString();
|
||||
}
|
||||
} else {
|
||||
uint32_t iterId = 0;
|
||||
while (++iterId < moveEvent->getItemIdRange().size()) {
|
||||
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
|
||||
ItemType& it = Item::items.getItemType(moveEvent->getItemIdRange().at(iterId));
|
||||
it.wieldInfo = moveEvent->getWieldInfo();
|
||||
it.minReqLevel = moveEvent->getReqLevel();
|
||||
it.minReqMagicLevel = moveEvent->getReqMagLv();
|
||||
it.vocationString = moveEvent->getVocationString();
|
||||
}
|
||||
addEvent(*moveEvent, moveEvent->getItemIdRange().at(iterId), itemIdMap);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoveEvents::registerLuaEvent(MoveEvent* event)
|
||||
{
|
||||
MoveEvent_ptr moveEvent{ event };
|
||||
if (moveEvent->getItemIdRange().size() > 0) {
|
||||
if (moveEvent->getItemIdRange().size() == 1) {
|
||||
uint32_t id = moveEvent->getItemIdRange().at(0);
|
||||
addEvent(*moveEvent, id, itemIdMap);
|
||||
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
|
||||
ItemType& it = Item::items.getItemType(id);
|
||||
it.wieldInfo = moveEvent->getWieldInfo();
|
||||
it.minReqLevel = moveEvent->getReqLevel();
|
||||
it.minReqMagicLevel = moveEvent->getReqMagLv();
|
||||
it.vocationString = moveEvent->getVocationString();
|
||||
}
|
||||
} else {
|
||||
auto v = moveEvent->getItemIdRange();
|
||||
for (auto i = v.begin(); i != v.end(); i++) {
|
||||
if (moveEvent->getEventType() == MOVE_EVENT_EQUIP) {
|
||||
ItemType& it = Item::items.getItemType(*i);
|
||||
it.wieldInfo = moveEvent->getWieldInfo();
|
||||
it.minReqLevel = moveEvent->getReqLevel();
|
||||
it.minReqMagicLevel = moveEvent->getReqMagLv();
|
||||
it.vocationString = moveEvent->getVocationString();
|
||||
}
|
||||
addEvent(*moveEvent, *i, itemIdMap);
|
||||
}
|
||||
}
|
||||
} else if (moveEvent->getActionIdRange().size() > 0) {
|
||||
if (moveEvent->getActionIdRange().size() == 1) {
|
||||
int32_t id = moveEvent->getActionIdRange().at(0);
|
||||
addEvent(*moveEvent, id, actionIdMap);
|
||||
} else {
|
||||
auto v = moveEvent->getActionIdRange();
|
||||
for (auto i = v.begin(); i != v.end(); i++) {
|
||||
addEvent(*moveEvent, *i, actionIdMap);
|
||||
}
|
||||
}
|
||||
} else if (moveEvent->getUniqueIdRange().size() > 0) {
|
||||
if (moveEvent->getUniqueIdRange().size() == 1) {
|
||||
int32_t id = moveEvent->getUniqueIdRange().at(0);
|
||||
addEvent(*moveEvent, id, uniqueIdMap);
|
||||
} else {
|
||||
auto v = moveEvent->getUniqueIdRange();
|
||||
for (auto i = v.begin(); i != v.end(); i++) {
|
||||
addEvent(*moveEvent, *i, uniqueIdMap);
|
||||
}
|
||||
}
|
||||
} else if (moveEvent->getPosList().size() > 0) {
|
||||
if (moveEvent->getPosList().size() == 1) {
|
||||
Position pos = moveEvent->getPosList().at(0);
|
||||
addEvent(*moveEvent, pos, positionMap);
|
||||
} else {
|
||||
auto v = moveEvent->getPosList();
|
||||
for (auto i = v.begin(); i != v.end(); i++) {
|
||||
addEvent(*moveEvent, *i, positionMap);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MoveEvents::addEvent(MoveEvent moveEvent, int32_t id, MoveListMap& map)
|
||||
void MoveEvents::addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map)
|
||||
{
|
||||
auto it = map.find(id);
|
||||
if (it == map.end()) {
|
||||
MoveEventList moveEventList;
|
||||
moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent));
|
||||
moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent);
|
||||
map[id] = moveEventList;
|
||||
} else {
|
||||
std::list<MoveEvent>& moveEventList = it->second.moveEvent[moveEvent.getEventType()];
|
||||
for (MoveEvent& existingMoveEvent : moveEventList) {
|
||||
if (existingMoveEvent.getSlot() == moveEvent.getSlot()) {
|
||||
std::list<MoveEvent*>& moveEventList = it->second.moveEvent[moveEvent->getEventType()];
|
||||
for (MoveEvent* existingMoveEvent : moveEventList) {
|
||||
if (existingMoveEvent->getSlot() == moveEvent->getSlot()) {
|
||||
std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << id << std::endl;
|
||||
}
|
||||
}
|
||||
moveEventList.push_back(std::move(moveEvent));
|
||||
moveEventList.push_back(moveEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,64 +212,59 @@ MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType, slots_t slot)
|
||||
|
||||
auto it = itemIdMap.find(item->getID());
|
||||
if (it != itemIdMap.end()) {
|
||||
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
|
||||
for (MoveEvent& moveEvent : moveEventList) {
|
||||
if ((moveEvent.getSlot() & slotp) != 0) {
|
||||
return &moveEvent;
|
||||
std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
|
||||
for (MoveEvent* moveEvent : moveEventList) {
|
||||
if ((moveEvent->getSlot() & slotp) != 0) {
|
||||
return moveEvent;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType)
|
||||
MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType)
|
||||
{
|
||||
MoveListMap::iterator it;
|
||||
|
||||
if (item->hasAttribute(ITEM_ATTRIBUTE_UNIQUEID)) {
|
||||
it = uniqueIdMap.find(item->getUniqueId());
|
||||
if (it != uniqueIdMap.end()) {
|
||||
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
|
||||
if (item->hasAttribute(ITEM_ATTRIBUTE_MOVEMENTID)) {
|
||||
it = movementIdMap.find(item->getMovementId());
|
||||
if (it != movementIdMap.end()) {
|
||||
std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
|
||||
if (!moveEventList.empty()) {
|
||||
return &(*moveEventList.begin());
|
||||
return *moveEventList.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item->hasAttribute(ITEM_ATTRIBUTE_ACTIONID)) {
|
||||
it = actionIdMap.find(item->getActionId());
|
||||
if (it != actionIdMap.end()) {
|
||||
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
|
||||
if (!moveEventList.empty()) {
|
||||
return &(*moveEventList.begin());
|
||||
}
|
||||
}
|
||||
if (!item->hasCollisionEvent() && !item->hasSeparationEvent()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
it = itemIdMap.find(item->getID());
|
||||
if (it != itemIdMap.end()) {
|
||||
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
|
||||
std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
|
||||
if (!moveEventList.empty()) {
|
||||
return &(*moveEventList.begin());
|
||||
return *moveEventList.begin();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void MoveEvents::addEvent(MoveEvent moveEvent, const Position& pos, MovePosListMap& map)
|
||||
void MoveEvents::addEvent(MoveEvent* moveEvent, const Position& pos, MovePosListMap& map)
|
||||
{
|
||||
auto it = map.find(pos);
|
||||
if (it == map.end()) {
|
||||
MoveEventList moveEventList;
|
||||
moveEventList.moveEvent[moveEvent.getEventType()].push_back(std::move(moveEvent));
|
||||
moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent);
|
||||
map[pos] = moveEventList;
|
||||
} else {
|
||||
std::list<MoveEvent>& moveEventList = it->second.moveEvent[moveEvent.getEventType()];
|
||||
std::list<MoveEvent*>& moveEventList = it->second.moveEvent[moveEvent->getEventType()];
|
||||
if (!moveEventList.empty()) {
|
||||
std::cout << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << pos << std::endl;
|
||||
}
|
||||
|
||||
moveEventList.push_back(std::move(moveEvent));
|
||||
moveEventList.push_back(moveEvent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -389,9 +272,9 @@ MoveEvent* MoveEvents::getEvent(const Tile* tile, MoveEvent_t eventType)
|
||||
{
|
||||
auto it = positionMap.find(tile->getPosition());
|
||||
if (it != positionMap.end()) {
|
||||
std::list<MoveEvent>& moveEventList = it->second.moveEvent[eventType];
|
||||
std::list<MoveEvent*>& moveEventList = it->second.moveEvent[eventType];
|
||||
if (!moveEventList.empty()) {
|
||||
return &(*moveEventList.begin());
|
||||
return *moveEventList.begin();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
@ -442,7 +325,7 @@ uint32_t MoveEvents::onPlayerDeEquip(Player* player, Item* item, slots_t slot)
|
||||
if (!moveEvent) {
|
||||
return 1;
|
||||
}
|
||||
return moveEvent->fireEquip(player, item, slot, false);
|
||||
return moveEvent->fireEquip(player, item, slot, true);
|
||||
}
|
||||
|
||||
uint32_t MoveEvents::onItemMove(Item* item, Tile* tile, bool isAdd)
|
||||
@ -488,6 +371,20 @@ uint32_t MoveEvents::onItemMove(Item* item, Tile* tile, bool isAdd)
|
||||
|
||||
MoveEvent::MoveEvent(LuaScriptInterface* interface) : Event(interface) {}
|
||||
|
||||
MoveEvent::MoveEvent(const MoveEvent* copy) :
|
||||
Event(copy),
|
||||
eventType(copy->eventType),
|
||||
stepFunction(copy->stepFunction),
|
||||
moveFunction(copy->moveFunction),
|
||||
equipFunction(copy->equipFunction),
|
||||
slot(copy->slot),
|
||||
reqLevel(copy->reqLevel),
|
||||
reqMagLevel(copy->reqMagLevel),
|
||||
premium(copy->premium),
|
||||
vocationString(copy->vocationString),
|
||||
wieldInfo(copy->wieldInfo),
|
||||
vocEquipMap(copy->vocEquipMap) {}
|
||||
|
||||
std::string MoveEvent::getScriptEventName() const
|
||||
{
|
||||
switch (eventType) {
|
||||
@ -624,6 +521,40 @@ bool MoveEvent::configureEvent(const pugi::xml_node& node)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MoveEvent::loadFunction(const pugi::xml_attribute& attr)
|
||||
{
|
||||
const char* functionName = attr.as_string();
|
||||
if (strcasecmp(functionName, "onstepinfield") == 0) {
|
||||
stepFunction = StepInField;
|
||||
} else if (strcasecmp(functionName, "onstepoutfield") == 0) {
|
||||
stepFunction = StepOutField;
|
||||
} else if (strcasecmp(functionName, "onaddfield") == 0) {
|
||||
moveFunction = AddItemField;
|
||||
} else if (strcasecmp(functionName, "onremovefield") == 0) {
|
||||
moveFunction = RemoveItemField;
|
||||
} else if (strcasecmp(functionName, "onequipitem") == 0) {
|
||||
equipFunction = EquipItem;
|
||||
} else if (strcasecmp(functionName, "ondeequipitem") == 0) {
|
||||
equipFunction = DeEquipItem;
|
||||
} else {
|
||||
std::cout << "[Warning - MoveEvent::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
scripted = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
MoveEvent_t MoveEvent::getEventType() const
|
||||
{
|
||||
return eventType;
|
||||
}
|
||||
|
||||
void MoveEvent::setEventType(MoveEvent_t type)
|
||||
{
|
||||
eventType = type;
|
||||
}
|
||||
|
||||
uint32_t MoveEvent::StepInField(Creature* creature, Item* item, const Position&)
|
||||
{
|
||||
MagicField* field = item->getMagicField();
|
||||
@ -715,6 +646,10 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item,
|
||||
player->sendIcons();
|
||||
}
|
||||
|
||||
if (it.abilities->absorbPercent[combatTypeToIndex(COMBAT_DROWNDAMAGE)] == 100) {
|
||||
player->removeCondition(CONDITION_DROWN);
|
||||
}
|
||||
|
||||
if (it.abilities->regeneration) {
|
||||
Condition* condition = Condition::createCondition(static_cast<ConditionId_t>(slot), CONDITION_REGENERATION, -1, 0);
|
||||
|
||||
@ -747,13 +682,6 @@ uint32_t MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item,
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) {
|
||||
if (it.abilities->specialSkills[i]) {
|
||||
needUpdateSkills = true;
|
||||
player->setVarSpecialSkill(static_cast<SpecialSkills_t>(i), it.abilities->specialSkills[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (needUpdateSkills) {
|
||||
player->sendSkills();
|
||||
}
|
||||
@ -829,13 +757,6 @@ uint32_t MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots_t
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = SPECIALSKILL_FIRST; i <= SPECIALSKILL_LAST; ++i) {
|
||||
if (it.abilities->specialSkills[i] != 0) {
|
||||
needUpdateSkills = true;
|
||||
player->setVarSpecialSkill(static_cast<SpecialSkills_t>(i), -it.abilities->specialSkills[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (needUpdateSkills) {
|
||||
player->sendSkills();
|
||||
}
|
||||
@ -862,44 +783,6 @@ uint32_t MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots_t
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool MoveEvent::loadFunction(const pugi::xml_attribute& attr, bool isScripted)
|
||||
{
|
||||
const char* functionName = attr.as_string();
|
||||
if (strcasecmp(functionName, "onstepinfield") == 0) {
|
||||
stepFunction = StepInField;
|
||||
} else if (strcasecmp(functionName, "onstepoutfield") == 0) {
|
||||
stepFunction = StepOutField;
|
||||
} else if (strcasecmp(functionName, "onaddfield") == 0) {
|
||||
moveFunction = AddItemField;
|
||||
} else if (strcasecmp(functionName, "onremovefield") == 0) {
|
||||
moveFunction = RemoveItemField;
|
||||
} else if (strcasecmp(functionName, "onequipitem") == 0) {
|
||||
equipFunction = EquipItem;
|
||||
} else if (strcasecmp(functionName, "ondeequipitem") == 0) {
|
||||
equipFunction = DeEquipItem;
|
||||
} else {
|
||||
if (!isScripted) {
|
||||
std::cout << "[Warning - MoveEvent::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isScripted) {
|
||||
scripted = false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
MoveEvent_t MoveEvent::getEventType() const
|
||||
{
|
||||
return eventType;
|
||||
}
|
||||
|
||||
void MoveEvent::setEventType(MoveEvent_t type)
|
||||
{
|
||||
eventType = type;
|
||||
}
|
||||
|
||||
uint32_t MoveEvent::fireStepEvent(Creature* creature, Item* item, const Position& pos)
|
||||
{
|
||||
if (scripted) {
|
||||
@ -933,24 +816,19 @@ bool MoveEvent::executeStep(Creature* creature, Item* item, const Position& pos)
|
||||
return scriptInterface->callFunction(4);
|
||||
}
|
||||
|
||||
uint32_t MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool isCheck)
|
||||
uint32_t MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool boolean)
|
||||
{
|
||||
if (scripted) {
|
||||
if (!equipFunction || equipFunction(this, player, item, slot, isCheck) == 1) {
|
||||
if (executeEquip(player, item, slot, isCheck)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
return executeEquip(player, item, slot);
|
||||
} else {
|
||||
return equipFunction(this, player, item, slot, isCheck);
|
||||
return equipFunction(this, player, item, slot, boolean);
|
||||
}
|
||||
}
|
||||
|
||||
bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool isCheck)
|
||||
bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot)
|
||||
{
|
||||
//onEquip(player, item, slot, isCheck)
|
||||
//onDeEquip(player, item, slot, isCheck)
|
||||
//onEquip(player, item, slot)
|
||||
//onDeEquip(player, item, slot)
|
||||
if (!scriptInterface->reserveScriptEnv()) {
|
||||
std::cout << "[Error - MoveEvent::executeEquip] Call stack overflow" << std::endl;
|
||||
return false;
|
||||
@ -966,9 +844,8 @@ bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool isCh
|
||||
LuaScriptInterface::setMetatable(L, -1, "Player");
|
||||
LuaScriptInterface::pushThing(L, item);
|
||||
lua_pushnumber(L, slot);
|
||||
LuaScriptInterface::pushBoolean(L, isCheck);
|
||||
|
||||
return scriptInterface->callFunction(4);
|
||||
return scriptInterface->callFunction(3);
|
||||
}
|
||||
|
||||
uint32_t MoveEvent::fireAddRemItem(Item* item, Item* tileItem, const Position& pos)
|
||||
|
160
src/movement.h
160
src/movement.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -23,9 +23,6 @@
|
||||
#include "baseevents.h"
|
||||
#include "item.h"
|
||||
#include "luascript.h"
|
||||
#include "vocation.h"
|
||||
|
||||
extern Vocations g_vocations;
|
||||
|
||||
enum MoveEvent_t {
|
||||
MOVE_EVENT_STEP_IN,
|
||||
@ -42,13 +39,12 @@ enum MoveEvent_t {
|
||||
};
|
||||
|
||||
class MoveEvent;
|
||||
using MoveEvent_ptr = std::unique_ptr<MoveEvent>;
|
||||
|
||||
struct MoveEventList {
|
||||
std::list<MoveEvent> moveEvent[MOVE_EVENT_LAST];
|
||||
std::list<MoveEvent*> moveEvent[MOVE_EVENT_LAST];
|
||||
};
|
||||
|
||||
using VocEquipMap = std::map<uint16_t, bool>;
|
||||
typedef std::map<uint16_t, bool> VocEquipMap;
|
||||
|
||||
class MoveEvents final : public BaseEvents
|
||||
{
|
||||
@ -67,54 +63,51 @@ class MoveEvents final : public BaseEvents
|
||||
|
||||
MoveEvent* getEvent(Item* item, MoveEvent_t eventType);
|
||||
|
||||
bool registerLuaEvent(MoveEvent* event);
|
||||
bool registerLuaFunction(MoveEvent* event);
|
||||
void clear(bool fromLua) override final;
|
||||
protected:
|
||||
typedef std::map<int32_t, MoveEventList> MoveListMap;
|
||||
void clearMap(MoveListMap& map);
|
||||
|
||||
private:
|
||||
using MoveListMap = std::map<int32_t, MoveEventList>;
|
||||
using MovePosListMap = std::map<Position, MoveEventList>;
|
||||
void clearMap(MoveListMap& map, bool fromLua);
|
||||
void clearPosMap(MovePosListMap& map, bool fromLua);
|
||||
typedef std::map<Position, MoveEventList> MovePosListMap;
|
||||
void clear() final;
|
||||
LuaScriptInterface& getScriptInterface() final;
|
||||
std::string getScriptBaseName() const final;
|
||||
Event* getEvent(const std::string& nodeName) final;
|
||||
bool registerEvent(Event* event, const pugi::xml_node& node) final;
|
||||
|
||||
LuaScriptInterface& getScriptInterface() override;
|
||||
std::string getScriptBaseName() const override;
|
||||
Event_ptr getEvent(const std::string& nodeName) override;
|
||||
bool registerEvent(Event_ptr event, const pugi::xml_node& node) override;
|
||||
void addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map);
|
||||
|
||||
void addEvent(MoveEvent moveEvent, int32_t id, MoveListMap& map);
|
||||
|
||||
void addEvent(MoveEvent moveEvent, const Position& pos, MovePosListMap& map);
|
||||
void addEvent(MoveEvent* moveEvent, const Position& pos, MovePosListMap& map);
|
||||
MoveEvent* getEvent(const Tile* tile, MoveEvent_t eventType);
|
||||
|
||||
MoveEvent* getEvent(Item* item, MoveEvent_t eventType, slots_t slot);
|
||||
|
||||
MoveListMap uniqueIdMap;
|
||||
MoveListMap actionIdMap;
|
||||
|
||||
MoveListMap movementIdMap;
|
||||
MoveListMap itemIdMap;
|
||||
MovePosListMap positionMap;
|
||||
|
||||
LuaScriptInterface scriptInterface;
|
||||
};
|
||||
|
||||
using StepFunction = std::function<uint32_t(Creature* creature, Item* item, const Position& pos)>;
|
||||
using MoveFunction = std::function<uint32_t(Item* item, Item* tileItem, const Position& pos)>;
|
||||
using EquipFunction = std::function<uint32_t(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean)>;
|
||||
typedef uint32_t (StepFunction)(Creature* creature, Item* item, const Position& pos);
|
||||
typedef uint32_t (MoveFunction)(Item* item, Item* tileItem, const Position& pos);
|
||||
typedef uint32_t (EquipFunction)(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
|
||||
|
||||
class MoveEvent final : public Event
|
||||
{
|
||||
public:
|
||||
explicit MoveEvent(LuaScriptInterface* interface);
|
||||
explicit MoveEvent(const MoveEvent* copy);
|
||||
|
||||
MoveEvent_t getEventType() const;
|
||||
void setEventType(MoveEvent_t type);
|
||||
|
||||
bool configureEvent(const pugi::xml_node& node) override;
|
||||
bool loadFunction(const pugi::xml_attribute& attr, bool isScripted) override;
|
||||
bool configureEvent(const pugi::xml_node& node) final;
|
||||
bool loadFunction(const pugi::xml_attribute& attr) final;
|
||||
|
||||
uint32_t fireStepEvent(Creature* creature, Item* item, const Position& pos);
|
||||
uint32_t fireAddRemItem(Item* item, Item* tileItem, const Position& pos);
|
||||
uint32_t fireEquip(Player* player, Item* item, slots_t slot, bool isCheck);
|
||||
uint32_t fireEquip(Player* player, Item* item, slots_t slot, bool boolean);
|
||||
|
||||
uint32_t getSlot() const {
|
||||
return slot;
|
||||
@ -122,7 +115,7 @@ class MoveEvent final : public Event
|
||||
|
||||
//scripting
|
||||
bool executeStep(Creature* creature, Item* item, const Position& pos);
|
||||
bool executeEquip(Player* player, Item* item, slots_t slot, bool isCheck);
|
||||
bool executeEquip(Player* player, Item* item, slots_t slot);
|
||||
bool executeAddRemItem(Item* item, Item* tileItem, const Position& pos);
|
||||
//
|
||||
|
||||
@ -139,104 +132,29 @@ class MoveEvent final : public Event
|
||||
const std::string& getVocationString() const {
|
||||
return vocationString;
|
||||
}
|
||||
void setVocationString(const std::string& str) {
|
||||
vocationString = str;
|
||||
}
|
||||
uint32_t getWieldInfo() const {
|
||||
return wieldInfo;
|
||||
}
|
||||
const VocEquipMap& getVocEquipMap() const {
|
||||
return vocEquipMap;
|
||||
}
|
||||
void addVocEquipMap(std::string vocName) {
|
||||
int32_t vocationId = g_vocations.getVocationId(vocName);
|
||||
if (vocationId != -1) {
|
||||
vocEquipMap[vocationId] = true;
|
||||
}
|
||||
}
|
||||
bool getTileItem() const {
|
||||
return tileItem;
|
||||
}
|
||||
void setTileItem(bool b) {
|
||||
tileItem = b;
|
||||
}
|
||||
std::vector<uint32_t> getItemIdRange() {
|
||||
return itemIdRange;
|
||||
}
|
||||
void addItemId(uint32_t id) {
|
||||
itemIdRange.emplace_back(id);
|
||||
}
|
||||
std::vector<uint32_t> getActionIdRange() {
|
||||
return actionIdRange;
|
||||
}
|
||||
void addActionId(uint32_t id) {
|
||||
actionIdRange.emplace_back(id);
|
||||
}
|
||||
std::vector<uint32_t> getUniqueIdRange() {
|
||||
return uniqueIdRange;
|
||||
}
|
||||
void addUniqueId(uint32_t id) {
|
||||
uniqueIdRange.emplace_back(id);
|
||||
}
|
||||
std::vector<Position> getPosList() {
|
||||
return posList;
|
||||
}
|
||||
void addPosList(Position pos) {
|
||||
posList.emplace_back(pos);
|
||||
}
|
||||
std::string getSlotName() {
|
||||
return slotName;
|
||||
}
|
||||
void setSlotName(std::string name) {
|
||||
slotName = name;
|
||||
}
|
||||
void setSlot(uint32_t s) {
|
||||
slot = s;
|
||||
}
|
||||
uint32_t getRequiredLevel() {
|
||||
return reqLevel;
|
||||
}
|
||||
void setRequiredLevel(uint32_t level) {
|
||||
reqLevel = level;
|
||||
}
|
||||
uint32_t getRequiredMagLevel() {
|
||||
return reqMagLevel;
|
||||
}
|
||||
void setRequiredMagLevel(uint32_t level) {
|
||||
reqMagLevel = level;
|
||||
}
|
||||
bool needPremium() {
|
||||
return premium;
|
||||
}
|
||||
void setNeedPremium(bool b) {
|
||||
premium = b;
|
||||
}
|
||||
uint32_t getWieldInfo() {
|
||||
return wieldInfo;
|
||||
}
|
||||
void setWieldInfo(WieldInfo_t info) {
|
||||
wieldInfo |= info;
|
||||
}
|
||||
|
||||
static uint32_t StepInField(Creature* creature, Item* item, const Position& pos);
|
||||
static uint32_t StepOutField(Creature* creature, Item* item, const Position& pos);
|
||||
protected:
|
||||
std::string getScriptEventName() const final;
|
||||
|
||||
static uint32_t AddItemField(Item* item, Item* tileItem, const Position& pos);
|
||||
static uint32_t RemoveItemField(Item* item, Item* tileItem, const Position& pos);
|
||||
static StepFunction StepInField;
|
||||
static StepFunction StepOutField;
|
||||
|
||||
static uint32_t EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
|
||||
static uint32_t DeEquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool boolean);
|
||||
static MoveFunction AddItemField;
|
||||
static MoveFunction RemoveItemField;
|
||||
static EquipFunction EquipItem;
|
||||
static EquipFunction DeEquipItem;
|
||||
|
||||
MoveEvent_t eventType = MOVE_EVENT_NONE;
|
||||
StepFunction stepFunction;
|
||||
MoveFunction moveFunction;
|
||||
EquipFunction equipFunction;
|
||||
|
||||
private:
|
||||
std::string getScriptEventName() const override;
|
||||
|
||||
StepFunction* stepFunction = nullptr;
|
||||
MoveFunction* moveFunction = nullptr;
|
||||
EquipFunction* equipFunction = nullptr;
|
||||
uint32_t slot = SLOTP_WHEREEVER;
|
||||
std::string slotName;
|
||||
|
||||
//onEquip information
|
||||
uint32_t reqLevel = 0;
|
||||
@ -245,12 +163,6 @@ class MoveEvent final : public Event
|
||||
std::string vocationString;
|
||||
uint32_t wieldInfo = 0;
|
||||
VocEquipMap vocEquipMap;
|
||||
bool tileItem = false;
|
||||
|
||||
std::vector<uint32_t> itemIdRange;
|
||||
std::vector<uint32_t> actionIdRange;
|
||||
std::vector<uint32_t> uniqueIdRange;
|
||||
std::vector<Position> posList;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -99,18 +99,16 @@ void NetworkMessage::addItem(uint16_t id, uint8_t count)
|
||||
{
|
||||
const ItemType& it = Item::items[id];
|
||||
|
||||
add<uint16_t>(it.clientId);
|
||||
|
||||
addByte(0xFF); // MARK_UNMARKED
|
||||
if (it.disguise) {
|
||||
add<uint16_t>(it.disguiseId);
|
||||
} else {
|
||||
add<uint16_t>(it.id);
|
||||
}
|
||||
|
||||
if (it.stackable) {
|
||||
addByte(count);
|
||||
} else if (it.isSplash() || it.isFluidContainer()) {
|
||||
addByte(fluidMap[count & 7]);
|
||||
}
|
||||
|
||||
if (it.isAnimation) {
|
||||
addByte(0xFE); // random phase (0xFF for async)
|
||||
addByte(getLiquidColor(count));
|
||||
}
|
||||
}
|
||||
|
||||
@ -118,21 +116,20 @@ void NetworkMessage::addItem(const Item* item)
|
||||
{
|
||||
const ItemType& it = Item::items[item->getID()];
|
||||
|
||||
add<uint16_t>(it.clientId);
|
||||
addByte(0xFF); // MARK_UNMARKED
|
||||
if (it.disguise) {
|
||||
add<uint16_t>(it.disguiseId);
|
||||
} else {
|
||||
add<uint16_t>(it.id);
|
||||
}
|
||||
|
||||
if (it.stackable) {
|
||||
addByte(std::min<uint16_t>(0xFF, item->getItemCount()));
|
||||
} else if (it.isSplash() || it.isFluidContainer()) {
|
||||
addByte(fluidMap[item->getFluidType() & 7]);
|
||||
}
|
||||
|
||||
if (it.isAnimation) {
|
||||
addByte(0xFE); // random phase (0xFF for async)
|
||||
addByte(getLiquidColor(item->getFluidType()));
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkMessage::addItemId(uint16_t itemId)
|
||||
{
|
||||
add<uint16_t>(Item::items[itemId].clientId);
|
||||
add<uint16_t>(itemId);
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -31,16 +31,14 @@ class RSA;
|
||||
class NetworkMessage
|
||||
{
|
||||
public:
|
||||
using MsgSize_t = uint16_t;
|
||||
typedef uint16_t MsgSize_t;
|
||||
// Headers:
|
||||
// 2 bytes for unencrypted message size
|
||||
// 4 bytes for checksum
|
||||
// 2 bytes for encrypted message size
|
||||
static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 8;
|
||||
static constexpr MsgSize_t INITIAL_BUFFER_POSITION = 4;
|
||||
enum { HEADER_LENGTH = 2 };
|
||||
enum { CHECKSUM_LENGTH = 4 };
|
||||
enum { XTEA_MULTIPLE = 8 };
|
||||
enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - CHECKSUM_LENGTH - XTEA_MULTIPLE };
|
||||
enum { MAX_BODY_LENGTH = NETWORKMESSAGE_MAXSIZE - HEADER_LENGTH - XTEA_MULTIPLE };
|
||||
enum { MAX_PROTOCOL_BODY_LENGTH = MAX_BODY_LENGTH - 10 };
|
||||
|
||||
NetworkMessage() = default;
|
||||
@ -150,6 +148,18 @@ class NetworkMessage
|
||||
}
|
||||
|
||||
protected:
|
||||
inline bool canAdd(size_t size) const {
|
||||
return (size + info.position) < MAX_BODY_LENGTH;
|
||||
}
|
||||
|
||||
inline bool canRead(int32_t size) {
|
||||
if ((info.position + size) > (info.length + 8) || size >= (NETWORKMESSAGE_MAXSIZE - info.position)) {
|
||||
info.overrun = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
struct NetworkMessageInfo {
|
||||
MsgSize_t length = 0;
|
||||
MsgSize_t position = INITIAL_BUFFER_POSITION;
|
||||
@ -158,19 +168,6 @@ class NetworkMessage
|
||||
|
||||
NetworkMessageInfo info;
|
||||
uint8_t buffer[NETWORKMESSAGE_MAXSIZE];
|
||||
|
||||
private:
|
||||
bool canAdd(size_t size) const {
|
||||
return (size + info.position) < MAX_BODY_LENGTH;
|
||||
}
|
||||
|
||||
bool canRead(int32_t size) {
|
||||
if ((info.position + size) > (info.length + 8) || size >= (NETWORKMESSAGE_MAXSIZE - info.position)) {
|
||||
info.overrun = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif // #ifndef __NETWORK_MESSAGE_H__
|
||||
|
1150
src/npc.cpp
1150
src/npc.cpp
File diff suppressed because it is too large
Load Diff
171
src/npc.h
171
src/npc.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -21,86 +21,20 @@
|
||||
#define FS_NPC_H_B090D0CB549D4435AFA03647195D156F
|
||||
|
||||
#include "creature.h"
|
||||
#include "luascript.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
class Npc;
|
||||
class Player;
|
||||
class BehaviourDatabase;
|
||||
|
||||
class Npcs
|
||||
{
|
||||
public:
|
||||
static void loadNpcs();
|
||||
static void reload();
|
||||
};
|
||||
|
||||
class NpcScriptInterface final : public LuaScriptInterface
|
||||
{
|
||||
public:
|
||||
NpcScriptInterface();
|
||||
|
||||
bool loadNpcLib(const std::string& file);
|
||||
|
||||
private:
|
||||
void registerFunctions();
|
||||
|
||||
static int luaActionSay(lua_State* L);
|
||||
static int luaActionMove(lua_State* L);
|
||||
static int luaActionMoveTo(lua_State* L);
|
||||
static int luaActionTurn(lua_State* L);
|
||||
static int luaActionFollow(lua_State* L);
|
||||
static int luagetDistanceTo(lua_State* L);
|
||||
static int luaSetNpcFocus(lua_State* L);
|
||||
static int luaGetNpcCid(lua_State* L);
|
||||
static int luaGetNpcParameter(lua_State* L);
|
||||
static int luaOpenShopWindow(lua_State* L);
|
||||
static int luaCloseShopWindow(lua_State* L);
|
||||
static int luaDoSellItem(lua_State* L);
|
||||
|
||||
// metatable
|
||||
static int luaNpcGetParameter(lua_State* L);
|
||||
static int luaNpcSetFocus(lua_State* L);
|
||||
|
||||
static int luaNpcOpenShopWindow(lua_State* L);
|
||||
static int luaNpcCloseShopWindow(lua_State* L);
|
||||
|
||||
private:
|
||||
bool initState() override;
|
||||
bool closeState() override;
|
||||
|
||||
bool libLoaded;
|
||||
};
|
||||
|
||||
class NpcEventsHandler
|
||||
{
|
||||
public:
|
||||
NpcEventsHandler(const std::string& file, Npc* npc);
|
||||
|
||||
void onCreatureAppear(Creature* creature);
|
||||
void onCreatureDisappear(Creature* creature);
|
||||
void onCreatureMove(Creature* creature, const Position& oldPos, const Position& newPos);
|
||||
void onCreatureSay(Creature* creature, SpeakClasses, const std::string& text);
|
||||
void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, bool ignore = false, bool inBackpacks = false);
|
||||
void onPlayerCloseChannel(Player* player);
|
||||
void onPlayerEndTrade(Player* player);
|
||||
void onThink();
|
||||
|
||||
bool isLoaded() const;
|
||||
|
||||
private:
|
||||
Npc* npc;
|
||||
NpcScriptInterface* scriptInterface;
|
||||
|
||||
int32_t creatureAppearEvent = -1;
|
||||
int32_t creatureDisappearEvent = -1;
|
||||
int32_t creatureMoveEvent = -1;
|
||||
int32_t creatureSayEvent = -1;
|
||||
int32_t playerCloseChannelEvent = -1;
|
||||
int32_t playerEndTradeEvent = -1;
|
||||
int32_t thinkEvent = -1;
|
||||
bool loaded = false;
|
||||
};
|
||||
|
||||
class Npc final : public Creature
|
||||
{
|
||||
public:
|
||||
@ -110,53 +44,41 @@ class Npc final : public Creature
|
||||
Npc(const Npc&) = delete;
|
||||
Npc& operator=(const Npc&) = delete;
|
||||
|
||||
Npc* getNpc() override {
|
||||
Npc* getNpc() final {
|
||||
return this;
|
||||
}
|
||||
const Npc* getNpc() const override {
|
||||
const Npc* getNpc() const final {
|
||||
return this;
|
||||
}
|
||||
|
||||
bool isPushable() const override {
|
||||
return pushable && walkTicks != 0;
|
||||
bool isPushable() const final {
|
||||
return baseSpeed > 0;
|
||||
}
|
||||
|
||||
void setID() override {
|
||||
void setID() final {
|
||||
if (id == 0) {
|
||||
id = npcAutoID++;
|
||||
}
|
||||
}
|
||||
|
||||
void removeList() override;
|
||||
void addList() override;
|
||||
void removeList() final;
|
||||
void addList() final;
|
||||
|
||||
static Npc* createNpc(const std::string& name);
|
||||
|
||||
bool canSee(const Position& pos) const override;
|
||||
bool canSee(const Position& pos) const final;
|
||||
|
||||
bool load();
|
||||
void reload();
|
||||
|
||||
const std::string& getName() const override {
|
||||
const std::string& getName() const final {
|
||||
return name;
|
||||
}
|
||||
const std::string& getNameDescription() const override {
|
||||
const std::string& getNameDescription() const final {
|
||||
return name;
|
||||
}
|
||||
|
||||
CreatureType_t getType() const override {
|
||||
return CREATURETYPE_NPC;
|
||||
}
|
||||
|
||||
uint8_t getSpeechBubble() const override {
|
||||
return speechBubble;
|
||||
}
|
||||
void setSpeechBubble(const uint8_t bubble) {
|
||||
speechBubble = bubble;
|
||||
}
|
||||
|
||||
void doSay(const std::string& text);
|
||||
void doSayToPlayer(Player* player, const std::string& text);
|
||||
|
||||
void doMoveTo(const Position& pos);
|
||||
|
||||
@ -168,45 +90,38 @@ class Npc final : public Creature
|
||||
}
|
||||
void setMasterPos(Position pos, int32_t radius = 1) {
|
||||
masterPos = pos;
|
||||
if (masterRadius == -1) {
|
||||
if (masterRadius == 0) {
|
||||
masterRadius = radius;
|
||||
}
|
||||
}
|
||||
|
||||
void onPlayerCloseChannel(Player* player);
|
||||
void onPlayerTrade(Player* player, int32_t callback, uint16_t itemId, uint8_t count,
|
||||
uint8_t amount, bool ignore = false, bool inBackpacks = false);
|
||||
void onPlayerEndTrade(Player* player, int32_t buyCallback, int32_t sellCallback);
|
||||
|
||||
void turnToCreature(Creature* creature);
|
||||
void setCreatureFocus(Creature* creature);
|
||||
|
||||
NpcScriptInterface* getScriptInterface();
|
||||
|
||||
static uint32_t npcAutoID;
|
||||
|
||||
private:
|
||||
protected:
|
||||
explicit Npc(const std::string& name);
|
||||
|
||||
void onCreatureAppear(Creature* creature, bool isLogin) override;
|
||||
void onRemoveCreature(Creature* creature, bool isLogout) override;
|
||||
void onCreatureAppear(Creature* creature, bool isLogin) final;
|
||||
void onRemoveCreature(Creature* creature, bool isLogout) final;
|
||||
void onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos,
|
||||
const Tile* oldTile, const Position& oldPos, bool teleport) override;
|
||||
const Tile* oldTile, const Position& oldPos, bool teleport) final;
|
||||
|
||||
void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) override;
|
||||
void onThink(uint32_t interval) override;
|
||||
std::string getDescription(int32_t lookDistance) const override;
|
||||
void onCreatureSay(Creature* creature, SpeakClasses type, const std::string& text) final;
|
||||
void onThink(uint32_t interval) final;
|
||||
std::string getDescription(int32_t lookDistance) const final;
|
||||
|
||||
bool isImmune(CombatType_t) const override {
|
||||
return !attackable;
|
||||
bool isImmune(CombatType_t) const final {
|
||||
return true;
|
||||
}
|
||||
bool isImmune(ConditionType_t) const override {
|
||||
return !attackable;
|
||||
bool isImmune(ConditionType_t) const final {
|
||||
return true;
|
||||
}
|
||||
bool isAttackable() const override {
|
||||
return attackable;
|
||||
bool isAttackable() const final {
|
||||
return false;
|
||||
}
|
||||
bool getNextStep(Direction& dir, uint32_t& flags) override;
|
||||
bool getNextStep(Direction& dir, uint32_t& flags) final;
|
||||
|
||||
void setIdle(bool idle);
|
||||
void updateIdleStatus();
|
||||
@ -215,41 +130,29 @@ class Npc final : public Creature
|
||||
bool getRandomStep(Direction& dir) const;
|
||||
|
||||
void reset();
|
||||
bool loadFromXml();
|
||||
|
||||
void addShopPlayer(Player* player);
|
||||
void removeShopPlayer(Player* player);
|
||||
void closeAllShopWindows();
|
||||
|
||||
std::map<std::string, std::string> parameters;
|
||||
|
||||
std::set<Player*> shopPlayerSet;
|
||||
std::set<Player*> spectators;
|
||||
|
||||
std::string name;
|
||||
std::string filename;
|
||||
|
||||
NpcEventsHandler* npcEventHandler;
|
||||
|
||||
Position masterPos;
|
||||
|
||||
uint32_t walkTicks;
|
||||
int32_t focusCreature;
|
||||
int32_t masterRadius;
|
||||
uint32_t lastTalkCreature;
|
||||
uint32_t focusCreature;
|
||||
uint32_t masterRadius;
|
||||
|
||||
uint8_t speechBubble;
|
||||
int64_t conversationStartTime;
|
||||
int64_t conversationEndTime;
|
||||
int64_t staticMovementTime;
|
||||
|
||||
bool floorChange;
|
||||
bool attackable;
|
||||
bool ignoreHeight;
|
||||
bool loaded;
|
||||
bool isIdle;
|
||||
bool pushable;
|
||||
|
||||
static NpcScriptInterface* scriptInterface;
|
||||
BehaviourDatabase* behaviourDatabase;
|
||||
|
||||
friend class Npcs;
|
||||
friend class NpcScriptInterface;
|
||||
friend class BehaviourDatabase;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -23,19 +23,18 @@
|
||||
|
||||
#include "game.h"
|
||||
|
||||
#include "iomarket.h"
|
||||
#ifndef _WIN32
|
||||
#include <csignal> // for sigemptyset()
|
||||
#endif
|
||||
|
||||
#include "configmanager.h"
|
||||
#include "scriptmanager.h"
|
||||
#include "rsa.h"
|
||||
#include "protocolold.h"
|
||||
#include "protocollogin.h"
|
||||
#include "protocolstatus.h"
|
||||
#include "databasemanager.h"
|
||||
#include "scheduler.h"
|
||||
#include "databasetasks.h"
|
||||
#include "script.h"
|
||||
#include <fstream>
|
||||
|
||||
DatabaseTasks g_databaseTasks;
|
||||
Dispatcher g_dispatcher;
|
||||
@ -45,7 +44,6 @@ Game g_game;
|
||||
ConfigManager g_config;
|
||||
Monsters g_monsters;
|
||||
Vocations g_vocations;
|
||||
extern Scripts* g_scripts;
|
||||
RSA g_RSA;
|
||||
|
||||
std::mutex g_loaderLock;
|
||||
@ -58,9 +56,9 @@ void startupErrorMessage(const std::string& errorStr)
|
||||
g_loaderSignal.notify_all();
|
||||
}
|
||||
|
||||
void mainLoader(int argc, char* argv[], ServiceManager* services);
|
||||
void mainLoader(int argc, char* argv[], ServiceManager* servicer);
|
||||
|
||||
[[noreturn]] void badAllocationHandler()
|
||||
void badAllocationHandler()
|
||||
{
|
||||
// Use functions that only use stack allocation
|
||||
puts("Allocation failed, server out of memory.\nDecrease the size of your map or compile in 64 bits mode.\n");
|
||||
@ -73,6 +71,15 @@ int main(int argc, char* argv[])
|
||||
// Setup bad allocation handler
|
||||
std::set_new_handler(badAllocationHandler);
|
||||
|
||||
#ifndef _WIN32
|
||||
// ignore sigpipe...
|
||||
struct sigaction sigh;
|
||||
sigh.sa_handler = SIG_IGN;
|
||||
sigh.sa_flags = 0;
|
||||
sigemptyset(&sigh.sa_mask);
|
||||
sigaction(SIGPIPE, &sigh, nullptr);
|
||||
#endif
|
||||
|
||||
ServiceManager serviceManager;
|
||||
|
||||
g_dispatcher.start();
|
||||
@ -84,6 +91,19 @@ int main(int argc, char* argv[])
|
||||
|
||||
if (serviceManager.is_running()) {
|
||||
std::cout << ">> " << g_config.getString(ConfigManager::SERVER_NAME) << " Server Online!" << std::endl << std::endl;
|
||||
#ifdef _WIN32
|
||||
SetConsoleCtrlHandler([](DWORD) -> BOOL {
|
||||
g_dispatcher.addTask(createTask([]() {
|
||||
g_dispatcher.addTask(createTask(
|
||||
std::bind(&Game::shutdown, &g_game)
|
||||
));
|
||||
g_scheduler.stop();
|
||||
g_databaseTasks.stop();
|
||||
g_dispatcher.stop();
|
||||
}));
|
||||
ExitThread(0);
|
||||
}, 1);
|
||||
#endif
|
||||
serviceManager.run();
|
||||
} else {
|
||||
std::cout << ">> No services running. The server is NOT online." << std::endl;
|
||||
@ -126,21 +146,6 @@ void mainLoader(int, char*[], ServiceManager* services)
|
||||
std::cout << "Visit our forum for updates, support, and resources: http://otland.net/." << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
// check if config.lua or config.lua.dist exist
|
||||
std::ifstream c_test("./config.lua");
|
||||
if (!c_test.is_open()) {
|
||||
std::ifstream config_lua_dist("./config.lua.dist");
|
||||
if (config_lua_dist.is_open()) {
|
||||
std::cout << ">> copying config.lua.dist to config.lua" << std::endl;
|
||||
std::ofstream config_lua("config.lua");
|
||||
config_lua << config_lua_dist.rdbuf();
|
||||
config_lua.close();
|
||||
config_lua_dist.close();
|
||||
}
|
||||
} else {
|
||||
c_test.close();
|
||||
}
|
||||
|
||||
// read global config
|
||||
std::cout << ">> Loading config" << std::endl;
|
||||
if (!g_config.load()) {
|
||||
@ -160,14 +165,16 @@ void mainLoader(int, char*[], ServiceManager* services)
|
||||
//set RSA key
|
||||
try {
|
||||
g_RSA.loadPEM("key.pem");
|
||||
} catch(const std::exception& e) {
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
startupErrorMessage(e.what());
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << ">> Establishing database connection..." << std::flush;
|
||||
|
||||
if (!Database::getInstance().connect()) {
|
||||
Database* db = Database::getInstance();
|
||||
if (!db->connect()) {
|
||||
startupErrorMessage("Failed to connect to database.");
|
||||
return;
|
||||
}
|
||||
@ -183,8 +190,6 @@ void mainLoader(int, char*[], ServiceManager* services)
|
||||
}
|
||||
g_databaseTasks.start();
|
||||
|
||||
DatabaseManager::updateDatabase();
|
||||
|
||||
if (g_config.getBoolean(ConfigManager::OPTIMIZE_DATABASE) && !DatabaseManager::optimizeTables()) {
|
||||
std::cout << "> No tables were optimized." << std::endl;
|
||||
}
|
||||
@ -198,42 +203,26 @@ void mainLoader(int, char*[], ServiceManager* services)
|
||||
|
||||
// load item data
|
||||
std::cout << ">> Loading items" << std::endl;
|
||||
if (!Item::items.loadFromOtb("data/items/items.otb")) {
|
||||
startupErrorMessage("Unable to load items (OTB)!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Item::items.loadFromXml()) {
|
||||
startupErrorMessage("Unable to load items (XML)!");
|
||||
if (!Item::items.loadItems()) {
|
||||
startupErrorMessage("Unable to load items (SRV)!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << ">> Loading script systems" << std::endl;
|
||||
if (!ScriptingManager::getInstance().loadScriptSystems()) {
|
||||
if (!ScriptingManager::getInstance()->loadScriptSystems()) {
|
||||
startupErrorMessage("Failed to load script systems");
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << ">> Loading lua scripts" << std::endl;
|
||||
if (!g_scripts->loadScripts("scripts", false, false)) {
|
||||
startupErrorMessage("Failed to load lua scripts");
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << ">> Loading monsters" << std::endl;
|
||||
if (!g_monsters.loadFromXml()) {
|
||||
startupErrorMessage("Unable to load monsters!");
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << ">> Loading lua monsters" << std::endl;
|
||||
if (!g_scripts->loadScripts("monster", false, false)) {
|
||||
startupErrorMessage("Failed to load lua monsters");
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << ">> Loading outfits" << std::endl;
|
||||
if (!Outfits::getInstance().loadFromXml()) {
|
||||
auto& outfits = Outfits::getInstance();
|
||||
if (!outfits.loadFromXml()) {
|
||||
startupErrorMessage("Unable to load outfits!");
|
||||
return;
|
||||
}
|
||||
@ -266,14 +255,11 @@ void mainLoader(int, char*[], ServiceManager* services)
|
||||
g_game.setGameState(GAME_STATE_INIT);
|
||||
|
||||
// Game client protocols
|
||||
services->add<ProtocolGame>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::GAME_PORT)));
|
||||
services->add<ProtocolLogin>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
|
||||
services->add<ProtocolGame>(g_config.getNumber(ConfigManager::GAME_PORT));
|
||||
services->add<ProtocolLogin>(g_config.getNumber(ConfigManager::LOGIN_PORT));
|
||||
|
||||
// OT protocols
|
||||
services->add<ProtocolStatus>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::STATUS_PORT)));
|
||||
|
||||
// Legacy login protocol
|
||||
services->add<ProtocolOld>(static_cast<uint16_t>(g_config.getNumber(ConfigManager::LOGIN_PORT)));
|
||||
services->add<ProtocolStatus>(g_config.getNumber(ConfigManager::STATUS_PORT));
|
||||
|
||||
RentPeriod_t rentPeriod;
|
||||
std::string strRentPeriod = asLowerCaseString(g_config.getString(ConfigManager::HOUSE_RENT_PERIOD));
|
||||
@ -292,9 +278,6 @@ void mainLoader(int, char*[], ServiceManager* services)
|
||||
|
||||
g_game.map.houses.payHouses(rentPeriod);
|
||||
|
||||
IOMarket::checkExpiredOffers();
|
||||
IOMarket::getInstance().updateStatistics();
|
||||
|
||||
std::cout << ">> Loaded all modules, server starting up..." << std::endl;
|
||||
|
||||
#ifndef _WIN32
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -29,6 +29,14 @@ extern Scheduler g_scheduler;
|
||||
const uint16_t OUTPUTMESSAGE_FREE_LIST_CAPACITY = 2048;
|
||||
const std::chrono::milliseconds OUTPUTMESSAGE_AUTOSEND_DELAY {10};
|
||||
|
||||
class OutputMessageAllocator
|
||||
{
|
||||
public:
|
||||
typedef OutputMessage value_type;
|
||||
template<typename U>
|
||||
struct rebind {typedef LockfreePoolingAllocator<U, OUTPUTMESSAGE_FREE_LIST_CAPACITY> other;};
|
||||
};
|
||||
|
||||
void OutputMessagePool::scheduleSendAll()
|
||||
{
|
||||
auto functor = std::bind(&OutputMessagePool::sendAll, this);
|
||||
@ -71,7 +79,5 @@ void OutputMessagePool::removeProtocolFromAutosend(const Protocol_ptr& protocol)
|
||||
|
||||
OutputMessage_ptr OutputMessagePool::getOutputMessage()
|
||||
{
|
||||
// LockfreePoolingAllocator<void,...> will leave (void* allocate) ill-formed because
|
||||
// of sizeof(T), so this guaranatees that only one list will be initialized
|
||||
return std::allocate_shared<OutputMessage>(LockfreePoolingAllocator<void, OUTPUTMESSAGE_FREE_LIST_CAPACITY>());
|
||||
return std::allocate_shared<OutputMessage>(OutputMessageAllocator());
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -28,80 +28,76 @@ class Protocol;
|
||||
|
||||
class OutputMessage : public NetworkMessage
|
||||
{
|
||||
public:
|
||||
OutputMessage() = default;
|
||||
public:
|
||||
OutputMessage() = default;
|
||||
|
||||
// non-copyable
|
||||
OutputMessage(const OutputMessage&) = delete;
|
||||
OutputMessage& operator=(const OutputMessage&) = delete;
|
||||
// non-copyable
|
||||
OutputMessage(const OutputMessage&) = delete;
|
||||
OutputMessage& operator=(const OutputMessage&) = delete;
|
||||
|
||||
uint8_t* getOutputBuffer() {
|
||||
return buffer + outputBufferStart;
|
||||
}
|
||||
uint8_t* getOutputBuffer() {
|
||||
return buffer + outputBufferStart;
|
||||
}
|
||||
|
||||
void writeMessageLength() {
|
||||
add_header(info.length);
|
||||
}
|
||||
void writeMessageLength() {
|
||||
add_header(info.length);
|
||||
}
|
||||
|
||||
void addCryptoHeader(bool addChecksum) {
|
||||
if (addChecksum) {
|
||||
add_header(adlerChecksum(buffer + outputBufferStart, info.length));
|
||||
}
|
||||
void addCryptoHeader() {
|
||||
writeMessageLength();
|
||||
}
|
||||
|
||||
writeMessageLength();
|
||||
}
|
||||
inline void append(const NetworkMessage& msg) {
|
||||
auto msgLen = msg.getLength();
|
||||
memcpy(buffer + info.position, msg.getBuffer() + 4, msgLen);
|
||||
info.length += msgLen;
|
||||
info.position += msgLen;
|
||||
}
|
||||
|
||||
void append(const NetworkMessage& msg) {
|
||||
auto msgLen = msg.getLength();
|
||||
memcpy(buffer + info.position, msg.getBuffer() + 8, msgLen);
|
||||
info.length += msgLen;
|
||||
info.position += msgLen;
|
||||
}
|
||||
inline void append(const OutputMessage_ptr& msg) {
|
||||
auto msgLen = msg->getLength();
|
||||
memcpy(buffer + info.position, msg->getBuffer() + 4, msgLen);
|
||||
info.length += msgLen;
|
||||
info.position += msgLen;
|
||||
}
|
||||
|
||||
void append(const OutputMessage_ptr& msg) {
|
||||
auto msgLen = msg->getLength();
|
||||
memcpy(buffer + info.position, msg->getBuffer() + 8, msgLen);
|
||||
info.length += msgLen;
|
||||
info.position += msgLen;
|
||||
}
|
||||
protected:
|
||||
template <typename T>
|
||||
inline void add_header(T add) {
|
||||
assert(outputBufferStart >= sizeof(T));
|
||||
outputBufferStart -= sizeof(T);
|
||||
memcpy(buffer + outputBufferStart, &add, sizeof(T));
|
||||
//added header size to the message size
|
||||
info.length += sizeof(T);
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void add_header(T add) {
|
||||
assert(outputBufferStart >= sizeof(T));
|
||||
outputBufferStart -= sizeof(T);
|
||||
memcpy(buffer + outputBufferStart, &add, sizeof(T));
|
||||
//added header size to the message size
|
||||
info.length += sizeof(T);
|
||||
}
|
||||
|
||||
MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION;
|
||||
MsgSize_t outputBufferStart = INITIAL_BUFFER_POSITION;
|
||||
};
|
||||
|
||||
class OutputMessagePool
|
||||
{
|
||||
public:
|
||||
// non-copyable
|
||||
OutputMessagePool(const OutputMessagePool&) = delete;
|
||||
OutputMessagePool& operator=(const OutputMessagePool&) = delete;
|
||||
public:
|
||||
// non-copyable
|
||||
OutputMessagePool(const OutputMessagePool&) = delete;
|
||||
OutputMessagePool& operator=(const OutputMessagePool&) = delete;
|
||||
|
||||
static OutputMessagePool& getInstance() {
|
||||
static OutputMessagePool instance;
|
||||
return instance;
|
||||
}
|
||||
static OutputMessagePool& getInstance() {
|
||||
static OutputMessagePool instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void sendAll();
|
||||
void scheduleSendAll();
|
||||
void sendAll();
|
||||
void scheduleSendAll();
|
||||
|
||||
static OutputMessage_ptr getOutputMessage();
|
||||
static OutputMessage_ptr getOutputMessage();
|
||||
|
||||
void addProtocolToAutosend(Protocol_ptr protocol);
|
||||
void removeProtocolFromAutosend(const Protocol_ptr& protocol);
|
||||
private:
|
||||
OutputMessagePool() = default;
|
||||
//NOTE: A vector is used here because this container is mostly read
|
||||
//and relatively rarely modified (only when a client connects/disconnects)
|
||||
std::vector<Protocol_ptr> bufferedProtocols;
|
||||
void addProtocolToAutosend(Protocol_ptr protocol);
|
||||
void removeProtocolFromAutosend(const Protocol_ptr& protocol);
|
||||
private:
|
||||
OutputMessagePool() = default;
|
||||
//NOTE: A vector is used here because this container is mostly read
|
||||
//and relatively rarely modified (only when a client connects/disconnects)
|
||||
std::vector<Protocol_ptr> bufferedProtocols;
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -39,13 +39,13 @@ void Party::disband()
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Player* currentLeader = leader;
|
||||
leader = nullptr;
|
||||
|
||||
currentLeader->setParty(nullptr);
|
||||
currentLeader->sendClosePrivate(CHANNEL_PARTY);
|
||||
g_game.updatePlayerShield(currentLeader);
|
||||
g_game.updatePlayerHelpers(*currentLeader);
|
||||
currentLeader->sendCreatureSkull(currentLeader);
|
||||
currentLeader->sendTextMessage(MESSAGE_INFO_DESCR, "Your party has been disbanded.");
|
||||
|
||||
@ -70,8 +70,8 @@ void Party::disband()
|
||||
|
||||
member->sendCreatureSkull(currentLeader);
|
||||
currentLeader->sendCreatureSkull(member);
|
||||
g_game.updatePlayerHelpers(*member);
|
||||
}
|
||||
|
||||
memberList.clear();
|
||||
delete this;
|
||||
}
|
||||
@ -112,12 +112,10 @@ bool Party::leaveParty(Player* player)
|
||||
player->setParty(nullptr);
|
||||
player->sendClosePrivate(CHANNEL_PARTY);
|
||||
g_game.updatePlayerShield(player);
|
||||
g_game.updatePlayerHelpers(*player);
|
||||
|
||||
for (Player* member : memberList) {
|
||||
member->sendCreatureSkull(player);
|
||||
player->sendPlayerPartyIcons(member);
|
||||
g_game.updatePlayerHelpers(*member);
|
||||
}
|
||||
|
||||
leader->sendCreatureSkull(player);
|
||||
@ -127,6 +125,7 @@ bool Party::leaveParty(Player* player)
|
||||
player->sendTextMessage(MESSAGE_INFO_DESCR, "You have left the party.");
|
||||
|
||||
updateSharedExperience();
|
||||
updateVocationsList();
|
||||
|
||||
clearPlayerPoints(player);
|
||||
|
||||
@ -213,10 +212,9 @@ bool Party::joinParty(Player& player)
|
||||
|
||||
memberList.push_back(&player);
|
||||
|
||||
g_game.updatePlayerHelpers(player);
|
||||
|
||||
player.removePartyInvitation(this);
|
||||
updateSharedExperience();
|
||||
updateVocationsList();
|
||||
|
||||
const std::string& leaderName = leader->getName();
|
||||
ss.str(std::string());
|
||||
@ -244,12 +242,6 @@ bool Party::removeInvite(Player& player, bool removeFromPlayer/* = true*/)
|
||||
|
||||
if (empty()) {
|
||||
disband();
|
||||
} else {
|
||||
for (Player* member : memberList) {
|
||||
g_game.updatePlayerHelpers(*member);
|
||||
}
|
||||
|
||||
g_game.updatePlayerHelpers(*leader);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -277,7 +269,7 @@ bool Party::invitePlayer(Player& player)
|
||||
std::ostringstream ss;
|
||||
ss << player.getName() << " has been invited.";
|
||||
|
||||
if (empty()) {
|
||||
if (memberList.empty() && inviteList.empty()) {
|
||||
ss << " Open the party channel to communicate with your members.";
|
||||
g_game.updatePlayerShield(leader);
|
||||
leader->sendCreatureSkull(leader);
|
||||
@ -287,11 +279,6 @@ bool Party::invitePlayer(Player& player)
|
||||
|
||||
inviteList.push_back(&player);
|
||||
|
||||
for (Player* member : memberList) {
|
||||
g_game.updatePlayerHelpers(*member);
|
||||
}
|
||||
g_game.updatePlayerHelpers(*leader);
|
||||
|
||||
leader->sendCreatureShield(&player);
|
||||
player.sendCreatureShield(leader);
|
||||
|
||||
@ -336,6 +323,15 @@ void Party::broadcastPartyMessage(MessageClasses msgClass, const std::string& ms
|
||||
}
|
||||
}
|
||||
|
||||
void Party::broadcastPartyLoot(const std::string& loot)
|
||||
{
|
||||
leader->sendTextMessage(MESSAGE_INFO_DESCR, loot);
|
||||
|
||||
for (Player* member : memberList) {
|
||||
member->sendTextMessage(MESSAGE_INFO_DESCR, loot);
|
||||
}
|
||||
}
|
||||
|
||||
void Party::updateSharedExperience()
|
||||
{
|
||||
if (sharedExpActive) {
|
||||
@ -347,6 +343,30 @@ void Party::updateSharedExperience()
|
||||
}
|
||||
}
|
||||
|
||||
void Party::updateVocationsList()
|
||||
{
|
||||
std::set<uint32_t> vocationIds;
|
||||
|
||||
uint32_t vocationId = leader->getVocation()->getFromVocation();
|
||||
if (vocationId != VOCATION_NONE) {
|
||||
vocationIds.insert(vocationId);
|
||||
}
|
||||
|
||||
for (const Player* member : memberList) {
|
||||
vocationId = member->getVocation()->getFromVocation();
|
||||
if (vocationId != VOCATION_NONE) {
|
||||
vocationIds.insert(vocationId);
|
||||
}
|
||||
}
|
||||
|
||||
size_t size = vocationIds.size();
|
||||
if (size > 1) {
|
||||
extraExpRate = static_cast<float>(size * (10 + (size - 1) * 5)) / 100.f;
|
||||
} else {
|
||||
extraExpRate = 0.20f;
|
||||
}
|
||||
}
|
||||
|
||||
bool Party::setSharedExperience(Player* player, bool sharedExpActive)
|
||||
{
|
||||
if (!player || leader != player) {
|
||||
@ -399,7 +419,7 @@ bool Party::canUseSharedExperience(const Player* player) const
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t minLevel = static_cast<uint32_t>(std::ceil((static_cast<float>(highestLevel) * 2) / 3));
|
||||
uint32_t minLevel = static_cast<int32_t>(std::ceil((static_cast<float>(highestLevel) * 2) / 3));
|
||||
if (player->getLevel() < minLevel) {
|
||||
return false;
|
||||
}
|
||||
|
15
src/party.h
15
src/party.h
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* The Forgotten Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Mark Samman <mark.samman@gmail.com>
|
||||
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
||||
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -26,7 +26,7 @@
|
||||
class Player;
|
||||
class Party;
|
||||
|
||||
using PlayerVector = std::vector<Player*>;
|
||||
typedef std::vector<Player*> PlayerVector;
|
||||
|
||||
class Party
|
||||
{
|
||||
@ -61,12 +61,13 @@ class Party
|
||||
bool isPlayerInvited(const Player* player) const;
|
||||
void updateAllPartyIcons();
|
||||
void broadcastPartyMessage(MessageClasses msgClass, const std::string& msg, bool sendToInvitations = false);
|
||||
void broadcastPartyLoot(const std::string& loot);
|
||||
bool empty() const {
|
||||
return memberList.empty() && inviteList.empty();
|
||||
}
|
||||
bool canOpenCorpse(uint32_t ownerId) const;
|
||||
|
||||
void shareExperience(uint64_t experience, Creature* source = nullptr);
|
||||
void shareExperience(uint64_t experience, Creature* source/* = nullptr*/);
|
||||
bool setSharedExperience(Player* player, bool sharedExpActive);
|
||||
bool isSharedExperienceActive() const {
|
||||
return sharedExpActive;
|
||||
@ -77,10 +78,12 @@ class Party
|
||||
bool canUseSharedExperience(const Player* player) const;
|
||||
void updateSharedExperience();
|
||||
|
||||
void updateVocationsList();
|
||||
|
||||
void updatePlayerTicks(Player* player, uint32_t points);
|
||||
void clearPlayerPoints(Player* player);
|
||||
|
||||
private:
|
||||
protected:
|
||||
bool canEnableSharedExperience();
|
||||
|
||||
std::map<uint32_t, int64_t> ticksMap;
|
||||
@ -90,6 +93,8 @@ class Party
|
||||
|
||||
Player* leader;
|
||||
|
||||
float extraExpRate = 0.20f;
|
||||
|
||||
bool sharedExpActive = false;
|
||||
bool sharedExpEnabled = false;
|
||||
};
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user