mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-24 05:52:28 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 296d48993e | |||
| 1d4438ae1f | |||
| 03adf20fe9 | |||
| dd6cde68bb | |||
| 95c8b60ec6 | |||
| fa418172d6 | |||
| 7b106fc622 | |||
| 62a453f0d7 | |||
| fa9314811e | |||
| d2d7b8108d | |||
| 99793cab8b | |||
| e2484997dd | |||
| 66935fe21b | |||
| db988e4261 | |||
| fdd260d5fa | |||
| 9110bc863e | |||
| 4a41583805 | |||
| 5396c0c88b | |||
| 968ae26c99 | |||
| 7bf466cf3f | |||
| 49d7eb1402 | |||
| d83ced6f76 | |||
| 1ea8888607 | |||
| f9eb4603a3 | |||
| d656be6be4 | |||
| fbbbd3b09d | |||
| a6d1652f44 | |||
| f0bf285836 | |||
| 1f560529da | |||
| f65a6d2761 |
@@ -476,7 +476,7 @@ bool Database::CheckDatabaseConversions() {
|
||||
CheckDatabaseConvertCorpseDeblob();
|
||||
|
||||
/* Run EQEmu Server script (Checks for database updates) */
|
||||
if(system("perl eqemu_server.pl ran_from_world"));
|
||||
system("perl eqemu_server.pl ran_from_world");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -561,8 +561,9 @@ std::string DynamicZoneBase::GetDynamicZoneTypeName(DynamicZoneType dz_type)
|
||||
return "Mission";
|
||||
case DynamicZoneType::Quest:
|
||||
return "Quest";
|
||||
default:
|
||||
return "Unknown";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
EQ::Net::DynamicPacket DynamicZoneBase::GetSerializedDzPacket()
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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*/
|
||||
|
||||
@@ -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
@@ -194,7 +194,7 @@ void MD5::Final(uint8 digest[16], MD5Context *ctx) {
|
||||
|
||||
/* The heart of the MD5 algorithm. */
|
||||
void MD5::Transform(uint32 hash[4], const uint32 input[16]) {
|
||||
register uint32 a = hash[0], b = hash[1], c = hash[2], d = hash[3];
|
||||
uint32 a = hash[0], b = hash[1], c = hash[2], d = hash[3];
|
||||
|
||||
MD5STEP(F1, a, b, c, d, input[ 0]+0xd76aa478, 7);
|
||||
MD5STEP(F1, d, a, b, c, input[ 1]+0xe8c7b756, 12);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -221,9 +221,6 @@ XS(XS_EQDB_escape_string)
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
XS(boot_EQDB); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(boot_EQDB)
|
||||
{
|
||||
|
||||
@@ -260,9 +260,6 @@ XS(XS_EQDBRes_fetch_lengths)
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
XS(boot_EQDBRes); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(boot_EQDBRes)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
@@ -410,6 +411,7 @@ RULE_INT(Spells, ClericInnateHealFocus, 5, "Clerics on live get a 5 pct innate h
|
||||
RULE_BOOL(Spells, DOTsScaleWithSpellDmg, false, "Allow SpellDmg stat to affect DoT spells")
|
||||
RULE_BOOL(Spells, HOTsScaleWithHealAmt, false, "Allow HealAmt stat to affect HoT spells")
|
||||
RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate damage bonuses and then heal bonuses. False: Lifetaps heal using the amount damaged to mob.")
|
||||
RULE_BOOL(Spells, UseFadingMemoriesMaxLevel, false, "Enables to limit field in spell data to set the max level that over which an NPC will ignore fading memories effect and not lose aggro.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
|
||||
+2
-2
@@ -916,8 +916,8 @@ typedef enum {
|
||||
#define SE_EndurancePool 190 // implemented
|
||||
#define SE_Amnesia 191 // implemented - Silence vs Melee Effect
|
||||
#define SE_Hate 192 // implemented - Instant and hate over time.
|
||||
#define SE_SkillAttack 193 // implemented
|
||||
#define SE_FadingMemories 194 // implemented
|
||||
#define SE_SkillAttack 193 // implemented,
|
||||
#define SE_FadingMemories 194 // implemented, @Aggro, Remove from hate lists and make invisible. Can set max level of NPCs that can be affected. base: success chance, limit: max level (ROF2), max: max level (modern client), Note: Support for max level requires Rule (Spells, UseFadingMemoriesMaxLevel) to be true. If used from limit field, then it set as the level, ie. max level of 75 would use limit value of 75. If set from max field, max level 75 would use max value of 1075, if you want to set it so it checks a level range above the spell target then for it to only work on mobs 5 levels or below you set max value to 5.
|
||||
#define SE_StunResist 195 // implemented
|
||||
#define SE_StrikeThrough 196 // implemented
|
||||
#define SE_SkillDamageTaken 197 // implemented
|
||||
|
||||
+6
-2
@@ -80,7 +80,6 @@ SET(zone_sources
|
||||
fearpath.cpp
|
||||
forage.cpp
|
||||
global_loot_manager.cpp
|
||||
gm_commands/door_manipulation.cpp
|
||||
groups.cpp
|
||||
guild.cpp
|
||||
guild_mgr.cpp
|
||||
@@ -200,7 +199,6 @@ SET(zone_headers
|
||||
fastmath.h
|
||||
forage.h
|
||||
global_loot_manager.h
|
||||
gm_commands/door_manipulation.h
|
||||
groups.h
|
||||
guild_mgr.h
|
||||
hate_list.h
|
||||
@@ -327,6 +325,8 @@ SET(gm_commands
|
||||
gm_commands/distance.cpp
|
||||
gm_commands/doanim.cpp
|
||||
gm_commands/door.cpp
|
||||
gm_commands/door_manipulation.cpp
|
||||
gm_commands/door_manipulation.h
|
||||
gm_commands/dye.cpp
|
||||
gm_commands/dz.cpp
|
||||
gm_commands/dzkickplayers.cpp
|
||||
@@ -580,6 +580,10 @@ SET(gm_commands
|
||||
gm_commands/zunderworld.cpp
|
||||
)
|
||||
|
||||
SOURCE_GROUP(gm_commands FILES
|
||||
${gm_commands}
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers} ${gm_commands})
|
||||
|
||||
INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
+19
-36
@@ -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
-1
@@ -7104,7 +7104,8 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot s
|
||||
bardsong_timer.Disable();
|
||||
}
|
||||
|
||||
Result = DoCastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, aa_id);
|
||||
Result = Mob::CastSpell(spell_id, target_id, slot, cast_time, mana_cost, oSpellWillFinish, item_slot, 0xFFFFFFFF, 0, resist_adjust, aa_id);
|
||||
|
||||
}
|
||||
return Result;
|
||||
}
|
||||
|
||||
+2
-13
@@ -6178,16 +6178,10 @@ void Client::MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 coun
|
||||
void Client::SendZonePoints()
|
||||
{
|
||||
int count = 0;
|
||||
LinkedListIterator<ZonePoint *> iterator(zone->zone_point_list);
|
||||
iterator.Reset();
|
||||
while (iterator.MoreElements()) {
|
||||
ZonePoint *data = iterator.GetData();
|
||||
|
||||
for (auto& data : zone->zone_point_list) {
|
||||
if (ClientVersionBit() & data->client_version_mask) {
|
||||
count++;
|
||||
}
|
||||
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
uint32 zpsize = sizeof(ZonePoints) + ((count + 1) * sizeof(ZonePoint_Entry));
|
||||
@@ -6196,11 +6190,7 @@ void Client::SendZonePoints()
|
||||
zp->count = count;
|
||||
|
||||
int i = 0;
|
||||
iterator.Reset();
|
||||
while(iterator.MoreElements())
|
||||
{
|
||||
ZonePoint* data = iterator.GetData();
|
||||
|
||||
for (auto& data : zone->zone_point_list) {
|
||||
LogZonePoints(
|
||||
"Sending zone point to client [{}] mask [{}] x [{}] y [{}] z [{}] number [{}]",
|
||||
GetCleanName(),
|
||||
@@ -6222,7 +6212,6 @@ void Client::SendZonePoints()
|
||||
zp->zpe[i].zoneinstance = data->target_zone_instance;
|
||||
i++;
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
FastQueuePacket(&outapp);
|
||||
|
||||
+1
-3
@@ -980,7 +980,7 @@ public:
|
||||
//bool DecreaseByType(uint32 type, uint8 amt);
|
||||
bool DecreaseByID(uint32 type, int16 quantity);
|
||||
uint8 SlotConvert2(uint8 slot); //Maybe not needed.
|
||||
void Escape(); //AA Escape
|
||||
void Escape(); //keep or quest function
|
||||
void DisenchantSummonedBags(bool client_update = true);
|
||||
void RemoveNoRent(bool client_update = true);
|
||||
void RemoveDuplicateLore(bool client_update = true);
|
||||
@@ -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); }
|
||||
|
||||
+67
-51
@@ -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()) {
|
||||
@@ -8792,39 +8806,41 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app)
|
||||
spell_id = item->Click.Effect;
|
||||
bool is_casting_bard_song = false;
|
||||
|
||||
if
|
||||
(
|
||||
spell_id > 0 &&
|
||||
(
|
||||
!IsValidSpell(spell_id) ||
|
||||
casting_spell_id ||
|
||||
delaytimer ||
|
||||
spellend_timer.Enabled() ||
|
||||
IsStunned() ||
|
||||
IsFeared() ||
|
||||
IsMezzed() ||
|
||||
DivineAura() ||
|
||||
(spells[spell_id].target_type == ST_Ring) ||
|
||||
(IsSilenced() && !IsDiscipline(spell_id)) ||
|
||||
(IsAmnesiad() && IsDiscipline(spell_id)) ||
|
||||
(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) ||
|
||||
(inst->IsScaling() && inst->GetExp() <= 0) // charms don't have spells when less than 0
|
||||
)
|
||||
)
|
||||
{
|
||||
/*
|
||||
Bards on live can click items while casting spell gems, it stops that song cast and replaces it with item click cast.
|
||||
Can not click while casting other items.
|
||||
*/
|
||||
if (GetClass() == BARD && IsCasting() && casting_spell_slot < CastingSlot::MaxGems)
|
||||
{
|
||||
is_casting_bard_song = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (spell_id > 0) {
|
||||
|
||||
if (!IsValidSpell(spell_id) ||
|
||||
IsStunned() ||
|
||||
IsFeared() ||
|
||||
IsMezzed() ||
|
||||
DivineAura() ||
|
||||
(spells[spell_id].target_type == ST_Ring) ||
|
||||
(IsSilenced() && !IsDiscipline(spell_id)) ||
|
||||
(IsAmnesiad() && IsDiscipline(spell_id)) ||
|
||||
(IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) ||
|
||||
(inst->IsScaling() && inst->GetExp() <= 0)) { // charms don't have spells when less than 0
|
||||
|
||||
SendSpellBarEnable(spell_id);
|
||||
return;
|
||||
}
|
||||
|
||||
if (casting_spell_id ||
|
||||
delaytimer ||
|
||||
spellend_timer.Enabled()) {
|
||||
|
||||
/*
|
||||
Bards on live can click items while casting spell gems, it stops that song cast and replaces it with item click cast.
|
||||
Can not click while casting other items.
|
||||
*/
|
||||
if (GetClass() == BARD && IsCasting() && casting_spell_slot < CastingSlot::MaxGems)
|
||||
{
|
||||
is_casting_bard_song = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
SendSpellBarEnable(spell_id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Modern clients don't require pet targeted for item clicks that are ST_Pet
|
||||
if (spell_id > 0 && (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_SummonedPet))
|
||||
|
||||
+5
-4
@@ -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) ||
|
||||
|
||||
+2
-1
@@ -209,7 +209,8 @@ enum {
|
||||
IMMUNE_AGGRO_CLIENT = 49,
|
||||
IMMUNE_AGGRO_NPC = 50,
|
||||
MODIFY_AVOID_DAMAGE = 51, //Modify by percent the NPCs chance to riposte, block, parry or dodge individually, or for all skills
|
||||
MAX_SPECIAL_ATTACK = 52
|
||||
IMMUNE_FADING_MEMORIES = 52,
|
||||
MAX_SPECIAL_ATTACK = 53
|
||||
};
|
||||
|
||||
typedef enum { //fear states
|
||||
|
||||
+41
-6
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
+27
-1
@@ -1514,7 +1514,7 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
if (RemoveFromXTargets) {
|
||||
if (RemoveFromXTargets && mob) {
|
||||
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m)))
|
||||
m->CastToClient()->RemoveXTarget(mob, false);
|
||||
// FadingMemories calls this function passing the client.
|
||||
@@ -1526,6 +1526,32 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
|
||||
}
|
||||
}
|
||||
|
||||
void EntityList::RemoveFromTargetsFadingMemories(Mob *spell_target, bool RemoveFromXTargets, uint32 max_level)
|
||||
{
|
||||
for (auto &e : mob_list) {
|
||||
auto &mob = e.second;
|
||||
|
||||
if (!mob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (max_level && mob->GetLevel() > max_level)
|
||||
continue;
|
||||
|
||||
if (mob->GetSpecialAbility(IMMUNE_FADING_MEMORIES))
|
||||
continue;
|
||||
|
||||
if (RemoveFromXTargets && spell_target) {
|
||||
if (mob->IsClient() && (spell_target->CheckAggro(mob) || spell_target->IsOnFeignMemory(mob)))
|
||||
mob->CastToClient()->RemoveXTarget(spell_target, false);
|
||||
else if (spell_target->IsClient() && (mob->CheckAggro(spell_target) || mob->IsOnFeignMemory(spell_target)))
|
||||
spell_target->CastToClient()->RemoveXTarget(mob, false);
|
||||
}
|
||||
|
||||
mob->RemoveFromHateList(spell_target);
|
||||
}
|
||||
}
|
||||
|
||||
void EntityList::RemoveFromXTargets(Mob *mob)
|
||||
{
|
||||
auto it = client_list.begin();
|
||||
|
||||
@@ -389,6 +389,7 @@ public:
|
||||
void ExpeditionWarning(uint32 minutes_left);
|
||||
|
||||
void RemoveFromTargets(Mob* mob, bool RemoveFromXTargets = false);
|
||||
void RemoveFromTargetsFadingMemories(Mob* spell_target, bool RemoveFromXTargets = false, uint32 max_level = 0);
|
||||
void RemoveFromXTargets(Mob* mob);
|
||||
void RemoveFromAutoXTargets(Mob* mob);
|
||||
void ReplaceWithTarget(Mob* pOldMob, Mob*pNewTarget);
|
||||
|
||||
+242
-118
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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]");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
void command_reloadzps(Client *c, const Seperator *sep)
|
||||
{
|
||||
content_db.LoadStaticZonePoints(&zone->zone_point_list, zone->GetShortName(), zone->GetInstanceVersion());
|
||||
content_db.LoadStaticZonePoints(zone->zone_point_list, zone->GetShortName(), zone->GetInstanceVersion());
|
||||
c->Message(Chat::White, "Reloading server zone_points.");
|
||||
}
|
||||
|
||||
|
||||
@@ -108,10 +108,7 @@ void command_showzonepoints(Client *c, const Seperator *sep)
|
||||
found_zone_points++;
|
||||
}
|
||||
|
||||
LinkedListIterator<ZonePoint *> iterator(zone->zone_point_list);
|
||||
iterator.Reset();
|
||||
while (iterator.MoreElements()) {
|
||||
ZonePoint *zone_point = iterator.GetData();
|
||||
for (auto& zone_point : zone->zone_point_list) {
|
||||
std::string zone_long_name = ZoneLongName(zone_point->target_zone_id);
|
||||
std::string node_name = fmt::format("ZonePoint To [{}]", zone_long_name);
|
||||
|
||||
@@ -142,8 +139,6 @@ void command_showzonepoints(Client *c, const Seperator *sep)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
iterator.Advance();
|
||||
|
||||
found_zone_points++;
|
||||
}
|
||||
|
||||
|
||||
+35
-11
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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.");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -635,6 +635,24 @@ Lua_NPC_Loot_List Lua_NPC::GetLootList(lua_State* L) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Lua_NPC::AddAISpellEffect(int spell_effect_id, int base_value, int limit_value, int max_value)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddSpellEffectToNPCList(spell_effect_id, base_value, limit_value, max_value, true);
|
||||
}
|
||||
|
||||
void Lua_NPC::RemoveAISpellEffect(int spell_effect_id)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->RemoveSpellEffectFromNPCList(spell_effect_id, true);
|
||||
}
|
||||
|
||||
bool Lua_NPC::HasAISpellEffect(int spell_effect_id)
|
||||
{
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->HasAISpellEffect(spell_effect_id);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_npc() {
|
||||
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -642,6 +660,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox)
|
||||
.def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int))&Lua_NPC::AddAISpell)
|
||||
.def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int,int,int))&Lua_NPC::AddAISpell)
|
||||
.def("AddAISpellEffect", (void(Lua_NPC::*)(int,int,int,int))&Lua_NPC::AddAISpellEffect)
|
||||
.def("AddCash", (void(Lua_NPC::*)(int,int,int,int))&Lua_NPC::AddCash)
|
||||
.def("AddItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::AddItem)
|
||||
.def("AddItem", (void(Lua_NPC::*)(int,int,bool))&Lua_NPC::AddItem)
|
||||
@@ -711,6 +730,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("GetSwarmOwner", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmOwner)
|
||||
.def("GetSwarmTarget", (int(Lua_NPC::*)(void))&Lua_NPC::GetSwarmTarget)
|
||||
.def("GetWaypointMax", (int(Lua_NPC::*)(void))&Lua_NPC::GetWaypointMax)
|
||||
.def("HasAISpellEffect", (bool(Lua_NPC::*)(int))&Lua_NPC::HasAISpellEffect)
|
||||
.def("HasItem", (bool(Lua_NPC::*)(uint32))&Lua_NPC::HasItem)
|
||||
.def("IsAnimal", (bool(Lua_NPC::*)(void))&Lua_NPC::IsAnimal)
|
||||
.def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding)
|
||||
@@ -726,6 +746,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("PickPocket", (void(Lua_NPC::*)(Lua_Client))&Lua_NPC::PickPocket)
|
||||
.def("RecalculateSkills", (void(Lua_NPC::*)(void))&Lua_NPC::RecalculateSkills)
|
||||
.def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell)
|
||||
.def("RemoveAISpellEffect", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpellEffect)
|
||||
.def("RemoveCash", (void(Lua_NPC::*)(void))&Lua_NPC::RemoveCash)
|
||||
.def("RemoveItem", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveItem)
|
||||
.def("RemoveItem", (void(Lua_NPC::*)(int,int))&Lua_NPC::RemoveItem)
|
||||
|
||||
@@ -149,6 +149,9 @@ public:
|
||||
float GetHealScale();
|
||||
float GetSpellScale();
|
||||
Lua_NPC_Loot_List GetLootList(lua_State* L);
|
||||
void AddAISpellEffect(int spell_effect_id, int base_value, int limit_value, int max_value);
|
||||
void RemoveAISpellEffect(int spell_effect_id);
|
||||
bool HasAISpellEffect(int spell_effect_id);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+4
-1
@@ -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);
|
||||
@@ -357,6 +357,9 @@ public:
|
||||
void ApplySpellEffectIllusion(int32 spell_id, Mob* caster, int buffslot, int base, int limit, int max);
|
||||
void ApplyIllusionToCorpse(int32 spell_id, Corpse* new_corpse);
|
||||
void SendIllusionWearChange(Client* c);
|
||||
int16 GetItemSlotToConsumeCharge(int32 spell_id, uint32 inventory_slot);
|
||||
bool CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot);
|
||||
bool IsFromTriggeredSpell(EQ::spells::CastingSlot slot, uint32 item_slot = 0xFFFFFFFF);
|
||||
|
||||
//Bard
|
||||
bool ApplyBardPulse(int32 spell_id, Mob *spell_target, EQ::spells::CastingSlot slot);
|
||||
|
||||
@@ -2799,6 +2799,17 @@ void NPC::RemoveSpellEffectFromNPCList(uint16 iSpellEffectID, bool apply_bonus)
|
||||
}
|
||||
}
|
||||
|
||||
bool NPC::HasAISpellEffect(uint16 spell_effect_id)
|
||||
{
|
||||
for (const auto& spell_effect : AIspellsEffects) {
|
||||
if (spell_effect.spelleffectid == spell_effect_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value) {
|
||||
for (uint32 i=0; i < spelleffect_list->numentries; i++) {
|
||||
if (spelleffect_list->entries[i].spelleffectid == iSpellEffectID && spelleffect_list->entries[i].base_value == base_value
|
||||
|
||||
@@ -452,6 +452,7 @@ public:
|
||||
void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base_value, int32 limit, int32 max_value, bool apply_bonus = false);
|
||||
void RemoveSpellFromNPCList(uint16 spell_id);
|
||||
void RemoveSpellEffectFromNPCList(uint16 iSpellEffectID, bool apply_bonus = false);
|
||||
bool HasAISpellEffect(uint16 spell_effect_id);
|
||||
Timer *GetRefaceTimer() const { return reface_timer; }
|
||||
const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; }
|
||||
|
||||
|
||||
+24
-7
@@ -1849,14 +1849,14 @@ XS(XS_NPC_AddAISpellEffect); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_NPC_AddAISpellEffect) {
|
||||
dXSARGS;
|
||||
if (items != 5)
|
||||
Perl_croak(aTHX_ "Usage: NPC::AddAISpellEffect(THIS, spell_effect id, base_value, limit_value, max_value)"); // @categories Spells and Disciplines
|
||||
Perl_croak(aTHX_ "Usage: NPC::AddAISpellEffect(THIS, int spell_effect_id, int base_value, int limit_value, int max_value)"); // @categories Spells and Disciplines
|
||||
{
|
||||
NPC *THIS;
|
||||
|
||||
int spell_effect_id = (int)SvIV(ST(1));
|
||||
int base_value = (int)SvIV(ST(2));
|
||||
int limit_value = (int)SvIV(ST(3));
|
||||
int max_value = (int)SvIV(ST(4));
|
||||
int spell_effect_id = (int) SvIV(ST(1));
|
||||
int base_value = (int) SvIV(ST(2));
|
||||
int limit_value = (int) SvIV(ST(3));
|
||||
int max_value = (int) SvIV(ST(4));
|
||||
|
||||
VALIDATE_THIS_IS_NPC;
|
||||
THIS->AddSpellEffectToNPCList(spell_effect_id, base_value, limit_value, max_value, true);
|
||||
@@ -1868,16 +1868,32 @@ XS(XS_NPC_RemoveAISpellEffect); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_NPC_RemoveAISpellEffect) {
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: NPC::RemoveAISpellEffect(THIS, int spelleffect_id)"); // @categories Spells and Disciplines
|
||||
Perl_croak(aTHX_ "Usage: NPC::RemoveAISpellEffect(THIS, int spell_effect_id)"); // @categories Spells and Disciplines
|
||||
{
|
||||
NPC *THIS;
|
||||
int spell_effect_id = (int)SvIV(ST(1));
|
||||
int spell_effect_id = (int) SvIV(ST(1));
|
||||
VALIDATE_THIS_IS_NPC;
|
||||
THIS->RemoveSpellEffectFromNPCList(spell_effect_id, true);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_NPC_HasAISpellEffect); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_NPC_HasAISpellEffect) {
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: NPC::HasAISpellEffect(THIS, int spell_effect_id)"); // @categories Spells and Disciplines
|
||||
{
|
||||
NPC *THIS;
|
||||
bool has_spell_effect = false;
|
||||
int spell_effect_id = (int) SvIV(ST(1));
|
||||
VALIDATE_THIS_IS_NPC;
|
||||
has_spell_effect = THIS->HasAISpellEffect(spell_effect_id);
|
||||
ST(0) = boolSV(has_spell_effect);
|
||||
sv_2mortal(ST(0));
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
@@ -1959,6 +1975,7 @@ XS(boot_NPC) {
|
||||
newXSproto(strcpy(buf, "GetSwarmOwner"), XS_NPC_GetSwarmOwner, file, "$");
|
||||
newXSproto(strcpy(buf, "GetSwarmTarget"), XS_NPC_GetSwarmTarget, file, "$");
|
||||
newXSproto(strcpy(buf, "GetWaypointMax"), XS_NPC_GetWaypointMax, file, "$");
|
||||
newXSproto(strcpy(buf, "HasAISpellEffect"), XS_NPC_HasAISpellEffect, file, "$$");
|
||||
newXSproto(strcpy(buf, "HasItem"), XS_NPC_HasItem, file, "$$");
|
||||
newXSproto(strcpy(buf, "IsAnimal"), XS_NPC_IsAnimal, file, "$");
|
||||
newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, 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
@@ -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;
|
||||
|
||||
+342
-310
File diff suppressed because it is too large
Load Diff
+221
-217
@@ -206,55 +206,11 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
}
|
||||
|
||||
//Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits.
|
||||
if(item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
|
||||
if (item_slot && IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt))
|
||||
{
|
||||
EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(item_slot);
|
||||
int bitmask = 1;
|
||||
bitmask = bitmask << (CastToClient()->GetClass() - 1);
|
||||
if( itm && itm->GetItem()->Classes != 65535 ) {
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that requires equipping but shouldn't let them equip it
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that they don't meet the race/class requirements to cast
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class");
|
||||
}
|
||||
else {
|
||||
if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::RoF)
|
||||
{
|
||||
// Line 181 in eqstr_us.txt was changed in RoF+
|
||||
Message(Chat::Yellow, "Your race, class, or deity cannot use this item.");
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageString(Chat::Red, CANNOT_USE_ITEM);
|
||||
}
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
if (itm && (itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && item_slot > EQ::invslot::EQUIPMENT_END){
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are attempting to cast a must equip clicky without having it equipped
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
if (!CheckItemRaceClassDietyRestrictionsOnCast(item_slot)) {
|
||||
StopCastSpell(spell_id, send_spellbar_enable);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -817,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
@@ -1316,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;
|
||||
}
|
||||
@@ -1627,61 +1597,11 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
|
||||
if(IsClient() && (slot == CastingSlot::Item || slot == CastingSlot::PotionBelt)
|
||||
&& inventory_slot != 0xFFFFFFFF) // 10 is an item
|
||||
{
|
||||
bool fromaug = false;
|
||||
EQ::ItemData* augitem = nullptr;
|
||||
uint32 recastdelay = 0;
|
||||
int recasttype = 0;
|
||||
|
||||
while (true) {
|
||||
if (item == nullptr)
|
||||
break;
|
||||
|
||||
for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) {
|
||||
const EQ::ItemInstance* aug_i = item->GetAugment(r);
|
||||
|
||||
if (!aug_i)
|
||||
continue;
|
||||
const EQ::ItemData* aug = aug_i->GetItem();
|
||||
if (!aug)
|
||||
continue;
|
||||
|
||||
if (aug->Click.Effect == spell_id)
|
||||
{
|
||||
recastdelay = aug_i->GetItem()->RecastDelay;
|
||||
recasttype = aug_i->GetItem()->RecastType;
|
||||
fromaug = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug)
|
||||
{
|
||||
//const ItemData* item = item->GetItem();
|
||||
int16 charges = item->GetItem()->MaxCharges;
|
||||
|
||||
if(fromaug) { charges = -1; } //Don't destroy the parent item
|
||||
|
||||
if(charges > -1) { // charged item, expend a charge
|
||||
LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetCharges(), item->GetItem()->MaxCharges);
|
||||
DeleteChargeFromSlot = inventory_slot;
|
||||
} else {
|
||||
LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetItem()->MaxCharges);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogSpells("Item used to cast spell [{}] was missing from inventory slot [{}] after casting!", spell_id, inventory_slot);
|
||||
Message(Chat::Red, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot);
|
||||
InterruptSpell();
|
||||
return;
|
||||
}
|
||||
DeleteChargeFromSlot = GetItemSlotToConsumeCharge(spell_id, inventory_slot);
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -1699,8 +1619,9 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
|
||||
|
||||
TryTriggerOnCastFocusEffect(focusTriggerOnCast, spell_id);
|
||||
|
||||
if(DeleteChargeFromSlot >= 0)
|
||||
if (DeleteChargeFromSlot >= 0) {
|
||||
CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true);
|
||||
}
|
||||
|
||||
//
|
||||
// at this point the spell has successfully been cast
|
||||
@@ -2300,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;
|
||||
|
||||
@@ -2474,9 +2395,6 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
|
||||
|
||||
else if(!SpellOnTarget(spell_id, spell_target, 0, true, resist_adjust, false, level_override)) {
|
||||
if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) {
|
||||
// Prevent mana usage/timers being set for beneficial buffs
|
||||
if(casting_spell_aa_id)
|
||||
InterruptSpell();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2497,16 +2415,6 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
|
||||
case AECaster:
|
||||
case AETarget:
|
||||
{
|
||||
#ifdef BOTS
|
||||
if(IsBot()) {
|
||||
bool StopLogic = false;
|
||||
if(!this->CastToBot()->DoFinishedSpellAETarget(spell_id, spell_target, slot, StopLogic))
|
||||
return false;
|
||||
if(StopLogic)
|
||||
break;
|
||||
}
|
||||
#endif //BOTS
|
||||
|
||||
// we can't cast an AE spell without something to center it on
|
||||
assert(ae_center != nullptr);
|
||||
|
||||
@@ -2674,54 +2582,39 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
|
||||
if (mgb) {
|
||||
SetMGB(false);
|
||||
}
|
||||
/*
|
||||
Set Recast Timer on spells.
|
||||
*/
|
||||
|
||||
//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)
|
||||
if(IsClient() && !isproc && !IsFromTriggeredSpell(slot, inventory_slot))
|
||||
{
|
||||
//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(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;
|
||||
@@ -2741,14 +2634,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));
|
||||
@@ -3573,10 +3472,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;
|
||||
|
||||
@@ -3670,6 +3565,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
|
||||
@@ -4099,8 +3999,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes
|
||||
// if SpellEffect returned false there's a problem applying the
|
||||
// spell. It's most likely a buff that can't stack.
|
||||
LogSpells("Spell [{}] could not apply its effects [{}] -> [{}]\n", spell_id, GetName(), spelltar->GetName());
|
||||
if(casting_spell_aa_id)
|
||||
if (casting_spell_aa_id) {
|
||||
MessageString(Chat::SpellFailure, SPELL_NO_HOLD);
|
||||
}
|
||||
safe_delete(action_packet);
|
||||
return false;
|
||||
}
|
||||
@@ -6287,13 +6188,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;
|
||||
}
|
||||
@@ -6301,40 +6208,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; }
|
||||
@@ -6638,7 +6511,138 @@ void Mob::DoBardCastingFromItemClick(bool is_casting_bard_song, uint32 cast_time
|
||||
CastSpell(spell_id, target_id, CastingSlot::Item, cast_time, 0, 0, item_slot);
|
||||
}
|
||||
//Instant cast items do not stop bard songs or interrupt casting.
|
||||
else if (DoCastingChecksOnCaster(spell_id)) {
|
||||
SpellFinished(spell_id, entity_list.GetMob(target_id), CastingSlot::Item, 0, item_slot);
|
||||
else if (CheckItemRaceClassDietyRestrictionsOnCast(item_slot) && DoCastingChecksOnCaster(spell_id)) {
|
||||
int16 DeleteChargeFromSlot = GetItemSlotToConsumeCharge(spell_id, item_slot);
|
||||
if (SpellFinished(spell_id, entity_list.GetMob(target_id), CastingSlot::Item, 0, item_slot)) {
|
||||
if (IsClient() && DeleteChargeFromSlot >= 0) {
|
||||
CastToClient()->DeleteItemInInventory(DeleteChargeFromSlot, 1, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16 Mob::GetItemSlotToConsumeCharge(int32 spell_id, uint32 inventory_slot)
|
||||
{
|
||||
int16 DeleteChargeFromSlot = -1;
|
||||
|
||||
if (!IsClient() || inventory_slot == 0xFFFFFFFF) {
|
||||
return DeleteChargeFromSlot;
|
||||
}
|
||||
|
||||
EQ::ItemInstance *item = nullptr;
|
||||
item = CastToClient()->GetInv().GetItem(inventory_slot);
|
||||
|
||||
bool fromaug = false;
|
||||
EQ::ItemData* augitem = nullptr;
|
||||
|
||||
while (true) {
|
||||
if (item == nullptr)
|
||||
break;
|
||||
|
||||
for (int r = EQ::invaug::SOCKET_BEGIN; r <= EQ::invaug::SOCKET_END; r++) {
|
||||
const EQ::ItemInstance* aug_i = item->GetAugment(r);
|
||||
|
||||
if (!aug_i) {
|
||||
continue;
|
||||
}
|
||||
const EQ::ItemData* aug = aug_i->GetItem();
|
||||
if (!aug) {
|
||||
continue;
|
||||
}
|
||||
if (aug->Click.Effect == spell_id){
|
||||
fromaug = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (item && item->IsClassCommon() && (item->GetItem()->Click.Effect == spell_id) && item->GetCharges() || fromaug){
|
||||
int16 charges = item->GetItem()->MaxCharges;
|
||||
|
||||
if (fromaug) { charges = -1; } //Don't destroy the parent item
|
||||
|
||||
if (charges > -1) { // charged item, expend a charge
|
||||
LogSpells("Spell [{}]: Consuming a charge from item [{}] ([{}]) which had [{}]/[{}] charges", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetCharges(), item->GetItem()->MaxCharges);
|
||||
DeleteChargeFromSlot = inventory_slot;
|
||||
}
|
||||
else {
|
||||
LogSpells("Spell [{}]: Cast from unlimited charge item [{}] ([{}]) ([{}] charges)", spell_id, item->GetItem()->Name, item->GetItem()->ID, item->GetItem()->MaxCharges);
|
||||
}
|
||||
}
|
||||
else{
|
||||
LogSpells("Item used to cast spell [{}] was missing from inventory slot [{}] after casting!", spell_id, inventory_slot);
|
||||
Message(Chat::Red, "Casting Error: Active casting item not found in inventory slot %i", inventory_slot);
|
||||
InterruptSpell();
|
||||
return DeleteChargeFromSlot;
|
||||
}
|
||||
return DeleteChargeFromSlot;
|
||||
}
|
||||
|
||||
bool Mob::CheckItemRaceClassDietyRestrictionsOnCast(uint32 inventory_slot) {
|
||||
|
||||
if (inventory_slot == 0xFFFFFFFF) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits.
|
||||
EQ::ItemInstance *itm = CastToClient()->GetInv().GetItem(inventory_slot);
|
||||
int bitmask = 1;
|
||||
bitmask = bitmask << (CastToClient()->GetClass() - 1);
|
||||
if (itm && itm->GetItem()->Classes != 65535) {
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that requires equipping but shouldn't let them equip it
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) which they shouldn't be able to equip!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item with an invalid class");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
if ((itm->GetItem()->Click.Type == EQ::item::ItemEffectClick2) && !(itm->GetItem()->Classes & bitmask)) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are casting a spell from an item that they don't meet the race/class requirements to cast
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click a race/class restricted effect on item [{}] (id: [{}]) which they shouldn't be able to click!",
|
||||
CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking race/class restricted item with an invalid class");
|
||||
}
|
||||
else {
|
||||
if (CastToClient()->ClientVersion() >= EQ::versions::ClientVersion::RoF)
|
||||
{
|
||||
// Line 181 in eqstr_us.txt was changed in RoF+
|
||||
Message(Chat::Yellow, "Your race, class, or deity cannot use this item.");
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageString(Chat::Red, CANNOT_USE_ITEM);
|
||||
}
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
if (itm && (itm->GetItem()->Click.Type == EQ::item::ItemEffectEquipClick) && inventory_slot > EQ::invslot::EQUIPMENT_END) {
|
||||
if (CastToClient()->ClientVersion() < EQ::versions::ClientVersion::SoF) {
|
||||
// They are attempting to cast a must equip clicky without having it equipped
|
||||
LogError("HACKER: [{}] (account: [{}]) attempted to click an equip-only effect on item [{}] (id: [{}]) without equiping it!", CastToClient()->GetCleanName(), CastToClient()->AccountName(), itm->GetItem()->Name, itm->GetItem()->ID);
|
||||
database.SetHackerFlag(CastToClient()->AccountName(), CastToClient()->GetCleanName(), "Clicking equip-only item without equiping it");
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Red, MUST_EQUIP_ITEM);
|
||||
}
|
||||
return(false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mob::IsFromTriggeredSpell(CastingSlot slot, uint32 item_slot) {
|
||||
//spells triggered using spells finished use item slot, but there is no item set.
|
||||
if ((slot == CastingSlot::Item) && (item_slot == 0xFFFFFFFF)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
+20
-30
@@ -1061,7 +1061,7 @@ Zone::~Zone() {
|
||||
safe_delete_array(long_name);
|
||||
safe_delete(Weather_Timer);
|
||||
NPCEmoteList.Clear();
|
||||
zone_point_list.Clear();
|
||||
zone_point_list.clear();
|
||||
entity_list.Clear();
|
||||
ClearBlockedSpells();
|
||||
|
||||
@@ -1103,7 +1103,7 @@ bool Zone::Init(bool iStaticZone) {
|
||||
}
|
||||
|
||||
LogInfo("Loading static zone points");
|
||||
if (!content_db.LoadStaticZonePoints(&zone_point_list, short_name, GetInstanceVersion())) {
|
||||
if (!content_db.LoadStaticZonePoints(zone_point_list, short_name, GetInstanceVersion())) {
|
||||
LogError("Loading static zone points failed");
|
||||
return false;
|
||||
}
|
||||
@@ -1213,8 +1213,8 @@ void Zone::ReloadStaticData() {
|
||||
LogInfo("Reloading Zone Static Data");
|
||||
|
||||
LogInfo("Reloading static zone points");
|
||||
zone_point_list.Clear();
|
||||
if (!content_db.LoadStaticZonePoints(&zone_point_list, GetShortName(), GetInstanceVersion())) {
|
||||
zone_point_list.clear();
|
||||
if (!content_db.LoadStaticZonePoints(zone_point_list, GetShortName(), GetInstanceVersion())) {
|
||||
LogError("Loading static zone points failed");
|
||||
}
|
||||
|
||||
@@ -1884,33 +1884,27 @@ void Zone::SetTime(uint8 hour, uint8 minute, bool update_world /*= true*/)
|
||||
}
|
||||
|
||||
ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Client* client, float max_distance) {
|
||||
LinkedListIterator<ZonePoint*> iterator(zone_point_list);
|
||||
ZonePoint* closest_zp = nullptr;
|
||||
float closest_dist = FLT_MAX;
|
||||
float max_distance2 = max_distance * max_distance;
|
||||
iterator.Reset();
|
||||
while(iterator.MoreElements())
|
||||
{
|
||||
ZonePoint* zp = iterator.GetData();
|
||||
for (auto& zp : zone_point_list) {
|
||||
uint32 mask_test = client->ClientVersionBit();
|
||||
if (!(zp->client_version_mask & mask_test)) {
|
||||
iterator.Advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if (zp->target_zone_id == to)
|
||||
{
|
||||
auto dist = Distance(glm::vec2(zp->x, zp->y), glm::vec2(location));
|
||||
auto dist = Distance(glm::vec2(zp->x, zp->y), glm::vec2(location));
|
||||
if ((zp->x == 999999 || zp->x == -999999) && (zp->y == 999999 || zp->y == -999999))
|
||||
dist = 0;
|
||||
|
||||
|
||||
if (dist < closest_dist)
|
||||
{
|
||||
closest_zp = zp;
|
||||
closest_zp = zp.get();
|
||||
closest_dist = dist;
|
||||
}
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
// if we have a water map and it says we're in a zoneline, lets assume it's just a really big zone line
|
||||
@@ -1942,19 +1936,15 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, const char* to_n
|
||||
}
|
||||
|
||||
ZonePoint* Zone::GetClosestZonePointWithoutZone(float x, float y, float z, Client* client, float max_distance) {
|
||||
LinkedListIterator<ZonePoint*> iterator(zone_point_list);
|
||||
ZonePoint* closest_zp = nullptr;
|
||||
float closest_dist = FLT_MAX;
|
||||
float max_distance2 = max_distance*max_distance;
|
||||
iterator.Reset();
|
||||
while(iterator.MoreElements())
|
||||
{
|
||||
ZonePoint* zp = iterator.GetData();
|
||||
|
||||
for (auto& zp : zone_point_list) {
|
||||
uint32 mask_test = client->ClientVersionBit();
|
||||
|
||||
if(!(zp->client_version_mask & mask_test))
|
||||
if (!(zp->client_version_mask & mask_test))
|
||||
{
|
||||
iterator.Advance();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1964,24 +1954,24 @@ ZonePoint* Zone::GetClosestZonePointWithoutZone(float x, float y, float z, Clien
|
||||
delta_x = 0;
|
||||
if(zp->y == 999999 || zp->y == -999999)
|
||||
delta_y = 0;
|
||||
|
||||
float dist = delta_x*delta_x+delta_y*delta_y;///*+(zp->z-z)*(zp->z-z)*/;
|
||||
|
||||
float dist = delta_x*delta_x+delta_y*delta_y;
|
||||
if (dist < closest_dist)
|
||||
{
|
||||
closest_zp = zp;
|
||||
closest_zp = zp.get();
|
||||
closest_dist = dist;
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
if(closest_dist > max_distance2)
|
||||
closest_zp = nullptr;
|
||||
|
||||
|
||||
return closest_zp;
|
||||
}
|
||||
|
||||
bool ZoneDatabase::LoadStaticZonePoints(LinkedList<ZonePoint *> *zone_point_list, const char *zonename, uint32 version)
|
||||
bool ZoneDatabase::LoadStaticZonePoints(std::list<std::unique_ptr<ZonePoint>> &zone_point_list, const char *zonename, uint32 version)
|
||||
{
|
||||
zone_point_list->Clear();
|
||||
zone_point_list.clear();
|
||||
zone->numzonepoints = 0;
|
||||
zone->virtual_zone_point_list.clear();
|
||||
|
||||
@@ -2036,7 +2026,7 @@ bool ZoneDatabase::LoadStaticZonePoints(LinkedList<ZonePoint *> *zone_point_list
|
||||
continue;
|
||||
}
|
||||
|
||||
zone_point_list->Insert(zp);
|
||||
zone_point_list.push_back(std::unique_ptr<ZonePoint>(zp));
|
||||
zone->numzonepoints++;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -184,7 +184,7 @@ public:
|
||||
IPathfinder *pathing;
|
||||
LinkedList<NPC_Emote_Struct *> NPCEmoteList;
|
||||
LinkedList<Spawn2 *> spawn2_list;
|
||||
LinkedList<ZonePoint *> zone_point_list;
|
||||
std::list<std::unique_ptr<ZonePoint>> zone_point_list;
|
||||
std::vector<ZonePointsRepository::ZonePoints> virtual_zone_point_list;
|
||||
|
||||
Map *zonemap;
|
||||
|
||||
+1
-1
@@ -452,7 +452,7 @@ public:
|
||||
int &ruleset,
|
||||
char **map_filename);
|
||||
bool SaveZoneCFG(uint32 zoneid, uint16 instance_version, NewZone_Struct* zd);
|
||||
bool LoadStaticZonePoints(LinkedList<ZonePoint*>* zone_point_list,const char* zonename, uint32 version);
|
||||
bool LoadStaticZonePoints(std::list<std::unique_ptr<ZonePoint>> &zone_point_list,const char* zonename, uint32 version);
|
||||
bool UpdateZoneSafeCoords(const char* zonename, const glm::vec3& location);
|
||||
uint8 GetUseCFGSafeCoords();
|
||||
int getZoneShutDownDelay(uint32 zoneID, uint32 version);
|
||||
|
||||
Reference in New Issue
Block a user