Merge branch 'master' into master

This commit is contained in:
Paul Coene
2019-02-28 15:05:18 -05:00
committed by GitHub
49 changed files with 1604 additions and 765 deletions
+3 -1
View File
@@ -4514,7 +4514,9 @@ void Mob::ApplyMeleeDamageMods(uint16 skill, int &damage, Mob *defender, ExtraAt
if (defender->IsClient() && defender->GetClass() == WARRIOR)
dmgbonusmod -= 5;
// 168 defensive
dmgbonusmod += (defender->spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect);
dmgbonusmod += (defender->spellbonuses.MeleeMitigationEffect +
defender->itembonuses.MeleeMitigationEffect +
defender->aabonuses.MeleeMitigationEffect);
}
damage += damage * dmgbonusmod / 100;
+336 -170
View File
@@ -29,7 +29,9 @@
extern volatile bool is_zone_loaded;
// This constructor is used during the bot create command
Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1) {
Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) {
GiveNPCTypeData(npcTypeData);
if(botOwner) {
this->SetBotOwner(botOwner);
this->_botOwnerCharacterID = botOwner->CharacterID();
@@ -46,23 +48,23 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
_lastTotalPlayTime = 0;
_startTotalPlayTime = time(&_startTotalPlayTime);
_lastZoneId = 0;
_baseMR = npcTypeData.MR;
_baseCR = npcTypeData.CR;
_baseDR = npcTypeData.DR;
_baseFR = npcTypeData.FR;
_basePR = npcTypeData.PR;
_baseCorrup = npcTypeData.Corrup;
_baseAC = npcTypeData.AC;
_baseSTR = npcTypeData.STR;
_baseSTA = npcTypeData.STA;
_baseDEX = npcTypeData.DEX;
_baseAGI = npcTypeData.AGI;
_baseINT = npcTypeData.INT;
_baseWIS = npcTypeData.WIS;
_baseCHA = npcTypeData.CHA;
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
_baseMR = npcTypeData->MR;
_baseCR = npcTypeData->CR;
_baseDR = npcTypeData->DR;
_baseFR = npcTypeData->FR;
_basePR = npcTypeData->PR;
_baseCorrup = npcTypeData->Corrup;
_baseAC = npcTypeData->AC;
_baseSTR = npcTypeData->STR;
_baseSTA = npcTypeData->STA;
_baseDEX = npcTypeData->DEX;
_baseAGI = npcTypeData->AGI;
_baseINT = npcTypeData->INT;
_baseWIS = npcTypeData->WIS;
_baseCHA = npcTypeData->CHA;
_baseATK = npcTypeData->ATK;
_baseRace = npcTypeData->race;
_baseGender = npcTypeData->gender;
RestRegenHP = 0;
RestRegenMana = 0;
RestRegenEndurance = 0;
@@ -80,6 +82,7 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
SetShowHelm(true);
SetPauseAI(false);
rest_timer.Disable();
ping_timer.Disable();
SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT);
if (IsCasterClass(GetClass()))
SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel));
@@ -104,9 +107,11 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
}
// This constructor is used when the bot is loaded out of the database
Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData)
: NPC(&npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1)
Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData)
: NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1)
{
GiveNPCTypeData(npcTypeData);
this->_botOwnerCharacterID = botOwnerCharacterID;
if(this->_botOwnerCharacterID > 0)
this->SetBotOwner(entity_list.GetClientByCharID(this->_botOwnerCharacterID));
@@ -122,25 +127,25 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
_startTotalPlayTime = time(&_startTotalPlayTime);
_lastZoneId = lastZoneId;
berserk = false;
_baseMR = npcTypeData.MR;
_baseCR = npcTypeData.CR;
_baseDR = npcTypeData.DR;
_baseFR = npcTypeData.FR;
_basePR = npcTypeData.PR;
_baseCorrup = npcTypeData.Corrup;
_baseAC = npcTypeData.AC;
_baseSTR = npcTypeData.STR;
_baseSTA = npcTypeData.STA;
_baseDEX = npcTypeData.DEX;
_baseAGI = npcTypeData.AGI;
_baseINT = npcTypeData.INT;
_baseWIS = npcTypeData.WIS;
_baseCHA = npcTypeData.CHA;
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
current_hp = npcTypeData.current_hp;
current_mana = npcTypeData.Mana;
_baseMR = npcTypeData->MR;
_baseCR = npcTypeData->CR;
_baseDR = npcTypeData->DR;
_baseFR = npcTypeData->FR;
_basePR = npcTypeData->PR;
_baseCorrup = npcTypeData->Corrup;
_baseAC = npcTypeData->AC;
_baseSTR = npcTypeData->STR;
_baseSTA = npcTypeData->STA;
_baseDEX = npcTypeData->DEX;
_baseAGI = npcTypeData->AGI;
_baseINT = npcTypeData->INT;
_baseWIS = npcTypeData->WIS;
_baseCHA = npcTypeData->CHA;
_baseATK = npcTypeData->ATK;
_baseRace = npcTypeData->race;
_baseGender = npcTypeData->gender;
current_hp = npcTypeData->current_hp;
current_mana = npcTypeData->Mana;
RestRegenHP = 0;
RestRegenMana = 0;
RestRegenEndurance = 0;
@@ -158,10 +163,11 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
if (!stance_flag && bot_owner)
bot_owner->Message(13, "Could not locate stance for '%s'", GetCleanName());
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive));
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == EQEmu::constants::stanceAggressive));
SetPauseAI(false);
rest_timer.Disable();
ping_timer.Disable();
SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT);
if (IsCasterClass(GetClass()))
SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel));
@@ -313,112 +319,252 @@ bool Bot::IsStanding() {
return result;
}
NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack) {
NPCType BotNPCType;
int CopyLength = 0;
CopyLength = botName.copy(BotNPCType.name, 63);
BotNPCType.name[CopyLength] = '\0';
CopyLength = 0;
CopyLength = botLastName.copy(BotNPCType.lastname, 69);
BotNPCType.lastname[CopyLength] = '\0';
CopyLength = 0;
BotNPCType.npc_spells_id = botSpellsID;
BotNPCType.level = botLevel;
BotNPCType.race = botRace;
BotNPCType.class_ = botClass;
BotNPCType.gender = gender;
BotNPCType.size = size;
BotNPCType.luclinface = face;
BotNPCType.hairstyle = hairStyle;
BotNPCType.haircolor = hairColor;
BotNPCType.eyecolor1 = eyeColor;
BotNPCType.eyecolor2 = eyeColor2;
BotNPCType.beardcolor = beardColor;
BotNPCType.beard = beard;
BotNPCType.drakkin_heritage = drakkinHeritage;
BotNPCType.drakkin_tattoo = drakkinTattoo;
BotNPCType.drakkin_details = drakkinDetails;
BotNPCType.current_hp = hp;
BotNPCType.Mana = mana;
BotNPCType.MR = mr;
BotNPCType.CR = cr;
BotNPCType.DR = dr;
BotNPCType.FR = fr;
BotNPCType.PR = pr;
BotNPCType.Corrup = corrup;
BotNPCType.AC = ac;
BotNPCType.STR = str;
BotNPCType.STA = sta;
BotNPCType.DEX = dex;
BotNPCType.AGI = agi;
BotNPCType.INT = _int;
BotNPCType.WIS = wis;
BotNPCType.CHA = cha;
BotNPCType.ATK = attack;
BotNPCType.npc_id = 0;
BotNPCType.texture = 0;
BotNPCType.d_melee_texture1 = 0;
BotNPCType.d_melee_texture2 = 0;
BotNPCType.qglobal = false;
BotNPCType.attack_speed = 0;
BotNPCType.runspeed = 0.7f;
BotNPCType.bodytype = 1;
BotNPCType.findable = 0;
BotNPCType.hp_regen = 1;
BotNPCType.mana_regen = 1;
BotNPCType.maxlevel = botLevel;
BotNPCType.light = 0; // due to the way that bots are coded..this is sent post-spawn
return BotNPCType;
NPCType *Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack) {
auto bot_npc_type = new NPCType{ 0 };
int copy_length = 0;
copy_length = botName.copy(bot_npc_type->name, 63);
bot_npc_type->name[copy_length] = '\0';
copy_length = 0;
copy_length = botLastName.copy(bot_npc_type->lastname, 69);
bot_npc_type->lastname[copy_length] = '\0';
copy_length = 0;
bot_npc_type->current_hp = hp;
bot_npc_type->max_hp = hp;
bot_npc_type->size = size;
bot_npc_type->runspeed = 0.7f;
bot_npc_type->gender = gender;
bot_npc_type->race = botRace;
bot_npc_type->class_ = botClass;
bot_npc_type->bodytype = 1;
bot_npc_type->deity = EQEmu::deity::DeityAgnostic;
bot_npc_type->level = botLevel;
//bot_npc_type->npc_id = 0;
//bot_npc_type->texture = 0;
//bot_npc_type->helmtexture = 0;
//bot_npc_type->herosforgemodel = 0;
//bot_npc_type->loottable_id = 0;
bot_npc_type->npc_spells_id = botSpellsID;
//bot_npc_type->npc_spells_effects_id = 0;
//bot_npc_type->npc_faction_id = 0;
//bot_npc_type->merchanttype = 0;
//bot_npc_type->alt_currency_type = 0;
//bot_npc_type->adventure_template = 0;
//bot_npc_type->trap_template = 0;
//bot_npc_type->light = 0;
bot_npc_type->AC = ac;
bot_npc_type->Mana = mana;
bot_npc_type->ATK = attack;
bot_npc_type->STR = str;
bot_npc_type->STA = sta;
bot_npc_type->DEX = dex;
bot_npc_type->AGI = agi;
bot_npc_type->INT = _int;
bot_npc_type->WIS = wis;
bot_npc_type->CHA = cha;
bot_npc_type->MR = mr;
bot_npc_type->FR = fr;
bot_npc_type->CR = cr;
bot_npc_type->PR = pr;
bot_npc_type->DR = dr;
bot_npc_type->Corrup = corrup;
//bot_npc_type->PhR = 0;
bot_npc_type->haircolor = hairColor;
bot_npc_type->beardcolor = beardColor;
bot_npc_type->eyecolor1 = eyeColor;
bot_npc_type->eyecolor2 = eyeColor2;
bot_npc_type->hairstyle = hairStyle;
bot_npc_type->luclinface = face;
bot_npc_type->beard = beard;
bot_npc_type->drakkin_heritage = drakkinHeritage;
bot_npc_type->drakkin_tattoo = drakkinTattoo;
bot_npc_type->drakkin_details = drakkinDetails;
//bot_npc_type->armor_tint = { 0 };
//bot_npc_type->min_dmg = 0;
//bot_npc_type->max_dmg = 0;
//bot_npc_type->charm_ac = 0;
//bot_npc_type->charm_min_dmg = 0;
//bot_npc_type->charm_max_dmg = 0;
//bot_npc_type->charm_attack_delay = 0;
//bot_npc_type->charm_accuracy_rating = 0;
//bot_npc_type->charm_avoidance_rating = 0;
//bot_npc_type->charm_atk = 0;
//bot_npc_type->attack_count = 0;
//*bot_npc_type->special_abilities = { 0 };
//bot_npc_type->d_melee_texture1 = 0;
//bot_npc_type->d_melee_texture2 = 0;
//*bot_npc_type->ammo_idfile = { 0 };
//bot_npc_type->prim_melee_type = 0;
//bot_npc_type->sec_melee_type = 0;
//bot_npc_type->ranged_type = 0;
bot_npc_type->hp_regen = 1;
bot_npc_type->mana_regen = 1;
//bot_npc_type->aggroradius = 0;
//bot_npc_type->assistradius = 0;
//bot_npc_type->see_invis = 0;
//bot_npc_type->see_invis_undead = false;
//bot_npc_type->see_hide = false;
//bot_npc_type->see_improved_hide = false;
//bot_npc_type->qglobal = false;
//bot_npc_type->npc_aggro = false;
//bot_npc_type->spawn_limit = 0;
//bot_npc_type->mount_color = 0;
//bot_npc_type->attack_speed = 0.0f;
//bot_npc_type->attack_delay = 0;
//bot_npc_type->accuracy_rating = 0;
//bot_npc_type->avoidance_rating = 0;
//bot_npc_type->findable = false;
bot_npc_type->trackable = true;
//bot_npc_type->slow_mitigation = 0;
bot_npc_type->maxlevel = botLevel;
//bot_npc_type->scalerate = 0;
//bot_npc_type->private_corpse = false;
//bot_npc_type->unique_spawn_by_name = false;
//bot_npc_type->underwater = false;
//bot_npc_type->emoteid = 0;
//bot_npc_type->spellscale = 0.0f;
//bot_npc_type->healscale = 0.0f;
//bot_npc_type->no_target_hotkey = false;
//bot_npc_type->raid_target = false;
//bot_npc_type->armtexture = 0;
//bot_npc_type->bracertexture = 0;
//bot_npc_type->handtexture = 0;
//bot_npc_type->legtexture = 0;
//bot_npc_type->feettexture = 0;
//bot_npc_type->ignore_despawn = false;
bot_npc_type->show_name = true;
//bot_npc_type->untargetable = false;
bot_npc_type->skip_global_loot = true;
//bot_npc_type->rare_spawn = false;
bot_npc_type->stuck_behavior = Ground;
return bot_npc_type;
}
NPCType Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender) {
NPCType Result;
int CopyLength = 0;
CopyLength = botName.copy(Result.name, 63);
Result.name[CopyLength] = '\0';
CopyLength = 0;
CopyLength = botLastName.copy(Result.lastname, 69);
Result.lastname[CopyLength] = '\0';
CopyLength = 0;
Result.level = botLevel;
Result.race = botRace;
Result.class_ = botClass;
Result.gender = gender;
// default values
Result.maxlevel = botLevel;
Result.size = 6.0;
Result.npc_id = 0;
Result.current_hp = 0;
Result.drakkin_details = 0;
Result.drakkin_heritage = 0;
Result.drakkin_tattoo = 0;
Result.runspeed = 0.7f;
Result.bodytype = 1;
Result.findable = 0;
Result.hp_regen = 1;
Result.mana_regen = 1;
Result.texture = 0;
Result.d_melee_texture1 = 0;
Result.d_melee_texture2 = 0;
Result.qglobal = false;
Result.npc_spells_id = 0;
Result.attack_speed = 0;
Result.STR = 75;
Result.STA = 75;
Result.DEX = 75;
Result.AGI = 75;
Result.WIS = 75;
Result.INT = 75;
Result.CHA = 75;
Result.ATK = 75;
Result.MR = 25;
Result.FR = 25;
Result.DR = 15;
Result.PR = 15;
Result.CR = 25;
Result.Corrup = 15;
Result.AC = 12;
return Result;
NPCType *Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender) {
auto bot_npc_type = new NPCType{ 0 };
int copy_length = 0;
copy_length = botName.copy(bot_npc_type->name, 63);
bot_npc_type->name[copy_length] = '\0';
copy_length = 0;
copy_length = botLastName.copy(bot_npc_type->lastname, 69);
bot_npc_type->lastname[copy_length] = '\0';
copy_length = 0;
//bot_npc_type->current_hp = 0;
//bot_npc_type->max_hp = 0;
bot_npc_type->size = 6.0f;
bot_npc_type->runspeed = 0.7f;
bot_npc_type->gender = gender;
bot_npc_type->race = botRace;
bot_npc_type->class_ = botClass;
bot_npc_type->bodytype = 1;
bot_npc_type->deity = EQEmu::deity::DeityAgnostic;
bot_npc_type->level = botLevel;
//bot_npc_type->npc_id = 0;
//bot_npc_type->texture = 0;
//bot_npc_type->helmtexture = 0;
//bot_npc_type->herosforgemodel = 0;
//bot_npc_type->loottable_id = 0;
//bot_npc_type->npc_spells_id = 0;
//bot_npc_type->npc_spells_effects_id = 0;
//bot_npc_type->npc_faction_id = 0;
//bot_npc_type->merchanttype = 0;
//bot_npc_type->alt_currency_type = 0;
//bot_npc_type->adventure_template = 0;
//bot_npc_type->trap_template = 0;
//bot_npc_type->light = 0;
bot_npc_type->AC = 12;
//bot_npc_type->Mana = 0;
bot_npc_type->ATK = 75;
bot_npc_type->STR = 75;
bot_npc_type->STA = 75;
bot_npc_type->DEX = 75;
bot_npc_type->AGI = 75;
bot_npc_type->INT = 75;
bot_npc_type->WIS = 75;
bot_npc_type->CHA = 75;
bot_npc_type->MR = 25;
bot_npc_type->FR = 25;
bot_npc_type->CR = 25;
bot_npc_type->PR = 15;
bot_npc_type->DR = 15;
bot_npc_type->Corrup = 15;
//bot_npc_type->PhR = 0;
//bot_npc_type->haircolor = 0;
//bot_npc_type->beardcolor = 0;
//bot_npc_type->eyecolor1 = 0;
//bot_npc_type->eyecolor2 = 0;
//bot_npc_type->hairstyle = 0;
//bot_npc_type->luclinface = 0;
//bot_npc_type->beard = 0;
//bot_npc_type->drakkin_heritage = 0;
//bot_npc_type->drakkin_tattoo = 0;
//bot_npc_type->drakkin_details = 0;
//bot_npc_type->armor_tint = { 0 };
//bot_npc_type->min_dmg = 0;
//bot_npc_type->max_dmg = 0;
//bot_npc_type->charm_ac = 0;
//bot_npc_type->charm_min_dmg = 0;
//bot_npc_type->charm_max_dmg = 0;
//bot_npc_type->charm_attack_delay = 0;
//bot_npc_type->charm_accuracy_rating = 0;
//bot_npc_type->charm_avoidance_rating = 0;
//bot_npc_type->charm_atk = 0;
//bot_npc_type->attack_count = 0;
//*bot_npc_type->special_abilities = { 0 };
//bot_npc_type->d_melee_texture1 = 0;
//bot_npc_type->d_melee_texture2 = 0;
//*bot_npc_type->ammo_idfile = { 0 };
//bot_npc_type->prim_melee_type = 0;
//bot_npc_type->sec_melee_type = 0;
//bot_npc_type->ranged_type = 0;
bot_npc_type->hp_regen = 1;
bot_npc_type->mana_regen = 1;
//bot_npc_type->aggroradius = 0;
//bot_npc_type->assistradius = 0;
//bot_npc_type->see_invis = 0;
//bot_npc_type->see_invis_undead = false;
//bot_npc_type->see_hide = false;
//bot_npc_type->see_improved_hide = false;
//bot_npc_type->qglobal = false;
//bot_npc_type->npc_aggro = false;
//bot_npc_type->spawn_limit = 0;
//bot_npc_type->mount_color = 0;
//bot_npc_type->attack_speed = 0.0f;
//bot_npc_type->attack_delay = 0;
//bot_npc_type->accuracy_rating = 0;
//bot_npc_type->avoidance_rating = 0;
//bot_npc_type->findable = false;
bot_npc_type->trackable = true;
//bot_npc_type->slow_mitigation = 0;
bot_npc_type->maxlevel = botLevel;
//bot_npc_type->scalerate = 0;
//bot_npc_type->private_corpse = false;
//bot_npc_type->unique_spawn_by_name = false;
//bot_npc_type->underwater = false;
//bot_npc_type->emoteid = 0;
//bot_npc_type->spellscale = 0.0f;
//bot_npc_type->healscale = 0.0f;
//bot_npc_type->no_target_hotkey = false;
//bot_npc_type->raid_target = false;
//bot_npc_type->armtexture = 0;
//bot_npc_type->bracertexture = 0;
//bot_npc_type->handtexture = 0;
//bot_npc_type->legtexture = 0;
//bot_npc_type->feettexture = 0;
//bot_npc_type->ignore_despawn = false;
bot_npc_type->show_name = true;
//bot_npc_type->untargetable = false;
bot_npc_type->skip_global_loot = true;
//bot_npc_type->rare_spawn = false;
bot_npc_type->stuck_behavior = Ground;
return bot_npc_type;
}
void Bot::GenerateBaseStats()
@@ -1867,6 +2013,17 @@ bool Bot::Process() {
if(GetAppearance() == eaDead && GetHP() > 0)
SetAppearance(eaStanding);
if (IsMoving()) {
ping_timer.Disable();
}
else {
if (!ping_timer.Enabled())
ping_timer.Start(BOT_KEEP_ALIVE_INTERVAL);
if (ping_timer.Check())
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
}
if (IsStunned() || IsMezzed())
return true;
@@ -2091,10 +2248,9 @@ void Bot::AI_Process() {
Client* bot_owner = (GetBotOwner() && GetBotOwner()->IsClient() ? GetBotOwner()->CastToClient() : nullptr);
Group* bot_group = GetGroup();
Mob* follow_mob = entity_list.GetMob(GetFollowID());
// Primary reasons for not processing AI
if (!bot_owner || !bot_group || !follow_mob || !IsAIControlled())
if (!bot_owner || !bot_group || !IsAIControlled())
return;
if (bot_owner->IsDead()) {
@@ -2104,11 +2260,18 @@ void Bot::AI_Process() {
return;
}
// We also need a leash owner (subset of primary AI criteria)
// We also need a leash owner and follow mob (subset of primary AI criteria)
Client* leash_owner = (bot_group->GetLeader() && bot_group->GetLeader()->IsClient() ? bot_group->GetLeader()->CastToClient() : bot_owner);
if (!leash_owner)
return;
Mob* follow_mob = entity_list.GetMob(GetFollowID());
if (!follow_mob) {
follow_mob = leash_owner;
SetFollowID(leash_owner->GetID());
}
// Berserk updates should occur if primary AI criteria are met
if (GetClass() == WARRIOR || GetClass() == BERSERKER) {
if (!berserk && GetHP() > 0 && GetHPRatio() < 30.0f) {
@@ -2603,7 +2766,7 @@ void Bot::AI_Process() {
// we can't fight if we don't have a target, are stun/mezzed or dead..
// Stop attacking if the target is enraged
TEST_TARGET();
if (GetBotStance() == BotStancePassive || (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())))
if (GetBotStance() == EQEmu::constants::stancePassive || (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())))
return;
// First, special attack per class (kick, backstab etc..)
@@ -2730,7 +2893,7 @@ void Bot::AI_Process() {
FaceTarget(GetTarget());
// This is a mob that is fleeing either because it has been feared or is low on hitpoints
if (GetBotStance() != BotStancePassive) {
if (GetBotStance() != EQEmu::constants::stancePassive) {
AI_PursueCastCheck(); // This appears to always return true..can't trust for success/fail
return;
}
@@ -2738,7 +2901,7 @@ void Bot::AI_Process() {
} // end not in combat range
if (!IsMoving() && !spellend_timer.Enabled()) { // This may actually need work...
if (GetBotStance() == BotStancePassive)
if (GetBotStance() == EQEmu::constants::stancePassive)
return;
if (GetTarget() && AI_EngagedCastCheck())
@@ -2796,7 +2959,7 @@ void Bot::AI_Process() {
// Ok to idle
if (fm_dist <= GetFollowDistance()) {
if (!IsMoving() && AI_think_timer->Check() && !spellend_timer.Enabled()) {
if (GetBotStance() != BotStancePassive) {
if (GetBotStance() != EQEmu::constants::stancePassive) {
if (!AI_IdleCastCheck() && !IsCasting() && GetClass() != BARD)
BotMeditate(true);
}
@@ -2841,7 +3004,7 @@ void Bot::AI_Process() {
// Basically, bard bots get a chance to cast idle spells while moving
if (IsMoving()) {
if (GetBotStance() != BotStancePassive) {
if (GetBotStance() != EQEmu::constants::stancePassive) {
if (GetClass() == BARD && !spellend_timer.Enabled() && AI_think_timer->Check()) {
AI_IdleCastCheck();
return;
@@ -3111,6 +3274,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
// Load pet
LoadPet();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
ping_timer.Start(8000);
// there is something askew with spawn struct appearance fields...
// I re-enabled this until I can sort it out
uint32 itemID = 0;
@@ -8049,7 +8213,7 @@ bool Bot::CheckLoreConflict(const EQEmu::ItemData* item) {
}
bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
Log(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!");
return false;
}
@@ -8104,19 +8268,19 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl
Group *g = caster->GetGroup();
float hpRatioToHeal = 25.0f;
switch(caster->GetBotStance()) {
case BotStanceReactive:
case BotStanceBalanced:
hpRatioToHeal = 50.0f;
break;
case BotStanceBurn:
case BotStanceBurnAE:
hpRatioToHeal = 20.0f;
break;
case BotStanceAggressive:
case BotStanceEfficient:
default:
hpRatioToHeal = 25.0f;
break;
case EQEmu::constants::stanceReactive:
case EQEmu::constants::stanceBalanced:
hpRatioToHeal = 50.0f;
break;
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
hpRatioToHeal = 20.0f;
break;
case EQEmu::constants::stanceAggressive:
case EQEmu::constants::stanceEfficient:
default:
hpRatioToHeal = 25.0f;
break;
}
if(g) {
@@ -8655,11 +8819,11 @@ bool Bot::HasOrMayGetAggro() {
}
void Bot::SetDefaultBotStance() {
BotStanceType defaultStance = BotStanceBalanced;
EQEmu::constants::StanceType defaultStance = EQEmu::constants::stanceBalanced;
if (GetClass() == WARRIOR)
defaultStance = BotStanceAggressive;
defaultStance = EQEmu::constants::stanceAggressive;
_baseBotStance = BotStancePassive;
_baseBotStance = EQEmu::constants::stancePassive;
_botStance = defaultStance;
}
@@ -8932,4 +9096,6 @@ std::string Bot::CreateSayLink(Client* c, const char* message, const char* name)
return saylink;
}
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQEmu::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
#endif
+50 -116
View File
@@ -44,6 +44,8 @@
#define BOT_LEASH_DISTANCE 250000 // as DSq value (500 units)
#define BOT_KEEP_ALIVE_INTERVAL 5000 // 5 seconds
extern WorldServer worldserver;
const int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
@@ -52,91 +54,7 @@ const int MaxDisciplineTimer = 10;
const int DisciplineReuseStart = MaxSpellTimer + 1;
const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
enum BotStanceType {
BotStancePassive,
BotStanceBalanced,
BotStanceEfficient,
BotStanceReactive,
BotStanceAggressive,
BotStanceBurn,
BotStanceBurnAE,
BotStanceUnknown,
MaxStances = BotStanceUnknown
};
#define BOT_STANCE_COUNT 8
#define VALIDBOTSTANCE(x) ((x >= (int)BotStancePassive && x <= (int)BotStanceBurnAE) ? ((BotStanceType)x) : (BotStanceUnknown))
static const std::string bot_stance_name[BOT_STANCE_COUNT] = {
"Passive", // 0
"Balanced", // 1
"Efficient", // 2
"Reactive", // 3
"Aggressive", // 4
"Burn", // 5
"BurnAE", // 6
"Unknown" // 7
};
static const char* GetBotStanceName(int stance_id) { return bot_stance_name[VALIDBOTSTANCE(stance_id)].c_str(); }
#define VALIDBOTEQUIPSLOT(x) ((x >= EQEmu::invslot::EQUIPMENT_BEGIN && x <= EQEmu::invslot::EQUIPMENT_END) ? (x) : (EQEmu::invslot::EQUIPMENT_COUNT))
static const std::string bot_equip_slot_name[EQEmu::invslot::EQUIPMENT_COUNT + 1] =
{
"Charm", // slotCharm
"Ear 1", // slotEar1
"Head", // slotHead
"Face", // slotFace
"Ear 2", // slotEar2
"Neck", // slotNeck
"Shoulders", // slotShoulders
"Arms", // slotArms
"Back", // slotBack
"Wrist 1", // slotWrist1
"Wrist 2", // slotWrist2
"Range", // slotRange
"Hands", // slotHands
"Primary", // slotPrimary
"Secondary", // slotSecondary
"Finger 1", // slotFinger1
"Finger 2", // slotFinger2
"Chest", // slotChest
"Legs", // slotLegs
"Feet", // slotFeet
"Waist", // slotWaist
"Power Source", // slotPowerSource
"Ammo", // slotAmmo
"Unknown"
};
static const char* GetBotEquipSlotName(int slot_id) { return bot_equip_slot_name[VALIDBOTEQUIPSLOT(slot_id)].c_str(); }
enum SpellTypeIndex {
SpellType_NukeIndex,
SpellType_HealIndex,
SpellType_RootIndex,
SpellType_BuffIndex,
SpellType_EscapeIndex,
SpellType_PetIndex,
SpellType_LifetapIndex,
SpellType_SnareIndex,
SpellType_DOTIndex,
SpellType_DispelIndex,
SpellType_InCombatBuffIndex,
SpellType_MezIndex,
SpellType_CharmIndex,
SpellType_SlowIndex,
SpellType_DebuffIndex,
SpellType_CureIndex,
SpellType_ResurrectIndex,
SpellType_HateReduxIndex,
SpellType_InCombatBuffSongIndex,
SpellType_OutOfCombatBuffSongIndex,
SpellType_PreCombatBuffIndex,
SpellType_PreCombatBuffSongIndex,
MaxSpellTypes
};
// nHSND negative Healer/Slower/Nuker/Doter
// pH positive Healer
@@ -226,32 +144,38 @@ public:
BotRoleRaidHealer
};
enum EqExpansions { // expansions are off..EQ should be '0'
ExpansionNone,
ExpansionEQ,
ExpansionRoK,
ExpansionSoV,
ExpansionSoL,
ExpansionPoP,
ExpansionLoY,
ExpansionLDoN,
ExpansionGoD,
ExpansionOoW,
ExpansionDoN,
ExpansionDoDH,
ExpansionPoR,
ExpansionTSS,
ExpansionSoF,
ExpansionSoD,
ExpansionUF,
ExpansionHoT,
ExpansionVoA,
ExpansionRoF
enum SpellTypeIndex : uint32 {
spellTypeIndexNuke,
spellTypeIndexHeal,
spellTypeIndexRoot,
spellTypeIndexBuff,
spellTypeIndexEscape,
spellTypeIndexPet,
spellTypeIndexLifetap,
spellTypeIndexSnare,
spellTypeIndexDot,
spellTypeIndexDispel,
spellTypeIndexInCombatBuff,
spellTypeIndexMez,
spellTypeIndexCharm,
spellTypeIndexSlow,
spellTypeIndexDebuff,
spellTypeIndexCure,
spellTypeIndexResurrect,
spellTypeIndexHateRedux,
spellTypeIndexInCombatBuffSong,
spellTypeIndexOutOfCombatBuffSong,
spellTypeIndexPreCombatBuff,
spellTypeIndexPreCombatBuffSong
};
static const uint32 SPELL_TYPE_FIRST = spellTypeIndexNuke;
static const uint32 SPELL_TYPE_LAST = spellTypeIndexPreCombatBuffSong;
static const uint32 SPELL_TYPE_COUNT = SPELL_TYPE_LAST + 1;
// Class Constructors
Bot(NPCType npcTypeData, Client* botOwner);
Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData);
Bot(NPCType *npcTypeData, Client* botOwner);
Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData);
//abstract virtual function implementations requird by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
@@ -337,9 +261,10 @@ public:
void Stand();
bool IsSitting();
bool IsStanding();
int GetBotWalkspeed() const { return (int)((float)_GetWalkSpeed() * 1.786f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
int GetBotRunspeed() const { return (int)((float)_GetRunSpeed() * 1.786f); }
int GetBotFearSpeed() const { return (int)((float)_GetFearSpeed() * 1.786f); }
virtual int GetWalkspeed() const { return (int)((float)_GetWalkSpeed() * 1.785714f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
virtual int GetRunspeed() const { return (int)((float)_GetRunSpeed() * 1.785714f); }
virtual void WalkTo(float x, float y, float z);
virtual void RunTo(float x, float y, float z);
bool UseDiscipline(uint32 spell_id, uint32 target);
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets);
bool GetNeedsCured(Mob *tar);
@@ -494,7 +419,7 @@ public:
static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target);
static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target);
static NPCType CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
static NPCType *CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
// Static Bot Group Methods
static bool AddBotToGroup(Bot* bot, Group* group);
@@ -516,7 +441,7 @@ public:
virtual bool IsBot() const { return true; }
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
BotRoleType GetBotRole() { return _botRole; }
BotStanceType GetBotStance() { return _botStance; }
EQEmu::constants::StanceType GetBotStance() { return _botStance; }
uint8 GetChanceToCastBySpellType(uint32 spellType);
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
@@ -630,7 +555,12 @@ public:
// void SetBotOwnerCharacterID(uint32 botOwnerCharacterID) { _botOwnerCharacterID = botOwnerCharacterID; }
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == RANGER ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
void SetBotRole(BotRoleType botRole) { _botRole = botRole; }
void SetBotStance(BotStanceType botStance) { _botStance = ((botStance != BotStanceUnknown) ? (botStance) : (BotStancePassive)); }
void SetBotStance(EQEmu::constants::StanceType botStance) {
if (botStance >= EQEmu::constants::stancePassive && botStance <= EQEmu::constants::stanceBurnAE)
_botStance = botStance;
else
_botStance = EQEmu::constants::stancePassive;
}
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
@@ -656,7 +586,7 @@ public:
virtual void BotRangedAttack(Mob* other);
// Publicized private functions
static NPCType FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack);
static NPCType *FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack);
void BotRemoveEquipItem(int16 slot);
void RemoveBotItemBySlot(uint32 slotID, std::string* errorMessage);
uint32 GetTotalPlayTime();
@@ -724,12 +654,13 @@ private:
uint32 _lastZoneId;
bool _rangerAutoWeaponSelect;
BotRoleType _botRole;
BotStanceType _botStance;
BotStanceType _baseBotStance;
EQEmu::constants::StanceType _botStance;
EQEmu::constants::StanceType _baseBotStance;
unsigned int RestRegenHP;
unsigned int RestRegenMana;
unsigned int RestRegenEndurance;
Timer rest_timer;
Timer ping_timer;
int32 base_end;
int32 cur_end;
int32 max_end;
@@ -788,6 +719,9 @@ private:
bool LoadPet(); // Load and spawn bot pet if there is one
bool SavePet(); // Save and depop bot pet if there is one
bool DeletePet();
public:
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQEmu::constants::STANCE_TYPE_COUNT][cntHSND];
};
#endif // BOTS
+30 -29
View File
@@ -4249,7 +4249,7 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep)
return;
}
int clone_stance = BotStancePassive;
int clone_stance = EQEmu::constants::stancePassive;
if (!botdb.LoadStance(my_bot->GetBotID(), clone_stance))
c->Message(m_fail, "%s for bot '%s'", BotDatabase::fail::LoadStance(), my_bot->GetCleanName());
if (!botdb.SaveStance(clone_id, clone_stance))
@@ -5160,29 +5160,34 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
if (helper_command_alias_fail(c, "bot_subcommand_bot_stance", sep->arg[0], "botstance"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s [current | value: 0-6] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
c->Message(m_usage, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
c->Message(m_note, "value: %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s)",
BotStancePassive, GetBotStanceName(BotStancePassive),
BotStanceBalanced, GetBotStanceName(BotStanceBalanced),
BotStanceEfficient, GetBotStanceName(BotStanceEfficient),
BotStanceReactive, GetBotStanceName(BotStanceReactive),
BotStanceAggressive, GetBotStanceName(BotStanceAggressive),
BotStanceBurn, GetBotStanceName(BotStanceBurn),
BotStanceBurnAE, GetBotStanceName(BotStanceBurnAE)
EQEmu::constants::stancePassive, EQEmu::constants::GetStanceName(EQEmu::constants::stancePassive),
EQEmu::constants::stanceBalanced, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBalanced),
EQEmu::constants::stanceEfficient, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient),
EQEmu::constants::stanceReactive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceReactive),
EQEmu::constants::stanceAggressive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAggressive),
EQEmu::constants::stanceAssist, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAssist),
EQEmu::constants::stanceBurn, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurn),
EQEmu::constants::stanceEfficient2, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient2),
EQEmu::constants::stanceBurnAE, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurnAE)
);
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
bool current_flag = false;
auto bst = BotStanceUnknown;
auto bst = EQEmu::constants::stanceUnknown;
if (!strcasecmp(sep->arg[1], "current"))
current_flag = true;
else if (sep->IsNumber(1))
bst = VALIDBOTSTANCE(atoi(sep->arg[1]));
else if (sep->IsNumber(1)) {
bst = (EQEmu::constants::StanceType)atoi(sep->arg[1]);
if (bst < EQEmu::constants::stanceUnknown || bst > EQEmu::constants::stanceBurnAE)
bst = EQEmu::constants::stanceUnknown;
}
if (!current_flag && bst == BotStanceUnknown) {
if (!current_flag && bst == EQEmu::constants::stanceUnknown) {
c->Message(m_fail, "A [current] argument or valid numeric [value] is required to use this command");
return;
}
@@ -5200,7 +5205,12 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
bot_iter->Save();
}
Bot::BotGroupSay(bot_iter, "My current stance is '%s' (%u)", GetBotStanceName(bot_iter->GetBotStance()), bot_iter->GetBotStance());
Bot::BotGroupSay(
bot_iter,
"My current stance is '%s' (%i)",
EQEmu::constants::GetStanceName(bot_iter->GetBotStance()),
bot_iter->GetBotStance()
);
}
}
@@ -7220,7 +7230,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
inst = my_bot->CastToBot()->GetBotItem(i);
if (!inst || !inst->GetItem()) {
c->Message(m_message, "I need something for my %s (slot %i)", GetBotEquipSlotName(i), i);
c->Message(m_message, "I need something for my %s (slot %i)", EQEmu::invslot::GetInvPossessionsSlotName(i), i);
continue;
}
@@ -7230,7 +7240,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
}
linker.SetItemInst(inst);
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), i);
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), EQEmu::invslot::GetInvPossessionsSlotName(i), i);
++inventory_count;
}
@@ -7333,14 +7343,14 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
case EQEmu::invslot::slotWaist:
case EQEmu::invslot::slotPowerSource:
case EQEmu::invslot::slotAmmo:
c->Message(m_message, "My %s is %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
c->Message(m_message, "My %s is %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
break;
case EQEmu::invslot::slotShoulders:
case EQEmu::invslot::slotArms:
case EQEmu::invslot::slotHands:
case EQEmu::invslot::slotLegs:
case EQEmu::invslot::slotFeet:
c->Message(m_message, "My %s are %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
c->Message(m_message, "My %s are %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
break;
default:
c->Message(m_fail, "I'm soo confused...");
@@ -7383,7 +7393,7 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep)
item = inst->GetItem();
window_text.append("<c \"#FFFFFF\">");
window_text.append(GetBotEquipSlotName(i));
window_text.append(EQEmu::invslot::GetInvPossessionsSlotName(i));
window_text.append(": ");
if (item) {
//window_text.append("</c>");
@@ -7707,16 +7717,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
return bot_id;
}
auto DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(
bot_name.c_str(),
"",
bot_owner->GetLevel(),
bot_race,
bot_class,
bot_gender
);
auto my_bot = new Bot(DefaultNPCTypeStruct, bot_owner);
auto my_bot = new Bot(Bot::CreateDefaultNPCTypeStructForBot(bot_name.c_str(), "", bot_owner->GetLevel(), bot_race, bot_class, bot_gender), bot_owner);
if (!my_bot->Save()) {
bot_owner->Message(m_unknown, "Failed to create '%s' due to unknown cause", my_bot->GetCleanName());
+24 -28
View File
@@ -83,12 +83,8 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
return true;
}
static uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHSND];
bool BotDatabase::LoadBotSpellCastingChances()
{
memset(spell_casting_chances, 0, sizeof(spell_casting_chances));
query =
"SELECT"
" `spell_type_index`,"
@@ -119,14 +115,14 @@ bool BotDatabase::LoadBotSpellCastingChances()
for (auto row = results.begin(); row != results.end(); ++row) {
uint8 spell_type_index = atoi(row[0]);
if (spell_type_index >= MaxSpellTypes)
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
continue;
uint8 class_index = atoi(row[1]);
if (class_index < WARRIOR || class_index > BERSERKER)
continue;
--class_index;
uint8 stance_index = atoi(row[2]);
if (stance_index >= MaxStances)
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
continue;
for (uint8 conditional_index = nHSND; conditional_index < cntHSND; ++conditional_index) {
@@ -136,7 +132,7 @@ bool BotDatabase::LoadBotSpellCastingChances()
if (value > 100)
value = 100;
spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
}
}
@@ -357,8 +353,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
// TODO: Consider removing resists and basic attributes from the load query above since we're using defaultNPCType values instead
auto row = results.begin();
NPCType defaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[10]), atoi(row[8]), atoi(row[9]), atoi(row[7]));
NPCType tempNPCStruct = Bot::FillNPCTypeStruct(
auto defaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[10]), atoi(row[8]), atoi(row[9]), atoi(row[7]));
auto tempNPCStruct = Bot::FillNPCTypeStruct(
atoi(row[1]),
std::string(row[2]),
std::string(row[3]),
@@ -379,21 +375,21 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
atoi(row[25]),
atoi(row[28]),
atoi(row[29]),
defaultNPCTypeStruct.MR,
defaultNPCTypeStruct.CR,
defaultNPCTypeStruct.DR,
defaultNPCTypeStruct.FR,
defaultNPCTypeStruct.PR,
defaultNPCTypeStruct.Corrup,
defaultNPCTypeStruct.AC,
defaultNPCTypeStruct.STR,
defaultNPCTypeStruct.STA,
defaultNPCTypeStruct.DEX,
defaultNPCTypeStruct.AGI,
defaultNPCTypeStruct.INT,
defaultNPCTypeStruct.WIS,
defaultNPCTypeStruct.CHA,
defaultNPCTypeStruct.ATK
defaultNPCTypeStruct->MR,
defaultNPCTypeStruct->CR,
defaultNPCTypeStruct->DR,
defaultNPCTypeStruct->FR,
defaultNPCTypeStruct->PR,
defaultNPCTypeStruct->Corrup,
defaultNPCTypeStruct->AC,
defaultNPCTypeStruct->STR,
defaultNPCTypeStruct->STA,
defaultNPCTypeStruct->DEX,
defaultNPCTypeStruct->AGI,
defaultNPCTypeStruct->INT,
defaultNPCTypeStruct->WIS,
defaultNPCTypeStruct->CHA,
defaultNPCTypeStruct->ATK
);
loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct);
@@ -877,7 +873,7 @@ bool BotDatabase::LoadStance(Bot* bot_inst, bool& stance_flag)
return true;
auto row = results.begin();
bot_inst->SetBotStance((BotStanceType)atoi(row[0]));
bot_inst->SetBotStance((EQEmu::constants::StanceType)atoi(row[0]));
stance_flag = true;
return true;
@@ -2853,16 +2849,16 @@ bool BotDatabase::DeleteAllHealRotations(const uint32 owner_id)
/* Bot miscellaneous functions */
uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index) // class_index is 0-based
{
if (spell_type_index >= MaxSpellTypes)
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
return 0;
if (class_index >= PLAYER_CLASS_COUNT)
return 0;
if (stance_index >= MaxStances)
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
return 0;
if (conditional_index >= cntHSND)
return 0;
return spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
}
+82 -83
View File
@@ -192,25 +192,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
else {
float hpRatioToCast = 0.0f;
switch(this->GetBotStance())
{
case BotStanceEfficient:
case BotStanceAggressive:
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
break;
case BotStanceBalanced:
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
break;
case BotStanceReactive:
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
break;
case BotStanceBurn:
case BotStanceBurnAE:
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
break;
default:
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
break;
switch(this->GetBotStance()) {
case EQEmu::constants::stanceEfficient:
case EQEmu::constants::stanceAggressive:
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
break;
case EQEmu::constants::stanceBalanced:
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
break;
case EQEmu::constants::stanceReactive:
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
break;
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
break;
default:
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
break;
}
//If we're at specified mana % or below, don't heal as hybrid
@@ -381,23 +380,22 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
{
float manaRatioToCast = 75.0f;
switch(this->GetBotStance())
{
case BotStanceEfficient:
manaRatioToCast = 90.0f;
break;
case BotStanceBalanced:
case BotStanceAggressive:
manaRatioToCast = 75.0f;
break;
case BotStanceReactive:
case BotStanceBurn:
case BotStanceBurnAE:
manaRatioToCast = 50.0f;
break;
default:
manaRatioToCast = 75.0f;
break;
switch(this->GetBotStance()) {
case EQEmu::constants::stanceEfficient:
manaRatioToCast = 90.0f;
break;
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceAggressive:
manaRatioToCast = 75.0f;
break;
case EQEmu::constants::stanceReactive:
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
manaRatioToCast = 50.0f;
break;
default:
manaRatioToCast = 75.0f;
break;
}
//If we're at specified mana % or below, don't rune as enchanter
@@ -461,25 +459,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
{
float manaRatioToCast = 75.0f;
switch(this->GetBotStance())
{
case BotStanceEfficient:
manaRatioToCast = 90.0f;
break;
case BotStanceBalanced:
manaRatioToCast = 75.0f;
break;
case BotStanceReactive:
case BotStanceAggressive:
manaRatioToCast = 50.0f;
break;
case BotStanceBurn:
case BotStanceBurnAE:
manaRatioToCast = 25.0f;
break;
default:
manaRatioToCast = 50.0f;
break;
switch(this->GetBotStance()) {
case EQEmu::constants::stanceEfficient:
manaRatioToCast = 90.0f;
break;
case EQEmu::constants::stanceBalanced:
manaRatioToCast = 75.0f;
break;
case EQEmu::constants::stanceReactive:
case EQEmu::constants::stanceAggressive:
manaRatioToCast = 50.0f;
break;
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
manaRatioToCast = 25.0f;
break;
default:
manaRatioToCast = 50.0f;
break;
}
//If we're at specified mana % or below, don't nuke as cleric or enchanter
@@ -1310,7 +1307,7 @@ bool Bot::AI_EngagedCastCheck() {
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
uint8 botClass = GetClass();
BotStanceType botStance = GetBotStance();
EQEmu::constants::StanceType botStance = GetBotStance();
bool mayGetAggro = HasOrMayGetAggro();
Log(Logs::Detail, Logs::AI, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells.");
@@ -2573,79 +2570,79 @@ bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) {
uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
{
uint8 spell_type_index = MaxSpellTypes;
uint8 spell_type_index = SPELL_TYPE_COUNT;
switch (spellType) {
case SpellType_Nuke:
spell_type_index = SpellType_NukeIndex;
spell_type_index = spellTypeIndexNuke;
break;
case SpellType_Heal:
spell_type_index = SpellType_HealIndex;
spell_type_index = spellTypeIndexHeal;
break;
case SpellType_Root:
spell_type_index = SpellType_RootIndex;
spell_type_index = spellTypeIndexRoot;
break;
case SpellType_Buff:
spell_type_index = SpellType_BuffIndex;
spell_type_index = spellTypeIndexBuff;
break;
case SpellType_Escape:
spell_type_index = SpellType_EscapeIndex;
spell_type_index = spellTypeIndexEscape;
break;
case SpellType_Pet:
spell_type_index = SpellType_PetIndex;
spell_type_index = spellTypeIndexPet;
break;
case SpellType_Lifetap:
spell_type_index = SpellType_LifetapIndex;
spell_type_index = spellTypeIndexLifetap;
break;
case SpellType_Snare:
spell_type_index = SpellType_SnareIndex;
spell_type_index = spellTypeIndexSnare;
break;
case SpellType_DOT:
spell_type_index = SpellType_DOTIndex;
spell_type_index = spellTypeIndexDot;
break;
case SpellType_Dispel:
spell_type_index = SpellType_DispelIndex;
spell_type_index = spellTypeIndexDispel;
break;
case SpellType_InCombatBuff:
spell_type_index = SpellType_InCombatBuffIndex;
spell_type_index = spellTypeIndexInCombatBuff;
break;
case SpellType_Mez:
spell_type_index = SpellType_MezIndex;
spell_type_index = spellTypeIndexMez;
break;
case SpellType_Charm:
spell_type_index = SpellType_CharmIndex;
spell_type_index = spellTypeIndexCharm;
break;
case SpellType_Slow:
spell_type_index = SpellType_SlowIndex;
spell_type_index = spellTypeIndexSlow;
break;
case SpellType_Debuff:
spell_type_index = SpellType_DebuffIndex;
spell_type_index = spellTypeIndexDebuff;
break;
case SpellType_Cure:
spell_type_index = SpellType_CureIndex;
spell_type_index = spellTypeIndexCure;
break;
case SpellType_Resurrect:
spell_type_index = SpellType_ResurrectIndex;
spell_type_index = spellTypeIndexResurrect;
break;
case SpellType_HateRedux:
spell_type_index = SpellType_HateReduxIndex;
spell_type_index = spellTypeIndexHateRedux;
break;
case SpellType_InCombatBuffSong:
spell_type_index = SpellType_InCombatBuffSongIndex;
spell_type_index = spellTypeIndexInCombatBuffSong;
break;
case SpellType_OutOfCombatBuffSong:
spell_type_index = SpellType_OutOfCombatBuffSongIndex;
spell_type_index = spellTypeIndexOutOfCombatBuffSong;
break;
case SpellType_PreCombatBuff:
spell_type_index = SpellType_PreCombatBuffIndex;
spell_type_index = spellTypeIndexPreCombatBuff;
break;
case SpellType_PreCombatBuffSong:
spell_type_index = SpellType_PreCombatBuffSongIndex;
spell_type_index = spellTypeIndexPreCombatBuffSong;
break;
default:
spell_type_index = MaxSpellTypes;
spell_type_index = SPELL_TYPE_COUNT;
break;
}
if (spell_type_index >= MaxSpellTypes)
if (spell_type_index >= SPELL_TYPE_COUNT)
return 0;
uint8 class_index = GetClass();
@@ -2653,11 +2650,13 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
return 0;
--class_index;
uint8 stance_index = (uint8)GetBotStance();
if (stance_index >= MaxStances)
EQEmu::constants::StanceType stance_type = GetBotStance();
if (stance_type < EQEmu::constants::stancePassive || stance_type > EQEmu::constants::stanceBurnAE)
return 0;
uint8 stance_index = EQEmu::constants::ConvertStanceTypeToIndex(stance_type);
uint8 type_index = nHSND;
if (HasGroup()) {
if (IsGroupHealer()/* || IsRaidHealer()*/)
type_index |= pH;
+13
View File
@@ -38,6 +38,7 @@ extern volatile bool RunLoops;
#include "../common/rulesys.h"
#include "../common/string_util.h"
#include "../common/data_verification.h"
#include "../common/profanity_manager.h"
#include "data_bucket.h"
#include "position.h"
#include "net.h"
@@ -895,6 +896,10 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
language = 0; // No need for language when drunk
}
// Censor the message
if (EQEmu::ProfanityManager::IsCensorshipActive() && (chan_num != 8))
EQEmu::ProfanityManager::RedactMessage(message);
switch(chan_num)
{
case 0: { /* Guild Chat */
@@ -1092,6 +1097,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
break;
}
if (EQEmu::ProfanityManager::IsCensorshipActive())
EQEmu::ProfanityManager::RedactMessage(message);
#ifdef BOTS
if (message[0] == BOT_COMMAND_CHAR) {
if (bot_command_dispatch(this, message) == -2) {
@@ -2640,6 +2648,11 @@ bool Client::CheckAccess(int16 iDBLevel, int16 iDefaultLevel) {
}
void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing){
if (slot < 0 || slot >= EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize)
return;
if ((spellid < 3 || spellid > EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellIdMax) && spellid != 0xFFFFFFFF)
return;
auto outapp = new EQApplicationPacket(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct));
MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer;
mss->scribing=scribing;
+27 -24
View File
@@ -530,10 +530,14 @@ void Client::CompleteConnect()
SendAppearancePacket(AT_GuildID, GuildID(), false);
SendAppearancePacket(AT_GuildRank, rank, false);
}
for (uint32 spellInt = 0; spellInt < EQEmu::spells::SPELLBOOK_SIZE; spellInt++) {
if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000)
// moved to dbload and translators since we iterate there also .. keep m_pp values whatever they are when they get here
/*const auto sbs = EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize;
for (uint32 spellInt = 0; spellInt < sbs; ++spellInt) {
if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > EQEmu::spells::SPELL_ID_MAX)
m_pp.spell_book[spellInt] = 0xFFFFFFFF;
}
}*/
//SendAATable();
if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients");
@@ -1154,6 +1158,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
SetClientVersion(Connection()->ClientVersion());
m_ClientVersionBit = EQEmu::versions::ConvertClientVersionToClientVersionBit(Connection()->ClientVersion());
m_pp.SetPlayerProfileVersion(m_ClientVersion);
m_inv.SetInventoryVersion(m_ClientVersion);
/* Antighost code
@@ -1409,12 +1414,11 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000) { m_pp.ldon_points_tak = 0; }
if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000) { m_pp.ldon_points_available = 0; }
// need to rework .. not until full scope of change is accounted for, though
if (RuleB(World, UseClientBasedExpansionSettings)) {
m_pp.expansions = EQEmu::expansions::ConvertClientVersionToExpansionMask(ClientVersion());
m_pp.expansions = EQEmu::expansions::ConvertClientVersionToExpansionsMask(ClientVersion());
}
else {
m_pp.expansions = RuleI(World, ExpansionSettings);
m_pp.expansions = (RuleI(World, ExpansionSettings) & EQEmu::expansions::ConvertClientVersionToExpansionsMask(ClientVersion()));
}
if (!database.LoadAlternateAdvancement(this)) {
@@ -1587,8 +1591,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate)))
m_pp.RestTimer = 0;
/* This checksum should disappear once dynamic structs are in... each struct strategy will do it */
CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct) - 4);
/* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ // looks to be in place now
//CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct) - sizeof(m_pp.m_player_profile_version) - 4);
// m_pp.checksum = 0; // All server out-bound player profile packets are now translated - no need to waste cycles calculating this...
outapp = new EQApplicationPacket(OP_PlayerProfile, sizeof(PlayerProfile_Struct));
@@ -2807,24 +2812,19 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app)
return;
}
uint32 ApplyPoisonSuccessResult = 0;
ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer;
uint32 ApplyPoisonSuccessResult = 0;
const EQEmu::ItemInstance* PrimaryWeapon = GetInv().GetItem(EQEmu::invslot::slotPrimary);
const EQEmu::ItemInstance* SecondaryWeapon = GetInv().GetItem(EQEmu::invslot::slotSecondary);
const EQEmu::ItemInstance* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot];
const EQEmu::ItemData* poison=PoisonItemInstance->GetItem();
const EQEmu::ItemData* primary=nullptr;
const EQEmu::ItemData* secondary=nullptr;
bool IsPoison = PoisonItemInstance &&
(poison->ItemType == EQEmu::item::ItemTypePoison);
const EQEmu::ItemInstance* PoisonItemInstance = GetInv().GetItem(ApplyPoisonData->inventorySlot);
if (PrimaryWeapon) {
primary=PrimaryWeapon->GetItem();
}
const EQEmu::ItemData* primary = (PrimaryWeapon ? PrimaryWeapon->GetItem() : nullptr);
const EQEmu::ItemData* secondary = (SecondaryWeapon ? SecondaryWeapon->GetItem() : nullptr);
const EQEmu::ItemData* poison = (PoisonItemInstance ? PoisonItemInstance->GetItem() : nullptr);
if (SecondaryWeapon) {
secondary=SecondaryWeapon->GetItem();
}
bool IsPoison = (poison && poison->ItemType == EQEmu::item::ItemTypePoison);
if (IsPoison && GetClass() == ROGUE) {
@@ -5263,7 +5263,7 @@ void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app)
EQApplicationPacket* outapp = app->Copy();
DeleteSpell_Struct* dss = (DeleteSpell_Struct*)outapp->pBuffer;
if (dss->spell_slot < 0 || dss->spell_slot > int(EQEmu::spells::SPELLBOOK_SIZE))
if (dss->spell_slot < 0 || dss->spell_slot >= EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize)
return;
if (m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) {
@@ -9394,7 +9394,7 @@ void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app)
//check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance)
if (option >= 0 && option < numStances)
{
merc->SetStance(mercTemplate->Stances[option]);
merc->SetStance((EQEmu::constants::StanceType)mercTemplate->Stances[option]);
GetMercInfo().Stance = mercTemplate->Stances[option];
Log(Logs::General, Logs::Mercenaries, "Set Stance: %u for %s (%s)", merc->GetStance(), merc->GetName(), GetName());
@@ -13337,7 +13337,10 @@ void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app)
const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*)app->pBuffer;
int swapspelltemp;
if (swapspell->from_slot < 0 || swapspell->from_slot > EQEmu::spells::SPELLBOOK_SIZE || swapspell->to_slot < 0 || swapspell->to_slot > EQEmu::spells::SPELLBOOK_SIZE)
const auto sbs = EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize;
if (swapspell->from_slot < 0 || swapspell->from_slot >= sbs)
return;
if (swapspell->to_slot < 0 || swapspell->to_slot >= sbs)
return;
swapspelltemp = m_pp.spell_book[swapspell->from_slot];
+196 -81
View File
@@ -54,6 +54,7 @@
#include "../common/string_util.h"
#include "../say_link.h"
#include "../common/eqemu_logsys.h"
#include "../common/profanity_manager.h"
#include "data_bucket.h"
#include "command.h"
@@ -307,6 +308,7 @@ int command_init(void)
command_add("petitioninfo", "[petition number] - Get info about a petition", 20, command_petitioninfo) ||
command_add("pf", "- Display additional mob coordinate and wandering data", 0, command_pf) ||
command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) ||
command_add("profanity", "Manage censored language.", 150, command_profanity) ||
#ifdef EQPROFILE
command_add("profiledump", "- Dump profiling info to logs", 250, command_profiledump) ||
@@ -6405,34 +6407,29 @@ void command_beardcolor(Client *c, const Seperator *sep)
void command_scribespells(Client *c, const Seperator *sep)
{
uint8 max_level, min_level;
uint16 book_slot, curspell, count;
Client *t=c;
Client *t = c;
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
t = c->GetTarget()->CastToClient();
if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
t=c->GetTarget()->CastToClient();
if(!sep->arg[1][0])
{
if(sep->argnum < 1 || !sep->IsNumber(1)) {
c->Message(0, "FORMAT: #scribespells <max level> <min level>");
return;
}
max_level = (uint8)atoi(sep->arg[1]);
if (!c->GetGM() && max_level > RuleI(Character, MaxLevel))
max_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
min_level = sep->arg[2][0] ? (uint8)atoi(sep->arg[2]) : 1; //default to 1 if there isn't a 2nd argument
if (!c->GetGM() && min_level > RuleI(Character, MaxLevel))
min_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
uint8 max_level = (uint8)atol(sep->arg[1]);
if (!c->GetGM() && max_level > (uint8)RuleI(Character, MaxLevel))
max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument
if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel))
min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
if(max_level < 1 || min_level < 1)
{
if(max_level < 1 || min_level < 1) {
c->Message(0, "ERROR: Level must be greater than 1.");
return;
}
if (min_level > max_level) {
c->Message(0, "Error: Min Level must be less than or equal to Max Level.");
c->Message(0, "ERROR: Min Level must be less than or equal to Max Level.");
return;
}
@@ -6441,34 +6438,71 @@ void command_scribespells(Client *c, const Seperator *sep)
c->Message(0, "Scribing spells for %s.", t->GetName());
Log(Logs::General, Logs::Normal, "Scribe spells request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level);
for(curspell = 0, book_slot = t->GetNextAvailableSpellBookSlot(), count = 0; curspell < SPDAT_RECORDS && book_slot < EQEmu::spells::SPELLBOOK_SIZE; curspell++, book_slot = t->GetNextAvailableSpellBookSlot(book_slot))
{
if
(
spells[curspell].classes[WARRIOR] != 0 && // check if spell exists
spells[curspell].classes[t->GetPP().class_-1] <= max_level && //maximum level
spells[curspell].classes[t->GetPP().class_-1] >= min_level && //minimum level
spells[curspell].skill != 52
)
{
if (book_slot == -1) { //no more book slots
t->Message(13, "Unable to scribe spell %s (%u) to spellbook: no more spell book slots available.", spells[curspell].name, curspell);
if (t != c)
c->Message(13, "Error scribing spells: %s ran out of spell book slots on spell %s (%u)", t->GetName(), spells[curspell].name, curspell);
break;
}
if(!IsDiscipline(curspell) && !t->HasSpellScribed(curspell)) { //isn't a discipline & we don't already have it scribed
t->ScribeSpell(curspell, book_slot);
count++;
}
int book_slot = t->GetNextAvailableSpellBookSlot();
int spell_id = 0;
int count = 0;
for ( ; spell_id < SPDAT_RECORDS && book_slot < EQEmu::spells::SPELLBOOK_SIZE; ++spell_id) {
if (book_slot == -1) {
t->Message(
13,
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
spell_id
);
if (t != c)
c->Message(
13,
"Error scribing spells: %s ran out of spell book slots on spell %s (%i)",
t->GetName(),
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
spell_id
);
break;
}
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
c->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
return;
}
if (book_slot < 0 || book_slot >= EQEmu::spells::SPELLBOOK_SIZE) {
c->Message(13, "FATAL ERROR: Book slot out-of-range (slot: %i, min: 0, max: %i)", book_slot, EQEmu::spells::SPELLBOOK_SIZE);
return;
}
while (true) {
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
break;
if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level
break;
if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level
break;
if (spells[spell_id].skill == 52)
break;
uint16 spell_id_ = (uint16)spell_id;
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
c->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
return;
}
if (!IsDiscipline(spell_id_) && !t->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed
t->ScribeSpell(spell_id_, book_slot);
++count;
}
break;
}
book_slot = t->GetNextAvailableSpellBookSlot(book_slot);
}
if (count > 0) {
t->Message(0, "Successfully scribed %u spells.", count);
t->Message(0, "Successfully scribed %i spells.", count);
if (t != c)
c->Message(0, "Successfully scribed %u spells for %s.", count, t->GetName());
} else {
c->Message(0, "Successfully scribed %i spells for %s.", count, t->GetName());
}
else {
t->Message(0, "No spells scribed.");
if (t != c)
c->Message(0, "No spells scribed for %s.", t->GetName());
@@ -8724,28 +8758,24 @@ void command_reloadtitles(Client *c, const Seperator *sep)
void command_traindisc(Client *c, const Seperator *sep)
{
uint8 max_level, min_level;
uint16 curspell, count;
Client *t=c;
Client *t = c;
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
t = c->GetTarget()->CastToClient();
if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
t=c->GetTarget()->CastToClient();
if(!sep->arg[1][0])
{
if (sep->argnum < 1 || !sep->IsNumber(1)) {
c->Message(0, "FORMAT: #traindisc <max level> <min level>");
return;
}
max_level = (uint8)atoi(sep->arg[1]);
if (!c->GetGM() && max_level > RuleI(Character, MaxLevel))
max_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
min_level = sep->arg[2][0] ? (uint8)atoi(sep->arg[2]) : 1; //default to 1 if there isn't a 2nd argument
if (!c->GetGM() && min_level > RuleI(Character, MaxLevel))
min_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
uint8 max_level = (uint8)atol(sep->arg[1]);
if (!c->GetGM() && max_level >(uint8)RuleI(Character, MaxLevel))
max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
if(max_level < 1 || min_level < 1)
{
uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument
if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel))
min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
if(max_level < 1 || min_level < 1) {
c->Message(0, "ERROR: Level must be greater than 1.");
return;
}
@@ -8759,35 +8789,58 @@ void command_traindisc(Client *c, const Seperator *sep)
c->Message(0, "Training disciplines for %s.", t->GetName());
Log(Logs::General, Logs::Normal, "Train disciplines request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level);
for(curspell = 0, count = 0; curspell < SPDAT_RECORDS; curspell++)
{
if
(
spells[curspell].classes[WARRIOR] != 0 && // check if spell exists
spells[curspell].classes[t->GetPP().class_-1] <= max_level && //maximum level
spells[curspell].classes[t->GetPP().class_-1] >= min_level && //minimum level
spells[curspell].skill != 52
)
{
if(IsDiscipline(curspell)){
//we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little
for(int r = 0; r < MAX_PP_DISCIPLINES; r++) {
if(t->GetPP().disciplines.values[r] == curspell) {
t->Message(13, "You already know this discipline.");
break; //continue the 1st loop
} else if(t->GetPP().disciplines.values[r] == 0) {
t->GetPP().disciplines.values[r] = curspell;
database.SaveCharacterDisc(t->CharacterID(), r, curspell);
t->SendDisciplineUpdate();
t->Message(0, "You have learned a new discipline!");
count++; //success counter
break; //continue the 1st loop
} //if we get to this point, there's already a discipline in this slot, so we continue onto the next slot
}
int spell_id = 0;
int count = 0;
bool change = false;
for( ; spell_id < SPDAT_RECORDS; ++spell_id) {
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
c->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
return;
}
while (true) {
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
break;
if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level
break;
if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level
break;
if (spells[spell_id].skill == 52)
break;
uint16 spell_id_ = (uint16)spell_id;
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
c->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
return;
}
if (!IsDiscipline(spell_id_))
break;
for (uint32 r = 0; r < MAX_PP_DISCIPLINES; ++r) {
if (t->GetPP().disciplines.values[r] == spell_id_) {
t->Message(13, "You already know this discipline.");
break; // continue the 1st loop
}
else if (t->GetPP().disciplines.values[r] == 0) {
t->GetPP().disciplines.values[r] = spell_id_;
database.SaveCharacterDisc(t->CharacterID(), r, spell_id_);
change = true;
t->Message(0, "You have learned a new discipline!");
++count; // success counter
break; // continue the 1st loop
} // if we get to this point, there's already a discipline in this slot, so we continue onto the next slot
}
break;
}
}
if (change)
t->SendDisciplineUpdate();
if (count > 0) {
t->Message(0, "Successfully trained %u disciplines.", count);
if (t != c)
@@ -11035,6 +11088,68 @@ void command_picklock(Client *c, const Seperator *sep)
}
}
void command_profanity(Client *c, const Seperator *sep)
{
std::string arg1(sep->arg[1]);
while (true) {
if (arg1.compare("list") == 0) {
// do nothing
}
else if (arg1.compare("clear") == 0) {
EQEmu::ProfanityManager::DeleteProfanityList(&database);
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
worldserver.SendPacket(pack);
safe_delete(pack);
}
else if (arg1.compare("add") == 0) {
if (!EQEmu::ProfanityManager::AddProfanity(&database, sep->arg[2]))
c->Message(CC_Red, "Could not add '%s' to the profanity list.", sep->arg[2]);
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
worldserver.SendPacket(pack);
safe_delete(pack);
}
else if (arg1.compare("del") == 0) {
if (!EQEmu::ProfanityManager::RemoveProfanity(&database, sep->arg[2]))
c->Message(CC_Red, "Could not delete '%s' from the profanity list.", sep->arg[2]);
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
worldserver.SendPacket(pack);
safe_delete(pack);
}
else if (arg1.compare("reload") == 0) {
if (!EQEmu::ProfanityManager::UpdateProfanityList(&database))
c->Message(CC_Red, "Could not reload the profanity list.");
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
worldserver.SendPacket(pack);
safe_delete(pack);
}
else {
break;
}
std::string popup;
const auto &list = EQEmu::ProfanityManager::GetProfanityList();
for (const auto &iter : list) {
popup.append(iter);
popup.append("<br>");
}
if (list.empty())
popup.append("** Censorship Inactive **<br>");
else
popup.append("** End of List **<br>");
c->SendPopupToClient("Profanity List", popup.c_str());
return;
}
c->Message(0, "Usage: #profanity [list] - shows profanity list");
c->Message(0, "Usage: #profanity [clear] - deletes all entries");
c->Message(0, "Usage: #profanity [add] [<word>] - adds entry");
c->Message(0, "Usage: #profanity [del] [<word>] - deletes entry");
c->Message(0, "Usage: #profanity [reload] - reloads profanity list");
}
void command_mysql(Client *c, const Seperator *sep)
{
if(!sep->arg[1][0] || !sep->arg[2][0]) {
+1
View File
@@ -210,6 +210,7 @@ void command_permagender(Client *c, const Seperator *sep);
void command_permarace(Client *c, const Seperator *sep);
void command_petitioninfo(Client *c, const Seperator *sep);
void command_picklock(Client *c, const Seperator *sep);
void command_profanity(Client *c, const Seperator *sep);
#ifdef EQPROFILE
void command_profiledump(Client *c, const Seperator *sep);
+1 -1
View File
@@ -4178,7 +4178,7 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
WAPP2->RankMSGID = 12315;
else if (ClientEntry->IsBuyer())
WAPP2->RankMSGID = 6056;
else if (ClientEntry->Admin() >= 10)
else if (ClientEntry->Admin() >= 10 && ClientEntry->GetGM())
WAPP2->RankMSGID = 12312;
else
WAPP2->RankMSGID = 0xFFFFFFFF;
+15 -15
View File
@@ -66,7 +66,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
memset(equipment, 0, sizeof(equipment));
SetMercID(0);
SetStance(MercStanceBalanced);
SetStance(EQEmu::constants::stanceBalanced);
rest_timer.Disable();
if (GetClass() == ROGUE)
@@ -1908,7 +1908,7 @@ bool Merc::AI_IdleCastCheck() {
bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
//according to live, you can buff and heal through walls...
//now with PCs, this only applies if you can TARGET the target, but
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
@@ -3669,13 +3669,13 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
initialCastChance = 50;
break;
case MercStanceBalanced:
case EQEmu::constants::stanceBalanced:
initialCastChance = 25;
break;
case MercStanceBurn:
case EQEmu::constants::stanceBurn:
initialCastChance = 0;
break;
}
@@ -3717,11 +3717,11 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
numTargetsCheck = 1;
break;
case MercStanceBalanced:
case MercStanceBurn:
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceBurn:
numTargetsCheck = 2;
break;
}
@@ -3769,11 +3769,11 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
numTargetsCheck = 2;
break;
case MercStanceBalanced:
case MercStanceBurn:
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceBurn:
numTargetsCheck = 3;
break;
}
@@ -3820,11 +3820,11 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
numTargetsCheck = 1;
break;
case MercStanceBalanced:
case MercStanceBurn:
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceBurn:
numTargetsCheck = 2;
break;
}
@@ -5649,7 +5649,7 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
merc->SetSuspended(false);
SetMerc(merc);
merc->Unsuspend(setMaxStats);
merc->SetStance(GetMercInfo().Stance);
merc->SetStance((EQEmu::constants::StanceType)GetMercInfo().Stance);
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
+3 -15
View File
@@ -30,18 +30,6 @@ namespace EQEmu
const int MercAISpellRange = 100; // TODO: Write a method that calcs what the merc's spell range is based on spell, equipment, AA, whatever and replace this
enum MercStanceType {
MercStancePassive = 1,
MercStanceBalanced,
MercStanceEfficient,
MercStanceReactive,
MercStanceAggressive,
MercStanceAssist,
MercStanceBurn,
MercStanceEfficient2,
MercStanceBurnAE
};
struct MercSpell {
uint16 spellid; // <= 0 = no spell
uint32 type; // 0 = never, must be one (and only one) of the defined values
@@ -175,7 +163,7 @@ public:
uint8 GetTierID() { return _TierID; }
uint32 GetCostFormula() { return _CostFormula; }
uint32 GetMercNameType() { return _NameType; }
uint32 GetStance() { return _currentStance; }
EQEmu::constants::StanceType GetStance() { return _currentStance; }
int GetHatedCount() { return _hatedCount; }
inline const uint8 GetClientVersion() const { return _OwnerClientVersion; }
@@ -265,7 +253,7 @@ public:
void SetMercNameType( uint8 nametype ) { _NameType = nametype; }
void SetClientVersion(uint8 clientVersion) { _OwnerClientVersion = clientVersion; }
void SetSuspended(bool suspended) { _suspended = suspended; }
void SetStance( uint32 stance ) { _currentStance = stance; }
void SetStance( EQEmu::constants::StanceType stance ) { _currentStance = stance; }
void SetHatedCount( int count ) { _hatedCount = count; }
void Sit();
@@ -385,7 +373,7 @@ private:
uint8 _CostFormula;
uint8 _NameType;
uint8 _OwnerClientVersion;
uint32 _currentStance;
EQEmu::constants::StanceType _currentStance;
EQEmu::InventoryProfile m_inv;
int32 max_end;
+4 -4
View File
@@ -577,8 +577,8 @@ public:
virtual void SetMoving(bool move) { moving = move; m_Delta = glm::vec4(); }
virtual void GoToBind(uint8 bindnum = 0) { }
virtual void Gate(uint8 bindnum = 0);
int GetWalkspeed() const { return(_GetWalkSpeed()); }
int GetRunspeed() const { return(_GetRunSpeed()); }
virtual int GetWalkspeed() const { return(_GetWalkSpeed()); }
virtual int GetRunspeed() const { return(_GetRunSpeed()); }
int GetBaseRunspeed() const { return base_runspeed; }
int GetBaseWalkspeed() const { return base_walkspeed; }
int GetBaseFearSpeed() const { return base_fearspeed; }
@@ -999,8 +999,8 @@ public:
inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);}
float CalculateHeadingToTarget(float in_x, float in_y) { return HeadingAngleToMob(in_x, in_y); }
void WalkTo(float x, float y, float z);
void RunTo(float x, float y, float z);
virtual void WalkTo(float x, float y, float z);
virtual void RunTo(float x, float y, float z);
void NavigateTo(float x, float y, float z);
void RotateTo(float new_heading);
void RotateToWalking(float new_heading);
+2 -2
View File
@@ -379,7 +379,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
}
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
if((iSpellTypes & SpellTypes_Detrimental) != 0) {
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
//according to live, you can buff and heal through walls...
//now with PCs, this only applies if you can TARGET the target, but
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
@@ -2813,7 +2813,7 @@ DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID)
entry.max_hp = atoi(row[8]);
// some spell types don't make much since to be priority 0, so fix that
if (!(entry.type & SpellTypes_Innate) && entry.priority == 0)
if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0)
entry.priority = 1;
if (row[9])
+1 -1
View File
@@ -759,7 +759,7 @@ void MobMovementManager::FillCommandStruct(PlayerPositionUpdateServer_Struct *sp
spu->delta_y = FloatToEQ13(dy);
spu->delta_z = FloatToEQ13(dz);
spu->delta_heading = FloatToEQ10(dh);
spu->animation = anim;
spu->animation = (m->IsBot() ? (int)((float)anim / 1.785714f) : anim);
}
void MobMovementManager::UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode)
+5
View File
@@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/eq_stream_ident.h"
#include "../common/patches/patches.h"
#include "../common/rulesys.h"
#include "../common/profanity_manager.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/platform.h"
@@ -350,6 +351,10 @@ int main(int argc, char** argv) {
Log(Logs::General, Logs::Zone_Server, "Loading corpse timers");
database.GetDecayTimes(npcCorpseDecayTimes);
Log(Logs::General, Logs::Zone_Server, "Loading profanity list");
if (!EQEmu::ProfanityManager::LoadProfanityList(&database))
Log(Logs::General, Logs::Error, "Loading profanity list FAILED!");
Log(Logs::General, Logs::Zone_Server, "Loading commands");
int retval = command_init();
if (retval<0)
+140 -86
View File
@@ -975,126 +975,181 @@ void QuestManager::permagender(int gender_id) {
uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) {
QuestManagerCurrentQuestVars();
uint16 book_slot, count;
uint16 spell_id;
int book_slot = initiator->GetNextAvailableSpellBookSlot();
int spell_id = 0;
int count = 0;
uint32 char_id = initiator->CharacterID();
bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals);
bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets);
bool SpellGlobalCheckResult = 0;
bool SpellBucketCheckResult = 0;
bool SpellGlobalCheckResult = false;
bool SpellBucketCheckResult = false;
for ( ; spell_id < SPDAT_RECORDS && book_slot < EQEmu::spells::SPELLBOOK_SIZE; ++spell_id) {
if (book_slot == -1) {
initiator->Message(
13,
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
spell_id
);
break;
}
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
initiator->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
return count;
}
if (book_slot < 0 || book_slot >= EQEmu::spells::SPELLBOOK_SIZE) {
initiator->Message(13, "FATAL ERROR: Book slot out-of-range (slot: %i, min: 0, max: %i)", book_slot, EQEmu::spells::SPELLBOOK_SIZE);
return count;
}
for(spell_id = 0, book_slot = initiator->GetNextAvailableSpellBookSlot(), count = 0; spell_id < SPDAT_RECORDS && book_slot < EQEmu::spells::SPELLBOOK_SIZE; spell_id++, book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot))
{
if
(
spells[spell_id].classes[WARRIOR] != 0 && //check if spell exists
spells[spell_id].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
spells[spell_id].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
spells[spell_id].skill != 52 &&
spells[spell_id].effectid[EFFECT_COUNT - 1] != 10
)
{
if (book_slot == -1) //no more book slots
while (true) {
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
break;
if(!IsDiscipline(spell_id) && !initiator->HasSpellScribed(spell_id)) { //isn't a discipline & we don't already have it scribed
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] > max_level) // maximum level
break;
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] < min_level) // minimum level
break;
if (spells[spell_id].skill == 52)
break;
if (spells[spell_id].effectid[EFFECT_COUNT - 1] == 10)
break;
uint16 spell_id_ = (uint16)spell_id;
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
initiator->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
return count;
}
if (!IsDiscipline(spell_id_) && !initiator->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed
if (SpellGlobalRule) {
// Bool to see if the character has the required QGlobal to scribe it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id, char_id);
// bool to see if the character has the required QGlobal to scribe it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id_, char_id);
if (SpellGlobalCheckResult) {
initiator->ScribeSpell(spell_id, book_slot);
count++;
initiator->ScribeSpell(spell_id_, book_slot);
++count;
}
} else if (SpellBucketRule) {
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id, char_id);
}
else if (SpellBucketRule) {
// bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id_, char_id);
if (SpellBucketCheckResult) {
initiator->ScribeSpell(spell_id, book_slot);
count++;
initiator->ScribeSpell(spell_id_, book_slot);
++count;
}
} else {
initiator->ScribeSpell(spell_id, book_slot);
count++;
}
else {
initiator->ScribeSpell(spell_id_, book_slot);
++count;
}
}
break;
}
book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot);
}
return count; //how many spells were scribed successfully
return count; // how many spells were scribed successfully
}
uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
QuestManagerCurrentQuestVars();
uint16 count;
uint16 spell_id;
int spell_id = 0;
int count = 0;
uint32 char_id = initiator->CharacterID();
bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals);
bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets);
bool SpellGlobalCheckResult = 0;
bool SpellBucketCheckResult = 0;
bool SpellGlobalCheckResult = false;
bool SpellBucketCheckResult = false;
for(spell_id = 0, count = 0; spell_id < SPDAT_RECORDS; spell_id++)
{
if
(
spells[spell_id].classes[WARRIOR] != 0 && //check if spell exists
spells[spell_id].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
spells[spell_id].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
spells[spell_id].skill != 52 &&
( !RuleB(Spells, UseCHAScribeHack) || spells[spell_id].effectid[EFFECT_COUNT - 1] != 10 )
)
{
if(IsDiscipline(spell_id)){
//we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little
for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) {
if(initiator->GetPP().disciplines.values[r] == spell_id) {
initiator->Message(13, "You already know this discipline.");
break; //continue the 1st loop
}
else if(initiator->GetPP().disciplines.values[r] == 0) {
if (SpellGlobalRule) {
// Bool to see if the character has the required QGlobal to train it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id, char_id);
if (SpellGlobalCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id;
database.SaveCharacterDisc(char_id, r, spell_id);
initiator->SendDisciplineUpdate();
initiator->Message(0, "You have learned a new discipline!");
count++; //success counter
}
break; //continue the 1st loop
} else if (SpellBucketRule) {
// Bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id, char_id);
if (SpellBucketCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id;
database.SaveCharacterDisc(char_id, r, spell_id);
initiator->SendDisciplineUpdate();
initiator->Message(0, "You have learned a new discipline!");
count++;
}
break;
}
else {
initiator->GetPP().disciplines.values[r] = spell_id;
database.SaveCharacterDisc(char_id, r, spell_id);
initiator->SendDisciplineUpdate();
bool change = false;
for( ; spell_id < SPDAT_RECORDS; ++spell_id) {
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
initiator->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
return count;
}
while (true) {
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
break;
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] > max_level) // maximum level
break;
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] < min_level) // minimum level
break;
if (spells[spell_id].skill == 52)
break;
if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effectid[EFFECT_COUNT - 1] == 10)
break;
uint16 spell_id_ = (uint16)spell_id;
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
initiator->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
return count;
}
if (!IsDiscipline(spell_id_))
break;
for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) {
if (initiator->GetPP().disciplines.values[r] == spell_id_) {
initiator->Message(13, "You already know this discipline.");
break; // continue the 1st loop
}
else if (initiator->GetPP().disciplines.values[r] == 0) {
if (SpellGlobalRule) {
// bool to see if the character has the required QGlobal to train it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id_, char_id);
if (SpellGlobalCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id_;
database.SaveCharacterDisc(char_id, r, spell_id_);
change = true;
initiator->Message(0, "You have learned a new discipline!");
count++; //success counter
break; //continue the 1st loop
++count; // success counter
}
} //if we get to this point, there's already a discipline in this slot, so we skip it
break; // continue the 1st loop
}
else if (SpellBucketRule) {
// bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id_, char_id);
if (SpellBucketCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id_;
database.SaveCharacterDisc(char_id, r, spell_id_);
change = true;
initiator->Message(0, "You have learned a new discipline!");
++count;
}
break;
}
else {
initiator->GetPP().disciplines.values[r] = spell_id_;
database.SaveCharacterDisc(char_id, r, spell_id_);
change = true;;
initiator->Message(0, "You have learned a new discipline!");
++count; // success counter
break; // continue the 1st loop
}
}
}
break;
}
}
return count; //how many disciplines were learned successfully
if (change)
initiator->SendDisciplineUpdate();
return count; // how many disciplines were learned successfully
}
void QuestManager::unscribespells() {
QuestManagerCurrentQuestVars();
initiator->UnscribeSpellAll();
}
}
void QuestManager::untraindiscs() {
QuestManagerCurrentQuestVars();
@@ -2135,8 +2190,7 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
return false;
}
NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender);
Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator);
Bot* NewBot = new Bot(Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender), initiator);
if(NewBot)
{
+2 -4
View File
@@ -5053,7 +5053,7 @@ void Client::UnscribeSpell(int slot, bool update_client)
m_pp.spell_book[slot] = 0xFFFFFFFF;
database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot);
if(update_client)
if(update_client && slot < EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize)
{
auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct));
DeleteSpell_Struct* del = (DeleteSpell_Struct*)outapp->pBuffer;
@@ -5066,9 +5066,7 @@ void Client::UnscribeSpell(int slot, bool update_client)
void Client::UnscribeSpellAll(bool update_client)
{
int i;
for(i = 0; i < EQEmu::spells::SPELLBOOK_SIZE; i++)
for(int i = 0; i < EQEmu::spells::SPELLBOOK_SIZE; i++)
{
if(m_pp.spell_book[i] != 0xFFFFFFFF)
UnscribeSpell(i, update_client);
+23
View File
@@ -1063,3 +1063,26 @@ void NPC::RestoreGuardSpotCharm()
{
m_GuardPoint = m_GuardPointSaved;
}
/******************
* Bot-specific overloads to make them play nice with the new movement system
*/
#ifdef BOTS
#include "bot.h"
void Bot::WalkTo(float x, float y, float z)
{
if (IsSitting())
Stand();
Mob::WalkTo(x, y, z);
}
void Bot::RunTo(float x, float y, float z)
{
if (IsSitting())
Stand();
Mob::RunTo(x, y, z);
}
#endif
+6
View File
@@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/misc_functions.h"
#include "../common/rulesys.h"
#include "../common/servertalk.h"
#include "../common/profanity_manager.h"
#include "client.h"
#include "corpse.h"
@@ -793,6 +794,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
}
break;
}
case ServerOP_RefreshCensorship: {
if (!EQEmu::ProfanityManager::LoadProfanityList(&database))
Log(Logs::General, Logs::Error, "Received request to refresh the profanity list..but, the action failed");
break;
}
case ServerOP_ChangeWID: {
if (pack->size != sizeof(ServerChangeWID_Struct)) {
std::cout << "Wrong size on ServerChangeWID_Struct. Got: " << pack->size << ", Expected: " << sizeof(ServerChangeWID_Struct) << std::endl;
+19 -8
View File
@@ -1225,17 +1225,28 @@ bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Str
"`character_spells` "
"WHERE `id` = %u ORDER BY `slot_id`", character_id);
auto results = database.QueryDatabase(query);
int i = 0;
/* Initialize Spells */
for (i = 0; i < EQEmu::spells::SPELLBOOK_SIZE; i++){
pp->spell_book[i] = 0xFFFFFFFF;
}
memset(pp->spell_book, 0xFF, (sizeof(uint32) * EQEmu::spells::SPELLBOOK_SIZE));
// We have the ability to block loaded spells by max id on a per-client basis..
// but, we do not have to ability to keep players from using older clients after
// they have scribed spells on a newer one that exceeds the older one's limit.
// Load them all so that server actions are valid..but, nix them in translators.
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
if (i < EQEmu::spells::SPELLBOOK_SIZE && atoi(row[1]) <= SPDAT_RECORDS){
pp->spell_book[i] = atoi(row[1]);
}
int idx = atoi(row[0]);
int id = atoi(row[1]);
if (idx < 0 || idx >= EQEmu::spells::SPELLBOOK_SIZE)
continue;
if (id < 3 || id > SPDAT_RECORDS) // 3 ("Summon Corpse") is the first scribable spell in spells_us.txt
continue;
pp->spell_book[idx] = id;
}
return true;
}