|
|
 |
 |
 |
 |
|
 |
 |
|
 |
 |
|
 |
|
Development::Bug Reports Post detailed bug reports and what you would like to see next in the emu here. |
 |
|
 |

07-25-2008, 06:04 AM
|
 |
Developer
|
|
Join Date: Aug 2006
Location: USA
Posts: 5,946
|
|
After more testing, it seems like recast delay is working properly, but is picky about what kind of spells you set to which type:
From MobAI.cpp:
Code:
const int SpellType_Nuke=1;
const int SpellType_Heal=2;
const int SpellType_Root=4;
const int SpellType_Buff=8;
const int SpellType_Escape=16;
const int SpellType_Pet=32;
const int SpellType_Lifetap=64;
const int SpellType_Snare=128;
const int SpellType_DOT=256;
This list includes most of the spell types people might like to use for NPCs, but it seems like some just don't fit in anywhere. I would like to add a new one for in combat buffs to be used for spells like Yaulp and for Melee Disciplines. Yaulp works ok as a normal buff, but it is a little bit pointless to have NPCs constantly recasting that spell on themselves. They really should only use it during combat, but there currently isn't any way for that to work properly. The main reason I want to add this new category is for Disciplines.
It seems that when you set a discipline to type 1 (nuke), it will cast properly, but since it is self only, it apparently doesn't pass all of the checks for being a "nuke" spell. So, I think the problem is that it is never reaching the point where it is supposed to check the recast delay setting. Maybe the nuke one could just be edited to allow this, or a whole new type could be added:
Code:
const int SpellType_Combat_Buff=512;
Then, I think that would need to be added here in the MobAI.cpp:
Code:
const int SpellTypes_Detrimental = SpellType_Nuke|SpellType_Root|SpellType_Lifetap|SpellType_Snare|SpellType_DOT|SpellType_Combat_Buff;
const int SpellTypes_Beneficial = SpellType_Heal|SpellType_Buff|SpellType_Escape|SpellType_Pet;
Looking at some of the examples, we have:
Buff
Code:
case SpellType_Buff: {
if (
(spells[AIspells[i].spellid].targettype == ST_Target || tar == this)
&& tar->DontBuffMeBefore() < Timer::GetCurrentTime()
&& !tar->IsImmuneToSpell(AIspells[i].spellid, this)
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
&& !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar) //no buffing PC's pets, but they can buff themself
) {
AIDoSpellCast(i, tar, mana_cost, &tar->pDontBuffMeBefore);
return true;
}
break;
}
Nuke
Code:
case SpellType_Nuke: {
if (
manaR >= 40 && (rand()%100) < 50
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
) {
if(!checked_los) {
if(!CheckLosFN(tar))
return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call
checked_los = true;
}
AIDoSpellCast(i, tar, mana_cost);
return true;
}
break;
}
And, I am just guessing that putting Disciplines in Nuke is failing and going to the following:
Code:
default: {
cout<<"Error: Unknown spell type in AICastSpell. caster:"<<this->GetName()<<" type:"<<AIspells[i].type<<" slot:"<<i<<endl;
break;
}
But, the actual checks after a successful cast seem to be here:
Code:
//this gets called from InterruptSpell() for failure or SpellFinished() for success
void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, int8 slot) {
if (slot == 1) {
int32 recovery_time = 0;
if (iCastSucceeded) {
if (casting_spell_AIindex < MAX_AISPELLS) {
recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time;
if (AIspells[casting_spell_AIindex].recast_delay >= 0){
if (AIspells[casting_spell_AIindex].recast_delay <1000)
AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000);
}
else
AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time;
}
if (!IsEngaged())
recovery_time += RandomTimer(2000, 3000);
if (recovery_time < AIautocastspell_timer->GetSetAtTrigger())
recovery_time = AIautocastspell_timer->GetSetAtTrigger();
AIautocastspell_timer->Start(recovery_time, false);
}
else
AIautocastspell_timer->Start(800, false);
casting_spell_AIindex = MAX_AISPELLS;
}
}
So, I think that the only other thing that might need to be added is a new section for Combat_Buffs. Though, I am not sure if I need to add anything somewhere else to get it to work. I think that by copying the settings for buff and calling it Combat_Buff, and then adding Combat_Buff to detrimental spells, it will allow for NPCs to only cast these types of buffs during combat, which is the opposite from normal buffs.
Code:
case SpellType_Combat_Buff: {
if (
(spells[AIspells[i].spellid].targettype == ST_Target || tar == this)
&& tar->DontBuffMeBefore() < Timer::GetCurrentTime()
&& !tar->IsImmuneToSpell(AIspells[i].spellid, this)
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
&& !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar) //no buffing PC's pets, but they can buff themself
) {
AIDoSpellCast(i, tar, mana_cost, &tar->pDontBuffMeBefore);
return true;
}
break;
}
I guess I will test this out on my server and just cross my fingers that I don't break anything lol. I am such a gimpy coder, but this makes at least a little sense to me... If it works, I will post it in server code submissions with better details on how to add this code in, even though everything should already be here.
If anyone else has any input on getting this to work the way I want, I would love to hear it 
|
 |
