Merge branch 'master' into master

This commit is contained in:
Chris Miles 2017-11-21 21:26:52 -06:00 committed by GitHub
commit 712fe1f6ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 543 additions and 184 deletions

View File

@ -78,6 +78,8 @@ namespace EQEmu
SLOT_CURSOR_BAG_END = 340, SLOT_CURSOR_BAG_END = 340,
SLOT_TRIBUTE_BEGIN = 400, SLOT_TRIBUTE_BEGIN = 400,
SLOT_TRIBUTE_END = 404, SLOT_TRIBUTE_END = 404,
SLOT_GUILD_TRIBUTE_BEGIN = 450,
SLOT_GUILD_TRIBUTE_END = 451,
SLOT_BANK_BEGIN = 2000, SLOT_BANK_BEGIN = 2000,
SLOT_BANK_END = 2023, SLOT_BANK_END = 2023,
SLOT_BANK_BAGS_BEGIN = 2031, SLOT_BANK_BAGS_BEGIN = 2031,

View File

@ -4767,6 +4767,7 @@ struct BuffIconEntry_Struct
uint32 spell_id; uint32 spell_id;
int32 tics_remaining; int32 tics_remaining;
uint32 num_hits; uint32 num_hits;
char caster[64];
}; };
struct BuffIcon_Struct struct BuffIcon_Struct
@ -4776,6 +4777,7 @@ struct BuffIcon_Struct
uint16 count; uint16 count;
uint8 type; // 0 = self buff window, 1 = self target window, 4 = group, 5 = PC, 7 = NPC uint8 type; // 0 = self buff window, 1 = self target window, 4 = group, 5 = PC, 7 = NPC
int32 tic_timer; int32 tic_timer;
int32 name_lengths; // so ahh we kind of do these packets hacky, this is the total length of all the names to make creating the real packets in the translators easier
BuffIconEntry_Struct entries[0]; BuffIconEntry_Struct entries[0];
}; };

View File

@ -89,6 +89,7 @@ enum LogCategory {
HP_Update, HP_Update,
FixZ, FixZ,
Food, Food,
Traps,
MaxCategoryID /* Don't Remove this*/ MaxCategoryID /* Don't Remove this*/
}; };
@ -142,7 +143,8 @@ static const char* LogCategoryName[LogCategory::MaxCategoryID] = {
"Headless Client", "Headless Client",
"HP Update", "HP Update",
"FixZ", "FixZ",
"Food" "Food",
"Traps"
}; };
} }

View File

@ -460,7 +460,7 @@ namespace RoF
{ {
SETUP_VAR_ENCODE(BuffIcon_Struct); SETUP_VAR_ENCODE(BuffIcon_Struct);
uint32 sz = 12 + (17 * emu->count); uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
__packet->size = sz; __packet->size = sz;
__packet->pBuffer = new unsigned char[sz]; __packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz); memset(__packet->pBuffer, 0, sz);
@ -476,7 +476,7 @@ namespace RoF
__packet->WriteUInt32(emu->entries[i].spell_id); __packet->WriteUInt32(emu->entries[i].spell_id);
__packet->WriteUInt32(emu->entries[i].tics_remaining); __packet->WriteUInt32(emu->entries[i].tics_remaining);
__packet->WriteUInt32(emu->entries[i].num_hits); // Unknown __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown
__packet->WriteString(""); __packet->WriteString(emu->entries[i].caster);
} }
__packet->WriteUInt8(emu->type); // Unknown __packet->WriteUInt8(emu->type); // Unknown

View File

@ -528,7 +528,7 @@ namespace RoF2
{ {
SETUP_VAR_ENCODE(BuffIcon_Struct); SETUP_VAR_ENCODE(BuffIcon_Struct);
uint32 sz = 12 + (17 * emu->count); uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
__packet->size = sz; __packet->size = sz;
__packet->pBuffer = new unsigned char[sz]; __packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz); memset(__packet->pBuffer, 0, sz);
@ -544,7 +544,7 @@ namespace RoF2
__packet->WriteUInt32(emu->entries[i].spell_id); __packet->WriteUInt32(emu->entries[i].spell_id);
__packet->WriteUInt32(emu->entries[i].tics_remaining); __packet->WriteUInt32(emu->entries[i].tics_remaining);
__packet->WriteUInt32(emu->entries[i].num_hits); // Unknown __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown
__packet->WriteString(""); __packet->WriteString(emu->entries[i].caster);
} }
__packet->WriteUInt8(emu->type); // Unknown __packet->WriteUInt8(emu->type); // Unknown

View File

@ -391,7 +391,7 @@ namespace UF
{ {
SETUP_VAR_ENCODE(BuffIcon_Struct); SETUP_VAR_ENCODE(BuffIcon_Struct);
uint32 sz = 12 + (17 * emu->count); uint32 sz = 12 + (17 * emu->count) + emu->name_lengths; // 17 includes nullterm
__packet->size = sz; __packet->size = sz;
__packet->pBuffer = new unsigned char[sz]; __packet->pBuffer = new unsigned char[sz];
memset(__packet->pBuffer, 0, sz); memset(__packet->pBuffer, 0, sz);
@ -407,7 +407,7 @@ namespace UF
__packet->WriteUInt32(emu->entries[i].spell_id); __packet->WriteUInt32(emu->entries[i].spell_id);
__packet->WriteUInt32(emu->entries[i].tics_remaining); __packet->WriteUInt32(emu->entries[i].tics_remaining);
__packet->WriteUInt32(emu->entries[i].num_hits); __packet->WriteUInt32(emu->entries[i].num_hits);
__packet->WriteString(""); __packet->WriteString(emu->entries[i].caster);
} }
__packet->WriteUInt8(emu->type); __packet->WriteUInt8(emu->type);

View File

@ -44,6 +44,7 @@ RULE_INT(Character, DeathExpLossMaxLevel, 255) // Any level greater than this wi
RULE_INT(Character, DeathItemLossLevel, 10) RULE_INT(Character, DeathItemLossLevel, 10)
RULE_INT(Character, DeathExpLossMultiplier, 3) //Adjust how much exp is lost RULE_INT(Character, DeathExpLossMultiplier, 3) //Adjust how much exp is lost
RULE_BOOL(Character, UseDeathExpLossMult, false) //Adjust to use the above multiplier or to use code default. RULE_BOOL(Character, UseDeathExpLossMult, false) //Adjust to use the above multiplier or to use code default.
RULE_BOOL(Character, UseOldRaceRezEffects, false) // older clients had ID 757 for races with high starting STR, but it doesn't seem used anymore
RULE_INT(Character, CorpseDecayTimeMS, 10800000) RULE_INT(Character, CorpseDecayTimeMS, 10800000)
RULE_INT(Character, CorpseResTimeMS, 10800000) // time before cant res corpse(3 hours) RULE_INT(Character, CorpseResTimeMS, 10800000) // time before cant res corpse(3 hours)
RULE_BOOL(Character, LeaveCorpses, true) RULE_BOOL(Character, LeaveCorpses, true)

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9114 #define CURRENT_BINARY_DATABASE_VERSION 9115
#ifdef BOTS #ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017
#else #else

View File

@ -368,6 +368,7 @@
9112|2017_06_24_rule_values_expand.sql|SHOW COLUMNS FROM rule_values WHERE Field = 'rule_value' and Type = 'varchar(30)'|empty| 9112|2017_06_24_rule_values_expand.sql|SHOW COLUMNS FROM rule_values WHERE Field = 'rule_value' and Type = 'varchar(30)'|empty|
9113|2017_07_19_show_name.sql|SHOW COLUMNS FROM `npc_types` LIKE 'show_name'|empty| 9113|2017_07_19_show_name.sql|SHOW COLUMNS FROM `npc_types` LIKE 'show_name'|empty|
9114|2017_07_22_aura.sql|SHOW TABLES LIKE 'auras'|empty| 9114|2017_07_22_aura.sql|SHOW TABLES LIKE 'auras'|empty|
9115|2017_10_28_traps.sql|SHOW COLUMNS FROM `traps` LIKE 'triggered_number'|empty|
# Upgrade conditions: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,4 @@
alter table `traps` add column `triggered_number` tinyint(4) not null default 0;
alter table `traps` add column `group` tinyint(4) not null default 0;
alter table `traps` add column `despawn_when_triggered` tinyint(4) not null default 0;
alter table `traps` add column `undetectable` tinyint(4) not null default 0;

View File

