PHAT-CLIENT TUTORIAL

The following is an assortment of code we wrote to support the development of an RPG style gaming environment in MUE/MOO. PLEASE REALIZE that it is intended primarily for programmers who want to incorporate RPG gaming elements into their own MOOs, perhaps even in order to interface to the MAM framework, though of course this integration is not required to find the code useful. Hopefully you have already taken a moment to look at the general MUE/MOO introduction information, the combat system to get a feel for the game elements, as well as the MUE/MOO and MAM interfaces to get a sense of how XML messages get passed between MOO and MAM.

We needed to custom code the gaming environment into MOO because it grew out of earlier MUD systems that were geared more specifically toward hack and slash adventuring scenarios. Many wanted to take advantage of the extensible, real-time multiuser, chat-based functionality afforded by MUDs, but in the context of business, education, and research. This desire caused a lot of the game stuff to either be stripped out or left underdeveloped in various MOO server distributions (such as v1.1 the High Wired enCore implementation of LambdaMOO that we're using). We wanted to reintroduce the gaming elements, so you could experience the best and worst of both worlds. Undoubtedly, a lot of stuff could be done better. But hey, it's just a beginning, and it works for the most part.

NOTE: This tutorial assumes general understanding of how to program in MOO. If you lack this knowledge, you may want to refer to the following reference materials. It also assumes that you realize the code is provided primarily for reference purposes, and local modifications would need to be made to implement it in other environments. Of course, you are free to cut and paste as you see fit.

QUICK LINKS: MUE/MOO Game and Utility Code

Player Verbs and Properties
Monster Verbs and Properties
Barter Bot Verbs and Properties
Space Verbs and Properties
Utility Verbs

Phat-Client Tutorial: Menuing and Functionality
Phat-Client Tutorial: Agent Attributes
Phat-Client Tutorial: Custom Look & Feel

Phat-Client Tutorial: [for programmers] Packages and Agent Types
Phat-Client Tutorial: [for programmers] Messaging and Agent Layers
Phat-Client Tutorial: [for programmers] MUE/MOO and MAM interfaces
Phat-Client Tutorial: [for programmers] MUE/MOO Game and Utility Code
Phat-Client Tutorial: [for programmers] Server-Side Scripts
Phat-Client Tutorial: [for programmers] MAM UML Documentation
Phat-Client Tutorial: [for programmers] MAM Javadocs

PROXY portal

PLAYER VERBS AND PROPERTIES

The first thing we needed to do was give the $player class (obj #6 in this case) a few new verbs and properties. The verbs include code for: generating readout screens of current status, skills, experience points, and doing player comparisons; handling hitpoint checks, leveling, and funds transfers; and utilizing combat tactics and tongues in relation to other players and monsters.

PLAYER VERBS

The first verb we'll look at creates a skills table. It displays competency in the various tactics and tongues deployed in combat. To see an example of what it looks like in use, check out the player skills screen in the combat system overview.

--------------------------------------------------------------------
$player:sk*ills   none none none
permissions:      rd

col_width=68;
player:tell("");
player:tell("Skills for ", player.name, ":");
player:tell("------------------------------Tactics-------------------------------");
row1 = {{"tactic 1", "left", 8},{$string_utils:to_percent(player.tactic1),"right",5},{"","",10},{"tactic 2","left",13},{$string_utils:to_percent(player.tactic2),"right",5},{"","",10},{"tactic 3","left",12},{$string_utils:to_percent(player.tactic3),"right",5}};
player:tell($string_utils:flex_col_format(row1));
player:tell("------------------------------Tongues-------------------------------");
row2 = {{"tongue 1", "left", 8},{$string_utils:to_percent(player.tongue1),"right",5},{"","",10},{"tongue 2","left",13},{$string_utils:to_percent(player.tongue2),"right",5},{"","",10},{"tongue 3","left",12},{$string_utils:to_percent(player.tongue3),"right",5}};
player:tell($string_utils:flex_col_format(row2));
player:tell($string_utils:space(col_width, "-"));
player:tell($string_utils:format_two_col("Practice sessions attended: " + $string_utils:from_value(player.practice_sessions_attended), "Practice sessions remaining: " + $string_utils:from_value(player.practice_sessions_remaining),col_width));
endif
.

This verb creates an experience point table. It displays the required points for the various levels, the player's current level, and the needed points to advance. To see an example of what it looks like in use, check out the player experience screen in the combat system overview.

--------------------------------------------------------------------
$player:ex*perience   none none none
permissions:          rd

total_exp_levels = length(player.exp_table);
player:tell("");
player:tell(("Experience required, levels 2 to " + $string_utils:from_value(total_exp_levels + 1)) + ":");
player:tell("-------------------------------------------------");
current_exp = $string_utils:group_number(player.exp) + ")";
needed_exp = 0;
if (player.lev <= total_exp_levels)
  needed_exp = $string_utils:group_number(player.exp_table[player.lev]) + ")";
endif
exp_table = {{"( 2)", $string_utils:group_number(player.exp_table[1]) + " exp"}};
exp_table = listappend(exp_table, {"( 3)", $string_utils:group_number(player.exp_table[2]) + " exp"});
levels_after_2 = player.exp_table[3..total_exp_levels];
level_id = 3;
for exp_for_level in (levels_after_2)
  level_id = level_id + 1;
  exp_table = listappend(exp_table, {("( " + $string_utils:from_value(level_id)) + ")", $string_utils:group_number(exp_for_level) + " exp"});
endfor
for exp_line in (exp_table)
  {exp_level, exp_required_for_level} = exp_line;
  if (exp_level == "( 2)")
    s = $string_utils:flex_col_format({{exp_level, "", 8}, {exp_required_for_level, "right", 15}, {"", "", 2}, {"(Current:", "", 10}, {current_exp, "right", 14}});
  elseif (exp_level == "( 3)")
    if (player.lev <= total_exp_levels)
      s = $string_utils:flex_col_format({{exp_level, "", 8}, {exp_required_for_level, "right", 15}, {"", "", 2}, {"(Needed:", "", 10}, {needed_exp, "right", 14}});
    else
      s = $string_utils:flex_col_format({{exp_level, "", 8}, {exp_required_for_level, "right", 15}, {"", "", 2}, {"(Needed:", "", 10}, {"0)", "right", 14}});
    endif
  else
    s = $string_utils:flex_col_format({{exp_level, "", 8}, {exp_required_for_level, "right", 15}});
  endif
  player:tell(s);
endfor
player:tell("-------------------------------------------------");
if ($object_utils:isa(player, $guest))
  player:tell("Sorry ", this.name, ", but unless you register you will be unable to gain experience points and advance levels...");
  return;
endif
.

The next verb shows most of the player data. The code starts by determining what rank to assign the player's alienation, ambition, and anxiety attributes. It then uses a custom two column table layout (see the utilities section below) for displaying statistics on funds, player characteristics, login times, hitpoints, combat wins and losses, and so on. To see an example of what it looks like in use, check out the player stats screen in the combat system overview.

--------------------------------------------------------------------
$player:st*ats   none none none
permissions:     rd

col_width = 68;
quota_total = $quota_utils:get_quota(player);
quota_used = $quota_utils:summarize_one_user(player)[1];
if (player.ali <= 0.4)
  ali_rank = "very low";
elseif ((player.ali >= 0.5) && (player.ali < 1.5))
  ali_rank = "low";
elseif ((player.ali >= 1.5) && (player.ali < 2.5))
  ali_rank = "moderate";
elseif ((player.ali >= 2.5) && (player.ali < 3.5))
  ali_rank = "high";
elseif (player.ali >= 3.5)
  ali_rank = "very high";
endif
if (player.amb <= 0.4)
  amb_rank = "very low";
elseif ((player.amb >= 0.5) && (player.amb < 1.5))
  amb_rank = "low";
elseif ((player.amb >= 1.5) && (player.amb < 2.5))
  amb_rank = "moderate";
elseif ((player.amb >= 2.5) && (player.amb < 3.5))
  amb_rank = "high";
elseif (player.amb >= 3.5)
  amb_rank = "very high";
endif
if (player.anx <= 0.4)
  anx_rank = "very low";
elseif ((player.anx >= 0.5) && (player.anx < 1.5))
  anx_rank = "low";
elseif ((player.anx >= 1.5) && (player.anx < 2.5))
  anx_rank = "moderate";
elseif ((player.anx >= 2.5) && (player.anx < 3.5))
  anx_rank = "high";
elseif (player.anx >= 3.5)
  anx_rank = "very high";
endif
player:tell("");
player:tell($string_utils:format_two_col(("Stats for " + player.name) + ":", "Funds: " + $string_utils:format_funds(player.funds), col_width));
player:tell($string_utils:space(col_width, "-"));
row1 = {{"ALI: ", "left", 5}, {$string_utils:from_value($math_utils:trunc(player.ali)), "left", 3}, {"/", "", 1}, {$string_utils:from_value(player.ali_max), "", 4}, {("[" + ali_rank) + "]", "left", 13}, {"Login: " + ctime(time() - connected_seconds(player)), "right", 42}};
player:tell($string_utils:flex_col_format(row1));
row2 = {{"AMB: ", "left", 5}, {$string_utils:from_value($math_utils:trunc(player.amb)), "left", 3}, {"/", "", 1}, {$string_utils:from_value(player.amb_max), "", 4}, {("[" + amb_rank) + "]", "left", 13}, {"Time: " + ctime(), "right", 42}};
player:tell($string_utils:flex_col_format(row2));
row3 = {{"ANX: ", "left", 5}, {$string_utils:from_value($math_utils:trunc(player.anx)), "left", 3}, {"/", "", 1}, {$string_utils:from_value(player.anx_max), "", 4}, {("[" + anx_rank) + "]", "left", 13}, {"Played: " + $string_utils:from_seconds(connected_seconds(this)), "right", 42}};
player:tell($string_utils:flex_col_format(row3));
player:tell($string_utils:space(col_width, "-"));
player:tell($string_utils:format_two_col(((((("STR: " + $string_utils:from_value(player.str)) + "/") + $string_utils:from_value(player.str_max)) + " [+") + $string_utils:from_value(player.str_bon)) + "]", "EXP: " + $string_utils:group_number(player.exp), col_width));
player:tell($string_utils:format_two_col(((((("INT: " + $string_utils:from_value(player.int)) + "/") + $string_utils:from_value(player.int_max)) + " [+") + $string_utils:from_value(player.int_bon)) + "]", ((((("Level: " + $string_utils:from_value(player.lev)) + " out of ") + $string_utils:from_value(player:get_level_max())) + " [") + player.status) + "]", col_width));
player:tell($string_utils:format_two_col(((((("WIS: " + $string_utils:from_value(player.wis)) + "/") + $string_utils:from_value(player.wis_max)) + " [+") + $string_utils:from_value(player.wis_bon)) + "]", "You feel: " + player:get_health_description(), col_width));
player:tell($string_utils:format_two_col(((((("DEX: " + $string_utils:from_value(player.dex)) + "/") + $string_utils:from_value(player.dex_max)) + " [+") + $string_utils:from_value(player.dex_bon)) + "]", (((("Hitpoints: " + $string_utils:from_value(player.hp)) + "/") + $string_utils:from_value(player.hp_max)) + " -- wimpout at ") + $string_utils:from_value(player.wimpout),col_width));
player:tell($string_utils:format_two_col(((((("CHR: " + $string_utils:from_value(player.chr)) + "/") + $string_utils:from_value(player.chr_max)) + " [+") + $string_utils:from_value(player.chr_bon)) + "]", ((("Objects: " + $string_utils:group_number(quota_used)) + " bytes used of ") + $string_utils:group_number(quota_total)) + " max", col_width));
player:tell($string_utils:space(col_width, "-"));
player:tell($string_utils:format_two_col("Combat wins: " + $string_utils:from_value(player.combat_wins), "Combat losses: " + $string_utils:from_value(player.combat_losses), col_width));
if ($object_utils:isa(player, $guest))
  player:tell("Sorry ", this.name, ", but you won't see much change in these values until you become a registered agent...");
  return;
endif
.

The following verb basically does the same thing as the stats verb described above, but for the entire agent collective. Thus, the beginning of the code is gathering stats on all currently connected agents. It also shows how many connections are being made with the phat-client. To see an example of what it looks like in use, check out the player stats screen in the combat system overview.

--------------------------------------------------------------------
$player:co*llective   none none none
permissions:          rd

col_width = 68;
player_total = length(connected_players());
coll_ali = 0.0;
coll_amb = 0.0;
coll_anx = 0.0;
coll_str = 0;
coll_int = 0;
coll_wis = 0;
coll_dex = 0;
coll_chr = 0;
coll_combat_wins = 0;
coll_combat_losses = 0;
coll_exp = 0;
coll_lev = 0;
coll_hp = 0;
coll_hp_max = 0;
coll_wimpout = 0;
phat_agents = 0;
for p in (connected_players())
  if (p.phat_client != 0)
    phat_agents = phat_agents + 1;
  endif
  coll_ali = coll_ali + p.ali;
  coll_amb = coll_amb + p.amb;
  coll_anx = coll_anx + p.anx;
  coll_str = coll_str + p.str;
  coll_int = coll_int + p.int;
  coll_wis = coll_wis + p.wis;
  coll_dex = coll_dex + p.dex;
  coll_chr = coll_chr + p.chr;
  coll_combat_wins = coll_combat_wins + p.combat_wins;
  coll_combat_losses = coll_combat_losses + p.combat_losses;
  coll_exp = coll_exp + p.exp;
  coll_lev = coll_lev + p.lev;
  coll_hp = coll_hp + p.hp;
  coll_hp_max = coll_hp_max + p.hp_max;
  coll_wimpout = coll_wimpout + p.wimpout;
endfor
coll_ali = coll_ali / tofloat(length(connected_players()));
coll_amb = coll_amb / tofloat(length(connected_players()));
coll_anx = coll_anx / tofloat(length(connected_players()));
coll_str = coll_str / length(connected_players());
coll_int = coll_int / length(connected_players());
coll_dex = coll_wis / length(connected_players());
coll_wis = coll_dex / length(connected_players());
coll_chr = coll_chr / length(connected_players());
coll_exp = coll_exp / length(connected_players());
coll_lev = coll_lev / length(connected_players());
coll_hp = coll_hp / length(connected_players());
coll_hp_max = coll_hp_max / length(connected_players());
coll_wimpout = coll_wimpout / length(connected_players());
coll_combat_wins = coll_combat_wins / length(connected_players());
coll_combat_losses = coll_combat_losses / length(connected_players());
if (coll_ali <= 0.4)
  ali_rank = "very low";
elseif ((coll_ali >= 0.5) && (coll_ali < 1.5))
  ali_rank = "low";
elseif ((coll_ali >= 1.5) && (coll_ali < 2.5))
  ali_rank = "moderate";
elseif ((coll_ali >= 2.5) && (coll_ali < 3.5))
  ali_rank = "high";
elseif (coll_ali >= 3.5)
  ali_rank = "very high";
endif
if (coll_amb <= 0.4)
  amb_rank = "very low";
elseif ((coll_amb >= 0.5) && (coll_amb < 1.5))
  amb_rank = "low";
elseif ((coll_amb >= 1.5) && (coll_amb < 2.5))
  amb_rank = "moderate";
elseif ((coll_amb >= 2.5) && (coll_amb < 3.5))
  amb_rank = "high";
elseif (coll_amb >= 3.5)
  amb_rank = "very high";
endif
if (coll_anx <= 0.4)
  anx_rank = "very low";
elseif ((coll_anx >= 0.5) && (coll_anx < 1.5))
  anx_rank = "low";
elseif ((coll_anx >= 1.5) && (coll_anx < 2.5))
  anx_rank = "moderate";
elseif ((coll_anx >= 2.5) && (coll_anx < 3.5))
  anx_rank = "high";
elseif (coll_anx >= 3.5)
  anx_rank = "very high";
endif
player:tell("");
player:tell($string_utils:format_two_col("Stats for the agent collective:", (((("all [n = " + $string_utils:from_value(player_total)) + "]; ") + "phat-client [n = ") + $string_utils:from_value(phat_agents)) + "]", col_width));
player:tell($string_utils:space(col_width, "-"));
row1 = {{"ALI: ", "left", 5}, {$string_utils:from_value($math_utils:trunc(coll_ali)), "left", 3}, {"/", "", 1}, {$string_utils:from_value(player.ali_max), "", 4}, {("[" + ali_rank) + "]*", "left", 13}, {"Time: " + ctime(), "right", 42}};
player:tell($string_utils:flex_col_format(row1));
row2 = {{"AMB: ", "left", 5}, {$string_utils:from_value($math_utils:trunc(coll_amb)), "left", 3}, {"/", "", 1}, {$string_utils:from_value(player.amb_max), "", 4}, {("[" + amb_rank) + "]*", "left", 13}};
player:tell($string_utils:flex_col_format(row2));
row3 = {{"ANX: ", "left", 5}, {$string_utils:from_value($math_utils:trunc(coll_anx)), "left", 3}, {"/", "", 1}, {$string_utils:from_value(player.anx_max), "", 4}, {("[" + anx_rank) + "]*", "left", 13}, {"*All attribute scores are averaged", "right", 42}};
player:tell($string_utils:flex_col_format(row3));
player:tell($string_utils:space(col_width, "-"));
player:tell($string_utils:format_two_col(((("STR: " + $string_utils:from_value(coll_str)) + "/") + $string_utils:from_value(player.str_max)) + " [averaged]", ("EXP: " + $string_utils:group_number(coll_exp)) + " [averaged]", col_width));
player:tell($string_utils:format_two_col(((("INT: " + $string_utils:from_value(coll_int)) + "/") + $string_utils:from_value(player.int_max)) + " [averaged]", ((("Level: " + $string_utils:from_value(coll_lev)) + " out of ") + $string_utils:from_value(player:get_level_max())) + " [averaged]", col_width));
player:tell($string_utils:format_two_col(((("WIS: " + $string_utils:from_value(coll_wis)) + "/") + $string_utils:from_value(player.wis_max)) + " [averaged]", "", col_width));
player:tell($string_utils:format_two_col(((("DEX: " + $string_utils:from_value(coll_dex)) + "/") + $string_utils:from_value(player.dex_max)) + " [averaged]", "", col_width));
player:tell($string_utils:format_two_col(((("CHR: " + $string_utils:from_value(coll_chr)) + "/") + $string_utils:from_value(player.chr_max)) + " [averaged]", ((((("Hitpoints: " + $string_utils:from_value(coll_hp)) + "/") + $string_utils:from_value(coll_hp_max)) + " -- wimpout at ") + $string_utils:from_value(coll_wimpout)) + " [averaged]", col_width));
player:tell($string_utils:space(col_width, "-"));
player:tell($string_utils:format_two_col(("Combat wins: " + $string_utils:from_value(coll_combat_wins)) + " [averaged]", ("Combat losses: " + $string_utils:from_value(coll_combat_losses)) + " [averaged]", col_width));
.

The consider verb shows you a brief stats summary of the player or monster object you are contemplating engaging in combat. It also tells you how likely you will be to win. To see an example of what it looks like in use, check out the player consider screen in the combat system overview.

--------------------------------------------------------------------
$player:con*sider   any none none
permissions:        rd

if ((!$object_utils:isa(dobj, $player)) && (!$object_utils:isa(dobj, $generic_monster)))
  player:tell("Not possible...");
  return 0;
elseif ($object_utils:isa(dobj, $player) && (dobj.stats_done != 1))
  player:tell("It's not worth considering at this point...");
  return 0;
elseif ($object_utils:isa(dobj, $player) || $object_utils:isa(dobj,
 $generic_monster))
  col_width = 68;
  if (player.name == dobj.name)
    player:tell("Rather egocentric of you...");
    return 0;
  endif
  if ($object_utils:isa(dobj, $player))
    obj_checker = dobj;
  elseif ($object_utils:isa(dobj, $generic_monster))
    obj_checker = player;
  endif
  player:tell("");
  player:tell($string_utils:space(col_width, "-"));
  player:tell($string_utils:format_two_col(("Stats for " + dobj.name) + ":", "EXP: " + $string_utils:group_number(dobj.exp), col_width));
  player:tell($string_utils:format_two_col("Wins: " + $string_utils:from_value(dobj.combat_wins), ((((("Level: " + $string_utils:from_value(dobj.lev)) + " out of ") + $string_utils:from_value(length(dobj.exp_table) + 1)) + " [") + dobj.status) + "]", col_width));
  player:tell($string_utils:format_two_col("Losses: " + $string_utils:from_value(dobj.combat_losses), (((("Hitpoints: " + $string_utils:from_value(dobj.hp)) + "/") + $string_utils:from_value(dobj.hp_max)) + " -- wimpout at ") + $string_utils:from_value(dobj.wimpout), col_width));
  player:tell($string_utils:space(col_width, "-"));
  player:tell($string_utils:format_two_col(("Stats for " + player.name) + ":", "EXP: " + $string_utils:group_number(player.exp), col_width));
  player:tell($string_utils:format_two_col("Wins: " + $string_utils:from_value(player.combat_wins), ((((("Level: " + $string_utils:from_value(player.lev)) + " out of ") + $string_utils:from_value(length(player.exp_table) + 1)) + " [") + player.status) + "]", col_width));
  player:tell($string_utils:format_two_col("Losses: " + $string_utils:from_value(player.combat_losses), (((("Hitpoints: " + $string_utils:from_value(player.hp)) + "/") + $string_utils:from_value(player.hp_max)) + " -- wimpout at ") + $string_utils:from_value(player.wimpout), col_width));
  player:tell($string_utils:space(col_width, "-"));
  if ($object_utils:isa(player, $guest))
    player:tell("Be aware ", this.name, ", until you properly register you won't be able to effectively engage in battle...");
    return;
  elseif ($object_utils:isa(dobj, $player) && (player.hp_max > (dobj.hp_max + 10)))
    player:tell("You could probably take ", dobj.name, "...");
  elseif ($object_utils:isa(dobj, $player) && (player.hp_max < (dobj.hp_max - 10)))
    player:tell("You better watch out, ", dobj.name, " seems pretty tough...");
  elseif ($object_utils:isa(dobj, $player))
    player:tell("Looks like you and ", dobj.name, " are pretty evenly matched...");
  elseif ($object_utils:isa(dobj, $generic_monster))
    player:tell("Be careful, things are not always as they seem...");
  endif
endif
.

The generate stats verb is responsible for setting your scores for strength, intelligence, wisdom, dexterity, and charisma. The scores are determined when you roll five nine-sided dice in a special location, where you can also train in the various tactics and tongues needed for combat.

--------------------------------------------------------------------
$player:gen_stats   this none this
permissions:        rxd

done = player.stats_done;
max_limit = 35;
while (done == 0)
  rolls = 0;
  subtotal = 0;
  stats = {};
  while (rolls < 5)
    current_roll = random(9);
    rolls = rolls + 1;
    subtotal = subtotal + current_roll;
    if (subtotal > max_limit)
      stats = {};
      rolls = 0;
      subtotal = 0;
    else
      stats = listappend(stats, current_roll);
    endif
  endwhile
  player.str = stats[1];
  player.int = stats[2];
  player.wis = stats[3];
  player.dex = stats[4];
  player.chr = stats[5];
  col_width=68;
  player:tell("");
  player:tell("Randomly generated attribute scores for ", player.name, ":");
  player:tell($string_utils:space(col_width, "-"));
  player:tell($string_utils:format_two_col("         STRENGTH: " + $string_utils:from_value(player.str), " out of a maximum possible score of " + $string_utils:from_value(player.str_max) + "     ",col_width));
  player:tell($string_utils:format_two_col("     INTELLIGENCE: " + $string_utils:from_value(player.int), " out of a maximum possible score of " + $string_utils:from_value(player.int_max) + "     ",col_width));
  player:tell($string_utils:format_two_col("           WISDOM: " + $string_utils:from_value(player.wis), " out of a maximum possible score of " + $string_utils:from_value(player.wis_max) + "     ",col_width));
  player:tell($string_utils:format_two_col("        DEXTERITY: " + $string_utils:from_value(player.dex), " out of a maximum possible score of " + $string_utils:from_value(player.dex_max) + "     ",col_width));
  player:tell($string_utils:format_two_col("         CHARISMA: " + $string_utils:from_value(player.chr), " out of a maximum possible score of " + $string_utils:from_value(player.chr_max) + "     ",col_width));
  player:tell($string_utils:space(col_width, "-"));
  roll_again = $command_utils:yes_or_no("Regenerate scores?");
  if (!roll_again)
    done = 1;
  endif
endwhile
player.stats_done = 1;
player:tell("You have successfuly selected your scores!");
.

After you have successfully generated your scores, you are requested to input information that gets displayed when other agents look at or finger you. This includes such things as gender, personal description, research interests, and associated home page URL. This verb is essentially a subset of a verb that ships with v1.1 the High Wired enCore implementation of LambdaMOO.

--------------------------------------------------------------------
$player:reqprefs   this none none
permissions:       rxd

set_task_perms(player);
if ($object_utils:isa(player, $guest))
  return 0;
endif
if (player.stats_done !=1)
  $player:gen_stats();
endif
if (player.reqprefs != 1)
finished = 0;
while (!finished)
  player:tell("Now please respond to the following before continuing...");
  "------------------------- Gender  ------------------------";
  player:tell("");
  player:tell("Setting Your Gender");
  player:tell("-------------------");
  player:tell("Your gender is currently set to ", player.gender, ".");
  player:tell("Available genders: ", $string_utils:english_list($gender_utils.genders, "", " or "));
  if (gender = $command_utils:read("your choice.  Press RETURN or ENTER to continue"))
    if (gender in $gender_utils.genders)
      $gender_utils:set(player, gender);
      player.gender = gender;
      player:tell("Your gender has been set to ", gender, ".");
    else
      player:tell("Sorry, ", gender, " is not an available gender.  Please try again.");
      player:reqprefs();
      return 0;
    endif
  endif
  player:tell("");
  finished = 1;
endwhile
finished = 0;
while (!finished)
  "------------------------- Description  -------------";
  player:tell("Describing Yourself");
  player:tell("-------------------");
  if (player.description)
    player:tell("Your description is currently set to:");
    player:tell_lines(player.description);
    player:tell("");
  endif
  if (descr = $command_utils:read("a description of yourself.  Press RETURN or ENTER to continue"))
    player:tell("");
    player:tell("You typed:");
    player:tell(descr);
    if ($command_utils:yes_or_no("Are you sure you want to change your description to this?"))
      player.description = descr;
      player:tell("Description set.");
    else
      player:tell("No changes.");
    endif
  endif
  player:tell("");
  finished = 1;
endwhile
finished = 0;
while (!finished)
  "------------------------- Research/Interests  -------------";
  player:tell("Research and Interests");
  player:tell("----------------------");
  if (player.interests_msg)
    player:tell("Your research and interests message is currently set to:");
    player:tell_lines(player.interests_msg);
    player:tell("");
  endif
  if (msg = $command_utils:read("your research and other interests.  Press RETURN or ENTER to continue"))
    player:tell("");
    player:tell("You typed:");
    player:tell(msg);
    if ($command_utils:yes_or_no("Are you sure you want to change your research and interests message to this?"))
      player.interests_msg = msg;
      player:tell("Research and Interest Message Set.");
    else
      player:tell("No changes.");
    endif
  endif
  finished = 1;
  player:tell("");
endwhile
finished = 0;
while (!finished)
  "------------------------- Home Page  ----------------------";
  player:tell("Home Page");
  player:tell("---------");
  if (player.homepage)
    player:tell("Your home page is currently: ", player.homepage);
    player:tell("");
  endif
  if (homepage = $command_utils:read("the URL for your World Wide Web home page.  Press RETURN or ENTER to continue"))
    player:tell("");
    player:tell("You typed:");
    player:tell(homepage);
    if ($command_utils:yes_or_no("Are you sure you want to set your home page to this?"))
      player.homepage = homepage;
      player.url_address = homepage;
      player:tell("Homepage set.");
    else
      player:tell("No changes.");
    endif
  endif
  finished = 1;
endwhile
player.reqprefs = 1;
player:tell("");
player:tell("Finished.  Exiting...");
else
  player:tell("The requested preferences have already been set!");
endif
.

The next series of verbs are all related to combat. They should be pretty straight forward. We start with a tactic verb. There are six tactics that get mobilized in relation to nine tongues (see the skills screen in the combat system overview). Each of the tactics works pretty much the same way, so we'll only give a single example.

First is a check to see if the caller is a $generic_monster, or a $player. Then we see if the caller is skilled (practiced) in the tactic being used. We also check to see if the thing being attacked understands the tongue the attacker is speaking in. If not, nothing happens. Then we check the funds adjustment based on combat location, chosen tactic, and tongue. Finally, we do the damage calculations, and determine waht sort of notifications to send to the attacker, the attacked, and the observers.

--------------------------------------------------------------------
$player:bullshit   this in/inside/into any
permissions:       rd

if ($object_utils:isa(dobj, $generic_monster))
  dobj:bs( dobj, prepstr, iobj );
elseif ($object_utils:isa(dobj, $player))
  if (player.bullshit == 0)
    player:tell("You're not skilled in that tactic!");
    return;
  endif
  if (dobj.bullshit == 0)
    player:tell(dobj.name, " is not skilled in that tactic!");
    return;
  endif
  tongues = {"geek", "ghetto", "imperialist", "jingoist", "marxist", "pomo"};
  tongue_to_use = iobjstr;
  known_tongues = {};
  for tongue in (tongues)
    if (player.(tongue) > 0 && dobj.(tongue) > 0)
      known_tongues = listappend(known_tongues, tongue);
    endif
  endfor
  if (!(tongue_to_use in known_tongues))
    player:tell("Luckily for ", dobj.name, ", ", dobj.ps, " doesn't understand you.");
    return;
  endif
  if (player.name == dobj.name)
    player:tell("You lay it on yourself pretty heavy...");
    return;
  endif
  "check for funds changes depending on space, tactic and tongue";
  total_funds_change = player:calc_cost(player.game_space, "bullshit", tongue);
  damage = random(5);
  if (damage == 1)
    damage_str = "hitpoint";
  else
    damage_str = "hitpoints";
  endif
  dobj:tell(player.name, " is trying to bullshit you in ", tongue_to_use, "!");
  player_attack_successful = random(100) <= ((15 + player.int) + player.(tongue_to_use));
  defender_attack_successful = random(100) <= ((15 + dobj.int) + dobj.(tongue_to_use));
  if (player.hp <= player.wimpout)
    player:tell("You can't do that!  You're too much of a wimp...");
    return;
  elseif (dobj.hp <= dobj.wimpout)
    player:tell("You can't do that!  ", dobj.name, "  is too much of a wimp...");
    return;
  endif
  if (player.practice_sessions_attended == 0 || dobj.practice_sessions_attended == 0)
    player:tell("There's a serious language gap!");
    return;
  endif
  if (player_attack_successful && (!defender_attack_successful))
    player:tell("You just bullshit ", dobj.name, " for ", damage, " ", damage_str, "!");
    dobj:tell("You got bullshit by ", player.name, " and couldn't help but like it!");
    dobj:tell("It cost you ", damage, " ", damage_str, "!");
    dobj:hp_modify( -damage, this );
    player:adjust_funds( total_funds_change );
    fork( 2 )
      player:tell( "Whoa!  Your funds just got adjusted..." );
    endfork
  elseif (defender_attack_successful && (!player_attack_successful))
    player:tell("Your attempt backfired!  You got nailed by ", dobj.name, " in ", tongue_to_use, "!");
    player:tell("You lost ", damage, " ", damage_str, "!");
    dobj:tell("Reversal!  You counter-attacked ", player.name, " back in ", tongue_to_use, "...");
    dobj:tell("It cost ", player.name, " ", damage, " ", damage_str, "!");
    player.location:announce_all_but({dobj, player}, dobj.name, " just nailed ", player.name, "!");
    player:hp_modify( -damage, dobj );
  elseif (player_attack_successful && defender_attack_successful)
    player:tell("You bullshit ", dobj.name, " in ", tongue_to_use, " for ", damage, " ", damage_str, "!");
    player:tell("But you also got hit for ", damage, " ", damage_str, "!");
    dobj:tell(player.name, " just bullshit you for ", damage, " ", damage_str, ", but it felt like a favor!");
    dobj:tell("But you got ", player.name, " back for ", damage, " ", damage_str, "!");
    player.location:announce_all_but({dobj, player}, player.name, " and ", dobj.name, " are doing each other some serious damage, but they sure seem to be enjoying it!!!");
    player:hp_modify( -damage, dobj );
    dobj:hp_modify( -damage, player );
  else
    player:tell("You attempt to bullshit ", dobj.name, " for quite some time in ", tongue_to_use, ".  You feel rather weary...");
    player.location:announce_all_but({dobj, player}, player.name, " is trying to bullshit ", dobj.name, " in ", tongue_to_use, "!");
  endif
endif
.

Next comes a cost calculator (calc_cost) that determines whether funds need adjusting during battle. In this case, funds adjustment is based on combat location, chosen tactic and tongue. The cost calculator verb is followed by a hitpoint check (hp_check) which looks to see whether a wimpout factor has been reached. If so, it increments the combat losses property, sends an XML message to MAM, and teleports the player home. The following hitpoint modifier verb (hp_modify) is what calls the hitpoint check.

--------------------------------------------------------------------
$player:calc_cost   this none this
permissions:        rxd

{space, tactic, tongue} = args;
" Check for funds changes depending on space, tactic and tongue. ";
fund_transitions = {
                     { 
                       "bullshit",
                       {
                         { "cp", "pomo", 1.0 },
                         { "cp", "marxist", 1.0 },
                         { "cp", "ghetto", 1.0 },
                         { "cp", "geek", -1.0 },
                         { "cp", "imperialist", -1.0 },
                         { "cp", "jingoist", -1.0 },
                         { "fs", "pomo", 1.0 },
                         { "fs", "marxist", 1.0 },
                         { "fs", "geek", 1.0 },
                         { "fs", "ghetto", -1.0 },
                         { "fs", "jingoist", -1.0 },
                         { "fs", "imperialist", -1.0 },
                         { "si", "geek", 1.0 },
                         { "si", "imperialist", 1.0 },
                         { "si", "jingoist", 1.0 },
                         { "si", "marxist", -1.0 },
                         { "si", "pomo", -1.0 }
                       }
                     },
                     { 
                       "compliment",
                       {
                         { "cp", "pomo", 1.0 },
                         { "cp", "marxist", 1.0 },
                         { "cp", "ghetto", 1.0 },
                         { "fs", "pomo", 1.0 },
                         { "fs", "marxist", 1.0 },
                         { "fs", "geek", 1.0 }
                       }
                     },
                     { 
                       "confuse",
                       {
                         { "cp", "pomo", 1.0 },
                         { "cp", "marxist", 1.0 },
                         { "cp", "ghetto", 1.0 },
                         { "cp", "geek", -1.0 },
                         { "cp", "imperialist", -1.0 },
                         { "cp", "jingoist", -1.0 },
                         { "fs", "pomo", 1.0 },
                         { "fs", "marxist", 1.0 },
                         { "fs", "geek", 1.0 },
                         { "fs", "ghetto", -1.0 },
                         { "fs", "jingoist", -1.0 },
                         { "fs", "imperialist", -1.0 },
                         { "si", "geek", 1.0 },
                         { "si", "imperialist", 1.0 },
                         { "si", "jingoist", 1.0 },
                         { "si", "marxist", -1.0 },
                         { "si", "pomo", -1.0 }
                       }
                     },
                     {
                       "deceive",
                       {
                         { "cp", "geek", -3.0 },
                         { "cp", "imperialist", -3.0 },
                         { "cp", "jingoist", -3.0 },
                         { "fs", "pomo", 2.0 },
                         { "fs", "marxist", 2.0 },
                         { "fs", "geek", 2.0 },
                         { "si", "geek", 3.0 },
                         { "si", "imperialist", 3.0 },
                         { "si", "jingoist", 3.0 }
                       }
                     },
                     {
                       "insult",
                       {
                         { "cp", "geek", -3.5 },
                         { "cp", "imperialist", -3.5 },
                         { "cp", "jingoist", -3.5 },
                         { "fs", "ghetto", -4.0 },
                         { "fs", "jingoist", -4.0 },
                         { "fs", "imperialist", -4.0 },
                         { "si", "marxist", -7.0 },
                         { "si", "pomo", -7.0 }
                       }
                     },
					 {
					   "pulverize",
					   {
					     { "cp", "geek", -9.0 },
                         { "cp", "imperialist", -9.0 },
                         { "cp", "jingoist", -9.0 },
                         { "fs", "ghetto", -9.0 },
                         { "fs", "jingoist", -9.0 },
                         { "fs", "imperialist", -9.0 },
                         { "si", "marxist", -10.0 },
                         { "si", "pomo", -10.0 }
					   }
				     },
					 {
					   "indoctrinate",
					   {
                         { "fs", "pomo", 10.0 },
                         { "fs", "marxist", 10.0 },
                         { "fs", "geek", 10.0 },
                         { "fs", "ghetto", -10.0 },
                         { "fs", "jingoist", -10.0 },
                         { "fs", "imperialist", -10.0 },
                         { "si", "geek", 10.0 },
                         { "si", "imperialist", 10.0 },
                         { "si", "jingoist", 10.0 },
                         { "si", "marxist", -10.0 },
                         { "si", "pomo", -10.0 }
					   }
				     }
                   };
  total_funds_change = 0.0;
  for tactic_transition_set in (fund_transitions)
    {transition_tactic, transition_set} = tactic_transition_set;
    if( transition_tactic == tactic )
      for transition_mapping in (transition_set)
        {transition_space, transition_tongue, funds_change} = transition_mapping;
        if( space == transition_space && tongue == transition_tongue )
          total_funds_change = total_funds_change + funds_change;
        endif
      endfor
    endif
  endfor
return total_funds_change;
.
--------------------------------------------------------------------
$player:hp_check   this none this
permissions:       rxd

opponent = args[1];
if (this.hp <= this.wimpout && this.wimpout > 0)
  this:tell("You wimped out and ran away!");
  this.location:announce_all_but({opponent, this}, this.name, " just wimped out and ran away!");
  this.hp = this.wimpout;
  this.combat_losses = this.combat_losses + 1;
  if (this.phat_client && (this.wimpout_message != ""))
    for line in (this.wimpout_message)
      this:tell(line);
    endfor
  endif
  move(this, this.home);
  return;
endif
if (this.hp <= 0)
  this:death(opponent);
endif
.
--------------------------------------------------------------------
$player:hp_modify   this none this
permissions:        rxd

hp_modifier = args[1];
opponent = args[2]; 
this.hp = this.hp + hp_modifier;
this:hp_check(opponent);
.

The wimpout verb is what allows the player to set a floor for the hitpoints. This means that when the hitpoints reach the floor, the player runs away from combat and can no longer suffer damaged.

--------------------------------------------------------------------
$player:wimp*out   this at/to any
permissions:       rd

if ($object_utils:isa(player, $guest))
  player:tell("You can't set that property...");
  return;
endif
if (!$object_utils:isa(player, this.owner))
  player:tell("Don't you wish!  You can only change your own wimpout factor!");
  player:tell("No changes.");
  return 0;
endif
wimpout = $code_utils:tonum(iobjstr);
if (wimpout == player.wimpout)
  player:tell("Your wimpout factor was already set to ", wimpout, "!");
  player:tell("No changes.");
elseif (wimpout > player.hp || wimpout > player.hp_max)
  player:tell("Your wimpout factor must be less than your remaining hitpoints!");
  player:tell("No changes.");
elseif (wimpout <= player.hp && wimpout > 0)
  player:tell("Your new wimpout score is '", wimpout, "', out of ", player.hp_max, " total hitpoints.  You currently have ", player.hp, " hitpoints remaining.");
  player.wimpout = wimpout;
elseif (wimpout == 0)
  player:tell("Your new wimpout score is '", wimpout, "', out of ", player.hp_max, " total hitpoints.  You currently have ", player.hp, " hitpoints remaining.  Watch out, you could get slaughtered!");
  player.wimpout = wimpout;
endif
.

The next several verbs are responsible for doing level checks and adjustments. The first gets the current level, the next ('get_level_max') is called from within the stats screen when the player's current level is shown in relation to the maximum number of levels possible. The experience modification verb is called during experience point adjustment and looks to see if it's time to move the player to the next level. If so the player levels up, and the player's status is appropriately adjusted.

--------------------------------------------------------------------
$player:get_level   this none this
permissions:        rxd

current_level = 1;
for exp_required in (player.exp_table)
  if(this.exp > exp_required)
    current_level = current_level + 1;
  endif
endfor
return current_level;
.
--------------------------------------------------------------------
$player:get_level_max   this none this
permissions:            rxd

return length(player.exp_table) + 1;
.
--------------------------------------------------------------------
$player:exp_modify   this none this
permissions:         rxd

exp_gained = args[1];
current_exp = this.exp;
current_level = this.lev;
this.exp = this.exp + exp_gained;
if(this:get_level(this.exp) > this.lev)
  this:level_up();
endif
.
--------------------------------------------------------------------
$player:level_up   this none this
permissions:       rxd

player:tell("You leveled!  Congratulations...");
this.lev = this:get_level(this.exp);
this.hp_max = this.hp_max + random(7) + 15;
this.hp = this.hp_max;
player:update_status();
.
--------------------------------------------------------------------
$player:update_status   this none this
permissions:            rxd

status_descriptions = player.player_status_cp;
if(player.game_space == "fs")
  status_descriptions = player.player_status_fs;
elseif(player.game_space == "si")
  status_descriptions = player.player_status_si;
endif
player.status = status_descriptions[player.lev];
.

The death verb is what gets executed when a player loses all hitpoints because a wimpout factor was not set. Basically everything is lost and the player has to start from scratch. All the data ingested into the agent via the phat-client is wiped out as well!

--------------------------------------------------------------------
$player:death   this none this
permissions:    rxd

" Passes XML msg and resets player when hp = 0. ";
if ((dobj.phat_client != 0) && (dobj.death_message != ""))
  for line in (dobj.death_message)
    dobj:tell(line);
  endfor
endif
opponent = args[1];
dobj:tell("You got slaughtered!");
dobj.location:announce_all_but({opponent, dobj}, player.name, " just slaughtered ", dobj.name, "!  Not a pretty sight...");
dobj.lev = 1;
dobj.hp = 10;
dobj.wimpout = 0;
dobj.exp = 0;
dobj.str = 0;
dobj.str_bon = 0;
dobj.int = 0;
dobj.int_bon = 0;
dobj.wis = 0;
dobj.wis_bon = 0;
dobj.dex = 0;
dobj.dex_bon = 0;
dobj.chr = 0;
dobj.chr_bon = 0;
dobj.bullshit = 0;
dobj.compliment = 0;
dobj.confuse = 0;
dobj.debate = 0;
dobj.deceive = 0;
dobj.indoctrinate = 0;
dobj.insult = 0;
dobj.pacify = 0;
dobj.pulverize = 0;
dobj.geek = 0;
dobj.ghetto = 0;
dobj.imperialist = 0;
dobj.jingoist = 0;
dobj.marxist = 0;
dobj.pomo = 0;
dobj.practice_sessions_remaining = 3;
dobj.practice_sessions_attended = 0;
dobj.stats_done = 0;
dobj.game_space = "null";
dobj.combat_wins = 0;
dobj.combat_losses = 0;
dobj.stats_done = 0;
fork (2)
  dobj:tell("Your scores were all reset!");
endfork
fork (3)
  dobj:tell("Looks like you're starting from scratch!  Bummer...");
  move(dobj, dobj.home);
endfork
.

The following verb code adjusts the health description in relation to the current hitpoints. It starts by checking to see if the player has already generated some stats (which in our case happens by rolling the dice). If stats have not been generated, the player is flagged "helpless" since we know that certain conditions have not yet been met to make combat possible.

--------------------------------------------------------------------
$player:get_health_description   this none this
permissions:                     rxd

highest_multiplier = 0.0;
for health_state in (this.health_states)
  if (player.stats_done !=1 || player.hp == 0)
    current_description = "helpless";
  endif
  {multiplier, description} = health_state;
  if(multiplier > highest_multiplier)
    if(tofloat(this.hp) > tofloat(this.hp_max) * multiplier) 
      highest_multiplier = multiplier;
      current_description = description;
    endif
  endif
endfor
if ($object_utils:isa(player, $guest))
  current_description = "helpless";
  return current_description;
else
  return current_description;
endif
.

The next verb does funds adjustment when called from an object in-MOO or from a MAM process deemed worthy of increasing or decreasing player wealth. It checks to see if the player is legitimate and not just a guest. If so, it sends a MAM message, and then appropriately adjusts the player property.

--------------------------------------------------------------------
$player:adjust_funds   this none this
permissions:           rxd

" Pass a message to MAM for adjusting funds. ";
" Can be applied to any MOO object. ";
if ($object_utils:isa(player, $player) && !$object_utils:isa(player, $guest))
  {funds_transfer} = args;
  adjust_funds_message = {"‹mam-message type=\"mam.messaging.DoAdjustFundsMessage\"›",
			  "‹Source pref=\"true\" type=\"mam.internetworking.RoutingTag\" value=\"**myClient**\" /›",
			  "‹Destination pref=\"true\" type=\"mam.internetworking.RoutingTag\" value=\"local.ipAgent\" /›",
			  "‹FundsTransfer pref=\"true\" type=\"Double\" value=\"", $string_utils:from_value(funds_transfer), "\" /›", 
			  "‹/mam-message›"};
  if (this.phat_client)
    for line in (adjust_funds_message)
      this:tell(line);
    endfor
  endif
player.funds = player.funds + funds_transfer;
endif
.

Here we have the barter verb that provides the foundation for the in-MOO bartering system. It begins with a couple of checks to make sure that the initiator of a bartering session is a legitimate agent (which could include a special $bot type, detailed below), and not a guest. Then it sets some variables for handling how the agent you want to barter with displays the available objects for trading, and for verifying that those objects are indeed tradeable (a property flag defined on a special generic object we created called the '$tradeable_object', which has a few other unique properties listed below, and no special verbs). Then there is a bunch of stuff allowing the back-n-forth between agents, and eventual transfer of the object and adjustment of funds if a price is agreed upon.

--------------------------------------------------------------------
$player:barter   any none none
permissions:     rxd

if (parent(dobj) == $barter_bot)
  dobj:list();
  return 0;
endif
if ($object_utils:isa(player, $guest))
  player:tell("You can't barter until you properly register...");
  return 0;
endif
if (!$object_utils:isa(dobj, $player))
  player:tell("Impossible!  You need to barter with a legitimate agent...");
  return 0;
endif
if (dobj == this)
  player:tell("You barter with yourself for a while.");
  fork (3)
    player:tell("You start to look rather foolish...");
  endfork
  return 0;
endif
my_objects = {};
for available_object in (player:contents())
  if ($object_utils:isa(available_object, $tradeable_object))
    my_objects = listappend(my_objects, available_object);
  endif
endfor
others_objects = {};
for available_object in (dobj:contents())
  if ($object_utils:isa(available_object, $tradeable_object))
    others_objects = listappend(others_objects, available_object);
  endif
endfor
" TODO: abort if no tradeable objects ";
" Check to see if dobj has outstanding bids. ";
outstanding_bids = 0;
for available_object in (my_objects)
  if ($object_utils:isa(available_object, $tradeable_object))
    if ((available_object.last_bidder == dobj) && (available_object.currently_bid_on == 1))
      player:tell("There is an outstanding bid of ", $string_utils:format_funds(available_object.last_offer), " for ", available_object.aliases[1], ".");
      player:tell("Would you like to accept this offer?");
      answer = $command_utils:read("y/n");
      if (answer == "y")
        player:tell("Accepted...");
        " Validate that funds exist. ";
        if (dobj.funds >= available_object.last_offer)
          player:tell("All sales are final!");
          dobj:tell("You just acquired a brand new ", available_object.aliases[1], ".");
          player:adjust_funds(available_object.last_offer);
          dobj:adjust_funds(0.0 - available_object.last_offer);
          available_object.last_offer = 0.0;
          " Transfer ownership. ";
          $wiz_utils:set_owner(available_object, dobj);
          move(available_object, dobj);
        else
          player:tell("The transaction failed!  ", dobj.name, " did not have the needed funds...");
          dobj:tell("You don't have enough money to pay ", player.name, ".");
        endif
      else
        player:tell("Rejected!");
        dobj:tell("Your offer of ", $string_utils:format_funds(available_object.last_offer), " for ", available_object.aliases[1], " has been rejected.");
        available_object.currently_bid_on = 0;
      endif
      outstanding_bids = 1;
    endif
  endif
endfor
if (outstanding_bids == 1)
  return 0;
endif
" Let the player browse through the other player's stuff. ";
if (length(others_objects) > 0)
  player:tell_contents(others_objects);
else
  player:tell(dobj.name, " has nothing for you to acquire.");
  return 0;
endif
finished = 0;
while (!finished)
  player:tell("What would you like to acquire from ", dobj.name, "?");
  if (desired_str = $command_utils:read("item name"))
    " Check to see that desired is contained in player objects. ";
    desired_found = 0;
    for available_object in (others_objects)
      for available_object_alias in (available_object.aliases)
        if (available_object_alias == desired_str)
          desired = available_object;
          desired_found = 1;
        endif
      endfor
    endfor
    if (desired_found == 0)
      player:tell("Unfortunately, ", desired_str, " is not available.");
    else
      " Check to see that desired is a tradeable object. ";
      if ($object_utils:isa(desired, $tradeable_object))
        player:tell("You have selected \"", desired_str, "\".");
        goods = {};
        for m in (caller.contents)
          if ($object_utils:isa(m, $tradeable_object))
            goods = listappend(goods, m.name);
            if (desired_str == m.name)
              caller:tell("Unfortunately, you can't acquire another ", desired_str, "!");
              return;
            endif
          endif
        endfor
        finished = 1;
      else
        player:tell("Unfortunately, ", desired_str, " is not a tradeable object.");
      endif
    endif
  endif
endwhile
finished = 0;
while (!finished)
  player:tell("How much are you offering?");
  if (offer_str = $command_utils:read("amount"))
    try
      offer = tofloat(offer_str);
      if (offer > player.funds)
        player:tell("You can't afford ", $string_utils:format_funds(offer), "!  You only have ", $string_utils:format_funds(player.funds), " remaining...");
        return;
      endif
      player:tell("You offered: ", $string_utils:format_funds(offer), "...");
      dobj:tell(player.name, " has just offered you ", $string_utils:format_funds(offer), " for your \"", desired_str, "\".");
      desired.last_offer = offer;
      desired.last_bidder = player;
      desired.currently_bid_on = 1;
      finished = 1;
    except (E_INVARG)
      player:tell("Huh?  Try again...");
    endtry
  endif
endwhile
.

examples of $tradeable_object properties:
$tradeable_object.last_offer [float so we get dollars and cents]
$tradeable_object.last_bidder [object number of last bidder]
$tradeable_object.currently_bid_on [boolean set during barter]
$tradeable_object.msrp [float referencing manufacturer's suggested retail price]

PLAYER PROPERTIES

The property values are fairly self explanatory. Comments about what the property does and its value are set off by brackets. It is assumed you will be able to get the basic idea and extend as needed. Properties get set in several ways: 1) when called from within other verbs (most common); 2) when called from MAM (next most common); or 3) when modified by the player through some sort of interface. The trickiest part is setting the permission bits properly so that players can't change things they aren't supposed to with the @set command, while at the same time allowing other verbs to change things internal to MOO, or more importantly for our purposes, allowing properties to be changed from MAM. We have a stealthy way of doing this through calls passed from MAM to a special object hidden in MOO.

