Merge pull request #76 from Zaela/respawn

RespawnFromHover scriptable framework
This commit is contained in:
Alex 2013-06-26 10:13:56 -07:00
commit 82f4b4ee53
5 changed files with 319 additions and 111 deletions

View File

@ -319,6 +319,8 @@ Client::Client(EQStreamInterface* ieqs)
MaxXTargets = 5;
XTargetAutoAddHaters = true;
LoadAccountFlags();
initial_respawn_selection = 0;
}
Client::~Client() {
@ -415,6 +417,8 @@ Client::~Client() {
safe_delete_array(adv_requested_data);
safe_delete_array(adv_data);
ClearRespawnOptions();
numclients--;
UpdateWindowTitle();
if(zone)
@ -4310,45 +4314,68 @@ void Client::SendRespawnBinds()
// Client will respond with a 4 byte packet that includes the number of the selection made
//
//If no options have been given, default to Bind + Rez
if (respawn_options.empty())
{
BindStruct* b = &m_pp.binds[0];
RespawnOption opt;
opt.name = "Bind Location";
opt.zoneid = b->zoneId;
opt.x = b->x;
opt.y = b->y;
opt.z = b->z;
opt.heading = b->heading;
respawn_options.push_front(opt);
}
//Rez is always added at the end
RespawnOption rez;
rez.name = "Resurrect";
rez.zoneid = zone->GetZoneID();
rez.x = GetX();
rez.y = GetY();
rez.z = GetZ();
rez.heading = GetHeading();
respawn_options.push_back(rez);
const char* BindName = "Bind Location";
const char* Resurrect = "Resurrect";
int num_options = respawn_options.size();
uint32 PacketLength = 17 + (26 * num_options); //Header size + per-option invariant size
std::list<RespawnOption>::iterator itr;
RespawnOption* opt;
int PacketLength;
//Find string size for each option
for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr)
{
opt = &(*itr);
PacketLength += opt->name.size() + 1; //+1 for cstring
}
PacketLength = 17 + (26 * 2) + strlen(BindName) + strlen(Resurrect); // SoF
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RespawnWindow, PacketLength);
char* buffer = (char*)outapp->pBuffer;
EQApplicationPacket *outapp = new EQApplicationPacket(OP_RespawnWindow, PacketLength);
//Packet header
VARSTRUCT_ENCODE_TYPE(uint32, buffer, initial_respawn_selection); //initial selection (from 0)
VARSTRUCT_ENCODE_TYPE(uint32, buffer, RuleI(Character, RespawnFromHoverTimer) * 1000);
VARSTRUCT_ENCODE_TYPE(uint32, buffer, 0); //unknown
VARSTRUCT_ENCODE_TYPE(uint32, buffer, num_options); //number of options to display
char *Buffer = (char *)outapp->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, RuleI(Character, RespawnFromHoverTimer) * 1000);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 2); // Two options, Bind or Rez
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Entry 0
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, m_pp.binds[0].zoneId);
VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].x);
VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].y);
VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].z);
VARSTRUCT_ENCODE_TYPE(float, Buffer, m_pp.binds[0].heading);
VARSTRUCT_ENCODE_STRING(Buffer, BindName);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); // Entry 1
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, zone->GetZoneID());
VARSTRUCT_ENCODE_TYPE(float, Buffer, GetX());
VARSTRUCT_ENCODE_TYPE(float, Buffer, GetY());
VARSTRUCT_ENCODE_TYPE(float, Buffer, GetZ());
VARSTRUCT_ENCODE_TYPE(float, Buffer, GetHeading());
VARSTRUCT_ENCODE_STRING(Buffer, Resurrect);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1);
//Individual options
int count = 0;
for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr)
{
opt = &(*itr);
VARSTRUCT_ENCODE_TYPE(uint32, buffer, count++); //option num (from 0)
VARSTRUCT_ENCODE_TYPE(uint32, buffer, opt->zoneid);
VARSTRUCT_ENCODE_TYPE(float, buffer, opt->x);
VARSTRUCT_ENCODE_TYPE(float, buffer, opt->y);
VARSTRUCT_ENCODE_TYPE(float, buffer, opt->z);
VARSTRUCT_ENCODE_TYPE(float, buffer, opt->heading);
VARSTRUCT_ENCODE_STRING(buffer, opt->name.c_str());
VARSTRUCT_ENCODE_TYPE(uint8, buffer, (count == num_options)); //is this one Rez (the last option)?
}
QueuePacket(outapp);
safe_delete(outapp);
return;
}
@ -7734,3 +7761,104 @@ void Client::TryItemTick(int slot)
}
}
}
void Client::AddRespawnOption(std::string option_name, uint32 zoneid, float x, float y, float z, float heading, bool initial_selection, int8 position)
{
//If respawn window is already open, any changes would create an inconsistency with the client
if (IsHoveringForRespawn()) { return; }
if (zoneid == 0)
zoneid = zone->GetZoneID();
//Create respawn option
RespawnOption res_opt;
res_opt.name = option_name;
res_opt.zoneid = zoneid;
res_opt.x = x;
res_opt.y = y;
res_opt.z = z;
res_opt.heading = heading;
if (position == -1 || position >= respawn_options.size())
{
//No position specified, or specified beyond the end, simply append
respawn_options.push_back(res_opt);
//Make this option the initial selection for the window if desired
if (initial_selection)
initial_respawn_selection = static_cast<uint8>(respawn_options.size()) - 1;
}
else if (position == 0)
{
respawn_options.push_front(res_opt);
if (initial_selection)
initial_respawn_selection = 0;
}
else
{
//Insert new option between existing options
std::list<RespawnOption>::iterator itr;
uint8 pos = 0;
for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr)
{
if (pos++ == position)
{
respawn_options.insert(itr,res_opt);
//Make this option the initial selection for the window if desired
if (initial_selection)
initial_respawn_selection = pos;
return;
}
}
}
}
bool Client::RemoveRespawnOption(std::string option_name)
{
//If respawn window is already open, any changes would create an inconsistency with the client
if (IsHoveringForRespawn() || respawn_options.empty()) { return false; }
bool had = false;
RespawnOption* opt;
std::list<RespawnOption>::iterator itr;
for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr)
{
opt = &(*itr);
if (opt->name.compare(option_name) == 0)
{
respawn_options.erase(itr);
had = true;
//could be more with the same name, so keep going...
}
}
return had;
}
bool Client::RemoveRespawnOption(uint8 position)
{
//If respawn window is already open, any changes would create an inconsistency with the client
if (IsHoveringForRespawn() || respawn_options.empty()) { return false; }
//Easy cases first...
if (position == 0)
{
respawn_options.pop_front();
return true;
}
else if (position == (respawn_options.size() - 1))
{
respawn_options.pop_back();
return true;
}
std::list<RespawnOption>::iterator itr;
uint8 pos = 0;
for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr)
{
if (pos++ == position)
{
respawn_options.erase(itr);
return true;
}
}
return false;
}

View File

@ -179,6 +179,16 @@ struct XTarget_Struct
char Name[65];
};
struct RespawnOption
{
std::string name;
uint32 zoneid;
float x;
float y;
float z;
float heading;
};
const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000;
@ -1043,6 +1053,11 @@ public:
bool MoveItemToInventory(ItemInst *BInst, bool UpdateClient = false);
void HandleRespawnFromHover(uint32 Option);
bool IsHoveringForRespawn() { return RespawnFromHoverTimer.Enabled(); }
std::list<RespawnOption> respawn_options;
void AddRespawnOption(std::string option_name, uint32 zoneid, float x, float y, float z, float h = 0, bool initial_selection = false, int8 position = -1);
bool RemoveRespawnOption(std::string option_name);
bool RemoveRespawnOption(uint8 position);
void ClearRespawnOptions() { respawn_options.clear(); }
void SetPendingRezzData(int XP, uint32 DBID, uint16 SpellID, const char *CorpseName) { PendingRezzXP = XP; PendingRezzDBID = DBID; PendingRezzSpellID = SpellID; PendingRezzCorpseName = CorpseName; }
bool IsRezzPending() { return PendingRezzSpellID > 0; }
void ClearHover();
@ -1449,6 +1464,8 @@ private:
Timer ItemTickTimer;
std::map<std::string,std::string> accountflags;
uint8 initial_respawn_selection;
};
#include "parser.h"

