mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 14:41:28 +00:00
Reworked RespawnFromHover framework to allow future customization (scripting) of respawn options.
Added event_respawn to be triggered when a client respawns from hover into the current zone (may not be set up correctly!).
This commit is contained in:
parent
e811e3975b
commit
c5e4cf35c0
186
zone/client.cpp
186
zone/client.cpp
@ -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
|
||||
|
||||
int PacketLength;
|
||||
std::list<RespawnOption>::iterator itr;
|
||||
RespawnOption* opt;
|
||||
|
||||
PacketLength = 17 + (26 * 2) + strlen(BindName) + strlen(Resurrect); // SoF
|
||||
//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
|
||||
}
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_RespawnWindow, PacketLength);
|
||||
char* buffer = (char*)outapp->pBuffer;
|
||||
|
||||
char *Buffer = (char *)outapp->pBuffer;
|
||||
//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
|
||||
|
||||
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;
|
||||
}
|
||||
@ -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"
|
||||
|
||||
@ -2040,9 +2040,54 @@ 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))
|
||||
chosen = &respawn_options.front();
|
||||
}
|
||||
else if (Option == (respawn_options.size() - 1))
|
||||
{
|
||||
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; //must keep in scope!
|
||||
if (!chosen)
|
||||
{
|
||||
/* put error logging here */
|
||||
BindStruct* b = &m_pp.binds[0];
|
||||
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;
|
||||
@ -2075,7 +2120,8 @@ void Client::HandleRespawnFromHover(uint32 Option)
|
||||
SendHPUpdate();
|
||||
OPRezzAnswer(1, PendingRezzSpellID, zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ());
|
||||
|
||||
if (corpse && corpse->IsCorpse()) {
|
||||
if (corpse && corpse->IsCorpse())
|
||||
{
|
||||
_log(SPELLS__REZ, "Hover Rez in zone %s for corpse %s",
|
||||
zone->GetShortName(), PendingRezzCorpseName.c_str());
|
||||
|
||||
@ -2084,24 +2130,20 @@ void Client::HandleRespawnFromHover(uint32 Option)
|
||||
corpse->Rezzed(true);
|
||||
corpse->CompleteRezz();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Respawn at Bind Point.
|
||||
//
|
||||
if(m_pp.binds[0].zoneId == zone->GetZoneID())
|
||||
else //Not rez
|
||||
{
|
||||
PendingRezzSpellID = 0;
|
||||
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + 14);
|
||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + chosen->name.length() + 1);
|
||||
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");
|
||||
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);
|
||||
|
||||
@ -2110,17 +2152,27 @@ void Client::HandleRespawnFromHover(uint32 Option)
|
||||
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;
|
||||
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,17 +2181,16 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user