generic actor #92

Parent #1Owner #361Flags read, fertileSource hellcore/hellcore.db

Aliases: generic actor

16 verbs · 19 properties · 2 children

Verbs

VerbSpecFlagsDefinerLines
process_queuethis none thisrxd#92159
fork_process_queuethis none thisrx#9214
queue_actionthis none thisrxd#9227
suggest_next_actionthis none thisrxd#921
qu*eueany none nonerxd#9227
doing_msgthis none thisrxd#9214
clear_queuethis none thisrxd#925
cancel_current_actionthis none thisrxd#9232
jumpstartthis none thisrxd#9214
prequeue_actionthis none thisrxd#9216
backup_queuethis none thisrxd#9212
is_doingthis none thisrxd#9224
cancel_if_possiblethis none thisrxd#925
take_damagethis none thisrxd#920
inamethis none thisrxd#9220
floatsthis none thisrxd#922

Properties

PropertyDefinerFlagsOwnerValue
queue#92r#361{}
preemptible#92r#3610
executing#92r#361{}
process_queue#92r#3610
help_msg#92rc#361
list of 30{"A generic creature which can perform actions that take time to complete, queue those actions to be performed in order, and generate new actions to take when it has no more queued.", "", "A word about the action queue", "-----------------------------", "The action queue is meant to simulate the idea that it takes time to perform certain tasks, and you can't simply do things as fast as you can type their commands. The most obvious example, of course, is combat. A combat attack takes a certain amount of time to complete, and then (possibly) another attack happens. If the person attacking wants to also, say, use a medkit, or perform brain surgery, they can't very well be attacking at the same time.", "", "The way this works is this: the creator of the medkit, in his \"use medkit\" verb, does only one thing (after basic sanity-checking such as 'is the medkit usable'): it queues an action for the player to use the medkit. Then, the player's action queue gets to it just as soon as the player isn't busy with anything else. If the player wasn't already doing anything, she simply executes the medkit-using action immediately.", "", "This brings us to the first method on $actor, used by objects to tell the actor to do something:", "", "$actor:queue_action(OBJ action, LIST arguments, FLOAT duration, INT cancellable, STR typed-command)", "", "OBJ action : A child of $action, indicating the kind of action to be taken. These objects implement two basic methods - :_start() and :_finish(). These methods are passed the actor's obj#, and the arguments given by the second arg (LIST arguments). See 'help $action' for more details on this.", "", "LIST arguments: The specific parameters of the action. The format of this list is different for every action; for 'attack' it might be the weapon and the target, for 'use' it might be the item to be used.", "", "FLOAT duration: The number of real-time seconds between running the action's :_start and :_finish.", "", "INT cancellable: Whether this action can be cancelled by the player at will. This should be 1 for most things, but could be 0 for actions forced on the player by drugs or circumstances.", "", "STR typed-command: This is what is shown in the player's 'queue' command display. It has no other function.", "", "When this method is called, the actor immediately executes the action if it doesn't already have an action running. If it does, the action is added to the queue. Every time the actor finishes running an action, it grabs the next one off its queue and immediately starts executing that. When the queue empties, it asks the actor for its next suggested action via:", "", "$actor:suggest_next_action()", "", "This is the method you should override on your actors to have them spontaneously do things without external stimulus. By default, this method will keep you attacking anyone who is attacking you, so to preserve that behavior you may want to test pass(@args) and return it in your overridden method.", "", "The action system expects a two-element list to be returned: {LIST action, INT delay}. LIST action is an action specification, simply the arguments to be passed to :queue_action() (see above). INT delay is a number of seconds to hold off before actually executing this action. This lets your NPCs space out their actions when things aren't so urgent -- you don't want your wandering NPC to wander as fast as humanly possible.", ""}
last_action#92r#361{}
article#92rc#3610
last_last_action#92rc#361{}
action_count#92rc#3610
aliases#1r#362{"generic actor"}
description#1rc#361<clear>
object_size#1r#29{17687, 1298433815}
hidden_verbs#1rc#361<clear>
phelp_msg#1rc#361<clear>
weight#1rc#361<clear>
owner_verbs#1rc#361<clear>
plural_name#1rc#361<clear>
client_image#1rc#361<clear>
listening#1rc#361<clear>

