/* $Id: Players.cpp 184 2010-01-10 05:58:12Z sausage $
* EOSERV is released under the zlib license.
* See LICENSE.txt for more info.
*/
#include "handlers.h"
#include "eodata.hpp"
#include "map.hpp"
#include "npc.hpp"
CLIENT_F_FUNC(Quest)
{
PacketBuilder reply;
switch (action)
{
case PACKET_USE: // Player clicked on a quest NPC
{
if (this->state < EOClient::Playing) return false;
short id = reader.GetShort();
short quests_index = reader.GetShort();
UTIL_PTR_VECTOR_FOREACH(this->player->character->map->npcs, NPC, npc)
{
if (npc->index == id && npc->Data()->type == ENF::Quest)
{
this->player->character->npc = *npc;
this->player->character->npc_type = ENF::Quest;
PtrVector<Quest> quests;
Quest *quests_start = this->server->world->GetQuest(quests_index == 0 ? npc->Data()->type_index : quests_index);
if (quests_start->exists)
{
quests.push_back(quests_start);
Character_Quest *character_quest = this->player->character->GetQuest(quests_start->id);
if (!character_quest)
{
character_quest = new Character_Quest();
character_quest->id = quests_start->id;
character_quest->state = "begin";
this->player->character->quests.push_back(character_quest);
}
}
State *state = 0;
Quest *temp_quest = 0;
std::list<std::string> npc_text;
std::map<int, std::string> npc_input;
UTIL_PTR_LIST_FOREACH(this->player->character->quests, Character_Quest, quest)
{
temp_quest = this->server->world->GetQuest(quest->id);
if (!temp_quest->exists) continue;
state = temp_quest->GetState(quest->state);
if (!state) continue;
bool found = false;
UTIL_PTR_VECTOR_FOREACH(state->actions, Action, action)
{
if (action->name.compare("addnpctext") == 0 && ((int)(*action->args[0])) == npc->Data()->type_index)
{
if (quests.size() == 0)
{
npc_text.push_back(static_cast<std::string>((*action->args[1])));
if (!found)
found = true;
}
else if (quests[0]->id == quest->id)
{
npc_text.push_back(static_cast<std::string>((*action->args[1])));
}
else
{
if (!found)
found = true;
}
}
else if (action->name.compare("addnpcinput") == 0 && ((int)(*action->args[0])) == npc->Data()->type_index)
{
if (quests.size() == 0)
{
npc_input.insert(std::pair<int, std::string>(static_cast<int>((*action->args[1])), static_cast<std::string>((*action->args[2]))));
if (!found)
found = true;
}
else if (quests[0]->id == quest->id)
{
npc_input.insert(std::pair<int, std::string>(static_cast<int>((*action->args[1])), static_cast<std::string>((*action->args[2]))));
}
else
{
if (!found)
found = true;
}
}
else if (action->name.compare("addnpcchat") == 0 && ((int)(*action->args[0])) == npc->Data()->type_index)
{
if ((quests.size() == 0 || quests[0]->id == quest->id) && util::rand(0, 100) < 20) /* 20 percent chance */
{
npc->ShowDialog(static_cast<std::string>((*action->args[1])));
}
}
}
if (found)
{
quests.push_back(temp_quest);
}
}
if (quests.size() == 0) return false;
reply.SetID(PACKET_QUEST, PACKET_DIALOG);
reply.AddChar(quests.size());
reply.AddShort(npc->Data()->type_index);
reply.AddShort(quests[0]->id); //Quest_ID
reply.AddInt(0); // Session token
reply.AddByte(255);
UTIL_PTR_VECTOR_FOREACH(quests, Quest, npc_quests)
{
reply.AddShort(npc_quests->id);
reply.AddBreakString(npc_quests->name);
}
bool has_dialog = !npc_text.empty() || !npc_input.empty();
while (!npc_text.empty())
{
reply.AddShort(1);
reply.AddBreakString(npc_text.front());
npc_text.pop_front();
}
for (std::map<int, std::string>::iterator it = npc_input.begin(); it != npc_input.end(); ++it)
{
reply.AddShort(2);
reply.AddShort((*it).first);
reply.AddBreakString((*it).second);
}
if (has_dialog)
CLIENT_SEND(reply);
}
}
}
break;
case PACKET_ACCEPT: // User clicking a link, or accepting quest
{
if (this->state < EOClient::PlayingModal) return false;
reader.GetInt(); // Session token
short id = reader.GetShort();
short npc_quest_index = reader.GetShort();
unsigned char input_type = reader.GetChar();
unsigned char input = reader.GetChar();
if (!this->player->character->npc || this->player->character->npc_type != ENF::Quest ||
this->player->character->npc->Data()->type_index != npc_quest_index)
{
return false;
}
Character_Quest *character_quest = this->player->character->GetQuest(id);
State *state = 0;
if (character_quest) state = this->server->world->GetQuest(id)->GetState(character_quest->state);
else state = this->server->world->GetQuest(id)->GetState("begin");
UTIL_PTR_VECTOR_FOREACH(state->rules, Rule, rule)
{
if ((rule->name.compare("inputnpc") == 0 && (int)(*rule->args[0]) == input) ||
(rule->name.compare("talkedtonpc") == 0 && (int)(*rule->args[0]) == npc_quest_index) ||
(rule->name.compare("gotitems") == 0 && this->player->character->HasItem((int)(*rule->args[0])) >= (int)(*rule->args[1])) ||
(rule->name.compare("lostitems") == 0 && this->player->character->HasItem((int)(*rule->args[0])) < (int)(*rule->args[1])))
{
if (!character_quest)
{
character_quest = new Character_Quest();
character_quest->id = id;
this->player->character->quests.push_back(character_quest);
}
character_quest->state = rule->goto_state;
if (this->player->character->PerformQuestActions(character_quest->id))
{
util::quadchar index = PacketProcessor::ENumber(this->player->character->npc->index);
PacketBuilder builder(PACKET_QUEST, PACKET_USE);
builder.AddShort(this->player->character->npc->index);
builder.AddShort(npc_quest_index == id ? 0 : id);
PacketReader reader(builder.Get().substr(4)); // Skip the id and length
this->player->client->queue.AddAction(PACKET_QUEST, PACKET_USE, reader, 0.0);
}
break;
}
}
}
break;
case PACKET_LIST: // User requests his own quest history
{
if (this->state < EOClient::PlayingModal) return false;
unsigned char book_type = reader.GetChar();
reply.SetID(PACKET_QUEST, PACKET_LIST);
reply.AddChar(book_type);
reply.AddShort(this->player->character->quests.size());
if (this->player->character->quests.size() == 0)
{
CLIENT_SEND(reply);
return true;
}
Quest *temp_quest = 0;
State *state = 0;
UTIL_PTR_LIST_FOREACH(this->player->character->quests, Character_Quest, quest)
{
temp_quest = this->server->world->GetQuest(quest->id);
if (!temp_quest->exists) continue;
state = temp_quest->GetState(quest->state);
if (!state) continue;
bool completed = false;
UTIL_PTR_VECTOR_FOREACH(state->actions, Action, action)
{
if (action->name.compare("end") == 0)
{
completed = true;
break;
}
}
if (!completed && book_type == 1) // Progress
{
reply.AddBreakString(temp_quest->name);
reply.AddBreakString(state->description);
UTIL_PTR_VECTOR_FOREACH(state->rules, Rule, rule)
{
if (rule->name.compare("gotitems") == 0 || rule->name.compare("lostitems") == 0)
{
short item = ((int)(*rule->args[0]));
int amount = ((int)(*rule->args[1]));
reply.AddShort(BOOK_ICON_ITEM);
reply.AddShort(this->player->character->HasItem(item));
reply.AddShort(amount);
}
else if (rule->name.compare("inputnpc") == 0 || rule->name.compare("talkedtonpc") == 0)
{
reply.AddShort(BOOK_ICON_TALK);
reply.AddShort(0);
reply.AddShort(0);
}
else if (rule->name.compare("killednpcs") == 0 || rule->name.compare("killedplayers") == 0)
{
int amount = ((int)(*rule->args[1]));
reply.AddShort(BOOK_ICON_KILL);
reply.AddShort(quest->killed);
reply.AddShort(amount);
}
else if (rule->name.compare("entercoord") == 0 || rule->name.compare("leavecoord") == 0 ||
rule->name.compare("entermap") == 0 || rule->name.compare("leavemap") == 0)
{
reply.AddShort(BOOK_ICON_STEP);
reply.AddShort(0);
reply.AddShort(1);
}
break;
}
reply.AddByte(255);
}
else if (completed && book_type == 2) // History
{
reply.AddBreakString(temp_quest->name);
reply.AddByte(255);
}
}
CLIENT_SEND(reply);
}
break;
default:
return false;
}
return true;
}