From 3e1b86a7c3ec42015c97ee4bbe8a72220890b43c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sat, 24 Mar 2018 15:24:54 -0400 Subject: [PATCH 01/14] Fix lua/perl set_proximity to accept flag for say prox This will fix proximity say to always work --- zone/embparser_api.cpp | 7 +++++-- zone/lua_general.cpp | 5 +++++ zone/questmgr.cpp | 3 ++- zone/questmgr.h | 2 +- 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 0c8ef58b9..3026358c4 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1543,7 +1543,7 @@ XS(XS__set_proximity) { dXSARGS; if (items != 4 && items != 6) - Perl_croak(aTHX_ "Usage: set_proximity(min_x, max_x, min_y, max_y [, min_z, max_z])"); + Perl_croak(aTHX_ "Usage: set_proximity(min_x, max_x, min_y, max_y [, min_z, max_z], [say])"); float min_x = (float)SvNV(ST(0)); float max_x = (float)SvNV(ST(1)); @@ -1555,7 +1555,10 @@ XS(XS__set_proximity) else { float min_z = (float)SvNV(ST(4)); float max_z = (float)SvNV(ST(5)); - quest_manager.set_proximity(min_x, max_x, min_y, max_y, min_z, max_z); + bool bSay = false; + if (items == 7) + bSay = (bool)SvTRUE(ST(6)); + quest_manager.set_proximity(min_x, max_x, min_y, max_y, min_z, max_z, bSay); } XSRETURN_EMPTY; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index ec22fec1b..55f00bcf1 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -493,6 +493,10 @@ void lua_set_proximity(float min_x, float max_x, float min_y, float max_y, float quest_manager.set_proximity(min_x, max_x, min_y, max_y, min_z, max_z); } +void lua_set_proximity(float min_x, float max_x, float min_y, float max_y, float min_z, float max_z, bool say) { + quest_manager.set_proximity(min_x, max_x, min_y, max_y, min_z, max_z, say); +} + void lua_clear_proximity() { quest_manager.clear_proximity(); } @@ -1581,6 +1585,7 @@ luabind::scope lua_register_general() { luabind::def("respawn", &lua_respawn), luabind::def("set_proximity", (void(*)(float,float,float,float))&lua_set_proximity), luabind::def("set_proximity", (void(*)(float,float,float,float,float,float))&lua_set_proximity), + luabind::def("set_proximity", (void(*)(float,float,float,float,float,float,bool))&lua_set_proximity), luabind::def("clear_proximity", &lua_clear_proximity), luabind::def("enable_proximity_say", &lua_enable_proximity_say), luabind::def("disable_proximity_say", &lua_disable_proximity_say), diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 569afcd42..f0276e25b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1670,7 +1670,7 @@ void QuestManager::respawn(int npcTypeID, int grid) { } } -void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, float minz, float maxz) { +void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, float minz, float maxz, bool bSay) { QuestManagerCurrentQuestVars(); if (!owner || !owner->IsNPC()) return; @@ -1683,6 +1683,7 @@ void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, owner->CastToNPC()->proximity->max_y = maxy; owner->CastToNPC()->proximity->min_z = minz; owner->CastToNPC()->proximity->max_z = maxz; + owner->CastToNPC()->proximity->say = bSay; } void QuestManager::clear_proximity() { diff --git a/zone/questmgr.h b/zone/questmgr.h index 0c846c013..df0f0b669 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -150,7 +150,7 @@ public: void setnexthpevent(int at); void setnextinchpevent(int at); void respawn(int npc_type, int grid); - void set_proximity(float minx, float maxx, float miny, float maxy, float minz=-999999, float maxz=999999); + void set_proximity(float minx, float maxx, float miny, float maxy, float minz=-999999, float maxz=999999, bool bSay = false); void clear_proximity(); void enable_proximity_say(); void disable_proximity_say(); From d08b7dafa4e818f4e87a8dc539c7965d295ece73 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Sat, 24 Mar 2018 18:52:20 -0400 Subject: [PATCH 02/14] Export TryMoveAlong() to Perl. --- zone/perl_mob.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index f95deb3de..35f26cfc7 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -9039,6 +9039,35 @@ XS(XS_Mob_GetMeleeMitigation) { XSRETURN(1); } +XS(XS_Mob_TryMoveAlong); +XS(XS_Mob_TryMoveAlong) { + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::TryMoveAlong(THIS, distance, angle, send?)"); + { + Mob* THIS; + float distance = (float)SvNV(ST(1)); + float angle = (float)SvNV(ST(2)); + bool send = true; + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + if (items == 4) + send = (bool)SvTRUE(ST(3)); + + THIS->TryMoveAlong(distance, angle, send); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -9373,6 +9402,7 @@ XS(boot_Mob) newXSproto(strcpy(buf, "IsSilenced"), XS_Mob_IsSilenced, file, "$"); newXSproto(strcpy(buf, "IsAmnesiad"), XS_Mob_IsAmnesiad, file, "$"); newXSproto(strcpy(buf, "GetMeleeMitigation"), XS_Mob_GetMeleeMitigation, file, "$"); + newXSproto(strcpy(buf, "TryMoveAlong"), XS_Mob_TryMoveAlong, file, "$$;$"); XSRETURN_YES; } From a5a660b8288682de78e3c908b92db7244530a0c0 Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Sat, 24 Mar 2018 21:06:56 -0400 Subject: [PATCH 03/14] Fix quest::set_proximity(). --- zone/embparser_api.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 3026358c4..aacedcf2d 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1542,7 +1542,7 @@ XS(XS__set_proximity); XS(XS__set_proximity) { dXSARGS; - if (items != 4 && items != 6) + if (items != 4 && items != 6 && items != 7) Perl_croak(aTHX_ "Usage: set_proximity(min_x, max_x, min_y, max_y [, min_z, max_z], [say])"); float min_x = (float)SvNV(ST(0)); From aff481bd371da37d9ac3ef1a1287f92754e34f91 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 25 Mar 2018 01:12:22 -0400 Subject: [PATCH 04/14] Fix invalid read in con --- zone/client_packet.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fe8af2a98..6483c0cb2 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4833,7 +4833,6 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) mod_consider(tmob, con); QueuePacket(outapp); - safe_delete(outapp); // only wanted to check raid target once // and need con to still be around so, do it here! if (tmob->IsRaidTarget()) { @@ -4880,6 +4879,8 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app) else if ((invisible || invisible_undead || hidden || invisible_animals) && !IsInvisible(tmob)) Message_StringID(10, SUSPECT_SEES_YOU); + safe_delete(outapp); + return; } From 137d2d723d9ff86af9faeaf706267b6a2efb80f0 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 25 Mar 2018 01:16:06 -0400 Subject: [PATCH 05/14] Initialize AISpellVar --- zone/npc.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/zone/npc.cpp b/zone/npc.cpp index b9aec3abb..edd6e5673 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -399,6 +399,19 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if raid_target = d->raid_target; ignore_despawn = d->ignore_despawn; m_targetable = !d->untargetable; + + AISpellVar.fail_recast = RuleI(Spells, AI_SpellCastFinishedFailRecast); + AISpellVar.engaged_no_sp_recast_min = RuleI(Spells, AI_EngagedNoSpellMinRecast); + AISpellVar.engaged_no_sp_recast_max = RuleI(Spells, AI_EngagedNoSpellMaxRecast); + AISpellVar.engaged_beneficial_self_chance = RuleI(Spells, AI_EngagedBeneficialSelfChance); + AISpellVar.engaged_beneficial_other_chance = RuleI(Spells, AI_EngagedBeneficialOtherChance); + AISpellVar.engaged_detrimental_chance = RuleI(Spells, AI_EngagedDetrimentalChance); + AISpellVar.pursue_no_sp_recast_min = RuleI(Spells, AI_PursueNoSpellMinRecast); + AISpellVar.pursue_no_sp_recast_max = RuleI(Spells, AI_PursueNoSpellMaxRecast); + AISpellVar.pursue_detrimental_chance = RuleI(Spells, AI_PursueDetrimentalChance); + AISpellVar.idle_no_sp_recast_min = RuleI(Spells, AI_IdleNoSpellMinRecast); + AISpellVar.idle_no_sp_recast_max = RuleI(Spells, AI_IdleNoSpellMaxRecast); + AISpellVar.idle_beneficial_chance = RuleI(Spells, AI_IdleBeneficialChance); } NPC::~NPC() From 5b5c3a08e62f4ddd2017f4441cb3d5949a4ce38c Mon Sep 17 00:00:00 2001 From: Kinglykrab Date: Sun, 25 Mar 2018 14:33:38 -0400 Subject: [PATCH 06/14] Fix TryMoveAlong() in Perl. - Didn't have 4th option accounted for (my bad). --- zone/perl_mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 35f26cfc7..cb30cf809 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -9402,7 +9402,7 @@ XS(boot_Mob) newXSproto(strcpy(buf, "IsSilenced"), XS_Mob_IsSilenced, file, "$"); newXSproto(strcpy(buf, "IsAmnesiad"), XS_Mob_IsAmnesiad, file, "$"); newXSproto(strcpy(buf, "GetMeleeMitigation"), XS_Mob_GetMeleeMitigation, file, "$"); - newXSproto(strcpy(buf, "TryMoveAlong"), XS_Mob_TryMoveAlong, file, "$$;$"); + newXSproto(strcpy(buf, "TryMoveAlong"), XS_Mob_TryMoveAlong, file, "$$$;$"); XSRETURN_YES; } From e4e40505b6abeac1ceb44e3b1a67500e40fd5145 Mon Sep 17 00:00:00 2001 From: Uleat Date: Mon, 26 Mar 2018 03:38:08 -0400 Subject: [PATCH 07/14] Removal of test code --- zone/client.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index cd2c7d478..16157c807 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7578,27 +7578,7 @@ void Client::JoinGroupXTargets(Group *g) if (!g) return; - // test code for merge crashes - hopefully gcc won't optimize these out... - auto c1 = GetXTargetAutoMgr()->get_list().empty(); - auto c2 = GetXTargetAutoMgr()->get_list().size(); - auto c3 = GetXTargetAutoMgr()->get_list().begin(); - auto c4 = GetXTargetAutoMgr()->get_list().end(); - auto c5 = GetXTargetAutoMgr()->get_list().rbegin(); - auto c6 = GetXTargetAutoMgr()->get_list().rend(); - - auto g1 = g->GetXTargetAutoMgr()->get_list().empty(); - auto g2 = g->GetXTargetAutoMgr()->get_list().size(); - auto g3 = g->GetXTargetAutoMgr()->get_list().begin(); - auto g4 = g->GetXTargetAutoMgr()->get_list().end(); - auto g5 = g->GetXTargetAutoMgr()->get_list().rbegin(); - auto g6 = g->GetXTargetAutoMgr()->get_list().rend(); - if (!GetXTargetAutoMgr()->empty()) { - Log(Logs::Detail, Logs::Error, "XTarget Merge[clt] empty=%s, size=%u, (begin==end)=%s, (rbegin==rend)=%s", - (c1?"true":"false"), c2, (c3==c4?"true":"false"), (c5==c6?"true":"false")); - Log(Logs::Detail, Logs::Error, "XTarget Merge[grp] empty=%s, size=%u, (begin==end)=%s, (rbegin==rend)=%s", - (g1?"true":"false"), g2, (g3==g4?"true":"false"), (g5==g6?"true":"false")); - g->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr()); GetXTargetAutoMgr()->clear(); RemoveAutoXTargets(); From 2d20d5858e5acfad83b17b89eddcf5e775434af5 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 28 Mar 2018 16:06:45 -0400 Subject: [PATCH 08/14] Fix for a few of the Cast on Fade effects to make sure the trigger spell hits the correct target type. --- zone/spell_effects.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 5f151e487..1cb376e98 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3659,7 +3659,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster) case SE_CastOnFadeEffectNPC: case SE_CastOnFadeEffectAlways: { if (buff.ticsremaining == 0) { - SpellOnTarget(spells[buff.spellid].base[i], this); + SpellFinished(spells[buff.spellid].base[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[buff.spellid].base[i]].ResistDiff); } break; } From 0f3fbc3883d86009a6d3de9a5d67a9fcd4a6a866 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Wed, 28 Mar 2018 17:07:15 -0400 Subject: [PATCH 09/14] Add Change Log message --- changelog.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.txt b/changelog.txt index f2f200fa6..33848c7d8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,10 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 03/28/2018 == +Kayen: SE_CastOnFadeEffect, SE_CastOnFadeEffectNPC, SE_CastOnFadeEffectAlway triggered spell will now hit +the correct targets. + + == 03/07/2018 == Uleat: Added command '#ucs' to force a reconnect to UCS server. - Works in place of client auto-reconnect packet in zones where feature is unsupported From e594b7eac60db2ae9edd0f0b5ca5d53836baaa72 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Thu, 29 Mar 2018 18:28:36 -0400 Subject: [PATCH 10/14] Fix NPC chance on fishing (100 = 100% chance now) --- zone/forage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/forage.cpp b/zone/forage.cpp index 4dc3bc04f..81dd264bd 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -278,7 +278,7 @@ void Client::GoFish() //check for add NPC if (npc_chance > 0 && npc_id) { - if (npc_chance < zone->random.Int(0, 99)) { + if (zone->random.Roll(npc_chance)) { const NPCType *tmp = database.LoadNPCTypesData(npc_id); if (tmp != nullptr) { auto positionNPC = GetPosition(); From 3081f7b24fff7310bf427d72b29cfd3d625c71da Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 8 Apr 2018 18:26:25 -0400 Subject: [PATCH 11/14] Pets/NPCs don't use bane damage Adds rule NPC:UseBaneDamage (defaults to false) --- common/ruletypes.h | 3 ++- zone/attack.cpp | 15 +++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 6484a8586..f9c3b3c99 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -513,7 +513,8 @@ RULE_INT(NPC, MinorNPCCorpseDecayTimeMS, 450000) //level<55 RULE_INT(NPC, MajorNPCCorpseDecayTimeMS, 1500000) //level>=55 RULE_INT(NPC, CorpseUnlockTimer, 150000) RULE_INT(NPC, EmptyNPCCorpseDecayTimeMS, 0) -RULE_BOOL (NPC, UseItemBonusesForNonPets, true) +RULE_BOOL(NPC, UseItemBonusesForNonPets, true) +RULE_BOOL(NPC, UseBaneDamage, false) RULE_INT(NPC, SayPauseTimeInSec, 5) RULE_INT(NPC, OOCRegen, 0) RULE_BOOL(NPC, BuffFriends, false) diff --git a/zone/attack.cpp b/zone/attack.cpp index 26121134e..817a8e359 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2000,14 +2000,17 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool //if NPCs can't inheriently hit the target we don't add bane/magic dmg which isn't exactly the same as PCs int eleBane = 0; if (weapon) { - if (weapon->BaneDmgBody == other->GetBodyType()) { - eleBane += weapon->BaneDmgAmt; - } - - if (weapon->BaneDmgRace == other->GetRace()) { - eleBane += weapon->BaneDmgRaceAmt; + if (RuleB(NPC, UseBaneDamage)) { + if (weapon->BaneDmgBody == other->GetBodyType()) { + eleBane += weapon->BaneDmgAmt; + } + + if (weapon->BaneDmgRace == other->GetRace()) { + eleBane += weapon->BaneDmgRaceAmt; + } } + // I don't think NPCs use this either .... if (weapon->ElemDmgAmt) { eleBane += (weapon->ElemDmgAmt * other->ResistSpell(weapon->ElemDmgType, 0, this) / 100); } From 9344896238129d78b87a652bfae525c30333231c Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Sun, 8 Apr 2018 18:38:34 -0400 Subject: [PATCH 12/14] /pet attack is range limited --- common/ruletypes.h | 1 + zone/client_packet.cpp | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index f9c3b3c99..9846273bf 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -547,6 +547,7 @@ RULE_INT(Aggro, IntAggroThreshold, 75) // Int <= this will aggro regardless of l RULE_BOOL(Aggro, AllowTickPulling, false) // tick pulling is an exploit in an NPC's call for help fixed sometime in 2006 on live RULE_BOOL(Aggro, UseLevelAggro, true) // Level 18+ and Undead will aggro regardless of level difference. (this will disabled Rule:IntAggroThreshold if set to true) RULE_INT(Aggro, ClientAggroCheckInterval, 6) // Interval in which clients actually check for aggro - in seconds +RULE_REAL(Aggro, PetAttackRange, 40000.0) // max squared range /pet attack works at default is 200 RULE_CATEGORY_END() RULE_CATEGORY(TaskSystem) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 6483c0cb2..d8a514797 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -10046,6 +10046,14 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) break; } + // default range is 200, takes Z into account + // really they do something weird where they're added to the aggro list then remove them + // and will attack if they come in range -- too lazy, lets remove exploits for now + if (DistanceSquared(mypet->GetPosition(), target->GetPosition()) >= RuleR(Aggro, PetAttackRange)) { + // they say they're attacking then remove on live ... so they don't really say anything in this case ... + break; + } + if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { if (target != this && DistanceSquaredNoZ(mypet->GetPosition(), target->GetPosition()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { if (mypet->IsPetStop()) { From 714f8172ecc26aa7d5ed9dc91685ce90ee4a5f97 Mon Sep 17 00:00:00 2001 From: Uleat Date: Wed, 11 Apr 2018 18:49:49 -0400 Subject: [PATCH 13/14] Added CanDoCombat() checks to certain actions --- zone/client_packet.cpp | 5 +++++ zone/forage.cpp | 29 +++++++++++++++++------------ zone/npc.cpp | 3 ++- 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d8a514797..2c5d5f5f6 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -13712,6 +13712,11 @@ void Client::Handle_OP_Taunt(const EQApplicationPacket *app) if (GetTarget() == nullptr || !GetTarget()->IsNPC()) return; + if (!zone->CanDoCombat()) { + Message(13, "You cannot taunt in a no combat zone."); + return; + } + Taunt(GetTarget()->CastToNPC(), false); return; } diff --git a/zone/forage.cpp b/zone/forage.cpp index 81dd264bd..7adb245ee 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -279,21 +279,26 @@ void Client::GoFish() //check for add NPC if (npc_chance > 0 && npc_id) { if (zone->random.Roll(npc_chance)) { - const NPCType *tmp = database.LoadNPCTypesData(npc_id); - if (tmp != nullptr) { - auto positionNPC = GetPosition(); - positionNPC.x = positionNPC.x + 3; - auto npc = new NPC(tmp, nullptr, positionNPC, FlyMode3); - npc->AddLootTable(); - if (npc->DropsGlobalLoot()) - npc->CheckGlobalLootTables(); + if (zone->CanDoCombat()) { + const NPCType *tmp = database.LoadNPCTypesData(npc_id); + if (tmp != nullptr) { + auto positionNPC = GetPosition(); + positionNPC.x = positionNPC.x + 3; + auto npc = new NPC(tmp, nullptr, positionNPC, FlyMode3); + npc->AddLootTable(); + if (npc->DropsGlobalLoot()) + npc->CheckGlobalLootTables(); - npc->AddToHateList(this, 1, 0, false); // no help yelling + npc->AddToHateList(this, 1, 0, false); // no help yelling - entity_list.AddNPC(npc); + entity_list.AddNPC(npc); - Message(MT_Emote, - "You fish up a little more than you bargained for..."); + Message(MT_Emote, + "You fish up a little more than you bargained for..."); + } + } + else { + Message(MT_Emote, "You notice something lurking just below the water's surface..."); } } } diff --git a/zone/npc.cpp b/zone/npc.cpp index edd6e5673..96df168ff 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -1534,7 +1534,8 @@ void NPC::PickPocket(Client* thief) } if(zone->random.Roll(5)) { - AddToHateList(thief, 50); + if (zone->CanDoCombat()) + AddToHateList(thief, 50); Say("Stop thief!"); thief->Message(13, "You are noticed trying to steal!"); thief->SendPickPocketResponse(this, 0, PickPocketFailed); From fb7362c092da9ad9fcf51b4671640db08ac7a72d Mon Sep 17 00:00:00 2001 From: Uleat Date: Thu, 12 Apr 2018 20:14:02 -0400 Subject: [PATCH 14/14] Added bot command 'BotStopMeleeLevel' --- common/version.h | 2 +- .../sql/git/bots/bots_db_update_manifest.txt | 1 + .../2018_04_12_bots_stop_melee_level.sql | 8 +++ zone/bot.cpp | 32 +++++++++++- zone/bot.h | 3 ++ zone/bot_command.cpp | 51 ++++++++++++++++++- zone/bot_command.h | 1 + zone/bot_database.cpp | 49 +++++++++++++++--- zone/bot_database.h | 3 ++ 9 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 utils/sql/git/bots/required/2018_04_12_bots_stop_melee_level.sql diff --git a/common/version.h b/common/version.h index e003dc33f..a222c057c 100644 --- a/common/version.h +++ b/common/version.h @@ -32,7 +32,7 @@ #define CURRENT_BINARY_DATABASE_VERSION 9122 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9018 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9019 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index e072dcc4b..2bf71af18 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -17,6 +17,7 @@ 9016|2017_02_26_bots_spell_casting_chances_update.sql|SHOW TABLES LIKE 'bot_spell_casting_chances'|empty| 9017|2017_03_26_bots_spells_id_fix_for_saved_shadowknight_bots.sql|SELECT * FROM `bot_data` WHERE `class` = '5' AND `spells_id` = '3004'|not_empty| 9018|2018_02_02_Bot_Spells_Min_Max_HP.sql|SHOW COLUMNS FROM `bot_spells_entries` LIKE 'min_hp'|empty| +9019|2018_04_12_bots_stop_melee_level.sql|SHOW COLUMNS FROM `bot_data` LIKE 'stop_melee_level'|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/required/2018_04_12_bots_stop_melee_level.sql b/utils/sql/git/bots/required/2018_04_12_bots_stop_melee_level.sql new file mode 100644 index 000000000..b294b29bf --- /dev/null +++ b/utils/sql/git/bots/required/2018_04_12_bots_stop_melee_level.sql @@ -0,0 +1,8 @@ +ALTER TABLE `bot_data` ADD COLUMN `stop_melee_level` TINYINT(3) UNSIGNED NOT NULL DEFAULT '255' AFTER `follow_distance`; + +INSERT INTO `bot_command_settings`(`bot_command`, `access`, `aliases`) VALUES ('botstopmeleelevel', '0', 'sml'); + +SELECT @csml_raw := (SELECT `rule_value` FROM `rule_values` WHERE `rule_name` LIKE 'Bots:CasterStopMeleeLevel' LIMIT 1); +SELECT @csml_value := IF((SELECT @csml_raw REGEXP '^[0-9]+$') = '1', @csml_raw, '13'); + +UPDATE `bot_data` SET `stop_melee_level` = @csml_value WHERE `class` IN ('2', '6', '10', '11', '12', '13', '14'); diff --git a/zone/bot.cpp b/zone/bot.cpp index 8751d40f7..01bbb1593 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -78,6 +78,11 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm SetPauseAI(false); rest_timer.Disable(); SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT); + if (IsCasterClass(GetClass())) + SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel)); + else + SetStopMeleeLevel(255); + // Do this once and only in this constructor GenerateAppearance(); GenerateBaseStats(); @@ -151,6 +156,11 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to rest_timer.Disable(); SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT); + if (IsCasterClass(GetClass())) + SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel)); + else + SetStopMeleeLevel(255); + strcpy(this->name, this->GetCleanName()); memset(&_botInspectMessage, 0, sizeof(InspectMessage_Struct)); @@ -2041,6 +2051,13 @@ void Bot::SetTarget(Mob* mob) { } } +void Bot::SetStopMeleeLevel(uint8 level) { + if (IsCasterClass(GetClass()) || IsSpellFighterClass(GetClass())) + _stopMeleeLevel = level; + else + _stopMeleeLevel = 255; +} + void Bot::SetGuardMode() { WipeHateList(); SetTarget(nullptr); @@ -2386,11 +2403,20 @@ void Bot::AI_Process() { // Calculate casting distance float caster_distance_max = 0.0f; { - if (GetLevel() >= RuleI(Bots, CasterStopMeleeLevel)) { + if (GetLevel() >= GetStopMeleeLevel()) { switch (GetClass()) { case CLERIC: caster_distance_max = 1156.0f; // as DSq value (34 units) break; + case PALADIN: + caster_distance_max = 576.0f; // as DSq value (24 units) + break; + case RANGER: + caster_distance_max = 784.0f; // as DSq value (28 units) + break; + case SHADOWKNIGHT: + caster_distance_max = 676.0f; // as DSq value (26 units) + break; case DRUID: caster_distance_max = 1764.0f; // as DSq value (42 units) break; @@ -2409,7 +2435,11 @@ void Bot::AI_Process() { case ENCHANTER: caster_distance_max = 2500.0f; // as DSq value (50 units) break; + case BEASTLORD: + caster_distance_max = 900.0f; // as DSq value (30 units) + break; default: + // pure melee classes (and BARD) do not get this option break; } } diff --git a/zone/bot.h b/zone/bot.h index 1d38e5f57..c72e83f5c 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -406,6 +406,8 @@ public: bool AIHealRotation(Mob* tar, bool useFastHeals); bool GetPauseAI() { return _pauseAI; } void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; } + uint8 GetStopMeleeLevel() { return _stopMeleeLevel; } + void SetStopMeleeLevel(uint8 level); void SetGuardMode(); // Mob AI Virtual Override Methods @@ -742,6 +744,7 @@ private: bool _altoutofcombatbehavior; bool _showhelm; bool _pauseAI; + uint8 _stopMeleeLevel; // Private "base stats" Members int32 _baseMR; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index a91624df1..0a286e8f5 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1350,6 +1350,7 @@ int bot_command_init(void) bot_command_add("botreport", "Orders a bot to report its readiness", 0, bot_subcommand_bot_report) || bot_command_add("botspawn", "Spawns a created bot", 0, bot_subcommand_bot_spawn) || bot_command_add("botstance", "Changes the stance of a bot", 0, bot_subcommand_bot_stance) || + bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", 0, bot_subcommand_bot_stop_melee_level) || bot_command_add("botsummon", "Summons bot(s) to your location", 0, bot_subcommand_bot_summon) || bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", 0, bot_subcommand_bot_tattoo) || bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", 0, bot_subcommand_bot_toggle_archer) || @@ -2605,6 +2606,7 @@ void bot_command_bot(Client *c, const Seperator *sep) subcommand_list.push_back("botreport"); subcommand_list.push_back("botspawn"); subcommand_list.push_back("botstance"); + subcommand_list.push_back("botstopmeleelevel"); subcommand_list.push_back("botsummon"); subcommand_list.push_back("bottogglearcher"); subcommand_list.push_back("bottogglehelm"); @@ -4792,7 +4794,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(m_usage, "usage: %s ([class] [value]) ([race] [value]) ([name] [partial-full])", sep->arg[0]); - c->Message(m_note, "Note: filter criteria is orderless and optional"); + c->Message(m_note, "note: filter criteria is orderless and optional"); return; } @@ -5126,6 +5128,53 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep) } } +void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep) +{ + if (helper_command_alias_fail(c, "bot_subcommand_bot_stop_melee_level", sep->arg[0], "botstopmeleelevel")) + return; + if (helper_is_help_or_usage(sep->arg[1])) { + c->Message(m_usage, "usage: %s [current | reset | sync | value: 0-255]", sep->arg[0]); + c->Message(m_note, "note: Only caster and spell-casting fighter class bots may be modified"); + c->Message(m_note, "note: Use [reset] to set stop melee level to server rule"); + c->Message(m_note, "note: Use [sync] to set stop melee level to current bot level"); + return; + } + + auto my_bot = ActionableBots::AsTarget_ByBot(c); + if (!my_bot) { + c->Message(m_fail, "You must a bot that you own to use this command"); + return; + } + if (!IsCasterClass(my_bot->GetClass()) && !IsSpellFighterClass(my_bot->GetClass())) { + c->Message(m_fail, "You must a caster or spell-casting fighter class bot to use this command"); + return; + } + + uint8 sml = RuleI(Bots, CasterStopMeleeLevel); + + if (sep->IsNumber(1)) { + sml = atoi(sep->arg[1]); + } + else if (!strcasecmp(sep->arg[1], "sync")) { + sml = my_bot->GetLevel(); + } + else if (!strcasecmp(sep->arg[1], "current")) { + c->Message(m_message, "My current melee stop level is %u", my_bot->GetStopMeleeLevel()); + return; + } + else if (strcasecmp(sep->arg[1], "reset")) { + c->Message(m_fail, "A [current] or [reset] argument, or numeric [value] is required to use this command"); + return; + } + // [reset] falls through with initialization value + + my_bot->SetStopMeleeLevel(sml); + if (!botdb.SaveStopMeleeLevel(c->CharacterID(), my_bot->GetBotID(), sml)) + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::SaveStopMeleeLevel(), my_bot->GetCleanName()); + + c->Message(m_action, "Successfully set stop melee level for %s to %u", my_bot->GetCleanName(), sml); +} + void bot_subcommand_bot_summon(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_subcommand_bot_summon", sep->arg[0], "botsummon")) diff --git a/zone/bot_command.h b/zone/bot_command.h index c25c7b0eb..f2c706cac 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -612,6 +612,7 @@ void bot_subcommand_bot_out_of_combat(Client *c, const Seperator *sep); void bot_subcommand_bot_report(Client *c, const Seperator *sep); void bot_subcommand_bot_spawn(Client *c, const Seperator *sep); void bot_subcommand_bot_stance(Client *c, const Seperator *sep); +void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep); void bot_subcommand_bot_summon(Client *c, const Seperator *sep); void bot_subcommand_bot_tattoo(Client *c, const Seperator *sep); void bot_subcommand_bot_toggle_archer(Client *c, const Seperator *sep); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 4c5aabb82..10449ab7a 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -340,7 +340,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) " `disease`," /* not in-use[41] */ " `corruption`," /* not in-use[42] */ " `show_helm`," // 43 - " `follow_distance`" // 44 + " `follow_distance`," // 44 + " `stop_melee_level`" // 45 " FROM `bot_data`" " WHERE `bot_id` = '%u'" " LIMIT 1", @@ -397,13 +398,16 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct); if (loaded_bot) { loaded_bot->SetShowHelm((atoi(row[43]) > 0 ? true : false)); + uint32 bfd = atoi(row[44]); if (bfd < 1) bfd = 1; if (bfd > BOT_FOLLOW_DISTANCE_DEFAULT_MAX) bfd = BOT_FOLLOW_DISTANCE_DEFAULT_MAX; loaded_bot->SetFollowDistance(bfd); - + + uint8 sml = atoi(row[45]); + loaded_bot->SetStopMeleeLevel(sml); } return true; @@ -457,7 +461,8 @@ bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id) " `disease`," " `corruption`," " `show_helm`," - " `follow_distance`" + " `follow_distance`," + " `stop_melee_level`" ")" " VALUES (" "'%u'," /* owner_id */ @@ -501,7 +506,8 @@ bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id) " '%i'," /* disease */ " '%i'," /* corruption */ " '1'," /* show_helm */ - " '%i'" /* follow_distance */ + " '%i'," /* follow_distance */ + " '%u'" /* stop_melee_level */ ")", bot_inst->GetBotOwnerCharacterID(), bot_inst->GetBotSpellID(), @@ -540,7 +546,8 @@ bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id) bot_inst->GetPR(), bot_inst->GetDR(), bot_inst->GetCorrup(), - BOT_FOLLOW_DISTANCE_DEFAULT + BOT_FOLLOW_DISTANCE_DEFAULT, + (IsCasterClass(bot_inst->GetClass()) ? (uint8)RuleI(Bots, CasterStopMeleeLevel) : 255) ); auto results = QueryDatabase(query); if (!results.Success()) @@ -599,7 +606,8 @@ bool BotDatabase::SaveBot(Bot* bot_inst) " `disease` = '%i'," " `corruption` = '%i'," " `show_helm` = '%i'," - " `follow_distance` = '%i'" + " `follow_distance` = '%i'," + " `stop_melee_level` = '%u'" " WHERE `bot_id` = '%u'", bot_inst->GetBotOwnerCharacterID(), bot_inst->GetBotSpellID(), @@ -641,6 +649,7 @@ bool BotDatabase::SaveBot(Bot* bot_inst) bot_inst->GetBaseCorrup(), ((bot_inst->GetShowHelm()) ? (1) : (0)), bot_inst->GetFollowDistance(), + bot_inst->GetStopMeleeLevel(), bot_inst->GetBotID() ); auto results = QueryDatabase(query); @@ -2026,7 +2035,8 @@ bool BotDatabase::CreateCloneBot(const uint32 owner_id, const uint32 bot_id, con " `disease`," " `corruption`," " `show_helm`," - " `follow_distance`" + " `follow_distance`," + " `stop_melee_level`" ")" " SELECT" " bd.`owner_id`," @@ -2073,7 +2083,8 @@ bool BotDatabase::CreateCloneBot(const uint32 owner_id, const uint32 bot_id, con " bd.`disease`," " bd.`corruption`," " bd.`show_helm`," - " bd.`follow_distance`" + " bd.`follow_distance`," + " bd.`stop_melee_level`" " FROM `bot_data` bd" " WHERE" " bd.`owner_id` = '%u'" @@ -2153,6 +2164,27 @@ bool BotDatabase::CreateCloneBotInventory(const uint32 owner_id, const uint32 bo return true; } +bool BotDatabase::SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id, const uint8 sml_value) +{ + if (!owner_id || !bot_id) + return false; + + query = StringFormat( + "UPDATE `bot_data`" + " SET `stop_melee_level` = '%u'" + " WHERE `owner_id` = '%u'" + " AND `bot_id` = '%u'", + sml_value, + owner_id, + bot_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + /* Bot bot-group functions */ bool BotDatabase::QueryBotGroupExistence(const std::string& group_name, bool& extant_flag) @@ -2852,6 +2884,7 @@ const char* BotDatabase::fail::SaveFollowDistance() { return "Failed to save fol const char* BotDatabase::fail::SaveAllFollowDistances() { return "Failed to save all follow distances"; } const char* BotDatabase::fail::CreateCloneBot() { return "Failed to create clone bot"; } const char* BotDatabase::fail::CreateCloneBotInventory() { return "Failed to create clone bot inventory"; } +const char* BotDatabase::fail::SaveStopMeleeLevel() { return "Failed to save stop melee level"; } /* fail::Bot bot-group functions */ const char* BotDatabase::fail::QueryBotGroupExistence() { return "Failed to query bot-group existence"; } diff --git a/zone/bot_database.h b/zone/bot_database.h index 073fd3b5f..2361c0cf1 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -143,6 +143,8 @@ public: bool CreateCloneBot(const uint32 owner_id, const uint32 bot_id, const std::string& clone_name, uint32& clone_id); bool CreateCloneBotInventory(const uint32 owner_id, const uint32 bot_id, const uint32 clone_id); + bool SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id, const uint8 sml_value); + /* Bot bot-group functions */ bool QueryBotGroupExistence(const std::string& botgroup_name, bool& extant_flag); @@ -253,6 +255,7 @@ public: static const char* SaveAllFollowDistances(); static const char* CreateCloneBot(); static const char* CreateCloneBotInventory(); + static const char* SaveStopMeleeLevel(); /* fail::Bot bot-group functions */ static const char* QueryBotGroupExistence();