@ -380,7 +380,7 @@ void Adventure::MoveCorpsesToGraveyard()
std::list<uint32> dbid_list; std::list<uint32> dbid_list;
std::list<uint32> charid_list; std::list<uint32> charid_list;
std::string query = StringFormat("SELECT id, charid FROM character_corpses WHERE instanceid=%d", GetInstanceID()); std::string query = StringFormat("SELECT id, charid FROM character_corpses WHERE instance_id=%d", GetInstanceID());
auto results = database.QueryDatabase(query); auto results = database.QueryDatabase(query);
if(!results.Success()) if(!results.Success())
@ -395,8 +395,8 @@ void Adventure::MoveCorpsesToGraveyard()
float z = GetTemplate()->graveyard_z; float z = GetTemplate()->graveyard_z;
query = StringFormat("UPDATE character_corpses " query = StringFormat("UPDATE character_corpses "
"SET zoneid = %d, instanceid = 0, " "SET zone_id = %d, instance_id = 0, "
"x = %f, y = %f, z = %f WHERE instanceid = %d", "x = %f, y = %f, z = %f WHERE instance_id = %d",
GetTemplate()->graveyard_zone_id, GetTemplate()->graveyard_zone_id,
x, y, z, GetInstanceID()); x, y, z, GetInstanceID());
database.QueryDatabase(query); database.QueryDatabase(query);

View File

@ -2533,6 +2533,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
if (other == this) if (other == this)
return; return;
if (other->IsTrap())
return;
if (damage < 0) { if (damage < 0) {
hate = 1; hate = 1;
} }
@ -3364,7 +3367,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
// pets that have GHold will never automatically add NPCs // pets that have GHold will never automatically add NPCs
// pets that have Hold and no Focus will add NPCs if they're engaged // pets that have Hold and no Focus will add NPCs if they're engaged
// pets that have Hold and Focus will not add NPCs // pets that have Hold and Focus will not add NPCs
if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld()) if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld() && !attacker->IsTrap())
{ {
if (!pet->IsHeld()) { if (!pet->IsHeld()) {
Log(Logs::Detail, Logs::Aggro, "Sending pet %s into battle due to attack.", pet->GetName()); Log(Logs::Detail, Logs::Aggro, "Sending pet %s into battle due to attack.", pet->GetName());
@ -3922,10 +3925,10 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *
float WPC = ProcChance * (100.0f + // Proc chance for this weapon float WPC = ProcChance * (100.0f + // Proc chance for this weapon
static_cast<float>(weapon->ProcRate)) / 100.0f; static_cast<float>(weapon->ProcRate)) / 100.0f;
if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really.
if (weapon->Proc.Level > ourlevel) { if (weapon->Proc.Level2 > ourlevel) {
Log(Logs::Detail, Logs::Combat, Log(Logs::Detail, Logs::Combat,
"Tried to proc (%s), but our level (%d) is lower than required (%d)", "Tried to proc (%s), but our level (%d) is lower than required (%d)",
weapon->Name, ourlevel, weapon->Proc.Level); weapon->Name, ourlevel, weapon->Proc.Level2);
if (IsPet()) { if (IsPet()) {
Mob *own = GetOwner(); Mob *own = GetOwner();
if (own) if (own)
@ -3962,7 +3965,7 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *
float APC = ProcChance * (100.0f + // Proc chance for this aug float APC = ProcChance * (100.0f + // Proc chance for this aug
static_cast<float>(aug->ProcRate)) / 100.0f; static_cast<float>(aug->ProcRate)) / 100.0f;
if (zone->random.Roll(APC)) { if (zone->random.Roll(APC)) {
if (aug->Proc.Level > ourlevel) { if (aug->Proc.Level2 > ourlevel) {
if (IsPet()) { if (IsPet()) {
Mob *own = GetOwner(); Mob *own = GetOwner();
if (own) if (own)

View File

@ -331,6 +331,8 @@ Client::Client(EQStreamInterface* ieqs)
interrogateinv_flag = false; interrogateinv_flag = false;
trapid = 0;
for (int i = 0; i < InnateSkillMax; ++i) for (int i = 0; i < InnateSkillMax; ++i)
m_pp.InnateSkills[i] = InnateDisabled; m_pp.InnateSkills[i] = InnateDisabled;
@ -1221,11 +1223,6 @@ void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num
EffSkill = 100; EffSkill = 100;
cm->skill_in_language = EffSkill; cm->skill_in_language = EffSkill;
// Garble the message based on listener skill
if (ListenerSkill < 100) {
GarbleMessage(buffer, (100 - ListenerSkill));
}
cm->chan_num = chan_num; cm->chan_num = chan_num;
strcpy(&cm->message[0], buffer); strcpy(&cm->message[0], buffer);
QueuePacket(&app); QueuePacket(&app);

View File

@ -1302,6 +1302,8 @@ public:
int32 CalcATK(); int32 CalcATK();
uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns.
protected: protected:
friend class Mob; friend class Mob;
void CalcItemBonuses(StatBonuses* newbon); void CalcItemBonuses(StatBonuses* newbon);

View File

@ -5329,31 +5329,44 @@ void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app)
p_timers.Start(pTimerDisarmTraps, reuse - 1); p_timers.Start(pTimerDisarmTraps, reuse - 1);
Trap* trap = entity_list.FindNearbyTrap(this, 60); uint8 success = SKILLUP_FAILURE;
float curdist = 0;
Trap* trap = entity_list.FindNearbyTrap(this, 250, curdist, true);
if (trap && trap->detected) if (trap && trap->detected)
{ {
int uskill = GetSkill(EQEmu::skills::SkillDisarmTraps); float max_radius = (trap->radius * 2) * (trap->radius * 2); // radius is used to trigger trap, so disarm radius should be a bit bigger.
if ((zone->random.Int(0, 49) + uskill) >= (zone->random.Int(0, 49) + trap->skill)) Log(Logs::General, Logs::Traps, "%s is attempting to disarm trap %d. Curdist is %0.2f maxdist is %0.2f", GetName(), trap->trap_id, curdist, max_radius);
if (curdist <= max_radius)
{ {
Message(MT_Skills, "You disarm a trap."); int uskill = GetSkill(EQEmu::skills::SkillDisarmTraps);
trap->disarmed = true; if ((zone->random.Int(0, 49) + uskill) >= (zone->random.Int(0, 49) + trap->skill))
trap->chkarea_timer.Disable(); {
trap->respawn_timer.Start((trap->respawn_time + zone->random.Int(0, trap->respawn_var)) * 1000); success = SKILLUP_SUCCESS;
Message_StringID(MT_Skills, DISARMED_TRAP);
trap->disarmed = true;
Log(Logs::General, Logs::Traps, "Trap %d is disarmed.", trap->trap_id);
trap->UpdateTrap();
}
else
{
Message_StringID(MT_Skills, FAIL_DISARM_DETECTED_TRAP);
if (zone->random.Int(0, 99) < 25) {
trap->Trigger(this);
}
}
CheckIncreaseSkill(EQEmu::skills::SkillDisarmTraps, nullptr);
return;
} }
else else
{ {
if (zone->random.Int(0, 99) < 25) { Message_StringID(MT_Skills, TRAP_TOO_FAR);
Message(MT_Skills, "You set off the trap while trying to disarm it!");
trap->Trigger(this);
}
else {
Message(MT_Skills, "You failed to disarm a trap.");
}
} }
CheckIncreaseSkill(EQEmu::skills::SkillDisarmTraps, nullptr);
return;
} }
Message(MT_Skills, "You did not find any traps close enough to disarm."); else
{
Message_StringID(MT_Skills, LDON_SENSE_TRAP2);
}
return; return;
} }
@ -11480,7 +11493,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
Client *client_moved = entity_list.GetClientByName(raid_command_packet->leader_name); Client *client_moved = entity_list.GetClientByName(raid_command_packet->leader_name);
if (client_moved) { if (client_moved && client_moved->GetRaid()) {
client_moved->GetRaid()->SendHPManaEndPacketsTo(client_moved); client_moved->GetRaid()->SendHPManaEndPacketsTo(client_moved);
client_moved->GetRaid()->SendHPManaEndPacketsFrom(client_moved); client_moved->GetRaid()->SendHPManaEndPacketsFrom(client_moved);
@ -12107,7 +12120,8 @@ void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app)
p_timers.Start(pTimerSenseTraps, reuse - 1); p_timers.Start(pTimerSenseTraps, reuse - 1);
Trap* trap = entity_list.FindNearbyTrap(this, 800); float trap_curdist = 0;
Trap* trap = entity_list.FindNearbyTrap(this, 800, trap_curdist);
CheckIncreaseSkill(EQEmu::skills::SkillSenseTraps, nullptr); CheckIncreaseSkill(EQEmu::skills::SkillSenseTraps, nullptr);

View File

@ -269,8 +269,17 @@ bool Client::Process() {
} }
} }
if (force_spawn_updates && mob != this && distance <= client_update_range) if (force_spawn_updates && mob != this) {
mob->SendPositionUpdateToClient(this);
if (mob->is_distance_roamer) {
Log(Logs::General, Logs::Debug, "Updating distance roamer %s", mob->GetCleanName());
mob->SendPositionUpdateToClient(this);
continue;
}
if (distance <= client_update_range)
mob->SendPositionUpdateToClient(this);
}
} }
} }
@ -1055,7 +1064,8 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
SetMana(0); SetMana(0);
SetHP(GetMaxHP()/5); SetHP(GetMaxHP()/5);
int rez_eff = 756; int rez_eff = 756;
if (GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE) if (RuleB(Character, UseOldRaceRezEffects) &&
(GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE))
rez_eff = 757; rez_eff = 757;
SpellOnTarget(rez_eff, this); // Rezz effects SpellOnTarget(rez_eff, this); // Rezz effects
} }

View File

@ -321,6 +321,7 @@ int command_init(void)
command_add("reloadqst", " - Clear quest cache (any argument causes it to also stop all timers)", 150, command_reloadqst) || command_add("reloadqst", " - Clear quest cache (any argument causes it to also stop all timers)", 150, command_reloadqst) ||
command_add("reloadrulesworld", "Executes a reload of all rules in world specifically.", 80, command_reloadworldrules) || command_add("reloadrulesworld", "Executes a reload of all rules in world specifically.", 80, command_reloadworldrules) ||
command_add("reloadstatic", "- Reload Static Zone Data", 150, command_reloadstatic) || command_add("reloadstatic", "- Reload Static Zone Data", 150, command_reloadstatic) ||
command_add("reloadtraps", "- Repops all traps in the current zone.", 80, command_reloadtraps) ||
command_add("reloadtitles", "- Reload player titles from the database", 150, command_reloadtitles) || command_add("reloadtitles", "- Reload player titles from the database", 150, command_reloadtitles) ||
command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", 255, command_reloadworld) || command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", 255, command_reloadworld) ||
command_add("reloadzps", "- Reload zone points from database", 150, command_reloadzps) || command_add("reloadzps", "- Reload zone points from database", 150, command_reloadzps) ||
@ -383,6 +384,7 @@ int command_init(void)
command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", 50, command_title) || command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", 50, command_title) ||
command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", 50, command_titlesuffix) || command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", 50, command_titlesuffix) ||
command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) ||
command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) ||
command_add("tune", "Calculate ideal statical values related to combat.", 100, command_tune) || command_add("tune", "Calculate ideal statical values related to combat.", 100, command_tune) ||
command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) || command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) ||
command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) || command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) ||
@ -10872,6 +10874,16 @@ void command_reloadperlexportsettings(Client *c, const Seperator *sep)
} }
} }
void command_trapinfo(Client *c, const Seperator *sep)
{
entity_list.GetTrapInfo(c);
}
void command_reloadtraps(Client *c, const Seperator *sep)
{
entity_list.UpdateAllTraps(true, true);
c->Message(CC_Default, "Traps reloaded for %s.", zone->GetShortName());
}
// All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block.
#ifdef BOTS #ifdef BOTS