View File

@ -2040,87 +2040,140 @@ void Client::HandleRespawnFromHover(uint32 Option)
{
RespawnFromHoverTimer.Disable();
if(Option == 1) // Resurrect
RespawnOption* chosen = nullptr;
bool is_rez = false;
//Find the selected option
if (Option == 0)
{
if((PendingRezzXP < 0) || (PendingRezzSpellID == 0))
{
_log(SPELLS__REZ, "Unexpected Rezz from hover request.");
return;
}
SetHP(GetMaxHP() / 5);
Corpse* corpse = entity_list.GetCorpseByName(PendingRezzCorpseName.c_str());
if(corpse)
{
x_pos = corpse->GetX();
y_pos = corpse->GetY();
z_pos = corpse->GetZ();
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + 10);
ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer;
gmg->bind_zone_id = zone->GetZoneID();
gmg->bind_instance_id = zone->GetInstanceID();
gmg->x = GetX();
gmg->y = GetY();
gmg->z = GetZ();
gmg->heading = GetHeading();
strcpy(gmg->zone_name, "Resurrect");
FastQueuePacket(&outapp);
ClearHover();
SendHPUpdate();
OPRezzAnswer(1, PendingRezzSpellID, zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ());
if (corpse && corpse->IsCorpse()) {
_log(SPELLS__REZ, "Hover Rez in zone %s for corpse %s",
zone->GetShortName(), PendingRezzCorpseName.c_str());
_log(SPELLS__REZ, "Found corpse. Marking corpse as rezzed.");
corpse->Rezzed(true);
corpse->CompleteRezz();
}
return;
chosen = &respawn_options.front();
}
// Respawn at Bind Point.
//
if(m_pp.binds[0].zoneId == zone->GetZoneID())
else if (Option == (respawn_options.size() - 1))
{
PendingRezzSpellID = 0;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + 14);
ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer;
gmg->bind_zone_id = m_pp.binds[0].zoneId;
gmg->x = m_pp.binds[0].x;
gmg->y = m_pp.binds[0].y;
gmg->z = m_pp.binds[0].z;
gmg->heading = 0;
strcpy(gmg->zone_name, "Bind Location");
FastQueuePacket(&outapp);
CalcBonuses();
SetHP(GetMaxHP());
SetMana(GetMaxMana());
SetEndurance(GetMaxEndurance());
x_pos = m_pp.binds[0].x;
y_pos = m_pp.binds[0].y;
z_pos = m_pp.binds[0].z;
ClearHover();
entity_list.RefreshClientXTargets(this);
SendHPUpdate();
chosen = &respawn_options.back();
is_rez = true; //Rez must always be the last option
}
else
{
std::list<RespawnOption>::iterator itr;
uint32 pos = 0;
for (itr = respawn_options.begin(); itr != respawn_options.end(); ++itr)
{
if (pos++ == Option)
{
chosen = &(*itr);
break;
}
}
}
//If they somehow chose an option they don't have, just send them to bind
RespawnOption* default_to_bind = nullptr;
if (!chosen)
{
/* put error logging here */
BindStruct* b = &m_pp.binds[0];
default_to_bind = new RespawnOption;
default_to_bind->name = "Bind Location";
default_to_bind->zoneid = b->zoneId;
default_to_bind->x = b->x;
default_to_bind->y = b->y;
default_to_bind->z = b->z;
default_to_bind->heading = b->heading;
chosen = default_to_bind;
is_rez = false;
}
if (chosen->zoneid == zone->GetZoneID()) //If they should respawn in the current zone...
{
if (is_rez)
{
if (PendingRezzXP < 0 || PendingRezzSpellID == 0)
{
_log(SPELLS__REZ, "Unexpected Rezz from hover request.");
return;
}
SetHP(GetMaxHP() / 5);
Corpse* corpse = entity_list.GetCorpseByName(PendingRezzCorpseName.c_str());
if (corpse)
{
x_pos = corpse->GetX();
y_pos = corpse->GetY();
z_pos = corpse->GetZ();
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + 10);
ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer;
gmg->bind_zone_id = zone->GetZoneID();
gmg->bind_instance_id = zone->GetInstanceID();
gmg->x = GetX();
gmg->y = GetY();
gmg->z = GetZ();
gmg->heading = GetHeading();
strcpy(gmg->zone_name, "Resurrect");
FastQueuePacket(&outapp);
ClearHover();
SendHPUpdate();
OPRezzAnswer(1, PendingRezzSpellID, zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ());
if (corpse && corpse->IsCorpse())
{
_log(SPELLS__REZ, "Hover Rez in zone %s for corpse %s",
zone->GetShortName(), PendingRezzCorpseName.c_str());
_log(SPELLS__REZ, "Found corpse. Marking corpse as rezzed.");
corpse->Rezzed(true);
corpse->CompleteRezz();
}
}
else //Not rez
{
PendingRezzSpellID = 0;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + chosen->name.length() + 1);
ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer;
gmg->bind_zone_id = zone->GetZoneID();
gmg->x = chosen->x;
gmg->y = chosen->y;
gmg->z = chosen->z;
gmg->heading = chosen->heading;
strcpy(gmg->zone_name, chosen->name.c_str());
FastQueuePacket(&outapp);
CalcBonuses();
SetHP(GetMaxHP());
SetMana(GetMaxMana());
SetEndurance(GetMaxEndurance());
x_pos = chosen->x;
y_pos = chosen->y;
z_pos = chosen->z;
heading = chosen->heading;
ClearHover();
entity_list.RefreshClientXTargets(this);
SendHPUpdate();
}
//After they've respawned into the same zone, trigger EVENT_RESPAWN
parse->EventPlayer(EVENT_RESPAWN, this, static_cast<std::string>(itoa(Option)), is_rez ? 1 : 0);
//Pop Rez option from the respawn options list;
//easiest way to make sure it stays at the end and
//doesn't disrupt adding/removing scripted options
respawn_options.pop_back();
}
else
{
//Heading to a different zone
if(isgrouped)
{
Group *g = GetGroup();
@ -2129,18 +2182,19 @@ void Client::HandleRespawnFromHover(uint32 Option)
}
Raid* r = entity_list.GetRaidByClient(this);
if(r)
r->MemberZoned(this);
m_pp.zone_id = m_pp.binds[0].zoneId;
m_pp.zone_id = chosen->zoneid;
m_pp.zoneInstance = 0;
database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id));
database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(chosen->zoneid));
Save();
GoToDeath();
MovePC(chosen->zoneid,chosen->x,chosen->y,chosen->z,chosen->heading,1);
}
safe_delete(default_to_bind);
}
void Client::ClearHover()

View File

@ -97,7 +97,8 @@ const char *QuestEventSubroutines[_LargestEventID] = {
"EVENT_CONNECT",
"EVENT_ITEM_TICK",
"EVENT_DUEL_WIN",
"EVENT_DUEL_LOSE"
"EVENT_DUEL_LOSE",
"EVENT_RESPAWN"
};
extern Zone* zone;
@ -818,6 +819,13 @@ void PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * d
break;
}
case EVENT_RESPAWN:
{
ExportVar(packagename.c_str(), "respawn_option", data);
ExportVar(packagename.c_str(), "is_rez", extradata);
break;
}
//nothing special about these events
case EVENT_DEATH:
case EVENT_SPAWN:

View File

@ -61,6 +61,7 @@ typedef enum {
EVENT_ITEM_TICK,
EVENT_DUEL_WIN,
EVENT_DUEL_LOSE,
EVENT_RESPAWN, //PC respawning from hover without changing zones
_LargestEventID
} QuestEventID;