mirror of
https://github.com/EQEmu/Server.git
synced 2026-01-05 04:13:52 +00:00
commit
7d99becca9
@ -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
|
||||
|
||||
@ -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)
|
||||
@ -546,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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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');
|
||||
@ -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);
|
||||
}
|
||||
|
||||
32
zone/bot.cpp
32
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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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: <target_bot> %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 <target> 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 <target> 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"))
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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"; }
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -10045,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()) {
|
||||
@ -13703,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;
|
||||
}
|
||||
|
||||
@ -1542,8 +1542,8 @@ XS(XS__set_proximity);
|
||||
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])");
|
||||
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));
|
||||
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;
|
||||
|
||||
@ -278,22 +278,27 @@ void Client::GoFish()
|
||||
|
||||
//check for add NPC
|
||||
if (npc_chance > 0 && npc_id) {
|
||||
if (npc_chance < zone->random.Int(0, 99)) {
|
||||
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->random.Roll(npc_chance)) {
|
||||
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...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
16
zone/npc.cpp
16
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()
|
||||
@ -1521,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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user