$player.phat_client [whether connected via phat_client - boolean]
$player.language [language being spoken - "string"]
$player.chip [whether id chip is present or not - boolean]
$player.stats_done [whether stats generated or not - boolean]
$player.reqprefs [whether summary info filled out or not - boolean]
$player.ali [alienation - float]
$player.ali_max [alienation max - float]
$player.amb  [ambition - float]
$player.amb_max [ambition max - float]
$player.anx [anxiety - float]
$player.anx_max [anxiety max - float]
$player.str [strength - integer] 
$player.str_max [strength max - integer]
$player.int [intelligence - integer]
$player.int_max [intelligence max - integer]
$player.wis [wisdom - integer]
$player.wis_max [wisdom max - integer]
$player.dex [dexterity - integer]
$player.dex_max [dexterity max - integer]
$player.chr [charisma - integer]
$player.chr_max [charisma max - integer]
$player.str_bon [strength bonus - integer]
$player.int_bon [intelligence bonus - integer]
$player.wis_bon [wisdom bonus - integer]
$player.dex_bon [dexterity bonus - integer]
$player.chr_bon [charisma bonus - integer]
$player.funds [amount of available money - integer]
$player.exp [experience points - integer]
$player.exp_table [experience point breakdown for levels - {"list"}]
$player.lev [level based on experience points - integer]
$player.status [ranking based on level - "string"]
$player.health_states [feeling states based on hitpoints - {"list"}]
$player.hp [currently available hitpoints - integer]
$player.hp_max [maximum hitpoints - integer]
$player.wimpout [hitpoint value determining when to run from combat - integer]
$player.wimpout_message [XML message passed to MAM on wimpout - {"string"}]
$player.death_message [XML message passed to MAM on death - {"string"}]
$player.combat_wins [battles won - integer]
$player.combat_losses [battles lost - integer]
$player.practice_sessions_attended [cumulative tactic or tongue practices - integer]
$player.practice_sessions_remaining [remaining tactic or tongue practices - integer]
$player.(tactic) [proficiency in combat tactic - percent]
$player.(tongue) [proficiency in combat tongue - percent]
$player.game_space [last space/$room adventured in - "string"]
$player.player_status_cp [status list used in specific quest space - {"list"}]
$player.player_status_fs [status list used in specific quest space - {"list"}]
$player.player_status_si [status list used in specific quest space - {"list"}]

