home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 May / Amiga_Games_Extra_CD_5-96.bin / spiele / publicdomain / amsrc / combat / fight.m < prev    next >
Text File  |  1996-02-19  |  46KB  |  1,720 lines

  1. /*
  2.  * Amiga MUD
  3.  *
  4.  * Copyright (c) 1996 by Chris Gray
  5.  */
  6.  
  7. /*
  8.  * fight.m - add player stats, fighting, etc. to the starter dungeon.
  9.  */
  10.  
  11. private tp_fight CreateTable().
  12. use tp_fight
  13.  
  14. /*
  15.  * NOTE: there is a direct dependency on r_arrivals, to send killed
  16.  *    players there.
  17.  */
  18.  
  19. define t_fight STEPS_PER_REGAINED_HIT_POINT 25.
  20. define t_fight BLUTOS_AFTER_DYING 25.
  21. define t_fight RANDOM_MONSTER_LIFE 100.
  22. define t_fight FOREVER_LIFE -1.
  23.  
  24. /* first, the new player properties. Note: these are all the effective values,
  25.    which may include modifications from armour, weapons, spells, etc. */
  26.  
  27. define t_fight p_pInited CreateBoolProp().    /* player has been set up */
  28. define t_fight p_pFightTerse CreateBoolProp().
  29. define t_fight p_pFightSuperTerse CreateBoolProp().
  30. define t_fight p_pHitMax CreateIntProp().    /* max hit points */
  31. define t_fight p_pHitNow CreateIntProp().    /* current hit points */
  32. define t_fight p_pHitCount CreateIntProp().    /* part count for healing */
  33. define t_fight p_pExperience CreateIntProp().    /* experience */
  34. define t_fight p_pLevel CreateIntProp().    /* current level */
  35. define t_fight p_pStrength CreateIntProp().    /* strength */
  36. define t_fight p_pSpeed CreateIntProp().    /* speed/dexterity */
  37. define t_fight p_pProtection CreateIntProp().    /* current armour class */
  38. define t_fight p_pWeapon CreateThingProp().    /* current weapon */
  39. define t_fight p_pShield CreateThingProp().    /* current shield */
  40. define t_fight p_pArmour CreateThingProp().    /* current armour */
  41. define t_fight p_pCurrentTarget CreateThingProp().    /* current opponent */
  42. define t_fight p_pDieNotifyList CreateActionListProp().
  43. define t_fight p_pNewMonster CreateThingProp(). /* temp during creation */
  44. define t_fight p_pWieldChecker CreateActionProp().     /* allow player check */
  45.  
  46. /* monster-only properties */
  47.  
  48. define t_fight p_mActions CreateStringProp().    /* single string of actions */
  49. define t_fight p_mActionIndexes CreateIntListProp().    /* indices of them */
  50. define t_fight p_mMovesUntilVanish CreateIntProp().    /* when it leaves */
  51. define t_fight p_mInitAction CreateActionProp().    /* startup action */
  52. define t_fight p_mMoveAction CreateActionProp().    /* step action */
  53. define t_fight p_mAfterMoveAction CreateActionProp().    /* on enter new room */
  54. define t_fight p_mFightAction CreateActionProp().    /* fight action */
  55. define t_fight p_mSpecialAction CreateActionProp().    /* action to do */
  56. define t_fight p_mKillAction CreateActionProp().    /* action on kill */
  57. define t_fight p_mCreateAction CreateActionProp().    /* something new */
  58. define t_fight p_mArriveAction CreateActionProp().    /* something comes */
  59. define t_fight p_mArrivedAction CreateActionProp().    /* check new loc */
  60. define t_fight p_mExpired CreateBoolProp().        /* it has "died" */
  61. define t_fight p_mBlocker CreateBoolProp().        /* it can block */
  62. define t_fight p_mHunting CreateBoolProp().        /* look for target */
  63.  
  64. /* properties for weapons/shield/armour */
  65.  
  66. define t_fight p_oHitBonus CreateIntProp().    /* hitpoints bonus */
  67. define t_fight p_oStrBonus CreateIntProp().    /* strength bonus */
  68. define t_fight p_oSpeedBonus CreateIntProp().    /* speed bonus */
  69. define t_fight p_oArmourProt CreateIntProp().    /* armour protection bonus */
  70. define t_fight p_oShieldProt CreateIntProp().    /* shield protection bonus */
  71. define t_fight p_oAccuracy CreateIntProp().    /* base accuracy level */
  72. define t_fight p_oDamage CreateIntProp().    /* base damage level */
  73. define t_fight p_oHealing CreateIntProp().    /* strength of heal */
  74. define t_fight p_oWieldChecker CreateActionProp().    /* when wield a weapon */
  75.  
  76. /* properties on rooms */
  77.  
  78. define t_fight p_rNoGenerateMonsters CreateBoolProp()./* no new baddies */
  79. define t_fight p_rMonsterList CreateThingListProp().  /* what might turn up */
  80. define t_fight p_rMonsterChance CreateIntListProp().    /* diff per person */
  81. define t_fight p_rMonsterTotal CreateIntProp(). /* total of the chances */
  82. define t_fight p_rKillAction CreateActionProp().      /* when monster killed */
  83.  
  84. /* this is an ancestor of all generic monsters */
  85.  
  86. define t_fight GenericMonster CreateThing(nil).
  87.  
  88. /*
  89.  * fighterDesc - return additional description for a fighter.
  90.  */
  91.  
  92. define tp_fight proc fighterDesc()string:
  93.     thing it, weapon, shield, armour;
  94.     string name, s, shieldName, armourName;
  95.  
  96.     it := It();
  97.     s := "";
  98.     name := Capitalize(CharacterNameS(it));
  99.     weapon := it@p_pWeapon;
  100.     shield := it@p_pShield;
  101.     if shield ~= nil then
  102.     shieldName := FormatName(shield@p_oName);
  103.     fi;
  104.     armour := it@p_pArmour;
  105.     if armour ~= nil then
  106.     armourName := FormatName(armour@p_oName);
  107.     fi;
  108.     if weapon = nil then
  109.     if shield = nil then
  110.         if armour ~= nil then
  111.         s := name + " is wearing " + armourName;
  112.         fi;
  113.     else
  114.         s := name + AAn(" is using", shieldName);
  115.         if armour ~= nil then
  116.         s := s + " and wearing " + armourName;
  117.         fi;
  118.     fi;
  119.     else
  120.     s := name + AAn(" is wielding", FormatName(weapon@p_oName));
  121.     if shield = nil then
  122.         if armour ~= nil then
  123.         s := s + " and wearing " + armourName;
  124.         fi;
  125.     else
  126.         if armour ~= nil then
  127.         s := s + AAn(", using", shieldName);
  128.         s := s + " and wearing " + armourName;
  129.         else
  130.         s := s + AAn(" and using", shieldName);
  131.         fi;
  132.     fi;
  133.     fi;
  134.     if s ~= "" then
  135.     s := " " + s + ".";
  136.     fi;
  137.     s
  138. corp;
  139.  
  140. /*
  141.  * AddFighterDesc - add the description about weapons, etc. to the list
  142.  *    of extra descriptions on the passed character, if it is not
  143.  *    already there.
  144.  */
  145.  
  146. define t_fight proc AddFighterDesc(thing theCharacter)void:
  147.     list action la;
  148.  
  149.     la := theCharacter@p_pDescMore;
  150.     if la = nil then
  151.     la := CreateActionList();
  152.     theCharacter@p_pDescMore := la;
  153.     fi;
  154.     if FindElement(la, fighterDesc) = -1 then
  155.     AddTail(la, fighterDesc);
  156.     fi;
  157. corp;
  158.  
  159. /*
  160.  * useItem - start using the given item.
  161.  */
  162.  
  163. define tp_fight proc useItem(thing th)void:
  164.     thing me;
  165.     list action la;
  166.  
  167.     me := Me();
  168.     me@p_pHitMax := me@p_pHitMax + th@p_oHitBonus;
  169.     me@p_pStrength := me@p_pStrength + th@p_oStrBonus;
  170.     me@p_pSpeed := me@p_pSpeed + th@p_oSpeedBonus;
  171.     me@p_pProtection := me@p_pProtection + th@p_oArmourProt + th@p_oShieldProt;
  172.     AddFighterDesc(me);
  173. corp;
  174.  
  175. /*
  176.  * unUseItem - stop using the given item.
  177.  */
  178.  
  179. define tp_fight proc unUseItem(thing th, theCharacter)void:
  180.  
  181.     theCharacter@p_pHitMax := theCharacter@p_pHitMax - th@p_oHitBonus;
  182.     theCharacter@p_pStrength := theCharacter@p_pStrength - th@p_oStrBonus;
  183.     theCharacter@p_pSpeed := theCharacter@p_pSpeed - th@p_oSpeedBonus;
  184.     theCharacter@p_pProtection := theCharacter@p_pProtection -
  185.     th@p_oArmourProt - th@p_oShieldProt;
  186. corp;
  187.  
  188. /* the wear/use/wield routines for armour/shields/weapons */
  189.  
  190. define tp_fight proc armourDrop(thing th)status:
  191.     thing theCharacter;
  192.  
  193.     theCharacter := th@p_oCarryer;
  194.     if theCharacter ~= nil then
  195.     unUseItem(th, theCharacter);
  196.     theCharacter -- p_pArmour;
  197.     th -- p_oUnGetChecker;
  198.     fi;
  199.     continue
  200. corp;
  201.  
  202. define tp_fight proc shieldDrop(thing th)status:
  203.     thing theCharacter;
  204.  
  205.     theCharacter := th@p_oCarryer;
  206.     if theCharacter ~= nil then
  207.     unUseItem(th, theCharacter);
  208.     theCharacter -- p_pShield;
  209.     th -- p_oUnGetChecker;
  210.     fi;
  211.     continue
  212. corp;
  213.  
  214. define tp_fight proc weaponDrop(thing th)status:
  215.     thing theCharacter;
  216.  
  217.     theCharacter := th@p_oCarryer;
  218.     if theCharacter ~= nil then
  219.     unUseItem(th, theCharacter);
  220.     theCharacter -- p_pWeapon;
  221.     th -- p_oUnGetChecker;
  222.     fi;
  223.     continue
  224. corp;
  225.  
  226. define tp_fight proc armourWear()status:
  227.     thing th, me, oldArmour;
  228.  
  229.     th := It();
  230.     me := Me();
  231.     if me@p_pWeapon = th then
  232.     ignore weaponDrop(th);
  233.     fi;
  234.     if me@p_pShield = th then
  235.     ignore shieldDrop(th);
  236.     fi;
  237.     oldArmour := me@p_pArmour;
  238.     if oldArmour ~= nil then
  239.     unUseItem(oldArmour, me);
  240.     oldArmour -- p_oUnGetChecker;
  241.     fi;
  242.     useItem(th);
  243.     th@p_oUnGetChecker := armourDrop;
  244.     me@p_pArmour := th;
  245.     Print("You are now wearing the " + FormatName(th@p_oName) + ".\n");
  246.     succeed
  247. corp;
  248.  
  249. define tp_fight proc shieldUse()status:
  250.     thing th, me, oldShield;
  251.  
  252.     th := It();
  253.     me := Me();
  254.     if me@p_pWeapon = th then
  255.     ignore weaponDrop(th);
  256.     fi;
  257.     if me@p_pArmour = th then
  258.     ignore armourDrop(th);
  259.     fi;
  260.     oldShield := me@p_pShield;
  261.     if oldShield ~= nil then
  262.     unUseItem(oldShield, me);
  263.     oldShield -- p_oUnGetChecker;
  264.     fi;
  265.     useItem(th);
  266.     th@p_oUnGetChecker := shieldDrop;
  267.     me@p_pShield := th;
  268.     Print("You are now using the " + FormatName(th@p_oName) + ".\n");
  269.     succeed
  270. corp;
  271.  
  272. define tp_fight proc weaponWield()status:
  273.     thing th, me, oldWeapon;
  274.  
  275.     th := It();
  276.     me := Me();
  277.     if me@p_pShield = th then
  278.     ignore shieldDrop(th);
  279.     fi;
  280.     if me@p_pArmour = th then
  281.     ignore armourDrop(th);
  282.     fi;
  283.     oldWeapon := me@p_pWeapon;
  284.     if oldWeapon ~= nil then
  285.     unUseItem(oldWeapon, me);
  286.     oldWeapon -- p_oUnGetChecker;
  287.     fi;
  288.     useItem(th);
  289.     th@p_oUnGetChecker := weaponDrop;
  290.     me@p_pWeapon := th;
  291.     Print("You are now wielding the " + FormatName(th@p_oName) + ".\n");
  292.     succeed
  293. corp;
  294.  
  295. /*
  296.  * SetupWeapon - set up a model object as a weapon.
  297.  */
  298.  
  299. define t_fight proc public SetupWeapon(thing theModel; int hitBonus, strBonus,
  300.     speedBonus, armourProt, shieldProt, accuracy, damage)void:
  301.  
  302.     if hitBonus ~= 0 then
  303.     theModel@p_oHitBonus := hitBonus;
  304.     fi;
  305.     if strBonus ~= 0 then
  306.     theModel@p_oStrBonus := strBonus;
  307.     fi;
  308.     if speedBonus ~= 0 then
  309.     theModel@p_oSpeedBonus := speedBonus;
  310.     fi;
  311.     if armourProt ~= 0 then
  312.     theModel@p_oArmourProt := armourProt;
  313.     theModel@p_oWearChecker := armourWear;
  314.     fi;
  315.     if shieldProt ~= 0 then
  316.     theModel@p_oShieldProt := shieldProt;
  317.     theModel@p_oUseChecker := shieldUse;
  318.     fi;
  319.     if accuracy ~= 0 then
  320.     theModel@p_oAccuracy := accuracy;
  321.     fi;
  322.     if damage ~= 0 then
  323.     theModel@p_oDamage := damage;
  324.     theModel@p_oWieldChecker := weaponWield;
  325.     fi;
  326. corp;
  327.  
  328. /*
  329.  * WeaponSell - companion routine - set up what is for sale.
  330.  */
  331.  
  332. define t_fight proc public WeaponSell(thing room; string name, desc;
  333.     int price, hitBonus, strBonus, speedBonus,
  334.         armourProt, shieldProt, accuracy, damage
  335.     )thing:
  336.     thing theModel;
  337.  
  338.     theModel := AddForSale(room, name, desc, price, nil);
  339.     SetupWeapon(theModel, hitBonus, strBonus, speedBonus, armourProt,
  340.         shieldProt, accuracy, damage);
  341.     theModel
  342. corp;
  343.  
  344. define tp_fight proc healBuy()status:
  345.     thing th, me;
  346.     int n, hits, delta;
  347.  
  348.     th := It();
  349.     me := Me();
  350.     n := th@p_oHealing;
  351.     n := n / 2 + Random(n / 2);
  352.     hits := me@p_pHitNow;
  353.     delta := me@p_pHitMax - hits;
  354.     if delta = 0 then
  355.     Print("You didn't need healing. Oh well - easy come, easy go!\n");
  356.     else
  357.     if n > delta then
  358.         n := delta;
  359.     fi;
  360.     me@p_pHitNow := hits + n;
  361.     if n = 1 then
  362.         Print("You are healed for one point.\n");
  363.     else
  364.         Print("You are healed for " + IntToString(n) + " points.\n");
  365.     fi;
  366.     fi;
  367.     if not me@p_pHidden then
  368.     OPrint(Capitalize(CharacterNameG(me)) + " makes a purchase.\n");
  369.     fi;
  370.     /* Tell 'StoreBuy' to destroy the copy, do not add it to our inventory,
  371.        and print no messages, but charge us for it. */
  372.     succeed
  373. corp;
  374.  
  375. /*
  376.  * HealSell - set up a healing "object".
  377.  */
  378.  
  379. define t_fight proc public HealSell(thing room; string name;
  380.     int cost, healing)void:
  381.     thing theModel;
  382.  
  383.     theModel := AddForSale(room, name, "", cost, healBuy);
  384.     theModel@p_oHealing := healing;
  385. corp;
  386.  
  387. /*
  388.  * showStats - show the statistics of the given thing (monster or player)
  389.  */
  390.  
  391. define tp_fight proc showStats(thing theCharacter; bool forceLong)void:
  392.     string name;
  393.     int ac;
  394.     thing th;
  395.  
  396.     name := theCharacter@p_pName;
  397.     if not theCharacter@p_pStandard then
  398.     name := FormatName(name);
  399.     fi;
  400.     Print(name);
  401.     Print(": Hit: ");
  402.     IPrint(theCharacter@p_pHitNow);
  403.     Print("/");
  404.     IPrint(theCharacter@p_pHitMax);
  405.     Print(" Exp: ");
  406.     IPrint(theCharacter@p_pExperience);
  407.     Print(" Lvl: ");
  408.     IPrint(theCharacter@p_pLevel);
  409.     Print(" Str: ");
  410.     IPrint(theCharacter@p_pStrength);
  411.     Print(" Spd: ");
  412.     IPrint(theCharacter@p_pSpeed);
  413.     Print(" AC: ");
  414.     ac := theCharacter@p_pProtection;
  415.     if ac > 0 then
  416.     Print("+");
  417.     fi;
  418.     IPrint(ac);
  419.     Print(" Bl: ");
  420.     IPrint(theCharacter@p_pMoney);
  421.     Print("\n");
  422.     if forceLong or not theCharacter@p_pFightTerse or theCharacter ~= Me() then
  423.     th := theCharacter@p_pWeapon;
  424.     if th ~= nil then
  425.         Print("Weapon: ");
  426.         Print(FormatName(th@p_oName));
  427.         Print("\n");
  428.     fi;
  429.     th := theCharacter@p_pShield;
  430.     if th ~= nil then
  431.         Print("Shield: ");
  432.         Print(FormatName(th@p_oName));
  433.         Print("\n");
  434.     fi;
  435.     th := theCharacter@p_pArmour;
  436.     if th ~= nil then
  437.         Print("Armour: ");
  438.         Print(FormatName(th@p_oName));
  439.         Print("\n");
  440.     fi;
  441.     fi;
  442. corp;
  443.  
  444. /**************************************************************************\
  445. *                                       *
  446. *    Code, etc. for monsters                        *
  447. *                                       *
  448. \**************************************************************************/
  449.  
  450. /* NOTE: each monster points at the player it is currently attacking, which
  451.    is usually the player which caused it to come into being, but can be
  452.    another, if that other attacked the monster. Players point at the monster
  453.    they most recently created or attacked, but those pointers are subject
  454.    to testing before they are used, since the monsters can leave. */
  455.  
  456. /* the fighting code */
  457.  
  458. /*
  459.  * AddExperience - do all needed on an experience gain or loss.
  460.  */
  461.  
  462. define t_fight proc public AddExperience(thing thePlayer; int n)void:
  463.     int level, value;
  464.  
  465.     n := n + thePlayer@p_pExperience;
  466.     thePlayer@p_pExperience := n;
  467.     level := 0;
  468.     value := 10;
  469.     while value <= n do
  470.     value := value * 10;
  471.     level := level + 1;
  472.     od;
  473.     value := thePlayer@p_pLevel;
  474.     if level ~= value then
  475.     thePlayer@p_pLevel := level;
  476.     if level > value then
  477.         SPrint(thePlayer, "You have gained a level!\n");
  478.         if Random(2) = 0 then
  479.         SPrint(thePlayer, "You gain speed.\n");
  480.         thePlayer@p_pSpeed := thePlayer@p_pSpeed + 1;
  481.         else
  482.         SPrint(thePlayer, "You gain strength.\n");
  483.         thePlayer@p_pStrength := thePlayer@p_pStrength + 1;
  484.         fi;
  485.         thePlayer@p_pHitMax := thePlayer@p_pHitMax + 8 + Random(5);
  486.     else
  487.         SPrint(thePlayer, "You have lost a level!\n");
  488.         if thePlayer@p_pSpeed > thePlayer@p_pStrength or
  489.         thePlayer@p_pSpeed = thePlayer@p_pStrength and Random(2) = 0
  490.         then
  491.         SPrint(thePlayer, "You lose speed.\n");
  492.         thePlayer@p_pSpeed := thePlayer@p_pSpeed - 1;
  493.         else
  494.         SPrint(thePlayer, "You lose strength.\n");
  495.         thePlayer@p_pStrength := thePlayer@p_pStrength - 1;
  496.         fi;
  497.         n := thePlayer@p_pHitMax - 8 - Random(5);
  498.         if n < 10 then
  499.         n := 10;
  500.         fi;
  501.         thePlayer@p_pHitMax := n;
  502.     fi;
  503.     fi;
  504. corp;
  505.  
  506. /*
  507.  * FindLoot - the player finds loot from a kill.
  508.  */
  509.  
  510. define t_fight proc public FindLoot(int amount)void:
  511.     thing theCharacter;
  512.  
  513.     if amount = 0 then
  514.     amount := 1;
  515.     fi;
  516.     if amount = 1 then
  517.     Print("You found one bluto of loot.\n");
  518.     else
  519.     Print("You found ");
  520.     IPrint(amount);
  521.     Print(" blutos of loot.\n");
  522.     fi;
  523.     theCharacter := Me();
  524.     theCharacter@p_pMoney := theCharacter@p_pMoney + amount;
  525. corp;
  526.  
  527. /*
  528.  * part of KillPlayer.
  529.  */
  530.  
  531. define tp_fight proc showAndLook()status:
  532.  
  533.     ShowIcon();
  534.     ignore ShowRoomToMe(true);
  535.     continue
  536. corp;
  537.  
  538. /*
  539.  * KillPlayer - general routine for killing a player. Send him back to the
  540.  *    arrivals room with no belongings and 25 blutos.
  541.  *    NOTE: if a player tells Packrat to go into the proving grounds, she
  542.  *    will be set up to fight, just as if she were a player. Then, if
  543.  *    someone kills her, this routine will be called by her!
  544.  */
  545.  
  546. define t_fight proc public KillPlayer(thing theVictim, theKiller)void:
  547.     list action la;
  548.     list thing lt;
  549.     int count, i;
  550.     action a;
  551.     thing here, item, killerLoc;
  552.     string victimName;
  553.  
  554.     la := theVictim@p_pDieNotifyList;
  555.     count := Count(la);
  556.     i := 0;
  557.     while i ~= count do
  558.     a := la[i];
  559.     if call(a, bool)(theVictim) then
  560.         DelElement(la, a);
  561.         count := count - 1;
  562.     else
  563.         i := i + 1;
  564.     fi;
  565.     od;
  566.     here := AgentLocation(theVictim);
  567.     lt := theVictim@p_pCarrying;
  568.     count := Count(lt);
  569.     i := 0;
  570.     while i < count do
  571.     item := lt[i];
  572.     if DoDrop(here, theVictim, item) = continue then
  573.         count := count - 1;
  574.     else
  575.         i := i + 1;
  576.     fi;
  577.     od;
  578.  
  579.     /* this makes for a non-obvious cheat, but I want it disabled anyway */
  580.     victimName := theVictim@p_pName;
  581.     if victimName ~= "Packrat" then
  582.     theVictim@p_pMoney := BLUTOS_AFTER_DYING;
  583.     fi;
  584.     theVictim@p_pHitNow := 10;
  585.     theVictim@p_pExperience := theVictim@p_pExperience / 2;
  586.     AddExperience(theVictim, 0);
  587.  
  588.     if theVictim@p_pHidden then
  589.     theVictim@p_pHidden := false;
  590.     ABPrint(here, theVictim, theVictim,
  591.         "Something fades into view. It is the corpse of " + victimName +
  592.         "!\n");
  593.     fi;
  594.     ABPrint(here, theVictim, theVictim,
  595.     "The corpse just fades away, but you catch a glimpse from the corner "
  596.     "of your eye of a pale cloud floating off.\n");
  597.     ignore ForceAction(theVictim, DoUnShowIcon);
  598.     SetAgentLocation(theVictim, r_arrivals);
  599.     SPrint(theVictim,
  600. "You feel an abrupt tearing of the local continuum as your essence "
  601. "departs from your ravaged body. Looking down, you see your corpse fade "
  602. "away to nothing, leaving little sign of your recent presence. A feeling "
  603. "of lightness pervades your being, and you begin to float away from the "
  604. "scene. The floating quickens and the world becomes a blur of motion. Soon "
  605. "you can again make out your surroundings.\n");
  606.     ABPrint(r_arrivals, theVictim, theVictim, "From the corner of your eye "
  607.     "you see a pale cloud float into view, which then solidifies into " +
  608.     victimName + ".\n");
  609.     ignore ForceAction(theVictim, showAndLook);
  610.  
  611.     /* did the killer kill off the only light source? */
  612.     if theKiller ~= theVictim and not LightAt(here) then
  613.     /* it may have gotten dark there */
  614.     killerLoc := AgentLocation(theKiller);
  615.     if killerLoc ~= here or not HasLight(theKiller) then
  616.         ForEachAgent(here, UnShowRoomFromAgent);
  617.         if killerLoc = here then
  618.         if theKiller = Me() then
  619.             UnShowRoomFromMe();
  620.         else
  621.             UnShowRoomFromAgent(theKiller);
  622.         fi;
  623.         fi;
  624.     fi;
  625.     fi;
  626. corp;
  627.  
  628. /*
  629.  * MonsterHitPlayer - have the monster hit the player.
  630.  *    Note: this can be executed by the player or by the monster!
  631.  *    Return 'true' if the player is still alive.
  632.  */
  633.  
  634. define t_fight proc public MonsterHitPlayer(
  635.     thing theMonster, thePlayer, where)bool:
  636.     string monsterName, playerName;
  637.     int n, hits;
  638.     bool alive;
  639.  
  640.     if thePlayer@p_pCurrentTarget = nil then
  641.     thePlayer@p_pCurrentTarget := theMonster;
  642.     fi; 
  643.     alive := true;
  644.     n := theMonster@p_oAccuracy;
  645.     if n ~= 0 and Here()@p_rMonsterList ~= nil then
  646.     /* Special case - if accuracy = 0, the monster never attacks.
  647.        Also, don't let monsters attack in a non-combat area, since
  648.        the player can't hit back! */
  649.     monsterName := Capitalize(CharacterNameG(theMonster));
  650.     playerName := thePlayer@p_pName;
  651.     if thePlayer@p_pHidden then
  652.         ABPrint(where, thePlayer, thePlayer,
  653.         monsterName + " attacks something.\n");
  654.     else
  655.         ABPrint(where, thePlayer, thePlayer,
  656.         monsterName + " attacks " + playerName + ".\n");
  657.     fi;
  658.     /* first, does the attack hit? Note: 5 is "standard". */
  659.     n := n * 3 + theMonster@p_pSpeed - 5;
  660.     if n ~= 0 then
  661.         n := Random(60 / n);
  662.     else
  663.         n := 1;
  664.     fi;
  665.     if n = 0 then
  666.         /* A hit. Get basic damage done by monster */
  667.         n := (theMonster@p_oDamage + 1) * 4;
  668.         n := (n / 2 + Random(n / 2 + 3)) / 4;
  669.         /* Modify by player armour class */
  670.         n := n + thePlayer@p_pProtection - 9;
  671.         /* and for excessive player strength */
  672.         n := n - (thePlayer@p_pStrength - 3) / 5;
  673.         if n > 0 then
  674.         hits := thePlayer@p_pHitNow;
  675.         hits := hits - n;
  676.         SPrint(thePlayer, monsterName + " hits for " +
  677.             if n = 1 then
  678.             "one point"
  679.             else
  680.             IntToString(n) + " points"
  681.             fi +
  682.             ". [" + IntToString(hits) + "/" +
  683.             IntToString(thePlayer@p_pHitMax) + "]\n");
  684.         if hits <= 0 then
  685.             SPrint(thePlayer, "You are killed!\n");
  686.             if not thePlayer@p_pHidden then
  687.             ABPrint(where, thePlayer, thePlayer,
  688.                 monsterName + " kills " + playerName + "!\n");
  689.             fi;
  690.             KillPlayer(thePlayer, theMonster);
  691.             alive := false;
  692.         else
  693.             thePlayer@p_pHitNow := hits;
  694.         fi;
  695.         else
  696.         if not thePlayer@p_pFightSuperTerse then
  697.             SPrint(thePlayer,
  698.             monsterName + " hits but does no damage.\n");
  699.         fi;
  700.         fi;
  701.     else
  702.         if not thePlayer@p_pFightTerse then
  703.         SPrint(thePlayer, monsterName + " attacks but misses.\n");
  704.         fi;
  705.     fi;
  706.     fi;
  707.     alive
  708. corp;
  709.  
  710. /*
  711.  * KillMonster - normal stuff for killing a monster.
  712.  */
  713.  
  714. define t_fight proc KillMonster(thing theMonster)void:
  715.     string monsterName;
  716.     thing theKiller, here;
  717.     list thing lt;
  718.     int i;
  719.  
  720.     theKiller := Me();
  721.     monsterName := CharacterNameS(theMonster);
  722.     Print(Capitalize(monsterName) + " is killed!\n");
  723.     if theKiller@p_pHidden then
  724.     OPrint(Capitalize(monsterName) + " is killed!\n");
  725.     else
  726.     OPrint(Capitalize(theKiller@p_pName) + " kills " + monsterName +"!\n");
  727.     fi;
  728.     theKiller -- p_pCurrentTarget;
  729.     lt := theMonster@p_pCarrying;
  730.     if lt ~= nil then
  731.     here := Here();
  732.     i := Count(lt);
  733.     while i ~= 0 do
  734.         i := i - 1;
  735.         ignore DoDrop(here, theMonster, lt[i]);
  736.     od;
  737.     fi;
  738.     if theMonster ~= theKiller then
  739.     i := theMonster@p_pMoney;
  740.     if i ~= 0 then
  741.         FindLoot((i + 1) / 2 + Random((i + 1) / 2));
  742.     fi;
  743.     fi;
  744.     ignore ForceAction(theMonster, DoUnShowIcon);
  745. corp;
  746.  
  747. /*
  748.  * PlayerHitMonster - the player is attacking the monster.
  749.  *    Return 'true' if the monster is still alive.
  750.  *    Note: this is only ever executed by the player.
  751.  */
  752.  
  753. define t_fight proc public PlayerHitMonster(thing thePlayer, theMonster)bool:
  754.     thing weapon, here, theModel;
  755.     string monsterName;
  756.     int n, hits;
  757.     list thing lt;
  758.     bool alive;
  759.     action a;
  760.  
  761.     alive := true;
  762.     theMonster@p_mMovesUntilVanish := RANDOM_MONSTER_LIFE;
  763.     thePlayer@p_pCurrentTarget := theMonster;
  764.     if theMonster@p_pCurrentTarget ~= thePlayer then
  765.     /* if multiple people are bashing on a monster, it randomly
  766.        targets one of them */
  767.     if theMonster@p_pCurrentTarget = nil or Random(2) = 0 then
  768.         theMonster@p_pCurrentTarget := thePlayer;
  769.     fi;
  770.     fi;
  771.     weapon := thePlayer@p_pWeapon;
  772.     monsterName := CharacterNameG(theMonster);
  773.     if thePlayer@p_pHidden then
  774.     OPrint(Capitalize(monsterName) + " is attacked.\n");
  775.     else
  776.     OPrint(Capitalize(thePlayer@p_pName) + " attacks " +
  777.         monsterName + ".\n");
  778.     fi;
  779.  
  780.     /* first, does the attack hit? Note: 5 is "standard". */
  781.     if weapon = nil then
  782.     n := 5;
  783.     else
  784.     n := weapon@p_oAccuracy;
  785.     fi;
  786.     n := (n + thePlayer@p_pLevel) * 3 + thePlayer@p_pSpeed - 5;
  787.     /* weaponless level 0 character has one chance in 4 of hitting */
  788.     /* weaponless level 1 - 4 character has one chance in 3 of hitting */
  789.     /* weaponless level 5 - 10 character has one chance in 2 of hitting */
  790.     /* weaponless level 11 and up character always hits */
  791.     if n ~= 0 then
  792.     n := Random(60 / n);
  793.     else
  794.     n := 1;
  795.     fi;
  796.     if n = 0 then
  797.     /* A hit! How much damage? */
  798.     if weapon = nil then
  799.         n := 3;
  800.     else
  801.         n := weapon@p_oDamage;
  802.     fi;
  803.     n := (n + thePlayer@p_pStrength) * 2;
  804.     n := (n / 2 + Random(n / 2 + 3)) / 4;
  805.     /* Modify by monster armour class */
  806.     n := n + theMonster@p_pProtection - 9;
  807.     if n > 0 then
  808.         Print("You hit for " + IntToString(n) +
  809.         if n = 1 then " point.\n" else " points.\n" fi);
  810.         hits := theMonster@p_pHitNow;
  811.         if n >= hits then
  812.         /* it is killed */
  813.         theModel := Parent(theMonster);
  814.         a := theMonster@p_mKillAction;
  815.         if a ~= nil then
  816.             alive := call(a, bool)(thePlayer, theMonster);
  817.         else
  818.             alive := false;
  819.             KillMonster(theMonster);
  820.             DestroyMachine(theMonster);
  821.             AddExperience(thePlayer, hits);
  822.         fi;
  823.         a := Here()@p_rKillAction;
  824.         if a ~= nil then
  825.             call(a, void)(thePlayer, theModel);
  826.         fi;
  827.         else
  828.         theMonster@p_pHitNow := hits - n;
  829.         AddExperience(thePlayer, n);
  830.         fi;
  831.     else
  832.         if not thePlayer@p_pFightSuperTerse then
  833.         Print("You hit " + monsterName + " but do no damage.\n");
  834.         fi;
  835.     fi;
  836.     else
  837.     if not thePlayer@p_pFightTerse then
  838.         Print("You attack " + monsterName + " but miss.\n");
  839.     fi;
  840.     fi;
  841.     alive
  842. corp;
  843.  
  844. /*
  845.  * PlayerHitPlayer - the current player is attacking the given player.
  846.  */
  847.  
  848. define t_fight proc public PlayerHitPlayer(thing theTarget)void:
  849.     thing here, theAttacker, weapon;
  850.     string attackerName, targetName, points;
  851.     int n, hits;
  852.  
  853.     here := Here();
  854.     theAttacker := Me();
  855.     attackerName := Capitalize(theAttacker@p_pName);
  856.     targetName := theTarget@p_pName;
  857.     if theTarget@p_pInited then
  858.     if not theAttacker@p_pHidden and not theTarget@p_pHidden then
  859.         ABPrint(here, theTarget, theAttacker,
  860.         attackerName + " attacks " + targetName + ".\n");
  861.     fi;
  862.     weapon := theAttacker@p_pWeapon;
  863.  
  864.     /* first, does the attack hit? Note: 5 is "standard". */
  865.     if weapon = nil then
  866.         n := 5;
  867.     else
  868.         n := weapon@p_oAccuracy;
  869.     fi;
  870.     n := n + theAttacker@p_pLevel;
  871.     if n ~= 0 then
  872.         n := (Random(200 / n) + 5) / 10;
  873.     else
  874.         n := 1;
  875.     fi;
  876.     if n = 0 then
  877.         /* A hit! How much damage? */
  878.         if weapon = nil then
  879.         n := 5;
  880.         else
  881.         n := weapon@p_oDamage;
  882.         fi;
  883.         n := n + theAttacker@p_pStrength / 5;
  884.         n := n / 2 + Random(n / 2) + 1;
  885.         /* Modify by target armour class */
  886.         n := n + theTarget@p_pProtection - 9;
  887.         /* and for excessive player strength */
  888.         n := n - theTarget@p_pStrength / 5;
  889.         if n > 0 then
  890.         hits := theTarget@p_pHitNow;
  891.         SPrint(theTarget, attackerName +
  892.             " attacks you and hits for " +
  893.             if n = 1 then
  894.             "one point"
  895.             else
  896.             IntToString(n) + " points"
  897.             fi + ". [" + IntToString(hits - n) + "/" +
  898.             IntToString(hits) + "]\n");
  899.         Print("You hit for " + if n = 1 then "one point" else
  900.             IntToString(n) + " points" fi + ".\n");
  901.         if n >= hits then
  902.             /* target is killed */
  903.             n := hits;
  904.             if not theAttacker@p_pHidden and not theTarget@p_pHidden
  905.             then
  906.             ABPrint(here, theTarget, theAttacker,
  907.                 attackerName + " kills " + targetName + "!\n");
  908.             fi;
  909.             SPrint(theTarget, attackerName + " has killed you!\n");
  910.             Print(Capitalize(targetName) + " is killed!\n");
  911.             if theTarget@p_pMoney ~= 0 then
  912.             FindLoot(theTarget@p_pMoney);
  913.             fi;
  914.             KillPlayer(theTarget, theAttacker);
  915.         else
  916.             theTarget@p_pHitNow := hits - n;
  917.         fi;
  918.         AddExperience(theAttacker, n);
  919.         else
  920.         if not theTarget@p_pFightSuperTerse then
  921.             SPrint(theTarget, attackerName +
  922.             " attacks you and hits, but does no damage.\n");
  923.         fi;
  924.         if not theAttacker@p_pFightSuperTerse then
  925.             Print("You hit " + targetName + " but do no damage.\n");
  926.         fi;
  927.         fi;
  928.     else
  929.         if not theTarget@p_pFightTerse then
  930.         SPrint(theTarget, attackerName + " attacks you but misses.\n");
  931.         fi;
  932.         if not theAttacker@p_pFightTerse then
  933.         Print("You attack " + targetName + " but miss.\n");
  934.         fi;
  935.     fi;
  936.     else
  937.     Print(Capitalize(targetName) + " is not set up for fighting.\n");
  938.     fi;
  939. corp;
  940.  
  941. /*
  942.  * InitMonsterModels - clear a room for a new opponent set.
  943.  */
  944.  
  945. define t_fight proc public InitMonsterModels(thing room; int initTotal)void:
  946.  
  947.     room@p_rMonsterList := CreateThingList();
  948.     room@p_rMonsterChance := CreateIntList();
  949.     room@p_rMonsterTotal := initTotal;
  950. corp;
  951.  
  952. /*
  953.  * AddPossibleMonster - add a monster to a room's opponent list.
  954.  */
  955.  
  956. define t_fight proc public AddPossibleMonster(thing room, theMonster;
  957.     int likelihood)void:
  958.  
  959.     AddTail(room@p_rMonsterList, theMonster);
  960.     AddTail(room@p_rMonsterChance, likelihood);
  961.     room@p_rMonsterTotal := room@p_rMonsterTotal + likelihood;
  962. corp;
  963.  
  964. define t_fight proc public MonsterMove(thing theMonster; int dir)bool:
  965.     false
  966. corp;
  967.  
  968. /*
  969.  * doRunAway - part of RunAwaySoon, and hence of RunAway.
  970.  */
  971.  
  972. define tp_fight proc doRunAway()void:
  973.     int dir;
  974.  
  975.     dir := Random(12);
  976.     if MonsterMove(Me(), dir) then
  977.     After(1, doRunAway);
  978.     fi;
  979. corp;
  980.  
  981. /*
  982.  * RunAwaySoon - the current monster panics and tries hard to leave this
  983.  *    location. This routine returns a 'status', so that it can be called
  984.  *    via 'ForceAction'.
  985.  */
  986.  
  987. define t_fight proc public RunAwaySoon()status:
  988.  
  989.     OPrint(Capitalize(CharacterNameG(Me())) + " panics!\n");
  990.     After(0, doRunAway);
  991.     continue
  992. corp;
  993.  
  994. /*
  995.  * RunAway - something has happened to cause a monster to want to run away.
  996.  *    Arrange for it to try to do so. This routine is not normally called
  997.  *    by the monster that wants to run away, so we use ForceAction and
  998.  *    After to make it work out right.
  999.  */
  1000.  
  1001. define t_fight proc public RunAway(thing theCoward)void:
  1002.  
  1003.     ignore ForceAction(theCoward, RunAwaySoon);
  1004. corp;
  1005.  
  1006. /*
  1007.  * MonsterAction - a monster does one of its random "actions".
  1008.  */
  1009.  
  1010. define t_fight proc public MonsterAction(thing theMonster)void:
  1011.     list int li;
  1012.     string monsterName, s;
  1013.     int pos, n;
  1014.  
  1015.     if LightAt(Here()) then
  1016.     li := theMonster@p_mActionIndexes;
  1017.     monsterName := Capitalize(CharacterNameG(theMonster));
  1018.     if li = nil then
  1019.         OPrint(monsterName + " runs around.\n");
  1020.     else
  1021.         s := theMonster@p_mActions;
  1022.         n := Random(Count(li));
  1023.         if n = 0 then
  1024.         pos := 0;
  1025.         else
  1026.         pos := li[n - 1];
  1027.         fi;
  1028.         n := li[n];
  1029.         s := SubString(s, pos, n - pos);
  1030.         OPrint(monsterName + " " + s + ".\n");
  1031.     fi;
  1032.     else
  1033.     OPrint("You hear a noise.\n");
  1034.     fi;
  1035. corp;
  1036.  
  1037. /*
  1038.  * checkMonsterArrival - a monster has moved somewhere - inform each other
  1039.  *    monster that cares.
  1040.  */
  1041.  
  1042. define tp_fight proc checkMonsterArrival(thing theTarget)void:
  1043.     action a;
  1044.  
  1045.     a := theTarget@p_mArriveAction;
  1046.     if a ~= nil then
  1047.     call(a, void)(theTarget, Me());
  1048.     fi;
  1049. corp;
  1050.  
  1051. /*
  1052.  * PickNewTarget - the monster has no target here. Randomly pick one.
  1053.  */
  1054.  
  1055. define tp_fight proc huntTarget(thing theTarget)void:
  1056.     thing me;
  1057.  
  1058.     me := Me();
  1059.     if me@p_mHunting and theTarget@p_pStandard then
  1060.     me -- p_mHunting;
  1061.     me@p_pCurrentTarget := theTarget;
  1062.     ignore MonsterHitPlayer(me, theTarget, Here());
  1063.     fi;
  1064. corp;
  1065.  
  1066. define t_fight proc public PickNewTarget()void:
  1067.  
  1068.     Me()@p_mHunting := true;
  1069.     ForEachAgent(Here(), huntTarget);
  1070. corp;
  1071.  
  1072. /*
  1073.  * MonsterMove - try to move in given direction and fight attacker.
  1074.  */
  1075.  
  1076. replace MonsterMove(thing theMonster; int dir)bool:
  1077.     thing theTarget, here;
  1078.     action a;
  1079.  
  1080.     if TryToMove(dir) then
  1081.     MachineMove(dir);
  1082.     a := theMonster@p_mArrivedAction;
  1083.     if a ~= nil then
  1084.         call(a, void)();
  1085.     fi;
  1086.     here := Here();
  1087.     ForEachAgent(here, checkMonsterArrival);
  1088.     theTarget := theMonster@p_pCurrentTarget;
  1089.     /* if our current target is here - attack! */
  1090.     if theTarget ~= nil and AgentLocation(theTarget) = here then
  1091.         if LightAt(here) then
  1092.         ignore MonsterHitPlayer(theMonster, theTarget, here);
  1093.         else
  1094.         OPrint("You hear a noise.\n");
  1095.         fi;
  1096.     elif Random(5) = 0 then
  1097.         PickNewTarget();
  1098.     fi;
  1099.     true
  1100.     else
  1101.     false
  1102.     fi
  1103. corp;
  1104.  
  1105. /*
  1106.  * DoMonsterMove - attack target, move in random direction or do "action"
  1107.  */
  1108.  
  1109. define t_fight proc public DoMonsterMove(thing theMonster)void:
  1110.     thing theTarget, here;
  1111.     action a;
  1112.  
  1113.     here := Here();
  1114.     if Random(3) = 0 then
  1115.     theTarget := theMonster@p_pCurrentTarget;
  1116.     if theTarget ~= nil and AgentLocation(theTarget) = here then
  1117.         if LightAt(here) then
  1118.         ignore MonsterHitPlayer(theMonster, theTarget, here);
  1119.         else
  1120.         OPrint("You hear a noise.\n");
  1121.         fi;
  1122.     elif Random(5) = 0 then
  1123.         PickNewTarget();
  1124.     fi;
  1125.     else
  1126.     if MonsterMove(theMonster, Random(12)) then
  1127.         a := theMonster@p_mAfterMoveAction;
  1128.         if a ~= nil then
  1129.         call(a, void)();
  1130.         fi;
  1131.     else
  1132.         MonsterAction(theMonster);
  1133.     fi;
  1134.     fi;
  1135. corp;
  1136.  
  1137. /*
  1138.  * MonsterStillMoving - see if monster leaves, and if so, nuke it.
  1139.  */
  1140.  
  1141. define t_fight proc public MonsterStillMoving(thing theMonster;
  1142.     action dieAction)bool:
  1143.     thing obj, theTarget;
  1144.     int n, count;
  1145.     list thing lt;
  1146.  
  1147.     n := theMonster@p_mMovesUntilVanish;
  1148.     if n = FOREVER_LIFE then
  1149.     true
  1150.     elif n <= 0 then
  1151.     theMonster@p_mExpired := true;
  1152.     if dieAction ~= nil then
  1153.         call(dieAction, void)(theMonster);
  1154.     fi;
  1155.     if LightAt(Here()) then
  1156.         OPrint(Capitalize(CharacterNameG(theMonster)) + " leaves.\n");
  1157.         ForEachAgent(Here(), UnShowIconOnce);
  1158.     fi;
  1159.     theTarget := theMonster@p_pCurrentTarget;
  1160.     if theTarget ~= nil then
  1161.         if theTarget@p_pCurrentTarget = theMonster then
  1162.         theTarget -- p_pCurrentTarget;
  1163.         fi;
  1164.     fi;
  1165.     lt := theMonster@p_pCarrying;
  1166.     if lt ~= nil then
  1167.         count := Count(lt);
  1168.         while count ~= 0 do
  1169.         count := count - 1;
  1170.         obj := lt[count];
  1171.         ZapObject(obj);
  1172.         DelElement(lt, obj);
  1173.         od;
  1174.     fi;
  1175.     /* After this point, the monster has no properties */
  1176.     DestroyMachine(theMonster);
  1177.     /* At this point, the monster does not exist, so this routine must
  1178.        do nothing but exit. In particular, OPrint is a no-no. */
  1179.     /* NOTE: because of a possible reference from a player who attacked
  1180.        the monster and therefore has a reference to its thing as that
  1181.        player's p_pCurrentTarget, the thing, although emptied out,
  1182.        will not go away until all such players delete their reference. */
  1183.     false
  1184.     else
  1185.     theMonster@p_mMovesUntilVanish := n - 1;
  1186.     true
  1187.     fi
  1188. corp;
  1189.  
  1190. /*
  1191.  * MonsterReschedule - setup to do his step again.
  1192.  */
  1193.  
  1194. define t_fight proc public MonsterReschedule(thing theMonster)void:
  1195.     int n;
  1196.  
  1197.     n := theMonster@p_pSpeed;
  1198.     if n = 0 then
  1199.     n := 100;
  1200.     else
  1201.     n := 100 / n;
  1202.     fi;
  1203.     After(Random(n) + 1, theMonster@p_mMoveAction);
  1204. corp;
  1205.  
  1206. /*
  1207.  * DummyMonsterInit - init routine that just does the Reschedule.
  1208.  */
  1209.  
  1210. define t_fight proc public DummyMonsterInit()void:
  1211.  
  1212.     MonsterReschedule(Me());
  1213. corp;
  1214.  
  1215. /*
  1216.  * MonsterInit - initialization of a random moving monster.
  1217.  */
  1218.  
  1219. define t_fight proc public MonsterInit()void:
  1220.     thing theMonster;
  1221.  
  1222.     theMonster := Me();
  1223.     ignore SetMachineActive(theMonster, theMonster@p_mMoveAction);
  1224.     if LightAt(Here()) then
  1225.     OPrint(Capitalize(CharacterNameG(theMonster)) + " has appeared.\n");
  1226.     ForEachAgent(Here(), ShowIconOnce);
  1227.     fi;
  1228.     MonsterReschedule(theMonster);
  1229. corp;
  1230.  
  1231. /*
  1232.  * MonsterNoNo - standard monster response to many actions.
  1233.  */
  1234.  
  1235. define t_fight proc public MonsterNoNo()status:
  1236.  
  1237.     Print("You don't want to do that!\n");
  1238.     fail
  1239. corp;
  1240.  
  1241. GenericMonster@p_pName := "MONSTER;GENERIC".
  1242. GenericMonster@p_oTouchChecker := MonsterNoNo.
  1243. GenericMonster@p_oSmellChecker := MonsterNoNo.
  1244. GenericMonster@p_oPushChecker := MonsterNoNo.
  1245. GenericMonster@p_oPullChecker := MonsterNoNo.
  1246. GenericMonster@p_oTurnChecker := MonsterNoNo.
  1247. GenericMonster@p_oLiftChecker := MonsterNoNo.
  1248. GenericMonster@p_oLowerChecker := MonsterNoNo.
  1249. GenericMonster@p_oEatChecker := MonsterNoNo.
  1250. SetThingStatus(GenericMonster, ts_readonly).
  1251.  
  1252. /*
  1253.  * CreateMonsterModel - create a new model monster.
  1254.  */
  1255.  
  1256. define t_fight proc public CreateMonsterModel(string name, desc;
  1257.     action initAction, moveAction;
  1258.     int hits, speed, protection, accuracy, damage, money)thing:
  1259.     thing theModel;
  1260.  
  1261.     theModel := CreateThing(GenericMonster);
  1262.     SetThingStatus(theModel, ts_readonly);
  1263.     theModel@p_pName := name;
  1264.     if desc ~= "" then
  1265.     theModel@p_pDesc := desc;
  1266.     else
  1267.     theModel@p_pDesc := "This is an ordinary " + FormatName(name) + ".";
  1268.     fi;
  1269.     if initAction ~= nil then
  1270.     theModel@p_mInitAction := initAction;
  1271.     fi;
  1272.     theModel@p_mMoveAction := moveAction;
  1273.     theModel@p_pHitMax := hits;
  1274.     theModel@p_pSpeed := speed;
  1275.     theModel@p_pProtection := protection;
  1276.     theModel@p_oAccuracy := accuracy;
  1277.     theModel@p_oDamage := damage;
  1278.     if money ~= 0 then
  1279.     theModel@p_pMoney := money;
  1280.     fi;
  1281.     /* Note that we put this extra description on the model, rather than
  1282.        on the individual monsters. This saves time and space, but can mess
  1283.        up if some code ends up wanting to add description stuff to
  1284.        monsters created this way. */
  1285.     AddFighterDesc(theModel);
  1286.     theModel
  1287. corp;
  1288.  
  1289. /*
  1290.  * AddModelAction - add a 'moves around' action to a model monster.
  1291.  *    Note: keep in mind that these messages are seen by both the player
  1292.  *    involved and by any bystanders. (So don't use 'you'!).
  1293.  */
  1294.  
  1295. define t_fight proc public AddModelAction(thing theModel; string a)void:
  1296.     list int li;
  1297.     int len;
  1298.  
  1299.     li := theModel@p_mActionIndexes;
  1300.     if li = nil then
  1301.     li := CreateIntList();
  1302.     theModel@p_mActionIndexes := li;
  1303.     theModel@p_mActions := a;
  1304.     else
  1305.     a := theModel@p_mActions + a;
  1306.     theModel@p_mActions := a;
  1307.     fi;
  1308.     AddTail(li, Length(a));
  1309. corp;
  1310.  
  1311. /*
  1312.  * checkMonsterCreation - a new monster has arrived. Tell all who care.
  1313.  */
  1314.  
  1315. define tp_fight proc checkMonsterCreation(thing theOther)void:
  1316.     action a;
  1317.  
  1318.     a := theOther@p_mCreateAction;
  1319.     if a ~= nil then
  1320.     call(a, void)(theOther, Me()@p_pNewMonster);
  1321.     fi;
  1322. corp;
  1323.  
  1324. /*
  1325.  * CreateMonster - create a new monster with random hitpoints.
  1326.  *    Note: this routine assumes it is called by a player.
  1327.  */
  1328.  
  1329. define t_fight proc public CreateMonster(thing theCreator,theModel,where)thing:
  1330.     thing theMonster;
  1331.     int hits;
  1332.  
  1333.     theMonster := CreateThing(theModel);
  1334.     SetupMachine(theMonster);
  1335.     hits := theModel@p_pHitMax;
  1336.     hits := hits / 2 + Random(hits / 2) + 1;
  1337.     theMonster@p_pHitMax := hits;
  1338.     theMonster@p_pHitNow := hits;
  1339.     theMonster@p_mMovesUntilVanish := RANDOM_MONSTER_LIFE;
  1340.     theMonster@p_pCurrentTarget := theCreator;
  1341.     theMonster@p_mExpired := false;
  1342.     CreateMachine(theModel@p_pName, theMonster, where, theModel@p_mInitAction);
  1343.     theCreator@p_pCurrentTarget := theMonster;
  1344.     Me()@p_pNewMonster := theMonster;
  1345.     ForEachAgent(where, checkMonsterCreation);
  1346.     Me() -- p_pNewMonster;
  1347.     theMonster
  1348. corp;
  1349.  
  1350. /*
  1351.  * CreateSpecificMonster - create a copy of a model monster.
  1352.  */
  1353.  
  1354. define t_fight proc public CreateSpecificMonster(
  1355.     thing theCreator, theModel, where)thing:
  1356.     thing theMonster;
  1357.  
  1358.     theMonster := CreateThing(theModel);
  1359.     SetupMachine(theMonster);
  1360.     theMonster@p_pHitNow := theModel@p_pHitMax;
  1361.     theMonster@p_mMovesUntilVanish := RANDOM_MONSTER_LIFE;
  1362.     theMonster@p_pCurrentTarget := theCreator;
  1363.     CreateMachine(theModel@p_pName, theMonster, where, theModel@p_mInitAction);
  1364.     theCreator@p_pCurrentTarget := theMonster;
  1365.     Me()@p_pNewMonster := theMonster;
  1366.     ForEachAgent(where, checkMonsterCreation);
  1367.     Me() -- p_pNewMonster;
  1368.     theMonster
  1369. corp;
  1370.  
  1371. /*
  1372.  * PickNewMonster - pick a new monster for the player to fight.
  1373.  *    Return the thing of the generated monster, if any.
  1374.  */
  1375.  
  1376. define t_fight proc public PickNewMonster(thing theCreator, where)thing:
  1377.     list thing lt;
  1378.     list int li;
  1379.     int choice, count;
  1380.  
  1381.     lt := where@p_rMonsterList;
  1382.     if lt ~= nil then
  1383.     li := where@p_rMonsterChance;
  1384.     choice := Random(where@p_rMonsterTotal);
  1385.     count := Count(li);
  1386.     while count ~= 0 and choice >= li[count - 1] do
  1387.         choice := choice - li[count - 1];
  1388.         count := count - 1;
  1389.     od;
  1390.     if count ~= 0 then
  1391.         CreateMonster(theCreator, lt[count - 1], where)
  1392.     else
  1393.         nil
  1394.     fi
  1395.     else
  1396.     nil
  1397.     fi
  1398. corp;
  1399.  
  1400. /* a 'checker' for healing and generating random monsters */
  1401.  
  1402. define tp_fight proc monsterEnterCheck()status:
  1403.     thing me, theMonster;
  1404.     int i;
  1405.  
  1406.     me := Me();
  1407.     i := me@p_pHitCount;
  1408.     i := i + 1;
  1409.     if i = STEPS_PER_REGAINED_HIT_POINT then
  1410.     i := me@p_pHitNow;
  1411.     if i < me@p_pHitMax then
  1412.         me@p_pHitNow := i + 1;
  1413.     fi;
  1414.     i := 0;
  1415.     fi;
  1416.     me@p_pHitCount := i;
  1417.     theMonster := me@p_pCurrentTarget;
  1418.     if theMonster = nil and not Here()@p_rNoGenerateMonsters then
  1419.     theMonster := PickNewMonster(me, Here());
  1420.     fi;
  1421.     continue
  1422. corp;
  1423.  
  1424. /* a 'checker' to let monsters try to hit fleeing players */
  1425.  
  1426. define tp_fight proc monsterLeaveCheck(int dir)status:
  1427.     thing me, theMonster, here;
  1428.     int n;
  1429.     status result;
  1430.  
  1431.     result := continue;
  1432.     me := Me();
  1433.     theMonster := me@p_pCurrentTarget;
  1434.     if theMonster ~= nil then
  1435.     here := Here();
  1436.     if AgentLocation(theMonster) = here then
  1437.         /* the monster I was fighting is still here with me */
  1438.         n := Random(3);
  1439.         if n = 0 and theMonster@p_mBlocker then
  1440.         if LightAt(here) then
  1441.             Print(Capitalize(CharacterNameG(theMonster)) +
  1442.             " blocks you.\n");
  1443.         else
  1444.             Print("Something blocks you.\n");
  1445.         fi;
  1446.         result := fail;
  1447.         else
  1448.         if n = 1 and LightAt(here) then
  1449.             if not MonsterHitPlayer(theMonster, me, here) then
  1450.             /* player has died - fail the move */
  1451.             result := fail;
  1452.             fi;
  1453.         fi;
  1454.         me -- p_pCurrentTarget;
  1455.         fi;
  1456.     else
  1457.         me -- p_pCurrentTarget;
  1458.     fi;
  1459.     fi;
  1460.     result
  1461. corp;
  1462.  
  1463. /*
  1464.  * StandardAttack - standard code for player attack something.
  1465.  */
  1466.  
  1467. define t_fight proc public StandardAttack(
  1468.     thing thePlayer, theTarget, where)void:
  1469.     int mSpeed;
  1470.  
  1471.     if theTarget@p_pStandard then
  1472.     /* a monster set up like a player, e.g. Packrat after being forced
  1473.        into the proving grounds */
  1474.     if theTarget@p_pInited then
  1475.         PlayerHitPlayer(theTarget);
  1476.     else
  1477.         Print(Capitalize(CharacterNameS(theTarget)) +
  1478.           " is not set up for fighting.\n");
  1479.     fi;
  1480.     else
  1481.     mSpeed := theTarget@p_pSpeed;
  1482.     if Random(thePlayer@p_pSpeed + mSpeed) < mSpeed then
  1483.         /* monster hits first */
  1484.         if MonsterHitPlayer(theTarget, thePlayer, where) then
  1485.         ignore PlayerHitMonster(thePlayer, theTarget);
  1486.         fi;
  1487.     else
  1488.         /* player hits first */
  1489.         if PlayerHitMonster(thePlayer, theTarget) then
  1490.         ignore MonsterHitPlayer(theTarget, thePlayer, where);
  1491.         fi;
  1492.     fi;
  1493.     fi;
  1494. corp;
  1495.  
  1496. /*
  1497.  * InitFighter - set up a player for fighting. Can be called repeatedly,
  1498.  *    since it checks for duplicates.
  1499.  */
  1500.  
  1501. define t_fight proc public InitFighter(thing fighter)void:
  1502.  
  1503.     if not fighter@p_pInited then
  1504.     fighter@p_pInited := true;
  1505.     fighter@p_pHitMax := 10;
  1506.     fighter@p_pHitNow := 10;
  1507.     fighter@p_pHitCount := 0;
  1508.     fighter@p_pExperience := 0;
  1509.     fighter@p_pStrength := 5;    /* "standard" strength */
  1510.     fighter@p_pSpeed := 5;        /* "standard" speed */
  1511.     fighter@p_pProtection := 9;    /* armour class +9 */
  1512.     fighter@p_pLevel := 0;
  1513.     fighter@p_pDieNotifyList := CreateActionList();
  1514.     AddPlayerEnterChecker(fighter, monsterEnterCheck, false);
  1515.     AddPlayerLeaveChecker(fighter, monsterLeaveCheck, false);
  1516.     SPrint(fighter, "\nCombat initialized!\n\n");
  1517.     fi;
  1518. corp;
  1519.  
  1520. /*
  1521.  * LeaveFighting - utility to call when leaving a fighting area. It returns
  1522.  *    'fail' for monsters, to keep them in, 'succeed' for players. Is
  1523.  *    suitable for a regular move checker.
  1524.  */
  1525.  
  1526. define t_fight proc public LeaveFighting()status:
  1527.     thing fighter;
  1528.  
  1529.     fighter := Me();
  1530.     if fighter@p_pInited then
  1531.     /* a player or someone like Packrat */
  1532.     continue
  1533.     else
  1534.     /* must be a monster - don't let them out */
  1535.     fail
  1536.     fi
  1537. corp;
  1538.  
  1539. /*
  1540.  * HealingBuy - routine to buy healing at a healer.
  1541.  */
  1542.  
  1543. define t_fight proc public HealingBuy(string what)bool:
  1544.     thing me;
  1545.  
  1546.     me := Me();
  1547.     if me@p_pHitNow >= me@p_pHitMax then
  1548.     Print("The proprietor looks at you for a moment, and then frowns "
  1549.         "in puzzlement and says: 'But you are in perfect health!'\n");
  1550.     false
  1551.     else
  1552.     StoreBuy(what)
  1553.     fi
  1554. corp;
  1555.  
  1556. /************************\
  1557. *             *
  1558. * The new commands added *
  1559. *             *
  1560. \************************/
  1561.  
  1562. define tp_fight proc v_status(string who)bool:
  1563.     thing me;
  1564.  
  1565.     me := Me();
  1566.     if who == "full" then
  1567.     if me@p_pInited then
  1568.         showStats(me, true);
  1569.         true
  1570.     else
  1571.         Print("There is no combat status to show you.\n");
  1572.         false
  1573.     fi
  1574.     elif who ~= "" and IsWizard() then
  1575.     me := FindAgent(who);
  1576.     if me ~= nil then
  1577.         showStats(me, true);
  1578.         true
  1579.     else
  1580.         Print("There is no " + who + " here.\n");
  1581.         false
  1582.     fi
  1583.     else
  1584.     if me@p_pInited then
  1585.         showStats(me, false);
  1586.         true
  1587.     else
  1588.         Print("There is no combat status to show you.\n");
  1589.         false
  1590.     fi
  1591.     fi
  1592. corp;
  1593.  
  1594. define tp_fight proc v_wield(string what)bool:
  1595.     VerbCarry("wield", nil, p_oWieldChecker, p_pWieldChecker,
  1596.           "You cannot wield", what)
  1597. corp;
  1598.  
  1599. /*
  1600.  * v_hit - the basic attacking verb.
  1601.  */
  1602.  
  1603. define tp_fight proc v_hit(string what)bool:
  1604.     thing me, theTarget, here;
  1605.     action a;
  1606.  
  1607.     me := Me();
  1608.     here := Here();
  1609.     if what = "" then
  1610.     Print("You must specify who or what you want to attack.\n");
  1611.     false
  1612.     elif not me@p_pInited then
  1613.     Print("You are not yet set up for fighting.\n");
  1614.     false
  1615.     elif not CanSee(here, me) then
  1616.     Print("You can't see to fight.\n");
  1617.     false
  1618.     elif here@p_rMonsterList = nil then
  1619.     Print("This is a non-combat area.\n");
  1620.     false
  1621.     else
  1622.     theTarget := FindAgent(what);
  1623.     if theTarget = nil then
  1624.         if FindName(here@p_rContents, p_oName, what) ~= fail or
  1625.         FindName(me@p_pCarrying, p_oName, what) ~= fail or
  1626.         MatchName(here@p_rScenery, what) ~= -1
  1627.         then
  1628.         Print("You can only attack players or monsters.\n");
  1629.         else
  1630.         Print(IsAre("There", "no", FormatName(what), "here.\n"));
  1631.         fi;
  1632.         false
  1633.     else
  1634.         if ThingCharacter(theTarget) ~= nil then
  1635.         if theTarget = me then
  1636.             Print("Masochism is not implemented.\n");
  1637.         else
  1638.             /* attacking another player */
  1639.             PlayerHitPlayer(theTarget);
  1640.         fi;
  1641.         else
  1642.         /* attacking a monster */
  1643.         a := theTarget@p_mFightAction;
  1644.         if a ~= nil then
  1645.             call(a, void)(theTarget);
  1646.         else
  1647.             StandardAttack(me, theTarget, here);
  1648.         fi;
  1649.         fi;
  1650.         true
  1651.     fi
  1652.     fi
  1653. corp;
  1654.  
  1655. /*
  1656.  * verbs to allow briefer output during combat.
  1657.  */
  1658.  
  1659. define tp_fight proc v_fightterse()bool:
  1660.     thing me;
  1661.  
  1662.     me := Me();
  1663.     if me@p_pFightTerse and not me@p_pFightSuperTerse then
  1664.     Print("You are already in fightterse mode.\n");
  1665.     false
  1666.     else
  1667.     me -- p_pFightSuperTerse;
  1668.     me@p_pFightTerse := true;
  1669.     Print("fightterse mode set.\n");
  1670.     true
  1671.     fi
  1672. corp;
  1673.  
  1674. define tp_fight proc v_fightsuperterse()bool:
  1675.     thing me;
  1676.  
  1677.     me := Me();
  1678.     if me@p_pFightSuperTerse then
  1679.     Print("You are already in fightsuperterse mode.\n");
  1680.     false
  1681.     else
  1682.     me@p_pFightTerse := true;
  1683.     me@p_pFightSuperTerse := true;
  1684.     Print("fightsuperterse mode set.\n");
  1685.     true
  1686.     fi
  1687. corp;
  1688.  
  1689. define tp_fight proc v_fightverbose()bool:
  1690.     thing me;
  1691.  
  1692.     me := Me();
  1693.     if not me@p_pFightTerse then
  1694.     Print("You are already in fightverbose mode.\n");
  1695.     false
  1696.     else
  1697.     me -- p_pFightTerse;
  1698.     me -- p_pFightSuperTerse;
  1699.     Print("fightverbose mode set.\n");
  1700.     true
  1701.     fi
  1702. corp;
  1703.  
  1704. Verb1(G, "status", 0, v_status).
  1705. Synonym(G, "status", "st").
  1706. Verb1(G, "wield", 0, v_wield).
  1707. Verb1(G, "hit", 0, v_hit).
  1708. Synonym(G, "hit", "fight").
  1709. Synonym(G, "hit", "attack").
  1710. Synonym(G, "hit", "kill").
  1711. Synonym(G, "hit", "h").
  1712. Synonym(G, "hit", "k").
  1713. Verb0(G, "fightterse", 0, v_fightterse).
  1714. Synonym(G, "fightterse", "fightbrief").
  1715. Verb0(G, "fightsuperterse", 0, v_fightsuperterse).
  1716. Synonym(G, "fightsuperterse", "fightsuperbrief").
  1717. Verb0(G, "fightverbose", 0, v_fightverbose).
  1718.  
  1719. unuse tp_fight
  1720.