Ir para conteúdo

HSinhori

Membro
  • Registro em

  • Última visita

Tudo que HSinhori postou

  1. HSinhori postou uma resposta no tópico em Outros Clients
    Eu já procurei em varios lugares, achei varios artigos sobre, porém nenhum resolveu, alguém sabe como eu faço para tanto o "soul" quanto as "skills" passarem de 256 no client? Eu sei que no servidor ela passa, pois aparece que eu avancei para o skill 300+, mas no cliente, quando é pra estar 257 ele zera e recomeça... Eu acredito que possa ser no client, posso estar errado, alguem sabe onde é, e como resolver? Eu uso OTClient e tfs 0.4 rev 4393
  2. HSinhori postou uma resposta no tópico em Outros Clients
    Gostaria de pedir uma ajuda pra adicionar um botão no final de cada skill que ao ser clicado chama uma talkaction, um botão tipo esse: Ficaria tipo: Magic Level 10 Fist Fighting 10 e por ai vai.... Alguém saberia me ajudar, esses são os arquivos do otclient skills.lua skill.otui
  3. Um específico
  4. Eu estou usando tfs 0.4, 8.7, eu procurei mas não encontrei, como eu faço um npc abrir diferentes janelas de trade de acordo com a vocation do player, se knight, abre o trade com itens de knight, se rp, abre o trade com itens de rp, alguém tem algum npc assim?
  5. Não é god, pq a armadura não vai Mano, eu sou muito burro, eu tava adicionando o war hammer com elemento de fire no movements e fazia o normal pra testar, hahah, deu certo agora, obg mano
  6. sim, lá também tem, tanto que aparece na descrição do war hammer
  7. eu coloquei, na armadura funcionou, porém nas armas (weapons) ele ignora e equipa de qualquer forma
  8. ficou assim: <movevent type="Equip" level="45" itemid="7758" slot="left-hand" event="function" value="onEquipItem"/> <movevent type="DeEquip" level="45" itemid="7758" slot="left-hand" event="function" value="onDeEquipItem"/> porém ainda é equipado Na real, deu certo até certa parte, armaduras não são equipadas, porém as armas são equipadas ainda
  9. coloquei assim <movevent type="Equip" itemid="7758" slot="left-hand" event="function" value="onEquipItem"/> <movevent type="DeEquip" itemid="7758" slot="left-hand" event="function" value="onDeEquipItem"/> no movements.xml e o item continua sendo equipado
  10. Opa, preciso de ajuda com uma coisa, alguns itens como zaoan armor e war hammer possuem level mínimo necessário, porém pelo menos no meu servidor eles podem ser equipados e eu gostaria que se o level não for o necessário, o item não seja equipado, eu uso TFS 04 rev4393, eu acredito que seja no movements.cpp, eu tentei mexer, mas meu conhecimento em C/C++ é bem fraco, alguém poderia me ajudar? Por script eu sei fazer, mas gostaria que fosse diretamente nas sources pra ser automático já //////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game //////////////////////////////////////////////////////////////////////// // This 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 3 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, see <http://www.gnu.org/licenses/>. //////////////////////////////////////////////////////////////////////// #include "otpch.h" #include <libxml/xmlmemory.h> #include <libxml/parser.h> #include "movement.h" #include "tools.h" #include "creature.h" #include "player.h" #include "tile.h" #include "vocation.h" #include "combat.h" #include "game.h" extern Game g_game; extern MoveEvents* g_moveEvents; MoveEvent* MoveEventScript::event = NULL; void MoveEventScript::registerFunctions() { LuaInterface::registerFunctions(); lua_register(m_luaState, "callFunction", MoveEventScript::luaCallFunction); } int32_t MoveEventScript::luaCallFunction(lua_State* L) { //callFunction(...) MoveEvent* event = MoveEventScript::event; if(!event) { error(__FUNCTION__, "MoveEvent not set!"); lua_pushboolean(L, false); return 1; } if(event->getEventType() == MOVE_EVENT_EQUIP || event->getEventType() == MOVE_EVENT_DE_EQUIP) { ScriptEnviroment* env = getEnv(); bool boolean = popNumber(L); slots_t slot = (slots_t)popNumber(L); Item* item = env->getItemByUID(popNumber(L)); if(!item) { error(__FUNCTION__, getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); return 1; } Player* player = env->getPlayerByUID(popNumber(L)); if(!player) { error(__FUNCTION__, getError(LUA_ERROR_PLAYER_NOT_FOUND)); lua_pushboolean(L, false); return 1; } if(event->getEventType() != MOVE_EVENT_EQUIP) lua_pushboolean(L, MoveEvent::DeEquipItem(event, player, item, slot, boolean)); else lua_pushboolean(L, MoveEvent::EquipItem(event, player, item, slot, boolean)); return 1; } else if(event->getEventType() == MOVE_EVENT_STEP_IN) { ScriptEnviroment* env = getEnv(); Item* item = env->getItemByUID(popNumber(L)); if(!item) { error(__FUNCTION__, getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); return 1; } Creature* creature = env->getCreatureByUID(popNumber(L)); if(!creature) { error(__FUNCTION__, getError(LUA_ERROR_CREATURE_NOT_FOUND)); lua_pushboolean(L, false); return 1; } lua_pushboolean(L, MoveEvent::StepInField(creature, item)); return 1; } else if(event->getEventType() == MOVE_EVENT_ADD_ITEM) { ScriptEnviroment* env = getEnv(); Item* item = env->getItemByUID(popNumber(L)); if(!item) { error(__FUNCTION__, getError(LUA_ERROR_ITEM_NOT_FOUND)); lua_pushboolean(L, false); return 1; } lua_pushboolean(L, MoveEvent::AddItemField(item)); return 1; } error(__FUNCTION__, "callFunction not available for current event."); lua_pushboolean(L, false); return 1; } MoveEvents::MoveEvents(): m_lastCacheTile(NULL) { m_interface.initState(); } inline void MoveEvents::clearMap(MoveListMap& map) { for(MoveListMap::iterator it = map.begin(); it != map.end(); ++it) { for(int32_t i = MOVE_EVENT_FIRST; i <= MOVE_EVENT_LAST; ++i) { EventList& moveEventList = it->second.moveEvent[i]; for(EventList::iterator it = moveEventList.begin(); it != moveEventList.end(); ++it) delete (*it); moveEventList.clear(); } } map.clear(); } void MoveEvents::clear() { clearMap(m_itemIdMap); clearMap(m_actionIdMap); clearMap(m_uniqueIdMap); for(MovePosListMap::iterator it = m_positionMap.begin(); it != m_positionMap.end(); ++it) { for(int32_t i = MOVE_EVENT_FIRST; i <= MOVE_EVENT_LAST; ++i) { EventList& moveEventList = it->second.moveEvent[i]; for(EventList::iterator it = moveEventList.begin(); it != moveEventList.end(); ++it) delete (*it); moveEventList.clear(); } } m_positionMap.clear(); m_interface.reInitState(); m_lastCacheTile = NULL; m_lastCacheItemVector.clear(); } Event* MoveEvents::getEvent(const std::string& nodeName) { std::string tmpNodeName = asLowerCaseString(nodeName); if(tmpNodeName == "movevent" || tmpNodeName == "moveevent" || tmpNodeName == "movement") return new MoveEvent(&m_interface); return NULL; } bool MoveEvents::registerEvent(Event* event, xmlNodePtr p, bool override) { MoveEvent* moveEvent = dynamic_cast<MoveEvent*>(event); if(!moveEvent) return false; std::string strValue, endStrValue; MoveEvent_t eventType = moveEvent->getEventType(); if((eventType == MOVE_EVENT_ADD_ITEM || eventType == MOVE_EVENT_REMOVE_ITEM) && readXMLString(p, "tileitem", strValue) && booleanString(strValue)) { switch(eventType) { case MOVE_EVENT_ADD_ITEM: moveEvent->setEventType(MOVE_EVENT_ADD_TILEITEM); break; case MOVE_EVENT_REMOVE_ITEM: moveEvent->setEventType(MOVE_EVENT_REMOVE_TILEITEM); break; default: break; } } StringVec strVector; IntegerVec intVector, endIntVector; bool success = true; if(readXMLString(p, "itemid", strValue)) { strVector = explodeString(strValue, ";"); for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it) { intVector = vectorAtoi(explodeString((*it), "-")); if(!intVector[0]) continue; bool equip = moveEvent->getEventType() == MOVE_EVENT_EQUIP; addEvent(moveEvent, intVector[0], m_itemIdMap, override); if(equip) { ItemType& it = Item::items.getItemType(intVector[0]); it.wieldInfo = moveEvent->getWieldInfo(); it.minReqLevel = moveEvent->getReqLevel(); it.minReqMagicLevel = moveEvent->getReqMagLv(); it.vocationString = moveEvent->getVocationString(); } if(intVector.size() > 1) { while(intVector[0] < intVector[1]) { addEvent(new MoveEvent(moveEvent), ++intVector[0], m_itemIdMap, override); if(equip) { ItemType& tit = Item::items.getItemType(intVector[0]); tit.wieldInfo = moveEvent->getWieldInfo(); tit.minReqLevel = moveEvent->getReqLevel(); tit.minReqMagicLevel = moveEvent->getReqMagLv(); tit.vocationString = moveEvent->getVocationString(); } } } } } if(readXMLString(p, "fromid", strValue) && readXMLString(p, "toid", endStrValue)) { intVector = vectorAtoi(explodeString(strValue, ";")); endIntVector = vectorAtoi(explodeString(endStrValue, ";")); if(intVector[0] && endIntVector[0] && intVector.size() == endIntVector.size()) { for(size_t i = 0, size = intVector.size(); i < size; ++i) { bool equip = moveEvent->getEventType() == MOVE_EVENT_EQUIP; addEvent(moveEvent, intVector[i], m_itemIdMap, override); if(equip) { ItemType& it = Item::items.getItemType(intVector[i]); it.wieldInfo = moveEvent->getWieldInfo(); it.minReqLevel = moveEvent->getReqLevel(); it.minReqMagicLevel = moveEvent->getReqMagLv(); it.vocationString = moveEvent->getVocationString(); } while(intVector[i] < endIntVector[i]) { addEvent(new MoveEvent(moveEvent), ++intVector[i], m_itemIdMap, override); if(equip) { ItemType& tit = Item::items.getItemType(intVector[i]); tit.wieldInfo = moveEvent->getWieldInfo(); tit.minReqLevel = moveEvent->getReqLevel(); tit.minReqMagicLevel = moveEvent->getReqMagLv(); tit.vocationString = moveEvent->getVocationString(); } } } } else std::clog << "[Warning - MoveEvents::registerEvent] Malformed entry (from item: \"" << strValue << "\", to item: \"" << endStrValue << "\")" << std::endl; } if(readXMLString(p, "uniqueid", strValue)) { strVector = explodeString(strValue, ";"); for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it) { intVector = vectorAtoi(explodeString((*it), "-")); if(!intVector[0]) continue; addEvent(moveEvent, intVector[0], m_uniqueIdMap, override); if(intVector.size() > 1) { while(intVector[0] < intVector[1]) addEvent(new MoveEvent(moveEvent), ++intVector[0], m_uniqueIdMap, override); } } } if(readXMLString(p, "fromuid", strValue) && readXMLString(p, "touid", endStrValue)) { intVector = vectorAtoi(explodeString(strValue, ";")); endIntVector = vectorAtoi(explodeString(endStrValue, ";")); if(intVector[0] && endIntVector[0] && intVector.size() == endIntVector.size()) { for(size_t i = 0, size = intVector.size(); i < size; ++i) { addEvent(moveEvent, intVector[i], m_uniqueIdMap, override); while(intVector[i] < endIntVector[i]) addEvent(new MoveEvent(moveEvent), ++intVector[i], m_uniqueIdMap, override); } } else std::clog << "[Warning - MoveEvents::registerEvent] Malformed entry (from unique: \"" << strValue << "\", to unique: \"" << endStrValue << "\")" << std::endl; } if(readXMLString(p, "actionid", strValue) || readXMLString(p, "aid", strValue)) { strVector = explodeString(strValue, ";"); for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it) { intVector = vectorAtoi(explodeString((*it), "-")); if(!intVector[0]) continue; addEvent(moveEvent, intVector[0], m_actionIdMap, override); if(intVector.size() > 1) { while(intVector[0] < intVector[1]) addEvent(new MoveEvent(moveEvent), ++intVector[0], m_actionIdMap, override); } } } if(readXMLString(p, "fromaid", strValue) && readXMLString(p, "toaid", endStrValue)) { intVector = vectorAtoi(explodeString(strValue, ";")); endIntVector = vectorAtoi(explodeString(endStrValue, ";")); if(intVector[0] && endIntVector[0] && intVector.size() == endIntVector.size()) { for(size_t i = 0, size = intVector.size(); i < size; ++i) { addEvent(moveEvent, intVector[i], m_actionIdMap, override); while(intVector[i] < endIntVector[i]) addEvent(new MoveEvent(moveEvent), ++intVector[i], m_actionIdMap, override); } } else std::clog << "[Warning - MoveEvents::registerEvent] Malformed entry (from action: \"" << strValue << "\", to action: \"" << endStrValue << "\")" << std::endl; } if(readXMLString(p, "pos", strValue) || readXMLString(p, "position", strValue)) { strVector = explodeString(strValue, ";"); for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it) { intVector = vectorAtoi(explodeString((*it), ",")); if(intVector.size() > 2) addEvent(moveEvent, Position(intVector[0], intVector[1], intVector[2]), m_positionMap, override); else success = false; } } return success; } void MoveEvents::addEvent(MoveEvent* moveEvent, int32_t id, MoveListMap& map, bool override) { MoveListMap::iterator it = map.find(id); if(it != map.end()) { EventList& moveEventList = it->second.moveEvent[moveEvent->getEventType()]; for(EventList::iterator it = moveEventList.begin(); it != moveEventList.end(); ++it) { if((*it)->getSlot() != moveEvent->getSlot()) continue; if(override) { delete *it; *it = moveEvent; } else std::clog << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << id << std::endl; return; } moveEventList.push_back(moveEvent); } else { MoveEventList moveEventList; moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent); map[id] = moveEventList; } } MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType) { MoveListMap::iterator it; if(item->getUniqueId()) { it = m_uniqueIdMap.find(item->getUniqueId()); if(it != m_uniqueIdMap.end()) { EventList& moveEventList = it->second.moveEvent[eventType]; if(!moveEventList.empty()) return *moveEventList.begin(); } } if(item->getActionId()) { it = m_actionIdMap.find(item->getActionId()); if(it != m_actionIdMap.end()) { EventList& moveEventList = it->second.moveEvent[eventType]; if(!moveEventList.empty()) return *moveEventList.begin(); } } it = m_itemIdMap.find(item->getID()); if(it != m_itemIdMap.end()) { EventList& moveEventList = it->second.moveEvent[eventType]; if(!moveEventList.empty()) return *moveEventList.begin(); } return NULL; } MoveEvent* MoveEvents::getEvent(Item* item, MoveEvent_t eventType, slots_t slot) { uint32_t slotp = 0; switch(slot) { case SLOT_HEAD: slotp = SLOTP_HEAD; break; case SLOT_NECKLACE: slotp = SLOTP_NECKLACE; break; case SLOT_BACKPACK: slotp = SLOTP_BACKPACK; break; case SLOT_ARMOR: slotp = SLOTP_ARMOR; break; case SLOT_RIGHT: slotp = SLOTP_RIGHT; break; case SLOT_LEFT: slotp = SLOTP_LEFT; break; case SLOT_LEGS: slotp = SLOTP_LEGS; break; case SLOT_FEET: slotp = SLOTP_FEET; break; case SLOT_AMMO: slotp = SLOTP_AMMO; break; case SLOT_RING: slotp = SLOTP_RING; break; default: break; } MoveListMap::iterator it = m_itemIdMap.find(item->getID()); if(it == m_itemIdMap.end()) return NULL; EventList& moveEventList = it->second.moveEvent[eventType]; for(EventList::iterator it = moveEventList.begin(); it != moveEventList.end(); ++it) { if(((*it)->getSlot() & slotp)) return *it; } return NULL; } void MoveEvents::addEvent(MoveEvent* moveEvent, Position pos, MovePosListMap& map, bool override) { MovePosListMap::iterator it = map.find(pos); if(it != map.end()) { bool add = true; if(!it->second.moveEvent[moveEvent->getEventType()].empty()) { if(!override) { std::clog << "[Warning - MoveEvents::addEvent] Duplicate move event found: " << pos << std::endl; add = false; } else it->second.moveEvent[moveEvent->getEventType()].clear(); } if(add) it->second.moveEvent[moveEvent->getEventType()].push_back(moveEvent); } else { MoveEventList moveEventList; moveEventList.moveEvent[moveEvent->getEventType()].push_back(moveEvent); map[pos] = moveEventList; } } MoveEvent* MoveEvents::getEvent(const Tile* tile, MoveEvent_t eventType) { MovePosListMap::iterator it = m_positionMap.find(tile->getPosition()); if(it == m_positionMap.end()) return NULL; EventList& moveEventList = it->second.moveEvent[eventType]; if(!moveEventList.empty()) return *moveEventList.begin(); return NULL; } bool MoveEvents::hasEquipEvent(Item* item) { MoveEvent* event = NULL; return (event = getEvent(item, MOVE_EVENT_EQUIP)) && !event->isScripted() && (event = getEvent(item, MOVE_EVENT_DE_EQUIP)) && !event->isScripted(); } bool MoveEvents::hasTileEvent(Item* item) { return getEvent(item, MOVE_EVENT_STEP_IN) || getEvent(item, MOVE_EVENT_STEP_OUT) || getEvent( item, MOVE_EVENT_ADD_TILEITEM) || getEvent(item, MOVE_EVENT_REMOVE_TILEITEM); } uint32_t MoveEvents::onCreatureMove(Creature* actor, Creature* creature, const Tile* fromTile, const Tile* toTile, bool isStepping) { MoveEvent_t eventType = MOVE_EVENT_STEP_OUT; const Tile* tile = fromTile; if(isStepping) { eventType = MOVE_EVENT_STEP_IN; tile = toTile; } Position fromPos; if(fromTile) fromPos = fromTile->getPosition(); Position toPos; if(toTile) toPos = toTile->getPosition(); uint32_t ret = 1; MoveEvent* moveEvent = NULL; if((moveEvent = getEvent(tile, eventType))) ret &= moveEvent->fireStepEvent(actor, creature, NULL, Position(), fromPos, toPos); Item* tileItem = NULL; if(m_lastCacheTile == tile) { if(m_lastCacheItemVector.empty()) return ret; //We cannot use iterators here since the scripts can invalidate the iterator for(uint32_t i = 0; i < m_lastCacheItemVector.size(); ++i) { if((tileItem = m_lastCacheItemVector[i]) && (moveEvent = getEvent(tileItem, eventType))) ret &= moveEvent->fireStepEvent(actor, creature, tileItem, tile->getPosition(), fromPos, toPos); } return ret; } m_lastCacheTile = tile; m_lastCacheItemVector.clear(); //We cannot use iterators here since the scripts can invalidate the iterator Thing* thing = NULL; for(int32_t i = tile->__getFirstIndex(), j = tile->__getLastIndex(); i < j; ++i) //already checked the ground { if(!(thing = tile->__getThing(i)) || !(tileItem = thing->getItem())) continue; if((moveEvent = getEvent(tileItem, eventType))) { m_lastCacheItemVector.push_back(tileItem); ret &= moveEvent->fireStepEvent(actor, creature, tileItem, tile->getPosition(), fromPos, toPos); } else if(hasTileEvent(tileItem)) m_lastCacheItemVector.push_back(tileItem); } return ret; } bool MoveEvents::onPlayerEquip(Player* player, Item* item, slots_t slot, bool isCheck) { if(MoveEvent* moveEvent = getEvent(item, MOVE_EVENT_EQUIP, slot)) return moveEvent->fireEquip(player, item, slot, isCheck); return true; } bool MoveEvents::onPlayerDeEquip(Player* player, Item* item, slots_t slot, bool isRemoval) { if(MoveEvent* moveEvent = getEvent(item, MOVE_EVENT_DE_EQUIP, slot)) return moveEvent->fireEquip(player, item, slot, isRemoval); return true; } uint32_t MoveEvents::onItemMove(Creature* actor, Item* item, Tile* tile, bool isAdd) { MoveEvent_t eventType = MOVE_EVENT_REMOVE_ITEM, tileEventType = MOVE_EVENT_REMOVE_TILEITEM; if(isAdd) { eventType = MOVE_EVENT_ADD_ITEM; tileEventType = MOVE_EVENT_ADD_TILEITEM; } uint32_t ret = 1; MoveEvent* moveEvent = getEvent(tile, eventType); if(moveEvent) ret &= moveEvent->fireAddRemItem(actor, item, NULL, tile->getPosition()); moveEvent = getEvent(item, eventType); if(moveEvent) ret &= moveEvent->fireAddRemItem(actor, item, NULL, tile->getPosition()); Item* tileItem = NULL; if(m_lastCacheTile == tile) { if(m_lastCacheItemVector.empty()) return ret; //We cannot use iterators here since the scripts can invalidate the iterator for(uint32_t i = 0; i < m_lastCacheItemVector.size(); ++i) { if((tileItem = m_lastCacheItemVector[i]) && tileItem != item && (moveEvent = getEvent(tileItem, tileEventType))) ret &= moveEvent->fireAddRemItem(actor, item, tileItem, tile->getPosition()); } return ret; } m_lastCacheTile = tile; m_lastCacheItemVector.clear(); //we cannot use iterators here since the scripts can invalidate the iterator Thing* thing = NULL; for(int32_t i = tile->__getFirstIndex(), j = tile->__getLastIndex(); i < j; ++i) //already checked the ground { if(!(thing = tile->__getThing(i)) || !(tileItem = thing->getItem()) || tileItem == item) continue; if((moveEvent = getEvent(tileItem, tileEventType))) { m_lastCacheItemVector.push_back(tileItem); ret &= moveEvent->fireAddRemItem(actor, item, tileItem, tile->getPosition()); } else if(hasTileEvent(tileItem)) m_lastCacheItemVector.push_back(tileItem); } return ret; } void MoveEvents::onAddTileItem(const Tile* tile, Item* item) { if(m_lastCacheTile != tile) return; std::vector<Item*>::iterator it = std::find(m_lastCacheItemVector.begin(), m_lastCacheItemVector.end(), item); if(it == m_lastCacheItemVector.end() && hasTileEvent(item)) m_lastCacheItemVector.push_back(item); } void MoveEvents::onRemoveTileItem(const Tile* tile, Item* item) { if(m_lastCacheTile != tile) return; for(uint32_t i = 0; i < m_lastCacheItemVector.size(); ++i) { if(m_lastCacheItemVector[i] != item) continue; m_lastCacheItemVector[i] = NULL; break; } } MoveEvent::MoveEvent(LuaInterface* _interface): Event(_interface) { m_eventType = MOVE_EVENT_NONE; stepFunction = NULL; moveFunction = NULL; equipFunction = NULL; slot = SLOTP_WHEREEVER; wieldInfo = 0; reqLevel = 0; reqMagLevel = 0; premium = false; } MoveEvent::MoveEvent(const MoveEvent* copy): Event(copy) { m_eventType = copy->m_eventType; stepFunction = copy->stepFunction; moveFunction = copy->moveFunction; equipFunction = copy->equipFunction; slot = copy->slot; if(copy->m_eventType == MOVE_EVENT_EQUIP) { wieldInfo = copy->wieldInfo; reqLevel = copy->reqLevel; reqMagLevel = copy->reqMagLevel; vocationString = copy->vocationString; premium = copy->premium; vocEquipMap = copy->vocEquipMap; } } MoveEvent::~MoveEvent() { // } std::string MoveEvent::getScriptEventName() const { switch(m_eventType) { case MOVE_EVENT_STEP_IN: return "onStepIn"; case MOVE_EVENT_STEP_OUT: return "onStepOut"; case MOVE_EVENT_EQUIP: return "onEquip"; case MOVE_EVENT_DE_EQUIP: return "onDeEquip"; case MOVE_EVENT_ADD_ITEM: return "onAddItem"; case MOVE_EVENT_REMOVE_ITEM: return "onRemoveItem"; default: break; } std::clog << "[Error - MoveEvent::getScriptEventName] No valid event type." << std::endl; return ""; } std::string MoveEvent::getScriptEventParams() const { switch(m_eventType) { case MOVE_EVENT_STEP_IN: case MOVE_EVENT_STEP_OUT: return "cid, item, position, lastPosition, fromPosition, toPosition, actor"; case MOVE_EVENT_EQUIP: case MOVE_EVENT_DE_EQUIP: return "cid, item, slot, boolean"; case MOVE_EVENT_ADD_ITEM: case MOVE_EVENT_REMOVE_ITEM: return "moveItem, tileItem, position, cid"; default: break; } std::clog << "[Error - MoveEvent::getScriptEventParams] No valid event type." << std::endl; return ""; } bool MoveEvent::configureEvent(xmlNodePtr p) { std::string strValue; int32_t intValue; if(readXMLString(p, "type", strValue) || readXMLString(p, "event", strValue)) { std::string tmpStrValue = asLowerCaseString(strValue); if(tmpStrValue == "stepin") m_eventType = MOVE_EVENT_STEP_IN; else if(tmpStrValue == "stepout") m_eventType = MOVE_EVENT_STEP_OUT; else if(tmpStrValue == "equip") m_eventType = MOVE_EVENT_EQUIP; else if(tmpStrValue == "deequip") m_eventType = MOVE_EVENT_DE_EQUIP; else if(tmpStrValue == "additem") m_eventType = MOVE_EVENT_ADD_ITEM; else if(tmpStrValue == "removeitem") m_eventType = MOVE_EVENT_REMOVE_ITEM; else { if(tmpStrValue == "function" || tmpStrValue == "buffer" || tmpStrValue == "script") std::clog << "[Error - MoveEvent::configureMoveEvent] No event type found." << std::endl; else std::clog << "[Error - MoveEvent::configureMoveEvent] Unknown event type \"" << strValue << "\"" << std::endl; return false; } if(m_eventType == MOVE_EVENT_EQUIP || m_eventType == MOVE_EVENT_DE_EQUIP) { if(readXMLString(p, "slot", strValue)) { std::string tmpStrValue = asLowerCaseString(strValue); if(tmpStrValue == "head") slot = SLOTP_HEAD; else if(tmpStrValue == "necklace") slot = SLOTP_NECKLACE; else if(tmpStrValue == "backpack") slot = SLOTP_BACKPACK; else if(tmpStrValue == "armor") slot = SLOTP_ARMOR; else if(tmpStrValue == "left-hand") slot = SLOTP_LEFT; else if(tmpStrValue == "right-hand") slot = SLOTP_RIGHT; else if(tmpStrValue == "hands" || tmpStrValue == "two-handed") slot = SLOTP_TWO_HAND; else if(tmpStrValue == "hand" || tmpStrValue == "shield") slot = SLOTP_RIGHT | SLOTP_LEFT; else if(tmpStrValue == "legs") slot = SLOTP_LEGS; else if(tmpStrValue == "feet") slot = SLOTP_FEET; else if(tmpStrValue == "ring") slot = SLOTP_RING; else if(tmpStrValue == "ammo" || tmpStrValue == "ammunition") slot = SLOTP_AMMO; else if(tmpStrValue == "pickupable") slot = SLOTP_RIGHT | SLOTP_LEFT | SLOTP_AMMO; else if(tmpStrValue == "wherever" || tmpStrValue == "any") slot = SLOTP_WHEREEVER; else std::clog << "[Warning - MoveEvent::configureMoveEvent] Unknown slot type \"" << strValue << "\"" << std::endl; } wieldInfo = 0; if(readXMLInteger(p, "lvl", intValue) || readXMLInteger(p, "level", intValue)) { reqLevel = intValue; if(reqLevel > 0) wieldInfo |= WIELDINFO_LEVEL; } if(readXMLInteger(p, "maglv", intValue) || readXMLInteger(p, "maglevel", intValue)) { reqMagLevel = intValue; if(reqMagLevel > 0) wieldInfo |= WIELDINFO_MAGLV; } if(readXMLString(p, "prem", strValue) || readXMLString(p, "premium", strValue)) { premium = booleanString(strValue); if(premium) wieldInfo |= WIELDINFO_PREMIUM; } StringVec vocStringVec; std::string error = ""; for(xmlNodePtr vocationNode = p->children; vocationNode; vocationNode = vocationNode->next) { if(!parseVocationNode(vocationNode, vocEquipMap, vocStringVec, error)) std::clog << "[Warning - MoveEvent::configureEvent] " << error << std::endl; } if(!vocEquipMap.empty()) wieldInfo |= WIELDINFO_VOCREQ; vocationString = parseVocationString(vocStringVec); } } else { std::clog << "[Error - MoveEvent::configureMoveEvent] No event type found." << std::endl; return false; } return true; } bool MoveEvent::loadFunction(const std::string& functionName) { std::string tmpFunctionName = asLowerCaseString(functionName); if(tmpFunctionName == "onstepinfield") stepFunction = StepInField; else if(tmpFunctionName == "onaddfield") moveFunction = AddItemField; else if(tmpFunctionName == "onequipitem") equipFunction = EquipItem; else if(tmpFunctionName == "ondeequipitem") equipFunction = DeEquipItem; else { std::clog << "[Warning - MoveEvent::loadFunction] Function \"" << functionName << "\" does not exist." << std::endl; return false; } m_scripted = EVENT_SCRIPT_FALSE; return true; } MoveEvent_t MoveEvent::getEventType() const { if(m_eventType == MOVE_EVENT_NONE) std::clog << "[Error - MoveEvent::getEventType] MOVE_EVENT_NONE" << std::endl; return m_eventType; } void MoveEvent::setEventType(MoveEvent_t type) { m_eventType = type; } uint32_t MoveEvent::StepInField(Creature* creature, Item* item) { if(MagicField* field = item->getMagicField()) { field->onStepInField(creature, creature->getPlayer()); return 1; } return LUA_ERROR_ITEM_NOT_FOUND; } uint32_t MoveEvent::AddItemField(Item* item) { if(MagicField* field = item->getMagicField()) { if(Tile* tile = item->getTile()) { if(CreatureVector* creatures = tile->getCreatures()) { for(CreatureVector::iterator cit = creatures->begin(); cit != creatures->end(); ++cit) field->onStepInField(*cit); } } return 1; } return LUA_ERROR_ITEM_NOT_FOUND; } bool MoveEvent::EquipItem(MoveEvent* moveEvent, Player* player, Item* item, slots_t slot, bool isCheck) { if(player->isItemAbilityEnabled(slot)) return true; if(!player->hasFlag(PlayerFlag_IgnoreEquipCheck) && moveEvent->getWieldInfo() != 0) { if(player->getLevel() < (uint32_t)moveEvent->getReqLevel() || player->getMagicLevel() < (uint32_t)moveEvent->getReqMagLv()) return false; if(moveEvent->isPremium() && !player->isPremium()) return false; if(!moveEvent->getVocEquipMap().empty() && moveEvent->getVocEquipMap().find(player->getVocationId()) == moveEvent->getVocEquipMap().end()) return false; } if(isCheck) return true; const ItemType& it = Item::items[item->getID()]; if(it.transformEquipTo) { Item* newItem = g_game.transformItem(item, it.transformEquipTo); g_game.startDecay(newItem); } player->setItemAbility(slot, true); if(it.abilities.invisible) { Condition* condition = Condition::createCondition((ConditionId_t)slot, CONDITION_INVISIBLE, -1, 0); player->addCondition(condition); } if(it.abilities.manaShield) { Condition* condition = Condition::createCondition((ConditionId_t)slot, CONDITION_MANASHIELD, -1, 0); player->addCondition(condition); } if(it.abilities.speed) g_game.changeSpeed(player, it.abilities.speed); if(it.abilities.conditionSuppressions) { player->setConditionSuppressions(it.abilities.conditionSuppressions, false); player->sendIcons(); } if(it.abilities.regeneration) { Condition* condition = Condition::createCondition((ConditionId_t)slot, CONDITION_REGENERATION, -1, 0); if(it.abilities.healthGain) condition->setParam(CONDITIONPARAM_HEALTHGAIN, it.abilities.healthGain); if(it.abilities.healthTicks) condition->setParam(CONDITIONPARAM_HEALTHTICKS, it.abilities.healthTicks); if(it.abilities.manaGain) condition->setParam(CONDITIONPARAM_MANAGAIN, it.abilities.manaGain); if(it.abilities.manaTicks) condition->setParam(CONDITIONPARAM_MANATICKS, it.abilities.manaTicks); player->addCondition(condition); } bool needUpdateSkills = false; for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if(it.abilities.skills[i]) { player->setVarSkill((skills_t)i, it.abilities.skills[i]); if(!needUpdateSkills) needUpdateSkills = true; } if(it.abilities.skillsPercent[i]) { player->setVarSkill((skills_t)i, (int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities.skillsPercent[i] - 100) / 100.f))); if(!needUpdateSkills) needUpdateSkills = true; } } if(needUpdateSkills) player->sendSkills(); bool needUpdateStats = false; for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) { if(it.abilities.stats[s]) { player->setVarStats((stats_t)s, it.abilities.stats[s]); if(!needUpdateStats) needUpdateStats = true; } if(it.abilities.statsPercent[s]) { player->setVarStats((stats_t)s, (int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities.statsPercent[s] - 100) / 100.f))); if(!needUpdateStats) needUpdateStats = true; } } if(needUpdateStats) player->sendStats(); return true; } bool MoveEvent::DeEquipItem(MoveEvent*, Player* player, Item* item, slots_t slot, bool isRemoval) { if(!player->isItemAbilityEnabled(slot)) return true; const ItemType& it = Item::items[item->getID()]; if(isRemoval && it.transformDeEquipTo) { g_game.transformItem(item, it.transformDeEquipTo); g_game.startDecay(item); } player->setItemAbility(slot, false); if(it.abilities.invisible) player->removeCondition(CONDITION_INVISIBLE, (ConditionId_t)slot); if(it.abilities.manaShield) player->removeCondition(CONDITION_MANASHIELD, (ConditionId_t)slot); if(it.abilities.speed) g_game.changeSpeed(player, -it.abilities.speed); if(it.abilities.conditionSuppressions) { player->setConditionSuppressions(it.abilities.conditionSuppressions, true); player->sendIcons(); } if(it.abilities.regeneration) player->removeCondition(CONDITION_REGENERATION, (ConditionId_t)slot); bool needUpdateSkills = false; for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { if(it.abilities.skills[i]) { needUpdateSkills = true; player->setVarSkill((skills_t)i, -it.abilities.skills[i]); } if(it.abilities.skillsPercent[i]) { needUpdateSkills = true; player->setVarSkill((skills_t)i, -(int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((it.abilities.skillsPercent[i] - 100) / 100.f))); } } if(needUpdateSkills) player->sendSkills(); bool needUpdateStats = false; for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s) { if(it.abilities.stats[s]) { needUpdateStats = true; player->setVarStats((stats_t)s, -it.abilities.stats[s]); } if(it.abilities.statsPercent[s]) { needUpdateStats = true; player->setVarStats((stats_t)s, -(int32_t)(player->getDefaultStats((stats_t)s) * ((it.abilities.statsPercent[s] - 100) / 100.f))); } } if(needUpdateStats) player->sendStats(); return true; } uint32_t MoveEvent::fireStepEvent(Creature* actor, Creature* creature, Item* item, const Position& pos, const Position& fromPos, const Position& toPos) { if(isScripted()) return executeStep(actor, creature, item, pos, fromPos, toPos); return stepFunction(creature, item); } uint32_t MoveEvent::executeStep(Creature* actor, Creature* creature, Item* item, const Position& pos, const Position& fromPos, const Position& toPos) { //onStepIn(cid, item, position, lastPosition, fromPosition, toPosition, actor) //onStepOut(cid, item, position, lastPosition, fromPosition, toPosition, actor) if(m_interface->reserveEnv()) { MoveEventScript::event = this; ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(creature->getPosition()); std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(creature) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); env->streamPosition(scriptstream, "position", pos, 0); env->streamPosition(scriptstream, "lastPosition", creature->getLastPosition(), 0); env->streamPosition(scriptstream, "fromPosition", fromPos, 0); env->streamPosition(scriptstream, "toPosition", toPos, 0); scriptstream << "local actor = " << env->addThing(actor) << std::endl; if(m_scriptData) scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << creature->getName() << " itemid: " << item->getID() << " - " << pos; env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(creature->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(creature)); LuaInterface::pushThing(L, item, env->addThing(item)); LuaInterface::pushPosition(L, pos, 0); LuaInterface::pushPosition(L, creature->getLastPosition(), 0); LuaInterface::pushPosition(L, fromPos, 0); LuaInterface::pushPosition(L, toPos, 0); lua_pushnumber(L, env->addThing(actor)); bool result = m_interface->callFunction(7); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - MoveEvent::executeStep] Call stack overflow." << std::endl; return 0; } } bool MoveEvent::fireEquip(Player* player, Item* item, slots_t slot, bool boolean) { if(isScripted()) return executeEquip(player, item, slot, boolean); return equipFunction(this, player, item, slot, boolean); } bool MoveEvent::executeEquip(Player* player, Item* item, slots_t slot, bool boolean) { //onEquip(cid, item, slot, boolean) //onDeEquip(cid, item, slot, boolean) if(m_interface->reserveEnv()) { MoveEventScript::event = this; ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(player->getPosition()); std::stringstream scriptstream; scriptstream << "local cid = " << env->addThing(player) << std::endl; env->streamThing(scriptstream, "item", item, env->addThing(item)); scriptstream << "local slot = " << slot << std::endl; scriptstream << "local boolean = " << (boolean ? "true" : "false") << std::endl; if(m_scriptData) scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; desc << player->getName() << " itemid: " << item->getID() << " slot: " << slot; env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(player->getPosition()); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); lua_pushnumber(L, env->addThing(player)); LuaInterface::pushThing(L, item, env->addThing(item)); lua_pushnumber(L, slot); lua_pushboolean(L, boolean); bool result = m_interface->callFunction(4); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - MoveEvent::executeEquip] Call stack overflow." << std::endl; return false; } } uint32_t MoveEvent::fireAddRemItem(Creature* actor, Item* item, Item* tileItem, const Position& pos) { if(isScripted()) return executeAddRemItem(actor, item, tileItem, pos); return moveFunction(item); } uint32_t MoveEvent::executeAddRemItem(Creature* actor, Item* item, Item* tileItem, const Position& pos) { //onAddItem(moveItem, tileItem, position, cid) //onRemoveItem(moveItem, tileItem, position, cid) if(m_interface->reserveEnv()) { MoveEventScript::event = this; ScriptEnviroment* env = m_interface->getEnv(); if(m_scripted == EVENT_SCRIPT_BUFFER) { env->setRealPos(pos); std::stringstream scriptstream; env->streamThing(scriptstream, "moveItem", item, env->addThing(item)); env->streamThing(scriptstream, "tileItem", tileItem, env->addThing(tileItem)); env->streamPosition(scriptstream, "position", pos, 0); scriptstream << "local cid = " << env->addThing(actor) << std::endl; if(m_scriptData) scriptstream << *m_scriptData; bool result = true; if(m_interface->loadBuffer(scriptstream.str())) { lua_State* L = m_interface->getState(); result = m_interface->getGlobalBool(L, "_result", true); } m_interface->releaseEnv(); return result; } else { #ifdef __DEBUG_LUASCRIPTS__ std::stringstream desc; if(tileItem) desc << "tileid: " << tileItem->getID(); desc << " itemid: " << item->getID() << " - " << pos; env->setEvent(desc.str()); #endif env->setScriptId(m_scriptId, m_interface); env->setRealPos(pos); lua_State* L = m_interface->getState(); m_interface->pushFunction(m_scriptId); LuaInterface::pushThing(L, item, env->addThing(item)); LuaInterface::pushThing(L, tileItem, env->addThing(tileItem)); LuaInterface::pushPosition(L, pos, 0); lua_pushnumber(L, env->addThing(actor)); bool result = m_interface->callFunction(4); m_interface->releaseEnv(); return result; } } else { std::clog << "[Error - MoveEvent::executeAddRemItem] Call stack overflow." << std::endl; return 0; } }
  11. function doPlayerGiveItem(cid, itemid, amount, subType) local item = 0 if(isItemStackable(itemid)) then item = doCreateItemEx(itemid, amount) if(doPlayerAddItemEx(cid, item, true) ~= RETURNVALUE_NOERROR) then return false end else for i = 1, amount do item = doCreateItemEx(itemid, subType) if(doPlayerAddItemEx(cid, item, true) ~= RETURNVALUE_NOERROR) then return false end end end return true end function doPlayerGiveItemContainer(cid, containerid, itemid, amount, subType) for i = 1, amount do local container = doCreateItemEx(containerid, 1) for x = 1, getContainerCapById(containerid) do doAddContainerItem(container, itemid, subType) end if(doPlayerAddItemEx(cid, container, true) ~= RETURNVALUE_NOERROR) then return false end end return true end function doPlayerTakeItem(cid, itemid, amount) return getPlayerItemCount(cid, itemid) >= amount and doPlayerRemoveItem(cid, itemid, amount) end function doPlayerBuyItem(cid, itemid, count, cost, charges) return doPlayerRemoveMoney(cid, cost) and doPlayerGiveItem(cid, itemid, count, charges) end function doPlayerBuyItemContainer(cid, containerid, itemid, count, cost, charges) return doPlayerRemoveMoney(cid, cost) and doPlayerGiveItemContainer(cid, containerid, itemid, count, charges) end function doPlayerSellItem(cid, itemid, count, cost) if(not doPlayerTakeItem(cid, itemid, count)) then return false end if(not doPlayerAddMoney(cid, cost)) then error('[doPlayerSellItem] Could not add money to: ' .. getPlayerName(cid) .. ' (' .. cost .. 'gp).') end return true end function doPlayerWithdrawMoney(cid, amount) if(not getBooleanFromString(getConfigInfo('bankSystem'))) then return false end local balance = getPlayerBalance(cid) if(amount > balance or not doPlayerAddMoney(cid, amount)) then return false end doPlayerSetBalance(cid, balance - amount) return true end function doPlayerDepositMoney(cid, amount) if(not getBooleanFromString(getConfigInfo('bankSystem'))) then return false end if(not doPlayerRemoveMoney(cid, amount)) then return false end doPlayerSetBalance(cid, getPlayerBalance(cid) + amount) return true end function doPlayerAddStamina(cid, minutes) return doPlayerSetStamina(cid, getPlayerStamina(cid) + minutes) end function isPremium(cid) return (isPlayer(cid) and (getPlayerPremiumDays(cid) > 0 or getBooleanFromString(getConfigValue('freePremium')))) end function getMonthDayEnding(day) if(day == "01" or day == "21" or day == "31") then return "st" elseif(day == "02" or day == "22") then return "nd" elseif(day == "03" or day == "23") then return "rd" end return "th" end function getMonthString(m) return os.date("%B", os.time{year = 1970, month = m, day = 1}) end function getArticle(str) return str:find("[AaEeIiOoUuYy]") == 1 and "an" or "a" end function isNumeric(str) return tonumber(str) ~= nil end function doNumberFormat(i) local str, found = string.gsub(i, "(%d)(%d%d%d)$", "%1,%2", 1), 0 repeat str, found = string.gsub(str, "(%d)(%d%d%d),", "%1,%2,", 1) until found == 0 return str end function doPlayerAddAddons(cid, addon) for i = 0, table.maxn(maleOutfits) do doPlayerAddOutfit(cid, maleOutfits[i], addon) end for i = 0, table.maxn(femaleOutfits) do doPlayerAddOutfit(cid, femaleOutfits[i], addon) end end function doPlayerWithdrawAllMoney(cid) return doPlayerWithdrawMoney(cid, getPlayerBalance(cid)) end function doPlayerDepositAllMoney(cid) return doPlayerDepositMoney(cid, getPlayerMoney(cid)) end function doPlayerTransferAllMoneyTo(cid, target) return doPlayerTransferMoneyTo(cid, target, getPlayerBalance(cid)) end function playerExists(name) return getPlayerGUIDByName(name) ~= nil end function getTibiaTime() local minutes, hours = getWorldTime(), 0 while (minutes > 60) do hours = hours + 1 minutes = minutes - 60 end return {hours = hours, minutes = minutes} end function doWriteLogFile(file, text) local f = io.open(file, "a+") if(not f) then return false end f:write("[" .. os.date("%d/%m/%Y %H:%M:%S") .. "] " .. text .. "\n") f:close() return true end function getExperienceForLevel(lv) lv = lv - 1 return ((50 * lv * lv * lv) - (150 * lv * lv) + (400 * lv)) / 3 end function doMutePlayer(cid, time) local condition = createConditionObject(CONDITION_MUTED) setConditionParam(condition, CONDITION_PARAM_TICKS, time == -1 and time or time * 1000) return doAddCondition(cid, condition) end function getPlayerGroupName(cid) return getGroupInfo(getPlayerGroupId(cid)).name end function getPlayerVocationName(cid) return getVocationInfo(getPlayerVocation(cid)).name end function getPromotedVocation(vid) return getVocationInfo(vid).promotedVocation end function doPlayerRemovePremiumDays(cid, days) return doPlayerAddPremiumDays(cid, -days) end function getPlayerMasterPos(cid) return getTownTemplePosition(getPlayerTown(cid)) end function getHouseOwner(houseId) return getHouseInfo(houseId).owner end function getHouseName(houseId) return getHouseInfo(houseId).name end function getHouseEntry(houseId) return getHouseInfo(houseId).entry end function getHouseRent(houseId) return getHouseInfo(houseId).rent end function getHousePrice(houseId) return getHouseInfo(houseId).price end function getHouseTown(houseId) return getHouseInfo(houseId).town end function getHouseDoorsCount(houseId) return table.maxn(getHouseInfo(houseId).doors) end function getHouseBedsCount(houseId) return table.maxn(getHouseInfo(houseId).beds) end function getHouseTilesCount(houseId) return table.maxn(getHouseInfo(houseId).tiles) end function getItemNameById(itemid) return getItemDescriptionsById(itemid).name end function getItemPluralNameById(itemid) return getItemDescriptionsById(itemid).plural end function getItemArticleById(itemid) return getItemDescriptionsById(itemid).article end function getItemName(uid) return getItemDescriptions(uid).name end function getItemPluralName(uid) return getItemDescriptions(uid).plural end function getItemArticle(uid) return getItemDescriptions(uid).article end function getItemText(uid) return getItemDescriptions(uid).text end function getItemSpecialDescription(uid) return getItemDescriptions(uid).special end function getItemWriter(uid) return getItemDescriptions(uid).writer end function getItemDate(uid) return getItemDescriptions(uid).date end function getTilePzInfo(pos) return getTileInfo(pos).protection end function getTileZoneInfo(pos) local tmp = getTileInfo(pos) if(tmp.pvp) then return 2 end if(tmp.nopvp) then return 1 end return 0 end function doShutdown() return doSetGameState(GAMESTATE_SHUTDOWN) end function doSummonCreature(name, pos, displayError) local displayError, cid = displayError or true, doCreateMonster(name, pos, false, false, displayError) if(not cid) then cid = doCreateNpc(name, pos, displayError) end return cid end function getOnlinePlayers() local players = {} for i, cid in ipairs(getPlayersOnline()) do table.insert(players, getCreatureName(cid)) end return players end function getPlayerByName(name) local cid = getCreatureByName(name) return isPlayer(cid) and cid or nil end function isPlayer(cid) return isCreature(cid) and cid >= AUTOID_PLAYERS and cid < AUTOID_MONSTERS end function isPlayerGhost(cid) return isPlayer(cid) and (getCreatureCondition(cid, CONDITION_GAMEMASTER, GAMEMASTER_INVISIBLE) or getPlayerFlagValue(cid, PLAYERFLAG_CANNOTBESEEN)) end function isMonster(cid) return isCreature(cid) and cid >= AUTOID_MONSTERS and cid < AUTOID_NPCS end function isNpc(cid) return isCreature(cid) and cid >= AUTOID_NPCS end function doPlayerSetExperienceRate(cid, value) return doPlayerSetRate(cid, SKILL__LEVEL, value) end function doPlayerSetMagicRate(cid, value) return doPlayerSetRate(cid, SKILL__MAGLEVEL, value) end function doPlayerAddLevel(cid, amount, round) local experience, level, amount = 0, getPlayerLevel(cid), amount or 1 if(amount > 0) then experience = getExperienceForLevel(level + amount) - (round and getPlayerExperience(cid) or getExperienceForLevel(level)) else experience = -((round and getPlayerExperience(cid) or getExperienceForLevel(level)) - getExperienceForLevel(level + amount)) end return doPlayerAddExperience(cid, experience) end function doPlayerAddMagLevel(cid, amount) for i = 1, amount do doPlayerAddSpentMana(cid, getPlayerRequiredMana(cid, getPlayerMagLevel(cid, true) + 1) - getPlayerSpentMana(cid), false) end return true end function doPlayerAddSkill(cid, skill, amount, round) local amount = amount or 1 if(skill == SKILL__LEVEL) then return doPlayerAddLevel(cid, amount, round) elseif(skill == SKILL__MAGLEVEL) then return doPlayerAddMagLevel(cid, amount) end for i = 1, amount do doPlayerAddSkillTry(cid, skill, getPlayerRequiredSkillTries(cid, skill, getPlayerSkillLevel(cid, skill) + 1) - getPlayerSkillTries(cid, skill), false) end return true end function getPartyLeader(cid) local party = getPartyMembers(cid) if(type(party) ~= 'table') then return 0 end return party[1] end function isInParty(cid) return type(getPartyMembers(cid)) == 'table' end function isPrivateChannel(channelId) return channelId >= CHANNEL_PRIVATE end function doPlayerResetIdleTime(cid) return doPlayerSetIdleTime(cid, 0) end function doBroadcastMessage(text, class) local class = class or MESSAGE_STATUS_WARNING if(type(class) == 'string') then local className = MESSAGE_TYPES[class] if(className == nil) then return false end class = className elseif(class < MESSAGE_FIRST or class > MESSAGE_LAST) then return false end for _, pid in ipairs(getPlayersOnline()) do doPlayerSendTextMessage(pid, class, text) end print("> Broadcasted message: \"" .. text .. "\".") return true end function doPlayerBroadcastMessage(cid, text, class, checkFlag, ghost) local checkFlag, ghost, class = checkFlag or true, ghost or false, class or TALKTYPE_BROADCAST if(checkFlag and not getPlayerFlagValue(cid, PLAYERFLAG_CANBROADCAST)) then return false end if(type(class) == 'string') then local className = TALKTYPE_TYPES[class] if(className == nil) then return false end class = className elseif(class < TALKTYPE_FIRST or class > TALKTYPE_LAST) then return false end for _, pid in ipairs(getPlayersOnline()) do doCreatureSay(cid, text, class, ghost, pid) end print("> " .. getCreatureName(cid) .. " broadcasted message: \"" .. text .. "\".") return true end function getBooleanFromString(input) local tmp = type(input) if(tmp == 'boolean') then return input end if(tmp == 'number') then return input > 0 end local str = string.lower(tostring(input)) return (str == "yes" or str == "true" or (tonumber(str) ~= nil and tonumber(str) > 0)) end function doCopyItem(item, attributes) local attributes = ((type(attributes) == 'table') and attributes or { "aid" }) local ret = doCreateItemEx(item.itemid, item.type) for _, key in ipairs(attributes) do local value = getItemAttribute(item.uid, key) if(value ~= nil) then doItemSetAttribute(ret, key, value) end end if(isContainer(item.uid)) then for i = (getContainerSize(item.uid) - 1), 0, -1 do local tmp = getContainerItem(item.uid, i) if(tmp.itemid > 0) then doAddContainerItemEx(ret, doCopyItem(tmp, true).uid) end end end return getThing(ret) end function doRemoveThing(uid) if(isCreature(uid)) then return doRemoveCreature(uid) end return doRemoveItem(uid) end function setAttackFormula(combat, type, minl, maxl, minm, maxm, min, max) local min, max = min or 0, max or 0 return setCombatFormula(combat, type, -1, 0, -1, 0, minl, maxl, minm, maxm, -min, -max) end function setHealingFormula(combat, type, minl, maxl, minm, maxm, min, max) local min, max = min or 0, max or 0 return setCombatFormula(combat, type, 1, 0, 1, 0, minl, maxl, minm, maxm, min, max) end function doChangeTypeItem(uid, subtype) local thing = getThing(uid) if(thing.itemid < 100) then return false end local subtype = subtype or 1 return doTransformItem(thing.uid, thing.itemid, subtype) end function doSetItemText(uid, text, writer, date) local thing = getThing(uid) if(thing.itemid < 100) then return false end doItemSetAttribute(uid, "text", text) if(writer ~= nil) then doItemSetAttribute(uid, "writer", tostring(writer)) if(date ~= nil) then doItemSetAttribute(uid, "date", tonumber(date)) end end return true end function doItemSetActionId(uid, aid) return doItemSetAttribute(uid, "aid", aid) end function getFluidSourceType(itemid) local item = getItemInfo(itemid) return item and item.fluidSource or false end function getDepotId(uid) return getItemAttribute(uid, "depotid") or false end function getItemDescriptions(uid) local thing = getThing(uid) if(thing.itemid < 100) then return false end local item = getItemInfo(thing.itemid) return { name = getItemAttribute(uid, "name") or item.name, plural = getItemAttribute(uid, "pluralname") or item.plural, article = getItemAttribute(uid, "article") or item.article, special = getItemAttribute(uid, "description") or "", text = getItemAttribute(uid, "text") or "", writer = getItemAttribute(uid, "writer") or "", date = getItemAttribute(uid, "date") or 0 } end function getItemWeightById(itemid, count, precision) local item, count, precision = getItemInfo(itemid), count or 1, precision or false if(not item) then return false end if(count > 100) then -- print a warning, as its impossible to have more than 100 stackable items without "cheating" the count print('[Warning] getItemWeightById', 'Calculating weight for more than 100 items!') end local weight = item.weight * count --[[if(precision) then return weight end local t = string.explode(tostring(weight), ".") if(table.maxn(t) == 2) then return tonumber(t[1] .. "." .. string.sub(t[2], 1, 2)) end]]-- return weight end function getItemWeaponType(uid) local thing = getThing(uid) if(thing.itemid < 100) then return false end return getItemInfo(thing.itemid).weaponType end function getItemRWInfo(uid) local thing = getThing(uid) if(thing.itemid < 100) then return false end local item, flags = getItemInfo(thing.itemid), 0 if(item.readable) then flags = 1 end if(item.writable) then flags = flags + 2 end return flags end function getItemLevelDoor(itemid) local item = getItemInfo(itemid) return item and item.levelDoor or false end function isContainer(uid) local thing = getThing(uid) return thing.uid > 0 and thing.items ~= nil end function isItemStackable(itemid) local item = getItemInfo(itemid) return item and item.stackable or false end function isItemRune(itemid) local item = getItemInfo(itemid) return item and item.type == ITEM_TYPE_RUNE or false end function isItemDoor(itemid) local item = getItemInfo(itemid) return item and item.type == ITEM_TYPE_DOOR or false end function isItemContainer(itemid) local item = getItemInfo(itemid) return item and item.group == ITEM_GROUP_CONTAINER or false end function isItemFluidContainer(itemid) local item = getItemInfo(itemid) return item and item.group == ITEM_GROUP_FLUID or false end function isItemMovable(itemid) local item = getItemInfo(itemid) return item and item.movable or false end function isCorpse(uid) local thing = getThing(uid) if(thing.itemid < 100) then return false end local item = getItemInfo(thing.itemid) return item and item.corpseType ~= 0 or false end function getContainerCapById(itemid) local item = getItemInfo(itemid) if(not item or item.group ~= 2) then return false end return item.maxItems end function getMonsterAttackSpells(name) local monster = getMonsterInfo(name) return monster and monster.attacks or false end function getMonsterHealingSpells(name) local monster = getMonsterInfo(name) return monster and monster.defenses or false end function getMonsterLootList(name) local monster = getMonsterInfo(name) return monster and monster.loot or false end function getMonsterSummonList(name) local monster = getMonsterInfo(name) return monster and monster.summons or false end function choose(...) local arg = {...} return arg[math.random(1, table.maxn(arg))] end
  12. Buenas a todos, eu uso TFS 0.4, rev 4393 (8.71), eu gostaria que o NPC ao vender algo na loja, consultasse se o main backpack (a que fica no inventário) tem slot disponível pra concluir a venda, se não ele não vende e avisa o motivo. Eu acredito que seja no "sendGoods()" em algum desses arquivos a seguir, porém eu não sei fazer, alguém consegue me ajudar? PLAYER.CPP void Player::updateInventoryGoods(uint32_t itemId) { if(Item::items[itemId].worth) { sendGoods(); return; } for(ShopInfoList::iterator it = shopOffer.begin(); it != shopOffer.end(); ++it) { if(it->itemId != itemId) continue; sendGoods(); break; } } NPC.CPP (provavelmente na penúltima linha desse) void Npc::onPlayerTrade(Player* player, ShopEvent_t type, int32_t callback, uint16_t itemId, uint8_t count, uint8_t amount, bool ignore/* = false*/, bool inBackpacks/* = false*/) { if(type == SHOPEVENT_BUY) { if(NpcState* npcState = getState(player, true)) { npcState->amount = amount; npcState->subType = count; npcState->itemId = itemId; npcState->buyPrice = getListItemPrice(itemId, SHOPEVENT_BUY); npcState->ignore = ignore; npcState->inBackpacks = inBackpacks; const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_SHOPBUY); executeResponse(player, npcState, response); } } else if(type == SHOPEVENT_SELL) { if(NpcState* npcState = getState(player, true)) { npcState->amount = amount; npcState->subType = count; npcState->itemId = itemId; npcState->sellPrice = getListItemPrice(itemId, SHOPEVENT_SELL); npcState->ignore = ignore; const NpcResponse* response = getResponse(player, npcState, EVENT_PLAYER_SHOPSELL); executeResponse(player, npcState, response); } } if(m_npcEventHandler) m_npcEventHandler->onPlayerTrade(player, callback, itemId, count, amount, ignore, inBackpacks); player->sendGoods(); } PROTOCOLGAME.CPP void ProtocolGame::sendGoods(const ShopInfoList& shop) { NetworkMessage_ptr msg = getOutputBuffer(); if(msg) { TRACK_MESSAGE(msg); msg->put<char>(0x7B); msg->put<uint32_t>((uint32_t)g_game.getMoney(player)); std::map<uint32_t, uint32_t> goodsMap; if(shop.size() >= 5) { for(ShopInfoList::const_iterator sit = shop.begin(); sit != shop.end(); ++sit) { if(sit->sellPrice < 0) continue; int8_t subType = -1; if(sit->subType) { const ItemType& it = Item::items[sit->itemId]; if(it.hasSubType() && !it.stackable) subType = sit->subType; } uint32_t count = player->__getItemTypeCount(sit->itemId, subType); if(count > 0) goodsMap[sit->itemId] = count; } } else { std::map<uint32_t, uint32_t> tmpMap; player->__getAllItemTypeCount(tmpMap); for(ShopInfoList::const_iterator sit = shop.begin(); sit != shop.end(); ++sit) { if(sit->sellPrice < 0) continue; int8_t subType = -1; const ItemType& it = Item::items[sit->itemId]; if(sit->subType && it.hasSubType() && !it.stackable) subType = sit->subType; if(subType != -1) { uint32_t count = subType; if(!it.isFluidContainer() && !it.isSplash()) count = player->__getItemTypeCount(sit->itemId, subType); if(count > 0) goodsMap[sit->itemId] = count; else goodsMap[sit->itemId] = 0; } else goodsMap[sit->itemId] = tmpMap[sit->itemId]; } } msg->put<char>(std::min(goodsMap.size(), (size_t)255)); std::map<uint32_t, uint32_t>::const_iterator it = goodsMap.begin(); for(uint32_t i = 0; it != goodsMap.end() && i < 255; ++it, ++i) { msg->putItemId(it->first); msg->put<char>(std::min(it->second, (uint32_t)255)); } } }
  13. Uso tfs 0.4 Rev 4393 (v8.7), eu estou procurando e até agora não encontrei as duas coisas que procuro: 1° - Gostaria de um sistema em que o npc aceite outras moedas além dos gold pieces, achei uns mas não é bem o que procuro, eu queria que ao colocar um item a venda no npc, eu coloco qual tipo de moeda de troca é(por exemplo, um cooper shield) e a quantidade, e na janela de trade aparece que a moeda de troca é um cooper shield por aquele item específico. 2° - Gostaria que ao comprar um item do npc, ele não vá para bags por exemplo, apenas para backpacks, mesmo tendo espaço na bag, a transação não será efetuada
  14. Desculpa reviver depois de tanto tempo, mas, os kills contam de todos do servidor ou cada player tem uma contagem?
  15. Eu uso tfs 0.4 E gostaria de saber se da pra fazer o seguinte: Quando um número X de um monster Y for morto no servidor, uma Raid Z acontece no servidor, por exemplo: Ao ser morto 10.000 dragons no servidor (conta os kills de todos os players do servidor) a Raid de Demodras acontece **(conta os dragons, Dragon lords e os hatchlings)** Desde já agradeço a quem puder ajudar.
  16. HSinhori postou uma resposta no tópico em Suporte Tibia OTServer
    Bom, eu to procurando na net mas não estou encontrando (talvez por não saber as palavras chaves corretas), mas eu queria colocar no OTClient, que de acordo com a vocation do player, X skills apareçam e Y não, uso TFS 0.4 8.70 e o OTClient atual, como devo proceder para funcionar, alguém saberia me ajudar ou me indicar um tópico com tais informações?
  17. Eu fiz todas as edições nas mesmas versões (rev e otc) e não aconteceu nada de diferente, continua tudo igual, sabe dizer pq? eu tenho que ativar de alguma forma? Alguém?
  18. Buenas galera do TK, to com um probleminha nas portas quando faço o uso de chaves, é o seguinte: - Tanto a porta quanto a chave tem o mesmo action ID 100 adicionados pelo remeres mapeditor - A porta vem trancada e quando uso a chave nela ela abre normalmente, até ai esta tudo certo - Quando tento fechar com a chave, nada acontece << aqui ja esta errado - Quando fecho "manualmente" usando o botao direito direto na porta ela fecha e ja tranca << aqui tbm está errado pois gostaria que fosse igual ao global Essa é minha doors.lua se alguém souber me ajudar: local function checkStackpos(item, position) position.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE local thing = getThingFromPos(position) position.stackpos = STACKPOS_TOP_FIELD local field = getThingFromPos(position) return (item.uid == thing.uid or thing.itemid < 100 or field.itemid == 0) end local function doorEnter(cid, item, toPosition) doTransformItem(item.uid, item.itemid + 1) doTeleportThing(cid, toPosition) end function onUse(cid, item, fromPosition, itemEx, toPosition) if(fromPosition.x ~= CONTAINER_POSITION and isPlayerPzLocked(cid) and getTileInfo(fromPosition).protection) then doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) return true end if(getItemLevelDoor(item.itemid) > 0) then if(item.actionid == 189) then if(not isPremium(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end doorEnter(cid, item, toPosition) return true end local gender = item.actionid - 186 if(isInArray({PLAYERSEX_FEMALE, PLAYERSEX_MALE, PLAYERSEX_GAMEMASTER}, gender)) then if(gender ~= getPlayerSex(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end doorEnter(cid, item, toPosition) return true end local skull = item.actionid - 180 if(skull >= SKULL_NONE and skull <= SKULL_BLACK) then if(skull ~= getCreatureSkullType(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end doorEnter(cid, item, toPosition) return true end local group = item.actionid - 150 if(group >= 0 and group < 30) then if(group > getPlayerGroupId(cid)) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end doorEnter(cid, item, toPosition) return true end local vocation = item.actionid - 100 if(vocation >= 0 and vocation < 50) then local playerVocationInfo = getVocationInfo(getPlayerVocation(cid)) if(playerVocationInfo.id ~= vocation and playerVocationInfo.fromVocation ~= vocation) then doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") return true end doorEnter(cid, item, toPosition) return true end if(item.actionid == 190 or (item.actionid ~= 0 and getPlayerLevel(cid) >= (item.actionid - getItemLevelDoor(item.itemid)))) then doorEnter(cid, item, toPosition) else doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "Only the worthy may pass.") end return true end if(isInArray(specialDoors, item.itemid)) then if(item.actionid == 100 or (item.actionid ~= 0 and getPlayerStorageValue(cid, item.actionid) > 0)) then doorEnter(cid, item, toPosition) else doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "The door seems to be sealed against unwanted intruders.") end return true end if(isInArray(keys, item.itemid)) then if(itemEx.actionid > 0) then if(item.actionid == itemEx.actionid and doors[itemEx.itemid] ~= nil) then doTransformItem(itemEx.uid, doors[itemEx.itemid]) return true end doPlayerSendCancel(cid, "The key does not match.") return true end return false end if(isInArray(horizontalOpenDoors, item.itemid) and checkStackpos(item, fromPosition)) then local newPosition = toPosition newPosition.y = newPosition.y + 1 local doorPosition = fromPosition doorPosition.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE local doorCreature = getThingfromPos(doorPosition) if(doorCreature.itemid ~= 0) then local pzDoorPosition = getTileInfo(doorPosition).protection local pzNewPosition = getTileInfo(newPosition).protection if((pzDoorPosition and not pzNewPosition and doorCreature.uid ~= cid) or (not pzDoorPosition and pzNewPosition and doorCreature.uid == cid and isPlayerPzLocked(cid))) then doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) else doTeleportThing(doorCreature.uid, newPosition) if(not isInArray(closingDoors, item.itemid)) then doTransformItem(item.uid, item.itemid - 1) end end return true end doTransformItem(item.uid, item.itemid - 1) return true end if(isInArray(verticalOpenDoors, item.itemid) and checkStackpos(item, fromPosition)) then local newPosition = toPosition newPosition.x = newPosition.x + 1 local doorPosition = fromPosition doorPosition.stackpos = STACKPOS_TOP_MOVEABLE_ITEM_OR_CREATURE local doorCreature = getThingfromPos(doorPosition) if(doorCreature.itemid ~= 0) then if(getTileInfo(doorPosition).protection and not getTileInfo(newPosition).protection and doorCreature.uid ~= cid) then doPlayerSendDefaultCancel(cid, RETURNVALUE_NOTPOSSIBLE) else doTeleportThing(doorCreature.uid, newPosition) if(not isInArray(closingDoors, item.itemid)) then doTransformItem(item.uid, item.itemid - 1) end end return true end doTransformItem(item.uid, item.itemid - 1) return true end if(doors[item.itemid] ~= nil and checkStackpos(item, fromPosition)) then if(item.actionid == 0) then doTransformItem(item.uid, doors[item.itemid]) else doPlayerSendTextMessage(cid, MESSAGE_INFO_DESCR, "It is locked.") end return true end return false end
  19. não sei como eh teu site e tbm não baixei esse arquivo, mas creio ter um arquivo chamado "download" onde vai ter onde colocar o link, se n encontrar manda site completo
  20. Opa, eu tenho esse botão do purse no otclient (8.60) purseButton = inventoryPanel:getChildById('purseButton') local function purseFunction() local purse = g_game.getLocalPlayer():getInventoryItem(InventorySlotPurse) if purse then g_game.use(purse) end end purseButton.onClick = purseFunction eu estou tentando fazer com que ao clicar nele ele abra uma janela (battle list, vip, inventory), mas não sei como fazer, o máximo que consegui ate agora é abrir uma bp em um dos slots, alguem poderia me ajudar? Eu sei que eh na parte do "local purse = g_game..." mas eu não sei que codigo usar ali Pra quem não entendeu é o seguinte: 1º - Ja tenho o botao na janela do inventario 2º - Ao clicar nele, ele teria que abrir uma outra janela
  21. Não sei se estou no local correto mas, estou procurando um tutorial de como compilar o objectbuilder e oque usar para o mesmo, será que alguém poderia me ajudar? na fonte no github quando clico em compiling volta a pagina principal da fonte
  22. function onLogin(cid) local strg = getPlayerStorageValue(cid, 11090) local strgname = getPlayerStorageValue(cid, 11091) local strgValue = 0 local name = getPlayerName(cid) if (strg < 1) then doCreatureSetStorage(cid, 11090, 1) else if strgname == "" then strgname = name doCreatureSetStorage(cid, 11090, name) end db.executeQuery("UPDATE `players` SET `name` = '"..strgname.."' WHERE `id` = "..getPlayerGUID(cid)..";") db.executeQuery("UPDATE `players` SET `name` = '"..strgname.." ["..strg.."]' WHERE `id` = "..getPlayerGUID(cid)..";") strgValue = strg + 1 doCreatureSetStorage(cid, 11090, strgValue) end doPlayerSave(cid, true) return true end Eu tenho um ot de war em que é permitido entrar no mesmo personagem ao mesmo tempo, mas isso buga o "exura sio" e o "exiva", então tentei fazer esse script pra que ao entrar no boneco seja adicionado um numero após o nome "Nome [1]... Nome [2]... e assim por em diante", mas não obtive sucesso, o que eu consegui de outra forma que não esse script foi adicionar o numero um varias vezes, ficando o nome do personagem assim "Nome [1] [1] [1]", alguém poderia me ajudar? Esse script não apresenta erro na distro em momento algum porém também não funciona
  23. Se eu quisesse por esse botão dentro da própria janela de skills ou dentro do inventário pra ter outra função, seria difícil?
  24. Eu uso o tfs 0.4 editado pelo luanluciano93 --> TibiaKing Eu gostaria que alguém me ajuda-se a fazer com que o NPC vende-se um item especifico apenas se o player estiver com uma BP especifica no slot correto no inventario, vou tentar em tópicos: 1 - NPC só vende a potion pro player se ele estiver com a golden backpack no slot (Ex: 19) do inventário 2 - Somente esse item pode ir pra essa backpack, qualquer outro vai pra bp normal 3 - Se o player estiver com outra golden backpack em qualquer outro lugar, isso não conta, mesmo que a BP correta esteja cheia 4 - Se por ventura a BP correta esteja cheia, o item não pode ser comprado 5 - E obviamente se o player estiver com espaço para apenas 30 potions e quiser comprar 100, será comprado apenas 30 e cobrando o valor correto Lembrando que nesse otserv, o sistema do NPC é pela janelinha de trade

Informação Importante

Confirmação de Termo