examples of list properties:
$player.exp_table - {1500, 12000, 40500, etc.}
$player.health_states - {{0.000001,"panicked"},{0.2,"depressed"},{0.3,"edgy"},{"etc."}} 
$player.player_status_cp/fs/si = {"initiate","convert","adherent","etc."}

Top

MONSTER VERBS AND PROPERTIES

We also wanted to have "monster" objects that were NPCs, and would be confronted as obstacles during player quests. To do this we created a $generic_monster object (obj #155 in this case) that would be used as a parent for spawning new monsters for local customization if need be.

NOTE: To add an object to the generic objects database, you simply have to: 1) add the obj# to the #0.class_registry; and then 2) @prop #0.obj_name to #0.

MONSTER VERBS

The first verb responsible for tactics is quite simple, it does a reflection from the tactic executed by the player. The next several do hit point checks and modification as well as status adjustment.

--------------------------------------------------------------------
$generic_monster:tactic   this in/inside/into any
permissions:              rd

this:provoked_attack(iobjstr, 1, "tactic");
.
--------------------------------------------------------------------
$generic_monster:hp_check   this none this
permissions:                rxd

opponent = args[1];
if (this.hp <= this.wimpout && this.wimpout > 0)
  this:tell("You wimped out and ran away!");
  this.location:announce_all_but({opponent, this}, this.name, " just wimped out and ran away!");
  this.hp = this.wimpout;
  this.combat_losses = this.combat_losses + 1;
  if (this.phat_client && (this.wimpout_message != ""))
    for line in (this.wimpout_message)
      this:tell(line);
    endfor
  endif
  move(this, this.home);
  return;