|
 |

07-25-2008, 06:27 AM
|
 |
Developer
|
|
Join Date: Aug 2006
Location: USA
Posts: 5,946
|
|
Just found 3 more lines that I think needed to be adjusted for it to work:
Code:
|| AIspells[i].type == SpellType_Combat_Buff
Code:
if(!AICastSpell(target, 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Combat_Buff)) {
Code:
if(!AICastSpell(target, 90, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Combat_Buff)) {
Compiling it now and will post if it works or not. Crossing my fingers.
|

07-25-2008, 07:27 AM
|
 |
Developer
|
|
Join Date: Aug 2006
Location: USA
Posts: 5,946
|
|
Well, it compiled fine, but now when I set my NPCs spells to type 512, it doesn't seem to use them at all in combat or out of combat... Guess this needs further looking into, but I think it is closer now heh.
|
 |
|
 |

07-26-2008, 10:50 AM
|
 |
Developer
|
|
Join Date: Aug 2006
Location: USA
Posts: 5,946
|
|
Well, I didn't have to add a new category after-all lol. I found the reason why my recast delays weren't working. It was because I was using recast timers similar to actual disc reuse timers. Some of them were 20 minutes or more. And in seconds, that is 1200.
I looked at recast delay settings in the source and I found this:
MobAI.cpp
Code:
void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, int8 slot) {
if (slot == 1) {
int32 recovery_time = 0;
if (iCastSucceeded) {
if (casting_spell_AIindex < MAX_AISPELLS) {
recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time;
if (AIspells[casting_spell_AIindex].recast_delay >= 0){
if (AIspells[casting_spell_AIindex].recast_delay <1000)
AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000);
}
else
AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time;
}
if (!IsEngaged())
recovery_time += RandomTimer(2000, 3000);
if (recovery_time < AIautocastspell_timer->GetSetAtTrigger())
recovery_time = AIautocastspell_timer->GetSetAtTrigger();
AIautocastspell_timer->Start(recovery_time, false);
}
else
AIautocastspell_timer->Start(800, false);
casting_spell_AIindex = MAX_AISPELLS;
}
}
And I simply changed the check for < 1000 to < 10000 and now my recast delays are working perfectly. Here is the change I made, highlighted in RED:
Code:
void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, int8 slot) {
if (slot == 1) {
int32 recovery_time = 0;
if (iCastSucceeded) {
if (casting_spell_AIindex < MAX_AISPELLS) {
recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time;
if (AIspells[casting_spell_AIindex].recast_delay >= 0){
if (AIspells[casting_spell_AIindex].recast_delay <10000)
AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000);
}
else
AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time;
}
if (!IsEngaged())
recovery_time += RandomTimer(2000, 3000);
if (recovery_time < AIautocastspell_timer->GetSetAtTrigger())
recovery_time = AIautocastspell_timer->GetSetAtTrigger();
AIautocastspell_timer->Start(recovery_time, false);
}
else
AIautocastspell_timer->Start(800, false);
casting_spell_AIindex = MAX_AISPELLS;
}
}
I don't see any reason why this couldn't be added to the source updates. I am not sure why the current limit was set to 1000 or less. I guess it wouldn't even apply in most cases, but disciplines for pets and NPC encounters that might last 20+ minutes could be effected by this.
|
 |
|
 |
 |
|
 |

09-02-2008, 09:31 PM
|
 |
Developer
|
|
Join Date: Aug 2006
Location: USA
Posts: 5,946
|
|
Well, I am starting again to get the new spell type category working for NPCs. I haven't fully decided if I want to just make 1 new category or 2. I think Yaulp would be fine to set as a buff that is used in combat, but disciplines need a little extra rules to them. I currently have my NPC disciplines set to the nuke category and that almost works the way that I want with the only problem being that they will stack multiple discs at once which makes them way too overpowered. The idea is to make a new category just for discs that will do combat buffs, but only do 1 at a time and won't use another until the previous one has worn off.
I think the code I posted above for combat_buffs is probably very close to working for this. I just need to figure out why the new category wasn't being used when I set it for an NPC in their spell list. I imagine I am only missing something minor to get it to start using them. Then, the only other thing I would need is a way to make sure only 1 combat buff (disc) is being used at a time.
Of course since KLS added a new field for dispells and it is 512, then the new discipline field would have to use 1024. And if I added a combat_buff field as well for Yaulp and maybe others, it would have to be 2048.
If anyone has time to look over my code, I think these would make for some nice new features to help expand what NPCs can do without having to make quest scripts for every mob.
|
 |
|
 |

09-02-2008, 10:03 PM
|
Administrator
|
|
Join Date: Sep 2006
Posts: 1,348
|
|
Sir, get out of my head. I was seriously just thinking about expanding the npc cast system further with in combat buffs classification only like 2 days ago.
|
 |
|
 |

09-02-2008, 11:44 PM
|
 |