View File

@ -228,6 +228,7 @@ void command_reloadperlexportsettings(Client *c, const Seperator *sep);
void command_reloadqst(Client *c, const Seperator *sep); void command_reloadqst(Client *c, const Seperator *sep);
void command_reloadstatic(Client *c, const Seperator *sep); void command_reloadstatic(Client *c, const Seperator *sep);
void command_reloadtitles(Client *c, const Seperator *sep); void command_reloadtitles(Client *c, const Seperator *sep);
void command_reloadtraps(Client* c, const Seperator *sep);
void command_reloadworld(Client *c, const Seperator *sep); void command_reloadworld(Client *c, const Seperator *sep);
void command_reloadworldrules(Client *c, const Seperator *sep); void command_reloadworldrules(Client *c, const Seperator *sep);
void command_reloadzps(Client *c, const Seperator *sep); void command_reloadzps(Client *c, const Seperator *sep);
@ -296,6 +297,7 @@ void command_timezone(Client *c, const Seperator *sep);
void command_title(Client *c, const Seperator *sep); void command_title(Client *c, const Seperator *sep);
void command_titlesuffix(Client *c, const Seperator *sep); void command_titlesuffix(Client *c, const Seperator *sep);
void command_traindisc(Client *c, const Seperator *sep); void command_traindisc(Client *c, const Seperator *sep);
void command_trapinfo(Client* c, const Seperator *sep);
void command_tune(Client *c, const Seperator *sep); void command_tune(Client *c, const Seperator *sep);
void command_undye(Client *c, const Seperator *sep); void command_undye(Client *c, const Seperator *sep);
void command_undyeme(Client *c, const Seperator *sep); void command_undyeme(Client *c, const Seperator *sep);

View File

@ -604,6 +604,11 @@ enum { //type arguments to DoAnim
}; };
enum {
SKILLUP_UNKNOWN = 0,
SKILLUP_SUCCESS = 1,
SKILLUP_FAILURE = 2
};
typedef enum { typedef enum {
petFamiliar, //only listens to /pet get lost petFamiliar, //only listens to /pet get lost

View File

@ -365,7 +365,7 @@ public:
//trap stuff //trap stuff
Mob* GetTrapTrigger(Trap* trap); Mob* GetTrapTrigger(Trap* trap);
void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos); void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos);
Trap* FindNearbyTrap(Mob* searcher, float max_dist); Trap* FindNearbyTrap(Mob* searcher, float max_dist, float &curdist, bool detected = false);
void AddHealAggro(Mob* target, Mob* caster, uint16 hate); void AddHealAggro(Mob* target, Mob* caster, uint16 hate);
Mob* FindDefenseNPC(uint32 npcid); Mob* FindDefenseNPC(uint32 npcid);
@ -473,6 +473,10 @@ public:
void RefreshClientXTargets(Client *c); void RefreshClientXTargets(Client *c);
void SendAlternateAdvancementStats(); void SendAlternateAdvancementStats();
void GetTrapInfo(Client* client);
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
void UpdateAllTraps(bool respawn, bool repopnow = false);
void ClearTrapPointers();
protected: protected:
friend class Zone; friend class Zone;
void Depop(bool StartSpawnTimer = false); void Depop(bool StartSpawnTimer = false);

View File

