Atualmente - Versão 0.3.2.1
Atualizada em: 05/09/07 - 12:30
SVN
ChangeLog da v0.1 pra 0.2b:
Arrumados alguns bugs.
Opção de Teleport.
Opção de Mensagem.
Melhora nos items.
Descrição especial nos items.
E mais algumas coisas.
ChangeLog da v0.2 pra 0.3.2:
Re-Escrito.
Limpo (tem muito pra limpa ainda...)
É possivel por os items de recompensa dentro de containers agora (um bom numero). Respeite sempre a capacidade do container, eu não coloquei nenhum check pra isso...
Colocar o ActionID do item.
E mais algumas coisas..
-------------------------------------------------------------------
Olá de novo. Como eu disse eu ia tentar fazer, não está completo ainda mas a base principal ta feita. O dovia do Vitor disse que ia re-escreve, mas nao fez nada então eu refiz. Eu tava tentando testa meu C++ (que ta horrivel como todos podem ver), mas até que saiu a bagaça
Todo:
Limpeza?
Achar e corrigir bugs.
Melhorar as listas que eu fiz uma zona....
Definir que os players só podem realizar a quest 1x. [done]
Melhorar Containers [done]
Teleports [done]
Setar ActionID nas recompensas [done]
Opção de movimentar (moveable) [done]
Bom, não está completa ainda, então ela TEM bugs
Features:
Nome da quest: quando o player der look no item, ele vai ver o nome da quest (pode ser setado no xml o nome lógico e você pode ligar/desligar essa opção por lá também).
Quest ID: é a chave das nossas quests, devem ser sempre unicas a não ser que ela tenha segmentos.
Segment: é uma parte da quest, você pode ter X segmentos dentro da quest principal. sempre seguindo em ordem crescente.
X, Y, Z: É a localização do item no map, pode ser QUALQUER ITEM.
Moveable: Se vai poder mover ou não o item (DEFAULT = 1 = MOVEABLE, 0 = NOTMOVEABLE - não pode mover)
backpack: Pode dar items dentro de containers, como o Tibia da Cip. Foi mudado:
*Você tem que por o id da backpack, se não por, ela vai usa o id padrão.
*Quando você por pra dar o item dentro de uma bp, vai ligar essa opção automaticamente no id padrão (bp marrom).
Type: pode ser o id do item ou o nome dele. Preste atenção pra items que tem o mesmo nome...
Desc: Adiciona uma descrição especial ao item.
Ex: 19:28 You see a boots of haste.It weighs 7.5 oz. It was made by and old and unknow guy. A rare masterpiece.
Message: Manda uma mensagem pro player quando ele 'realiza a quest'.
Teleport: teleporta o player quando ele 'realiza a quest'.
ActionId: define uma actionID pro item de recompensa (cada recompensa pode ter o seu).
Container ID: vai dar o item definido em um container que você deve ter posto acima:
Ex:
<backpack type="1988"/><backpack type="1995"/>
<item type="warrior helmet" desc="It was made by and old and unknow guy. A rare masterpiece." containerId="2"/>
O warrior helmet vai ir no container 2 (o de id 1995)
Eu acho que é tudo, vamo pro code.
-------------------------------------------------------------------
Actions.cpp
Depois de:
#include "actions.h"
Adicione:
#ifdef _QUEST_XML_ #include "quest.h" extern Quests quests; #endif
Na função:
ReturnValue Actions::internalUseItem(Player* player, const Position& pos, uint8_t index, Item* item)
Depois de:
if(Door* door = item->getDoor()){if(!door->canUse(player)){
return RET_CANNOTUSETHISOBJECT;
}
}
Adicione:
#ifdef _QUEST_XML_ Quests* quest = quests.getQuest(item->getPosition()); if(quest && item && item->quest){ std::stringstream message; int32_t value; player->getStorageValue((uint32_t)quest->id, value); if(value){ if(value >= quest->sgId){ player->sendTextMessage(MSG_INFO_DESCR, "Its empty."); return RET_NOERROR; } if(value != quest->sgId-1){ player->sendTextMessage(MSG_INFO_DESCR, "You are not prepared yet."); return RET_NOERROR; } } else{ if(quest->sgId != 1){ player->sendTextMessage(MSG_INFO_DESCR, "You are not prepared yet."); return RET_NOERROR; } } Container* backpack = NULL; Container* rewarded = NULL; std::list<Container*> listContainer;//Vo usar no rewarded. std::list<Container*> adicionar; std::list<Container*>::iterator Adding; std::list<Item*> removel; //I couldnt find any other way... bool error = false; ReturnValue ret = RET_NOERROR, ret2 = RET_NOERROR, ret3 = RET_NOERROR, ret4 = RET_NOERROR; int nova=1; int bpcids; if(quest->bId != 0 || quest->containers != 0){ backpack = Item::CreateItem(quest->bId != 0 ? quest->bId : 1988)->getContainer(); removel.push_back(backpack); } RewardList2::iterator get = quest->rewardList.begin(); if(quest->containers != 0){ int atd; for(atd = 0; atd < quest->containers; atd++){ rewarded = Item::CreateItem(quest->bpType[atd])->getContainer(); if(rewarded) { listContainer.push_back(rewarded); } } } for(get; get != quest->rewardList.end(); ++get){ Item* reward = Item::CreateItem((*get).first->getID(), (*get).first->getItemCountOrSubtype()); if(reward){ if((*get).first->getSpecialDescription() != "") reward->setSpecialDescription((*get).first->getSpecialDescription()); if((*get).first->getActionId() != 0) reward->setActionId((*get).first->getActionId()); } if((*get).second != 0){ Adding = listContainer.begin(); int add=0; while(Adding != listContainer.end()){ add++; if((*get).second == add){ Container* toAdd = (*Adding); toAdd->__addThing(reward); } Adding++; } } else if(backpack) backpack->__addThing(reward); else{ ret = g_game.internalAddItem(player, reward); //Nao da pra usar o test, pois ele não ocupa o slot... if(ret != RET_NOERROR) error = true; else{ removel.push_back(reward); message << "You have found " << reward->getDescription(2); player->sendTextMessage(MSG_INFO_DESCR, message.str().c_str()); } } } if(backpack){ for(Adding = listContainer.begin(); Adding != listContainer.end(); Adding++){ Container* toAdd2 = (*Adding); backpack->__addThing(toAdd2); } ret2 = g_game.internalAddItem(player, backpack, INDEX_WHEREEVER, 0, true); if(ret2 == RET_NOERROR){ g_game.internalAddItem(player, backpack); message << "You have found " << backpack->getDescription(2); player->sendTextMessage(MSG_INFO_DESCR, message.str().c_str()); } else return ret2; } if(error){//Then we Add it... for(std::list<Item*>::iterator remove = removel.begin(); remove != removel.end(); ++remove){ ret3 = g_game.internalRemoveItem((*remove), (*remove)->getItemCount(), true);//Testing befre, if we get an error it could crash the server if(ret3 == RET_NOERROR) g_game.internalRemoveItem((*remove), (*remove)->getItemCount()); else{//Ok, this should not happen... std::stringstream bug; bug << "Atention!!! Player: "<<player->getName()<<" bugged the quest: "<<quest->name<<"."; for(AutoList<Player>::listiterator it = Player::listPlayer.list.begin(); it != Player::listPlayer.list.end(); ++it){ if((*it).second && (*it).second->getAccessLevel() > 0) (*it).second->sendTextMessage(MSG_STATUS_WARNING, bug.str().c_str()); } player->sendTextMessage(MSG_STATUS_WARNING, "You have bugged the quest, we are contacting a gm now (you should do it also)"); return ret3; } } return RET_CANNOTUSETHISOBJECT; } player->addStorageValue(quest->id, quest->sgId); if(quest->teleport.x != 0xFFFF){ if(g_game.internalTeleport(player,quest->teleport) == RET_NOERROR) g_game.addMagicEffect(quest->teleport, NM_ME_ENERGY_AREA); } if(quest->questDesc != "") player->sendTextMessage(MSG_INFO_DESCR, quest->questDesc); return ret; } #endif
Actions.h
Depois de:
LuaScriptInterface m_scriptInterface;
Adcione:
#ifdef _QUEST_XML_ typedef std::list<std::pair<Item*, int> > RewardList2; typedef RewardList2::value_type RewardList2_Pair; RewardList2 rewardList2; #endif
Otserv.cpp
Depois de:
#include "raids.h"
Adicione:
#ifdef _QUEST_XML_ #include "quest.h" Quests quests; #endif
Depois de:
Raids::getInstance()->startup();
Adicione:
#ifdef _QUEST_XML_ std::cout << ":: Loading The Chaos Style Quest System "; if(!quests.loadQuests()) std::cout << ":: Unable to load!"; else std::cout << "[done]" << std::endl; #endif
Game.cpp:
Na função:
ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, int32_t index,Item* item, uint32_t count, uint32_t flags /*= 0*/)
{
Depois de:
if(item == toItem){return RET_NOERROR; //silently ignore move
}
Adicione:
#ifdef _QUEST_XML_ //Nova linha if(toItem && toItem->quest && item->getID() == toItem->getID() && item->isStackable()) return RET_CANNOTTHROW; #endif
Item.h
Mude:
bool isNotMoveable() const {return !items[id].moveable;}
Para:
#ifndef _QUEST_XML_ bool isNotMoveable() const {return !items[id].moveable;} #else bool isNotMoveable() const; #endif
Item.cpp:
Na função:
Item::Item(const unsigned short _type, unsigned short _count /*= 0*/) :ItemAttributes()
Depois de:
count = 1;
Adicione:
#ifdef _QUEST_XML_ quest = false; questmoveable = true; #endif
Na função:
Item::Item(const Item &i) :Thing(), ItemAttributes()
{
Depois de:
fluid = i.fluid;
Adicione:
#ifdef _QUEST_XML_ quest = false; questmoveable = true; #endif
No fim do arquivo adicione:
#ifdef _QUEST_XML_ bool Item::isNotMoveable() const{ if(quest && !questmoveable) return true; return !items[id].moveable; } #endif
Agora crie 2 arquivos no seu projeto:
quest.cpp
#ifdef _QUEST_XML_ ////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game ////////////////////////////////////////////////////////////////////// // Quests system loaded on XML base - By The Chaos // Thanks to LooS!k for help also. ////////////////////////////////////////////////////////////////////// // This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ////////////////////////////////////////////////////////////////////// #include <string> #include <sstream> #include "quest.h" #include "configmanager.h" #include "game.h" extern Game g_game; extern ConfigManager g_config; Quests::Quests(){ id = 0; sgId = 1; containers = 0; } bool Quests::loadQuests() { std::string filename = g_config.getString(ConfigManager::DATA_DIRECTORY) + "quests.xml"; xmlDocPtr doc; doc = xmlParseFile(filename.c_str()); std::stringstream desc; int itId, itCount, bpId; std::string Ident; std::string strCmd; int intValue; if( doc ) { xmlNodePtr root,p; root = xmlDocGetRootElement(doc); if( xmlStrcmp(root->name,(const xmlChar*) "quests") != 0 ) { std::cout << "- Problem: Check the quests.xml file."<< std::endl; return false; } p = root->children; while( p ) { if( xmlStrcmp(p->name, (const xmlChar*) "quest") == 0 ) { Quests* quest = new Quests; if( readXMLString(p, "name", strCmd) ) { quest->name = strCmd; } else { std::cout << "missing name for quest..." << std::endl; strCmd = "unknown"; } if( readXMLInteger(p, "showname", intValue) ) { quest->showName = (bool)intValue; } else { quest->showName = false; } if( readXMLInteger(p,"questId",intValue) ) { quest->id = intValue; } else { std::cout << "Error: Missing questId for quest: " << quest->name << std::endl; return false; } if( readXMLInteger(p,"segment",intValue) ) { quest->sgId = intValue; } else { std::cout << "Error: Missing segment value for quest: " << quest->name << std::endl; return false; } if( readXMLInteger(p,"x",intValue) ) { quest->pos.x = intValue; } else { std::cout << "Error: Missing X Value for quest: " << quest->name << std::endl; return false; } if( readXMLInteger(p,"y",intValue) ) { quest->pos.y = intValue; } else { std::cout << "Error: Missing Y Value for quest: " << quest->name << std::endl; return false; } if( readXMLInteger(p,"z",intValue) ) { quest->pos.z = intValue; } else { std::cout << "Error: Missing Z Value for quest: " << quest->name << std::endl; return false; } if( readXMLInteger(p,"backpack",intValue) ) { quest->bId = intValue; } else { quest->bId = 0; } Tile* thatTile = g_game.getTile(quest->pos.x, quest->pos.y, quest->pos.z); if( !thatTile ) { std::cout << "Error: Tile not found on quest: "<< quest->name << std::endl; return false; } Item *thatItem = thatTile->getTopThing()->getItem(); if( !thatItem ) { std::cout << "Error: Internal error - check map on: " << quest->pos.x << "/" << quest->pos.y <<"/"<< quest->pos.z << std::endl; return false; } thatItem->quest = true; if( readXMLInteger(p,"moveable",intValue) ) { thatItem->questmoveable = (bool)intValue; } else { thatItem->questmoveable = true; } if( quest->name != "unknown" && quest->showName ) { desc << " It seems to be sealed somehow. You can read "<< quest->name << " on it."; thatItem->setSpecialDescription(desc.str()); desc.str(""); } xmlNodePtr tmp = p->children; while( tmp ) { if( xmlStrcmp(tmp->name, (const xmlChar*) "reward") == 0 ) { xmlNodePtr slot = tmp->children; while( slot ) { if( xmlStrcmp(slot->name, (const xmlChar*)"item") == 0 ) { itId = 0, itCount = 0; if( readXMLString(slot,"type",strCmd) ) { Ident = strCmd; } if( isdigit(Ident[0]) ) { itId = atoi(Ident.c_str()); } else { itId = Item::items.getItemIdByName(Ident); } if( readXMLInteger(slot,"count",intValue) ) { itCount = intValue; } else { itCount = 1; } if( itCount > 100 ) { itCount = 100; std::cout << "Warning: Count on " << Ident << " is higher than 100. Quest: " << quest->name << std::endl; } Item* reward = Item::CreateItem(itId, itCount); if( !reward ) { std::cout << "Error: Error while creating reward item." << quest->name << std::endl; return false; } if( readXMLString(slot, "desc", strCmd) ) { reward->setSpecialDescription(strCmd.c_str()); } if( readXMLInteger(slot,"actId",intValue) ) { reward->setActionId(intValue); } /* container id*/ int cid = 0; if( readXMLInteger(slot,"containerId", intValue) ) { cid = intValue; } quest->rewardList.push_back( RewardList_Pair(reward, cid) ); } slot = slot->next; } } if( xmlStrcmp(tmp->name, (const xmlChar*) "message") == 0 ) { if(readXMLString(tmp, "value", strCmd)) { quest->questDesc += strCmd; } } if( xmlStrcmp(tmp->name, (const xmlChar*) "backpack") == 0 ) { if( readXMLString(tmp,"id",strCmd) ) { Ident = strCmd; } if( isdigit(Ident[0]) ) { bpId = atoi(Ident.c_str()); } else { bpId = Item::items.getItemIdByName(Ident); } quest->bpType[quest->containers] = bpId; quest->containers++; } if( xmlStrcmp(tmp->name, (const xmlChar*) "teleport") == 0 ) { if( readXMLInteger(tmp,"x",intValue) ) { quest->teleport.x = intValue; } if( readXMLInteger(tmp,"y",intValue) ) { quest->teleport.y = intValue; } if( readXMLInteger(tmp,"z",intValue) ) { quest->teleport.z = intValue; } } tmp = tmp->next; } questMap[quest->pos] = quest; } p=p->next; } xmlFreeDoc(doc); } else { std::cout << filename << " not found!" << std::endl; return false; } return true; } Quests::~Quests() { //std::cout << "Quest " << this->name << std::endl; QuestMap::iterator it = questMap.find(this->pos); if(it != questMap.end()) { delete it->second; questMap.erase(it); } } void Quests::erase(bool loadxml) { QuestMap::iterator it = questMap.begin(); while(it != questMap.end()) { delete it->second; questMap.erase(it); it = questMap.begin(); } if(loadxml){ if(!loadQuests()){ std::cout << "\n::Error while reloading the quests.xml. "<<std::endl; return; } } } Quests* Quests::getQuest(Position pos) { QuestMap::iterator it = questMap.find(pos); while(it != questMap.end()) { return it->second; } return NULL; } #endif
E quest.h
#ifdef _QUEST_XML_ ////////////////////////////////////////////////////////////////////// // OpenTibia - an opensource roleplaying game ////////////////////////////////////////////////////////////////////// // Quests system loaded on XML base - By The Chaos ////////////////////////////////////////////////////////////////////// // This 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ////////////////////////////////////////////////////////////////////// /*#include <string> #include <map>*/ #include "position.h" #include "definitions.h" #include "item.h" class Quests{ public: Quests(); ~Quests(); bool showName; int bpType[99], sgId, bId, containers; //Não deve ter mais que 99 containers... pelo amor... uint32_t id; std::string questDesc, name; Position teleport, pos; //Centalizar na posição da quest na action... vai facilitar e nao meche no items... bool loadQuests(); void erase(bool loadxml); typedef std::list<std::pair<Item* , int> > RewardList; typedef RewardList::value_type RewardList_Pair; RewardList rewardList; //std::list<Item *> rewardList; Quests* getQuest(Position pos); typedef std::map<Position,Quests*> QuestMap; QuestMap questMap; protected: }; #endif
Adicione nas opções do seu projeto:
-D_QUEST_XML_
E DE REBUILD ALL.
Crie um arquivo chamado quests.xml na sua pasta DATA e siga o exemplo::
<quests> <quest name="DelOownage" showname="1" questId="803" segment="1" x="121" y="92" z="7" movement="0"> <backpack type="1988"/> <backpack type="1995"/> <message is="Uhuulll"/> <reward> <item type="knight axe" containerId="1"/> <item type="warrior helmet" desc="It was made by and old and unknow guy. A rare masterpiece." containerId="2"/> <item type="royal helmet" desc="Este item tem uma action" containerId="1" actId="9876"/> </reward> </quest> </quests>
Como vocês podem ver, você pode por nome na quest, mudar pra mostar o nome ou não, a posição você TEM que por, pois ela é essencial, se os items vão vim em containers, e por ae vai (obedeça a cap do container...), colocar actionID e mais coisinhas.
There is nothing important that i remember right now, so start the tests and remember, its not finished yet.
Signed,
The Chaos.
Ps.: Se alguem tiver bugs ou lag, qualquer coisa anormal, poste aqui por favor pra gente ta vendo.
E eu gostaria de algumas ideias
Ps².: Eu sei que muitos adoram e acham facil por lua, mas eu prefiro ainda desse jeito e sei que muita gente vai ter mais facilidade, apesar de saber que o lua tem mais funcionalidades e talz. Oka?
Atualizado pra versão v0.3.2 em 31Ago07