endif
if (this.hp <= 0)
  this:death(opponent);
endif
.
--------------------------------------------------------------------
$generic_monster:hp_modify   this none this
permissions:                 rxd

hp_modifier = args[1];
opponent = args[2]; 
this.hp = this.hp + hp_modifier;
this:hp_check(opponent);
.
--------------------------------------------------------------------
$generic_monster:update_status   this none this
permissions:                     rxd

status_descriptions = this.player_status_cp;
if(this.game_space == "fs")
  status_descriptions = this.player_status_fs;
elseif(this.game_space == "si")
  status_descriptions = this.player_status_si;
endif
this.status = status_descriptions[this.lev];
.

If the player is victorious in combat, extra practice sessions are granted for improving tactics and tongues, the monster goes "home", and hitpoint adjustments get made.

--------------------------------------------------------------------
$generic_monster:death   this none this
permissions:             rxd

player.location:announce(player.name, " just trashed ", this.name, "!  Not a pretty sight...");
player.combat_wins = player.combat_wins + 1;
practice_sessions_granted = random(2);
if (practice_sessions_granted == 1)
  psg = "session";
else
  psg = "sessions";
endif
player.practice_sessions_remaining = player.practice_sessions_remaining + practice_sessions_granted;
fork (2)
  player:tell(this.name, " disappears...");
  player.location:announce(this.name, " disappears...");
  player:tell("You just earned ", practice_sessions_granted, " practice ", psg, "!  You now have ", player.practice_sessions_remaining, " remaining.");
  player:exp_modify(this.hp_max + random(player.str * 2));
  move(this, this.home);
endfork
.

The final two verbs are responsible for handling the attacks. The first is a provoked attack, meaning it is in response to an attack initiated by the player, the second is an unprovoked attack, meaning it is initiated by the monster. Both verbs follow the same basic structure: they check if game spaces match, determine which tactics and tongues will be used, figure out how to apply the hitpoint damage, and spit out some strings for the benefit of telling combatants and observers what's going on.

--------------------------------------------------------------------
$generic_monster:provoked_attack   this none this
permissions:                       rxd

player_obj = player;
monster_obj = this;
if(monster_obj.game_space != player_obj.game_space);
  player:tell("You can't seem to communicate with ", monster_obj.name, " no matter how hard you try!");
  return;
endif
{tongue_to_use, player_initiated, player_tactic} = args;
tactics = {"tactic1", "tactic2", "tactic3", "etc."};
tongues = {"tongue1", "tongue2", "tongue3", "etc."};
if(player_tactic == "")
  player_tactic=tactics[random(3)];
endif
known_tactics = {};
for tactic in (tactics)
  if(player_obj.(tactic) > 0)
    known_tactics = listappend(known_tactics, tactic);
  endif
endfor
if (!(player_tactic in known_tactics) && player_initiated)
  player:tell("You're not skilled in that tactic!");
  return;