@ -75,7 +75,6 @@ Mob::Mob(const char* in_name,
uint32 in_drakkin_tattoo, uint32 in_drakkin_tattoo,
uint32 in_drakkin_details, uint32 in_drakkin_details,
EQEmu::TintProfile in_armor_tint, EQEmu::TintProfile in_armor_tint,
uint8 in_aa_title, uint8 in_aa_title,
uint8 in_see_invis, // see through invis/ivu uint8 in_see_invis, // see through invis/ivu
uint8 in_see_invis_undead, uint8 in_see_invis_undead,
@ -91,21 +90,21 @@ Mob::Mob(const char* in_name,
uint8 in_handtexture, uint8 in_handtexture,
uint8 in_legtexture, uint8 in_legtexture,
uint8 in_feettexture uint8 in_feettexture
) : ) :
attack_timer(2000), attack_timer(2000),
attack_dw_timer(2000), attack_dw_timer(2000),
ranged_timer(2000), ranged_timer(2000),
tic_timer(6000), tic_timer(6000),
mana_timer(2000), mana_timer(2000),
spellend_timer(0), spellend_timer(0),
rewind_timer(30000), //Timer used for determining amount of time between actual player position updates for /rewind. rewind_timer(30000),
bindwound_timer(10000), bindwound_timer(10000),
stunned_timer(0), stunned_timer(0),
spun_timer(0), spun_timer(0),
bardsong_timer(6000), bardsong_timer(6000),
gravity_timer(1000), gravity_timer(1000),
viral_timer(0), viral_timer(0),
m_FearWalkTarget(-999999.0f,-999999.0f,-999999.0f), m_FearWalkTarget(-999999.0f, -999999.0f, -999999.0f),
m_TargetLocation(glm::vec3()), m_TargetLocation(glm::vec3()),
m_TargetV(glm::vec3()), m_TargetV(glm::vec3()),
flee_timer(FLEE_CHECK_TIMER), flee_timer(FLEE_CHECK_TIMER),
@ -119,45 +118,46 @@ Mob::Mob(const char* in_name,
position_update_melee_push_timer(1000) position_update_melee_push_timer(1000)
{ {
targeted = 0; targeted = 0;
tar_ndx=0; tar_ndx = 0;
tar_vector=0; tar_vector = 0;
currently_fleeing = false; currently_fleeing = false;
last_major_update_position = m_Position; last_major_update_position = m_Position;
is_distance_roamer = false;
AI_Init(); AI_Init();
SetMoving(false); SetMoving(false);
moved=false; moved = false;
m_RewindLocation = glm::vec3(); m_RewindLocation = glm::vec3();
_egnode = nullptr; _egnode = nullptr;
name[0]=0; name[0] = 0;
orig_name[0]=0; orig_name[0] = 0;
clean_name[0]=0; clean_name[0] = 0;
lastname[0]=0; lastname[0] = 0;
if(in_name) { if (in_name) {
strn0cpy(name,in_name,64); strn0cpy(name, in_name, 64);
strn0cpy(orig_name,in_name,64); strn0cpy(orig_name, in_name, 64);
} }
if(in_lastname) if (in_lastname)
strn0cpy(lastname,in_lastname,64); strn0cpy(lastname, in_lastname, 64);
cur_hp = in_cur_hp; cur_hp = in_cur_hp;
max_hp = in_max_hp; max_hp = in_max_hp;
base_hp = in_max_hp; base_hp = in_max_hp;
gender = in_gender; gender = in_gender;
race = in_race; race = in_race;
base_gender = in_gender; base_gender = in_gender;
base_race = in_race; base_race = in_race;
class_ = in_class; class_ = in_class;
bodytype = in_bodytype; bodytype = in_bodytype;
orig_bodytype = in_bodytype; orig_bodytype = in_bodytype;
deity = in_deity; deity = in_deity;
level = in_level; level = in_level;
orig_level = in_level; orig_level = in_level;
npctype_id = in_npctype_id; npctype_id = in_npctype_id;
size = in_size; size = in_size;
base_size = size; base_size = size;
runspeed = in_runspeed; runspeed = in_runspeed;
// neotokyo: sanity check // neotokyo: sanity check
if (runspeed < 0 || runspeed > 20) if (runspeed < 0 || runspeed > 20)
runspeed = 1.25f; runspeed = 1.25f;
@ -170,7 +170,8 @@ Mob::Mob(const char* in_name,
fearspeed = 0.625f; fearspeed = 0.625f;
base_fearspeed = 25; base_fearspeed = 25;
// npcs // npcs
} else { }
else {
base_walkspeed = base_runspeed * 100 / 265; base_walkspeed = base_runspeed * 100 / 265;
walkspeed = ((float)base_walkspeed) * 0.025f; walkspeed = ((float)base_walkspeed) * 0.025f;
base_fearspeed = base_runspeed * 100 / 127; base_fearspeed = base_runspeed * 100 / 127;
@ -182,7 +183,7 @@ Mob::Mob(const char* in_name,
current_speed = base_runspeed; current_speed = base_runspeed;
m_PlayerState = 0; m_PlayerState = 0;
// sanity check // sanity check
@ -194,8 +195,8 @@ Mob::Mob(const char* in_name,
m_Light.Type[EQEmu::lightsource::LightActive] = m_Light.Type[EQEmu::lightsource::LightInnate]; m_Light.Type[EQEmu::lightsource::LightActive] = m_Light.Type[EQEmu::lightsource::LightInnate];
m_Light.Level[EQEmu::lightsource::LightActive] = m_Light.Level[EQEmu::lightsource::LightInnate]; m_Light.Level[EQEmu::lightsource::LightActive] = m_Light.Level[EQEmu::lightsource::LightInnate];
texture = in_texture; texture = in_texture;
helmtexture = in_helmtexture; helmtexture = in_helmtexture;
armtexture = in_armtexture; armtexture = in_armtexture;
bracertexture = in_bracertexture; bracertexture = in_bracertexture;
handtexture = in_handtexture; handtexture = in_handtexture;
@ -203,21 +204,21 @@ Mob::Mob(const char* in_name,
feettexture = in_feettexture; feettexture = in_feettexture;
multitexture = (armtexture || bracertexture || handtexture || legtexture || feettexture); multitexture = (armtexture || bracertexture || handtexture || legtexture || feettexture);
haircolor = in_haircolor; haircolor = in_haircolor;
beardcolor = in_beardcolor; beardcolor = in_beardcolor;
eyecolor1 = in_eyecolor1; eyecolor1 = in_eyecolor1;
eyecolor2 = in_eyecolor2; eyecolor2 = in_eyecolor2;
hairstyle = in_hairstyle; hairstyle = in_hairstyle;
luclinface = in_luclinface; luclinface = in_luclinface;
beard = in_beard; beard = in_beard;
drakkin_heritage = in_drakkin_heritage; drakkin_heritage = in_drakkin_heritage;
drakkin_tattoo = in_drakkin_tattoo; drakkin_tattoo = in_drakkin_tattoo;
drakkin_details = in_drakkin_details; drakkin_details = in_drakkin_details;
attack_speed = 0; attack_speed = 0;
attack_delay = 0; attack_delay = 0;
slow_mitigation = 0; slow_mitigation = 0;
findable = false; findable = false;
trackable = true; trackable = true;
has_shieldequiped = false; has_shieldequiped = false;
has_twohandbluntequiped = false; has_twohandbluntequiped = false;
has_twohanderequipped = false; has_twohanderequipped = false;
@ -228,19 +229,19 @@ Mob::Mob(const char* in_name,
SpellPowerDistanceMod = 0; SpellPowerDistanceMod = 0;
last_los_check = false; last_los_check = false;
if(in_aa_title>0) if (in_aa_title > 0)
aa_title = in_aa_title; aa_title = in_aa_title;
else else
aa_title =0xFF; aa_title = 0xFF;
AC = in_ac; AC = in_ac;
ATK = in_atk; ATK = in_atk;
STR = in_str; STR = in_str;
STA = in_sta; STA = in_sta;
DEX = in_dex; DEX = in_dex;
AGI = in_agi; AGI = in_agi;
INT = in_int; INT = in_int;
WIS = in_wis; WIS = in_wis;
CHA = in_cha; CHA = in_cha;
MR = CR = FR = DR = PR = Corrup = 0; MR = CR = FR = DR = PR = Corrup = 0;
ExtraHaste = 0; ExtraHaste = 0;
@ -261,8 +262,8 @@ Mob::Mob(const char* in_name,
hidden = false; hidden = false;
improved_hidden = false; improved_hidden = false;
invulnerable = false; invulnerable = false;
IsFullHP = (cur_hp == max_hp); IsFullHP = (cur_hp == max_hp);
qglobal=0; qglobal = 0;
spawned = false; spawned = false;
InitializeBuffSlots(); InitializeBuffSlots();
@ -374,13 +375,13 @@ Mob::Mob(const char* in_name,
} }
destructibleobject = false; destructibleobject = false;
wandertype=0; wandertype = 0;
pausetype=0; pausetype = 0;
cur_wp = 0; cur_wp = 0;
m_CurrentWayPoint = glm::vec4(); m_CurrentWayPoint = glm::vec4();
cur_wp_pause = 0; cur_wp_pause = 0;
patrol=0; patrol = 0;
follow=0; follow = 0;
follow_dist = 100; // Default Distance for Follow follow_dist = 100; // Default Distance for Follow
no_target_hotkey = false; no_target_hotkey = false;
flee_mode = false; flee_mode = false;
@ -394,7 +395,7 @@ Mob::Mob(const char* in_name,
rooted = false; rooted = false;
charmed = false; charmed = false;
has_virus = false; has_virus = false;
for (i=0; i<MAX_SPELL_TRIGGER*2; i++) { for (i = 0; i < MAX_SPELL_TRIGGER * 2; i++) {
viral_spells[i] = 0; viral_spells[i] = 0;
} }
pStandingPetOrder = SPO_Follow; pStandingPetOrder = SPO_Follow;
@ -425,7 +426,7 @@ Mob::Mob(const char* in_name,
nimbus_effect3 = 0; nimbus_effect3 = 0;
m_targetable = true; m_targetable = true;
m_TargetRing = glm::vec3(); m_TargetRing = glm::vec3();
flymode = FlyMode3; flymode = FlyMode3;
// Pathing // Pathing
@ -442,7 +443,7 @@ Mob::Mob(const char* in_name,
m_AllowBeneficial = false; m_AllowBeneficial = false;
m_DisableMelee = false; m_DisableMelee = false;
for (int i = 0; i < EQEmu::skills::HIGHEST_SKILL + 2; i++) { SkillDmgTaken_Mod[i] = 0; } for (int i = 0; i < EQEmu::skills::HIGHEST_SKILL + 2; i++) { SkillDmgTaken_Mod[i] = 0; }
for (int i = 0; i < HIGHEST_RESIST+2; i++) { Vulnerability_Mod[i] = 0; } for (int i = 0; i < HIGHEST_RESIST + 2; i++) { Vulnerability_Mod[i] = 0; }
emoteid = 0; emoteid = 0;
endur_upkeep = false; endur_upkeep = false;
@ -1445,6 +1446,7 @@ void Mob::SendPosition() {
if (DistanceSquared(last_major_update_position, m_Position) >= (100 * 100)) { if (DistanceSquared(last_major_update_position, m_Position) >= (100 * 100)) {
entity_list.QueueClients(this, app, true, true); entity_list.QueueClients(this, app, true, true);
last_major_update_position = m_Position; last_major_update_position = m_Position;
is_distance_roamer = true;
} }
else { else {
entity_list.QueueCloseClients(this, app, true, RuleI(Range, MobPositionUpdates), nullptr, false); entity_list.QueueCloseClients(this, app, true, RuleI(Range, MobPositionUpdates), nullptr, false);
@ -3395,13 +3397,18 @@ int Mob::GetHaste()
else // 1-25 else // 1-25
h += itembonuses.haste > 10 ? 10 : itembonuses.haste; h += itembonuses.haste > 10 ? 10 : itembonuses.haste;
// 60+ 100, 51-59 85, 1-50 level+25 // mobs are different!
if (level > 59) // 60+ Mob *owner = nullptr;
cap = RuleI(Character, HasteCap); if (IsPet())
else if (level > 50) // 51-59 owner = GetOwner();
cap = 85; else if (IsNPC() && CastToNPC()->GetSwarmOwner())
else // 1-50 owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
cap = level + 25; if (owner) {
cap = 10 + level;
cap += std::max(0, owner->GetLevel() - 39) + std::max(0, owner->GetLevel() - 60);
} else {
cap = 150;
}
if(h > cap) if(h > cap)
h = cap; h = cap;
@ -3960,10 +3967,17 @@ int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) {
bool Mob::TryFadeEffect(int slot) bool Mob::TryFadeEffect(int slot)
{ {
if (!buffs[slot].spellid)
return false;
if(IsValidSpell(buffs[slot].spellid)) if(IsValidSpell(buffs[slot].spellid))
{ {
for(int i = 0; i < EFFECT_COUNT; i++) for(int i = 0; i < EFFECT_COUNT; i++)
{ {
if (!spells[buffs[slot].spellid].effectid[i])
continue;
if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways ||
spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect) spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect)
{ {

View File

@ -162,6 +162,8 @@ public:
inline virtual bool IsMob() const { return true; } inline virtual bool IsMob() const { return true; }
inline virtual bool InZone() const { return true; } inline virtual bool InZone() const { return true; }
bool is_distance_roamer;
//Somewhat sorted: needs documenting! //Somewhat sorted: needs documenting!
//Attack //Attack
@ -955,7 +957,7 @@ public:
void SendToFixZ(float new_x, float new_y, float new_z); void SendToFixZ(float new_x, float new_y, float new_z);
float GetZOffset() const; float GetZOffset() const;
void FixZ(int32 z_find_offset = 5); void FixZ(int32 z_find_offset = 5);
float GetFixedZ(glm::vec3 position, int32 z_find_offset = 5); float GetFixedZ(glm::vec3 position, int32 z_find_offset = 5);
void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false);
inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; } inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; }
inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; } inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; }
@ -1225,6 +1227,7 @@ protected:
glm::vec4 m_Position; glm::vec4 m_Position;
/* Used to determine when an NPC has traversed so many units - to send a zone wide pos update */ /* Used to determine when an NPC has traversed so many units - to send a zone wide pos update */
glm::vec4 last_major_update_position; glm::vec4 last_major_update_position;
int animation; // this is really what MQ2 calls SpeedRun just packed like (int)(SpeedRun * 40.0f) int animation; // this is really what MQ2 calls SpeedRun just packed like (int)(SpeedRun * 40.0f)
float base_size; float base_size;
float size; float size;

View File

@ -1298,7 +1298,7 @@ void Mob::AI_Process() {
if (AI_PursueCastCheck()) { if (AI_PursueCastCheck()) {
//we did something, so do not process movement. //we did something, so do not process movement.
} }
else if (AI_movement_timer->Check()) else if (AI_movement_timer->Check() && target)
{ {
if (!IsRooted()) { if (!IsRooted()) {
Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName()); Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName());

View File

@ -542,8 +542,10 @@ int main(int argc, char** argv) {
process_timer.Stop(); process_timer.Stop();
process_timer.Start(1000, true); process_timer.Start(1000, true);
uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion()); if (zone && zone->GetZoneID() && zone->GetInstanceVersion()) {
zone->StartShutdownTimer(shutdown_timer); uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion());
zone->StartShutdownTimer(shutdown_timer);
}
} }
else if (!previous_loaded && current_loaded) { else if (!previous_loaded && current_loaded) {
process_timer.Stop(); process_timer.Stop();

View File

@ -436,6 +436,26 @@ void NPC::SetTarget(Mob* mob) {
//attack_timer.Disable(); //attack_timer.Disable();
attack_dw_timer.Disable(); attack_dw_timer.Disable();
} }
// either normal pet and owner is client or charmed pet and owner is client
Mob *owner = nullptr;
if (IsPet() && IsPetOwnerClient()) {
owner = GetOwner();
} else if (IsCharmed()) {
owner = GetOwner();
if (owner && !owner->IsClient())
owner = nullptr;
}
if (owner) {
auto client = owner->CastToClient();
if (client->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) {
auto app = new EQApplicationPacket(OP_PetHoTT, sizeof(ClientTarget_Struct));
auto ct = (ClientTarget_Struct *)app->pBuffer;
ct->new_target = mob ? mob->GetID() : 0;
client->FastQueuePacket(&app);
}
}
Mob::SetTarget(mob); Mob::SetTarget(mob);
} }

View File

@ -475,22 +475,6 @@ Pet::Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 po
// Class should use npc constructor to set light properties // Class should use npc constructor to set light properties
} }
void Pet::SetTarget(Mob *mob)
{
if (mob == GetTarget())
return;
auto owner = GetOwner();
if (owner && owner->IsClient() && owner->CastToClient()->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) {
auto app = new EQApplicationPacket(OP_PetHoTT, sizeof(ClientTarget_Struct));
auto ct = (ClientTarget_Struct *)app->pBuffer;
ct->new_target = mob ? mob->GetID() : 0;
owner->CastToClient()->QueuePacket(app);
safe_delete(app);
}
NPC::SetTarget(mob);
}
bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) { bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) {
return GetPoweredPetEntry(pet_type, 0, into); return GetPoweredPetEntry(pet_type, 0, into);
} }

View File

@ -7,7 +7,6 @@ struct NPCType;
class Pet : public NPC { class Pet : public NPC {
public: public:
Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power); Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power);
virtual void SetTarget(Mob *mob);
virtual bool CheckSpellLevelRestriction(uint16 spell_id); virtual bool CheckSpellLevelRestriction(uint16 spell_id);
}; };

View File

@ -323,6 +323,10 @@ void Raid::SaveRaidLeaderAA()
void Raid::UpdateGroupAAs(uint32 gid) void Raid::UpdateGroupAAs(uint32 gid)
{ {
if (gid < 0 || gid > MAX_RAID_GROUPS)
return;
Client *gl = GetGroupLeader(gid); Client *gl = GetGroupLeader(gid);
if (gl) if (gl)

View File

@ -37,6 +37,7 @@ int Mob::GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target)
case EQEmu::skills::SkillDragonPunch: case EQEmu::skills::SkillDragonPunch:
case EQEmu::skills::SkillEagleStrike: case EQEmu::skills::SkillEagleStrike:
case EQEmu::skills::SkillTigerClaw: case EQEmu::skills::SkillTigerClaw:
case EQEmu::skills::SkillRoundKick:
if (skill_level >= 25) if (skill_level >= 25)
base++; base++;
if (skill_level >= 75) if (skill_level >= 75)

View File

@ -137,6 +137,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
buffs[buffslot].magic_rune = 0; buffs[buffslot].magic_rune = 0;
buffs[buffslot].numhits = 0; buffs[buffslot].numhits = 0;
if (spells[spell_id].numhits > 0) {
int numhit = spells[spell_id].numhits;
numhit += numhit * caster->GetFocusEffect(focusFcLimitUse, spell_id) / 100;
numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id);
buffs[buffslot].numhits = numhit;
}
if (spells[spell_id].EndurUpkeep > 0) if (spells[spell_id].EndurUpkeep > 0)
SetEndurUpkeep(true); SetEndurUpkeep(true);
@ -184,14 +193,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
} }
} }
if(spells[spell_id].numhits > 0 && buffslot >= 0){
int numhit = spells[spell_id].numhits;
numhit += numhit*caster->GetFocusEffect(focusFcLimitUse, spell_id)/100;
numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id);
buffs[buffslot].numhits = numhit;
}
if (!IsPowerDistModSpell(spell_id)) if (!IsPowerDistModSpell(spell_id))
SetSpellPowerDistanceMod(0); SetSpellPowerDistanceMod(0);

View File

@ -3309,8 +3309,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
buffs[emptyslot].spellid = spell_id; buffs[emptyslot].spellid = spell_id;
buffs[emptyslot].casterlevel = caster_level; buffs[emptyslot].casterlevel = caster_level;
if (caster && caster->IsClient()) if (caster && !caster->IsAura()) // maybe some other things we don't want to ...
strcpy(buffs[emptyslot].caster_name, caster->GetName()); strcpy(buffs[emptyslot].caster_name, caster->GetCleanName());
else else
memset(buffs[emptyslot].caster_name, 0, 64); memset(buffs[emptyslot].caster_name, 0, 64);
buffs[emptyslot].casterid = caster ? caster->GetID() : 0; buffs[emptyslot].casterid = caster ? caster->GetID() : 0;
@ -5548,6 +5548,8 @@ void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot)
bi->entries[0].spell_id = buff.spellid; bi->entries[0].spell_id = buff.spellid;
bi->entries[0].tics_remaining = buff.ticsremaining; bi->entries[0].tics_remaining = buff.ticsremaining;
bi->entries[0].num_hits = buff.numhits; bi->entries[0].num_hits = buff.numhits;
strn0cpy(bi->entries[0].caster, buff.caster_name, 64);
bi->name_lengths = strlen(bi->entries[0].caster);
FastQueuePacket(&outapp); FastQueuePacket(&outapp);
} }
@ -5633,6 +5635,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
else else
buff->type = 0; buff->type = 0;
buff->name_lengths = 0; // hacky shit
uint32 index = 0; uint32 index = 0;
for(int i = 0; i < buff_count; ++i) for(int i = 0; i < buff_count; ++i)
{ {
@ -5642,6 +5645,8 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
buff->entries[index].spell_id = buffs[i].spellid; buff->entries[index].spell_id = buffs[i].spellid;
buff->entries[index].tics_remaining = buffs[i].ticsremaining; buff->entries[index].tics_remaining = buffs[i].ticsremaining;
buff->entries[index].num_hits = buffs[i].numhits; buff->entries[index].num_hits = buffs[i].numhits;
strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64);
buff->name_lengths += strlen(buff->entries[index].caster);
++index; ++index;
} }
} }

