Merge remote-tracking branch 'upstream/master'

This commit is contained in:
neckkola
2022-02-11 17:36:52 -04:00
33 changed files with 1545 additions and 783 deletions
+51 -6
View File
@@ -226,12 +226,12 @@ std::string EQ::constants::GetLDoNThemeName(uint32 theme_id)
const std::map<uint8, std::string>& EQ::constants::GetFlyModeMap() const std::map<uint8, std::string>& EQ::constants::GetFlyModeMap()
{ {
static const std::map<uint8, std::string> flymode_map = { static const std::map<uint8, std::string> flymode_map = {
{ EQ::constants::GravityBehavior::Ground, "Ground" }, { GravityBehavior::Ground, "Ground" },
{ EQ::constants::GravityBehavior::Flying, "Flying" }, { GravityBehavior::Flying, "Flying" },
{ EQ::constants::GravityBehavior::Levitating, "Levitating" }, { GravityBehavior::Levitating, "Levitating" },
{ EQ::constants::GravityBehavior::Water, "Water" }, { GravityBehavior::Water, "Water" },
{ EQ::constants::GravityBehavior::Floating, "Floating" }, { GravityBehavior::Floating, "Floating" },
{ EQ::constants::GravityBehavior::LevitateWhileRunning, "Levitating While Running" }, { GravityBehavior::LevitateWhileRunning, "Levitating While Running" },
}; };
return flymode_map; return flymode_map;
} }
@@ -337,3 +337,48 @@ std::string EQ::constants::GetAccountStatusName(uint8 account_status)
return status_name; return status_name;
} }
const std::map<uint8, std::string>& EQ::constants::GetConsiderLevelMap()
{
static const std::map<uint8, std::string> consider_level_map = {
{ ConsiderLevel::Ally, "Ally" },
{ ConsiderLevel::Warmly, "Warmly" },
{ ConsiderLevel::Kindly, "Kindly" },
{ ConsiderLevel::Amiably, "Amiably" },
{ ConsiderLevel::Indifferently, "Indifferently" },
{ ConsiderLevel::Apprehensively, "Apprehensively" },
{ ConsiderLevel::Dubiously, "Dubiously" },
{ ConsiderLevel::Threateningly, "Threateningly" },
{ ConsiderLevel::Scowls, "Scowls" }
};
return consider_level_map;
}
std::string EQ::constants::GetConsiderLevelName(uint8 faction_consider_level)
{
auto consider_levels = EQ::constants::GetConsiderLevelMap();
if (!consider_levels[faction_consider_level].empty()) {
return consider_levels[faction_consider_level];
}
return std::string();
}
const std::map<uint8, std::string>& EQ::constants::GetEnvironmentalDamageMap()
{
static const std::map<uint8, std::string> damage_type_map = {
{ EnvironmentalDamage::Lava, "Lava" },
{ EnvironmentalDamage::Drowning, "Drowning" },
{ EnvironmentalDamage::Falling, "Falling" },
{ EnvironmentalDamage::Trap, "Trap" }
};
return damage_type_map;
}
std::string EQ::constants::GetEnvironmentalDamageName(uint8 damage_type)
{
if (EQ::ValueWithin(damage_type, EnvironmentalDamage::Lava, EnvironmentalDamage::Trap)) {
auto damage_types = EQ::constants::GetEnvironmentalDamageMap();
return damage_types[damage_type];
}
return std::string();
}
+25
View File
@@ -230,6 +230,13 @@ namespace EQ
LevitateWhileRunning LevitateWhileRunning
}; };
enum EnvironmentalDamage : uint8 {
Lava = 250,
Drowning,
Falling,
Trap
};
const char *GetStanceName(StanceType stance_type); const char *GetStanceName(StanceType stance_type);
int ConvertStanceTypeToIndex(StanceType stance_type); int ConvertStanceTypeToIndex(StanceType stance_type);
@@ -248,6 +255,12 @@ namespace EQ
extern const std::map<uint8, std::string>& GetAccountStatusMap(); extern const std::map<uint8, std::string>& GetAccountStatusMap();
std::string GetAccountStatusName(uint8 account_status); std::string GetAccountStatusName(uint8 account_status);
extern const std::map<uint8, std::string>& GetConsiderLevelMap();
std::string GetConsiderLevelName(uint8 consider_level);
extern const std::map<uint8, std::string>& GetEnvironmentalDamageMap();
std::string GetEnvironmentalDamageName(uint8 damage_type);
const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_FIRST = stancePassive;
const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_LAST = stanceBurnAE;
const int STANCE_TYPE_COUNT = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE;
@@ -391,4 +404,16 @@ enum AugmentActions : int {
Destroy Destroy
}; };
enum ConsiderLevel : uint8 {
Ally = 1,
Warmly,
Kindly,
Amiably,
Indifferently,
Apprehensively,
Dubiously,
Threateningly,
Scowls
};
#endif /*COMMON_EMU_CONSTANTS_H*/ #endif /*COMMON_EMU_CONSTANTS_H*/
+1 -1
View File
@@ -2762,7 +2762,7 @@ struct EnvDamage2_Struct {
/*0004*/ uint16 unknown4; /*0004*/ uint16 unknown4;
/*0006*/ uint32 damage; /*0006*/ uint32 damage;
/*0010*/ uint8 unknown10[12]; /*0010*/ uint8 unknown10[12];
/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling /*0022*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap
/*0023*/ uint8 unknown2[4]; /*0023*/ uint8 unknown2[4];
/*0027*/ uint16 constant; //Always FFFF /*0027*/ uint16 constant; //Always FFFF
/*0029*/ uint16 unknown29; /*0029*/ uint16 unknown29;
+1 -1
View File
@@ -3061,7 +3061,7 @@ struct EnvDamage2_Struct {
/*0006*/ uint32 damage; /*0006*/ uint32 damage;
/*0010*/ float unknown10; // New to Underfoot - Seen 1 /*0010*/ float unknown10; // New to Underfoot - Seen 1
/*0014*/ uint8 unknown14[12]; /*0014*/ uint8 unknown14[12];
/*0026*/ uint8 dmgtype; // FA = Lava; FC = Falling /*0026*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap
/*0027*/ uint8 unknown27[4]; /*0027*/ uint8 unknown27[4];
/*0031*/ uint16 unknown31; // New to Underfoot - Seen 66 /*0031*/ uint16 unknown31; // New to Underfoot - Seen 66
/*0033*/ uint16 constant; // Always FFFF /*0033*/ uint16 constant; // Always FFFF
+1 -1
View File
@@ -3032,7 +3032,7 @@ struct EnvDamage2_Struct {
/*0006*/ uint32 damage; /*0006*/ uint32 damage;
/*0010*/ float unknown10; // New to Underfoot - Seen 1 /*0010*/ float unknown10; // New to Underfoot - Seen 1
/*0014*/ uint8 unknown14[12]; /*0014*/ uint8 unknown14[12];
/*0026*/ uint8 dmgtype; // FA = Lava; FC = Falling /*0026*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap
/*0027*/ uint8 unknown27[4]; /*0027*/ uint8 unknown27[4];
/*0031*/ uint16 unknown31; // New to Underfoot - Seen 66 /*0031*/ uint16 unknown31; // New to Underfoot - Seen 66
/*0033*/ uint16 constant; // Always FFFF /*0033*/ uint16 constant; // Always FFFF
+1 -1
View File
@@ -2539,7 +2539,7 @@ struct EnvDamage2_Struct {
/*0004*/ uint16 unknown4; /*0004*/ uint16 unknown4;
/*0006*/ uint32 damage; /*0006*/ uint32 damage;
/*0010*/ uint8 unknown10[12]; /*0010*/ uint8 unknown10[12];
/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling /*0022*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap
/*0023*/ uint8 unknown2[4]; /*0023*/ uint8 unknown2[4];
/*0027*/ uint16 constant; //Always FFFF /*0027*/ uint16 constant; //Always FFFF
/*0029*/ uint16 unknown29; /*0029*/ uint16 unknown29;
+1 -1
View File
@@ -2509,7 +2509,7 @@ struct EnvDamage2_Struct {
/*0004*/ uint16 unknown4; /*0004*/ uint16 unknown4;
/*0006*/ uint32 damage; /*0006*/ uint32 damage;
/*0010*/ uint8 unknown10[12]; /*0010*/ uint8 unknown10[12];
/*0022*/ uint8 dmgtype; //FA = Lava; FC = Falling /*0022*/ uint8 dmgtype; // FA = Lava, FB = Drowning, FC = Falling, FD = Trap
/*0023*/ uint8 unknown2[4]; /*0023*/ uint8 unknown2[4];
/*0027*/ uint16 constant; //Always FFFF /*0027*/ uint16 constant; //Always FFFF
/*0029*/ uint16 unknown29; /*0029*/ uint16 unknown29;
+1
View File
@@ -192,6 +192,7 @@ RULE_INT(Character, MagicianTrackingDistanceMultiplier, 0, "If you want magician
RULE_INT(Character, EnchanterTrackingDistanceMultiplier, 0, "If you want enchanters to be able to track, increase this above 0. 0 disables tracking packets.") RULE_INT(Character, EnchanterTrackingDistanceMultiplier, 0, "If you want enchanters to be able to track, increase this above 0. 0 disables tracking packets.")
RULE_INT(Character, BeastlordTrackingDistanceMultiplier, 0, "If you want beastlords to be able to track, increase this above 0. 0 disables tracking packets.") RULE_INT(Character, BeastlordTrackingDistanceMultiplier, 0, "If you want beastlords to be able to track, increase this above 0. 0 disables tracking packets.")
RULE_INT(Character, BerserkerTrackingDistanceMultiplier, 0, "If you want berserkers to be able to track, increase this above 0. 0 disables tracking packets.") RULE_INT(Character, BerserkerTrackingDistanceMultiplier, 0, "If you want berserkers to be able to track, increase this above 0. 0 disables tracking packets.")
RULE_BOOL(Character, OnInviteReceiveAlreadyinGroupMessage, true, "If you want clients to receive a message when trying to invite a player into a group that is currently in another group.")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Mercs) RULE_CATEGORY(Mercs)
+18 -35
View File
@@ -1239,10 +1239,6 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
if (!IsValidSpell(rank->spell)) { if (!IsValidSpell(rank->spell)) {
return; return;
} }
//do not allow AA to cast if your actively casting another AA.
if (rank->spell == casting_spell_id && rank->id == casting_spell_aa_id) {
return;
}
if (!CanUseAlternateAdvancementRank(rank)) { if (!CanUseAlternateAdvancementRank(rank)) {
return; return;
@@ -1257,13 +1253,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
uint32 charges = 0; uint32 charges = 0;
// We don't have the AA // We don't have the AA
if (!GetAA(rank_id, &charges)) { if (!GetAA(rank_id, &charges))
return; return;
}
//if expendable make sure we have charges //if expendable make sure we have charges
if (ability->charges > 0 && charges < 1) { if (ability->charges > 0 && charges < 1)
return; return;
}
//check cooldown //check cooldown
if (!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) { if (!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) {
@@ -1280,28 +1274,31 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
Message(Chat::Red, "You can use this ability again in %u minute(s) %u seconds", Message(Chat::Red, "You can use this ability again in %u minute(s) %u seconds",
aaremain_min, aaremain_sec); aaremain_min, aaremain_sec);
} }
return; return;
} }
if (!IsCastWhileInvis(rank->spell)) { int timer_duration = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
CommonBreakInvisible(); if (timer_duration < 0) {
timer_duration = 0;
} }
if (!IsCastWhileInvis(rank->spell))
CommonBreakInvisible();
if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) { if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) {
MessageString(Chat::SpellFailure, SNEAK_RESTRICT); MessageString(Chat::SpellFailure, SNEAK_RESTRICT);
return; return;
} }
// //
// Modern clients don't require pet targeted for AA casts that are ST_Pet // Modern clients don't require pet targeted for AA casts that are ST_Pet
if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet) { if (spells[rank->spell].target_type == ST_Pet || spells[rank->spell].target_type == ST_SummonedPet)
target_id = GetPetID(); target_id = GetPetID();
}
// extra handling for cast_not_standing spells // extra handling for cast_not_standing spells
if (!IgnoreCastingRestriction(rank->spell)) { if (!IgnoreCastingRestriction(rank->spell)) {
if (GetAppearance() == eaSitting) { // we need to stand! if (GetAppearance() == eaSitting) // we need to stand!
SetAppearance(eaStanding, false); SetAppearance(eaStanding, false);
}
if (GetAppearance() != eaStanding) { if (GetAppearance() != eaStanding) {
MessageString(Chat::SpellFailure, STAND_TO_CAST); MessageString(Chat::SpellFailure, STAND_TO_CAST);
@@ -1318,31 +1315,18 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
if (!DoCastingChecksOnCaster(rank->spell)) { if (!DoCastingChecksOnCaster(rank->spell)) {
return; return;
} }
SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false, -1, false, rank->id);
if (!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQ::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].resist_difficulty, false, -1,
rank->spell_type + pTimerAAStart, timer_duration, false, rank->id)) {
return;
}
} }
//Known issue: If you attempt to give a Bard an AA with a cast time, the cast timer will not display on the client (no live bard AA have cast time).
else { else {
CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, 0xFFFFFFFF, 0, nullptr, rank->id); if (!CastSpell(rank->spell, target_id, EQ::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, timer_duration, nullptr, rank->id)) {
}
}
}
void Client::SetAARecastTimer(AA::Rank *rank_in, int32 spell_id) {
if (!rank_in) {
return; return;
} }
//calculate AA cooldown
int timer_duration = rank_in->recast_time - GetAlternateAdvancementCooldownReduction(rank_in);
if (timer_duration <= 0) {
return;
} }
}
CastToClient()->GetPTimers().Start(rank_in->spell_type + pTimerAAStart, timer_duration);
CastToClient()->SendAlternateAdvancementTimer(rank_in->spell_type, 0, 0);
LogSpells("Spell [{}]: Setting AA reuse timer [{}] to [{}]", spell_id, rank_in->spell_type + pTimerAAStart, timer_duration);
} }
int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) { int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
@@ -1376,7 +1360,6 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
} }
void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) { void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
for (auto &iter : aa_ranks) { for (auto &iter : aa_ranks) {
AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first); AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first);
if (ability && aa_id == ability->id) { if (ability && aa_id == ability->id) {
-2
View File
@@ -1495,8 +1495,6 @@ public:
void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0); void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0);
void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot); void SetItemRecastTimer(int32 spell_id, uint32 inventory_slot);
bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot); bool HasItemRecastTimer(int32 spell_id, uint32 inventory_slot);
void SetDisciplineRecastTimer(int32 spell_id);
void SetAARecastTimer(AA::Rank *rank_in, int32 spell_id);
inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQ::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested
inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); } inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); }
+35 -21
View File
@@ -5784,8 +5784,7 @@ void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app)
void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
{ {
if (!ClientFinishedLoading()) if (!ClientFinishedLoading()) {
{
SetHP(GetHP() - 1); SetHP(GetHP() - 1);
return; return;
} }
@@ -5795,38 +5794,46 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
DumpPacket(app); DumpPacket(app);
return; return;
} }
EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer;
auto damage = ed->damage;
int damage = ed->damage; if (ed->dmgtype == EQ::constants::EnvironmentalDamage::Falling) {
uint32 mod = spellbonuses.ReduceFallDamage + itembonuses.ReduceFallDamage + aabonuses.ReduceFallDamage;
if (ed->dmgtype == 252) {
int mod = spellbonuses.ReduceFallDamage + itembonuses.ReduceFallDamage + aabonuses.ReduceFallDamage;
damage -= damage * mod / 100; damage -= damage * mod / 100;
} }
if (damage < 0) if (damage < 0) {
damage = 31337; damage = 31337;
}
if (admin >= minStatusToAvoidFalling && GetGM()) { if (admin >= minStatusToAvoidFalling && GetGM()) {
Message(Chat::Red, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); Message(
Chat::Red,
fmt::format(
"Your GM status protects you from {} points of {} (Type {}) damage.",
ed->damage,
EQ::constants::GetEnvironmentalDamageName(ed->dmgtype),
ed->dmgtype
).c_str()
);
SetHP(GetHP() - 1);//needed or else the client wont acknowledge SetHP(GetHP() - 1);//needed or else the client wont acknowledge
return; return;
} } else if (GetInvul()) {
else if (GetInvul()) { Message(
Message(Chat::Red, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); Chat::Red,
fmt::format(
"Your invulnerability protects you from {} points of {} (Type {}) damage.",
ed->damage,
EQ::constants::GetEnvironmentalDamageName(ed->dmgtype),
ed->dmgtype
).c_str()
);
SetHP(GetHP() - 1);//needed or else the client wont acknowledge SetHP(GetHP() - 1);//needed or else the client wont acknowledge
return; return;
} } else if (zone->GetZoneID() == Zones::TUTORIAL || zone->GetZoneID() == Zones::LOAD) { // Hard coded tutorial and load zones for no fall damage
else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) {
// Hard coded tutorial and load zones for no fall damage
return; return;
} } else {
else {
SetHP(GetHP() - (damage * RuleR(Character, EnvironmentDamageMulipliter))); SetHP(GetHP() - (damage * RuleR(Character, EnvironmentDamageMulipliter)));
/* EVENT_ENVIRONMENTAL_DAMAGE */
int final_damage = (damage * RuleR(Character, EnvironmentDamageMulipliter)); int final_damage = (damage * RuleR(Character, EnvironmentDamageMulipliter));
std::string export_string = fmt::format( std::string export_string = fmt::format(
"{} {} {}", "{} {} {}",
@@ -6953,6 +6960,13 @@ void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app)
Invitee->CastToClient()->QueuePacket(app); Invitee->CastToClient()->QueuePacket(app);
} }
} }
else {
if (RuleB(Character, OnInviteReceiveAlreadyinGroupMessage)) {
if (!Invitee->CastToClient()->MercOnlyOrNoGroup()) {
Message(Chat::LightGray, "%s is already in another group.", Invitee->GetCleanName());
}
}
}
} }
#ifdef BOTS #ifdef BOTS
else if (Invitee->IsBot()) { else if (Invitee->IsBot()) {
+5 -4
View File
@@ -183,7 +183,7 @@ int command_init(void)
command_add("findzone", "[search criteria] - Search database zones", AccountStatus::GMAdmin, command_findzone) || command_add("findzone", "[search criteria] - Search database zones", AccountStatus::GMAdmin, command_findzone) ||
command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) || command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) ||
command_add("flag", "[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided", AccountStatus::Player, command_flag) || command_add("flag", "[status] [acctname] - Refresh your admin status, or set an account's admin status if arguments provided", AccountStatus::Player, command_flag) ||
command_add("flagedit", "- Edit zone flags on your target", AccountStatus::GMAdmin, command_flagedit) || command_add("flagedit", "- Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) ||
command_add("flags", "- displays the flags of you or your target", AccountStatus::Player, command_flags) || command_add("flags", "- displays the flags of you or your target", AccountStatus::Player, command_flags) ||
command_add("flymode", "[0/1/2/3/4/5] - Set your or your player target's flymode to ground/flying/levitate/water/floating/levitate_running", AccountStatus::Guide, command_flymode) || command_add("flymode", "[0/1/2/3/4/5] - Set your or your player target's flymode to ground/flying/levitate/water/floating/levitate_running", AccountStatus::Guide, command_flymode) ||
command_add("fov", "- Check wether you're behind or in your target's field of view", AccountStatus::QuestTroupe, command_fov) || command_add("fov", "- Check wether you're behind or in your target's field of view", AccountStatus::QuestTroupe, command_fov) ||
@@ -209,7 +209,7 @@ int command_init(void)
command_add("hair", "- Change the hair style of your target", AccountStatus::QuestTroupe, command_hair) || command_add("hair", "- Change the hair style of your target", AccountStatus::QuestTroupe, command_hair) ||
command_add("haircolor", "- Change the hair color of your target", AccountStatus::QuestTroupe, command_haircolor) || command_add("haircolor", "- Change the hair color of your target", AccountStatus::QuestTroupe, command_haircolor) ||
command_add("haste", "[percentage] - Set your haste percentage", AccountStatus::GMAdmin, command_haste) || command_add("haste", "[percentage] - Set your haste percentage", AccountStatus::GMAdmin, command_haste) ||
command_add("hatelist", " - Display hate list for target.", AccountStatus::QuestTroupe, command_hatelist) || command_add("hatelist", "- Display hate list for NPC.", AccountStatus::QuestTroupe, command_hatelist) ||
command_add("heal", "- Completely heal your target", AccountStatus::Steward, command_heal) || command_add("heal", "- Completely heal your target", AccountStatus::Steward, command_heal) ||
command_add("helm", "- Change the helm of your target", AccountStatus::QuestTroupe, command_helm) || command_add("helm", "- Change the helm of your target", AccountStatus::QuestTroupe, command_helm) ||
command_add("help", "[search term] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || command_add("help", "[search term] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) ||
@@ -252,14 +252,14 @@ int command_init(void)
command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) || command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) ||
command_add("mysql", "[Help|Query] [SQL Query] - Mysql CLI, see 'Help' for options.", AccountStatus::GMImpossible, command_mysql) || command_add("mysql", "[Help|Query] [SQL Query] - Mysql CLI, see 'Help' for options.", AccountStatus::GMImpossible, command_mysql) ||
command_add("mystats", "- Show details about you or your pet", AccountStatus::Guide, command_mystats) || command_add("mystats", "- Show details about you or your pet", AccountStatus::Guide, command_mystats) ||
command_add("name", "[newname] - Rename your player target", AccountStatus::GMLeadAdmin, command_name) || command_add("name", "[New Name] - Rename your player target", AccountStatus::GMLeadAdmin, command_name) ||
command_add("netstats", "- Gets the network stats for a stream.", AccountStatus::GMMgmt, command_netstats) || command_add("netstats", "- Gets the network stats for a stream.", AccountStatus::GMMgmt, command_netstats) ||
command_add("network", "- Admin commands for the udp network interface.", AccountStatus::GMImpossible, command_network) || command_add("network", "- Admin commands for the udp network interface.", AccountStatus::GMImpossible, command_network) ||
command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", AccountStatus::QuestTroupe, command_npccast) || command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", AccountStatus::QuestTroupe, command_npccast) ||
command_add("npcedit", "[column] [value] - Mega NPC editing command", AccountStatus::GMAdmin, command_npcedit) || command_add("npcedit", "[column] [value] - Mega NPC editing command", AccountStatus::GMAdmin, command_npcedit) ||
command_add("npceditmass", "[name-search] [column] [value] - Mass (Zone wide) NPC data editing command", AccountStatus::GMAdmin, command_npceditmass) || command_add("npceditmass", "[name-search] [column] [value] - Mass (Zone wide) NPC data editing command", AccountStatus::GMAdmin, command_npceditmass) ||
command_add("npcemote", "[message] - Make your NPC target emote a message.", AccountStatus::GMLeadAdmin, command_npcemote) || command_add("npcemote", "[message] - Make your NPC target emote a message.", AccountStatus::GMLeadAdmin, command_npcemote) ||
command_add("npcloot", "[show/money/add/remove] [itemid/all/money: pp gp sp cp] - Manipulate the loot an NPC is carrying", AccountStatus::QuestTroupe, command_npcloot) || command_add("npcloot", "- Manipulate the loot an NPC is carrying. Use #npcloot help for more information.", AccountStatus::QuestTroupe, command_npcloot) ||
command_add("npcsay", "[message] - Make your NPC target say a message.", AccountStatus::GMLeadAdmin, command_npcsay) || command_add("npcsay", "[message] - Make your NPC target say a message.", AccountStatus::GMLeadAdmin, command_npcsay) ||
command_add("npcshout", "[message] - Make your NPC target shout a message.", AccountStatus::GMLeadAdmin, command_npcshout) || command_add("npcshout", "[message] - Make your NPC target shout a message.", AccountStatus::GMLeadAdmin, command_npcshout) ||
command_add("npcspawn", "[create/add/update/remove/delete] - Manipulate spawn DB", AccountStatus::GMAreas, command_npcspawn) || command_add("npcspawn", "[create/add/update/remove/delete] - Manipulate spawn DB", AccountStatus::GMAreas, command_npcspawn) ||
@@ -378,6 +378,7 @@ int command_init(void)
command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) ||
command_add("tune", "Calculate statistical values related to combat.", AccountStatus::GMAdmin, command_tune) || command_add("tune", "Calculate statistical values related to combat.", AccountStatus::GMAdmin, command_tune) ||
command_add("ucs", "- Attempts to reconnect to the UCS server", AccountStatus::Player, command_ucs) || command_add("ucs", "- Attempts to reconnect to the UCS server", AccountStatus::Player, command_ucs) ||
command_add("undye", "- Remove dye from all of your or your target's armor slots", AccountStatus::GMAdmin, command_undye) ||
command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) ||
command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) ||
command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) || command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) ||
+37 -2
View File
@@ -800,15 +800,50 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
return false; return false;
} }
bool instant_recast = true;
if (spell.recast_time > 0) {
uint32 reduced_recast = spell.recast_time / 1000;
auto focus = GetFocusEffect(focusReduceRecastTime, spell_id);
// do stupid stuff because custom servers.
// we really should be able to just do the -= focus but since custom servers could have shorter reuse timers
// we have to make sure we don't underflow the uint32 ...
// and yes, the focus effect can be used to increase the durations (spell 38944)
if (focus > reduced_recast) {
reduced_recast = 0;
if (GetPTimers().Enabled((uint32)DiscTimer))
GetPTimers().Clear(&database, (uint32)DiscTimer);
}
else {
reduced_recast -= focus;
}
if (reduced_recast > 0) {
instant_recast = false;
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) { if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
if (DoCastingChecksOnCaster(spell_id)) { if (DoCastingChecksOnCaster(spell_id)) {
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline); SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, (uint32)DiscTimer, reduced_recast, false);
}
}
else {
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
}
SendDisciplineTimer(spells[spell_id].timer_id, reduced_recast);
}
}
if (instant_recast) {
if (GetClass() == BARD && IsCasting() && spells[spell_id].cast_time == 0) {
if (DoCastingChecksOnCaster(spell_id)) {
SpellFinished(spell_id, entity_list.GetMob(target), EQ::spells::CastingSlot::Discipline, 0, -1, spells[spell_id].resist_difficulty, false, -1, 0xFFFFFFFF, 0, false);
} }
} }
else { else {
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline); CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
} }
}
return(true); return(true);
} }
+35
View File
@@ -8092,6 +8092,39 @@ XS(XS__getbodytypename) {
} }
} }
XS(XS__getconsiderlevelname);
XS(XS__getconsiderlevelname) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: quest::getconsiderlevelname(uint8 consider_level)");
{
dXSTARG;
uint8 consider_level = (uint8) SvUV(ST(0));
std::string consider_level_name = quest_manager.getconsiderlevelname(consider_level);
sv_setpv(TARG, consider_level_name.c_str());
XSprePUSH;
PUSHTARG;
XSRETURN(1);
}
}
XS(XS__getenvironmentaldamagename);
XS(XS__getenvironmentaldamagename) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: quest::getenvironmentaldamagename(uint8 damage_type)");
dXSTARG;
uint8 damage_type = (uint8) SvIV(ST(0));
std::string environmental_damage_name = quest_manager.getenvironmentaldamagename(damage_type);
sv_setpv(TARG, environmental_damage_name.c_str());
XSprePUSH;
PUSHTARG;
XSRETURN(1);
}
/* /*
This is the callback perl will look for to setup the This is the callback perl will look for to setup the
quest package's XSUBs quest package's XSUBs
@@ -8376,6 +8409,7 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file);
newXS(strcpy(buf, "getclassname"), XS__getclassname, file); newXS(strcpy(buf, "getclassname"), XS__getclassname, file);
newXS(strcpy(buf, "getcleannpcnamebyid"), XS__getcleannpcnamebyid, file); newXS(strcpy(buf, "getcleannpcnamebyid"), XS__getcleannpcnamebyid, file);
newXS(strcpy(buf, "getconsiderlevelname"), XS__getconsiderlevelname, file);
newXS(strcpy(buf, "gethexcolorcode"), XS__gethexcolorcode, file); newXS(strcpy(buf, "gethexcolorcode"), XS__gethexcolorcode, file);
newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file); newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file);
newXS(strcpy(buf, "getexpmodifierbycharid"), XS__getexpmodifierbycharid, file); newXS(strcpy(buf, "getexpmodifierbycharid"), XS__getexpmodifierbycharid, file);
@@ -8398,6 +8432,7 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "getcurrencyitemid"), XS__getcurrencyitemid, file); newXS(strcpy(buf, "getcurrencyitemid"), XS__getcurrencyitemid, file);
newXS(strcpy(buf, "getgendername"), XS__getgendername, file); newXS(strcpy(buf, "getgendername"), XS__getgendername, file);
newXS(strcpy(buf, "getdeityname"), XS__getdeityname, file); newXS(strcpy(buf, "getdeityname"), XS__getdeityname, file);
newXS(strcpy(buf, "getenvironmentaldamagename"), XS__getenvironmentaldamagename, file);
newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file);
newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file); newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file);
newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file); newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file);
+235 -111
View File
@@ -2,138 +2,262 @@
void command_ai(Client *c, const Seperator *sep) void command_ai(Client *c, const Seperator *sep)
{ {
Mob *target = c->GetTarget(); int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob");
c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID");
c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance");
c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID");
return;
}
if (strcasecmp(sep->arg[1], "factionid") == 0) { if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
if (target && sep->IsNumber(2)) { c->Message(Chat::White, "You must target an NPC to use this command.");
if (target->IsNPC()) { return;
target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2]));
} }
else {
c->Message(Chat::White, "%s is not an NPC.", target->GetName()); auto target = c->GetTarget()->CastToNPC();
bool is_consider = !strcasecmp(sep->arg[1], "consider");
bool is_faction = !strcasecmp(sep->arg[1], "faction");
bool is_guard = !strcasecmp(sep->arg[1], "guard");
bool is_roambox = !strcasecmp(sep->arg[1], "roambox");
bool is_spells = !strcasecmp(sep->arg[1], "spells");
if (
!is_consider &&
!is_faction &&
!is_guard &&
!is_roambox &&
!is_spells
) {
c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers to a mob");
c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID");
c->Message(Chat::White, "Usage: #ai guard - Save an NPC's guard spot to their current location");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance");
c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID");
return;
} }
}
else { if (is_consider) {
c->Message(Chat::White, "Usage: (targeted) #ai factionid [factionid]"); if (arguments == 2) {
} auto mob_name = sep->arg[2];
} auto mob_to_consider = entity_list.GetMob(mob_name);
else if (strcasecmp(sep->arg[1], "spellslist") == 0) { if (mob_to_consider) {
if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) { auto consider_level = static_cast<uint8>(mob_to_consider->GetReverseFactionCon(target));
if (target->IsNPC()) {
target->CastToNPC()->AI_AddNPCSpells(atoi(sep->arg[2]));
}
else {
c->Message(Chat::White, "%s is not an NPC.", target->GetName());
}
}
else {
c->Message(Chat::White, "Usage: (targeted) #ai spellslist [npc_spells_id]");
}
}
else if (strcasecmp(sep->arg[1], "con") == 0) {
if (target && sep->arg[2][0] != 0) {
Mob *tar2 = entity_list.GetMob(sep->arg[2]);
if (tar2) {
c->Message( c->Message(
Chat::White, Chat::White,
"%s considering %s: %i", fmt::format(
target->GetName(), "{} ({}) considers {} ({}) as {} ({}).",
tar2->GetName(), target->GetCleanName(),
tar2->GetReverseFactionCon(target)); target->GetID(),
mob_to_consider->GetCleanName(),
mob_to_consider->GetID(),
EQ::constants::GetConsiderLevelName(consider_level),
consider_level
).c_str()
);
} }
else { } else {
c->Message(Chat::White, "Error: %s not found.", sep->arg[2]); c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers a mob");
} }
} } else if (is_faction) {
else { if (sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: (targeted) #ai con [mob name]"); auto faction_id = std::stoi(sep->arg[2]);
} auto faction_name = content_db.GetFactionName(faction_id);
} target->SetNPCFactionID(faction_id);
else if (strcasecmp(sep->arg[1], "guard") == 0) {
if (target && target->IsNPC()) {
target->CastToNPC()->SaveGuardSpot(target->GetPosition());
}
else {
c->Message( c->Message(
Chat::White, Chat::White,
"Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)" fmt::format(
"{} ({}) is now on Faction {}.",
target->GetCleanName(),
target->GetID(),
(
faction_name.empty() ?
std::to_string(faction_id) :
fmt::format(
"{} ({})",
faction_name,
faction_id
)
)
).c_str()
); );
} else {
c->Message(Chat::White, "Usage: #ai faction [Faction ID] - Set an NPC's Faction ID");
} }
} } else if (is_guard) {
else if (strcasecmp(sep->arg[1], "roambox") == 0) { auto target_position = target->GetPosition();
if (target && target->IsAIControlled() && target->IsNPC()) {
if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && target->SaveGuardSpot(target_position);
sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) {
uint32 tmp = 2500; c->Message(
uint32 tmp2 = 2500; Chat::White,
if (sep->IsNumber(7)) { fmt::format(
tmp = atoi(sep->arg[7]); "{} ({}) now has a guard spot of {:.2f}, {:.2f}, {:.2f} with a heading of {:.2f}.",
} target->GetCleanName(),
if (sep->IsNumber(8)) { target->GetID(),
tmp2 = atoi(sep->arg[8]); target_position.x,
} target_position.y,
target->CastToNPC()->AI_SetRoambox( target_position.z,
atof(sep->arg[2]), target_position.w
atof(sep->arg[3]), ).c_str()
atof(sep->arg[4]),
atof(sep->arg[5]),
atof(sep->arg[6]),
tmp,
tmp2
); );
} } else if (is_roambox) {
else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) {
uint32 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(4)) {
tmp = atoi(sep->arg[4]);
}
if (sep->IsNumber(5)) {
tmp2 = atoi(sep->arg[5]);
}
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2);
}
else {
c->Message(Chat::White, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]");
c->Message(Chat::White, "Usage: #ai roambox dist roamdist [delay] [mindelay]");
}
}
else {
c->Message(Chat::White, "You need a AI NPC targeted");
}
}
else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) {
if (target) {
if (target->IsAIControlled()) { if (target->IsAIControlled()) {
target->AI_Stop(); if (
arguments >= 6 &&
arguments <= 8 &&
sep->IsNumber(2) &&
sep->IsNumber(3) &&
sep->IsNumber(4) &&
sep->IsNumber(5) &&
sep->IsNumber(6)
) {
auto distance = std::stof(sep->arg[2]);
auto min_x = std::stof(sep->arg[3]);
auto max_x = std::stof(sep->arg[4]);
auto min_y = std::stof(sep->arg[5]);
auto max_y = std::stof(sep->arg[6]);
uint32 delay = 2500;
uint32 minimum_delay = 2500;
if (sep->IsNumber(7)) {
delay = std::stoul(sep->arg[7]);
} }
else {
c->Message(Chat::White, "Error: Target is not AI controlled"); if (sep->IsNumber(8)) {
minimum_delay = std::stoul(sep->arg[8]);
} }
target->CastToNPC()->AI_SetRoambox(
distance,
max_x,
min_x,
max_y,
min_y,
delay,
minimum_delay
);
c->Message(
Chat::White,
fmt::format(
"{} ({}) now has a roambox from {}, {} to {}, {} with {} and {} and a distance of {}.",
target->GetCleanName(),
target->GetID(),
min_x,
min_y,
max_x,
max_y,
(
delay ?
fmt::format(
"a delay of {} ({})",
ConvertMillisecondsToTime(delay),
delay
):
"no delay"
),
(
minimum_delay ?
fmt::format(
"a minimum delay of {} ({})",
ConvertMillisecondsToTime(minimum_delay),
minimum_delay
):
"no minimum delay"
),
distance
).c_str()
);
} else if (
arguments >= 3 &&
arguments <= 4 &&
sep->IsNumber(2) &&
sep->IsNumber(3)
) {
auto max_distance = std::stof(sep->arg[2]);
auto roam_distance_variance = std::stof(sep->arg[3]);
uint32 delay = 2500;
uint32 minimum_delay = 2500;
if (sep->IsNumber(4)) {
delay = std::stoul(sep->arg[4]);
} }
else {
c->Message(Chat::White, "Usage: Target a Mob with AI enabled and use this to turn off their AI."); if (sep->IsNumber(5)) {
minimum_delay = std::stoul(sep->arg[5]);
} }
target->CastToNPC()->AI_SetRoambox(
max_distance,
roam_distance_variance,
delay,
minimum_delay
);
c->Message(
Chat::White,
fmt::format(
"{} ({}) now has a roambox with a max distance of {} and a roam distance variance of {} with {} and {}.",
target->GetCleanName(),
target->GetID(),
max_distance,
roam_distance_variance,
(
delay ?
fmt::format(
"a delay of {} ({})",
delay,
ConvertMillisecondsToTime(delay)
):
"no delay"
),
(
minimum_delay ?
fmt::format(
"a minimum delay of {} ({})",
minimum_delay,
ConvertMillisecondsToTime(delay)
):
"no minimum delay"
)
).c_str()
);
} else {
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Min X] [Max X] [Min Y] [Max Y] [Delay] [Minimum Delay] - Set an NPC's roambox using X and Y coordinates");
c->Message(Chat::White, "Usage: #ai roambox [Distance] [Roam Distance] [Delay] [Minimum Delay] - Set an NPC's roambox using roam distance");
} }
else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) { } else {
if (target) { c->Message(Chat::White, "You must target an NPC with AI.");
if (!target->IsAIControlled()) {
target->AI_Start();
} }
else { } else if (is_spells) {
c->Message(Chat::White, "Error: Target is already AI controlled"); if (sep->IsNumber(2)) {
auto spell_list_id = std::stoul(sep->arg[2]);
if (spell_list_id >= 0) {
target->CastToNPC()->AI_AddNPCSpells(spell_list_id);
c->Message(
Chat::White,
fmt::format(
"{} ({}) is now using Spell List {}.",
target->GetCleanName(),
target->GetID(),
spell_list_id
).c_str()
);
} else {
c->Message(Chat::White, "Spell List ID must be greater than or equal to 0.");
} }
} else {
c->Message(Chat::White, "Usage: #ai spells [Spell List ID] - Set an NPC's Spell List ID");
} }
else {
c->Message(Chat::White, "Usage: Target a Mob with AI disabled and use this to turn on their AI.");
}
}
else {
c->Message(Chat::White, "#AI Sub-commands");
c->Message(Chat::White, " factionid");
c->Message(Chat::White, " spellslist");
c->Message(Chat::White, " con");
c->Message(Chat::White, " guard");
} }
} }
+260 -127
View File
@@ -2,156 +2,289 @@
void command_flagedit(Client *c, const Seperator *sep) void command_flagedit(Client *c, const Seperator *sep)
{ {
//super-command for editing zone flags int arguments = sep->argnum;
if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { if (!arguments) {
c->Message(Chat::White, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); auto flags_link = EQ::SayLinkEngine::GenerateQuestSaylink("#flags", false, "#flags");
c->Message( c->Message(
Chat::White, Chat::White,
"...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone" "Usage: #flagedit lock [Zone ID|Zone Short Name] [Flag Name] - Set the specified flag name on the zone, locking the zone"
); );
c->Message(Chat::White, "...unlockzone [zone id/short] - Removes the flag requirement from the specified zone");
c->Message(Chat::White, "...listzones - List all zones which require a flag, and their flag's name");
c->Message(Chat::White, "...give [zone id/short] - Give your target the zone flag for the specified zone.");
c->Message( c->Message(
Chat::White, Chat::White,
"...take [zone id/short] - Take the zone flag for the specified zone away from your target" "Usage: #flagedit unlock [Zone ID|Zone Short Name] - Removes the flag requirement from the specified zone"
); );
c->Message(Chat::White, "...Note: use #flags to view flags on a person");
return;
}
if (!strcasecmp(sep->arg[1], "lockzone")) {
uint32 zoneid = 0;
if (sep->arg[2][0] != '\0') {
zoneid = atoi(sep->arg[2]);
if (zoneid < 1) {
zoneid = ZoneID(sep->arg[2]);
}
}
if (zoneid < 1) {
c->Message(Chat::Red, "zone required. see help.");
return;
}
char flag_name[128];
if (sep->argplus[3][0] == '\0') {
c->Message(Chat::Red, "flag name required. see help.");
return;
}
database.DoEscapeString(flag_name, sep->argplus[3], 64);
flag_name[127] = '\0';
std::string query = StringFormat(
"UPDATE zone SET flag_needed = '%s' "
"WHERE zoneidnumber = %d AND version = %d",
flag_name, zoneid, zone->GetInstanceVersion());
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::Red, "Error updating zone: %s", results.ErrorMessage().c_str());
return;
}
c->Message(Chat::Yellow, "Success! Zone %s now requires a flag, named %s", ZoneName(zoneid), flag_name);
return;
}
if (!strcasecmp(sep->arg[1], "unlockzone")) {
uint32 zoneid = 0;
if (sep->arg[2][0] != '\0') {
zoneid = atoi(sep->arg[2]);
if (zoneid < 1) {
zoneid = ZoneID(sep->arg[2]);
}
}
if (zoneid < 1) {
c->Message(Chat::Red, "zone required. see help.");
return;
}
std::string query = StringFormat(
"UPDATE zone SET flag_needed = '' "
"WHERE zoneidnumber = %d AND version = %d",
zoneid, zone->GetInstanceVersion());
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(Chat::Yellow, "Error updating zone: %s", results.ErrorMessage().c_str());
return;
}
c->Message(Chat::Yellow, "Success! Zone %s no longer requires a flag.", ZoneName(zoneid));
return;
}
if (!strcasecmp(sep->arg[1], "listzones")) {
std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed "
"FROM zone WHERE flag_needed != ''";
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
return;
}
c->Message(Chat::White, "Zones which require flags:");
for (auto row = results.begin(); row != results.end(); ++row)
c->Message( c->Message(
Chat::White, Chat::White,
"Zone %s (%s,%s) version %s requires key %s", "Usage: #flagedit list - List all zones which require a flag, and their flag's name"
row[2], );
c->Message(
Chat::White,
"Usage: #flagedit give [Zone ID|Zone Short Name] - Give your target the zone flag for the specified zone."
);
c->Message(
Chat::White,
"Usage: #flagedit take [Zone ID|Zone Short Name] - Take the zone flag for the specified zone away from your target"
);
c->Message(
Chat::White,
fmt::format(
"Note: Use {} to view the flags a player has.",
flags_link
).c_str()
);
return;
}
bool is_give = !strcasecmp(sep->arg[1], "give");
bool is_list = !strcasecmp(sep->arg[1], "list");
bool is_lock = !strcasecmp(sep->arg[1], "lock");
bool is_take = !strcasecmp(sep->arg[1], "take");
bool is_unlock = !strcasecmp(sep->arg[1], "unlock");
if (
!is_give &&
!is_list &&
!is_lock &&
!is_take &&
!is_unlock
) {
auto flags_link = EQ::SayLinkEngine::GenerateQuestSaylink("#flags", false, "#flags");
c->Message(
Chat::White,
"Usage: #flagedit lock [Zone ID|Zone Short Name] [Flag Name] - Set the specified flag name on the zone, locking the zone"
);
c->Message(
Chat::White,
"Usage: #flagedit unlock [Zone ID|Zone Short Name] - Removes the flag requirement from the specified zone"
);
c->Message(
Chat::White,
"Usage: #flagedit list - List all zones which require a flag, and their flag's name"
);
c->Message(
Chat::White,
"Usage: #flagedit give [Zone ID|Zone Short Name] - Give your target the zone flag for the specified zone."
);
c->Message(
Chat::White,
"Usage: #flagedit take [Zone ID|Zone Short Name] - Take the zone flag for the specified zone away from your target"
);
c->Message(
Chat::White,
fmt::format(
"Note: Use {} to view the flags a player has.",
flags_link
).c_str()
);
return;
}
if (is_give) {
uint32 zone_id = (
sep->IsNumber(2) ?
std::stoul(sep->arg[2]) :
ZoneID(sep->arg[2])
);
std::string zone_short_name = str_tolower(ZoneName(zone_id, true));
bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos;
if (zone_id && !is_unknown_zone) {
std::string zone_long_name = ZoneLongName(zone_id);
auto target = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
target = c->GetTarget()->CastToClient();
}
target->SetZoneFlag(zone_id);
c->Message(
Chat::White,
fmt::format(
"{} now {} the flag for {} ({}).",
c == target ?
"You" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
),
c == target ? "have" : "has",
zone_long_name,
zone_id
).c_str()
);
return;
}
} else if (is_list) {
std::string query = SQL(
SELECT long_name, zoneidnumber, version, flag_needed
FROM zone
WHERE flag_needed != ''
ORDER BY long_name ASC
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
return;
}
std::string popup_text = "<table>";
popup_text += "<tr><td>Zone</td><td>Flag Required</td></tr>";
for (auto row : results) {
popup_text += fmt::format(
"<tr><td>{} ({}){}</td><td>{}</td></tr>",
row[0], row[0],
row[1], row[1],
row[3], (
row[4] std::stoi(row[2]) != 0 ?
fmt::format(
"[Version {}]",
row[2]
) :
""
),
row[3]
);
}
popup_text += "</table>";
c->SendPopupToClient(
"Zone Flags",
popup_text.c_str()
); );
return; return;
} } else if (is_lock) {
uint32 zone_id = (
if (!strcasecmp(sep->arg[1], "give")) { sep->IsNumber(2) ?
uint32 zoneid = 0; std::stoul(sep->arg[2]) :
if (sep->arg[2][0] != '\0') { ZoneID(sep->arg[2])
zoneid = atoi(sep->arg[2]); );
if (zoneid < 1) { std::string zone_short_name = str_tolower(ZoneName(zone_id, true));
zoneid = ZoneID(sep->arg[2]); bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos;
} if (zone_id && !is_unknown_zone) {
} if (arguments < 3) {
if (zoneid < 1) { c->Message(
c->Message(Chat::Red, "zone required. see help."); Chat::White,
"Usage: #flagedit lock [Zone ID|Zone Short Name] [Flag Name] - Set the specified flag name on the zone, locking the zone"
);
return; return;
} }
Mob *t = c->GetTarget(); std::string flag_name = EscapeString(sep->argplus[3]);
if (t == nullptr || !t->IsClient()) { std::string zone_long_name = ZoneLongName(zone_id);
c->Message(Chat::Red, "client target required");
auto query = fmt::format(
SQL(
UPDATE zone
SET flag_needed = '{}'
WHERE zoneidnumber = {} AND version = {}
),
flag_name,
zone_id,
zone->GetInstanceVersion()
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(
Chat::White,
fmt::format(
"Error updating zone flag for {} ({}).",
zone_long_name,
zone_id
).c_str()
);
return; return;
} }
t->CastToClient()->SetZoneFlag(zoneid); c->Message(
Chat::White,
fmt::format(
"{} ({}) now requires a flag, named {}.",
zone_long_name,
zone_id,
flag_name
).c_str()
);
return;
}
} else if (is_take) {
uint32 zone_id = (
sep->IsNumber(2) ?
std::stoul(sep->arg[2]) :
ZoneID(sep->arg[2])
);
std::string zone_short_name = str_tolower(ZoneName(zone_id, true));
bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos;
if (zone_id && !is_unknown_zone) {
std::string zone_long_name = ZoneLongName(zone_id);
auto target = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
target = c->GetTarget()->CastToClient();
}
target->ClearZoneFlag(zone_id);
c->Message(
Chat::White,
fmt::format(
"{} no longer {} the flag for {} ({}).",
c == target ?
"You" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
),
c == target ? "have" : "has",
zone_long_name,
zone_id
).c_str()
);
return;
}
} else if (is_unlock) {
uint32 zone_id = (
sep->IsNumber(2) ?
std::stoul(sep->arg[2]) :
ZoneID(sep->arg[2])
);
std::string zone_short_name = str_tolower(ZoneName(zone_id, true));
bool is_unknown_zone = zone_short_name.find("unknown") != std::string::npos;
if (zone_id && !is_unknown_zone) {
std::string zone_long_name = ZoneLongName(zone_id);
auto query = fmt::format(
SQL(
UPDATE zone
SET flag_needed = ''
WHERE zoneidnumber = {} AND version = {}
),
zone_id,
zone->GetInstanceVersion()
);
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
c->Message(
Chat::White,
fmt::format(
"Error updating zone flag for {} ({}).",
zone_long_name,
zone_id
).c_str()
);
return; return;
} }
if (!strcasecmp(sep->arg[1], "give")) { c->Message(
uint32 zoneid = 0; Chat::White,
if (sep->arg[2][0] != '\0') { fmt::format(
zoneid = atoi(sep->arg[2]); "{} ({}) no longer requires a flag.",
if (zoneid < 1) { zone_long_name,
zoneid = ZoneID(sep->arg[2]); zone_id
} ).c_str()
} );
if (zoneid < 1) {
c->Message(Chat::Red, "zone required. see help.");
return; return;
} }
}
Mob *t = c->GetTarget();
if (t == nullptr || !t->IsClient()) {
c->Message(Chat::Red, "client target required");
return;
}
t->CastToClient()->ClearZoneFlag(zoneid);
return;
}
c->Message(Chat::Yellow, "Invalid action specified. use '#flagedit help' for help");
} }
+4 -6
View File
@@ -2,14 +2,12 @@
void command_hatelist(Client *c, const Seperator *sep) void command_hatelist(Client *c, const Seperator *sep)
{ {
Mob *target = c->GetTarget(); if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
if (target == nullptr) { c->Message(Chat::White, "You must target an NPC to use this command.");
c->Message(Chat::White, "Error: you must have a target.");
return; return;
} }
c->Message(Chat::White, "Display hate list for %s..", target->GetName()); auto target = c->GetTarget();
target->PrintHateListToClient(c); target->PrintHateListToClient(c);
} }
+181 -80
View File
@@ -5,97 +5,198 @@ extern WorldServer worldserver;
void command_logs(Client *c, const Seperator *sep) void command_logs(Client *c, const Seperator *sep)
{ {
int logs_set = 0; int arguments = sep->argnum;
if (sep->argnum > 0) { if (!arguments) {
/* #logs reload_all */ c->Message(
if (strcasecmp(sep->arg[1], "reload_all") == 0) { Chat::White,
"#logs list - Shows current log settings and categories loaded into the current process' memory for the first 50 log categories"
);
c->Message(
Chat::White,
"#logs list [Start Category ID] - Shows current log settings and categories loaded into the current process' memory, only shows 50 at a time starting at specified Category ID"
);
c->Message(
Chat::White,
"#logs reload - Reload all settings in world and all zone processes with what is defined in the database"
);
c->Message(
Chat::White,
"#logs set [console|file|gmsay] [Category ID] [Debug Level (1-3)] - Sets log settings during the lifetime of the zone"
);
return;
}
bool is_list = !strcasecmp(sep->arg[1], "list");
bool is_reload = !strcasecmp(sep->arg[1], "reload");
bool is_set = !strcasecmp(sep->arg[1], "set");
if (!is_list && !is_reload && !is_set) {
c->Message(
Chat::White,
"#logs list - Shows current log settings and categories loaded into the current process' memory for the first 50 log categories"
);
c->Message(
Chat::White,
"#logs list [Start Category ID] - Shows current log settings and categories loaded into the current process' memory, only shows 50 at a time starting at specified Category ID"
);
c->Message(
Chat::White,
"#logs reload - Reload all settings in world and all zone processes with what is defined in the database"
);
c->Message(
Chat::White,
"#logs set [console|file|gmsay] [Category ID] [Debug Level (1-3)] - Sets log settings during the lifetime of the zone"
);
return;
}
if (is_list) {
uint32 start_category_id = 1;
if (sep->IsNumber(2)) {
start_category_id = std::stoul(sep->arg[2]);
}
uint32 max_category_id = (start_category_id + 49);
std::string popup_text = "<table>";
popup_text += "<tr>";
popup_text += "<td>ID</td>";
popup_text += "<td>Name</td>";
popup_text += "<td>Console</td>";
popup_text += "<td>File</td>";
popup_text += "<td>GM Say</td>";
popup_text += "</tr>";
for (int index = start_category_id; index <= max_category_id; index++) {
if (index >= Logs::LogCategory::MaxCategoryID) {
max_category_id = (Logs::LogCategory::MaxCategoryID - 1);
break;
}
popup_text += fmt::format(
"<tr><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td></tr>",
index,
Logs::LogCategoryName[index],
LogSys.log_settings[index].log_to_console,
LogSys.log_settings[index].log_to_file,
LogSys.log_settings[index].log_to_gmsay
);
}
popup_text += "</table>";
std::string popup_title = fmt::format(
"Log Settings [{} to {}]",
start_category_id,
max_category_id
);
c->SendPopupToClient(
popup_title.c_str(),
popup_text.c_str()
);
c->Message(
Chat::White,
fmt::format(
"Viewing log category settings from {} ({}) to {} ({}).",
Logs::LogCategoryName[start_category_id],
start_category_id,
Logs::LogCategoryName[max_category_id],
max_category_id
).c_str()
);
int next_category_id = (max_category_id + 1);
if (next_category_id < Logs::LogCategory::MaxCategoryID) {
auto next_list_string = fmt::format(
"#logs list {}",
next_category_id
);
auto next_list_link = EQ::SayLinkEngine::GenerateQuestSaylink(
next_list_string,
false,
next_list_string
);
c->Message(
Chat::White,
fmt::format(
"To view the next 50 log settings, you can use {}.",
next_list_link
).c_str()
);
}
} else if (is_reload) {
auto pack = new ServerPacket(ServerOP_ReloadLogs, 0); auto pack = new ServerPacket(ServerOP_ReloadLogs, 0);
worldserver.SendPacket(pack); worldserver.SendPacket(pack);
c->Message( c->Message(
Chat::Red, Chat::White,
"Successfully sent the packet to world to reload log settings from the database for all zones" "Reloaded log settings worldwide."
); );
safe_delete(pack); safe_delete(pack);
} } else if (is_set) {
/* #logs list_settings */ auto logs_set = false;
if (strcasecmp(sep->arg[1], "list_settings") == 0 || bool is_console = !strcasecmp(sep->arg[2], "console");
(strcasecmp(sep->arg[1], "set") == 0 && strcasecmp(sep->arg[3], "") == 0)) { bool is_file = !strcasecmp(sep->arg[2], "file");
c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]"); bool is_gmsay = !strcasecmp(sep->arg[2], "gmsay");
int redisplay_columns = 0;
for (int i = 0; i < Logs::LogCategory::MaxCategoryID; i++) { if (!is_console && !is_file && !is_gmsay) {
if (redisplay_columns == 10) {
c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]");
redisplay_columns = 0;
}
c->Message(
0,
StringFormat(
"--- %i | %u | %u | %u | %s",
i,
LogSys.log_settings[i].log_to_console,
LogSys.log_settings[i].log_to_file,
LogSys.log_settings[i].log_to_gmsay,
Logs::LogCategoryName[i]
).c_str());
redisplay_columns++;
}
}
/* #logs set */
if (strcasecmp(sep->arg[1], "set") == 0) {
if (strcasecmp(sep->arg[2], "console") == 0) {
LogSys.log_settings[atoi(sep->arg[3])].log_to_console = atoi(sep->arg[4]);
logs_set = 1;
}
else if (strcasecmp(sep->arg[2], "file") == 0) {
LogSys.log_settings[atoi(sep->arg[3])].log_to_file = atoi(sep->arg[4]);
logs_set = 1;
}
else if (strcasecmp(sep->arg[2], "gmsay") == 0) {
LogSys.log_settings[atoi(sep->arg[3])].log_to_gmsay = atoi(sep->arg[4]);
logs_set = 1;
}
else {
c->Message( c->Message(
Chat::White, Chat::White,
"--- #logs set [console|file|gmsay] <category_id> <debug_level (1-3)> - Sets log settings during the lifetime of the zone" "#logs set [console|file|gmsay] [Category ID] [Debug Level (1-3)] - Sets log settings during the lifetime of the zone"
); );
c->Message(Chat::White, "--- #logs set gmsay 20 1 - Would output Quest errors to gmsay"); c->Message(Chat::White, "Example: #logs set gmsay 20 1 - Would output Quest errors to gmsay");
return;
} }
if (logs_set == 1) {
c->Message(Chat::Yellow, "Your Log Settings have been applied"); logs_set = true;
c->Message(
Chat::Yellow, auto category_id = std::stoul(sep->arg[3]);
"Output Method: %s :: Debug Level: %i - Category: %s", auto setting = std::stoul(sep->arg[4]);
sep->arg[2],
atoi(sep->arg[4]), if (is_console) {
Logs::LogCategoryName[atoi(sep->arg[3])] LogSys.log_settings[category_id].log_to_console = setting;
} else if (is_file) {
LogSys.log_settings[category_id].log_to_file = setting;
} else if (is_gmsay) {
LogSys.log_settings[category_id].log_to_gmsay = setting;
}
if (logs_set) {
std::string popup_text = "<table>";
popup_text += fmt::format(
"<tr><td>ID</td><td>{}</td><tr>",
category_id
);
popup_text += fmt::format(
"<tr><td>Category</td><td>{}</td><tr>",
Logs::LogCategoryName[category_id]
);
popup_text += fmt::format(
"<tr><td>Method</td><td>{}</td></tr>",
sep->arg[2]
);
popup_text += fmt::format(
"<tr><td>Setting</td><td>{}</td></tr>",
setting
);
popup_text += "</table>";
c->SendPopupToClient(
"Log Settings Applied",
popup_text.c_str()
); );
} }
/* We use a general 'is_category_enabled' now, let's update when we update any output settings
This is used in hot places of code to check if its enabled in any way before triggering logs LogSys.log_settings[category_id].is_category_enabled = setting ? 1 : 0;
*/
if (atoi(sep->arg[4]) > 0) {
LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 1;
}
else {
LogSys.log_settings[atoi(sep->arg[3])].is_category_enabled = 0;
}
}
}
else {
c->Message(Chat::White, "#logs usage:");
c->Message(
Chat::White,
"--- #logs reload_all - Reload all settings in world and all zone processes with what is defined in the database"
);
c->Message(
Chat::White,
"--- #logs list_settings - Shows current log settings and categories loaded into the current process' memory"
);
c->Message(
Chat::White,
"--- #logs set [console|file|gmsay] <category_id> <debug_level (1-3)> - Sets log settings during the lifetime of the zone"
);
} }
} }
+31 -19
View File
@@ -2,29 +2,41 @@
void command_name(Client *c, const Seperator *sep) void command_name(Client *c, const Seperator *sep)
{ {
Client *target; int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #name [New Name] - Rename your player target");
return;
}
if ((strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient()))) { if (c->GetTarget() && c->GetTarget()->IsClient()) {
c->Message(Chat::White, "Usage: #name newname (requires player target)"); auto target = c->GetTarget()->CastToClient();
}
else { std::string new_name = sep->arg[1];
target = c->GetTarget()->CastToClient(); std::string old_name = target->GetCleanName();
char *oldname = strdup(target->GetName());
if (target->ChangeFirstName(sep->arg[1], c->GetName())) { if (target->ChangeFirstName(new_name.c_str(), c->GetCleanName())) {
c->Message(Chat::White, "Successfully renamed %s to %s", oldname, sep->arg[1]);
// until we get the name packet working right this will work
c->Message(Chat::White, "Sending player to char select.");
target->Kick("Name was changed");
}
else {
c->Message( c->Message(
Chat::Red, Chat::White,
"ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.", fmt::format(
oldname, "Successfully renamed {} to {}",
sep->arg[2] old_name,
new_name
).c_str()
);
c->Message(Chat::White, "Sending player to char select.");
target->Kick("Name was changed");
} else {
c->Message(
Chat::White,
fmt::format(
"Unable to rename {}. Check that the new name '{}' isn't already taken.",
old_name,
new_name
).c_str()
); );
} }
free(oldname);
} }
} }
+98 -70
View File
@@ -2,13 +2,10 @@
void command_netstats(Client *c, const Seperator *sep) void command_netstats(Client *c, const Seperator *sep)
{ {
if (c) { bool is_full = !strcasecmp(sep->arg[1], "full");
auto client = c; bool is_reset = !strcasecmp(sep->arg[1], "reset");
if (c->GetTarget() && c->GetTarget()->IsClient()) {
client = c->GetTarget()->CastToClient();
}
if (strcasecmp(sep->arg[1], "reset") == 0) { if (is_reset) {
auto connection = c->Connection(); auto connection = c->Connection();
c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset)."); c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset).");
connection->ResetStats(); connection->ResetStats();
@@ -24,118 +21,149 @@ void command_netstats(Client *c, const Seperator *sep)
now - stats.created now - stats.created
).count(); ).count();
c->Message(Chat::White, "Netstats:"); std::string popup_text = "<table>";
c->Message(Chat::White, "--------------------------------------------------------------------");
c->Message( popup_text += fmt::format(
Chat::White, "<tr><td>Sent Bytes</td><td>{} ({:.2f} Per Second)</td></tr>",
"Sent Bytes: %u (%.2f/sec)",
stats.sent_bytes, stats.sent_bytes,
stats.sent_bytes / sec_since_stats_reset stats.sent_bytes / sec_since_stats_reset
); );
c->Message(
Chat::White, popup_text += fmt::format(
"Recv Bytes: %u (%.2f/sec)", "<tr><td>Received Bytes</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.recv_bytes, stats.recv_bytes,
stats.recv_bytes / sec_since_stats_reset stats.recv_bytes / sec_since_stats_reset
); );
c->Message(
Chat::White, "Bytes Before Encode (Sent): %u, Compression Rate: %.2f%%", stats.bytes_before_encode, popup_text += "<br><br>";
popup_text += fmt::format(
"<tr><td>Bytes Before Encode (Sent)</td><td>{}</td><td>Compression Rate</td><td>{:.2f}%%</td></tr>",
stats.bytes_before_encode,
static_cast<double>(stats.bytes_before_encode - stats.sent_bytes) / static_cast<double>(stats.bytes_before_encode - stats.sent_bytes) /
static_cast<double>(stats.bytes_before_encode) * 100.0 static_cast<double>(stats.bytes_before_encode) * 100.0
); );
c->Message(
Chat::White, "Bytes After Decode (Recv): %u, Compression Rate: %.2f%%", stats.bytes_after_decode, popup_text += fmt::format(
"<tr><td>Bytes After Decode (Received)</td><td>{}</td><td>Compression Rate</td><td>{:.2f}%%</td></tr>",
stats.bytes_after_decode,
static_cast<double>(stats.bytes_after_decode - stats.recv_bytes) / static_cast<double>(stats.bytes_after_decode - stats.recv_bytes) /
static_cast<double>(stats.bytes_after_decode) * 100.0 static_cast<double>(stats.bytes_after_decode) * 100.0
); );
c->Message(Chat::White, "Min Ping: %u", stats.min_ping);
c->Message(Chat::White, "Max Ping: %u", stats.max_ping); popup_text += "<br><br>";
c->Message(Chat::White, "Last Ping: %u", stats.last_ping);
c->Message(Chat::White, "Average Ping: %u", stats.avg_ping); popup_text += fmt::format("<tr><td>Min Ping</td><td>{}</td></tr>", stats.min_ping);
c->Message(Chat::White, "--------------------------------------------------------------------"); popup_text += fmt::format("<tr><td>Max Ping</td><td>{}</td></tr>", stats.max_ping);
c->Message( popup_text += fmt::format("<tr><td>Last Ping</td><td>{}</td></tr>", stats.last_ping);
Chat::White, popup_text += fmt::format("<tr><td>Average Ping</td><td>{}</td></tr>", stats.avg_ping);
"(Realtime) Recv Packets: %u (%.2f/sec)",
popup_text += "<br><br>";
popup_text += fmt::format(
"<tr><td>(Realtime) Received Packets</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.recv_packets, stats.recv_packets,
stats.recv_packets / sec_since_stats_reset stats.recv_packets / sec_since_stats_reset
); );
c->Message(
Chat::White, popup_text += fmt::format(
"(Realtime) Sent Packets: %u (%.2f/sec)", "<tr><td>(Realtime) Sent Packets</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.sent_packets, stats.sent_packets,
stats.sent_packets / sec_since_stats_reset stats.sent_packets / sec_since_stats_reset
); );
c->Message(Chat::White, "(Sync) Recv Packets: %u", stats.sync_recv_packets);
c->Message(Chat::White, "(Sync) Sent Packets: %u", stats.sync_sent_packets); popup_text += "<br><br>";
c->Message(Chat::White, "(Sync) Remote Recv Packets: %u", stats.sync_remote_recv_packets);
c->Message(Chat::White, "(Sync) Remote Sent Packets: %u", stats.sync_remote_sent_packets); popup_text += fmt::format("<tr><td>(Sync) Received Packets</td><td>{}</td></tr>", stats.sync_recv_packets);
c->Message( popup_text += fmt::format("<tr><td>(Sync) Sent Packets</td><td>{}</td></tr>", stats.sync_sent_packets);
Chat::White, popup_text += fmt::format("<tr><td>(Sync) Remote Received Packets</td><td>{}</td></tr>", stats.sync_remote_recv_packets);
"Packet Loss In: %.2f%%", popup_text += fmt::format("<tr><td>(Sync) Remote Sent Packets</td><td>{}</td></tr>", stats.sync_remote_sent_packets);
100.0 * (1.0 - static_cast<double>(stats.sync_recv_packets) /
static_cast<double>(stats.sync_remote_sent_packets))); popup_text += "<br><br>";
c->Message(
Chat::White, popup_text += fmt::format(
"Packet Loss Out: %.2f%%", "<tr><td>Packet Loss In</td><td>{:.2f}%%</td></tr>",
100.0 * (1.0 - static_cast<double>(stats.sync_remote_recv_packets) / (100.0 * (1.0 - static_cast<double>(stats.sync_recv_packets) / static_cast<double>(stats.sync_remote_sent_packets)))
static_cast<double>(stats.sync_sent_packets))); );
c->Message(Chat::White, "--------------------------------------------------------------------");
c->Message( popup_text += fmt::format(
Chat::White, "<tr><td>Packet Loss Out</td><td>{:.2f}%%</td></tr>",
"Resent Packets: %u (%.2f/sec)", (100.0 * (1.0 - static_cast<double>(stats.sync_remote_recv_packets) / static_cast<double>(stats.sync_sent_packets)))
);
popup_text += "<br><br>";
popup_text += fmt::format(
"<tr><td>Resent Packets</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.resent_packets, stats.resent_packets,
stats.resent_packets / sec_since_stats_reset stats.resent_packets / sec_since_stats_reset
); );
c->Message(
Chat::White, popup_text += fmt::format(
"Resent Fragments: %u (%.2f/sec)", "<tr><td>Resent Fragments</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.resent_fragments, stats.resent_fragments,
stats.resent_fragments / sec_since_stats_reset stats.resent_fragments / sec_since_stats_reset
); );
c->Message(
Chat::White, popup_text += fmt::format(
"Resent Non-Fragments: %u (%.2f/sec)", "<tr><td>Resent Non-Fragments</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.resent_full, stats.resent_full,
stats.resent_full / sec_since_stats_reset stats.resent_full / sec_since_stats_reset
); );
c->Message(
Chat::White, popup_text += "<br><br>";
"Dropped Datarate Packets: %u (%.2f/sec)",
popup_text += fmt::format(
"<tr><td>Dropped Datarate Packets</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.dropped_datarate_packets, stats.dropped_datarate_packets,
stats.dropped_datarate_packets / sec_since_stats_reset stats.dropped_datarate_packets / sec_since_stats_reset
); );
if (opts.daybreak_options.outgoing_data_rate > 0.0) { if (opts.daybreak_options.outgoing_data_rate > 0.0) {
c->Message( popup_text += fmt::format(
Chat::White, "<tr><td>Outgoing Link Saturation</td><td>{:.2f}%% ({:.2f}kb Per Second)</td></tr>",
"Outgoing Link Saturation %.2f%% (%.2fkb/sec)", (100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) / opts.daybreak_options.outgoing_data_rate))),
100.0 * (1.0 - ((opts.daybreak_options.outgoing_data_rate - stats.datarate_remaining) /
opts.daybreak_options.outgoing_data_rate)),
opts.daybreak_options.outgoing_data_rate opts.daybreak_options.outgoing_data_rate
); );
} }
if (strcasecmp(sep->arg[1], "full") == 0) { if (is_full) {
c->Message(Chat::White, "--------------------------------------------------------------------"); popup_text += "<br><br>";
c->Message(Chat::White, "Sent Packet Types");
popup_text += "<tr><td>Sent Packet Types</td></tr>";
for (auto i = 0; i < _maxEmuOpcode; ++i) { for (auto i = 0; i < _maxEmuOpcode; ++i) {
auto cnt = eqs_stats.SentCount[i]; auto cnt = eqs_stats.SentCount[i];
if (cnt > 0) { if (cnt > 0) {
c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); popup_text += fmt::format(
"<tr><td>{}</td><td>{} ({:.2f} Per Second)</td></tr>",
OpcodeNames[i],
cnt,
cnt / sec_since_stats_reset
);
} }
} }
c->Message(Chat::White, "--------------------------------------------------------------------"); popup_text += "<br><br>";
c->Message(Chat::White, "Recv Packet Types");
popup_text += "<tr><td>Received Packet Types</td></tr>";
for (auto i = 0; i < _maxEmuOpcode; ++i) { for (auto i = 0; i < _maxEmuOpcode; ++i) {
auto cnt = eqs_stats.RecvCount[i]; auto cnt = eqs_stats.RecvCount[i];
if (cnt > 0) { if (cnt > 0) {
c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset); popup_text += fmt::format(
"<tr><td>{}</td><td>{} ({:.2f} Per Second)</td></tr>",
OpcodeNames[i],
cnt,
cnt / sec_since_stats_reset
);
} }
} }
} }
c->Message(Chat::White, "--------------------------------------------------------------------"); popup_text += "</table>";
}
c->SendPopupToClient(
"Network Statistics",
popup_text.c_str()
);
} }
+231 -86
View File
@@ -1,104 +1,249 @@
#include "../client.h" #include "../client.h"
#include "../corpse.h" #include "../corpse.h"
#include "../../common/data_verification.h"
void command_npcloot(Client *c, const Seperator *sep) void command_npcloot(Client *c, const Seperator *sep)
{ {
if (c->GetTarget() == 0) { if (!c->GetTarget() || (!c->GetTarget()->IsNPC() && !c->GetTarget()->IsCorpse())) {
c->Message(Chat::White, "Error: No target"); c->Message(Chat::White, "You must target an NPC or a Corpse to use this command.");
// #npcloot show return;
} }
else if (strcasecmp(sep->arg[1], "show") == 0) {
if (c->GetTarget()->IsNPC()) { int arguments = sep->argnum;
c->GetTarget()->CastToNPC()->QueryLoot(c); if (!arguments) {
c->Message(Chat::White, "Usage: #npcloot add [Item ID] [Charges] [Equip] [Augment 1 ID] [Augment 2 ID] [Augment 3 ID] [Augment 4 ID] [Augment 5 ID] [Augment 6 ID] - Adds the specified item to an NPC's loot");
c->Message(Chat::White, "Usage: #npcloot money [Platinum] [Gold] [Silver] [Copper] - Set an NPC's current money");
c->Message(Chat::White, "Usage: #npcloot remove [All|Item ID] - Remove loot from an NPC by ID or remove all loot");
c->Message(Chat::White, "Usage: #npcloot show - Shows target NPC's or Corpse's current loot");
return;
} }
else if (c->GetTarget()->IsCorpse()) {
c->GetTarget()->CastToCorpse()->QueryLoot(c); bool is_add = !strcasecmp(sep->arg[1], "add");
bool is_money = !strcasecmp(sep->arg[1], "money");
bool is_remove = !strcasecmp(sep->arg[1], "remove");
bool is_show = !strcasecmp(sep->arg[1], "show");
if (
!is_add &&
!is_money &&
!is_remove &&
!is_show
) {
c->Message(Chat::White, "Usage: #npcloot add [Item ID] [Charges] [Equip] [Augment 1 ID] [Augment 2 ID] [Augment 3 ID] [Augment 4 ID] [Augment 5 ID] [Augment 6 ID] - Adds the specified item to an NPC's loot");
c->Message(Chat::White, "Usage: #npcloot money [Platinum] [Gold] [Silver] [Copper] - Set an NPC's current money");
c->Message(Chat::White, "Usage: #npcloot remove [All|Item ID] - Remove loot from an NPC by ID or remove all loot");
c->Message(Chat::White, "Usage: #npcloot show - Shows target NPC's or Corpse's current loot");
return;
} }
else {
c->Message(Chat::White, "Error: Target's type doesnt have loot"); if (is_add) {
if (!c->GetTarget()->IsNPC() || !sep->IsNumber(2)) {
c->Message(Chat::White, "Usage: #npcloot add [Item ID] [Charges] [Equip] [Augment 1 ID] [Augment 2 ID] [Augment 3 ID] [Augment 4 ID] [Augment 5 ID] [Augment 6 ID] - Adds the specified item to an NPC's loot");
return;
} }
}
// These 2 types are *BAD* for the next few commands auto item_id = std::stoul(sep->arg[2]);
else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) { auto item_charges = sep->IsNumber(3) ? static_cast<uint16>(std::stoul(sep->arg[3])) : 1;
c->Message(Chat::White, "Error: Invalid target type, try a NPC =)."); bool equip_item = arguments >= 4 ? atobool(sep->arg[4]) : false;
// #npcloot add auto augment_one_id = sep->IsNumber(5) ? std::stoul(sep->arg[5]) : 0;
} auto augment_two_id = sep->IsNumber(6) ? std::stoul(sep->arg[6]) : 0;
else if (strcasecmp(sep->arg[1], "add") == 0) { auto augment_three_id = sep->IsNumber(7) ? std::stoul(sep->arg[7]) : 0;
// #npcloot add item auto augment_four_id = sep->IsNumber(8) ? std::stoul(sep->arg[8]) : 0;
if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) { auto augment_five_id = sep->IsNumber(9) ? std::stoul(sep->arg[9]) : 0;
uint32 item = atoi(sep->arg[2]); auto augment_six_id = sep->IsNumber(10) ? std::stoul(sep->arg[10]) : 0;
if (database.GetItem(item)) {
if (sep->arg[3][0] != 0 && sep->IsNumber(3)) { auto item_data = database.GetItem(item_id);
c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0);
} if (!item_data) {
else {
c->GetTarget()->CastToNPC()->AddItem(item, 1, 0);
}
c->Message(Chat::White, "Added item(%i) to the %s's loot.", item, c->GetTarget()->GetName());
}
else {
c->Message(Chat::White, "Error: #npcloot add: Item(%i) does not exist!", item);
}
}
else if (!sep->IsNumber(2)) {
c->Message(Chat::White, "Error: #npcloot add: Itemid must be a number.");
}
else {
c->Message(Chat::White, "Error: #npcloot add: This is not a valid target.");
}
}
// #npcloot remove
else if (strcasecmp(sep->arg[1], "remove") == 0) {
//#npcloot remove all
if (strcasecmp(sep->arg[2], "all") == 0) {
c->Message(Chat::White, "Error: #npcloot remove all: Not yet implemented.");
//#npcloot remove itemid
}
else {
if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) {
uint32 item = atoi(sep->arg[2]);
c->GetTarget()->CastToNPC()->RemoveItem(item);
c->Message(Chat::White, "Removed item(%i) from the %s's loot.", item, c->GetTarget()->GetName());
}
else if (!sep->IsNumber(2)) {
c->Message(Chat::White, "Error: #npcloot remove: Item must be a number.");
}
else {
c->Message(Chat::White, "Error: #npcloot remove: This is not a valid target.");
}
}
}
// #npcloot money
else if (strcasecmp(sep->arg[1], "money") == 0) {
if (c->GetTarget()->IsNPC() && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5)) {
if ((atoi(sep->arg[2]) < 34465 && atoi(sep->arg[2]) >= 0) &&
(atoi(sep->arg[3]) < 34465 && atoi(sep->arg[3]) >= 0) &&
(atoi(sep->arg[4]) < 34465 && atoi(sep->arg[4]) >= 0) &&
(atoi(sep->arg[5]) < 34465 && atoi(sep->arg[5]) >= 0)) {
c->GetTarget()->CastToNPC()->AddCash(
atoi(sep->arg[5]),
atoi(sep->arg[4]),
atoi(sep->arg[3]),
atoi(sep->arg[2]));
c->Message( c->Message(
Chat::White, Chat::White,
"Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.", fmt::format(
atoi(sep->arg[2]), "Item ID {} could not be found",
atoi(sep->arg[3]), item_id
atoi(sep->arg[4]), ).c_str()
atoi(sep->arg[5]), );
c->GetTarget()->GetName()); return;
} }
else {
c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465."); c->GetTarget()->CastToNPC()->AddItem(
item_id,
item_charges,
equip_item,
augment_one_id,
augment_two_id,
augment_three_id,
augment_four_id,
augment_five_id,
augment_six_id
);
auto item = database.CreateItem(
item_id,
item_charges,
augment_one_id,
augment_two_id,
augment_three_id,
augment_four_id,
augment_five_id,
augment_six_id
);
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
linker.SetItemInst(item);
auto item_link = linker.GenerateLink();
c->Message(
Chat::White,
fmt::format(
"Added {} ({}) to {} ({}).",
item_link,
item_id,
c->GetTarget()->GetCleanName(),
c->GetTarget()->GetID()
).c_str()
);
} else if (is_money) {
if (!c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
auto target = c->GetTarget()->CastToNPC();
if (sep->IsNumber(2)) {
uint16 platinum = EQ::Clamp(std::stoi(sep->arg[2]), 0, 65535);
uint16 gold = sep->IsNumber(3) ? EQ::Clamp(std::stoi(sep->arg[3]), 0, 65535) : 0;
uint16 silver = sep->IsNumber(4) ? EQ::Clamp(std::stoi(sep->arg[4]), 0, 65535) : 0;
uint16 copper = sep->IsNumber(5) ? EQ::Clamp(std::stoi(sep->arg[5]), 0, 65535) : 0;
target->AddCash(
copper,
silver,
gold,
platinum
);
auto money_string = (
(
copper ||
silver ||
gold ||
platinum
) ?
ConvertMoneyToString(
platinum,
gold,
silver,
copper
) :
"no money"
);
c->Message(
Chat::White,
fmt::format(
"{} ({}) now has {}.",
target->GetCleanName(),
target->GetID(),
money_string
).c_str()
);
} else {
c->Message(Chat::White, "Usage: #npcloot money [Platinum] [Gold] [Silver] [Copper] - Set an NPC's current money");
}
} else if (is_remove) {
if (!c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
auto target = c->GetTarget()->CastToNPC();
bool is_remove_all = !strcasecmp(sep->arg[2], "all");
if (is_remove_all) {
auto loot_list = target->GetLootList();
auto total_item_count = 0;
for (const auto& item_id : loot_list) {
auto item_count = target->CountItem(item_id);
target->RemoveItem(item_id);
c->Message(
Chat::White,
fmt::format(
"Removed {} {} ({}) from {} ({}).",
item_count,
database.CreateItemLink(item_id),
item_id,
target->GetCleanName(),
target->GetID()
).c_str()
);
total_item_count += item_count;
}
if (!total_item_count) {
c->Message(
Chat::White,
fmt::format(
"{} ({}) has no items to remove.",
target->GetCleanName(),
target->GetID()
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} Item{} removed from {} ({}).",
total_item_count,
total_item_count != 1 ? "s" : "",
target->GetCleanName(),
target->GetID()
).c_str()
);
}
} else {
if (sep->IsNumber(2)) {
auto item_id = std::stoul(sep->arg[2]);
auto item_count = target->CountItem(item_id);
if (item_count) {
target->RemoveItem(item_id);
c->Message(
Chat::White,
fmt::format(
"Removed {} {} ({}) from {} ({}).",
item_count,
database.CreateItemLink(item_id),
item_id,
target->GetCleanName(),
target->GetID()
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"{} ({}) does not have any {} ({}).",
target->GetCleanName(),
target->GetID(),
database.CreateItemLink(item_id),
item_id
).c_str()
);
}
} else {
c->Message(Chat::White, "Usage: #npcloot remove [All|Item ID] - Remove loot from an NPC by ID or remove all loot");
} }
} }
else { } else if (is_show) {
c->Message(Chat::White, "Usage: #npcloot money platinum gold silver copper"); if (c->GetTarget()->IsNPC()) {
c->GetTarget()->CastToNPC()->QueryLoot(c);
} else if (c->GetTarget()->IsCorpse()) {
c->GetTarget()->CastToCorpse()->QueryLoot(c);
} }
} }
else {
c->Message(Chat::White, "Usage: #npcloot [show/money/add/remove] [itemid/all/money: pp gp sp cp]");
}
} }
+35 -11
View File
@@ -2,21 +2,45 @@
void command_timers(Client *c, const Seperator *sep) void command_timers(Client *c, const Seperator *sep)
{ {
if (!c->GetTarget() || !c->GetTarget()->IsClient()) { auto target = c;
c->Message(Chat::White, "Need a player target for timers."); if (c->GetTarget() && c->GetTarget()->IsClient()) {
return; target = c->GetTarget()->CastToClient();
} }
Client *them = c->GetTarget()->CastToClient();
std::vector<std::pair<pTimerType, PersistentTimer *> > res; std::vector<std::pair<pTimerType, PersistentTimer *>> timers;
them->GetPTimers().ToVector(res); target->GetPTimers().ToVector(timers);
c->Message(Chat::White, "Timers for target:"); std::string popup_title = fmt::format(
"Recast Timers for {}",
c == target ?
"Yourself" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
)
);
int r; std::string popup_text = "<table>";
int l = res.size();
for (r = 0; r < l; r++) { popup_text += "<tr><td>Timer ID</td><td>Remaining</td></tr>";
c->Message(Chat::White, "Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime());
for (const auto& timer : timers) {
auto remaining_time = timer.second->GetRemainingTime();
if (remaining_time) {
popup_text += fmt::format(
"<tr><td>{}</td><td>{}</td></tr>",
timer.first,
ConvertSecondsToTime(remaining_time)
);
} }
} }
popup_text += "</table>";
c->SendPopupToClient(
popup_title.c_str(),
popup_text.c_str()
);
}
+17 -5
View File
@@ -2,11 +2,23 @@
void command_undye(Client *c, const Seperator *sep) void command_undye(Client *c, const Seperator *sep)
{ {
auto target = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) { if (c->GetTarget() && c->GetTarget()->IsClient()) {
c->GetTarget()->CastToClient()->Undye(); target = c->GetTarget()->CastToClient();
}
else {
c->Message(Chat::White, "ERROR: Client target required");
}
} }
target->Undye();
c->Message(
Chat::White,
fmt::format(
"Undyed armor for {}.",
c == target ?
"yourself" :
fmt::format(
"{} ({})",
target->GetCleanName(),
target->GetID()
)
).c_str()
);
}
+1 -4
View File
@@ -2,9 +2,6 @@
void command_undyeme(Client *c, const Seperator *sep) void command_undyeme(Client *c, const Seperator *sep)
{ {
if (c) {
c->Undye(); c->Undye();
c->Message(Chat::Red, "Dye removed from all slots. Please zone for the process to complete."); c->Message(Chat::White, "Undyed armor for yourself.");
} }
}
+12 -4
View File
@@ -2,9 +2,17 @@
void command_version(Client *c, const Seperator *sep) void command_version(Client *c, const Seperator *sep)
{ {
c->Message(Chat::White, "Current version information."); std::string popup_text = "<table>";
c->Message(Chat::White, " %s", CURRENT_VERSION);
c->Message(Chat::White, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); popup_text += fmt::format("<tr><td>Version</td><td>{}</td></tr>", CURRENT_VERSION);
c->Message(Chat::White, " Last modified on: %s", LAST_MODIFIED); popup_text += fmt::format("<tr><td>Compiled</td><td>{} {}</td></tr>", COMPILE_DATE, COMPILE_TIME);
popup_text += fmt::format("<tr><td>Last Modified</td><td>{}</td></tr>", LAST_MODIFIED);
popup_text += "</table>";
c->SendPopupToClient(
"Server Version Information",
popup_text.c_str()
);
} }
+46 -8
View File
@@ -645,15 +645,53 @@ bool HateList::IsHateListEmpty() {
void HateList::PrintHateListToClient(Client *c) void HateList::PrintHateListToClient(Client *c)
{ {
auto iterator = list.begin(); if (list.size()) {
while (iterator != list.end()) c->Message(
{ Chat::White,
struct_HateList *e = (*iterator); fmt::format(
c->Message(Chat::White, "- name: %s, damage: %d, hate: %d", "Displaying hate list for {} ({}).",
(e->entity_on_hatelist && e->entity_on_hatelist->GetName()) ? e->entity_on_hatelist->GetName() : "(null)", hate_owner->GetCleanName(),
e->hatelist_damage, e->stored_hate_amount); hate_owner->GetID()
).c_str()
);
++iterator; auto entity_number = 1;
for (const auto& hate_entity : list) {
if (hate_entity->entity_on_hatelist) {
c->Message(
Chat::White,
fmt::format(
"Hate Entity {} | Name: {} ({}) Damage: {} Hate: {}",
entity_number,
hate_entity->entity_on_hatelist->GetName(),
hate_entity->entity_on_hatelist->GetID(),
hate_entity->hatelist_damage,
hate_entity->stored_hate_amount
).c_str()
);
} else {
c->Message(
Chat::White,
fmt::format(
"Hate Entity {} | Damage: {} Hate: {}",
entity_number,
hate_entity->hatelist_damage,
hate_entity->stored_hate_amount
).c_str()
);
}
entity_number++;
}
} else {
c->Message(
Chat::White,
fmt::format(
"{} ({}) has nothing on its hatelist.",
hate_owner->GetCleanName(),
hate_owner->GetID()
).c_str()
);
} }
} }
+10
View File
@@ -3367,6 +3367,14 @@ std::string lua_get_body_type_name(uint32 bodytype_id) {
return quest_manager.getbodytypename(bodytype_id); return quest_manager.getbodytypename(bodytype_id);
} }
std::string lua_get_consider_level_name(uint8 consider_level) {
return quest_manager.getconsiderlevelname(consider_level);
}
std::string lua_get_environmental_damage_name(uint8 damage_type) {
return quest_manager.getenvironmentaldamagename(damage_type);
}
#define LuaCreateNPCParse(name, c_type, default_value) do { \ #define LuaCreateNPCParse(name, c_type, default_value) do { \
cur = table[#name]; \ cur = table[#name]; \
if(luabind::type(cur) != LUA_TNIL) { \ if(luabind::type(cur) != LUA_TNIL) { \
@@ -3814,6 +3822,8 @@ luabind::scope lua_register_general() {
luabind::def("get_faction_name", &lua_get_faction_name), luabind::def("get_faction_name", &lua_get_faction_name),
luabind::def("get_language_name", &lua_get_language_name), luabind::def("get_language_name", &lua_get_language_name),
luabind::def("get_body_type_name", &lua_get_body_type_name), luabind::def("get_body_type_name", &lua_get_body_type_name),
luabind::def("get_consider_level_name", &lua_get_consider_level_name),
luabind::def("get_environmental_damage_name", &lua_get_environmental_damage_name),
/* /*
Cross Zone Cross Zone
+1 -1
View File
@@ -326,7 +326,7 @@ public:
void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used, void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0);
bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0, bool SpellFinished(uint16 spell_id, Mob *target, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, uint16 mana_used = 0,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, bool from_casted_spell = false, uint32 aa_id = 0); uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, bool from_casted_spell = false, uint32 aa_id = 0);
void SendBeginCast(uint16 spell_id, uint32 casttime); void SendBeginCast(uint16 spell_id, uint32 casttime);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0, virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, int reflect_effectiveness = 0,
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0); bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1, int32 duration_override = 0);
+9
View File
@@ -1052,6 +1052,10 @@ std::string QuestManager::getbodytypename(uint32 bodytype_id) {
return EQ::constants::GetBodyTypeName(static_cast<bodyType>(bodytype_id)); return EQ::constants::GetBodyTypeName(static_cast<bodyType>(bodytype_id));
} }
std::string QuestManager::getconsiderlevelname(uint8 consider_level) {
return EQ::constants::GetConsiderLevelName(consider_level);
}
void QuestManager::safemove() { void QuestManager::safemove() {
QuestManagerCurrentQuestVars(); QuestManagerCurrentQuestVars();
if (initiator && initiator->IsClient()) if (initiator && initiator->IsClient())
@@ -3698,3 +3702,8 @@ const SPDat_Spell_Struct* QuestManager::getspell(uint32 spell_id) {
} }
return nullptr; return nullptr;
} }
std::string QuestManager::getenvironmentaldamagename(uint8 damage_type) {
std::string environmental_damage_name = EQ::constants::GetEnvironmentalDamageName(damage_type);
return environmental_damage_name;
}
+2
View File
@@ -119,6 +119,7 @@ public:
std::string getfactionname(int faction_id); std::string getfactionname(int faction_id);
std::string getlanguagename(int language_id); std::string getlanguagename(int language_id);
std::string getbodytypename(uint32 bodytype_id); std::string getbodytypename(uint32 bodytype_id);
std::string getconsiderlevelname(uint8 consider_level);
void safemove(); void safemove();
void rain(int weather); void rain(int weather);
void snow(int weather); void snow(int weather);
@@ -331,6 +332,7 @@ public:
int getitemstat(uint32 item_id, std::string stat_identifier); int getitemstat(uint32 item_id, std::string stat_identifier);
int getspellstat(uint32 spell_id, std::string stat_identifier, uint8 slot = 0); int getspellstat(uint32 spell_id, std::string stat_identifier, uint8 slot = 0);
const SPDat_Spell_Struct *getspell(uint32 spell_id); const SPDat_Spell_Struct *getspell(uint32 spell_id);
std::string getenvironmentaldamagename(uint8 damage_type);
Client *GetInitiator() const; Client *GetInitiator() const;
NPC *GetNPC() const; NPC *GetNPC() const;
+1 -1
View File
@@ -8248,7 +8248,7 @@ void Mob::SendCastRestrictionMessage(int requirement_id, bool target_requirement
*/ */
std::string msg = ""; const char *msg = "";
if (target_requirement) { if (target_requirement) {
msg = "Your target does not meet the spell requirements. "; msg = "Your target does not meet the spell requirements. ";
+66 -83
View File
@@ -209,6 +209,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
if (item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)) if (item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
{ {
if (!CheckItemRaceClassDietyRestrictionsOnCast(item_slot)) { if (!CheckItemRaceClassDietyRestrictionsOnCast(item_slot)) {
StopCastSpell(spell_id, send_spellbar_enable);
return false; return false;
} }
} }
@@ -696,14 +697,15 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
Always check again on SpellOnTarget to account for AE checks. Always check again on SpellOnTarget to account for AE checks.
*/ */
bool ignore_on_casting = false;
bool ignore_if_npc_or_gm = false; bool ignore_if_npc_or_gm = false;
if (!IsClient() || (IsClient() && CastToClient()->GetGM())) { if (!IsClient() || (IsClient() && CastToClient()->GetGM())) {
ignore_if_npc_or_gm = true; ignore_if_npc_or_gm = true;
} }
if (check_on_casting && !spell_target){ if (check_on_casting){
if (!spell_target) {
if (IsGroupSpell(spell_id) || if (IsGroupSpell(spell_id) ||
spells[spell_id].target_type == ST_AEClientV1 || spells[spell_id].target_type == ST_AEClientV1 ||
spells[spell_id].target_type == ST_AECaster || spells[spell_id].target_type == ST_AECaster ||
@@ -715,6 +717,12 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
spell_target = this; spell_target = this;
} }
} }
else {
if (IsGroupSpell(spell_id) && spell_target != this) {
ignore_on_casting = true;
}
}
}
//If we still do not have a target end. //If we still do not have a target end.
if (!spell_target){ if (!spell_target){
@@ -772,15 +780,22 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
Prevents buff from being cast based on tareget ing PC OR NPC (1 = PCs, 2 = NPCs) Prevents buff from being cast based on tareget ing PC OR NPC (1 = PCs, 2 = NPCs)
These target types skip pcnpc only check (according to dev quotes) These target types skip pcnpc only check (according to dev quotes)
*/ */
if (spells[spell_id].pcnpc_only_flag && spells[spell_id].target_type != ST_AETargetHateList && if (!ignore_on_casting) {
spells[spell_id].target_type != ST_HateList) { if (spells[spell_id].pcnpc_only_flag && spells[spell_id].target_type != ST_AETargetHateList && spells[spell_id].target_type != ST_HateList) {
if (spells[spell_id].pcnpc_only_flag == 1 && !spell_target->IsClient() && !spell_target->IsMerc() && !spell_target->IsBot()) { if (spells[spell_id].pcnpc_only_flag == 1 && !spell_target->IsClient() && !spell_target->IsMerc() && !spell_target->IsBot()) {
if (check_on_casting) {
Message(Chat::SpellFailure, "This spell only works on other PCs");
}
return false; return false;
} }
else if (spells[spell_id].pcnpc_only_flag == 2 && (spell_target->IsClient() || spell_target->IsMerc() || spell_target->IsBot())) { else if (spells[spell_id].pcnpc_only_flag == 2 && (spell_target->IsClient() || spell_target->IsMerc() || spell_target->IsBot())) {
if (check_on_casting) {
Message(Chat::SpellFailure, "This spell only works on NPCs.");
}
return false; return false;
} }
} }
}
/* /*
Cannot cast life tap on self Cannot cast life tap on self
*/ */
@@ -1271,7 +1286,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
item = CastToClient()->GetInv().GetItem(inventory_slot); //checked for in reagents and charges. item = CastToClient()->GetInv().GetItem(inventory_slot); //checked for in reagents and charges.
if (CastToClient()->HasItemRecastTimer(spell_id, inventory_slot)) { if (CastToClient()->HasItemRecastTimer(spell_id, inventory_slot)) {
MessageString(Chat::Red, SPELL_RECAST); MessageString(Chat::Red, SPELL_RECAST);
LogSpells("Casting of [{}] canceled: item spell reuse timer not expired", spell_id); LogSpells("Casting of [{}] canceled: item or augment spell reuse timer not expired", spell_id);
StopCasting(); StopCasting();
return; return;
} }
@@ -1586,7 +1601,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
} }
// we're done casting, now try to apply the spell // we're done casting, now try to apply the spell
if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, true)) if(!SpellFinished(spell_id, spell_target, slot, mana_used, inventory_slot, resist_adjust, false,-1, 0xFFFFFFFF, 0, true))
{ {
LogSpells("Casting of [{}] canceled: SpellFinished returned false", spell_id); LogSpells("Casting of [{}] canceled: SpellFinished returned false", spell_id);
// most of the cases we return false have a message already or are logic errors that shouldn't happen // most of the cases we return false have a message already or are logic errors that shouldn't happen
@@ -2206,7 +2221,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
// if you need to abort the casting, return false // if you need to abort the casting, return false
bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used, bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, uint16 mana_used,
uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override, uint32 inventory_slot, int16 resist_adjust, bool isproc, int level_override,
bool from_casted_spell, uint32 aa_id) uint32 timer, uint32 timer_duration, bool from_casted_spell, uint32 aa_id)
{ {
Mob *ae_center = nullptr; Mob *ae_center = nullptr;
@@ -2580,52 +2595,41 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
if (mgb) { if (mgb) {
SetMGB(false); SetMGB(false);
} }
/*
//all spell triggers use Item slot, but don't have an item associated. We don't need to check recast timers on these. Set Recast Timer on spells.
bool is_triggered_spell = false; */
if (slot == CastingSlot::Item && inventory_slot == 0xFFFFFFFF) { if(IsClient() && !isproc)
is_triggered_spell = true;
}
if (IsClient() && !isproc && !is_triggered_spell)
{ {
//Set Item or Augment Click Recast Timer if (slot == CastingSlot::AltAbility) {
if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) { if (!aa_id) {
CastToClient()->SetItemRecastTimer(spell_id, inventory_slot); aa_id = casting_spell_aa_id;
} }
//Set Discipline Recast Timer if (aa_id) {
else if (slot == CastingSlot::Discipline) { AA::Rank *rank = zone->GetAlternateAdvancementRank(aa_id);
if (spell_id == casting_spell_id || (GetClass() == BARD && spells[spell_id].cast_time == 0 && spell_id != casting_spell_id)) { //handle expendable AA's
CastToClient()->SetDisciplineRecastTimer(spell_id);
}
}
//Set AA Recast Timer.
else if (slot == CastingSlot::AltAbility){
uint32 active_aa_id = 0;
//aa_id is only passed directly into spellfinished when a bard is using AA while casting, this supports casting an AA while clicking an instant AA.
if (GetClass() == BARD && spells[spell_id].cast_time == 0 && aa_id) {
active_aa_id = aa_id;
}
else {
active_aa_id = casting_spell_aa_id;
}
AA::Rank *rank = zone->GetAlternateAdvancementRank(active_aa_id);
CastToClient()->SetAARecastTimer(rank, spell_id);
if (rank && rank->base_ability) { if (rank && rank->base_ability) {
ExpendAlternateAdvancementCharge(rank->base_ability->id); ExpendAlternateAdvancementCharge(rank->base_ability->id);
} }
//set AA recast timer
CastToClient()->SendAlternateAdvancementTimer(rank->spell_type, 0, 0);
}
}
//handle bard AA and Discipline recast timers when singing
if (GetClass() == BARD && spell_id != casting_spell_id && timer != 0xFFFFFFFF) {
CastToClient()->GetPTimers().Start(timer, timer_duration);
LogSpells("Spell [{}]: Setting BARD custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration);
}
//handles AA and Discipline recast timers
else if (spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF)
{
CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration);
LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration);
} }
//Set Custom Recast Timer
else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF) else if(spell_id == casting_spell_id && casting_spell_timer != 0xFFFFFFFF)
{ {
//aa new todo: aa expendable charges here
CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration);
LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration); LogSpells("Spell [{}]: Setting custom reuse timer [{}] to [{}]", spell_id, casting_spell_timer, casting_spell_timer_duration);
} }
//Set Spell Recast Timer
else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) {
int recast = spells[spell_id].recast_time/1000; int recast = spells[spell_id].recast_time/1000;
if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands
@@ -2655,6 +2659,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
} }
} }
} }
/*
Set Recast Timer on item clicks, including augmenets.
*/
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)){
CastToClient()->SetItemRecastTimer(spell_id, inventory_slot);
}
if (IsNPC()) { if (IsNPC()) {
CastToNPC()->AI_Event_SpellCastFinished(true, static_cast<uint16>(slot)); CastToNPC()->AI_Event_SpellCastFinished(true, static_cast<uint16>(slot));
@@ -2715,8 +2725,6 @@ bool Mob::ApplyBardPulse(int32 spell_id, Mob *spell_target, CastingSlot slot) {
if (!SpellFinished(spell_id, spell_target, slot, spells[spell_id].mana, 0xFFFFFFFF, spells[spell_id].resist_difficulty)) { if (!SpellFinished(spell_id, spell_target, slot, spells[spell_id].mana, 0xFFFFFFFF, spells[spell_id].resist_difficulty)) {
return false; return false;
} }
return true;
} }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -3479,10 +3487,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
} }
} }
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {
return false;
}
EQApplicationPacket *action_packet = nullptr, *message_packet = nullptr; EQApplicationPacket *action_packet = nullptr, *message_packet = nullptr;
float spell_effectiveness; float spell_effectiveness;
@@ -3576,6 +3580,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc); mod_spell_cast(spell_id, spelltar, reflect_effectiveness, use_resist_adjust, resist_adjust, isproc);
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {
safe_delete(action_packet);
return false;
}
// now check if the spell is allowed to land // now check if the spell is allowed to land
if (RuleB(Spells, EnableBlockedBuffs)) { if (RuleB(Spells, EnableBlockedBuffs)) {
// We return true here since the caster's client should act like normal // We return true here since the caster's client should act like normal
@@ -6193,13 +6202,19 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot)
} }
if (aug->Click.Effect == spell_id) { if (aug->Click.Effect == spell_id) {
if (aug_i->GetItem() && aug_i->GetItem()->RecastDelay > 0) {
recast_delay = aug_i->GetItem()->RecastDelay; recast_delay = aug_i->GetItem()->RecastDelay;
recast_type = aug_i->GetItem()->RecastType; recast_type = aug_i->GetItem()->RecastType;
}
break; break;
} }
} }
} }
//do not check if item has no recast delay.
if (!recast_delay) {
return false;
}
//if time is not expired, then it exists and therefore we have a recast on this item.
if (!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) { if (!CastToClient()->GetPTimers().Expired(&database, (pTimerItemStart + recast_type), false)) {
return true; return true;
} }
@@ -6207,40 +6222,6 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot)
return false; return false;
} }
void Client::SetDisciplineRecastTimer(int32 spell_id) {
if (!IsValidSpell(spell_id)) {
return;
}
if (spells[spell_id].recast_time == 0) {
return;
}
pTimerType DiscTimer = pTimerDisciplineReuseStart + spells[spell_id].timer_id;
uint32 timer_duration = spells[spell_id].recast_time / 1000;
auto focus = GetFocusEffect(focusReduceRecastTime, spell_id);
if (focus > timer_duration) {
timer_duration = 0;
if (GetPTimers().Enabled((uint32)DiscTimer)) {
GetPTimers().Clear(&database, (uint32)DiscTimer);
}
else {
timer_duration -= focus;
}
}
if (timer_duration <= 0) {
return;
}
CastToClient()->GetPTimers().Start((uint32)DiscTimer, timer_duration);
CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, timer_duration);
LogSpells("Spell [{}]: Setting disciple reuse timer [{}] to [{}]", spell_id, spells[spell_id].timer_id, timer_duration);
}
void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ)
{ {
if (!distance) { return; } if (!distance) { return; }
@@ -6668,4 +6649,6 @@ bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) {
} }
return(false); return(false);
} }
return true;
} }
+1
View File
@@ -469,6 +469,7 @@
#define LEADER_OF_X_GUILD 12258 #define LEADER_OF_X_GUILD 12258
#define NOT_IN_A_GUILD 12259 #define NOT_IN_A_GUILD 12259
#define TARGET_PLAYER_FOR_GUILD_STATUS 12260 #define TARGET_PLAYER_FOR_GUILD_STATUS 12260
#define TARGET_ALREADY_IN_GROUP 12265 //% 1 is already in another group.
#define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite <name> to invite someone to your group. #define GROUP_INVITEE_NOT_FOUND 12268 //You must target a player or use /invite <name> to invite someone to your group.
#define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself. #define GROUP_INVITEE_SELF 12270 //12270 You cannot invite yourself.
#define ALREADY_IN_PARTY 12272 //That person is already in your party. #define ALREADY_IN_PARTY 12272 //That person is already in your party.