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()
{
static const std::map<uint8, std::string> flymode_map = {
{ EQ::constants::GravityBehavior::Ground, "Ground" },
{ EQ::constants::GravityBehavior::Flying, "Flying" },
{ EQ::constants::GravityBehavior::Levitating, "Levitating" },
{ EQ::constants::GravityBehavior::Water, "Water" },
{ EQ::constants::GravityBehavior::Floating, "Floating" },
{ EQ::constants::GravityBehavior::LevitateWhileRunning, "Levitating While Running" },
{ GravityBehavior::Ground, "Ground" },
{ GravityBehavior::Flying, "Flying" },
{ GravityBehavior::Levitating, "Levitating" },
{ GravityBehavior::Water, "Water" },
{ GravityBehavior::Floating, "Floating" },
{ GravityBehavior::LevitateWhileRunning, "Levitating While Running" },
};
return flymode_map;
}
@@ -337,3 +337,48 @@ std::string EQ::constants::GetAccountStatusName(uint8 account_status)
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
};
enum EnvironmentalDamage : uint8 {
Lava = 250,
Drowning,
Falling,
Trap
};
const char *GetStanceName(StanceType stance_type);
int ConvertStanceTypeToIndex(StanceType stance_type);
@@ -248,6 +255,12 @@ namespace EQ
extern const std::map<uint8, std::string>& GetAccountStatusMap();
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_LAST = stanceBurnAE;
const int STANCE_TYPE_COUNT = stanceBurnAE;
@@ -391,4 +404,16 @@ enum AugmentActions : int {
Destroy
};
enum ConsiderLevel : uint8 {
Ally = 1,
Warmly,
Kindly,
Amiably,
Indifferently,
Apprehensively,
Dubiously,
Threateningly,
Scowls
};
#endif /*COMMON_EMU_CONSTANTS_H*/
+1 -1
View File
@@ -2762,7 +2762,7 @@ struct EnvDamage2_Struct {
/*0004*/ uint16 unknown4;
/*0006*/ uint32 damage;
/*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];
/*0027*/ uint16 constant; //Always FFFF
/*0029*/ uint16 unknown29;
+1 -1
View File
@@ -3061,7 +3061,7 @@ struct EnvDamage2_Struct {
/*0006*/ uint32 damage;
/*0010*/ float unknown10; // New to Underfoot - Seen 1
/*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];
/*0031*/ uint16 unknown31; // New to Underfoot - Seen 66
/*0033*/ uint16 constant; // Always FFFF
+1 -1
View File
@@ -3032,7 +3032,7 @@ struct EnvDamage2_Struct {
/*0006*/ uint32 damage;
/*0010*/ float unknown10; // New to Underfoot - Seen 1
/*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];
/*0031*/ uint16 unknown31; // New to Underfoot - Seen 66
/*0033*/ uint16 constant; // Always FFFF
+1 -1
View File
@@ -2539,7 +2539,7 @@ struct EnvDamage2_Struct {
/*0004*/ uint16 unknown4;
/*0006*/ uint32 damage;
/*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];
/*0027*/ uint16 constant; //Always FFFF
/*0029*/ uint16 unknown29;
+1 -1
View File
@@ -2509,7 +2509,7 @@ struct EnvDamage2_Struct {
/*0004*/ uint16 unknown4;
/*0006*/ uint32 damage;
/*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];
/*0027*/ uint16 constant; //Always FFFF
/*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, 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_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(Mercs)
+19 -36
View File
@@ -1239,10 +1239,6 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
if (!IsValidSpell(rank->spell)) {
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)) {
return;
@@ -1257,13 +1253,11 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
uint32 charges = 0;
// We don't have the AA
if (!GetAA(rank_id, &charges)) {
if (!GetAA(rank_id, &charges))
return;
}
//if expendable make sure we have charges
if (ability->charges > 0 && charges < 1) {
if (ability->charges > 0 && charges < 1)
return;
}
//check cooldown
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",
aaremain_min, aaremain_sec);
}
return;
}
if (!IsCastWhileInvis(rank->spell)) {
CommonBreakInvisible();
int timer_duration = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
if (timer_duration < 0) {
timer_duration = 0;
}
if (!IsCastWhileInvis(rank->spell))
CommonBreakInvisible();
if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) {
MessageString(Chat::SpellFailure, SNEAK_RESTRICT);
return;
}
//
// 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();
}
// extra handling for cast_not_standing spells
if (!IgnoreCastingRestriction(rank->spell)) {
if (GetAppearance() == eaSitting) { // we need to stand!
if (GetAppearance() == eaSitting) // we need to stand!
SetAppearance(eaStanding, false);
}
if (GetAppearance() != eaStanding) {
MessageString(Chat::SpellFailure, STAND_TO_CAST);
@@ -1318,33 +1315,20 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
if (!DoCastingChecksOnCaster(rank->spell)) {
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 {
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)) {
return;
}
}
}
}
void Client::SetAARecastTimer(AA::Rank *rank_in, int32 spell_id) {
if (!rank_in) {
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) {
if(!rank_in) {
return 0;
@@ -1376,7 +1360,6 @@ int Mob::GetAlternateAdvancementCooldownReduction(AA::Rank *rank_in) {
}
void Mob::ExpendAlternateAdvancementCharge(uint32 aa_id) {
for (auto &iter : aa_ranks) {
AA::Ability *ability = zone->GetAlternateAdvancementAbility(iter.first);
if (ability && aa_id == ability->id) {
-2
View File
@@ -1495,8 +1495,6 @@ public:
void SendItemRecastTimer(int32 recast_type, uint32 recast_delay = 0);
void SetItemRecastTimer(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 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)
{
if (!ClientFinishedLoading())
{
if (!ClientFinishedLoading()) {
SetHP(GetHP() - 1);
return;
}
@@ -5795,38 +5794,46 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
DumpPacket(app);
return;
}
EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer;
int damage = ed->damage;
if (ed->dmgtype == 252) {
int mod = spellbonuses.ReduceFallDamage + itembonuses.ReduceFallDamage + aabonuses.ReduceFallDamage;
auto damage = ed->damage;
if (ed->dmgtype == EQ::constants::EnvironmentalDamage::Falling) {
uint32 mod = spellbonuses.ReduceFallDamage + itembonuses.ReduceFallDamage + aabonuses.ReduceFallDamage;
damage -= damage * mod / 100;
}
if (damage < 0)
if (damage < 0) {
damage = 31337;
}
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
return;
}
else if (GetInvul()) {
Message(Chat::Red, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype);
} else if (GetInvul()) {
Message(
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
return;
}
else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) {
// Hard coded tutorial and load zones for no fall damage
} else if (zone->GetZoneID() == Zones::TUTORIAL || zone->GetZoneID() == Zones::LOAD) { // Hard coded tutorial and load zones for no fall damage
return;
}
else {
} else {
SetHP(GetHP() - (damage * RuleR(Character, EnvironmentDamageMulipliter)));
/* EVENT_ENVIRONMENTAL_DAMAGE */
int final_damage = (damage * RuleR(Character, EnvironmentDamageMulipliter));
std::string export_string = fmt::format(
"{} {} {}",
@@ -6953,6 +6960,13 @@ void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *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
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("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("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("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) ||
@@ -209,7 +209,7 @@ int command_init(void)
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("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("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) ||
@@ -252,14 +252,14 @@ int command_init(void)
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("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("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("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("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("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) ||
@@ -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("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("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("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) ||
command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) ||
+41 -6
View File
@@ -800,15 +800,50 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
return false;
}
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);
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 (DoCastingChecksOnCaster(spell_id)) {
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);
}
}
else {
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
}
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 {
CastSpell(spell_id, target, EQ::spells::CastingSlot::Discipline);
}
}
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
quest package's XSUBs
@@ -8376,6 +8409,7 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file);
newXS(strcpy(buf, "getclassname"), XS__getclassname, 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, "getcurrencyid"), XS__getcurrencyid, 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, "getgendername"), XS__getgendername, 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, "getguildidbycharid"), XS__getguildidbycharid, file);
newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file);
+242 -118
View File
@@ -2,138 +2,262 @@
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 (target && sep->IsNumber(2)) {
if (target->IsNPC()) {
target->CastToNPC()->SetNPCFactionID(atoi(sep->arg[2]));
}
else {
c->Message(Chat::White, "%s is not an NPC.", target->GetName());
}
}
else {
c->Message(Chat::White, "Usage: (targeted) #ai factionid [factionid]");
}
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
else if (strcasecmp(sep->arg[1], "spellslist") == 0) {
if (target && sep->IsNumber(2) && atoi(sep->arg[2]) >= 0) {
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]");
}
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 (strcasecmp(sep->arg[1], "con") == 0) {
if (target && sep->arg[2][0] != 0) {
Mob *tar2 = entity_list.GetMob(sep->arg[2]);
if (tar2) {
if (is_consider) {
if (arguments == 2) {
auto mob_name = sep->arg[2];
auto mob_to_consider = entity_list.GetMob(mob_name);
if (mob_to_consider) {
auto consider_level = static_cast<uint8>(mob_to_consider->GetReverseFactionCon(target));
c->Message(
Chat::White,
"%s considering %s: %i",
target->GetName(),
tar2->GetName(),
tar2->GetReverseFactionCon(target));
}
else {
c->Message(Chat::White, "Error: %s not found.", sep->arg[2]);
}
}
else {
c->Message(Chat::White, "Usage: (targeted) #ai con [mob name]");
}
}
else if (strcasecmp(sep->arg[1], "guard") == 0) {
if (target && target->IsNPC()) {
target->CastToNPC()->SaveGuardSpot(target->GetPosition());
}
else {
c->Message(
Chat::White,
"Usage: (targeted) #ai guard - sets npc to guard the current location (use #summon to move)"
);
}
}
else if (strcasecmp(sep->arg[1], "roambox") == 0) {
if (target && target->IsAIControlled() && target->IsNPC()) {
if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) &&
sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) {
uint32 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(7)) {
tmp = atoi(sep->arg[7]);
}
if (sep->IsNumber(8)) {
tmp2 = atoi(sep->arg[8]);
}
target->CastToNPC()->AI_SetRoambox(
atof(sep->arg[2]),
atof(sep->arg[3]),
atof(sep->arg[4]),
atof(sep->arg[5]),
atof(sep->arg[6]),
tmp,
tmp2
fmt::format(
"{} ({}) considers {} ({}) as {} ({}).",
target->GetCleanName(),
target->GetID(),
mob_to_consider->GetCleanName(),
mob_to_consider->GetID(),
EQ::constants::GetConsiderLevelName(consider_level),
consider_level
).c_str()
);
}
else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) {
uint32 tmp = 2500;
uint32 tmp2 = 2500;
} else {
c->Message(Chat::White, "Usage: #ai consider [Mob Name] - Show how an NPC considers a mob");
}
} else if (is_faction) {
if (sep->IsNumber(2)) {
auto faction_id = std::stoi(sep->arg[2]);
auto faction_name = content_db.GetFactionName(faction_id);
target->SetNPCFactionID(faction_id);
c->Message(
Chat::White,
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) {
auto target_position = target->GetPosition();
target->SaveGuardSpot(target_position);
c->Message(
Chat::White,
fmt::format(
"{} ({}) now has a guard spot of {:.2f}, {:.2f}, {:.2f} with a heading of {:.2f}.",
target->GetCleanName(),
target->GetID(),
target_position.x,
target_position.y,
target_position.z,
target_position.w
).c_str()
);
} else if (is_roambox) {
if (target->IsAIControlled()) {
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]);
}
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)) {
tmp = atoi(sep->arg[4]);
delay = std::stoul(sep->arg[4]);
}
if (sep->IsNumber(5)) {
tmp2 = atoi(sep->arg[5]);
minimum_delay = std::stoul(sep->arg[5]);
}
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2);
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 {
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 must target an NPC with AI.");
}
} else if (is_spells) {
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, "You need a AI NPC targeted");
}
}
else if (strcasecmp(sep->arg[1], "stop") == 0 && c->Admin() >= commandToggleAI) {
if (target) {
if (target->IsAIControlled()) {
target->AI_Stop();
}
else {
c->Message(Chat::White, "Error: Target is not AI controlled");
}
}
else {
c->Message(Chat::White, "Usage: Target a Mob with AI enabled and use this to turn off their AI.");
}
}
else if (strcasecmp(sep->arg[1], "start") == 0 && c->Admin() >= commandToggleAI) {
if (target) {
if (!target->IsAIControlled()) {
target->AI_Start();
}
else {
c->Message(Chat::White, "Error: Target is already AI controlled");
}
}
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");
}
}
+257 -124
View File
@@ -2,156 +2,289 @@
void command_flagedit(Client *c, const Seperator *sep)
{
//super-command for editing zone flags
if (sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) {
c->Message(Chat::White, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take].");
int arguments = sep->argnum;
if (!arguments) {
auto flags_link = EQ::SayLinkEngine::GenerateQuestSaylink("#flags", false, "#flags");
c->Message(
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(
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,
"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()
);
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]);
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();
}
}
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)
target->SetZoneFlag(zone_id);
c->Message(
Chat::White,
"Zone %s (%s,%s) version %s requires key %s",
row[2],
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[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;
} else if (is_lock) {
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) {
if (arguments < 3) {
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"
);
return;
}
std::string flag_name = EscapeString(sep->argplus[3]);
std::string zone_long_name = ZoneLongName(zone_id);
auto query = fmt::format(
SQL(
UPDATE zone
SET flag_needed = '{}'
WHERE zoneidnumber = {} AND version = {}
),
flag_name,
zone_id,
zone->GetInstanceVersion()
);
return;
}
if (!strcasecmp(sep->arg[1], "give")) {
uint32 zoneid = 0;
if (sep->arg[2][0] != '\0') {
zoneid = atoi(sep->arg[2]);
if (zoneid < 1) {
zoneid = ZoneID(sep->arg[2]);
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;
}
}
if (zoneid < 1) {
c->Message(Chat::Red, "zone required. see help.");
c->Message(
Chat::White,
fmt::format(
"{} ({}) now requires a flag, named {}.",
zone_long_name,
zone_id,
flag_name
).c_str()
);
return;
}
Mob *t = c->GetTarget();
if (t == nullptr || !t->IsClient()) {
c->Message(Chat::Red, "client target required");
return;
}
t->CastToClient()->SetZoneFlag(zoneid);
return;
}
if (!strcasecmp(sep->arg[1], "give")) {
uint32 zoneid = 0;
if (sep->arg[2][0] != '\0') {
zoneid = atoi(sep->arg[2]);
if (zoneid < 1) {
zoneid = ZoneID(sep->arg[2]);
} 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();
}
}
if (zoneid < 1) {
c->Message(Chat::Red, "zone required. see help.");
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;
}
Mob *t = c->GetTarget();
if (t == nullptr || !t->IsClient()) {
c->Message(Chat::Red, "client target required");
c->Message(
Chat::White,
fmt::format(
"{} ({}) no longer requires a flag.",
zone_long_name,
zone_id
).c_str()
);
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)
{
Mob *target = c->GetTarget();
if (target == nullptr) {
c->Message(Chat::White, "Error: you must have a target.");
if (!c->GetTarget() || !c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
c->Message(Chat::White, "Display hate list for %s..", target->GetName());
auto target = c->GetTarget();
target->PrintHateListToClient(c);
}
+182 -81
View File
@@ -5,97 +5,198 @@ extern WorldServer worldserver;
void command_logs(Client *c, const Seperator *sep)
{
int logs_set = 0;
if (sep->argnum > 0) {
/* #logs reload_all */
if (strcasecmp(sep->arg[1], "reload_all") == 0) {
auto pack = new ServerPacket(ServerOP_ReloadLogs, 0);
worldserver.SendPacket(pack);
c->Message(
Chat::Red,
"Successfully sent the packet to world to reload log settings from the database for all zones"
);
safe_delete(pack);
}
/* #logs list_settings */
if (strcasecmp(sep->arg[1], "list_settings") == 0 ||
(strcasecmp(sep->arg[1], "set") == 0 && strcasecmp(sep->arg[3], "") == 0)) {
c->Message(Chat::White, "[Category ID | console | file | gmsay | Category Description]");
int redisplay_columns = 0;
for (int i = 0; i < Logs::LogCategory::MaxCategoryID; i++) {
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(
Chat::White,
"--- #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");
}
if (logs_set == 1) {
c->Message(Chat::Yellow, "Your Log Settings have been applied");
c->Message(
Chat::Yellow,
"Output Method: %s :: Debug Level: %i - Category: %s",
sep->arg[2],
atoi(sep->arg[4]),
Logs::LogCategoryName[atoi(sep->arg[3])]
);
}
/* 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
*/
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;
}
}
int arguments = sep->argnum;
if (!arguments) {
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;
}
else {
c->Message(Chat::White, "#logs usage:");
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 reload_all - Reload all settings in world and all zone processes with what is defined in the database"
"#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_settings - Shows current log settings and categories loaded into the current process' memory"
"#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 set [console|file|gmsay] <category_id> <debug_level (1-3)> - Sets log settings during the lifetime of the zone"
"#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);
worldserver.SendPacket(pack);
c->Message(
Chat::White,
"Reloaded log settings worldwide."
);
safe_delete(pack);
} else if (is_set) {
auto logs_set = false;
bool is_console = !strcasecmp(sep->arg[2], "console");
bool is_file = !strcasecmp(sep->arg[2], "file");
bool is_gmsay = !strcasecmp(sep->arg[2], "gmsay");
if (!is_console && !is_file && !is_gmsay) {
c->Message(
Chat::White,
"#logs set [console|file|gmsay] [Category ID] [Debug Level (1-3)] - Sets log settings during the lifetime of the zone"
);
c->Message(Chat::White, "Example: #logs set gmsay 20 1 - Would output Quest errors to gmsay");
return;
}
logs_set = true;
auto category_id = std::stoul(sep->arg[3]);
auto setting = std::stoul(sep->arg[4]);
if (is_console) {
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()
);
}
LogSys.log_settings[category_id].is_category_enabled = setting ? 1 : 0;
}
}
+31 -19
View File
@@ -2,29 +2,41 @@
void command_name(Client *c, const Seperator *sep)
{
Client *target;
if ((strlen(sep->arg[1]) == 0) || (!(c->GetTarget() && c->GetTarget()->IsClient()))) {
c->Message(Chat::White, "Usage: #name newname (requires player target)");
int arguments = sep->argnum;
if (!arguments) {
c->Message(Chat::White, "Usage: #name [New Name] - Rename your player target");
return;
}
else {
target = c->GetTarget()->CastToClient();
char *oldname = strdup(target->GetName());
if (target->ChangeFirstName(sep->arg[1], c->GetName())) {
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 {
if (c->GetTarget() && c->GetTarget()->IsClient()) {
auto target = c->GetTarget()->CastToClient();
std::string new_name = sep->arg[1];
std::string old_name = target->GetCleanName();
if (target->ChangeFirstName(new_name.c_str(), c->GetCleanName())) {
c->Message(
Chat::Red,
"ERROR: Unable to rename %s. Check that the new name '%s' isn't already taken.",
oldname,
sep->arg[2]
Chat::White,
fmt::format(
"Successfully renamed {} to {}",
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);
}
}
+161 -133
View File
@@ -2,140 +2,168 @@
void command_netstats(Client *c, const Seperator *sep)
{
if (c) {
auto client = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
client = c->GetTarget()->CastToClient();
}
bool is_full = !strcasecmp(sep->arg[1], "full");
bool is_reset = !strcasecmp(sep->arg[1], "reset");
if (strcasecmp(sep->arg[1], "reset") == 0) {
auto connection = c->Connection();
c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset).");
connection->ResetStats();
return;
}
auto connection = c->Connection();
auto opts = connection->GetManager()->GetOptions();
auto eqs_stats = connection->GetStats();
auto &stats = eqs_stats.DaybreakStats;
auto now = EQ::Net::Clock::now();
auto sec_since_stats_reset = std::chrono::duration_cast<std::chrono::duration<double>>(
now - stats.created
).count();
c->Message(Chat::White, "Netstats:");
c->Message(Chat::White, "--------------------------------------------------------------------");
c->Message(
Chat::White,
"Sent Bytes: %u (%.2f/sec)",
stats.sent_bytes,
stats.sent_bytes / sec_since_stats_reset
);
c->Message(
Chat::White,
"Recv Bytes: %u (%.2f/sec)",
stats.recv_bytes,
stats.recv_bytes / sec_since_stats_reset
);
c->Message(
Chat::White, "Bytes Before Encode (Sent): %u, Compression Rate: %.2f%%", stats.bytes_before_encode,
static_cast<double>(stats.bytes_before_encode - stats.sent_bytes) /
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,
static_cast<double>(stats.bytes_after_decode - stats.recv_bytes) /
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);
c->Message(Chat::White, "Last Ping: %u", stats.last_ping);
c->Message(Chat::White, "Average Ping: %u", stats.avg_ping);
c->Message(Chat::White, "--------------------------------------------------------------------");
c->Message(
Chat::White,
"(Realtime) Recv Packets: %u (%.2f/sec)",
stats.recv_packets,
stats.recv_packets / sec_since_stats_reset
);
c->Message(
Chat::White,
"(Realtime) Sent Packets: %u (%.2f/sec)",
stats.sent_packets,
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);
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);
c->Message(
Chat::White,
"Packet Loss In: %.2f%%",
100.0 * (1.0 - static_cast<double>(stats.sync_recv_packets) /
static_cast<double>(stats.sync_remote_sent_packets)));
c->Message(
Chat::White,
"Packet Loss Out: %.2f%%",
100.0 * (1.0 - static_cast<double>(stats.sync_remote_recv_packets) /
static_cast<double>(stats.sync_sent_packets)));
c->Message(Chat::White, "--------------------------------------------------------------------");
c->Message(
Chat::White,
"Resent Packets: %u (%.2f/sec)",
stats.resent_packets,
stats.resent_packets / sec_since_stats_reset
);
c->Message(
Chat::White,
"Resent Fragments: %u (%.2f/sec)",
stats.resent_fragments,
stats.resent_fragments / sec_since_stats_reset
);
c->Message(
Chat::White,
"Resent Non-Fragments: %u (%.2f/sec)",
stats.resent_full,
stats.resent_full / sec_since_stats_reset
);
c->Message(
Chat::White,
"Dropped Datarate Packets: %u (%.2f/sec)",
stats.dropped_datarate_packets,
stats.dropped_datarate_packets / sec_since_stats_reset
);
if (opts.daybreak_options.outgoing_data_rate > 0.0) {
c->Message(
Chat::White,
"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)),
opts.daybreak_options.outgoing_data_rate
);
}
if (strcasecmp(sep->arg[1], "full") == 0) {
c->Message(Chat::White, "--------------------------------------------------------------------");
c->Message(Chat::White, "Sent Packet Types");
for (auto i = 0; i < _maxEmuOpcode; ++i) {
auto cnt = eqs_stats.SentCount[i];
if (cnt > 0) {
c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset);
}
}
c->Message(Chat::White, "--------------------------------------------------------------------");
c->Message(Chat::White, "Recv Packet Types");
for (auto i = 0; i < _maxEmuOpcode; ++i) {
auto cnt = eqs_stats.RecvCount[i];
if (cnt > 0) {
c->Message(Chat::White, "%s: %u (%.2f / sec)", OpcodeNames[i], cnt, cnt / sec_since_stats_reset);
}
}
}
c->Message(Chat::White, "--------------------------------------------------------------------");
if (is_reset) {
auto connection = c->Connection();
c->Message(Chat::White, "Resetting client stats (packet loss will not read correctly after reset).");
connection->ResetStats();
return;
}
auto connection = c->Connection();
auto opts = connection->GetManager()->GetOptions();
auto eqs_stats = connection->GetStats();
auto &stats = eqs_stats.DaybreakStats;
auto now = EQ::Net::Clock::now();
auto sec_since_stats_reset = std::chrono::duration_cast<std::chrono::duration<double>>(
now - stats.created
).count();
std::string popup_text = "<table>";
popup_text += fmt::format(
"<tr><td>Sent Bytes</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.sent_bytes,
stats.sent_bytes / sec_since_stats_reset
);
popup_text += fmt::format(
"<tr><td>Received Bytes</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.recv_bytes,
stats.recv_bytes / sec_since_stats_reset
);
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) * 100.0
);
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) * 100.0
);
popup_text += "<br><br>";
popup_text += fmt::format("<tr><td>Min Ping</td><td>{}</td></tr>", stats.min_ping);
popup_text += fmt::format("<tr><td>Max Ping</td><td>{}</td></tr>", stats.max_ping);
popup_text += fmt::format("<tr><td>Last Ping</td><td>{}</td></tr>", stats.last_ping);
popup_text += fmt::format("<tr><td>Average Ping</td><td>{}</td></tr>", stats.avg_ping);
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 / sec_since_stats_reset
);
popup_text += fmt::format(
"<tr><td>(Realtime) Sent Packets</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.sent_packets,
stats.sent_packets / sec_since_stats_reset
);
popup_text += "<br><br>";
popup_text += fmt::format("<tr><td>(Sync) Received Packets</td><td>{}</td></tr>", stats.sync_recv_packets);
popup_text += fmt::format("<tr><td>(Sync) Sent Packets</td><td>{}</td></tr>", stats.sync_sent_packets);
popup_text += fmt::format("<tr><td>(Sync) Remote Received Packets</td><td>{}</td></tr>", stats.sync_remote_recv_packets);
popup_text += fmt::format("<tr><td>(Sync) Remote Sent Packets</td><td>{}</td></tr>", stats.sync_remote_sent_packets);
popup_text += "<br><br>";
popup_text += fmt::format(
"<tr><td>Packet Loss In</td><td>{:.2f}%%</td></tr>",
(100.0 * (1.0 - static_cast<double>(stats.sync_recv_packets) / static_cast<double>(stats.sync_remote_sent_packets)))
);
popup_text += fmt::format(
"<tr><td>Packet Loss Out</td><td>{:.2f}%%</td></tr>",
(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 / sec_since_stats_reset
);
popup_text += fmt::format(
"<tr><td>Resent Fragments</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.resent_fragments,
stats.resent_fragments / sec_since_stats_reset
);
popup_text += fmt::format(
"<tr><td>Resent Non-Fragments</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.resent_full,
stats.resent_full / sec_since_stats_reset
);
popup_text += "<br><br>";
popup_text += fmt::format(
"<tr><td>Dropped Datarate Packets</td><td>{} ({:.2f} Per Second)</td></tr>",
stats.dropped_datarate_packets,
stats.dropped_datarate_packets / sec_since_stats_reset
);
if (opts.daybreak_options.outgoing_data_rate > 0.0) {
popup_text += fmt::format(
"<tr><td>Outgoing Link Saturation</td><td>{:.2f}%% ({:.2f}kb Per Second)</td></tr>",
(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
);
}
if (is_full) {
popup_text += "<br><br>";
popup_text += "<tr><td>Sent Packet Types</td></tr>";
for (auto i = 0; i < _maxEmuOpcode; ++i) {
auto cnt = eqs_stats.SentCount[i];
if (cnt > 0) {
popup_text += fmt::format(
"<tr><td>{}</td><td>{} ({:.2f} Per Second)</td></tr>",
OpcodeNames[i],
cnt,
cnt / sec_since_stats_reset
);
}
}
popup_text += "<br><br>";
popup_text += "<tr><td>Received Packet Types</td></tr>";
for (auto i = 0; i < _maxEmuOpcode; ++i) {
auto cnt = eqs_stats.RecvCount[i];
if (cnt > 0) {
popup_text += fmt::format(
"<tr><td>{}</td><td>{} ({:.2f} Per Second)</td></tr>",
OpcodeNames[i],
cnt,
cnt / sec_since_stats_reset
);
}
}
}
popup_text += "</table>";
c->SendPopupToClient(
"Network Statistics",
popup_text.c_str()
);
}
+227 -82
View File
@@ -1,104 +1,249 @@
#include "../client.h"
#include "../corpse.h"
#include "../../common/data_verification.h"
void command_npcloot(Client *c, const Seperator *sep)
{
if (c->GetTarget() == 0) {
c->Message(Chat::White, "Error: No target");
// #npcloot show
if (!c->GetTarget() || (!c->GetTarget()->IsNPC() && !c->GetTarget()->IsCorpse())) {
c->Message(Chat::White, "You must target an NPC or a Corpse to use this command.");
return;
}
else if (strcasecmp(sep->arg[1], "show") == 0) {
if (c->GetTarget()->IsNPC()) {
c->GetTarget()->CastToNPC()->QueryLoot(c);
}
else if (c->GetTarget()->IsCorpse()) {
c->GetTarget()->CastToCorpse()->QueryLoot(c);
}
else {
c->Message(Chat::White, "Error: Target's type doesnt have loot");
}
int arguments = sep->argnum;
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;
}
// These 2 types are *BAD* for the next few commands
else if (c->GetTarget()->IsClient() || c->GetTarget()->IsCorpse()) {
c->Message(Chat::White, "Error: Invalid target type, try a NPC =).");
// #npcloot add
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 if (strcasecmp(sep->arg[1], "add") == 0) {
// #npcloot add item
if (c->GetTarget()->IsNPC() && sep->IsNumber(2)) {
uint32 item = atoi(sep->arg[2]);
if (database.GetItem(item)) {
if (sep->arg[3][0] != 0 && sep->IsNumber(3)) {
c->GetTarget()->CastToNPC()->AddItem(item, atoi(sep->arg[3]), 0);
}
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);
}
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;
}
else if (!sep->IsNumber(2)) {
c->Message(Chat::White, "Error: #npcloot add: Itemid must be a number.");
auto item_id = std::stoul(sep->arg[2]);
auto item_charges = sep->IsNumber(3) ? static_cast<uint16>(std::stoul(sep->arg[3])) : 1;
bool equip_item = arguments >= 4 ? atobool(sep->arg[4]) : false;
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;
auto augment_three_id = sep->IsNumber(7) ? std::stoul(sep->arg[7]) : 0;
auto augment_four_id = sep->IsNumber(8) ? std::stoul(sep->arg[8]) : 0;
auto augment_five_id = sep->IsNumber(9) ? std::stoul(sep->arg[9]) : 0;
auto augment_six_id = sep->IsNumber(10) ? std::stoul(sep->arg[10]) : 0;
auto item_data = database.GetItem(item_id);
if (!item_data) {
c->Message(
Chat::White,
fmt::format(
"Item ID {} could not be found",
item_id
).c_str()
);
return;
}
else {
c->Message(Chat::White, "Error: #npcloot add: This is not a valid target.");
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;
}
}
// #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
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 (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.");
}
} else if (is_remove) {
if (!c->GetTarget()->IsNPC()) {
c->Message(Chat::White, "You must target an NPC to use this command.");
return;
}
}
// #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]));
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,
"Set %i Platinum, %i Gold, %i Silver, and %i Copper as %s's money.",
atoi(sep->arg[2]),
atoi(sep->arg[3]),
atoi(sep->arg[4]),
atoi(sep->arg[5]),
c->GetTarget()->GetName());
fmt::format(
"Removed {} {} ({}) from {} ({}).",
item_count,
database.CreateItemLink(item_id),
item_id,
target->GetCleanName(),
target->GetID()
).c_str()
);
total_item_count += item_count;
}
else {
c->Message(Chat::White, "Error: #npcloot money: Values must be between 0-34465.");
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 {
c->Message(Chat::White, "Usage: #npcloot money platinum gold silver copper");
} else if (is_show) {
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)
{
if (!c->GetTarget() || !c->GetTarget()->IsClient()) {
c->Message(Chat::White, "Need a player target for timers.");
return;
auto target = c;
if (c->GetTarget() && c->GetTarget()->IsClient()) {
target = c->GetTarget()->CastToClient();
}
Client *them = c->GetTarget()->CastToClient();
std::vector<std::pair<pTimerType, PersistentTimer *> > res;
them->GetPTimers().ToVector(res);
std::vector<std::pair<pTimerType, PersistentTimer *>> timers;
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;
int l = res.size();
for (r = 0; r < l; r++) {
c->Message(Chat::White, "Timer %d: %d seconds remain.", res[r].first, res[r].second->GetRemainingTime());
std::string popup_text = "<table>";
popup_text += "<tr><td>Timer ID</td><td>Remaining</td></tr>";
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)
{
auto target = c;
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()
);
}
+2 -5
View File
@@ -2,9 +2,6 @@
void command_undyeme(Client *c, const Seperator *sep)
{
if (c) {
c->Undye();
c->Message(Chat::Red, "Dye removed from all slots. Please zone for the process to complete.");
}
c->Undye();
c->Message(Chat::White, "Undyed armor for yourself.");
}
+12 -4
View File
@@ -2,9 +2,17 @@
void command_version(Client *c, const Seperator *sep)
{
c->Message(Chat::White, "Current version information.");
c->Message(Chat::White, " %s", CURRENT_VERSION);
c->Message(Chat::White, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME);
c->Message(Chat::White, " Last modified on: %s", LAST_MODIFIED);
std::string popup_text = "<table>";
popup_text += fmt::format("<tr><td>Version</td><td>{}</td></tr>", CURRENT_VERSION);
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)
{
auto iterator = list.begin();
while (iterator != list.end())
{
struct_HateList *e = (*iterator);
c->Message(Chat::White, "- name: %s, damage: %d, hate: %d",
(e->entity_on_hatelist && e->entity_on_hatelist->GetName()) ? e->entity_on_hatelist->GetName() : "(null)",
e->hatelist_damage, e->stored_hate_amount);
if (list.size()) {
c->Message(
Chat::White,
fmt::format(
"Displaying hate list for {} ({}).",
hate_owner->GetCleanName(),
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);
}
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 { \
cur = table[#name]; \
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_language_name", &lua_get_language_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
+1 -1
View File
@@ -326,7 +326,7 @@ public:
void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQ::spells::CastingSlot slot, uint16 mana_used,
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,
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);
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);
+9
View File
@@ -1052,6 +1052,10 @@ std::string QuestManager::getbodytypename(uint32 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() {
QuestManagerCurrentQuestVars();
if (initiator && initiator->IsClient())
@@ -3698,3 +3702,8 @@ const SPDat_Spell_Struct* QuestManager::getspell(uint32 spell_id) {
}
return nullptr;
}
std::string QuestManager::getenvironmentaldamagename(uint8 damage_type) {
std::string environmental_damage_name = EQ::constants::GetEnvironmentalDamageName(damage_type);
return environmental_damage_name;
}
+3 -1
View File
@@ -119,6 +119,7 @@ public:
std::string getfactionname(int faction_id);
std::string getlanguagename(int language_id);
std::string getbodytypename(uint32 bodytype_id);
std::string getconsiderlevelname(uint8 consider_level);
void safemove();
void rain(int weather);
void snow(int weather);
@@ -330,7 +331,8 @@ public:
std::string getinventoryslotname(int16 slot_id);
int getitemstat(uint32 item_id, std::string stat_identifier);
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;
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) {
msg = "Your target does not meet the spell requirements. ";
+87 -104
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 (!CheckItemRaceClassDietyRestrictionsOnCast(item_slot)) {
StopCastSpell(spell_id, send_spellbar_enable);
return false;
}
}
@@ -696,23 +697,30 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp
Always check again on SpellOnTarget to account for AE checks.
*/
bool ignore_on_casting = false;
bool ignore_if_npc_or_gm = false;
if (!IsClient() || (IsClient() && CastToClient()->GetGM())) {
ignore_if_npc_or_gm = true;
}
if (check_on_casting && !spell_target){
if (check_on_casting){
if (IsGroupSpell(spell_id) ||
spells[spell_id].target_type == ST_AEClientV1 ||
spells[spell_id].target_type == ST_AECaster ||
spells[spell_id].target_type == ST_Ring ||
spells[spell_id].target_type == ST_Beam){
return true;
if (!spell_target) {
if (IsGroupSpell(spell_id) ||
spells[spell_id].target_type == ST_AEClientV1 ||
spells[spell_id].target_type == ST_AECaster ||
spells[spell_id].target_type == ST_Ring ||
spells[spell_id].target_type == ST_Beam) {
return true;
}
else if (spells[spell_id].target_type == ST_Self) {
spell_target = this;
}
}
else if (spells[spell_id].target_type == ST_Self) {
spell_target = this;
else {
if (IsGroupSpell(spell_id) && spell_target != this) {
ignore_on_casting = true;
}
}
}
@@ -772,13 +780,20 @@ 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)
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 &&
spells[spell_id].target_type != ST_HateList) {
if (spells[spell_id].pcnpc_only_flag == 1 && !spell_target->IsClient() && !spell_target->IsMerc() && !spell_target->IsBot()) {
return false;
}
else if (spells[spell_id].pcnpc_only_flag == 2 && (spell_target->IsClient() || spell_target->IsMerc() || spell_target->IsBot())) {
return false;
if (!ignore_on_casting) {
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 (check_on_casting) {
Message(Chat::SpellFailure, "This spell only works on other PCs");
}
return false;
}
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;
}
}
}
/*
@@ -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.
if (CastToClient()->HasItemRecastTimer(spell_id, inventory_slot)) {
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();
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
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);
// 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
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,
bool from_casted_spell, uint32 aa_id)
uint32 timer, uint32 timer_duration, bool from_casted_spell, uint32 aa_id)
{
Mob *ae_center = nullptr;
@@ -2580,54 +2595,43 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
if (mgb) {
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.
bool is_triggered_spell = false;
if (slot == CastingSlot::Item && inventory_slot == 0xFFFFFFFF) {
is_triggered_spell = true;
}
if (IsClient() && !isproc && !is_triggered_spell)
/*
Set Recast Timer on spells.
*/
if(IsClient() && !isproc)
{
//Set Item or Augment Click Recast Timer
if (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt) {
CastToClient()->SetItemRecastTimer(spell_id, inventory_slot);
}
//Set Discipline Recast Timer
else if (slot == CastingSlot::Discipline) {
if (spell_id == casting_spell_id || (GetClass() == BARD && spells[spell_id].cast_time == 0 && spell_id != casting_spell_id)) {
CastToClient()->SetDisciplineRecastTimer(spell_id);
if (slot == CastingSlot::AltAbility) {
if (!aa_id) {
aa_id = casting_spell_aa_id;
}
if (aa_id) {
AA::Rank *rank = zone->GetAlternateAdvancementRank(aa_id);
//handle expendable AA's
if (rank && rank->base_ability) {
ExpendAlternateAdvancementCharge(rank->base_ability->id);
}
//set AA recast timer
CastToClient()->SendAlternateAdvancementTimer(rank->spell_type, 0, 0);
}
}
//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) {
ExpendAlternateAdvancementCharge(rank->base_ability->id);
}
//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);
}
//Set Custom Recast Timer
//handles AA and Discipline recast timers
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);
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) {
int recast = spells[spell_id].recast_time / 1000;
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);
}
else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) {
int recast = spells[spell_id].recast_time/1000;
if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands
{
recast -= GetAA(aaFervrentBlessing) * 420;
@@ -2647,14 +2651,20 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
}
recast = std::max(recast, 0);
}
LogSpells("Spell [{}]: Setting long reuse timer to [{}] s (orig [{}])", spell_id, recast, spells[spell_id].recast_time);
if (recast > 0) {
CastToClient()->GetPTimers().Start(pTimerSpellStart + spell_id, recast);
}
}
}
/*
Set Recast Timer on item clicks, including augmenets.
*/
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)){
CastToClient()->SetItemRecastTimer(spell_id, inventory_slot);
}
if (IsNPC()) {
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)) {
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;
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);
if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) {
safe_delete(action_packet);
return false;
}
// now check if the spell is allowed to land
if (RuleB(Spells, EnableBlockedBuffs)) {
// 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) {
recast_delay = aug_i->GetItem()->RecastDelay;
recast_type = aug_i->GetItem()->RecastType;
if (aug_i->GetItem() && aug_i->GetItem()->RecastDelay > 0) {
recast_delay = aug_i->GetItem()->RecastDelay;
recast_type = aug_i->GetItem()->RecastType;
}
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)) {
return true;
}
@@ -6207,40 +6222,6 @@ bool Client::HasItemRecastTimer(int32 spell_id, uint32 inventory_slot)
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)
{
if (!distance) { return; }
@@ -6668,4 +6649,6 @@ bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) {
}
return(false);
}
return true;
}
+1
View File
@@ -469,6 +469,7 @@
#define LEADER_OF_X_GUILD 12258
#define NOT_IN_A_GUILD 12259
#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_SELF 12270 //12270 You cannot invite yourself.
#define ALREADY_IN_PARTY 12272 //That person is already in your party.