View File

@ -19,6 +19,7 @@
#define PROC_TOOLOW 126 //Your will is not sufficient to command this weapon. #define PROC_TOOLOW 126 //Your will is not sufficient to command this weapon.
#define PROC_PETTOOLOW 127 //Your pet's will is not sufficient to command its weapon. #define PROC_PETTOOLOW 127 //Your pet's will is not sufficient to command its weapon.
#define YOU_FLURRY 128 //You unleash a flurry of attacks. #define YOU_FLURRY 128 //You unleash a flurry of attacks.
#define FAILED_DISARM_TRAP 129 //You failed to disarm the trap.
#define DOORS_LOCKED 130 //It's locked and you're not holding the key. #define DOORS_LOCKED 130 //It's locked and you're not holding the key.
#define DOORS_CANT_PICK 131 //This lock cannot be picked. #define DOORS_CANT_PICK 131 //This lock cannot be picked.
#define DOORS_INSUFFICIENT_SKILL 132 //You are not sufficiently skilled to pick this lock. #define DOORS_INSUFFICIENT_SKILL 132 //You are not sufficiently skilled to pick this lock.
@ -98,6 +99,7 @@
#define DUP_LORE 290 //Duplicate lore items are not allowed. #define DUP_LORE 290 //Duplicate lore items are not allowed.
#define TGB_ON 293 //Target other group buff is *ON*. #define TGB_ON 293 //Target other group buff is *ON*.
#define TGB_OFF 294 //Target other group buff is *OFF*. #define TGB_OFF 294 //Target other group buff is *OFF*.
#define DISARMED_TRAP 305 //You have disarmed the trap.
#define LDON_SENSE_TRAP1 306 //You do not Sense any traps. #define LDON_SENSE_TRAP1 306 //You do not Sense any traps.
#define TRADESKILL_NOCOMBINE 334 //You cannot combine these items in this container type! #define TRADESKILL_NOCOMBINE 334 //You cannot combine these items in this container type!
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together. #define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together.
@ -114,6 +116,8 @@
#define MEND_WORSEN 351 //You have worsened your wounds! #define MEND_WORSEN 351 //You have worsened your wounds!
#define MEND_FAIL 352 //You have failed to mend your wounds. #define MEND_FAIL 352 //You have failed to mend your wounds.
#define LDON_SENSE_TRAP2 367 //You have not detected any traps. #define LDON_SENSE_TRAP2 367 //You have not detected any traps.
#define TRAP_TOO_FAR 368 //You are too far away from that trap to affect it.
#define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap.
#define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one. #define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one.
#define PICK_LORE 379 //You cannot pick up a lore item you already possess. #define PICK_LORE 379 //You cannot pick up a lore item you already possess.
#define CONSENT_DENIED 390 //You do not have consent to summon that corpse. #define CONSENT_DENIED 390 //You do not have consent to summon that corpse.
@ -421,6 +425,7 @@
#define SENSE_ANIMAL 12472 //You sense an animal in this direction. #define SENSE_ANIMAL 12472 //You sense an animal in this direction.
#define SENSE_SUMMONED 12473 //You sense a summoned being in this direction. #define SENSE_SUMMONED 12473 //You sense a summoned being in this direction.
#define SENSE_NOTHING 12474 //You don't sense anything. #define SENSE_NOTHING 12474 //You don't sense anything.
#define SENSE_TRAP 12475 //You sense a trap in this direction.
#define LDON_SENSE_TRAP3 12476 //You don't sense any traps. #define LDON_SENSE_TRAP3 12476 //You don't sense any traps.
#define INTERRUPT_SPELL_OTHER 12478 //%1's casting is interrupted! #define INTERRUPT_SPELL_OTHER 12478 //%1's casting is interrupted!
#define YOU_HIT_NONMELEE 12481 //You were hit by non-melee for %1 damage. #define YOU_HIT_NONMELEE 12481 //You were hit by non-melee for %1 damage.

