Spirit Shards, Group and Solo EXP Debt (PVE/PVP), new lua functions, rules included

xp_debt in InfoStruct changed to float use SetInfoStructFloat and GetInfoStructFloat for it

SpawnScript file is now listed in /spawn details on the last page

LUA functions added:
bool GetRuleFlagBool(category, name)
float GetRuleFlagFloat(category, name)
int32 GetShardID(Spawn)
int32 GetShardCharID(Spawn)
int64 GetShardCreatedTimestamp(Spawn)
bool DeleteDBShardID(shardid) -- using GetShardID
int32 GetCharacterID(Spawn)
bool SetAccessToEntityCommandByCharID(Spawn, CharID, command_string, val) -- same as SetAccessToEntityCommand, but using a CharID instead of player

Rules added:

RULE_INIT(R_Combat, DeathExperienceDebt, "50.00"); // divide by 100, 50/100 = .5% debt per pve death
RULE_INIT(R_Combat, PVPDeathExperienceDebt, "25.00"); // divide by 100, 25/100 = .25% debt per pvp death
RULE_INIT(R_Combat, GroupExperienceDebt, "0"); // set to 1 means we will share debt between the group
RULE_INIT(R_Combat, ExperienceToDebt, "50.00"); // percentage of xp earned to debt vs obtained xp 50/100 = 50% to debt
RULE_INIT(R_Combat, ExperienceDebtRecoveryPercent, "5.00"); // recovery percentage per period of time, 5/100 = 5% recovered (so if .5% debt, .5*.05 = .025, .5-.025=.475% debt left)
RULE_INIT(R_Combat, ExperienceDebtRecoveryPeriod, "600"); // every 10 minutes (x*60 seconds) recover ExperienceDebtRecoveryPercent
RULE_INIT(R_Combat, EnableSpiritShards, "1");
RULE_INIT(R_Combat, SpiritShardSpawnScript, "SpawnScripts/Generic/SpiritShard.lua");
RULE_INIT(R_Combat, ShardDebtRecoveryPercent, "25.00"); // recovered percentage of debt upon obtainig shard, 25/100 means 25%.  If there is .5 DeathExperienceDebt, .5*25% = .125,  .5 - .125 = .375
This commit is contained in:
Image 2021-02-07 22:19:03 -05:00
parent ce323835bf
commit 65e7222de5
21 changed files with 715 additions and 29 deletions

View file

@ -0,0 +1,41 @@
alter table character_details modify column xp_debt float not null default 0.0;
CREATE TABLE `character_spirit_shards` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`timestamp` BIGINT signed NOT NULL DEFAULT CURRENT_TIMESTAMP,
`name` varchar(64) not null default '',
`level` int(10) unsigned NOT NULL DEFAULT 0,
`race` tinyint(3) unsigned NOT NULL DEFAULT 0,
`gender` tinyint(3) unsigned NOT NULL DEFAULT 0,
`adventure_class` tinyint(3) unsigned NOT NULL DEFAULT 0,
`model_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`soga_model_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`hair_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`hair_face_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`wing_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`chest_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`legs_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`soga_hair_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`soga_hair_face_type` mediumint(8) unsigned NOT NULL DEFAULT 0,
`hide_hood` tinyint(3) unsigned NOT NULL DEFAULT 0,
`size` mediumint(8) unsigned NOT NULL DEFAULT 0,
`collision_radius` mediumint(8) unsigned NOT NULL DEFAULT 0,
`action_state` mediumint(8) unsigned NOT NULL DEFAULT 0,
`visual_state` mediumint(8) unsigned NOT NULL DEFAULT 0,
`mood_state` mediumint(8) unsigned NOT NULL DEFAULT 0,
`emote_state` mediumint(8) unsigned NOT NULL DEFAULT 0,
`pos_state` mediumint(8) unsigned NOT NULL DEFAULT 0,
`activity_status` mediumint(8) unsigned NOT NULL DEFAULT 0,
`sub_title` varchar(255) not null default '',
`prefix_title` varchar(128) not null default '',
`suffix_title` varchar(128) not null default '',
`lastname` varchar(64) not null default '',
`x` float not null default 0.0,
`y` float not null default 0.0,
`z` float not null default 0.0,
`heading` float not null default 0.0,
`gridid` int(10) unsigned NOT NULL DEFAULT 0,
`zoneid` int(10) unsigned NOT NULL DEFAULT 0,
`instanceid` int(10) unsigned NOT NULL DEFAULT 0,
`charid` int(10) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

View file