endif
if(tongue_to_use == "")
  tongue_to_use = tongues[random(3)];
endif
known_tongues = {};
for tongue in (tongues)
  if(player_obj.(tongue) > 0)
    known_tongues = listappend(known_tongues, tongue);
  endif
endfor
if (!(tongue_to_use in known_tongues) && player_initiated)
  player:tell(monster_obj.name, " doesn't understand you...");
  return;
endif
if (player.hp <= player.wimpout)
  player:tell("You can't do that!  You're too much of a wimp...");
  return;
endif
total_funds_change = player:calc_cost(player.game_space, player_tactic, tongue_to_use);
player_damage = random(5);
if(player_damage == 1)
  player_damage_str = "hitpoint";
else
  player_damage_str = "hitpoints";
endif
monster_damage = 1 + (random(10 + player_obj.(player_tactic)) / 10);
if(monster_damage == 1)
  monster_damage_str = "hitpoint";
else
  monster_damage_str = "hitpoints";
endif
player_attack_successful = random(100) <= ((15 + player_obj.prop) + player_obj.(tongue_to_use));
if(player_obj.(tongue_to_use) <= 0)
  player_attack_successful = 0;
endif
defender_attack_successful = random(100) <= ((20) + monster_obj.(tongue_to_use));
if (monster_obj.hp <= monster_obj.wimpout && monster_obj.hp > 0)
  player:tell("You can't do that!  ", monster_obj.name, " is too much of a wimp...");
  return;