View File

@ -52,10 +52,12 @@ CREATE TABLE traps (
Trap::Trap() : Trap::Trap() :
Entity(), Entity(),
respawn_timer(600000), respawn_timer(600000),
chkarea_timer(500), chkarea_timer(1000),
reset_timer(5000),
m_Position(glm::vec3()) m_Position(glm::vec3())
{ {
trap_id = 0; trap_id = 0;
db_id = 0;
maxzdiff = 0; maxzdiff = 0;
radius = 0; radius = 0;
effect = 0; effect = 0;
@ -64,12 +66,20 @@ Trap::Trap() :
skill = 0; skill = 0;
level = 0; level = 0;
respawn_timer.Disable(); respawn_timer.Disable();
reset_timer.Disable();
detected = false; detected = false;
disarmed = false; disarmed = false;
respawn_time = 0; respawn_time = 0;
respawn_var = 0; respawn_var = 0;
hiddenTrigger = nullptr; hiddenTrigger = nullptr;
ownHiddenTrigger = false; ownHiddenTrigger = false;
chance = 0;
triggered_number = 0;
times_triggered = 0;
group = 0;
despawn_when_triggered = false;
charid = 0;
undetectable = false;
} }
Trap::~Trap() Trap::~Trap()
@ -80,8 +90,7 @@ Trap::~Trap()
bool Trap::Process() bool Trap::Process()
{ {
if (chkarea_timer.Enabled() && chkarea_timer.Check() if (chkarea_timer.Enabled() && chkarea_timer.Check() && !reset_timer.Enabled())
/*&& zone->GetClientCount() > 0*/ )
{ {
Mob* trigger = entity_list.GetTrapTrigger(this); Mob* trigger = entity_list.GetTrapTrigger(this);
if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM())) if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM()))
@ -89,6 +98,13 @@ bool Trap::Process()
Trigger(trigger); Trigger(trigger);
} }
} }
else if (reset_timer.Enabled() && reset_timer.Check())
{
Log(Logs::General, Logs::Traps, "Reset timer disabled in Reset Check Process for trap %d.", trap_id);
reset_timer.Disable();
charid = 0;
}
if (respawn_timer.Enabled() && respawn_timer.Check()) if (respawn_timer.Enabled() && respawn_timer.Check())
{ {
detected = false; detected = false;
@ -96,11 +112,15 @@ bool Trap::Process()
chkarea_timer.Enable(); chkarea_timer.Enable();
respawn_timer.Disable(); respawn_timer.Disable();
} }
return true; return true;
} }
void Trap::Trigger(Mob* trigger) void Trap::Trigger(Mob* trigger)
{ {
Log(Logs::General, Logs::Traps, "Trap %d triggered by %s for the %d time!", trap_id, trigger->GetName(), times_triggered + 1);
int i = 0; int i = 0;
const NPCType* tmp = 0; const NPCType* tmp = 0;
switch (effect) switch (effect)
@ -128,7 +148,7 @@ void Trap::Trigger(Mob* trigger)
entity_list.MessageClose(trigger,false,effectvalue,13,"%s",message.c_str()); entity_list.MessageClose(trigger,false,effectvalue,13,"%s",message.c_str());
} }
entity_list.SendAlarm(this,trigger,effectvalue); entity_list.SendAlarm(this,trigger, effectvalue2);
break; break;
case trapTypeMysticSpawn: case trapTypeMysticSpawn:
if (message.empty()) if (message.empty())
@ -201,12 +221,41 @@ void Trap::Trigger(Mob* trigger)
safe_delete(outapp); safe_delete(outapp);
} }
} }
respawn_timer.Start((respawn_time + zone->random.Int(0, respawn_var)) * 1000);
chkarea_timer.Disable(); if (trigger && trigger->IsClient())
disarmed = true; {
trigger->CastToClient()->trapid = trap_id;
charid = trigger->CastToClient()->CharacterID();
}
bool update = false;
if (despawn_when_triggered)
{
Log(Logs::General, Logs::Traps, "Trap %d is despawning after being triggered.", trap_id);
update = true;
}
else
{
reset_timer.Start(5000);
}
if (triggered_number > 0)
++times_triggered;
if (triggered_number > 0 && triggered_number <= times_triggered)
{
Log(Logs::General, Logs::Traps, "Triggered number for trap %d reached. %d/%d", trap_id, times_triggered, triggered_number);
update = true;
}
if (update)
{
UpdateTrap();
}
} }
Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) { Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist, float &trap_curdist, bool detected)
{
float dist = 999999; float dist = 999999;
Trap* current_trap = nullptr; Trap* current_trap = nullptr;
@ -215,61 +264,161 @@ Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) {
for (auto it = trap_list.begin(); it != trap_list.end(); ++it) { for (auto it = trap_list.begin(); it != trap_list.end(); ++it) {
cur = it->second; cur = it->second;
if(cur->disarmed) if(cur->disarmed || (detected && !cur->detected) || cur->undetectable)
continue; continue;
auto diff = glm::vec3(searcher->GetPosition()) - cur->m_Position; auto diff = glm::vec3(searcher->GetPosition()) - cur->m_Position;
float curdist = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z; float curdist = diff.x*diff.x + diff.y*diff.y;
diff.z = std::abs(diff.z);
if (curdist < max_dist2 && curdist < dist) if (curdist < max_dist2 && curdist < dist && diff.z <= cur->maxzdiff)
{ {
Log(Logs::General, Logs::Traps, "Trap %d is curdist %0.1f", cur->db_id, curdist);
dist = curdist; dist = curdist;
current_trap = cur; current_trap = cur;
} }
} }
if (current_trap != nullptr)
{
Log(Logs::General, Logs::Traps, "Trap %d is the closest trap.", current_trap->db_id);
trap_curdist = dist;
}
else
trap_curdist = INVALID_INDEX;
return current_trap; return current_trap;
} }
Mob* EntityList::GetTrapTrigger(Trap* trap) { Mob* EntityList::GetTrapTrigger(Trap* trap)
Mob* savemob = 0; {
float maxdist = trap->radius * trap->radius; float maxdist = trap->radius * trap->radius;
for (auto it = client_list.begin(); it != client_list.end(); ++it)
for (auto it = client_list.begin(); it != client_list.end(); ++it) { {
Client* cur = it->second; Client* cur = it->second;
auto diff = glm::vec3(cur->GetPosition()) - trap->m_Position; auto diff = glm::vec3(cur->GetPosition()) - trap->m_Position;
diff.z = std::abs(diff.z); diff.z = std::abs(diff.z);
if ((diff.x*diff.x + diff.y*diff.y) <= maxdist if ((diff.x*diff.x + diff.y*diff.y) <= maxdist
&& diff.z < trap->maxzdiff) && diff.z <= trap->maxzdiff)
{ {
if (zone->random.Roll(trap->chance)) //This prevents the trap from triggering on players while zoning.
return(cur); if (strcmp(cur->GetName(), "No name") == 0)
else continue;
savemob = cur;
}
if (cur->trapid == 0 && !cur->GetGM() && (trap->chance == 0 || zone->random.Roll(trap->chance)))
{
Log(Logs::General, Logs::Traps, "%s is about to trigger trap %d of chance %d. diff: %0.2f maxdist: %0.2f zdiff: %0.2f maxzdiff: %0.2f", cur->GetName(), trap->trap_id, trap->chance, (diff.x*diff.x + diff.y*diff.y), maxdist, diff.z, trap->maxzdiff);
return cur;
}
}
else
{
if (cur->trapid == trap->trap_id)
{
Log(Logs::General, Logs::Traps, "%s is clearing trapid for trap %d", cur->GetName(), trap->trap_id);
cur->trapid = 0;
}
}
} }
return savemob; return nullptr;
} }
//todo: rewrite this to not need direct access to trap members. bool EntityList::IsTrapGroupSpawned(uint32 trap_id, uint8 group)
{
auto it = trap_list.begin();
while (it != trap_list.end())
{
Trap* cur = it->second;
if (cur->IsTrap() && cur->group == group && cur->trap_id != trap_id)
{
return true;
}
++it;
}
return false;
}
void EntityList::UpdateAllTraps(bool respawn, bool repopnow)
{
auto it = trap_list.begin();
while (it != trap_list.end())
{
Trap* cur = it->second;
if (cur->IsTrap())
{
cur->UpdateTrap(respawn, repopnow);
}
++it;
}
Log(Logs::General, Logs::Traps, "All traps updated.");
}
void EntityList::GetTrapInfo(Client* client)
{
uint8 count = 0;
auto it = trap_list.begin();
while (it != trap_list.end())
{
Trap* cur = it->second;
if (cur->IsTrap())
{
bool isset = (cur->chkarea_timer.Enabled() && !cur->reset_timer.Enabled());
client->Message(CC_Default, " Trap: (%d) found at %0.2f,%0.2f,%0.2f. Times Triggered: %d Is Active: %d Group: %d Message: %s", cur->trap_id, cur->m_Position.x, cur->m_Position.y, cur->m_Position.z, cur->times_triggered, isset, cur->group, cur->message.c_str());
++count;
}
++it;
}
client->Message(CC_Default, "%d traps found.", count);
}
void EntityList::ClearTrapPointers()
{
auto it = trap_list.begin();
while (it != trap_list.end())
{
Trap* cur = it->second;
if (cur->IsTrap())
{
cur->DestroyHiddenTrigger();
}
++it;
}
}
bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) {
std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, "
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level " "maxzdiff, radius, chance, message, respawn_time, respawn_var, level, "
"FROM traps WHERE zone='%s' AND version=%u", zonename, version); "`group`, triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND version=%u", zonename, version);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
if (!results.Success()) { if (!results.Success()) {
return false; return false;
} }
for (auto row = results.begin(); row != results.end(); ++row) { for (auto row = results.begin(); row != results.end(); ++row) {
uint32 tid = atoi(row[0]);
uint8 grp = atoi(row[15]);
if (grp > 0)
{
// If a member of our group is already spawned skip loading this trap.
if (entity_list.IsTrapGroupSpawned(tid, grp))
{
continue;
}
}
auto trap = new Trap(); auto trap = new Trap();
trap->trap_id = atoi(row[0]); trap->trap_id = tid;
trap->db_id = tid;
trap->m_Position = glm::vec3(atof(row[1]), atof(row[2]), atof(row[3])); trap->m_Position = glm::vec3(atof(row[1]), atof(row[2]), atof(row[3]));
trap->effect = atoi(row[4]); trap->effect = atoi(row[4]);
trap->effectvalue = atoi(row[5]); trap->effectvalue = atoi(row[5]);
@ -282,8 +431,13 @@ bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) {
trap->respawn_time = atoi(row[12]); trap->respawn_time = atoi(row[12]);
trap->respawn_var = atoi(row[13]); trap->respawn_var = atoi(row[13]);
trap->level = atoi(row[14]); trap->level = atoi(row[14]);
trap->group = grp;
trap->triggered_number = atoi(row[16]);
trap->despawn_when_triggered = atobool(row[17]);
trap->undetectable = atobool(row[18]);
entity_list.AddTrap(trap); entity_list.AddTrap(trap);
trap->CreateHiddenTrigger(); trap->CreateHiddenTrigger();
Log(Logs::General, Logs::Traps, "Trap %d successfully loaded.", trap->trap_id);
} }
return true; return true;
@ -318,3 +472,87 @@ void Trap::CreateHiddenTrigger()
hiddenTrigger = npca; hiddenTrigger = npca;
ownHiddenTrigger = true; ownHiddenTrigger = true;
} }
bool ZoneDatabase::SetTrapData(Trap* trap, bool repopnow) {
uint32 dbid = trap->db_id;
std::string query;
if (trap->group > 0)
{
query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, "
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level, "
"triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND `group`=%d AND id != %d ORDER BY RAND() LIMIT 1", zone->GetShortName(), trap->group, dbid);
}
else
{
// We could just use the existing data here, but querying the DB is not expensive, and allows content developers to change traps without rebooting.
query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, "
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level, "
"triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND id = %d", zone->GetShortName(), dbid);
}
auto results = QueryDatabase(query);
if (!results.Success()) {
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
trap->db_id = atoi(row[0]);
trap->m_Position = glm::vec3(atof(row[1]), atof(row[2]), atof(row[3]));
trap->effect = atoi(row[4]);
trap->effectvalue = atoi(row[5]);
trap->effectvalue2 = atoi(row[6]);
trap->skill = atoi(row[7]);
trap->maxzdiff = atof(row[8]);
trap->radius = atof(row[9]);
trap->chance = atoi(row[10]);
trap->message = row[11];
trap->respawn_time = atoi(row[12]);
trap->respawn_var = atoi(row[13]);
trap->level = atoi(row[14]);
trap->triggered_number = atoi(row[15]);
trap->despawn_when_triggered = atobool(row[16]);
trap->undetectable = atobool(row[17]);
trap->CreateHiddenTrigger();
if (repopnow)
{
trap->chkarea_timer.Enable();
}
else
{
trap->respawn_timer.Start((trap->respawn_time + zone->random.Int(0, trap->respawn_var)) * 1000);
}
if (trap->trap_id != trap->db_id)
Log(Logs::General, Logs::Traps, "Trap (%d) DBID has changed from %d to %d", trap->trap_id, dbid, trap->db_id);
return true;
}
return false;
}
void Trap::UpdateTrap(bool respawn, bool repopnow)
{
respawn_timer.Disable();
chkarea_timer.Disable();
reset_timer.Disable();
if (hiddenTrigger)
{
hiddenTrigger->Depop();
hiddenTrigger = nullptr;
}
times_triggered = 0;
Client* trigger = entity_list.GetClientByCharID(charid);
if (trigger)
{
trigger->trapid = 0;
}
charid = 0;
if (respawn)
{
database.SetTrapData(this, repopnow);
}
}