@ -30,6 +30,7 @@
#include "LuaInterface.h"
#include "Rules/Rules.h"
#include "SpellProcess.h"
#include "World.h"
#include <math.h>
extern Classes classes;
@ -37,6 +38,7 @@ extern ConfigReader configReader;
extern MasterSkillList master_skill_list;
extern RuleManager rule_manager;
extern LuaInterface* lua_interface;
extern World world;
/* ******************************************************************************
@ -1183,6 +1185,51 @@ void Entity::KillSpawn(Spawn* dead, int8 damage_type, int16 kill_blow_type) {
GetZone()->KillSpawn(true, dead, this, true, damage_type, kill_blow_type);
}
void Entity::HandleDeathExperienceDebt(Spawn* killer)
{
if(!IsPlayer())
return;
float ruleDebt = 0.0f;
if(killer && killer->IsPlayer())
ruleDebt = rule_manager.GetGlobalRule(R_Combat, PVPDeathExperienceDebt)->GetFloat()/100.0f;
else
ruleDebt = rule_manager.GetGlobalRule(R_Combat, DeathExperienceDebt)->GetFloat()/100.0f;
if(ruleDebt > 0.0f)
{
bool groupDebt = rule_manager.GetGlobalRule(R_Combat, GroupExperienceDebt)->GetBool();
if(groupDebt && ((Player*)this)->GetGroupMemberInfo())
{
world.GetGroupManager()->GroupLock(__FUNCTION__, __LINE__);
PlayerGroup* group = world.GetGroupManager()->GetGroup(((Player*)this)->GetGroupMemberInfo()->group_id);
if (group)
{
group->MGroupMembers.readlock(__FUNCTION__, __LINE__);
deque<GroupMemberInfo*>* members = group->GetMembers();
int32 size = members->size();
float xpDebtPerMember = ruleDebt/(float)size;
deque<GroupMemberInfo*>::iterator itr;
for (itr = members->begin(); itr != members->end(); itr++) {
GroupMemberInfo* gmi = *itr;
if (gmi->client && gmi->client->GetPlayer()) {
gmi->client->GetPlayer()->GetInfoStruct()->set_xp_debt(gmi->client->GetPlayer()->GetInfoStruct()->get_xp_debt()+xpDebtPerMember);
gmi->client->GetPlayer()->SetCharSheetChanged(true);
}
}
group->MGroupMembers.releasereadlock(__FUNCTION__, __LINE__);
}
world.GetGroupManager()->ReleaseGroupLock(__FUNCTION__, __LINE__);
}
else
{
((Player*)this)->GetInfoStruct()->set_xp_debt(((Player*)this)->GetInfoStruct()->get_xp_debt()+ruleDebt);
((Player*)this)->SetCharSheetChanged(true);
}
}
}
void Entity::ProcessCombat() {
// This is a virtual function so when a NPC calls this it will use the NPC::ProcessCombat() version
// and a player will use the Player::ProcessCombat() version, leave this function blank.

View file

@ -3830,6 +3830,10 @@ void Commands::Process(int32 index, EQ2_16BitString* command_parms, Client* clie
if (spawn->IsNPC()) {
details4 += "\nRandomize: " + to_string(((NPC*)spawn)->GetRandomize()) + "\n";
}
const char* spawnScriptMsg = (spawn->GetSpawnScript() && strlen(spawn->GetSpawnScript())>0) ? spawn->GetSpawnScript() : "Not Set";
details4 += "\nSpawnScript: " + std::string(spawnScriptMsg) + "\n";
}
string title = string(spawn->GetName()) + "(" + to_string(spawn->GetDatabaseID()) + ")";

View file

@ -178,7 +178,7 @@ void Entity::MapInfoStruct()
get_int16_funcs["absorb"] = l::bind(&InfoStruct::get_absorb, &info_struct);
get_int32_funcs["xp"] = l::bind(&InfoStruct::get_xp, &info_struct);
get_int32_funcs["xp_needed"] = l::bind(&InfoStruct::get_xp_needed, &info_struct);
get_int32_funcs["xp_debt"] = l::bind(&InfoStruct::get_xp_debt, &info_struct);
get_float_funcs["xp_debt"] = l::bind(&InfoStruct::get_xp_debt, &info_struct);
get_int16_funcs["xp_yellow"] = l::bind(&InfoStruct::get_xp_yellow, &info_struct);
get_int16_funcs["xp_yellow_vitality_bar"] = l::bind(&InfoStruct::get_xp_yellow_vitality_bar, &info_struct);
get_int16_funcs["xp_blue_vitality_bar"] = l::bind(&InfoStruct::get_xp_blue_vitality_bar, &info_struct);
@ -318,7 +318,7 @@ void Entity::MapInfoStruct()
set_int16_funcs["absorb"] = l::bind(&InfoStruct::set_absorb, &info_struct, l::_1);
set_int32_funcs["xp"] = l::bind(&InfoStruct::set_xp, &info_struct, l::_1);
set_int32_funcs["xp_needed"] = l::bind(&InfoStruct::set_xp_needed, &info_struct, l::_1);
set_int32_funcs["xp_debt"] = l::bind(&InfoStruct::set_xp_debt, &info_struct, l::_1);
set_float_funcs["xp_debt"] = l::bind(&InfoStruct::set_xp_debt, &info_struct, l::_1);
set_int16_funcs["xp_yellow"] = l::bind(&InfoStruct::set_xp_yellow, &info_struct, l::_1);
set_int16_funcs["xp_yellow_vitality_bar"] = l::bind(&InfoStruct::set_xp_yellow_vitality_bar, &info_struct, l::_1);
set_int16_funcs["xp_blue_vitality_bar"] = l::bind(&InfoStruct::set_xp_blue_vitality_bar, &info_struct, l::_1);

View file

@ -191,7 +191,7 @@ struct InfoStruct{
absorb_ = 0;
xp_ = 0;
xp_needed_ = 0;
xp_debt_ = 0;
xp_debt_ = 0.0f;
xp_yellow_ = 0;
xp_yellow_vitality_bar_ = 0;
xp_blue_vitality_bar_ = 0;
@ -489,7 +489,7 @@ struct InfoStruct{
int16 get_absorb() { std::lock_guard<std::mutex> lk(classMutex); return absorb_; }
int32 get_xp() { std::lock_guard<std::mutex> lk(classMutex); return xp_; }
int32 get_xp_needed() { std::lock_guard<std::mutex> lk(classMutex); return xp_needed_; }
int32 get_xp_debt() { std::lock_guard<std::mutex> lk(classMutex); return xp_debt_; }
float get_xp_debt() { std::lock_guard<std::mutex> lk(classMutex); return xp_debt_; }
int16 get_xp_yellow() { std::lock_guard<std::mutex> lk(classMutex); return xp_yellow_; }
int16 get_xp_yellow_vitality_bar() { std::lock_guard<std::mutex> lk(classMutex); return xp_yellow_vitality_bar_; }
int16 get_xp_blue_vitality_bar() { std::lock_guard<std::mutex> lk(classMutex); return xp_blue_vitality_bar_; }
@ -658,7 +658,7 @@ struct InfoStruct{
void set_xp(int32 value) { std::lock_guard<std::mutex> lk(classMutex); xp_ = value; }
void set_xp_needed(int32 value) { std::lock_guard<std::mutex> lk(classMutex); xp_needed_ = value; }
void set_xp_debt(int32 value) { std::lock_guard<std::mutex> lk(classMutex); xp_debt_ = value; }
void set_xp_debt(float value) { std::lock_guard<std::mutex> lk(classMutex); xp_debt_ = value; }
void set_xp_yellow(int16 value) { std::lock_guard<std::mutex> lk(classMutex); xp_yellow_ = value; }
void set_xp_blue(int16 value) { std::lock_guard<std::mutex> lk(classMutex); xp_blue_ = value; }
@ -891,7 +891,7 @@ private:
int16 absorb_;
int32 xp_;
int32 xp_needed_;
int32 xp_debt_;
float xp_debt_;
int16 xp_yellow_;
int16 xp_yellow_vitality_bar_;
int16 xp_blue_vitality_bar_;
@ -1194,6 +1194,7 @@ public:
void AddHate(Entity* attacker, sint32 hate);
bool CheckInterruptSpell(Entity* attacker);
void KillSpawn(Spawn* dead, int8 damage_type = 0, int16 kill_blow_type = 0);
void HandleDeathExperienceDebt(Spawn* killer);
void SetAttackDelay(bool primary = false, bool ranged = false);
float CalculateAttackSpeedMod();
virtual void ProcessCombat();

View file

@ -1007,6 +1007,19 @@ int EQ2Emu_lua_IsPlayer(lua_State* state) {
return 0;
}
int EQ2Emu_lua_GetCharacterID(lua_State* state) {
if (!lua_interface)
return 0;
Spawn* spawn = lua_interface->GetSpawn(state);
if (spawn && spawn->IsPlayer()) {
lua_interface->SetInt32Value(state, ((Player*)spawn)->GetCharacterID());
return 1;
}
lua_interface->SetInt32Value(state, 0);
return 1;
}
int EQ2Emu_lua_FaceTarget(lua_State* state) {
if (!lua_interface)
return 0;
@ -10667,6 +10680,7 @@ int EQ2Emu_lua_SetAccessToEntityCommand(lua_State* state)
string command = lua_interface->GetStringValue(state, 3);
bool val = (lua_interface->GetInt8Value(state, 4) == 1);
lua_interface->ResetFunctionStack(state);
if (spawn && player && player->IsPlayer())
{
EntityCommand* cmd = spawn->FindEntityCommand(string(command), true);
@ -10681,6 +10695,32 @@ int EQ2Emu_lua_SetAccessToEntityCommand(lua_State* state)
return 0;
}
int EQ2Emu_lua_SetAccessToEntityCommandByCharID(lua_State* state)
{
if (!lua_interface)
return 0;
Spawn* spawn = lua_interface->GetSpawn(state);
int32 charID = lua_interface->GetInt32Value(state, 2);
string command = lua_interface->GetStringValue(state, 3);
bool val = (lua_interface->GetInt8Value(state, 4) == 1);
lua_interface->ResetFunctionStack(state);
if (spawn && charID)
{
EntityCommand* cmd = spawn->FindEntityCommand(string(command), true);
bool res = false;
if (cmd)
res = spawn->SetPermissionToEntityCommandByCharID(cmd, charID, val);
lua_interface->SetBooleanValue(state, res);
return 1;
}
return 0;
}
int EQ2Emu_lua_RemovePrimaryEntityCommand(lua_State* state)
{
if (!lua_interface)
@ -10689,6 +10729,7 @@ int EQ2Emu_lua_RemovePrimaryEntityCommand(lua_State* state)
Spawn* spawn = lua_interface->GetSpawn(state);
string command = lua_interface->GetStringValue(state, 2);
lua_interface->ResetFunctionStack(state);
if (spawn && command.length() > 0)
spawn->RemovePrimaryEntityCommand(command.c_str());
@ -10705,6 +10746,7 @@ int EQ2Emu_lua_SendUpdateDefaultCommand(lua_State* state) {
string command = lua_interface->GetStringValue(state, 3);
Spawn* player = lua_interface->GetSpawn(state, 4);
lua_interface->ResetFunctionStack(state);
if (spawn) {
spawn->GetZone()->SendUpdateDefaultCommand(spawn, command.c_str(), distance, player);
}
@ -10719,6 +10761,7 @@ int EQ2Emu_lua_SendTransporters(lua_State* state) {
Spawn* player = lua_interface->GetSpawn(state, 2);
int32 transport_id = lua_interface->GetInt32Value(state, 3);
lua_interface->ResetFunctionStack(state);
if (spawn && player && transport_id && player->IsPlayer()) {
Client* client = 0;
if (player && player->IsPlayer())
@ -10748,6 +10791,7 @@ int EQ2Emu_lua_SetTemporaryTransportID(lua_State* state) {
Spawn* player = lua_interface->GetSpawn(state);
int32 transport_id = lua_interface->GetInt32Value(state, 2);
lua_interface->ResetFunctionStack(state);
if (player && player->IsPlayer()) {
Client* client = 0;
if (player && player->IsPlayer())
@ -10765,6 +10809,8 @@ int EQ2Emu_lua_GetTemporaryTransportID(lua_State* state) {
if (!lua_interface)
return 0;
Spawn* player = lua_interface->GetSpawn(state);
lua_interface->ResetFunctionStack(state);
if (player && player->IsPlayer()) {
Client* client = 0;
if (player && player->IsPlayer())
@ -10803,6 +10849,8 @@ int EQ2Emu_lua_SetAlignment(lua_State* state) {
return 0;
}
lua_interface->ResetFunctionStack(state);
if (spell && spell->targets.size() > 0) {
ZoneServer* zone = spell->caster->GetZone();
for (int8 i = 0; i < spell->targets.size(); i++) {
@ -10827,6 +10875,8 @@ int EQ2Emu_lua_GetAlignment(lua_State* state) {
return 0;
Spawn* spawn = lua_interface->GetSpawn(state);
lua_interface->ResetFunctionStack(state);
if (!spawn) {
lua_interface->LogError("%s: LUA GetAlignment command error: spawn is not valid", lua_interface->GetScriptName(state));
return 0;
@ -11264,6 +11314,23 @@ int EQ2Emu_lua_SetInvulnerable(lua_State* state) {
return 0;
}
int EQ2Emu_lua_GetRuleFlagBool(lua_State* state) {
if (!lua_interface)
return 0;
string category = lua_interface->GetStringValue(state);
string name = lua_interface->GetStringValue(state, 2);
lua_interface->ResetFunctionStack(state);
Rule *ret = 0;
if ((ret = rule_manager.GetGlobalRule(category.c_str(), name.c_str()))) {
lua_interface->SetBooleanValue(state, ret->GetBool());
return 1;
}
lua_interface->LogError("%s: LUA GetRuleFlagBool Unknown rule with category '%s' and type '%s'", lua_interface->GetScriptName(state), category.c_str(), name.c_str());
return 0;
}
int EQ2Emu_lua_GetRuleFlagInt32(lua_State* state) {
if (!lua_interface)
return 0;
@ -11277,7 +11344,24 @@ int EQ2Emu_lua_GetRuleFlagInt32(lua_State* state) {
return 1;
}
lua_interface->LogError("%s: LUA GetRuleFlag Unknown rule with category '%s' and type '%s'", lua_interface->GetScriptName(state), category.c_str(), name.c_str());
lua_interface->LogError("%s: LUA GetRuleFlagInt32 Unknown rule with category '%s' and type '%s'", lua_interface->GetScriptName(state), category.c_str(), name.c_str());
return 0;
}
int EQ2Emu_lua_GetRuleFlagFloat(lua_State* state) {
if (!lua_interface)
return 0;
string category = lua_interface->GetStringValue(state);
string name = lua_interface->GetStringValue(state, 2);
lua_interface->ResetFunctionStack(state);
Rule *ret = 0;
if ((ret = rule_manager.GetGlobalRule(category.c_str(), name.c_str()))) {
lua_interface->SetFloatValue(state, ret->GetFloat());
return 1;
}
lua_interface->LogError("%s: LUA GetRuleFlagFloat Unknown rule with category '%s' and type '%s'", lua_interface->GetScriptName(state), category.c_str(), name.c_str());
return 0;
}
@ -11824,6 +11908,8 @@ int EQ2Emu_lua_IsOpen(lua_State* state) {
if (!lua_interface)
return 0;
Spawn* widget = lua_interface->GetSpawn(state);
lua_interface->ResetFunctionStack(state);
if (widget && widget->IsWidget())
{
lua_interface->SetBooleanValue(state, ((Widget*)widget)->IsOpen());
@ -11838,6 +11924,8 @@ int EQ2Emu_lua_MakeRandomInt(lua_State* state) {
sint32 min = lua_interface->GetSInt32Value(state);
sint32 max = lua_interface->GetSInt32Value(state, 2);
lua_interface->ResetFunctionStack(state);
sint32 result = MakeRandomInt(min, max);
lua_interface->SetSInt32Value(state, result);
return 1;
@ -11849,6 +11937,8 @@ int EQ2Emu_lua_MakeRandomFloat(lua_State* state) {
float min = lua_interface->GetFloatValue(state);
float max = lua_interface->GetFloatValue(state, 2);
lua_interface->ResetFunctionStack(state);
float result = MakeRandomFloat(min, max);
lua_interface->SetFloatValue(state, result);
return 1;
@ -11896,4 +11986,60 @@ int EQ2Emu_lua_RemoveIconValue(lua_State* state) {
lua_interface->SetBooleanValue(state, true);
return 1;
}
}
int EQ2Emu_lua_GetShardID(lua_State* state) {
Spawn* npc = lua_interface->GetSpawn(state);
lua_interface->ResetFunctionStack(state);
if (npc && npc->IsNPC()) {
NPC* shard = (NPC*)npc;
int32 shardid = shard->GetShardID();
lua_interface->SetInt32Value(state, shardid);
return 1;
}
lua_interface->SetInt32Value(state, 0);
return 1;
}
int EQ2Emu_lua_GetShardCharID(lua_State* state) {
Spawn* npc = lua_interface->GetSpawn(state);
lua_interface->ResetFunctionStack(state);
if (npc && npc->IsNPC()) {
NPC* shard = (NPC*)npc;
int32 charid = shard->GetShardCharID();
lua_interface->SetInt32Value(state, charid);
return 1;
}
lua_interface->SetInt32Value(state, 0);
return 1;
}
int EQ2Emu_lua_GetShardCreatedTimestamp(lua_State* state) {
Spawn* npc = lua_interface->GetSpawn(state);
lua_interface->ResetFunctionStack(state);
if (npc && npc->IsNPC()) {
NPC* shard = (NPC*)npc;
int64 timestamp = shard->GetShardCreatedTimestamp();
lua_interface->SetSInt64Value(state, timestamp);
return 1;
}
lua_interface->SetSInt64Value(state, 0);
return 1;
}
int EQ2Emu_lua_DeleteDBShardID(lua_State* state) {
if (!lua_interface)
return 0;
int32 shardid = lua_interface->GetInt32Value(state);
lua_interface->ResetFunctionStack(state);
if(shardid < 1)
lua_interface->SetBooleanValue(state, false);
else
lua_interface->SetBooleanValue(state, database.DeleteSpiritShard(shardid));
return 1;
}

View file

@ -183,6 +183,7 @@ int EQ2Emu_lua_Shout(lua_State* state);
int EQ2Emu_lua_SayOOC(lua_State* state);
int EQ2Emu_lua_Emote(lua_State* state);
int EQ2Emu_lua_IsPlayer(lua_State* state);
int EQ2Emu_lua_GetCharacterID(lua_State* state);
int EQ2Emu_lua_MovementLoopAdd(lua_State* state);
int EQ2Emu_lua_GetCurrentZoneSafeLocation(lua_State* state);
int EQ2Emu_lua_PlayFlavor(lua_State* state);
@ -497,6 +498,7 @@ int EQ2Emu_lua_SetSeeInvis(lua_State* state);
int EQ2Emu_lua_SetSeeHide(lua_State* state);
int EQ2Emu_lua_SetAccessToEntityCommand(lua_State* state);
int EQ2Emu_lua_SetAccessToEntityCommandByCharID(lua_State* state);
int EQ2Emu_lua_RemovePrimaryEntityCommand(lua_State* state);
int EQ2Emu_lua_SendUpdateDefaultCommand(lua_State* state);
@ -527,7 +529,9 @@ int EQ2Emu_lua_DamageSpawn(lua_State* state);
int EQ2Emu_lua_IsInvulnerable(lua_State* state);
int EQ2Emu_lua_SetInvulnerable(lua_State* state);
int EQ2Emu_lua_GetRuleFlagBool(lua_State* state);
int EQ2Emu_lua_GetRuleFlagInt32(lua_State* state);
int EQ2Emu_lua_GetRuleFlagFloat(lua_State* state);
int EQ2Emu_lua_GetAAInfo(lua_State* state);
int EQ2Emu_lua_SetAAInfo(lua_State* state);
@ -563,4 +567,9 @@ int EQ2Emu_lua_MakeRandomFloat(lua_State* state);
int EQ2Emu_lua_AddIconValue(lua_State* state);
int EQ2Emu_lua_RemoveIconValue(lua_State* state);
int EQ2Emu_lua_GetShardID(lua_State* state);
int EQ2Emu_lua_GetShardCharID(lua_State* state);
int EQ2Emu_lua_GetShardCreatedTimestamp(lua_State* state);
int EQ2Emu_lua_DeleteDBShardID(lua_State* state);
#endif

View file

@ -843,6 +843,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state, "IsPlayer", EQ2Emu_lua_IsPlayer);
lua_register(state, "GetCharacterID", EQ2Emu_lua_GetCharacterID);
lua_register(state, "FaceTarget", EQ2Emu_lua_FaceTarget);
lua_register(state, "MoveToLocation", EQ2Emu_lua_MoveToLocation);
lua_register(state, "ClearRunningLocations", EQ2Emu_lua_ClearRunningLocations);
@ -1213,6 +1214,7 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state, "SetSeeHide", EQ2Emu_lua_SetSeeHide);
lua_register(state, "SetAccessToEntityCommand", EQ2Emu_lua_SetAccessToEntityCommand);
lua_register(state, "SetAccessToEntityCommandByCharID", EQ2Emu_lua_SetAccessToEntityCommandByCharID);
lua_register(state, "RemovePrimaryEntityCommand", EQ2Emu_lua_RemovePrimaryEntityCommand);
lua_register(state, "SendUpdateDefaultCommand", EQ2Emu_lua_SendUpdateDefaultCommand);
@ -1241,7 +1243,9 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state, "IsInvulnerable", EQ2Emu_lua_IsInvulnerable);
lua_register(state, "SetInvulnerable", EQ2Emu_lua_SetInvulnerable);
lua_register(state, "GetRuleFlagBool", EQ2Emu_lua_GetRuleFlagBool);
lua_register(state, "GetRuleFlagInt32", EQ2Emu_lua_GetRuleFlagInt32);
lua_register(state, "GetRuleFlagFloat", EQ2Emu_lua_GetRuleFlagFloat);
lua_register(state, "GetAAInfo", EQ2Emu_lua_GetAAInfo);
lua_register(state, "SetAAInfo", EQ2Emu_lua_SetAAInfo);
@ -1277,6 +1281,11 @@ void LuaInterface::RegisterFunctions(lua_State* state) {
lua_register(state, "AddIconValue", EQ2Emu_lua_AddIconValue);
lua_register(state, "RemoveIconValue", EQ2Emu_lua_RemoveIconValue);
lua_register(state, "GetShardID", EQ2Emu_lua_GetShardID);
lua_register(state, "GetShardCharID", EQ2Emu_lua_GetShardCharID);
lua_register(state, "GetShardCreatedTimestamp", EQ2Emu_lua_GetShardCreatedTimestamp);
lua_register(state, "DeleteDBShardID", EQ2Emu_lua_DeleteDBShardID);
}
void LuaInterface::LogError(const char* error, ...) {

View file

@ -143,6 +143,9 @@ void NPC::Initialize(){
following = false;
SetFollowTarget(0);
m_petDismissing = false;
m_ShardID = 0;
m_ShardCharID = 0;
m_ShardCreatedTimestamp = 0;
}
EQ2Packet* NPC::serialize(Player* player, int16 version){

View file

@ -146,6 +146,14 @@ public:
bool IsDismissing() { return m_petDismissing; }
void SetDismissing(bool val) { m_petDismissing = val; }
int32 GetShardID() { return m_ShardID; }
void SetShardID(int32 shardid) { m_ShardID = shardid; }
int32 GetShardCharID() { return m_ShardCharID; }
void SetShardCharID(int32 charid) { m_ShardCharID = charid; }
sint64 GetShardCreatedTimestamp() { return m_ShardCreatedTimestamp; }
void SetShardCreatedTimestamp(sint64 timestamp) { m_ShardCreatedTimestamp = timestamp; }
private:
MovementLocation* runback;
int8 cast_percentage;
@ -177,6 +185,10 @@ private:
// the brain class and not the function defined above
::Brain* m_brain;
Mutex MBrain;
int32 m_ShardID;
int32 m_ShardCharID;
sint64 m_ShardCreatedTimestamp;
};
#endif

View file

@ -759,7 +759,19 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
CalculateXPPercentages();
packet->setDataByName("current_adv_xp", info_struct->get_xp()); // confirmed DoV
packet->setDataByName("needed_adv_xp", info_struct->get_xp_needed());// confirmed DoV
packet->setDataByName("debt_adv_xp", info_struct->get_xp_debt());//95= 9500% //confirmed DoV
if(version >= 60114)
{
// AoM ends up the debt_adv_xp field is the percentage of xp to the next level needed to advance out of debt (WHYY CANT THIS JUST BE A PERCENTAGE LIKE DOV!)
float currentPctOfLevel = (float)info_struct->get_xp() / (float)info_struct->get_xp_needed();
float neededPctAdvanceOutOfDebt = currentPctOfLevel + (info_struct->get_xp_debt() / 100.0f);
packet->setDataByName("debt_adv_xp", neededPctAdvanceOutOfDebt);
}
else
{
packet->setDataByName("exp_debt", (int16)(info_struct->get_xp_debt()/10.0f));//95= 9500% //confirmed DoV
}
packet->setDataByName("current_trade_xp", info_struct->get_ts_xp());// confirmed DoV
packet->setDataByName("needed_trade_xp", info_struct->get_ts_xp_needed());// confirmed DoV
@ -1431,9 +1443,12 @@ EQ2Packet* PlayerInfo::serialize(int16 version, int16 modifyPos, int32 modifyVal
}
}
player->GetDetrimentMutex()->releasereadlock(__FUNCTION__, __LINE__);
packet->setDataByName("spirit_rank", 2);
packet->setDataByName("spirit", 1);
packet->setDataByName("spirit_progress", .67);
// disabling as not in use right now
//packet->setDataByName("spirit_rank", 2);
//packet->setDataByName("spirit", 1);
//packet->setDataByName("spirit_progress", .67);
packet->setDataByName("combat_exp_enabled", 1);
/*for (int i = 0; i < 12; i++) {
packet->setSubstructDataByName("spell_effects", "spell_id", i + 1, i);
@ -3777,6 +3792,35 @@ float Player::CalculateTSXP(int8 level){
return total * world.GetXPRate() * zone_xp_modifier;
}
void Player::CalculateOfflineDebtRecovery(int32 unix_timestamp)
{
float xpDebt = GetXPDebt();
// not a real timestamp to work with
if(unix_timestamp < 1 || xpDebt == 0.0f)
return;
uint32 diff = (Timer::GetCurrentTime2() - unix_timestamp)/1000;
float recoveryDebtPercentage = rule_manager.GetGlobalRule(R_Combat, ExperienceDebtRecoveryPercent)->GetFloat()/100.0f;
int32 recoveryPeriodSeconds = rule_manager.GetGlobalRule(R_Combat, ExperienceDebtRecoveryPeriod)->GetInt32();
if(recoveryDebtPercentage == 0.0f || recoveryPeriodSeconds < 1)
return;
float periodsPassed = (float)diff/(float)recoveryPeriodSeconds;
// not enough time passed to calculate debt xp recovered
if(periodsPassed < 1.0f)
return;
float debtToSubtract = xpDebt * ((recoveryDebtPercentage*periodsPassed)/100.0f);
if(debtToSubtract >= xpDebt)
GetInfoStruct()->set_xp_debt(0.0f);
else
GetInfoStruct()->set_xp_debt(xpDebt - debtToSubtract);
}
void Player::SetNeededXP(int32 val){
GetInfoStruct()->set_xp_needed(val);
}
@ -3808,7 +3852,7 @@ void Player::SetTSXP(int32 val) {
GetInfoStruct()->set_ts_xp(val);
}
int32 Player::GetXPDebt(){
float Player::GetXPDebt(){
return GetInfoStruct()->get_xp_debt();
}
@ -3830,9 +3874,38 @@ int32 Player::GetTSXP() {
bool Player::AddXP(int32 xp_amount){
MStats.lock();
xp_amount += ((xp_amount) * stats[ITEM_STAT_COMBATEXPMOD]) / 100;
xp_amount += (int32)(((float)xp_amount) * stats[ITEM_STAT_COMBATEXPMOD]) / 100;
MStats.unlock();
if(GetInfoStruct()->get_xp_debt())
{
float expRatioToDebt = rule_manager.GetGlobalRule(R_Combat, ExperienceToDebt)->GetFloat()/100.0f;
int32 amountToTakeFromDebt = (int32)((float)expRatioToDebt * (float)xp_amount);
int32 amountRequiredClearDebt = (GetInfoStruct()->get_xp_debt()/100.0f) * xp_amount;
if(amountToTakeFromDebt > amountRequiredClearDebt)
{
GetInfoStruct()->set_xp_debt(0.0f);
if(amountRequiredClearDebt > xp_amount)
xp_amount = 0;
else
xp_amount -= amountRequiredClearDebt;
}
else
{
float amountRemovedPct = ((float)amountToTakeFromDebt/(float)amountRequiredClearDebt);
GetInfoStruct()->set_xp_debt(GetInfoStruct()->get_xp_debt()-amountRemovedPct);
if(amountToTakeFromDebt > xp_amount)
xp_amount = 0;
else
xp_amount -= amountToTakeFromDebt;
}
}
// used up in xp debt
if(!xp_amount)
return true;
float current_xp_percent = ((float)GetXP()/(float)GetNeededXP())*100;
float miniding_min_percent = ((int)(current_xp_percent/10)+1)*10;
while((xp_amount + GetXP()) >= GetNeededXP()){
@ -6155,4 +6228,82 @@ void Player::SetSpawnMap(Spawn* spawn)
player_spawn_reverse_id_map.insert(make_pair(spawn,tmp_id));
index_mutex.releasewritelock(__FUNCTION__, __LINE__);
}
NPC* Player::InstantiateSpiritShard(float origX, float origY, float origZ, float origHeading, int32 origGridID, ZoneServer* origZone)
{
NPC* npc = new NPC();
string newName(GetName());
newName.append("'s spirit shard");
strcpy(npc->appearance.name, newName.c_str());
/*vector<EntityCommand*>* primary_command_list = zone->GetEntityCommandList(result.GetInt32(9));
vector<EntityCommand*>* secondary_command_list = zone->GetEntityCommandList(result.GetInt32(10));
if(primary_command_list){
npc->SetPrimaryCommands(primary_command_list);
npc->primary_command_list_id = result.GetInt32(9);
}
if(secondary_command_list){
npc->SetSecondaryCommands(secondary_command_list);
npc->secondary_command_list_id = result.GetInt32(10);
}*/
npc->appearance.level = GetLevel();
npc->appearance.race = GetRace();
npc->appearance.gender = GetGender();
npc->appearance.adventure_class = GetAdventureClass();
//npc->appearance.lua_race_id = result.GetInt16(74);
npc->appearance.model_type = GetModelType();
npc->appearance.soga_model_type = GetSogaModelType();
npc->appearance.display_name = 1;
npc->features.hair_type = GetHairType();
npc->features.hair_face_type = GetFacialHairType();
npc->features.wing_type = GetWingType();
npc->features.chest_type = GetChestType();
npc->features.legs_type = GetLegsType();
npc->features.soga_hair_type = GetSogaHairType();
npc->features.soga_hair_face_type = GetSogaFacialHairType();
npc->appearance.attackable = 0;
npc->appearance.show_level = 1;
npc->appearance.targetable = 1;
npc->appearance.show_command_icon = 1;
npc->appearance.display_hand_icon = 0;
npc->appearance.hide_hood = GetHideHood();
npc->size = GetSize();
npc->appearance.pos.collision_radius = appearance.pos.collision_radius;
npc->appearance.action_state = appearance.action_state;
npc->appearance.visual_state = 6193; // ghostly look
npc->appearance.mood_state = appearance.mood_state;
npc->appearance.emote_state = appearance.emote_state;
npc->appearance.pos.state = appearance.pos.state;
npc->appearance.activity_status = appearance.activity_status;
strncpy(npc->appearance.sub_title, appearance.sub_title, sizeof(npc->appearance.sub_title));
npc->SetPrefixTitle(GetPrefixTitle());
npc->SetSuffixTitle(GetSuffixTitle());
npc->SetLastName(GetLastName());
npc->SetX(origX);
npc->SetY(origY);
npc->SetZ(origZ);
npc->SetHeading(origHeading);
npc->SetSpawnOrigX(origX);
npc->SetSpawnOrigY(origY);
npc->SetSpawnOrigZ(origZ);
npc->SetSpawnOrigHeading(origHeading);
npc->appearance.pos.grid_id = origGridID;
const char* script = rule_manager.GetGlobalRule(R_Combat, SpiritShardSpawnScript)->GetString();
int32 dbid = database.CreateSpiritShard(newName.c_str(), GetLevel(), GetRace(), GetGender(), GetAdventureClass(), GetModelType(), GetSogaModelType(),
GetHairType(), GetFacialHairType(), GetWingType(), GetChestType(), GetLegsType(), GetSogaHairType(), GetSogaFacialHairType(), GetHideHood(),
GetSize(), npc->appearance.pos.collision_radius, npc->appearance.action_state, npc->appearance.visual_state, npc->appearance.mood_state,
npc->appearance.emote_state, npc->appearance.pos.state, npc->appearance.activity_status, npc->appearance.sub_title, GetPrefixTitle(), GetSuffixTitle(),
GetLastName(), origX, origY, origZ, origHeading, origGridID, GetCharacterID(), origZone->GetZoneID(), origZone->GetInstanceID());
npc->SetShardID(dbid);
npc->SetShardCharID(GetCharacterID());
npc->SetShardCreatedTimestamp(Timer::GetCurrentTime2());
if(script)
npc->SetSpawnScript(script);
return npc;
}

