[...]

 

xMOO object hierarchy!

(the object hierarchy here is more-or-less what I want ultimately out of xMOO. objects prefixed with $ are part of LambdaCore; objects prefixed with ? are created but not quite right yet; objects prefixed with # are uncreated, and their code has not yet been debugged at all.) +-$Root Object +-$generic room | +-generic sound-carrying room | +-generic detailed room | | +-?generic region | | +-?generic vehicle | | +-#generic region ship | +-#generic death room +-$generic exit | +-#generic sound-carrying exit | +-generic lockable doored exit | +-generic fake exit +-$generic thing | +-#generic destructable thing | | +-#generic fungible thing | | | +-#generic liquid pool <-- refers to a generic liquid | | | +-#generic coin stack <-- stack of one money type | | | +-#generic currency <-- stack of all money types in a currency | | +-?generic wearable thing | | | +-generic radio | | | +-#generic recording device | | +-?generic furniture | | +-#generic smoking object <-- object with :smoke &c verbs | | +-#generic preparation device | | | +-#generic synther <-- synth stuff for forge | | | +-#generic forge <-- forge metals & synth | | | +-#generic stove <-- cook stuff | | | +-#generic sewing machine <-- fabric + patterns? | | +-#generic radio tape <-- play <tape> on/with <radio> | | +-#generic herb <-- smokable object | | +-#generic food <-- anything you can eat | | +-#generic liquid <-- can drink, need a liquid container to pick up | | +-#generic lever | | | +-#generic button | | | +-#generic pullcord | | +-#generic light | | +-#generic burning light | | +-#generic lighter | +-$generic container <!- modify to capacity-check | | +-#generic liquid container <-- can drink from: water bottle, etc | | +-#generic refilling liquid container <-- fountain, well | +-#generic altered object <-- object created via a template based on another object | +-#generic ruined object <-- object created via decay | +-#generic synthed object <-- object create via :synth | +-#generic corpse <-- maybe this should be elsewhere, though +-#generic embodied object +-#generic actor | +-#generic region ship attendant +-<$player hierarchy>

generic destroyable thing

(maybe needs a .ruin_type prop, to determine how it works when it's ruined-- it could vanish, or change to a #generic ruined object (the scraps of %t) or whatever else. a piece of cake could rot away completely, but a burning torch would leave behind a burnt-out stick of ash.) (this also could use a... .condition_display prop. displayed after object name in inventory. food could have fresh->{no title}->rotten, etc. prop would be a list that maps .condition values to messages.) .condition_max number of times the object can decay before it is ruined .condition current decay state of the object, between 0 and .condition_max .decay_msg string or list of strings, to display (one, randomly) when the object decays .decay_rate time in seconds between decay steps, or ≤ 0 for don't decay automatically .ruin_msg string or list of strings, to display (one, randomly) when the object decays to ruin. .repair_msg string or list of strings, to display (one, randomly) when the object is repaired. .repairable FALSE if you can't repair the object, TRUE otherwise. (maybe .repair_requirement for an object/key statement that is needed to repair the thing) .decay_task task ID of decay task :_repaired called to do a repair operation :_decay to decay at .decay_rate, tell its location, and ruin :_ruin called when object condition falls to 0. :initialize start decay process when object is created. @create $thing called "generic destroyable thing",destroy @prop destroy.condition_max 10 @prop destroy.condition 10 @prop destroy.decay_msg "%T crumbles slightly." @prop destroy.decay_rate 0 @prop destroy.ruin_msg "%T crumbles to pieces!" @prop destroy.repair_msg "%T looks good as new!"; @prop destroy.repairable 1 @prop destroy.decay_task 0 @verb destroy:_repaired tnt @program destroy:_repaired this.condition = this.condition_max; repair = typeof(this.repair_msg) == LIST ? this.repair_msg | {this.repair_msg}; check = random(length(repair)); this.location:tell($string_utils:pronoun_sub(repair[check])); try kill_task(this.decay_task) except (ANY) endtry if(this.decay_rate > 0) fork(this.decay_rate/2) this:_decay(); endfork endif . @verb destroy:_decay tnt @program destroy:_decay this.decay_task = task_id(); while(this.condition > 0) decay = typeof(this.decay_msg) == LIST ? this.decay_msg | {this.decay_msg}; check = random(length(decay)); this.location:tell($string_utils:pronoun_sub(decay[check])); this.condition = this.condition - 1; if(this.decay_rate > 0) suspend(this.decay_rate); endif endwhile this:_ruin(); . @verb destroy:initialize tnt @program destroy:initialize this.condition = this.condition_max; if(this.decay_rate > 0) fork(this.decay_rate/2) this:_decay(); endfork endif . @verb destroy:_ruin tnt @program destroy:_ruin "By default, this utterly destroys the object-- we're assuming the 'original' object will be set +f or have children (or have verb customization), so to avoid getting your objects ruined accidentally, um, set them +f. You'll still lose the object in VR terms (it'll be at #-1, most likely), but it will still /EXIST/."; if (caller() != this && caller_perms() != $code_utils:verb_perms()) return E_PERM; endif ruin = typeof(this.ruin_msg) == LIST ? this.ruin_msg | {this.ruin_msg}; check = random(length(ruin)) this.location:tell($string_utils:pronoun_sub(ruin[check])); if (this.f || children(this) || verbs(this)) this:moveto(#-1); return E_NACC; endif return $recycler:_recycle(this); .

generic fungible thing

(needs a take/get override, to properly merge stacks. needs a drop/throw override, to properly split stacks. both need to understand "(verb) five/5 coins [from container]") (the get/take override would have to be in a :huh verb, 'cause the parser by default won't match 'five coins' to an object w/ a coin/coins alias-- also, I don't think there's a built-in converse to the $string_utils:engligh_number verb, so I'd probably have to write my own 'twenty-five' => 25 verb.) (syntax guesses-- if there's no amount modifier and the get/drop is for the singular form [coin], do action for one unit. if it's for a plural or the form is collective [water], do action for all units) (needs general :_split, :_merge, :_spawn, :_remove verbs) :title tnt needs a hack to display .in_stack after if .in_stack ≥ 2 .stack_limit max. amount of unit which can be in one stack .in_stack number of units in stack right now @create $destroyable called "generic fungible thing",fungible @prop fungible.stack_limit 0 @prop fungible.in_stack 1 @program fungible:title return pass(@args) + this.in_stack > 1 ? " (", this.in_stack, ")" | ""; .

generic smoking object

:smoke this none none take a drag :light this none none light (coldstart) :light this with any light (with match) :tamp this none none tamp down material :fill this with any fill with burnable object :empty this none none empty out, salvaging any burnable object left .coldstart 0 if needs to be lit with some other burning object @create $destroyable called "generic smoking object",smoke @prop smoke.coldstart 1 rc @verb smoke:smoke this none none @verb smoke:light this none none @verb smoke:light this with any

generic burning light

:"light burn" this none none (or this with any if flint &c) :"unlight extinguish" this none none

generic lever

(this should override the $destroyable stuff to make them easy to break [but not decay automatically]) .flip_msg string, message to announce when the lever state changes (either push or pull) .state in {"push", "pull"} :"push pull" this none none :_push tnt whatever action should occur when lever is pushed :_pull tnt whatever action should occur when lever is pulled @create $destroyable called "generic lever",lever @prop lever.flip_msg "%N flips %t." @prop lever.state "" @verb lever:"push pull" this none none rd @program lever:push if (verb == this.state) player:tell("You can't ", verb, " that any further."); else this.location:announce_all($string_utils:pronoun_sub(this.flip_msg)); this.state = verb; this:("_"+(verb))(); endif . @verb lever:_push tnt @program lever:_push return; . @verb lever:_pull tnt @program lever:_pull return; .

generic button

:push this none none :pull this none none @create lever called "generic button",button @set button.flip_msg to "%N pushes %t." @verb button:pull this none none @program button:pull player:tell("You can't ", verb, " that."); . @verb button:push this none none @program button:push this.location:announce_all($string_utils:pronoun_sub(this.flip_msg)); this:_push(); .

generic pullcord

:push this none none :pull this none none @create lever called "generic pullcord",pullcord @set pullcord.flip_msg to "%N pulls %t." @verb pullcord:push this none none @program pullcord:push player:tell("You can't ", verb, " that."); . @verb pullcord:pull this none none @program pullcord:pull this.location:announce_all($string_utils:pronoun_sub(this.flip_msg)); this:_pull(); .

generic tape

(uses for tapes: - (manually) write a few messages to play on a radio channel - (with recording device) record messages in a certain room :play this with/on any (where 'any' is a radio) :remove this from any (where 'any' is a radio or recording device) (overload take/get to do :remove in that case, too)

generic preparation device

(children should add additional verbs that expand mixing functionality) :combine any with any sees if dobj + iobj makes a valid combo

generic embodied object

(body parts offhand: upper body, neck, head, horn, eye, ear, mouth/lip, tongue, nose, back, wing, shoulder, upper arm, elbow, forearm, wrist, hand/paw, finger, abdomen, hips, groin, upper leg, knee, lower leg, ankle, foot, toe, tail, paw, haunch, tentacle, fin, hoof, foreleg, nail/claw, tusk, shell, pseudopod, mandible, gill, beak. 'organs': skin, bone, muscle, heart, lung, brain, intestines, liver, spleen, stomach. each of those parts could be covered w/ various objects-- most specifically, 'skin' is the basic lowest layer [and can be changed via magic &c], followed by tattoos, then piercings. all the rest up would be clothing layers) not sure what the point of organs would be-- perhaps super-lowermost layers on various bodyparts? .parts {{"part name", {layer[, ...]}}, [{part, {layer[, ...]}]}} .body complex part list :use_sense tnt {STR sense, STR sense_msg, STR fail_distort, ?begin = "", ?end = ""} :_sense tnt {STR sense, INT difficulty} @create #1 called "generic embodied object",embodied,bodied

generic vehicle

(this needs better handling of being able to look outside the vehicle. maybe default 'look out' verb? also, needs a way to transmit messages [say, emote, etc] to vehicle location.) :"enter board embark" this none none :"exit leave out disembark" any none none :"drive" any any any rxd :description tnt override to display location at end :tell tnt preface all lines w/ "> " @create $detailed_room called "generic vehicle",vehicle @verb vehicle:"enter board embark" this none none @verb vehicle:"ex*it leave o*ut disembark" any none none @verb vehicle:drive any any any rxd @verb vehicle:description tnt rxd @verb vehicle:tell tnt @prop vehicle.preface "> " @prop vehicle.location_msg "> Outside, you see %l." @prop vehicle.player_enter_msg "%N %<climbs> aboard %t." @prop vehicle.player_exit_msg "%N %<disembarks> from %t." @program vehicle:enter if (player.location == this) player:tell("You're already in there!"); elseif (player.location != this.location) player:tell("There's nothing like that here."); elseif (!this:acceptable(player)) "again, a job for nogo_msg"; player:tell("You can't go in ", this:title(), "."); else $you:say_action(this.player_enter_msg, player, this, this.location); player:moveto(this); suspend(0); if(player.location == this) this:announce($string_utils:pronoun_sub(this.player_enter_msg, player, this, this)); endif endif . @program vehicle:exit if (player.location != this) player:tell("You're not inside that."); elseif (!this.location:acceptable(player)) "this looks like a job for nogo_msg"; player:tell("You can't go to ", this.location:title(), "."); else $you:say_action(this.player_exit_msg, player, this, this); player:moveto(this.location); old_room = this.location; suspend(0); if(player.location == old_room) old_room:announce($string_utils:pronoun_sub(this.player_exit_msg, player, this, old_room)); endif endif . @program vehicle:drive "This mimics almost exactly $room:go."; if (!args || !(dir = args[1])) player:tell("You need to specify a direction"); return E_INVARG; elseif (valid(exit = this.location:match_exit(dir))) exit:move(this); new_room = this.location; suspend(0); this:tell(this.location:title()); this:tell_lines(this.location:description()); if (length(args) > 1 && this.location == new_room) this:go(@listdelete(args, 1)); endif elseif (exit == $failed_match) player:tell("You can't go that way (", dir, ")."); else player:tell("There are several ways which could be called '", dir, "' here."); endif . @program vehicle:description desc = pass(@args); loc = $string_utils:pronoun_sub(this.location_msg, this, this, this.location); if (typeof(desc) == LIST) return {@desc, loc}; elseif (`length(desc) ! E_TYPE => 0') return {desc, loc}; else return loc; endif . @program vehicle:tell this:announce_all(tostr(this.preface, @args)); pass(@args); .

generic region ship

(the idea here is that a region ship will be connected to various terminals [which have various attendants] and those terminals will send itinerary messages to the region ship, which are either appended to the end of the itinerary or flagged differently if the stops are already in the itinerary. then, the region ship will fly around [this:moveto($region_space); suspend(10); this:moveto(target); suspend(10); etc] taking on passengers [who can miss their flight] and kicking off passengers [if they don't leave when by the time the ship starts up again] as they travel.) (this also needs some good announce verbs-- first thought is between inner rooms and rooms w/ windows, perhaps also between inner rooms and 'secret' rooms [ductwork, engineering corridors, etc]-- but in any case, this needs the capacity to send a message to every room inside the ship, even secret ones. this is a problem, 'cause :_search_rooms is specifically assumed to not search secret rooms, but without a listing of secret rooms /somewhere/, there's no way to announce to them about ship events.) :drive override to do nothing :enter override to only allow objects in .toboard in. .ports list of all ports this ship services. displayed as the region name, unless the port is not part of a region, in which case the room name is displayed. (...i think?) .base_port the room to return to when no trips are queued. .itinerary {} list of targets to visit. .rooms {} list of rooms 'inside' ship, starts as {}, use :_search_rooms to set :_search_rooms tnt starts at this, follows this.exits until no more rooms in queue (ignoring any rooms that are in a region) .passengers {} characters that have boarded that have not yet unboarded .toboard {} characters that have queued trips but are not yet on the ship :_add_itin (loc, dest, character to board) -- if loc and dest are already on itin in proper order, just add character to the board/leave lists. otherwise, tack on loc & dest to the end of the itinerary. :on_ship (obj or list of objs) -- returns list of objs from args that are findable on the ship. :travel tnt (the actual moving-about verb) .travel_id task ID of the travel task, or 0 .kickoff_msg message displayed when a player is kicked off the ship .missed_msg message told to player when they miss their ship .flight_time int, time to spend in region space .dive_time int, time to spend diving into/out of regions .port_time int, time to spend waiting at a port -> these should be on the attendant :charter any to any charter <ship> to <port> :itin*erary any none none itin <ship> :schedules none none none tells current info about all ships connected to port @create $vehicle called "generic region ship","region ship",ship @prop ship.itinerary {} r @prop ship.rooms {} r @prop ship.ports {} r @prop ship.base_port #-1 rc @prop ship.passengers {} r @prop ship.toboard {} r @prop ship.kickoff_msg "%N %<is> thrown off the ship." rc @prop ship.missed_msg "You just missed your flight on %t!" rc @prop ship.flight_time 10 rc @prop ship.dive_time 2 rc @prop ship.port_time 5 rc @verb ship:drive any any any rxd @verb ship:"enter board embark" this none none @verb ship:_search_rooms tnt @verb ship:_add_itin*erary tnt @verb ship:on_ship tnt @verb ship:travel tnt @program ship:drive "region ships are AUTOMATED. automation is the wave of the future."; player:tell("I don't understand that."); . @program ship:enter if (!(player in this.toboard)) player:tell("You can't get on ", this:title(), "; you don't have a trip scheduled."); else pass(@args); if (player.location == this) this.passengers = setadd(this.passengers, player); this.toboard = setremove(this.toboard, player); endif endif . @program ship:_search_rooms "I have worries about this taking forever on large ships."; visited = connected = {}; queue = {this}; while (length(queue)) node = queue[1]; $command_utils:suspend_if_needed(2); queue = setremove(queue, node); if (node in visited || $object_utils:isa(node, $region)) continue; endif visited = setadd(visited, node); for exit in (`node.exits ! E_PERM => node:obvious_exits()') try if (!(`exit.dest ! ANY => #-1' in visited)) queue = setadd(queue, exit.dest); endif except (ANY) "...ignore bad exits..."; endtry endfor connected = setadd(connected, node); endwhile this.rooms = connected; . @program ship:_add_itin {pickup, dropoff, character} = args; if (pindex = $list_utils:iassoc(this.itinerary, pickup)) this.itinerary[pindex][2] = setadd(this.itinerary[pindex][2], character); if (dindex = $list_utils:iassoc(this.itinerary, dropoff)) this.itinerary[dindex][3] = setadd(this.itinerary[dindex][3], character); else this.itinerary = {@this.itinerary, {dropoff, {}, {character}}}; dindex = length(this.itinerary)-1; endif else this.itinerary = {@this.itinerary, {pickup, {character}, {}}, {dropoff, {}, {character}}}; pindex = length(itinerary)-2; dindex = length(itinerary)-1; endif if(this.travel_id == 0) fork(0) this:travel(); endfork endif "that's time in seconds til ship is at pickup, dropoff"; return {pindex * (this.flight_time + this.port_time + (this.dive_time * 2)), dindex * (this.flight_time + this.port_time + (this.dive_time * 2))}; . @program ship:on_ship ":on_ship(OBJ or {OBJS})"; ""; "Returns a list of any objs from list that are in searchable ship rooms. Returns the empty list if no objects found."; objs = typeof(args[1]) == LIST ? objs | {objs}; found = {}; if (this.rooms == {}) this:_search_rooms(); endif for obj in (objs) if (obj.location in this.rooms) found = {@found, obj}; objs = setremove(objs, obj); endif endfor return found; . @program ship:travel "todo: work on end of verb so travel never stalls (also maybe forking so the task doesn't die if there are :moveto errors?)"; this.travel_id = task_id(); suspend(this.port_time); this:moveto(this.base_port.region[1]); suspend(this.dive_time); this:moveto(`$region_space ! E_PROPNF => #-1'); while (length(this.itinerary)) {target, ?toboard = {}, ?toleave = {}} = this.itinerary[1]; this.itinerary = this.itinerary[2..$]; suspend(this.flight_time); this:moveto(target.region[1]); suspend(this.dive_time); this:moveto(target); this.toboard = toboard; suspend(this.port_time); "kick off anyone scheduled to get off on this stop who isn't hidden"; if (stragglers = this:on_ship(toleave)) for p in (stragglers) this.passengers = setremove(this.passengers, p); $you:say_action(this.kickoff_msg, player, this); try p:moveto(this.location); suspend(0); except (ANY) endtry if(p.location == this.location) this.location:announce($string_utils:pronoun_sub(this.player_exit_msg, player, this, this.location)); endif endfor endif for p in (this.toboard) try p:tell($string_utils:pronoun_sub(this.missed_msg)); except (ANY) entry endfor this:moveto(`$region_space ! E_PROPNF => #-1'); endwhile suspend(this.port_time); this:moveto(this.base_port.region[1]); suspend(this.dive_time); this:moveto(this.base_port); this.travel_id = 0; return; .