NPC Actions (Programming)
---++NPC Action Programming
Having read ProgActions, you understand how to queue actions and how to create your own custom actions, and you know how these actions get executed. Now you'd like to know how to get your custom NPCs to perform actions.
The heart and soul of any NPC is in one verb: <B>:suggest_next_action()</B>. When we talked about how :process_queue() handles your action queue, for simplicity we left out one critical step. When the queue is empty, :suggest_next_action() is called on the actor to find out if it has any ideas for new actions it needs to perform. This is how auto-attacking happens in combat -- you only type 'attack' once, but your player object keeps track of who you're fighting, and your :suggest_next_action() constantly generates new 'attack' actions every time your queue empties out, until the fight is over. The default $monsters also use this to decide to do all sorts of things: wandering, scavenging, attacking enemies, and so on.
If we look at the default $monster:suggest_next_action(), we can see that it asks several sub-verbs for their opinion in order to decide what to do:
#706:suggest_next_action this none this 1: if (r = this:_fight()) 2: return {r, 0}; 3: elseif (r = this:_aggression()) 4: return {r, 0}; 5: elseif (r = this:_scavenge()) 6: this.lastaction = "s"; 7: return {r, 2}; 8: elseif (r = this:_comment()) 9: this.lastaction = "c"; 10: return {r, 5}; 11: elseif (r = this:_wander()) 12: this.lastaction = "w"; 13: return {r, this.wander_speed}; 14: endif
- suggest_next_action()'s ultimate job is to return back a two-element list -- an action spec, and a time to delay before actually executing the action. How is this any different from the action's duration, you ask? Simple: if your monster is doing things all the time, such as wandering, you probably don't want it to be doing those things as fast as possible. It's not realistic, and it's hard on the server. :) So we pass back a delay time to put between actions, to avoid getting too frantic.
As you can see, it walks down its list of sub-modules, asking each in turn whether it has an action to suggest. The order in which we ask indicates our priorities. :_fight() gets queried first, because that's the verb that generates 'attack' actions when we're fighting people. Usually this is more important than any wandering or scavenging we'd like to do.
When we create our own custom behavior, we can either override one of these sub-verbs, or override :suggest_next_action() itself. If we do the latter, we probably want to make ours look a lot like this one, calling several sub-modules in turn. You can maintain existing functionality by calling the existing sub-modules as shown above -- for instance, it's a good idea to still call :_fight() first so when you're in combat you keep swinging and don't try to do something else.
The only rules about what all this code can do are as follows: <UL> <LI>You must return an action spec and a delay time back from :suggest_next_action(). <LI>You CANNOT suspend() anywhere in these verbs. This changes the task_id of the actor's :process_queue(), which is poison to the action system. Don't do it. </UL>
- suggest_next_action() isn't the only way to give your NPCs intelligence. In addition to initiating their own actions, you may want them to respond to actions happening around them. Click ahead to ProgActionsEvents.
-- Main.GilMore - 30 Apr 2004