endif
if (player.hp <= player.wimpout && player.hp > 0)
  return;
endif
if(monster_obj.hp < 1)
  return;
endif
if (player_attack_successful && (!defender_attack_successful))
  player:tell("You just nailed ", monster_obj.name, " for ", monster_damage, " ", monster_damage_str, "!");
  monster_obj:hp_modify(-monster_damage, this);
  player_obj:adjust_funds(total_funds_change);
  fork(2)
    player:tell("Whoa!  Your funds just got adjusted...");
  endfork
elseif (defender_attack_successful && (!player_attack_successful))
  player:tell("Your verbal seduction backfired!  You got persuaded by ", monster_obj.name, " in ", tongue_to_use, "!");
  player:tell("You lost ", player_damage, " ", player_damage_str, "!");
  player.location:announce_all_but({monster_obj, player}, monster_obj.name, " made ", player.name, " see things from ", player.pp, " point of view!");
  player:hp_modify(-player_damage, monster_obj);
elseif (player_attack_successful && defender_attack_successful)
  player:tell("You sweet talked ", monster_obj.name, " in ", tongue_to_use, " for ", monster_damage, " ", monster_damage_str, "!");
  player:tell("But you also got sweet talked back by ", monster_obj.name, " for ", player_damage, " ", player_damage_str, "!");
  player.location:announce_all_but({monster_obj, player}, player.name, " and ", monster_obj.name, " are going on ad nauseum in ", tongue_to_use, "...");
  player:hp_modify(-player_damage, monster_obj);
  monster_obj:hp_modify(-monster_damage, player);
else
  player:tell("You struggle to speak with ", monster_obj.name, " for quite some time in ", tongue_to_use, ".  You feel rather weary...");
  player.location:announce_all_but({monster_obj, player}, player.name, " and ", monster_obj.name, " are struggling to speak in ", tongue_to_use, ".");
endif
.
--------------------------------------------------------------------
$generic_monster:unprovoked_attack   this none this
permissions:                         rxd

player_obj = player;
monster_obj = this;
if(monster_obj.game_space != player_obj.game_space);
  return;
endif
{tongue_to_use, player_initiated, player_tactic} = args;
tactics = {"tactic1", "tactic2", "tactic3", "etc."};
tongues = {"tongue1", "tongue2", "tongue3", "etc."};
if(player_tactic == "")
  player_tactic=tactics[random(3)];
endif
if(tongue_to_use == "")
  tongue_to_use = tongues[random(3)];
endif
known_tongues = {};
for tongue in (tongues)
  if(player_obj.(tongue) > 0)
    known_tongues = listappend(known_tongues, tongue);
  endif
endfor
player_damage = random(5);
if(player_damage == 1)
  player_damage_str = "hitpoint";
else
  player_damage_str = "hitpoints";
endif
attack_successful = random(100) <= ((15) + monster_obj.(tongue_to_use));
if (player.hp <= player.wimpout && player.hp > 0)
  return;
endif
if(monster_obj.hp < 1)
  return;
endif
if (attack_successful)
  player:tell("You got verbally assaulted by ", monster_obj.name, " in ", tongue_to_use, "!");
  player:tell("You lost ", player_damage, " ", player_damage_str, "!");
  player.location:announce_all_but({monster_obj, player}, monster_obj.name, " made ", player.name, " see things from ", player.pp, " point of view!");
  player:hp_modify(-player_damage, monster_obj);
endif
monster_obj.last_attack = time();
fork(7)
  if(monster_obj.location == player_obj.location)
    if(monster_obj.last_attack + 6 < time())
      monster_obj:unprovoked_attack(tongues[random(6)], 0, tactics[random(9)]);
    endif
  endif
endfork
.

MONSTER PROPERTIES

Most of the property values are scores generated on monster creation that get evaluated in relation to the current combatant's stats in effort to make the monster a worthy opponent. There could be any number of tactics, tongues, or property values that get set in this fashion. It is assumed you will be able to get the basic idea and extend as needed.