Ancestry

Ancestors (nearest first): #1 root

Children: #107 generic creature, #227 flywheel

Call graph

calls n92_0 #92:process_queue n20_48 #20:print n92_0->n20_48 n2_31 #2:tell n92_0->n2_31 n20_59 #20:name_and_number n92_0->n20_59 n103_24 #103:report_error n92_0->n103_24 n92_7 #92:cancel_current_action n92_0->n92_7 n18_43 #18:gil_float_to_int n92_0->n18_43 n20_122 #20:superview n92_0->n20_122 n150_0 #150:register n92_0->n150_0 n92_1 #92:fork_process_queue n92_7->n92_1 n92_1->n92_0 n92_2 #92:queue_action n92_2->n20_59 n92_2->n92_7 n92_2->n92_1 n1_56 #1:tell n92_2->n1_56 n92_4 #92:qu*eue n92_4->n20_59 n20_20 #20:match_player n92_4->n20_20 n107_43 #107:tell n92_4->n107_43 n6_9 #6:notify n92_4->n6_9 n92_5 #92:doing_msg n92_5->n20_59 n45_2 #45:has_verb n92_5->n45_2 n92_6 #92:clear_queue n92_6->n92_7 n92_8 #92:jumpstart n92_8->n20_59 n92_8->n92_1 n20_5 #20:from_list n92_8->n20_5 n92_3 #92:suggest_next_action n92_8->n92_3 n92_9 #92:prequeue_action n92_9->n92_7 n92_9->n92_2 n92_10 #92:backup_queue n92_10->n6_9 n92_10->n92_5 n92_12 #92:cancel_if_possible n92_12->n92_7 n92_14 #92:iname n1_30 #1:name n92_14->n1_30 n98_1 #98:article_for n92_14->n98_1

Source

