Here's one with a few changes:
Better comments
Returns 0 if the spell can't be cast
RUNES function commented out but explained and included
EDIT: I have a 3rd function I use to determine if the spell is in skill range. I use this outside NPCCAST beforehand in the AI to see if it's even an option before it runs the mana test and sets the spell.
For now I put it in the main function. You can remove this part if you want to use NPCCANCAST like I do. The mana check should always stay in NPCCAST; the game will fizzle an NPC that tries to cast above skill and although it deducts mana it doesn't actually ensure there is enough allowing them cast at 0 mana without it.
A simple example of using CANCAST in AI:
Code:
[FUNCTION npccast_sorcery_offense]
IF (<I.UID> == <OBJ.UID>)
return 0
ENDIF
IF (<npccancast s_summon_chaos_daemon>) && !(<R4>) && !(<FLAGS>&statf_conjured)
npccast s_summon_chaos_daemon,<obj>
return 1
ELIF (<npccancast s_detonation>) && (<attacker> > 1) && !(<R3>)
npccast s_detonation,<obj>
return 1
ELIF (<npccancast s_summon_djinn>) && !(<R3>) && !(<FLAGS>&statf_conjured)
npccast s_summon_djinn,<obj>
return 1
ELIF (<npccancast s_summon_efreet>) && !(<R3>) && !(<FLAGS>&statf_conjured)
npccast s_summon_efreet,<obj>
return 1
ELIF (<npccancast s_summon_hellhound>) && !(<R3>) && !(<FLAGS>&statf_conjured)
npccast s_summon_hellhound,<obj>
return 1
ELIF (<npccancast s_fire_bolt>)
npccast s_fire_bolt,<obj>
return 1
ENDIF
return 0
If goes down this list finding the first spell that isn't too difficult to cast and isn't randomly selected against or innapproriate to the situation. So they favor the most powerful spell the can cast but random selection keeps it interesting.
(Appropriate here means not using AoE against one target and not letting summons cast summons because that's just plain wrong)
I'm still working on the system. I should probably adjust this to look for a new spell if npccast returns 0 (not enough mana) but I don't want to overwhelm people.
The major functions
Code:
[FUNCTION npccast]
// Determine spell and skill used to cast
LOCAL.SPELL = <EVAL <ARGV[0]> &~ 0ff000000>
LOCAL.SKILL = <EVAL (<STREAT <SERV.SPELL.<LOCAL.SPELL>.SKILLREQ>>) &~ 0ff000000>
IF (<SERV.SPELL.<LOCAL.SPELL>.MANAUSE> > <MANA>)
RETURN 0
ENDIF
//You can remove this if you use NPCCANCAST to check first
IF (<FVAL <STRARG <SERV.SPELL.<LOCAL.SPELL>.SKILLREQ>>+50> > <I.<SERV.SKILL.<LOCAL.SKILL>.KEY>>)
RETURN 0
ENDIF
//Uncomment the following lines if you want NPCs to say WoP, you'll need the RUNES function
//IF (<EVAL STRLEN(<SERV.SPELL(<LOCAL.SPELL>).RUNES>)> > 0)
// SAY <RUNES <LOCAL.SPELL>>
//ENDIF
//If we didn't get a valid skill, default to Magery
IF !(<LOCAL.SKILL>)
LOCAL.SKILL = 25
ENDIF
// This is part of my casting management, you may also find it useful in systems
TAG.LASTCASTING=<eval (<SERV.TIME>+<SERV.SPELL.<LOCAL.SPELL>.CAST_TIME>)>
TAG.SPELLCASTING=<LOCAL.SPELL>
// Set spell target
IF (<ARGV> > 2) //use coords if given
ACT = 0
ACTP = <ARGV[1]>, <ARGV[2]>, <ARGV[3]>, <ARGV[4]>
ELIF (<SERV.SPELL.<LOCAL.SPELL>.FLAGS> & spellflag_targ_xyz) //use coords for ground targ spells regardless
ACTP = <ACT.P>
ACT = 0
ELSE
REF1 = <ARGV[1]>
IF !(<REF1.UID>)
REF1 = <ACT.UID>
IF !(<REF1.UID>)
REF1 = <UID>
ENDIF
ENDIF
ACT = <REF1.UID>
ACTP = <REF1.P>
ENDIF
// Set character action
ACTPRV = <UID>
ACTARG1 = <LOCAL.SPELL>
ACTION = <LOCAL.SKILL>
RETURN 1
This function returns the WoP for a given spell. I use this in my custom spellbooks and for NPC casting.
Code:
[Function RUNES]
LOCAL.LENGTH = <EVAL STRLEN(<SERV.SPELL(<args>).RUNES>)>
LOCAL.CURRUNE=0
local.RUNESTRING="0"
WHILE (<eval <LOCAL.CURRUNE>> <= <eval <LOCAL.LENGTH>>)
IF (strmatch(*0*,<LOCAL.runestring>))
IF (strmatch(A,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="An "
ELSEIF (strmatch(B,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Bet "
ELSEIF (strmatch(C,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Corp "
ELSEIF (strmatch(D,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Des "
ELSEIF (strmatch(E,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Ex "
ELSEIF (strmatch(F,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Flam "
ELSEIF (strmatch(G,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Grav "
ELSEIF (strmatch(H,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Hur "
ELSEIF (strmatch(I,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="In "
ELSEIF (strmatch(J,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Jux "
ELSEIF (strmatch(K,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Kal "
ELSEIF (strmatch(L,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Lor "
ELSEIF (strmatch(M,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Mani "
ELSEIF (strmatch(N,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Nox "
ELSEIF (strmatch(O,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Ort "
ELSEIF (strmatch(P,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Por "
ELSEIF (strmatch(Q,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Quas "
ELSEIF (strmatch(R,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Rel "
ELSEIF (strmatch(S,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Sanct "
ELSEIF (strmatch(T,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Tym "
ELSEIF (strmatch(U,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Uus "
ELSEIF (strmatch(V,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Vas "
ELSEIF (strmatch(W,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Wis "
ELSEIF (strmatch(X,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Xen "
ELSEIF (strmatch(Y,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Ylem "
ELSEIF (strmatch(Z,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="Zu "
ENDIF
ELSE
IF (strmatch(A,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>An "
ELSEIF (strmatch(B,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Bet "
ELSEIF (strmatch(C,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Corp "
ELSEIF (strmatch(D,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Des "
ELSEIF (strmatch(E,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Ex "
ELSEIF (strmatch(F,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Flam "
ELSEIF (strmatch(G,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Grav "
ELSEIF (strmatch(H,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Hur "
ELSEIF (strmatch(I,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>In "
ELSEIF (strmatch(J,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Jux "
ELSEIF (strmatch(K,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Kal "
ELSEIF (strmatch(L,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Lor "
ELSEIF (strmatch(M,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Mani "
ELSEIF (strmatch(N,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Nox "
ELSEIF (strmatch(O,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Ort "
ELSEIF (strmatch(P,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Por "
ELSEIF (strmatch(Q,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Quas "
ELSEIF (strmatch(R,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Rel "
ELSEIF (strmatch(S,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Sanct "
ELSEIF (strmatch(T,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Tym "
ELSEIF (strmatch(U,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Uus "
ELSEIF (strmatch(V,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Vas "
ELSEIF (strmatch(W,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Wis "
ELSEIF (strmatch(X,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Xen "
ELSEIF (strmatch(Y,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Ylem "
ELSEIF (strmatch(Z,<STRSUB <eval <LOCAL.CURRUNE>> 1 <SERV.SPELL(<args>).RUNES>>))
local.RUNESTRING="<local.RUNESTRING>Zu "
ENDIF
ENDIF
LOCAL.CURRUNE += 1
ENDWHILE
RETURN <local.RUNESTRING>
Code:
[FUNCTION npccancast]
LOCAL.SPELL = <EVAL <args>>
LOCAL.SKILL = <EVAL (<STREAT <SERV.SPELL.<LOCAL.SPELL>.SKILLREQ>>)>
IF !(<SERV.SPELL.<LOCAL.SPELL>.MANAUSE> > <MANA>) && !(<FVAL <STRARG <SERV.SPELL.<LOCAL.SPELL>.SKILLREQ>>+50> > <I.<SERV.SKILL.<LOCAL.SKILL>.KEY>>)
RETURN 1
ELSE
RETURN 0
ENDIF