Developer
|
|
Join Date: Aug 2006
Location: USA
Posts: 5,946
|
|
LMAO! SWEET! You keep saving me from work and trying to learn to program by examples from the source (which is no easy task).
So far, this is what I came up with, but it is missing the most important part (Changes are in RED):
MobAI.cpp
Code:
const int MobAISpellRange=100; // max range of buffs
const int SpellType_Nuke=1;
const int SpellType_Heal=2;
const int SpellType_Root=4;
const int SpellType_Buff=8;
const int SpellType_Escape=16;
const int SpellType_Pet=32;
const int SpellType_Lifetap=64;
const int SpellType_Snare=128;
const int SpellType_DOT=256;
const int SpellType_Dispel=512;
const int SpellType_Disc=1024;
const int SpellType_ComBuff=2048;
const int SpellTypes_Detrimental = SpellType_Nuke|SpellType_Root|SpellType_Lifetap|SpellType_Snare|SpellType_DOT|SpellType_Dispel|SpellType_Disc|SpellType_ComBuff;
const int SpellTypes_Beneficial = SpellType_Heal|SpellType_Buff|SpellType_Escape|SpellType_Pet;
This part is the main section and this is where the actual adjustments need to be made to tell the spells when to be used. This is not complete code in this section:
Code:
case SpellType_Disc: {
if (
(spells[AIspells[i].spellid].targettype == ST_Target || tar == this)
&& tar->DontBuffMeBefore() < Timer::GetCurrentTime()
&& !tar->IsImmuneToSpell(AIspells[i].spellid, this)
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
&& !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar) //no buffing PC's pets, but they can buff themself
) {
AIDoSpellCast(i, tar, mana_cost, &tar->pDontBuffMeBefore);
return true;
}
break;
}
case SpellType_ComBuff: {
if (
(spells[AIspells[i].spellid].targettype == ST_Target || tar == this)
&& tar->DontBuffMeBefore() < Timer::GetCurrentTime()
&& !tar->IsImmuneToSpell(AIspells[i].spellid, this)
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
&& !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar) //no buffing PC's pets, but they can buff themself
) {
AIDoSpellCast(i, tar, mana_cost, &tar->pDontBuffMeBefore);
return true;
}
break;
}
Code:
if( AIspells[i].type == SpellType_Nuke
|| AIspells[i].type == SpellType_Root
|| AIspells[i].type == SpellType_Lifetap
|| AIspells[i].type == SpellType_Snare
|| AIspells[i].type == SpellType_DOT
|| AIspells[i].type == SpellType_Dispel
|| AIspells[i].type == SpellType_Disc
|| AIspells[i].type == SpellType_ComBuff
Code:
bool NPC::AI_EngagedCastCheck() {
if (AIautocastspell_timer->Check(false)) {
_ZP(Mob_AI_Process_engaged_cast);
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
mlog(AI__SPELLS, "Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells.");
// try casting a heal or gate
if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Escape)) {
// try casting a heal on nearby
if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) {
//nobody to heal, try some detrimental spells.
if(!AICastSpell(target, 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Disc | SpellType_ComBuff)) {
//no spell to cast, try again soon.
AIautocastspell_timer->Start(RandomTimer(500, 1000), false);
}
} //else, spell casting finishing will reset the timer.
}
return(true);
}
return(false);
}
bool NPC::AI_PursueCastCheck() {
if (AIautocastspell_timer->Check(false)) {
_ZP(Mob_AI_Process_pursue_cast);
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
mlog(AI__SPELLS, "Engaged (pursuing) autocast check triggered. Trying to cast offensive spells.");
if(!AICastSpell(target, 90, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Disc | SpellType_ComBuff)) {
//no spell cast, try again soon.
AIautocastspell_timer->Start(RandomTimer(500, 2000), false);
} //else, spell casting finishing will reset the timer.
return(true);
}
return(false);
}
I imagine that combat buffs wouldn't be very hard to add. They would work like normal buffs, but only be cast during combat (mostly for Yaulp-Like spells with short duration and maybe some damage shields). The trickier one might be Disciplines, but I doubt it would be hard for KLS to do. Basically, Disciplines would need to cast the first disc and then not cast the second one until the buff duration was finished on the previous one. Or, they could all be treated like the same buff so that it won't cast another (cause it thinks of it as the same spell) until the buff timer fades. The only requirement for disciplines is that only one can be up at a time.
LOL, I am still laughing at KLS! I am just so glad to hear that I don't have to figure this out myself lol. Not that it would be a ton of work to do, cause I imagine it could be done pretty quick for someone with programming skill. But, mostly cause I am working on a new zone that will be more dynamic than any I have done so far and having more options for spells lists (especially disciplines) will really add a cool dynamic to the zone. My spell lists are done, but with them stacking discs, it makes them impossible to tune the encounters.
|
 |
|
 |
Thread Tools |
|
Display Modes |
Hybrid Mode
|
Posting Rules
|
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts
HTML code is Off
|
|
|
All times are GMT -4. The time now is 11:12 PM.
|
|
 |
|
 |
|
|
|
 |
|
 |
|
 |