View file

@ -530,7 +530,7 @@ public:
void SetNeededTSXP();
void SetTSXP(int32 val);
int32 GetNeededXP();
int32 GetXPDebt();
float GetXPDebt();
int32 GetXP();
int32 GetNeededTSXP();
int32 GetTSXP();
@ -539,6 +539,7 @@ public:
bool DoubleXPEnabled();
float CalculateXP(Spawn* victim);
float CalculateTSXP(int8 level);
void CalculateOfflineDebtRecovery(int32 unix_timestamp);
void InCombat(bool val, bool range = false);
void PrepareIncomingMovementPacket(int32 len, uchar* data, int16 version);
uchar* GetMovementPacketData(){
@ -956,6 +957,7 @@ public:
}
}
NPC* InstantiateSpiritShard(float origX, float origY, float origZ, float origHeading, int32 origGridID, ZoneServer* origZone);

View file

@ -218,6 +218,17 @@ void RuleManager::Init()
/* COMBAT */
RULE_INIT(R_Combat, MaxCombatRange, "4.0");
RULE_INIT(R_Combat, DeathExperienceDebt, "50.00"); // divide by 100, 50/100 = .5% debt per pve death
RULE_INIT(R_Combat, PVPDeathExperienceDebt, "25.00"); // divide by 100, 25/100 = .25% debt per pvp death
RULE_INIT(R_Combat, GroupExperienceDebt, "0"); // set to 1 means we will share debt between the group
RULE_INIT(R_Combat, ExperienceToDebt, "50.00"); // percentage of xp earned to debt vs obtained xp 50/100 = 50% to debt
RULE_INIT(R_Combat, ExperienceDebtRecoveryPercent, "5.00"); // recovery percentage per period of time, 5/100 = 5% recovered (so if .5% debt, .5*.05 = .025, .5-.025=.475% debt left)
RULE_INIT(R_Combat, ExperienceDebtRecoveryPeriod, "600"); // every 10 minutes (x*60 seconds) recover ExperienceDebtRecoveryPercent
RULE_INIT(R_Combat, EnableSpiritShards, "1");
RULE_INIT(R_Combat, SpiritShardSpawnScript, "SpawnScripts/Generic/SpiritShard.lua");
RULE_INIT(R_Combat, ShardDebtRecoveryPercent, "25.00"); // recovered percentage of debt upon obtainig shard, 25/100 means 25%. If there is .5 DeathExperienceDebt, .5*25% = .125, .5 - .125 = .375
RULE_INIT(R_Combat, ShardRecoveryByRadius, "1"); // allow shards to auto pick up by radius, not requiring to click/right click the shard
/* SPAWN */
RULE_INIT(R_Spawn, SpeedMultiplier, "300"); // note: this value was 1280 until 6/1/2009, then was 600 til Sep 2009, when it became 300...?
RULE_INIT(R_Spawn, ClassicRegen, "0");

