I'll try that. Never made one before and just googled how.
Code:
Index: bot.cpp
===================================================================
--- bot.cpp (revision 1958)
+++ bot.cpp (working copy)
@@ -1212,7 +1212,8 @@
mitigation += GetLevel() * 13/10; //the 13/10 might be wrong, but it is close...
}
int displayed = 0;
- displayed += ((avoidance+mitigation)*1000)/847; //natural AC
+ //displayed += ((avoidance+mitigation)*1000)/847;
+ displayed += ((avoidance+mitigation)*500)/950; //natural AC
//Iksar AC, untested
if(GetRace() == IKSAR)
@@ -10076,6 +10077,7 @@
c->Message(0, "#bot botgroup help - Displays the commands available to manage BOT ONLY groups.");
c->Message(0, "#bot mana [<bot name or target> | all] - Displays a mana report for all your spawned bots.");
c->Message(0, "#bot [hair|haircolor|beard|beardcolor|face|eyes|heritage|tattoo|details <value>] - Change your BOTs appearance.");
+ c->Message(0, "#bot setfollowdistance ### - sets target bots follow distance to ### (ie 30 or 250).");
// TODO:
// c->Message(0, "#bot illusion <bot/client name or target> - Enchanter Bot cast an illusion buff spell on you or your target.");
c->Message(0, "#bot pull [<bot name>] [target] - Bot Pulling Target NPC's");
@@ -10453,6 +10455,31 @@
return;
}
+ //Criimson added Bot follow distance - SetFollowDistance
+ if(!strcasecmp(sep->arg[1], "setfollowdistance")) {
+ if((c->GetTarget() == NULL) || (c->GetTarget() == c) || (!c->GetTarget()->IsBot()) ) {
+ c->Message(15, "You must target a bot!");
+ }
+ else {
+ int32 BotFollowDistance = atoi(sep->arg[2]);
+ c->GetTarget()->SetFollowDistance(BotFollowDistance);
+ }
+
+ return;
+ }
+
+
+ if(!strcasecmp(sep->arg[1], "picklock")) {
+ if((c->GetTarget() == NULL) || (c->GetTarget() == c) || !c->GetTarget()->IsBot() || (c->GetTarget()->GetClass() != ROGUE)) {
+ c->Message(15, "You must target a rogue bot!");
+ }
+ else {
+ entity_list.BotPickLock(c->GetTarget()->CastToBot());
+ }
+
+ return;
+ }
+
if(!strcasecmp(sep->arg[1], "archery")) {
if((c->GetTarget() == NULL) || (c->GetTarget() == c) || !c->GetTarget()->IsBot()) {
c->Message(15, "You must target a bot!");
@@ -10910,6 +10937,14 @@
Tracker = g->members[i];
TrackerClass = RANGER;
break;
+ //Criimson: Reordered code - was giving priority to bard
+ case DRUID:
+ // Unless we have a ranger, druid is next best.
+ if(TrackerClass != RANGER) {
+ Tracker = g->members[i];
+ TrackerClass = DRUID;
+ }
+ break;
case BARD:
// If we haven't found a tracker yet, use bard.
if(TrackerClass == 0) {
@@ -10917,13 +10952,7 @@
TrackerClass = BARD;
}
break;
- case DRUID:
- // Unless we have a ranger, druid is next best.
- if(TrackerClass != RANGER) {
- Tracker = g->members[i];
- TrackerClass = DRUID;
- }
- break;
+
default:
break;
}
@@ -11346,9 +11375,17 @@
else {
if(c->IsGrouped()) {
Group *g = c->GetGroup();
+ bool DoesGroupHaveEnchanter = false;
for(int i=0; i<MAX_GROUP_MEMBERS; i++) {
- if(g && g->members[i] && g->members[i]->IsBot() && ((g->members[i]->GetClass() == ENCHANTER) || g->members[i]->GetClass() == CLERIC)) {
+ if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) {
+ DoesGroupHaveEnchanter = true;
+ }
+ }
+
+ for(int i=0; i<MAX_GROUP_MEMBERS; i++) {
+ //Criimson - seperated cleric and chanter so chanter is primary
+ if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == ENCHANTER)) {
Bot *pacer = g->members[i]->CastToBot();
pacer->Say("Trying to pacify %s \n", target->GetCleanName());
@@ -11356,12 +11393,29 @@
if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm))
//if(pacer->IsPacified(target))
c->Message(0, "I have successfully pacified %s.", target->GetCleanName());
+ return;
/*else
c->Message(0, "I failed to pacify %s.", target->GetCleanName());*/
}
else
c->Message(0, "I failed to pacify %s.", target->GetCleanName());
}
+ //Criimson - seperated cleric and chanter so chanter is primary
+ if(g && g->members[i] && g->members[i]->IsBot() && (g->members[i]->GetClass() == CLERIC) && (DoesGroupHaveEnchanter == false)) {
+ Bot *pacer = g->members[i]->CastToBot();
+ pacer->Say("Trying to pacify %s \n", target->GetCleanName());
+
+ if(pacer->Bot_Command_CalmTarget(target)) {
+ if(target->FindType(SE_Lull) || target->FindType(SE_Harmony) || target->FindType(SE_Calm))
+ //if(pacer->IsPacified(target))
+ c->Message(0, "I have successfully pacified %s.", target->GetCleanName());
+ return;
+ /*else
+ c->Message(0, "I failed to pacify %s.", target->GetCleanName());*/
+ }
+ else
+ c->Message(0, "I failed to pacify %s.", target->GetCleanName());
+ }
/*else
c->Message(15, "You must have an Enchanter or Cleric in your group.");*/
}
@@ -13255,7 +13309,8 @@
int8 botCasterClass = caster->GetClass();
- if( botCasterClass == CLERIC || botCasterClass == DRUID || botCasterClass == SHAMAN || botCasterClass == PALADIN || botCasterClass == BEASTLORD || botCasterClass == RANGER) {
+ //CRIIMSON: Changed so heal based on health percentage is different for hybrids
+ if( botCasterClass == CLERIC || botCasterClass == DRUID || botCasterClass == SHAMAN) {
//If AI_EngagedCastCheck() said to the healer that he had to heal
if( iSpellTypes == SpellType_Heal ) {
// check in group
@@ -13300,6 +13355,52 @@
}
}
+ //CRIIMSON: Changed so heal based on health percentage is different for hybrids
+ if( botCasterClass == PALADIN || botCasterClass == BEASTLORD || botCasterClass == RANGER) {
+ //If AI_EngagedCastCheck() said to the healer that he had to heal
+ if( iSpellTypes == SpellType_Heal ) {
+ // check in group
+ if(caster->HasGroup()) {
+ Group *g = caster->GetGroup();
+
+ if(g) {
+ for(int i = 0; i < MAX_GROUP_MEMBERS; i++) {
+ if(g->members[i] && !g->members[i]->qglobal) {
+ if(g->members[i]->IsClient() && g->members[i]->GetHPRatio() < 20) {
+ if(caster->AICastSpell(g->members[i], iChance, SpellType_Heal))
+ return true;
+ }
+ else if((g->members[i]->GetClass() == WARRIOR || g->members[i]->GetClass() == PALADIN || g->members[i]->GetClass() == SHADOWKNIGHT) &&
+ g->members[i]->GetHPRatio() < 20)
+ {
+ if(caster->AICastSpell(g->members[i], 100, SpellType_Heal))
+ return true;
+ }
+ else if(g->members[i]->GetClass() == ENCHANTER && g->members[i]->GetHPRatio() < 20) {
+ if(caster->AICastSpell(g->members[i], 100, SpellType_Heal))
+ return true;
+ }
+ else if(g->members[i]->GetHPRatio() < 20) {
+ if(caster->AICastSpell(g->members[i], 100, SpellType_Heal))
+ return true;
+ }
+ }
+
+ if(g->members[i] && !g->members[i]->qglobal && g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < 20) {
+ if(g->members[i]->GetPet()->GetOwner() != caster && caster->IsEngaged() && g->members[i]->IsCasting() && g->members[i]->GetClass() != ENCHANTER )
+ continue;
+
+ if(caster->AICastSpell(g->members[i]->GetPet(), 100, SpellType_Heal))
+ return true;
+ }
+ }
+ }
+ }
+
+ // TODO: raid heals
+ }
+ }
+
//Ok for the buffs..
if( iSpellTypes == SpellType_Buff) {
// Let's try to make Bard working...
@@ -13332,6 +13433,7 @@
return false;
}
+
Mob* EntityList::GetMobByBotID(uint32 botID) {
Mob* Result = 0;
Index: bot.h
===================================================================
--- bot.h (revision 1958)
+++ bot.h (working copy)
@@ -292,6 +292,7 @@
static bool GroupHasClericClass(Group* group) { return GroupHasClass(group, CLERIC); }
static bool GroupHasDruidClass(Group* group) { return GroupHasClass(group, DRUID); }
static bool GroupHasShamanClass(Group* group) { return GroupHasClass(group, SHAMAN); }
+ static bool GroupHasEnchanterClass(Group* group) { return GroupHasClass(group, ENCHANTER); }
static bool GroupHasPriestClass(Group* group) { return GroupHasClass(group, CLERIC | DRUID | SHAMAN); }
// "GET" Class Methods
Index: botspellsai.cpp
===================================================================
--- botspellsai.cpp (revision 1958)
+++ botspellsai.cpp (working copy)
@@ -253,6 +253,29 @@
continue;
}
+ switch(tar->GetArchetype())
+ {
+ case ARCHETYPE_CASTER:
+ //TODO: probably more caster specific spell effects in here
+ if(IsEffectInSpell(selectedBotSpell.SpellId, SE_AttackSpeed) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ATK) ||
+ IsEffectInSpell(selectedBotSpell.SpellId, SE_STR) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ReverseDS))
+ {
+ continue;
+ }
+ break;
+ case ARCHETYPE_MELEE:
+ //TODO: Include mana regen (breeze/clarity/etc), I don't know what spell effect it is
+ if(IsEffectInSpell(selectedBotSpell.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(selectedBotSpell.SpellId, SE_ManaPool))
+ {
+ continue;
+ }
+ break;
+ case ARCHETYPE_HYBRID:
+ //Hybrids get all buffs
+ default:
+ break;
+ }
+
if(CheckSpellRecastTimers(this, itr->SpellIndex))
{
@@ -297,6 +320,17 @@
checked_los = true;
}
+ if(botClass == CLERIC && this->GetManaRatio() <= 75.0f)
+ {
+ //If we're at 75% mana or below, don't nuke as a cleric or 50% as enchanter
+ break;
+ }
+ if(botClass == ENCHANTER && this->GetManaRatio() <= 50.0f)
+ {
+ //If we're at 75% mana or below, don't nuke as a cleric or 50% as enchanter
+ break;
+ }
+
if(botClass == MAGICIAN || botClass == SHADOWKNIGHT || botClass == NECROMANCER || botClass == PALADIN || botClass == RANGER || botClass == DRUID || botClass == CLERIC) {
if(tar->GetBodyType() == BT_Undead || tar->GetBodyType() == BT_SummonedUndead || tar->GetBodyType() == BT_Vampire)
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Undead);
@@ -532,16 +566,18 @@
Mob* addMob = GetFirstIncomingMobToMez(this, botSpell);
- if(!addMob)
- break;
+ if(!addMob){
+ //Say("!addMob.");
+ break;}
if(!(!addMob->IsImmuneToSpell(botSpell.SpellId, this) && addMob->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
break;
castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost);
+
}
break;
- }
+ }
default: {
break;
}
@@ -722,6 +758,8 @@
mlog(AI__SPELLS, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells.");
+
+
if(botClass == CLERIC) {
if(!AICastSpell(this, 100, SpellType_Heal)) {
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
@@ -1311,7 +1349,7 @@
std::list<NPC*> npc_list;
entity_list.GetNPCList(npc_list);
-
+
for(std::list<NPC*>::iterator itr = npc_list.begin(); itr != npc_list.end(); itr++) {
NPC* npc = *itr;
Index: mob.cpp
===================================================================
--- mob.cpp (revision 1958)
+++ mob.cpp (working copy)
@@ -672,6 +672,53 @@
}
}
+uint8 Mob::GetArchetype() const {
+ switch(class_)
+ {
+ case PALADIN:
+ case RANGER:
+ case SHADOWKNIGHT:
+ case BARD:
+ case BEASTLORD:
+ case PALADINGM:
+ case RANGERGM:
+ case SHADOWKNIGHTGM:
+ case BARDGM:
+ case BEASTLORDGM:
+ return ARCHETYPE_HYBRID;
+ break;
+ case CLERIC:
+ case DRUID:
+ case SHAMAN:
+ case NECROMANCER:
+ case WIZARD:
+ case MAGICIAN:
+ case ENCHANTER:
+ case CLERICGM:
+ case DRUIDGM:
+ case SHAMANGM:
+ case NECROMANCERGM:
+ case WIZARDGM:
+ case MAGICIANGM:
+ case ENCHANTERGM:
+ return ARCHETYPE_CASTER;
+ break;
+ case WARRIOR:
+ case MONK:
+ case ROGUE:
+ case BERSERKER:
+ case WARRIORGM:
+ case MONKGM:
+ case ROGUEGM:
+ case BERSERKERGM:
+ return ARCHETYPE_MELEE;
+ break;
+ default:
+ return ARCHETYPE_HYBRID;
+ break;
+ }
+}
+
void Mob::CreateSpawnPacket(EQApplicationPacket* app, Mob* ForWho) {
app->SetOpcode(OP_NewSpawn);
app->size = sizeof(NewSpawn_Struct);
Index: mob.h
===================================================================
--- mob.h (revision 1958)
+++ mob.h (working copy)
@@ -52,6 +52,18 @@
#define CON_YELLOW 15
#define CON_RED 13
+#define APPEAR_HEIGHT 0x001d
+#define APPEAR_HP_TIC 0x0011
+
+#define ARCHETYPE_HYBRID 1
+#define ARCHETYPE_CASTER 2
+#define ARCHETYPE_MELEE 3
+
+#define CON_GREEN 2
+#define CON_LIGHTBLUE 18
+#define CON_BLUE 4
+
+
//LOS Parameters:
#define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from
#define SEE_POSITION 0.5f //ratio of GetSize() where NPCs try to see for LOS
@@ -669,9 +681,10 @@
void SetZone(int32 zone_id, int32 instance_id);
// neotokyo: moved from client to use in NPC too
- char GetCasterClass() const;
- virtual sint32 CalcMaxMana();
-
+ char GetCasterClass() const;
+ uint8 GetArchetype() const;
+ virtual sint32 CalcMaxMana();
+
inline virtual sint16 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; } // Quagmire - this is NOT the right math
inline virtual sint16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; }
inline virtual sint16 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }