Oh, no, it wouldn't be complicated at all! Here's what I would do to simulate the ability to call scripts at runtime, like your suggestion:
My script system already mimics a simple CPU, with two math registers (A and B), the status register (F), and the program counter (PC). If you want one AI to be able to call another AI, you would have to treat each different AI state routine just as a normal compiler would treat different routines in a program. Thus, you would need to add a stack, and a stack pointer (SP). Whenever you call another AI state///routine, you would simply push the current PC value onto the stack, and then set the PC to the beginning of the new state (a jump command).
The following syntax will be different from the one I've already posted, to make it more readable.
SKEL_IDLE:
lbl TOP
tEnemy 0 0
if target <> 0
call SKEL_ATTACK
end if
goto TOP
SKEL_ATTACK:
if distance > 3
approach 5
else
melee1
end if
return
Would compile into something like this://entry point for SKEL_IDLE: instruction # 0
//since this is the base routine, there is no need to save registers.
//lbl TOP, instruction # 0
00: tEnemy 0 0
03: move A target //set register A = target
06: compare A 0 //the simulated cpu compares target and 0, and stores the result of the compare in the status register
09: jump_equals 21 //use the reverse of the operation as the compare. If the compare operator equals 0, then skip the next lines.
11: set A PC //set A = PC
14: add A 5 //add 5 to A, so that the value of the stored PC will skip the store instruction and the set instruction.
//Otherwise, when we pop the PC from the stack, it will enter a loop and never exit.
//The amount of instructions in a push + set instruction = 5
17: push PC //save the PC to the stack
19: jump 24 //move to the next routine
21: set PC 0 //goto TOP
//entrypoint for SKEL_ATTACK: instruction # 24
24: push A //save A, as we will use it in this routine
26: move A distance //copy distance into A
28: compare A 3
31: jump_lessthan 37 //if A is less than 3, jump to melee attack
33: approach 5 //else ignore the jump and simply approach a maximum of five steps
35: jump 38 //now jump to the end of the routine
37: melee1
38: pop A //restore A
40: RET //return to the calling sub (i.e. pop PC)
Thus, by simply adding all the scripts together into a megaprogram and popping and pushing the PC, just like a real program acts, we can move between different AI states, without maintaining a list of AIs, or using select case statements!
Of course, as long as you're adding a stack and stack pointer, you might as well give the AI some memory to work with... nothing fancy, just 16 or 32 words worth of scratch memory to store previous targets and whatnot. But every time you add a new feature to the scripting system, the script interpretter slows down a bit. Thus, I want to keep my AI interpretter as simple and FAST as possible. Meanwhile, my Interface scripting system has enough features to implement checkers (which I've already done, although I'm not sure how to go about programming an AI for the checkers, so you can only play with yourself... =D).
What do you think, aab? Did I cover your question? =)