View File

@ -49,11 +49,14 @@ public:
NPC * GetHiddenTrigger() { return hiddenTrigger; } NPC * GetHiddenTrigger() { return hiddenTrigger; }
void SetHiddenTrigger(NPC* n) { hiddenTrigger = n; } void SetHiddenTrigger(NPC* n) { hiddenTrigger = n; }
void CreateHiddenTrigger(); void CreateHiddenTrigger();
void DestroyHiddenTrigger() { hiddenTrigger = nullptr; }
void UpdateTrap(bool respawn = true, bool repopnow = false);
//Trap data, leave this unprotected //Trap data, leave this unprotected
Timer respawn_timer; //Respawn Time when Trap's been disarmed Timer respawn_timer; //Respawn Time when Trap's been disarmed
Timer chkarea_timer; Timer chkarea_timer;
uint32 trap_id; //Database ID of trap Timer reset_timer; //How long a trap takes to reset before triggering again.
uint32 trap_id; //Original ID of the trap from DB. This value never changes.
uint32 db_id; //The DB ID of the trap that currently is spawned.
glm::vec3 m_Position; glm::vec3 m_Position;
float maxzdiff; //maximum z diff to be triggerable float maxzdiff; //maximum z diff to be triggerable
float radius; //radius around trap to be triggerable float radius; //radius around trap to be triggerable
@ -67,6 +70,12 @@ public:
bool disarmed; bool disarmed;
uint32 respawn_time; uint32 respawn_time;
uint32 respawn_var; uint32 respawn_var;
uint8 triggered_number;
uint8 times_triggered;
uint8 group;
bool despawn_when_triggered;
uint32 charid; //ID of character that triggered trap. This is cleared when the trap despawns are resets.
bool undetectable;
std::string message; std::string message;
protected: protected:

View File

@ -1428,7 +1428,8 @@ void Zone::StartShutdownTimer(uint32 set_time) {
bool Zone::Depop(bool StartSpawnTimer) { bool Zone::Depop(bool StartSpawnTimer) {
std::map<uint32,NPCType *>::iterator itr; std::map<uint32,NPCType *>::iterator itr;
entity_list.Depop(StartSpawnTimer); entity_list.Depop(StartSpawnTimer);
entity_list.ClearTrapPointers();
entity_list.UpdateAllTraps(false);
/* Refresh npctable (cache), getting current info from database. */ /* Refresh npctable (cache), getting current info from database. */
while(!npctable.empty()) { while(!npctable.empty()) {
itr = npctable.begin(); itr = npctable.begin();
@ -1496,6 +1497,8 @@ void Zone::Repop(uint32 delay) {
iterator.RemoveCurrent(); iterator.RemoveCurrent();
} }
entity_list.ClearTrapPointers();
quest_manager.ClearAllTimers(); quest_manager.ClearAllTimers();
if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay)) if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay))
@ -1503,6 +1506,8 @@ void Zone::Repop(uint32 delay) {
initgrids_timer.Start(); initgrids_timer.Start();
entity_list.UpdateAllTraps(true, true);
//MODDING HOOK FOR REPOP //MODDING HOOK FOR REPOP
mod_repop(); mod_repop();
} }

View File

@ -1433,6 +1433,11 @@ bool ZoneDatabase::SaveCharacterInventorySnapshot(uint32 character_id){
} }
bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){
/* If this is ever zero - the client hasn't fully loaded and potentially crashed during zone */
if (account_id <= 0)
return false;
clock_t t = std::clock(); /* Function timer start */ clock_t t = std::clock(); /* Function timer start */
std::string query = StringFormat( std::string query = StringFormat(
"REPLACE INTO `character_data` (" "REPLACE INTO `character_data` ("
@ -2953,9 +2958,11 @@ uint32 ZoneDatabase::GetKarma(uint32 acct_id)
if (!results.Success()) if (!results.Success())
return 0; return 0;
auto row = results.begin(); for (auto row = results.begin(); row != results.end(); ++row) {
return atoi(row[0]);
}
return atoi(row[0]); return 0;
} }
void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount) void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount)

View File

@ -16,6 +16,7 @@ class NPC;
class Petition; class Petition;
class Spawn2; class Spawn2;
class SpawnGroupList; class SpawnGroupList;
class Trap;
struct CharacterEventLog_Struct; struct CharacterEventLog_Struct;
struct Door; struct Door;
struct ExtendedProfile_Struct; struct ExtendedProfile_Struct;
@ -478,7 +479,7 @@ public:
/* Traps */ /* Traps */
bool LoadTraps(const char* zonename, int16 version); bool LoadTraps(const char* zonename, int16 version);
char* GetTrapMessage(uint32 trap_id); bool SetTrapData(Trap* trap, bool repopnow = false);
/* Time */ /* Time */
uint32 GetZoneTZ(uint32 zoneid, uint32 version); uint32 GetZoneTZ(uint32 zoneid, uint32 version);