I'm afraid I don't have a small example, but I do have a large example. This is the code for my "populace extension". It's going to take a minute to explain what that even means, I guess. For every area, I have an area control daemon, ~Project/dmn/control.c. Most objects in my lib support a concept of "extensions", which is an
object composition mechanism for managing objects that "extend" other objects. A project control daemon can have one or more populace extensions attached to it; each populace extension manages a bunch of monsters and/or NPCs that are supposed to appear in the area.
This code is going to pervasively refer to concepts from my lib, so it will probably be hard to understand; I recommend ignoring the bits that don't make any sense. Though two things I can tell you easily are that 'internal' means 'private static' and 'record' is just a macro for 'mixed' (though a record is normally a 'mixed array'); you FluffOS people with your fancy classes would do what I'm doing using 'records' using a class.
// Area populace extension, by Chaos Sun Oct 19 10:09:09 EDT 2003
//
// From area populace daemon module, by Chaos Mon Jul 15 01:30:05 CDT 1996
//
// For usage instructions see /txt/doc/build/populace
#include <daemon.h>
#include <interval.h>
#include <pathfind.h>
#include <population.h>
#include <services_defer_load.h>
#include <services_kernel.h>
#include <services_weather.h>
inherit "/std/extension";
#define Travel_Interval 6
// record def: make queue
#define Make_Queue_File 0
#define Make_Queue_Location 1
#define Make_Queue_Count 2
#define Make_Queue_Guard 3
#define Make_Queue_Fields 4
// end record def: make queue
// record def: travel
#define Travel_Actor 0
#define Travel_Object 1
#define Travel_File 2
#define Travel_Origin 3
#define Travel_Status 4
#define Travel_Fields 5
// end record def: travel
// record def: populace person
#define Populace_Person_File 0
#define Populace_Person_Home 1
#define Populace_Person_Guard 2
#define Populace_Person_Fields 3
// end record def: populace person
internal closure get_dir;
internal closure query_has_assignment;
internal int wander_time;
internal mapping active;
internal mapping populace;
internal status preload_immediate;
internal string array map_targets;
internal string array preferred_realms;
internal string array wander_realms;
internal string persistence_key;
internal string persistence_tag;
internal record array people;
internal record array queue;
internal record array travel;
object query_assignment(object who);
private varargs string array populace_get_dir(string path, int flags);
void process_make_queue();
void process_travel();
// Section: Serialization Support
mapping serialize_extension() {
return ([
"populace" : populace,
"people" : people,
"active" : active,
"travel" : travel,
"queue" : queue,
"persistence_tag" : persistence_tag,
"preferred_realms" : preferred_realms,
"wander_realms" : wander_realms,
"wander_time" : wander_time,
]);
}
void deserialize_extension(mapping info) {
populace = info["populace"];
people = info["people"];
active = info["active"];
travel = info["travel"];
queue = info["queue"];
persistence_tag = info["persistence_tag"];
preferred_realms = info["preferred_realms"];
wander_realms = info["wander_realms"];
wander_time = info["wander_time"];
if(persistence_tag) {
string name = extends->query_project_name();
if(!name)
error("Cannot use persistence tag on project with no project name");
persistence_key = name + "|" + persistence_tag;
} else {
persistence_key = 0;
}
}
// Section: Configuration Interface
void set_persistence_tag(string val) {
if(!is_a(extends, "/mod/daemon/persistence"))
error("Persistence tag requires that host daemon support persistence (inherit /mod/daemon/persistence)");
object array exts = extends->query_extension(load_name(this_object()));
if(exts)
foreach(object ext : exts)
if(ext && ext != this_object() && ext->query_persistence_tag() == val)
error("Cannot use same persistence tag as " + printable(ext));
string name = project_name(extends);
if(!name)
error("Cannot use persistence tag on project with no project_name()");
persistence_tag = val;
persistence_key = name + "|" + persistence_tag;
}
string query_persistence_tag() {
return persistence_tag;
}
string query_persistence_key() {
return persistence_key;
}
void set_preload_immediate(status val) {
preload_immediate = val && True;
}
status query_preload_immediate() {
return preload_immediate;
}
void set_wander_time(int val) {
check_argument(1, val, #'intp);
wander_time = val;
}
int query_wander_time() {
return wander_time;
}
void set_wander_realms(string array val) {
check_argument(1, val, (: arrayp($1) && all($1, #'stringp) :));
wander_realms = val;
}
string array query_wander_realms() {
return wander_realms;
}
void set_preferred_realms(string array val) {
check_argument(1, val, (: arrayp($1) && all($1, #'stringp) :));
preferred_realms = val;
}
string array query_preferred_realms() {
return preferred_realms;
}
void set_file_targets(string array val) {
populace["file_targets"] = val;
}
string array query_file_targets() {
return populace["file_targets"] || ({});
}
void add_file_target(string val) {
set_file_targets(query_file_targets() + ({ val }));
}
void add_file_selector(string val) {
check_argument(1, val, #'stringp);
populace["file_selectors"] ||= ({});
populace["file_selectors"] += ({ val });
if(member(val, '*') == Null && member(val, '?') == Null)
warn("selector " + printable(val) + " contains no wildcards");
string array matches = populace_get_dir(val, GETDIR_PATH);
switch(sizeof(matches)) {
case 0 :
if(val[<1] != '*')
warn("selector " + printable(val) + " matches no files (may need * on end, or possibly should be target)");
else
warn("selector " + printable(val) + " matches no files (probably should be target)");
break;
case 1 :
warn("selector " + printable(val) + " matches only one file (probably should be target)");
break;
}
}
void set_file_selectors(string array val) {
populace["file_selectors"] = ({});
foreach(string item : val)
add_file_selector(item);
}
string array query_file_selectors() {
return populace["file_selectors"] || ({});
}
void set_dynamic_targets(closure array val) {
populace["dynamic_targets"] = val;
}
closure array query_dynamic_targets() {
return populace["dynamic_targets"] || ({});
}
void add_dynamic_target(closure val) {
set_dynamic_targets(query_dynamic_targets() + ({ val }));
}
void add_map_realm_target(mixed val) {
populace["map_realm_targets"] ||= ({});
populace["map_realm_targets"] += ({ val });
}
mixed array query_map_realm_targets() {
return populace["map_realm_targets"] || ({});
}
void add_map_terrain_target(mixed val) {
populace["map_terrain_targets"] ||= ({});
populace["map_terrain_targets"] += ({ val });
}
mixed array query_map_terrain_targets() {
return populace["map_terrain_targets"] || ({});
}
void add_map_type_target(mixed val) {
populace["map_type_targets"] ||= ({});
populace["map_type_targets"] += ({ val });
}
mixed array query_map_type_targets() {
return populace["map_type_targets"] || ({});
}
void add_map_overlay_target(mixed val) {
populace["map_overlay_targets"] ||= ({});
populace["map_overlay_targets"] += ({ val });
}
mixed array query_map_overlay_targets() {
return populace["map_overlay_targets"] || ({});
}
void set_populaces(mapping val) {
populace["populaces"] = val;
}
mapping query_populaces() {
return populace["populaces"] || ([]);
}
varargs void add_populace(string file, int num, status guard) {
set_populaces(query_populaces() + ([ file : ({ num, guard })]));
}
varargs void add_population(string file, int num, status guard) {
add_populace(file, num, guard);
}
void set_people(record array val) {
people = val;
}
record array query_people() {
return people;
}
varargs void add_person(string file, mixed loc, status guard) {
record entry = allocate(Populace_Person_Fields);
entry[Populace_Person_File] = file;
entry[Populace_Person_Home] = loc;
entry[Populace_Person_Guard] = guard;
people += ({ entry });
}
// Section: Operational Interface
mapping query_populace_active() {
return active;
}
status query_populace_managed(object obj) {
if(clonep(obj)) {
object array list = active[load_name(obj)];
return list && member(list, obj) != Null;
} else {
string file = object_name(obj);
foreach(record person : people)
if(person[Populace_Person_File] == file)
return True;
return False;
}
}
// Section: Internals
mixed query_populace_weather() {
return extends()->query_weather() || Daemon_Weather;
}
string array resolve_map_targets() {
string array out = ({});
status any = False;
string array items;
if(items = populace["map_realm_targets"]) {
any = True;
foreach(mixed target : items)
out += extends->query_map_realm_rooms(target);
}
if(items = populace["map_terrain_targets"]) {
any = True;
foreach(mixed target : items)
out += extends->query_map_terrain_rooms(target);
}
if(items = populace["map_type_targets"]) {
any = True;
foreach(mixed target : items)
out += extends->query_map_type_rooms(target);
}
if(items = populace["map_overlay_targets"]) {
any = True;
foreach(mixed target : items)
out += extends->query_map_overlay_rooms(target);
}
if(any && !sizeof(out)) {
string array proc = ({});
if(items = populace["map_realm_targets"])
proc += ({ "realm: " + implode(map(items, #'printable), ", ") });
if(items = populace["map_terrain_targets"])
proc += ({ "terrain: " + implode(map(items, (: Terrain($1)->query_terrain_name() :)), ", ") });
if(items = populace["map_type_targets"])
proc += ({ "type: " + implode(map(items, #'printable), ", ") });
if(items = populace["map_overlay_targets"])
proc += ({ "overlay: " + implode(items, ", ") });
error("attached to " + printable(extends) + ", had no target results for map specification " + implode(proc, ", "));
}
return out;
}
private string array retrieve_get_dir() {
return get_dir ||= extends->query_control_daemon_closure_get_dir() || #'get_dir;
}
private varargs string array populace_get_dir(string path, int flags) {
mixed res = funcall(retrieve_get_dir(), path, flags);
if(!res) {
warn("directory retrieval on " + printable(path) + " had zero result; permissions problem?");
res = ({});
}
return res;
}
private string array find_locations() {
string array out = query_file_targets();
foreach(string loc : query_file_selectors())
foreach(string file : populace_get_dir(loc, GETDIR_PATH))
if(ends_with(file, ".c"))
out += ({ file });
map_targets ||= resolve_map_targets();
if(sizeof(map_targets))
out += map_targets;
return out;
}
string array query_locations() {
return populace["locations"] ||= find_locations();
}
string query_location() {
return random_element(query_locations());
}
object query_target_location(mixed who) {
closure array funcs = query_dynamic_targets();
if(sizeof(funcs)) {
foreach(closure func : funcs) {
mixed res = funcall(func, who);
if(res)
if(stringp(res))
return load_object(res);
else
return res;
}
}
mixed loc = query_location();
switch(typeof(loc)) {
case T_OBJECT :
return loc;
case T_STRING :
return load_object(loc);
default :
error("Invalid target location " + printable(loc) + ", extending " + printable(extends));
}
return 0;
}
private mixed resolve_home(mixed home, mixed who, status night) {
switch(typeof(home)) {
case T_STRING :
return load_object(home);
case T_NUMBER :
case T_OBJECT :
return home;
case T_CLOSURE :
return resolve_home(funcall(home, who), who, night);
case T_POINTER :
return resolve_home(home[night], who, night);
default :
error("Cannot resolve home " + printable(home));
}
return 0;
}
record array query_travel() {
return travel;
}
record array query_make_queue() {
return queue;
}
void initialize_make_queue() {
if(eval_cost() < Eval_Extremely_High) {
if(find_call_out("initialize_make_queue") == Null)
call_out("initialize_make_queue", 2);
return;
}
queue = ({});
foreach(record person : people) {
string file = person[Populace_Person_File];
object obj = find_object(file);
if(!obj || !environment(obj)) {
record entry = allocate(Make_Queue_Fields);
entry[Make_Queue_File] = file;
entry[Make_Queue_Location] = person[Populace_Person_Home];
entry[Make_Queue_Guard] = person[Populace_Person_Guard];
queue += ({ entry });
}
}
mapping curr = query_populace_active();
foreach(string file, mixed array info : query_populaces()) {
object array list = curr[file];
if(list && member(list, 0) != Null) {
list -= ({ 0 });
curr[file] = list;
}
int amt = info[0] - sizeof(list);
if(amt > 0) {
record entry = allocate(Make_Queue_Fields);
entry[Make_Queue_File] = file;
entry[Make_Queue_Count] = amt;
entry[Make_Queue_Guard] = info[1];
queue += ({ entry });
}
}
if(sizeof(queue))
Population_Start(#'process_make_queue);
}
mixed process_make_queue() {
record entry = random_element(queue);
if(!entry)
return Population_Terminate;
string what = entry[Make_Queue_File];
object where = resolve_home(entry[Make_Queue_Location], what, query_populace_weather()->query_night()) || query_target_location(
what);
// Deferring loading if the room has non-incarnos inventory gives autonomoi a chance to obtain belongings by scavenging
// rather than cloning, hopefully slightly helping with memory usage.
status defer = where && first_inventory(where) && !Daemon_Defer_Load->query_defer_load() && !any(all_inventory(where), "is_incar
nos");
if(defer)
Daemon_Defer_Load->set_defer_load(True);
object obj = load_object(what);
unless(obj->query_unique())
obj = new(obj);
if(defer)
Daemon_Defer_Load->set_defer_load(False);
if(obj) {
if(any_hook("can_populace_populate") || any_hook("do_populace_populate")) {
mapping info = ([
"who" : obj,
"where" : where,
"populace" : this_object(),
"control" : extends,
]);
mixed res = check_hook("can_populace_populate", info);
if(Hook_Success(res)) {
if(obj) {
if(obj->move(where) == Move_Succeed) {
obj->message(([
Message_Content : ({
({ 'a', 0 }), ({ "appear", 0 }),
}),
Message_Flags : Message_Flag_Require_Source_Discriminatory,
Message_Senses : Message_Sense_Visual | Message_Sense_Kinesthetic_For_Participants,
]));
check_hook("do_populace_populate", info);
} else {
safe_destruct(obj);
}
}
} else {
if(Hook_Display(res)) {
if(obj)
obj->display(res);
info["message"] = res;
}
check_hook("fail_populace_populate", info);
}
} else {
if(obj->move(where) == Move_Succeed && obj) {
obj->message(([
Message_Content : ({
({ 'a', 0 }), ({ "appear", 0 }),
}),
Message_Flags : Message_Flag_Require_Source_Discriminatory,
Message_Senses : Message_Sense_Visual | Message_Sense_Kinesthetic_For_Participants,
]));
} else {
safe_destruct(obj);
}
}
if(obj && environment(obj)) {
if(clonep(obj)) {
if(persistence_key) {
obj->set_info("System_Populace_Persistence_Key", persistence_key);
extends->add_persistence_target(obj);
}
active[what] ||= ({});
active[what] += ({ obj });
} else if(persistence_key) {
string curr = obj->query_info("System_Populace_Persistence_Key");
if(!curr) {
obj->set_info("System_Populace_Persistence_Key", persistence_key);
extends->add_persistence_target(obj);
} else if(curr == persistence_key) {
extends->add_persistence_target(obj);
} else {
warn("managing unique " + printable(obj) + ", with persistence, that is marked for persistence by another popula
ce extension, will not add to control persistence list");
}
}
if(entry[Make_Queue_Guard])
obj->set_area_guarding(True);
if(wander_time)
obj->set_move_time(semirandom(wander_time));
if(preferred_realms)
obj->set_preferred_realms(preferred_realms);
if(wander_realms)
obj->set_wander_realms(wander_realms);
}
}
if(--entry[Make_Queue_Count] < 1)
array_remove(&queue, entry);
if(!sizeof(queue))
return Population_Terminate;
return;
}
void process_entire_make_queue() {
while(sizeof(queue))
process_make_queue();
}
void initialize_travel() {
if(eval_cost() < Eval_Extremely_High) {
if(find_call_out("initialize_travel") == Null)
call_out("initialize_travel", 2);
return;
}
travel = ({});
status night = query_populace_weather()->query_night();
foreach(record person : people) {
if(eval_cost() < Eval_Extremely_High) {
if(find_call_out("initialize_travel") == Null)
call_out("initialize_travel", 2);
return;
}
string file = person[Populace_Person_File];
object obj = find_object(file);
if(!obj || obj->query_attacker() || obj->query_incapacitated())
continue;
object home = resolve_home(person[Populace_Person_Home], obj, night);
unless(home)
continue;
object env = environment(obj);
if(!env || env == home)
continue;
if(funcall(query_has_assignment, obj))
continue;
if(any_hook("can_populace_start_travel") || any_hook("do_populace_start_travel") || obj->any_hook("can_populace_start_travel
") || obj->any_hook("do_populace_start_travel")) {
mixed res;
mapping info = ([
"who" : obj,
"where" : env,
"destination" : home,
"populace" : this_object(),
"control" : extends,
"night" : night,
]);
res = check_hook("can_populace_start_travel", info);
if(Hook_Failure(res)) {
if(Hook_Display(res)) {
if(obj)
obj->display(res);
info["message"] = res;
}
check_hook("fail_populace_start_travel", info);
obj->check_hook("fail_populace_start_travel", info);
continue;
}
res = obj->check_hook("can_populace_start_travel", info);
if(Hook_Failure(res)) {
if(Hook_Display(res)) {
if(obj)
obj->display(res);
info["message"] = res;
}
check_hook("fail_populace_start_travel", info);
obj->check_hook("fail_populace_start_travel", info);
continue;
}
check_hook("do_populace_start_travel", info);
obj->check_hook("do_populace_start_travel", info);
}
record entry = allocate(Travel_Fields);
entry[Travel_Actor] = obj;
entry[Travel_Object] = home;
entry[Travel_File] = home;
travel += ({ entry });
}
if(sizeof(travel))
Interval_Set(#'process_travel, Travel_Interval);
}
private void travel_path(descriptor path, descriptor pathfind) {
record entry = Pathfind_Query_Info(pathfind, "Travel");
if(!path) {
array_remove(&travel, entry);
return;
}
entry[Travel_Status] = path;
object who = entry[Travel_Actor];
if(!who)
return;
if(environment(who) != entry[Travel_Origin])
return;
if(funcall(query_has_assignment, who))
return;
object dest = entry[Travel_Object] || load_object(entry[Travel_File]);
if(any_hook("can_populace_travel") || any_hook("do_populace_travel")) {
mapping info = ([
"who" : who,
"where" : environment(who),
"destination" : dest,
"path" : path,
"pathfind" : pathfind,
"populace" : this_object(),
"control" : extends,
]);
mixed res = check_hook("can_populace_travel", info);
if(Hook_Failure(res)) {
if(Hook_Display(res)) {
if(who)
who->display(res);
info["message"] = res;
}
check_hook("fail_populace_travel", info);
array_remove(&travel, entry);
return;
}
check_hook("do_populace_travel", info);
}
who->queue_path(path);
entry[Travel_Status] = time();
}
mixed process_travel() {
foreach(record entry : travel) {
if(eval_cost() < Eval_Extremely_High)
break;
if(entry[Travel_Status] == Path_Processing)
continue;
object who = entry[Travel_Actor];
if(!who) {
array_remove(&travel, entry);
continue;
}
object to = entry[Travel_Object];
if(!to) {
catch(to = load_object(entry[Travel_File]));
if(to) {
entry[Travel_Object] = to;
} else {
array_remove(&travel, entry);
continue;
}
if(eval_cost() < Eval_Extremely_High)
break;
}
if(environment(who) == to) {
array_remove(&travel, entry);
if(intp(entry[Travel_Status])) {
if(any_hook("can_populace_end_travel") || any_hook("do_populace_end_travel")) {
mapping info = ([
"who" : who,
"where" : to,
"populace" : this_object(),
"control" : extends,
]);
mixed res = check_hook("can_populace_end_travel", info);
if(Hook_Success(res)) {
check_hook("do_populace_end_travel", info);
} else {
if(Hook_Display(res)) {
if(who)
who->display(res);
info["message"] = res;
}
check_hook("fail_populace_end_travel", info);
}
}
}
continue;
}
if(funcall(query_has_assignment, who))
continue;
if(intp(entry[Travel_Status]) && entry[Travel_Status] > time() - Time_Minute * 5)
continue;
object from = environment(who);
entry[Travel_Origin] = from;
entry[Travel_Status] = Path_Processing;
object pathfinder = from->project_control() == to->project_control() ? from->project_control() : Daemon_Route;
pathfinder->find_path(([
Pathfind_Actor : who,
Pathfind_From : from,
Pathfind_To : to,
Pathfind_Validate : Path_Appropriate_Mobility_Check(who),
Pathfind_Callback : #'travel_path,
Pathfind_Info : ([
"Travel" : entry,
]),
]));
}
return sizeof(travel) ? 0 : Interval_Terminate;
}
void populace_reset() {
initialize_make_queue();
initialize_travel();
}
void unregister_character(object who) {
if(clonep(who)) {
string key = load_name(who);
object array list = active[key];
if(list) {
object array mod = list - ({ who, 0 });
if(sizeof(mod) < sizeof(list))
if(sizeof(mod))
active[key] = mod;
else
map_delete(active, key);
}
}
}
void synchronize_persistence() {
if(!persistence_key)
error("Attempt to synchronize persistence with no persistence key");
foreach(object obj : extends->query_persistence_targets()) {
if(clonep(obj) && obj->query_info("System_Populace_Persistence_Key") == persistence_key) {
string type = load_name(obj);
active[type] ||= ({});
active[type] += ({ obj });
}
}
}
// Section: Core Applies
void attach_extension(object obj) {
query_has_assignment ||= symbol_function("query_has_assignment", obj);
obj->add_hook("mod_daemon_reset", #'populace_reset);
obj->add_hook("mod_daemon_unregister_character", #'unregister_character);
if(persistence_tag)
obj->add_hook("at_persistence_daemon_initialized", #'synchronize_persistence);
validate_local_hooks("populace", ({
"end_travel",
"populate",
"start_travel",
"travel",
}));
if(Obj_Master_Kernel->query_preloading() && preload_immediate && !persistence_tag) {
initialize_make_queue();
limited(#'process_entire_make_queue);
} else {
populace_reset();
}
}
void detach_extension(object obj, int reason, int remove_flags) {
obj->remove_hook("mod_daemon_reset", #'populace_reset);
obj->remove_hook("mod_daemon_unregister_character", #'unregister_character);
obj->remove_hook("at_persistence_daemon_initialized", #'synchronize_persistence);
}
void preinit() {
::preinit();
populace = ([]);
active = ([]);
people = ({});
}
void create() {
::create();
if(preload_immediate && persistence_tag)
warn("have preload_immediate and persistence_tag; preload_immediate will not take effect");
}
status query_extension_multiple() {
return True;
}
Here's the /txt/doc/build/populace document that the code refers to, which I rewrote from its state of being totally out of date (documenting how to use much older code from before the extension mechanism existed) to a state of just being very incomplete, just for you:
populace - how to set up area populace extensions
------------------------------------------------------------------------------
Populace Extensions how to set up populace extensions
Chaos, 1996-07-15, 2010-07-07
------------------------------------------------------------------------------
This document explains the configuration and use of the generic populace
extension, /obj/extensions/populace, to manage an area's populace independently
of monsters loaded in individual rooms. /obj/extensions/populace is designed
to extend area control daemons, and to work well in conjunction with the
guardian extension (see /txt/doc/build/guardian), the pathfinder extension
(see /txt/doc/build/pathfinder) and the area persistence module
(/mod/daemon/persistence).
A populace extension creates and maintains a specific population for the area,
distributing it randomly amidst specified target rooms. At each reset(), it
creates new populace to replace any killed during the intervening time.
A basic area control daemon with a populace extension configured might look
like:
#include <daemon.h>
#include <Project.h>
inherit "/std/daemon";
inherit "/mod/daemon/control";
void configure() {
::configure();
set_creator("lars");
set_area("Project");
add_extension(LS_Extension("populace"), (:
$1->add_file_selector(Project_Room("east_street_*"));
$1->add_file_selector(Project_Room("west_street_*"));
$1->add_file_target(Project_Room("square"));
$1->add_populace(Project_Monster("guard"), 20);
$1->add_populace(Project_Monster("citizen"), 40);
$1->add_populace(Project_Monster("beggar"), 5);
$1->add_populace(Project_Monster("pickpocket"), 3);
$1->add_person(Project_NPC("bob"));
$1->add_person(Project_NPC("joe"));
$1->add_person(Project_NPC("mary"));
:));
}
[ Basic Configuration ]
void add_file_selector(string sel);
Defines a pattern for retrieving target rooms to which the area's populace
will be randomly distributed. The easiest way to do this is to simply use
the area's room macro with wildcards. For instance, if you wanted to have
the populace defined spread among all rooms in the area whose filenames
began with the word "street", you could do:
add_file_selector(Area_Room("street*"));
If multiple file selectors are used, any rooms consistent with more than
one will behave as if "weighted"; i.e. a room which is returned by two file
selectors will be twice as likely to show up as a room which is returned by
only one.
void add_file_target(string targ);
Sets an explicit filename to be used in addition to any files retrieved
by file selectors. As with selectors, weighting behavior results if a file
ends up being specified more than once.
varargs void add_populace(string file, int number, status guard);
Defines a populace of monsters to distribute within the area. The
first argument is the filename from which to clone the monsters. The second
argument is the number which should exist within the area. The third,
optional, argument is a flag which should be set to true if you want
set_area_guarding(True) to be called in the cloned monsters. (This is
not necessary for monsters which are always area guardians, and so call
set_area_guarding(True) in their own configure() function.)
varargs void add_person(string file, mixed home);
Adds an individual (a non-cloned NPC) to the area's population. The
first argument is the NPC's filename. The second, optional, argument
designates the filename of the NPC's home; if this is specified the NPC
will be placed at this location when created and will attempt to return
to it periodically if it has moved (using the area control daemon's
pathfinding capability, if any). You may also specify a two-element
string array; the first element is the NPC's home during the day and the
second is its home at night. If the home value or either element of a
home array are closures, they will be resolved, being passed an argument
of the NPC in question. If nothing is specified the NPC will be
distributed randomly amidst the locations defined by the daemon's file
selectors, just like the rest of the populace.
[ Operational Use ]
mapping query_populace_active();
You may use this function to retrieve the data which the daemon uses to
track existing monsters in its population base. This mapping's indices are
the filenames of population elements; its values are arrays of monsters.
These arrays may contain zero-value elements when monsters have been killed.
Note that this mapping only tracks "populace", not "people".
status query_populace_managed(object obj);
This function returns true if the object sent is managed by the populace
daemon, whether as a 'populace' or 'people' object. Cloned objects will be
examined for whether they are part of 'populace', non-clones will be examined
for whether they are part of 'people'.