process_queue

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1":process_queue()";
2"Keep running actions until we don't have any to run.";
3player = this;
4next_action_delay = 0;
5if (`this.location.no_actions ! ANY')
6this.queue = {};
7return;
8endif
9if ((!this.queue) && (this.health > 0))
10if (action = this:_suggest_next_action())
11this.queue = {action[1], @this.queue};
12next_action_delay = action[2];
13endif
14endif
15continuation = {};
16while (this.queue || continuation)
17this.preemptible = 0;
18pass_to_finish = 0;
19if (this.queue || continuation)
20if (continuation)
21action = continuation;
22startverb = "_continue";
23continuation = 0;
24else
25action = this.queue[1];
26this.queue = listdelete(this.queue, 1);
27startverb = "_start";
28endif
29if (!is_a(action[1], #1))
30continue;
31endif
32this.last_action && (this.last_last_action = this.last_action);
33this.last_action = action;
34debugtext = $su:print(action[2..$]);
35"...sanity check...";
36if (((!action[2]) || (typeof(action[2][1]) != OBJ)) || (!is_a(action[2][1], $garbage)))
37if (is_a(this.location, $room) && (!this.location:check_forbid_action(this, @action)))
38"...nobody in here cancelled us...";
39this.executing = {@action, @{$action, {}, 1, "", 0}[length(action) + 1..$]};
40try
41result = action[1]:(startverb)(this, action[2]);
42if ($debug)
43$archwiz:tell($su:nn(this), " => ", $su:nn(action[1]));
44endif
45except e (ANY)
46if (e[1] == E_NONE)
47result = E_NONE;
48else
49extra_info = $su:nn(action[1]) + tostr(" args: ", toliteral(action[2]));
50$rpg:report_error(e, extra_info);
51result = E_INVARG;
52endif
53endtry
54status = this.executing[5];
55this.executing[5] = 1;
56if (status in {2, 3})
57"Something called the action while we were running _start, so abort now.";
58this:cancel_current_action(status == 3);
59"We're never going to end up here, as :cancel_current_action kills the task we are running in.";
60elseif (typeof(result) != ERR)
61if (typeof(result) == LIST)
62if (length(result) > 1)
63pass_to_finish = result[2];
64else
65pass_to_finish = 0;
66endif
67do_continue = (length(result) > 2) ? result[3] | 0;
68durint = result[1];
69if (typeof(durint) == FLOAT)
70"slow down if lots of people are acting...";
71actors = -1;
72for x in (this.location:occupants($actor))
73if (`x.executing ! ANY => 0')
74actors = actors + 1;
75endif
76endfor
77durint = durint * (1.0 + (tofloat(actors) * 0.2));
78endif
79durint = $math_utils:gil_float_to_int(durint);
80else
81durint = 0;
82pass_to_finish = result;
83endif
84suspend(abs(durint));
85if (!is_a(this, $actor))
86return;
87endif
88if (has_callable_verb(action[1], "_finish"))
89this.executing[5] = 4;
90try
91continuation = action[1]:_finish(this, action[2], pass_to_finish);
92except e (ANY)
93if (e[1] != E_NONE)
94extra_info = $su:nn(action[1]) + tostr(" args: ", toliteral(action[2]));
95if (pass_to_finish)
96extra_info = {extra_info, tostr("pass_to_finish: ", toliteral(pass_to_finish))};
97endif
98$rpg:report_error(e, extra_info);
99endif
100endtry
101status = this.executing[5];
102endif
103endif
104`this._tohit_next = 0 ! ANY => 0';
105this.executing = {};
106else
107this:debug_tell("action", "forbid | ", $su:nn(action[1]), " ", debugtext);
108`action[1]:_forbidden(this, @action[2..$]) ! ANY => 0';
109endif
110endif
111endif
112data = action;
113if ((typeof(continuation) == LIST) && continuation)
114"...make it look like we're executing the continued action.  It'll execute back at the top.";
115this.executing = continuation = {@continuation, @{$action, {}, "", 1, 0}[length(continuation) + 1..$]};
116if (status in {2, 3})
117"Something called the action while we were running _finish, so abort instead of queuing continuation.";
118this:cancel_current_action(status == 3);
119continuation = 0;
120endif
121else
122continuation = 0;
123endif
124this.preemptible = next_action_delay > 0;
125suspend(next_action_delay);
126if (is_a(this, $garbage))
127"we died...";
128return;
129endif
130this.preemptible = 0;
131data = action;
132if (((!continuation) && (!this.queue)) && (!`this.possessors ! ANY => 0'))
133"...no more queued actions, do we have an auto-action?...";
134if (has_callable_verb(this, "_suggest_next_action") && (result = this:_suggest_next_action()))
135action = result[1];
136if (((action[1] in $rpg.repeatable_actions) || is_player(this)) || (action != this.last_action))
137next_action_delay = result[2];
138"...add our auto-action to the queue...";
139this.queue = {action, @this.queue};
140endif
141endif
142else
143next_action_delay = 0;
144endif
145this.action_count = this.action_count + 1;
146if ((this.action_count >= 90) && (!`this.no_action_warn ! ANY => 0'))
147next_action_delay = max(next_action_delay, 1);
148if (((time() - $nets.prognet.last_action_warning) > 10) && (!is_player(this)))
149debug_info = tostr($su:nn(data[1]), " args: ", $su:sv(data[2]), @pass_to_finish ? {" pass: " + toliteral(pass_to_finish)} | {});
150$nets.bugnet:announce(#-1, tostr($su:nn(this), " reached action count of ", this.action_count, "!"), 1);
151$nets.bugnet:announce(#-1, tostr("with: ", debug_info), 1);
152$nets.prognet.last_action_warning = time();
153if (`this.heart ! ANY' == 0)
154$heart:register(this);
155endif
156endif
157endif
158endwhile
159this.preemptible = 1;

fork_process_queue

Spec this none thisFlags rxOwner #361Definer #92

Referenced by

Source

1":fork_process_queue()";
2"Fork off a process_queue task.";
3"If args[1], do it even if there's a valid one already.";
4force = args ? args[1] | 0;
5if ((!force) && task_valid(this.process_queue))
6return;
7endif
8fork pq_task (0)
9player = this;
10this:process_queue();
11endfork
12this.process_queue = pq_task;
13this.preemptible = 0;
14this.executing = {};

queue_action

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1":queue_action(OBJ action, LIST callback_args[, BOOL interruptable, STR command_strin])";
2"Add an action to the queue, and start the queue if needed.";
3action = args[1];
4cargs = args[2];
5inter = (length(args) > 2) ? args[3] | 1;
6cmd = (length(args) > 3) ? args[4] | "";
7if (length(this.queue) > 25)
8this:tell($ansi.cyan + "[ Sorry -- can't queue ", action.name, " (", cmd, "), more than 25 actions! ]" + $ansi.reset);
9return;
10endif
11if (this.programmer)
12`this:debug_tell("action", "queue  | " + $su:nn(action)) ! ANY => 0';
13endif
14queue_item = {action, cargs, inter, cmd};
15this.queue = {@this.queue, queue_item};
16if (task_valid(this.process_queue))
17if (this.preemptible)
18"...we're already running a low-priority paused-before-action, so kill it...";
19this:cancel_current_action();
20else
21if (cmd)
22this:tell((($ansi.cyan + ("[ queued '" + args[4])) + "' ]") + $ansi.reset);
23endif
24endif
25else
26this:fork_process_queue();
27endif

suggest_next_action

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1return;

qu*eue

Spec any none noneFlags rxdOwner #361Definer #92

Referenced by

none

Source

1if (dobjstr && player.programmer)
2if (!valid(dobj))
3dobj = $su:match_player(dobjstr);
4if (!valid(dobj))
5player:tell("queue who?");
6return;
7endif
8endif
9if (!is_a(dobj, $actor))
10player:tell("You can't queue that.");
11return;
12endif
13player:tell("Showing queue for ", $su:nn(dobj), ":");
14else
15dobj = this;
16endif
17if ((!dobj.queue) && (!dobj.executing))
18player:notify("You've got nothing to do.");
19else
20if (dobj.executing)
21player:notify(("At the moment, you're " + tostr(dobj:doing_msg())) + ".");
22endif
23for x in (dobj.queue)
24player:notify("  --> " + x[4]);
25endfor
26player:notify("  --> (end of queue)");
27endif

doing_msg

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1if (this.executing)
2if (this.executing[1] == this)
3return "doing something";
4elseif (is_a(this.executing[1], $action))
5return this.executing[1]:(verb)(this, this.executing[2]);
6elseif ($ou:has_verb(this.executing[1], "action_" + verb))
7return this.executing[1]:("action_" + verb)(this, this.executing[2]);
8else
9doingverb = is_a(this.executing[1], $action) ? "doing_msg" | "action_doing_msg";
10$nets.gamenet:announce(#2, tostr($su:nn(this.executing[1]), " lacks a :", doingverb, "() => ", toliteral(this.executing)), 1);
11return "using " + this.executing[1]:iname();
12endif
13return "";
14endif

clear_queue

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1":clear_queue()";
2"Kill all queued actions.";
3this.queue = {};
4this:cancel_current_action(1);
5this.preemptible = 1;

cancel_current_action

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1":cancel_current_action([INT dontabort])";
2"If we're currently executing an action, abort it and immediately start our next action.";
3{?dontabort = 0} = args;
4if (this.executing && (length(this.executing) >= 5))
5if (this.executing[5] == 0)
6"We are in _start, set a flag telling :process_queue to stop the action.";
7this.executing[5] = 2 + dontabort;
8return;
9elseif (this.executing[5] in {2, 3})
10"Already asked to abort, nothing to do...";
11return;
12elseif (this.executing[5] == 4)
13"Doing _finish, so we can't really cancel now, but if it a continuing action it might still want to do something...";
14this.executing[5] = 2 + dontabort;
15return;
16elseif (this.executing[5] == 1)
17"We are between _start and _finish, cancel and move on.";
18if ((!dontabort) && has_callable_verb(this.executing[1], "_abort"))
19this.executing[1]:_abort(this, @this.executing[2..4]);
20endif
21this.executing = {};
22"It is intentional that this case doesn't return.";
23endif
24endif
25old_pq = this.process_queue;
26if (this.queue)
27"Calling with a 1 to force it.";
28this:fork_process_queue(1);
29endif
30if (task_valid(old_pq))
31kill_task(old_pq);
32endif

jumpstart

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1"See if we have an action from :suggest_next_action().  If so, throw it on the queue and fire it up.";
2"Don't do anything if we already have a valid process_queue.";
3if (!this.f)
4if (!task_valid(this.process_queue))
5if (this.queue)
6this.owner:tell($ansi.yellow, "  Restarting dead queue for ", $su:nn(this), ".", $ansi.reset);
7this.owner:tell($ansi.yellow, $su:from_list(this.queue[1], " "), $ansi.reset);
8this:fork_process_queue();
9elseif (r = this:suggest_next_action())
10this.queue = {r[1]};
11this:fork_process_queue();
12endif
13endif
14endif

prequeue_action

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1":prequeue_action(OBJ action, LIST callback_args, BOOL interruptable, STR command_string, [INT no_cancel_current])";
2"Insert an action at the head of the queue for immediate next execution, cancelling any action currently being executed (or not, with 5 args).";
3if ((length(args) < 5) || (!args[5]))
4if (!`this.executing[1].unstoppable ! ANY')
5this:cancel_current_action();
6endif
7endif
8if (!this.queue)
9this:queue_action(@args);
10else
11action = args[1];
12cargs = args[2];
13inter = (length(args) > 2) ? args[3] | 1;
14cmd = (length(args) > 3) ? args[4] | "";
15this.queue = {{action, cargs, inter, cmd}, @this.queue};
16endif

backup_queue

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

none

Source

1"Copied from generic actor (#716):queue by Gilmore (#98) Tue Aug  7 19:37:24 2007 CDT";
2if ((!this.queue) && (!this.executing))
3player:notify("You've got nothing to do.");
4else
5if (this.executing)
6player:notify(("At the moment, you're " + this:doing_msg()) + ".");
7endif
8for x in (this.queue)
9player:notify("  --> " + x[4]);
10endfor
11player:notify("  --> (end of queue)");
12endif

is_doing

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1if (this.executing)
2doing = this.executing;
3elseif (this.queue)
4doing = this.queue[1];
5else
6return args ? 0 | #-1;
7endif
8if (!args)
9return doing[1];
10else
11{?action = #-1, ?target = #-1} = args;
12endif
13doing_target = `doing[2][1] ! ANY => #-1';
14if (doing[1] == action)
15if (valid(target))
16if (target == doing_target)
17return 1;
18else
19return 0;
20endif
21endif
22return 1;
23endif
24return 0;

cancel_if_possible

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1if (this.executing)
2if (!`this.executing[1].unstoppable ! ANY')
3return this:cancel_current_action();
4endif
5endif

take_damage

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

none

Source

No program (verb defined but unprogrammed).

iname

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

Source

1name = this:name();
2if (!name)
3return "";
4endif
5if (this.f)
6name = strsub(name, "generic ", "");
7endif
8if ((this.article == "") || (has_property(this, "has_proper_name") && this.has_proper_name))
9return name;
10elseif (!this.article)
11if (index("aeiou", name[1]))
12return "an " + name;
13elseif (index("bcdfghjklmnpqrstvwxyz", name[1]))
14return "a " + name;
15else
16return ($english_utils:article_for(name) + " ") + name;
17endif
18else
19return (this.article + " ") + name;
20endif

floats

Spec this none thisFlags rxdOwner #361Definer #92

Referenced by

none

Source

1"Copied from generic thing (#5):floats by Gilmore (#98) Sat Jun 27 05:19:29 2009 CDT";
2return this.floats;