$generic_monster.(tactic) [proficiency in combat tactic - percent]
$generic_monster.(tongue) [proficiency in combat tongue - percent]
$generic_monster.str [strength - integer] 
$generic_monster.str_max [strength max - integer]
$generic_monster.int [intelligence - integer]
$generic_monster.int_max [intelligence max - integer]
$generic_monster.wis [wisdom - integer]
$generic_monster.wis_max [wisdom max - integer]
$generic_monster.dex [dexterity - integer]
$generic_monster.dex_max [dexterity max - integer]
$generic_monster.chr [charisma - integer]
$generic_monster.chr_max [charisma max - integer]
$generic_monster.str_bon [strength bonus - integer]
$generic_monster.int_bon [intelligence bonus - integer]
$generic_monster.wis_bon [wisdom bonus - integer]
$generic_monster.dex_bon [dexterity bonus - integer]
$generic_monster.chr_bon [charisma bonus - integer]
$generic_monster.funds [amount of available money - integer]
$generic_monster.exp [experience points - integer]
$generic_monster.exp_table [experience point breakdown for levels - {"list"}]
$generic_monster.lev [level based on experience points - integer]
$generic_monster.status [ranking based on level - "string"]
$generic_monster.hp [currently available hitpoints - integer]
$generic_monster.hp_max [maximum hitpoints - integer]
$generic_monster.wimpout [hitpoint value determining when to run from combat - integer]
$generic_monster.combat_wins [battles won - integer]
$generic_monster.combat_losses [battles lost - integer]
$generic_monster.last_attack [timer value set to determine next attack - integer]
$generic_monster.game_space [keyed to player's on entry - "string"]
$generic_monster.player_status_cp [status list used in specific quest space - {"list"}]
$generic_monster.player_status_fs [status list used in specific quest space - {"list"}]
$generic_monster.player_status_si [status list used in specific quest space - {"list"}]
$generic_monster.home [where the monster gets sent to on death - obj#]

examples of list properties:
$generic_monster.exp_table - {1500, 12000, 40500, etc.}
$generic_monster.player_status_cp/fs/si - {"initiate","convert","adherent","etc."}

Top

BARTER BOT VERBS AND PROPERTIES

BARTER BOT VERBS

Here we have examples of the verb code for the special $bot type we created called the '$barter_bot.' It starts with the 'buy' verb for when the $player agent tries to buy something from the $barter_bot, followed with the 'sell' verb for when the $player agent tries to sell something in order to gain more funds.

--------------------------------------------------------------------
$barter_bot:buy g*et   any out of/from inside/from this
permissions:           rd

if ((dobj = this:match_object(dobjstr)) == $nothing)
  player:tell("What do you want to take from ", this.name, "?");
else
  " Check from generic list first. ";
  buying_warez = 0;
  warez_list_idx = 0;
  for warez_item in (this.warez_list)
    warez_list_idx = warez_list_idx + 1;
    {warez_name, warez_thing, warez_count} = warez_item;
    if (dobjstr == warez_name)
      buying_warez = 1;
      if (warez_count == 0)
        player:tell("I'm out of that.  Try again later.");
        return;
      endif
      goods = {};
      for m in (player.contents)
		if ($object_utils:isa(m, $tradeable_object))
		  goods = listappend(goods, m.name);
		  if (warez_name == m.name)
			player:tell("You don't deserve another ", warez_name, "!");
			return;
		  endif
		endif
	  endfor
      player:tell("So, you want to acquire a ", warez_name, " eh?");
      negotiated_price = this:negotiate_sell(warez_name, warez_thing.msrp);
      if ((negotiated_price > 0.0) && (player.funds > negotiated_price))
        " Sucessful sale. ";
        this.warez_list[warez_list_idx] = {warez_name, warez_thing, warez_count - 1};
        player:tell("Sold for ", $string_utils:format_funds(negotiated_price), ".");
        player:adjust_funds(0.0 - negotiated_price);
        this.funds = this.funds + negotiated_price;
        warez_clone = player:_create(warez_thing);
        warez_clone.name = warez_name;
        warez_clone.aliases = {warez_name};
        $wiz_utils:set_owner(warez_clone, player);
        warez_clone:moveto(player);
      endif
    endif
  endfor
  if (buying_warez == 0)
    " Try buying from inventory. ";
    if ($command_utils:object_match_failed(dobj, dobjstr))
    elseif (!(dobj in this:contents()))
      player:tell("I have no ", dobj.name, "!");
    else
      if ($object_utils:isa(dobj, $tradeable_object))
        negotiated_price = this:negotiate_sell(dobj.name, dobj.msrp);
        if ((negotiated_price > 0.0) && (player.funds > negotiated_price))
          "Sucessful sale";
          player:tell("Sold for ", $string_utils:format_funds(negotiated_price), ".");
          player:adjust_funds(0.0 - negotiated_price);
          this.funds = this.funds + negotiated_price;
          $wiz_utils:set_owner(dobj, player);
          dobj:moveto(player);
          if (dobj.location == player)
            player.location:announce(player.name, " just acquired something from ", this.name, ".");
          endif
        endif
      else
        player:tell(dobj.name, " is not for sale!");
      endif
    endif
  endif
endif
.
--------------------------------------------------------------------
$barter_bot:sell   any at/to this
permissions:       rd

if ((this.location != player) && (this.location != player.location))
  player:tell("You can't get ", this.name, "'s attention.");
elseif (dobj == $nothing)
  player:tell("What do you want to sell ", prepstr, " ", this.name, "?");
elseif ($command_utils:object_match_failed(dobj, dobjstr))
elseif ((dobj.location != player) && (dobj.location != player.location))
  player:tell("You don't have ", dobj.name, ".");
else
  if ($object_utils:isa(dobj, $tradeable_object))
    negotiated_price = this:negotiate_buy(dobj.name, dobj.msrp);
    if ((negotiated_price >= 0.0) && (this.funds >= negotiated_price))
      "Sucessful purchase";
      player:tell("Purchased for ", $string_utils:format_funds(negotiated_price), ".");
      player:adjust_funds(negotiated_price);
      this.funds = this.funds - negotiated_price;
      player:tell("You'll never see that ", dobj.name," again!");
      player.location:announce(player.name, " just got rid of ", player.pp, " ", dobj.name,"...");
      player:moveto(dobj);
    else
      player:tell("Negotiation failed!  I only have ", $string_utils:format_funds(this.funds), " left...");
    endif
  else
    player:tell("I can't use that!!!");
  endif
endif
.

Here is the internally executed code for the $barter_bot:buy and :sell verbs that gets called when the $player executes the verbs above. It starts with the 'negotiate_sell' verb, and is followed by the 'negotiate_buy' verb.

--------------------------------------------------------------------
$barter_bot:negotiate_sell   this none this
permissions:                 rxd

{item_name, price} = args;
player:tell("Negotiating for ", item_name, " at a starting price of ", $string_utils:format_funds(price), ".");
ans = 0.0;
half_price = price / 2.0;
while (ans < price)
  player:tell("How much do you think it's worth?");
  ans_str = read(player);
  ans = tofloat(ans_str);
  if (ans > player.funds)
    player:tell("Hmmm...you don't appear to have the necessary funds...");
    return 0.0;
  elseif (ans < half_price)
    player:tell("Hah!  As if!");
    return 0.0;
  elseif (ans < price)
    player:tell("Now you're getting warmer...");
  endif
endwhile
player:tell("You got yourself a deal!");
return ans;
.
--------------------------------------------------------------------
$barter_bot:negotiate_buy   this none this
permissions:                rxd

{item_name, price} = args;
player:tell("Negotiating for ", item_name, " at a starting price of ", $string_utils:format_funds(price), ".");
double_price = price * 2.0;
ans = double_price;
while (ans > price)
  player:tell("How much do you want for it?");
  ans_str = read(player);
  ans = tofloat(ans_str);
  if (ans == 0.0)
    player:tell("OK, but I had no idea you were so stupid...ummm...I mean so generous!");
    return 0.0;
  endif
  if (ans > double_price)
    player:tell("Hah!  As if!");
    return 0.0;
  elseif (ans > price)
    player:tell("Now you're getting warmer...");
  endif
endwhile
return ans;
.

Finally, we have the verb code that generates a neatly formatted table listing of the $tradeable_objects the $barter_bot has available for agents to acquire.

--------------------------------------------------------------------
$barter_bot:list   this none this
permissions:       rxd

col_width = 68;
my_objects = {};
"add inventory items";
for available_object in (this:contents())
  if ($object_utils:isa(available_object, $tradeable_object))
    my_objects = listappend(my_objects, available_object.name);
  endif
endfor
" Add generic items that are sold. ";
" Show list to player. ";
row1 = {{"Items", "left", 34},{"Remaining", "right", 17},{"MSRP", "right", 17}};
player:tell("");
player:tell("Carrying:");
player:tell($string_utils:space(col_width, "-"));
player:tell($string_utils:flex_col_format(row1));
player:tell("");
for warez_item in (this.warez_list)
  {warez_name, warez_thing, warez_count} = warez_item;
  if (warez_count > 0)
    my_objects = listappend(my_objects, warez_name);
  endif
  row2 = {{warez_name, "left", 34},{$string_utils:from_value(warez_count), "right", 17},{$string_utils:format_funds(warez_thing.msrp), "right", 17}};
  player:tell($string_utils:flex_col_format(row2));
endfor
player:tell($string_utils:space(col_width, "-"));
player:tell($string_utils:format_two_col(this.name + " funds: " + $string_utils:format_funds(this.funds), player.name + " funds: " + $string_utils:format_funds(player.funds), col_width));
fork(2)
  player:tell("Make an offer on something: 'buy [item] from ", this.name, "'...");
endfork
.

BARTER BOT PROPERTIES

The $barter_bot only has a couple of properties to be concerned with:

$barter_bot.warez_list [colection list of items for bartering - {"item", obj#, amt}]
$barter_bot.funds [amount of available money - integer]

Top

SPACE VERBS AND PROPERTIES

SPACE VERBS

The check for encounter verb gets called when a space is entered (on the 'space:enterfunc'), at which point it proceeds to randomly select and create a "monster" type from a pre-defined list. It also checks if the room has been flagged as dangerous, as well as how many other monsters are in the same space. If it finds duplicates it removes them. It then gives the newly spawned monster a bunch of properties generated largely in relation to the player properties, and sticks it in the same space, at which point the battle begins whether provoked or not.

--------------------------------------------------------------------
$room:check_for_encounter   this none this
permissions:                rxd

{current_player, current_room} = args;
name = {"the monster1", "the monster2", "the monster3", "etc."};
if (current_room.room_type == "dangerous")
  monsters_max = current_room.monsters_max;
  monsters_here = {};
  for m in (current_room.contents)
    if ($object_utils:isa(m, $generic_monster))
      monsters_here = listappend(monsters_here, m);
    endif
  endfor
  if (length(monsters_here) < monsters_max)
    possible_monster_names = {};
    if ((current_player.game_space == "something") && (current_room.monsters_max > length(name)))
      current_room.monsters_max = length(name);
      possible_monster_names = name;
    elseif (current_player.game_space == "something")
      possible_monster_names = name;
    endif
    for monster_here in (monsters_here)
      for used_name in (monsters_here)
        possible_monster_names = setremove(possible_monster_names, "the " + used_name);
      endfor
    endfor
    if (length(possible_monster_names) > 0)
      nasty = current_player:_create($generic_monster);
      nasty.game_space = current_player.game_space;
      " This is where monster stats are set in relation to player stats ";
      nasty.prop1 = current_player.prop1;
      nasty.prop2 = current_player.prop3;
      nasty.prop3 = current_player.prop4;
      nasty.name = possible_monster_names[random(length(possible_monster_names))];
      nasty.aliases = {nasty.name[5..$]};
      current_level = current_player.lev;
      for exp_required in (nasty.exp_table)
        if (nasty.exp > exp_required)
          current_level = current_level + 1;
        endif
      endfor
      nasty.lev = current_level;
      nasty:update_status();
      nasty.hp_max = current_player.hp_max + random(current_player.wis);
      nasty.hp = (current_player.hp_max / 2 + random(current_player.int));
      move(nasty, current_room);
      monsters_here = listappend(monsters_here, nasty);
      fork (2)
        current_player:tell("You're being attacked by ", nasty.name, "!");
      endfork
    endif
  endif
  for nasty in (monsters_here)
    fork (3)
      if (nasty.location == player.location)
        nasty:unprovoked_attack("", 0, "");
      endif
    endfork
  endfor
endif
.

SPACE PROPERTIES

In order for the above to happen, a couple of properties are needed:

$room.room_type [set to either "normal" or "dangerous"]
$room.monsters_max [set to whatever max number you want in the space at once]

Top

UTILITY VERBS

The first utility verb we'll look at allows you to custom format a two column table layout. To see an example of what it looks like in use, check out the player stats screen in the combat system overview.

--------------------------------------------------------------------
$string_utils:format_two_col   this none this
permissions:                   rxd

" This formats two strings into a left & right justified col.     ";
" Syntax:  format(left_string, right_string, total_column_width); ";
col1_string = args[1];
col2_string = args[2];
total_width = args[3];
crop_col_left = total_width / 2;
crop_col_right = total_width - crop_col_left;
if (length(col1_string) + length(col2_string) > total_width && length(col1_string) > crop_col_left)
  col1_string = col1_string[1..crop_col_left];
endif
if (length(col1_string) + length(col2_string) > total_width && length(col2_string) > crop_col_right)
  col2_string = col2_string[1..crop_col_right];
endif
spaces_required = total_width - length(col1_string) - length(col2_string);
if (spaces_required > 0)
  col1_string = col1_string + $string_utils:space(spaces_required);
endif
formatted_string = col1_string + col2_string;
return formatted_string;
.

example:
col_width = 68;
player:tell($string_utils:format_two_col("something","something",col_width));

This verb allows you to custom format a multi-column table layout. To see an example of what it looks like in use, check out the player skills screen in the combat system overview.

--------------------------------------------------------------------
$string_utils:flex_col_format   this none this
permissions:                    rxd

" This formats a string based on list of {string, justification, width} ";
formatted_string = "";
colprefs = args[1];
for colpref in (colprefs)
  {s, just, width} = colpref;
  if (length(s) > width)
    s = s[1..width];
  endif
  if (just == "right")
    s = $string_utils:right(s, width);
  elseif (just == "center")
    s = $string_utils:center(s, width);
  else
    s = $string_utils:left(s, width);
  endif
  formatted_string = formatted_string + s;
endfor
return formatted_string;
.

example:
col_width = 68;
row1 = {{"something", "left", 8},{"something", "right", 5},{"something", "left", 10},{"", "", 13}};
player:tell($string_utils:flex_col_format(row1));

The next several verbs allow you to custom format number layouts into percentages, dollars and cents, and reduce values with multiple decimal places.

--------------------------------------------------------------------
$string_utils:to_percent   this none this
permissions:               rxd

" This returns a value followed by a % ";
n = args[1];
s = $string_utils:from_value(n);
s = s + "%";
return s;
.
--------------------------------------------------------------------
$string_utils:format_funds   this none this
permissions:                 rxd

" This returns a dollars and cents layout -- $0.00";
n = args[1];
whole_part = $math_utils:trunc(n / 100.0) * 100.0;
whole_part_str = $string_utils:from_value(whole_part);
num_whole_digits = length(whole_part_str) - 2;
whole_part_str = whole_part_str[1..num_whole_digits];
dec_part = $math_utils:trunc(n % 1.0) * 100.0;
dec_part_str = $string_utils:from_value(dec_part);
if (dec_part == 0.0)
  dec_part_str = "00";
elseif (dec_part < 10.0)
  dec_part_str = "0" + dec_part_str[1..1];
else
  dec_part_str = dec_part_str[1..2];
endif
s = "$" + whole_part_str + "." + dec_part_str;
return s;
.
--------------------------------------------------------------------
$math_utils:trunc   this none this
permissions:        rxd

" This truncs a value to two one decimal place -- 0.0 ";
n = args[1];
d = 100.0;
n = (d * n - d * n % 1.0) / d;
return n;
.

Top

Please send comments to nideffer@uci.edu.