View file

@ -78,6 +78,16 @@ enum RuleType {
/* COMBAT */
MaxCombatRange,
DeathExperienceDebt,
GroupExperienceDebt,
PVPDeathExperienceDebt,
ExperienceToDebt,
ExperienceDebtRecoveryPercent,
ExperienceDebtRecoveryPeriod,
EnableSpiritShards,
SpiritShardSpawnScript,
ShardDebtRecoveryPercent,
ShardRecoveryByRadius,
/* SPAWN */
SpeedMultiplier,

View file

@ -3849,18 +3849,21 @@ void Spawn::RemovePrimaryEntityCommand(const char* command) {
bool Spawn::SetPermissionToEntityCommand(EntityCommand* command, Player* player, bool permissionValue)
{
if (player != NULL)
{
map<int32, bool>::iterator itr = command->allow_or_deny.find(player->GetCharacterID());
if (itr == command->allow_or_deny.end())
command->allow_or_deny.insert(make_pair(player->GetCharacterID(), permissionValue));
else if (itr->second != permissionValue)
itr->second = permissionValue;
if(!player)
return false;
return SetPermissionToEntityCommandByCharID(command, player->GetCharacterID(), permissionValue);
}
return true;
}
return false;
bool Spawn::SetPermissionToEntityCommandByCharID(EntityCommand* command, int32 charID, bool permissionValue)
{
map<int32, bool>::iterator itr = command->allow_or_deny.find(charID);
if (itr == command->allow_or_deny.end())
command->allow_or_deny.insert(make_pair(charID, permissionValue));
else if (itr->second != permissionValue)
itr->second = permissionValue;
return true;
}
void Spawn::RemoveSpawnFromPlayer(Player* player)

View file

@ -314,6 +314,7 @@ public:
void AddPrimaryEntityCommand(const char* name, float distance, const char* command, const char* error_text, int16 cast_time, int32 spell_visual, bool defaultDenyList = false, Player* player = NULL);
void RemovePrimaryEntityCommand(const char* command);
bool SetPermissionToEntityCommand(EntityCommand* command, Player* player, bool permissionValue);
bool SetPermissionToEntityCommandByCharID(EntityCommand* command, int32 charID, bool permissionValue);
void RemoveSpawnFromPlayer(Player* player);

View file

@ -1064,6 +1064,112 @@ void WorldDatabase::LoadNPCs(ZoneServer* zone){
LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i NPC Equipment Appearance(s).", LoadNPCAppearanceEquipmentData(zone));
}
void WorldDatabase::LoadSpiritShards(ZoneServer* zone){
Query query;
MYSQL_ROW row;
NPC* npc = 0;
int32 id = 0;
int32 total = 0;
MYSQL_RES* result = query.RunQuery2(Q_SELECT,"SELECT timestamp, name, level, race, gender, adventure_class, model_type, soga_model_type, hair_type, hair_face_type, wing_type, chest_type, legs_type, soga_hair_type, soga_hair_face_type, hide_hood, size, collision_radius, action_state, visual_state, mood_state, emote_state, pos_state, activity_status, sub_title, prefix_title, suffix_title, lastname, x, y, z, heading, gridid, id, charid\n"
"FROM character_spirit_shards\n"
"WHERE zoneid = %u and (instanceid = 0 or instanceid = %u)",
zone->GetZoneID(), zone->GetInstanceID());
while(result && (row = mysql_fetch_row(result))){
/*npc->SetAppearanceID(atoi(row[12]));
AppearanceData* appearance = world.GetNPCAppearance(npc->GetAppearanceID());
if(appearance)
memcpy(&npc->appearance, appearance, sizeof(AppearanceData));
*/
sint64 timestamp = 0;
#ifdef WIN32
timestamp = _strtoui64(row[0], NULL, 10);
#else
timestamp = strtoull(row[0], 0, 10);
#endif
if(!row[1])
continue;
NPC* shard = new NPC();
shard->SetShardCreatedTimestamp(timestamp);
strcpy(shard->appearance.name, row[1]);
shard->appearance.level = atoul(row[2]);
shard->appearance.race = atoul(row[3]);
shard->appearance.gender = atoul(row[4]);
shard->appearance.adventure_class = atoul(row[5]);
//shard->appearance.lua_race_id = result.GetInt16(74);
shard->appearance.model_type = atoul(row[6]);
shard->appearance.soga_model_type = atoul(row[7]);
shard->appearance.display_name = 1;
shard->features.hair_type = atoul(row[8]);
shard->features.hair_face_type = atoul(row[9]);
shard->features.wing_type = atoul(row[10]);
shard->features.chest_type = atoul(row[11]);
shard->features.legs_type = atoul(row[12]);
shard->features.soga_hair_type = atoul(row[13]);
shard->features.soga_hair_face_type = atoul(row[14]);
shard->appearance.attackable = 0;
shard->appearance.show_level = 1;
shard->appearance.targetable = 1;
shard->appearance.show_command_icon = 1;
shard->appearance.display_hand_icon = 0;
shard->appearance.hide_hood = atoul(row[15]);
shard->size = atoul(row[16]);
shard->appearance.pos.collision_radius = atoul(row[17]);
shard->appearance.action_state = atoul(row[18]);
shard->appearance.visual_state = atoul(row[19]); // ghostly look
shard->appearance.mood_state = atoul(row[20]);
shard->appearance.emote_state = atoul(row[21]);
shard->appearance.pos.state = atoul(row[22]);
shard->appearance.activity_status = atoul(row[23]);
if(row[24])
strncpy(shard->appearance.sub_title, row[24], sizeof(shard->appearance.sub_title));
if(row[25])
shard->SetPrefixTitle(row[25]);
if(row[26])
shard->SetSuffixTitle(row[26]);
if(row[27])
shard->SetLastName(row[27]);
shard->SetX(atof(row[28]));
shard->SetY(atof(row[29]));
shard->SetZ(atof(row[30]));
shard->SetHeading(atof(row[31]));
shard->SetSpawnOrigX(shard->GetX());
shard->SetSpawnOrigY(shard->GetY());
shard->SetSpawnOrigZ(shard->GetZ());
shard->SetSpawnOrigHeading(shard->GetHeading());
shard->appearance.pos.grid_id = atoul(row[32]);
shard->SetShardID(atoul(row[33]));
shard->SetShardCharID(atoul(row[34]));
const char* script = rule_manager.GetGlobalRule(R_Combat, SpiritShardSpawnScript)->GetString();
if(script)
{
shard->SetSpawnScript(script);
zone->CallSpawnScript(shard, SPAWN_SCRIPT_PRESPAWN);
}
zone->AddSpawn(shard);
if(script)
zone->CallSpawnScript(shard, SPAWN_SCRIPT_SPAWN);
total++;
LogWrite(NPC__DEBUG, 5, "NPC", "---Loading Player Spirit Shard: '%s' (%u)", npc->appearance.name, id);
}
LogWrite(NPC__INFO, 0, "NPC", "--Loaded %i Spirit Shard(s).", total);
}
void WorldDatabase::LoadSigns(ZoneServer* zone){
Query query;
MYSQL_ROW row;
@ -1513,7 +1619,7 @@ bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* clien
if(info->get_xp_needed()== 0)
client->GetPlayer()->SetNeededXP();
info->set_xp_debt(result.GetInt32Str("xp_debt"));
info->set_xp_debt(result.GetFloatStr("xp_debt"));
info->set_xp_vitality(result.GetFloatStr("xp_vitality"));
info->set_ts_xp(result.GetInt32Str("tradeskill_xp"));
info->set_ts_xp_needed(result.GetInt32Str("tradeskill_xp_needed"));
@ -3810,7 +3916,7 @@ void WorldDatabase::Save(Client* client){
if(client->GetCurrentZone())
zone_id = client->GetCurrentZone()->GetZoneID();
query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u where id = %u", zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : 0, client->GetCharacterID());
query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %u, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i where char_id = %u",
query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i where char_id = %u",
player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(),
player->GetMentalResistanceBase(), player->GetDivineResistanceBase(), player->GetDiseaseResistanceBase(), player->GetPoisonResistanceBase(), player->GetCoinsCopper(), player->GetCoinsSilver(), player->GetCoinsGold(), player->GetCoinsPlat(), player->GetTotalHPBase(), player->GetTotalPowerBase(), player->GetXP(), player->GetNeededXP(), player->GetXPDebt(), player->GetXPVitality(), player->GetTSXP(), player->GetNeededTSXP(), player->GetTSXPVitality(), player->GetBankCoinsCopper(),
player->GetBankCoinsSilver(), player->GetBankCoinsGold(), player->GetBankCoinsPlat(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneID(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneX(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneY(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneZ(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneHeading(), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(),
@ -7169,3 +7275,49 @@ void WorldDatabase::LoadStartingSpells(World* world)
world->MStartingLists.releasewritelock();
}
bool WorldDatabase::DeleteSpiritShard(int32 id){
Query query;
query.RunQuery2(Q_DELETE, "delete FROM character_spirit_shards where id=%u",id);
if(query.GetErrorNumber() && query.GetError() && query.GetErrorNumber() < 0xFFFFFFFF){
LogWrite(WORLD__ERROR, 0, "World", "Error in DeleteSpiritShard query '%s': %s", query.GetQuery(), query.GetError());
return false;
}
return true;
}
int32 WorldDatabase::CreateSpiritShard(const char* name, int32 level, int8 race, int8 gender, int8 adventure_class,
int16 model_type, int16 soga_model_type, int16 hair_type, int16 hair_face_type, int16 wing_type,
int16 chest_type, int16 legs_type, int16 soga_hair_type, int16 soga_hair_face_type, int8 hide_hood,
int16 size, int16 collision_radius, int16 action_state, int16 visual_state, int16 mood_state, int16 emote_state,
int16 pos_state, int16 activity_status, char* sub_title, char* prefix_title, char* suffix_title, char* lastname,
float x, float y, float z, float heading, int32 gridid, int32 charid, int32 zoneid, int32 instanceid)
{
LogWrite(WORLD__INFO, 3, "World", "Saving Spirit Shard %s %u", name, charid);
Query query;
char* name_escaped = getEscapeString(name);
if(!sub_title)
sub_title = "";
char* subtitle_escaped = getEscapeString(sub_title);
char* prefix_escaped = getEscapeString(prefix_title);
char* suffix_escaped = getEscapeString(suffix_title);
char* lastname_escaped = getEscapeString(lastname);
string insert = string("INSERT INTO character_spirit_shards (name, level, race, gender, adventure_class, model_type, soga_model_type, hair_type, hair_face_type, wing_type, chest_type, legs_type, soga_hair_type, soga_hair_face_type, hide_hood, size, collision_radius, action_state, visual_state, mood_state, emote_state, pos_state, activity_status, sub_title, prefix_title, suffix_title, lastname, x, y, z, heading, gridid, charid, zoneid, instanceid) VALUES ('%s', %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, '%s', '%s', '%s', '%s', %f, %f, %f, %f, %u, %u, %u, %u)");
query.RunQuery2(Q_INSERT, insert.c_str(), name_escaped, level, race, gender, adventure_class, model_type, soga_model_type,
hair_type, hair_face_type, wing_type, chest_type, legs_type, soga_hair_type,
soga_hair_face_type, hide_hood, size, collision_radius, action_state, visual_state,
mood_state, emote_state, pos_state, activity_status, subtitle_escaped, prefix_escaped, suffix_escaped,
lastname_escaped, x, y, z, heading, gridid, charid, zoneid, instanceid);
safe_delete_array(name_escaped);
safe_delete_array(subtitle_escaped);
safe_delete_array(prefix_escaped);
safe_delete_array(suffix_escaped);
safe_delete_array(lastname_escaped);
return query.GetLastInsertedID();
}

View file

@ -245,6 +245,7 @@ public:
void LoadSpawns(ZoneServer* zone);
int8 GetAppearanceType(string type);
void LoadNPCs(ZoneServer* zone);
void LoadSpiritShards(ZoneServer* zone);
int32 LoadAppearances(ZoneServer* zone, Client* client = 0);
int32 LoadNPCSpells(ZoneServer* zone);
int32 LoadNPCSkills(ZoneServer* zone);
@ -594,6 +595,14 @@ public:
void LoadStartingSkills(World* world);
void LoadStartingSpells(World* world);
int32 CreateSpiritShard(const char* name, int32 level, int8 race, int8 gender, int8 adventure_class,
int16 model_type, int16 soga_model_type, int16 hair_type, int16 hair_face_type, int16 wing_type,
int16 chest_type, int16 legs_type, int16 soga_hair_type, int16 soga_hair_face_type, int8 hide_hood,
int16 size, int16 collision_radius, int16 action_state, int16 visual_state, int16 mood_state, int16 emote_state,
int16 pos_state, int16 activity_status, char* sub_title, char* prefix_title, char* suffix_title, char* lastname,
float x, float y, float z, float heading, int32 gridid, int32 charid, int32 zoneid, int32 instanceid);
bool DeleteSpiritShard(int32 id);
private:
DatabaseNew database_new;
map<int32, string> zone_names;

View file

@ -199,6 +199,7 @@ Client::Client(EQStream* ieqs) : pos_update(125), quest_pos_timer(2000), lua_deb
lastRegionRemapTime = 0;
regionDebugMessaging = false;
client_reloading_zone = false;
last_saved_timestamp = 0;
}
Client::~Client() {
@ -505,6 +506,14 @@ void Client::HandlePlayerRevive(int32 point_id)
QueuePacket(packet->serialize());
safe_delete(packet);
}
float origX, origY, origZ, origHeading = 0.0f;
origX = player->GetX();
origY = player->GetY();
origZ = player->GetZ();
origHeading = player->GetHeading();
ZoneServer* originalZone = GetCurrentZone();
int32 origGridID = GetPlayer()->appearance.pos.grid_id;
float x, y, z, heading;
RevivePoint* revive_point = 0;
@ -621,8 +630,21 @@ void Client::HandlePlayerRevive(int32 point_id)
QueuePacket(packet->serialize());
safe_delete(packet);
}
if(rule_manager.GetGlobalRule(R_Combat, EnableSpiritShards)->GetBool())
{
NPC* shard = player->InstantiateSpiritShard(origX, origY, origZ, origHeading, origGridID, originalZone);
GetCurrentZone()->RemoveSpawn(player, false);
if(shard->GetSpawnScript() && strlen(shard->GetSpawnScript()) > 0)
originalZone->CallSpawnScript(shard, SPAWN_SCRIPT_PRESPAWN);
originalZone->RemoveSpawn(player, false);
originalZone->AddSpawn(shard);
if(shard->GetSpawnScript() && strlen(shard->GetSpawnScript()) > 0)
originalZone->CallSpawnScript(shard, SPAWN_SCRIPT_SPAWN);
}
m_resurrect.writelock(__FUNCTION__, __LINE__);
if (current_rez.active)
@ -3987,6 +4009,7 @@ void Client::Save() {
UpdateCharacterInstances();
this->SetLastSavedTimeStamp(Timer::GetCurrentTime2());
database.Save(this);
if (GetPlayer()->UpdateQuickbarNeeded()) {
database.SaveQuickBar(GetCharacterID(), GetPlayer()->GetQuickbar());
@ -9249,6 +9272,7 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code)
firstlogin = zar->isFirstLogin();
LogWrite(ZONE__INFO, 0, "ZoneAuth", "Access Key: %u, Character Name: %s, Account ID: %u, Client Data Version: %u", zar->GetAccessKey(), zar->GetCharacterName(), zar->GetAccountID(), version);
if (database.loadCharacter(zar->GetCharacterName(), zar->GetAccountID(), this)) {
GetPlayer()->CalculateOfflineDebtRecovery(GetLastSavedTimeStamp());
GetPlayer()->vis_changed = false;
GetPlayer()->info_changed = false;

View file

@ -765,7 +765,12 @@ void ZoneServer::ProcessDepop(bool respawns_allowed, bool repop) {
DeleteData(false);
if(repop)
{
// reload spirit shards for the current zone
database.LoadSpiritShards(this);
LoadingData = true;
}
}
void ZoneServer::Depop(bool respawns, bool repop) {
@ -1244,6 +1249,10 @@ bool ZoneServer::Process()
if (reloading) {
LogWrite(COMMAND__DEBUG, 0, "Command", "-Loading Entity Commands...");
database.LoadEntityCommands(this);
LogWrite(NPC__INFO, 0, "NPC", "-Loading Spirit Shard data...");
database.LoadSpiritShards(this);
LogWrite(NPC__INFO, 0, "NPC", "-Load Spirit Shard data complete!");
LogWrite(NPC__INFO, 0, "NPC", "-Loading NPC data...");
database.LoadNPCs(this);
LogWrite(NPC__INFO, 0, "NPC", "-Load NPC data complete!");
@ -4195,6 +4204,8 @@ void ZoneServer::KillSpawn(bool spawnListLocked, Spawn* dead, Spawn* killer, boo
((Player*)dead)->UpdatePlayerStatistic(STAT_PLAYER_TOTAL_DEATHS, 1);
client = GetClientBySpawn(dead);
((Entity*)dead)->HandleDeathExperienceDebt(killer);
if(client) {
if(client->GetPlayer()->DamageEquippedItems(10, client))

View file

@ -0,0 +1,40 @@
--[[
Script Name : SpawnScripts/Generic/SpiritShard.lua
Script Purpose : Spirit Shard
--]]
function spawn(NPC)
local DebtToRemovePct = GetRuleFlagFloat("R_Combat", "ShardDebtRecoveryPercent")
if GetRuleFlagFloat("R_Combat", "ShardRecoveryByRadius") == true then
SetPlayerProximityFunction(NPC, 10.0, "recovershard")
end
AddPrimaryEntityCommand(nil,NPC,"Recover Spirit Shard",10.0,"recovershard","",0,0,1)
local charID = GetShardCharID(NPC)
SetAccessToEntityCommandByCharID(NPC, charID, "recovershard", true)
end
function recovershard(NPC, Spawn)
local charid = GetShardCharID(NPC)
if GetCharacterID(Spawn) == charid then
local DebtToRemovePct = GetRuleFlagFloat("R_Combat", "ShardDebtRecoveryPercent")
local DeathXPDebt = GetRuleFlagFloat("R_Combat", "DeathExperienceDebt")
local debt = GetInfoStructFloat(Spawn, "xp_debt")
local DebtToRemove = (DebtToRemovePct/100.0)*(DeathXPDebt/100.0);
if debt > DebtToRemove then
SetInfoStructFloat(Spawn, "xp_debt", debt - DebtToRemove)
else
SetInfoStructFloat(Spawn, "xp_debt", 0.0)
end
SendMessage(Spawn, "You recover a spirit shard and recovered some experience debt.", "white")
SetCharSheetChanged(Spawn, true)
local shardid = GetShardID(NPC)
DeleteDBShardID(shardid)
Despawn(NPC)
end
end