To enter combat:

Entering battle:

There are a few ways of entering combat. One is to initiate an attack,
another is to be attacked. You can also join in combat already in progress,
which has differences as far as messaging goes but is calculated pretty much
the same way. The difference in the two ways of entering attack is the
initiatives involved. All battles are essentially between two persons,
although you can have other monsters lining up for their turns.

Each battle-capable living thing has a combat queue (add to /obj/living?).
Whenever something attacks you, that thing is placed on the end of the
combat queue. The character will fight whatever is at the beginning of the
combat queue until it is vanquished (no longer present in the room) or until
redirected to a different target (it is Ok for a target to be present in the
queue multiple times, since if it is killed then each additional time it
will be ignored, as though it were vanquished already. Simply removing
something from the combat queue should therefore generate no message -- the
actual kill generates a message).

When one party chooses to attack another, the aggressor places the defender
at the front of its combat queue, and the defender will place the aggressor
at the end of its combat queue. If neither were fighting previous to this,
then the effect is the same for both. Both aggressor and defender get an
immediate initiative roll.

obj/living (for future reference)
add_attacker() will add an opponent to the end of your combat queue (stored
in the narrator).
add_target() will add an opponent to the front of your combat queue.
recalculate() will scan inventory items to recalculate all bonuses.

Initiative:

An attacker will always have a slight advantage over the defender as far as
initiative is concerned. The defender must be much faster than the attacker
for this to be otherwise (it would take a greatly skilled swordsman to
strike one who is swinging a blow toward him before his opponent's blow
lands, for example). Initiative is calculated as a number 0-9, plus bonuses,
plus readyness, with a +3 being given to the attacker. There can, of course,
be initiative bonuses added on by the various items and charms kept by the
player. Initiative is only applicable at the beginning of combat; from that
point on, the readyness score is used. At the beginning of combat, readyness
is usually 0 (highest). Implement ambushes by dropping it (or by applying a
one of initiative negative)

obj/living:
roll_initiative() will return an initiative roll, including bonuses and
readyness, for the player.
query_initiative_bonus() will give the sum of all bonuses the player
possesses and inherent bonuses.

Items held by player:
query_initiative_bonus() will return the bonus given to the player's
initiative for that item. For something like a heavy piece of armor, it may
well be negative. Most items will return 0 for this.

Readyness:

Readyness describes how occupied the player is. Every time the player
performs an action or gets struck, his readyness is hurt. High readyness can
be critical to the outcome of a battle, since being repeatedly struck will
knock you more and more senseless, to the point that you pass out. If your
readyness drops below -25 you pass out, 

obj/living:
query_readyness() will return a number which gives the current readyness
penalty.
add_readyness(x) allows you to change the current readyness level by an
amount.
set_readyness(x) sets readyness to an absolute value.

Speed:

Also known as dexterity, speed is not speed of running (which doesn't really
apply since we have no movement points) but how quickly you can recover from
blows and how quickly you can move in general. Naturally this is a very
useful attribute to possess a great deal of. Speed will increase your
readyness. The higher your speed, the greater your readyness will recover in
battle. Each round of combat your readyness will increase by (speed / 4) +
<2-5> + speed_bonus. There is a built-in speed negative if you are carrying
too much (see: Strength).

obj/living:
roll_speed() will return the speed, including bonuses and random roll.
query_speed_bonus() will return the current bonuses to your speed, including
from inventory and inherent.
query_speed() returns intrinsic speed.
set_speed() sets intrinsic speed.

Items held by player:
query_speed_bonus() will return the speed bonus given by that item. Again
most items should return 0. Heavy armor is probably negative, probably only
magical items would be positive. Natural speed for a skilled warrior should
be quite high for humans, other races as appropriate.

Constitution:

Constitution is general toughness. All races have very similar
constitutions, and your HP for your body parts doesn't vary. A higher
constitution can give bonuses for how long you can cheat death without
passing out. The formula for this is (con - 10) / 3, using the standard
Dungeons and Dragons 3-18 range for skill scores.

obj/living:
query_constitution() will return the constitution including bonuses.
set_constitution() will set base consitution.
query_constitution_bonus() will return the sum of all constitution bonuses,
inventory and inherent.

Items held by player:
query_con_bonus() will return the constitution bonuse given by the item. A
spell effect like stoneskin would affect this, as would other magical items.
Cursed items would be a detriment to your constitution (negative value). As
usual, this normally returns 0.

Strength:

Strength gives forcefulness to your blows and also controls how much you are
able to carry. Blows are calculated as a damage total, which damage is then
given to the player to be absorbed by armour, etc. Carrying capacity is
calculated simply as (strength - 5) * 5, in kilograms. (Note: There is a
speed negative the more you carry. Over 10% of capacity is -1, over 30% is
-2, over 50% is -3, and over 90% is -4). A strength of 10 is typical at 25
kilograms. Pushing capacity is carrying capacity tripled. Bulk capacity is
determined by containers, not by strength.

obj/living
query_strength() gives strength, including bonuses.
set_strength() will set your base strength.
query_strength_bonus() returns the sum of all strength bonuses.

Items held by player:
query_str_bonus() will return the strength bonus for the item, much as
before. This would largely be used for magical effects or poisons.

Armour:

Armour (armor), of course, is a protective device which you use to prevent
damage to certain critical body parts. The strength of the armour is
important in determining how quickly the armour falls apart under enemy
blows. Typical strength values should be 0 to 5, with a rough scale being
0-cloth, 1-leathers, 2-wood or hide, 3-chain mail, 4-steel plate or dragon
hide, 5-adamant. A rough idea of how armour strength affects absorbency is
as follows:

(Ds = armour_strength - weapon_strength)
Ds   Effect Armour  Effect Player  Effect Weapon
<-2  Armour breaks  All damage     No damage
-2   absorbs 1/3    2/3 damage     No damage
-1   absorbs 2/3    1/3 damage     No damage
0    absorbs all    no damage      No damage
1    absorbs 2/3    no damage      1/4 damage
2    absorbs 1/3    no damage      1/2 damage
>2   no damage      no damage      absorbs all

Weapons of wood against armour of steel is therefore not much good. Armour
also has hit points. When armour is struck, it takes damage according to the
above table. Once the armour's hit points are exhausted, the armour breaks,
and it no longer has any effect for the player until it is repaired. Note
that armour which is far too soft for the weapon breaks immediately (like a
sword through a cloth robe).

obj/armour:
query_strength() will tell you the 'toughness' of this piece of armour.
set_strength() allows you to edit the strength of the armour
query_maxhp() tells you how strong the armour is when fully repaired.
set_maxhp(x) lets you set the maxhp.
query_hp() tells you how damaged the piece of armour is. Armour can be
patched up, of course. Unprofessional patching (binding, tying, etc.) will
only bring it up to 70% of maxhp; reforging it can bring it to 100%
add_hp(x) Will add x to the current hp, but will not let it outside of
0-maxhp.
set_hp(x) allows you to directly set a value for the hp.
query_protection() will return a list of protected body parts, as an array
of NUMBERS (use the race coding system to identify where the armour protects
exactly).
add_protection(x) will add a protected body part to the list.
remove_protection(x) will remove a protected body part from the list.
query_race() will tell you which race this piece of armour is appropriate
for. This may match either race or meta-race (like "humanoid")
set_race(n) will set which race or meta-race can use this armour.
* There also needs to be a set of functions for armour messages.

Weapon:

Weapons are anything that can be used to strike an opponent (and which the
MUD recognizes as such, of course). Weapons can be broken (and repaired) and
are allowed to give the standard bonuses. Any extension to weapons will have
to be coded. Weapons & armour may have things such as a requirement of what
it takes to forge them, but that's a problem for the blacksmith and not the
weapon. Weapons have strength and damage levels. The strength levels are the
same as above (0-5), and use the same table. Weapon damage is done with
dice, to allow more complex damaging, and uses XdY + Z.

There are two basic types of weapon. There are projectile weapons, like bows
and slings, and there are simple weapons which directly strike their target.
The projectile weapons will be dealt with later; at this point I'm only
dealing with standard melee weapons. The projectile weapons will probably be
done as separate code.

Hand-held melee weapons use a set of strokes. This will be kept moderately
simple, since I am completely unfamiliar with fencing. The types of strokes
are as follows:

left - stroke toward the left side of the target
right - stroke toward the right side of the target
stab - thrust with the point of the weapon
crush - downward stroke, to 'knock them over the head'

Weapons damage will very by stroke according to the type of weapon.

obj/weapon:
query_strength()
set_strength(x)
query_maxhp()
set_maxhp(x)
query_hp()
set_hp(x)
add_hp(x) These variables are the same as for armour.
query_length() gives you the range, or length, of the weapon.
set_length() changes the length of the weapon.
set_stroke_damage() sets the damage done by left or right strokes. 0 means
the weapon can't do it.
query_stroke_damage() queries damage done by left or right strokes.
set_crush_damage() sets the damage done by swinging the weapon down on the
opponent. 0 for impossible.
query_crush_damage() queries the crush damage
set_stab_damage() sets the damage by thrusting the weapon forward directly.
0 if it is impossible.
query_stab_damage() sets the damage done by stabbing the opponent with the
weapon.
roll_damage() returns a dice roll plus the bonus
set_num_dice() will set the number of dice rolled (X).
query_num_dice() will return the number of dice rolled.
set_dice_sides() will set the number of sides on the dice rolled (Y).
query_dice_sides() will return the number of sides on the dice rolled.
set_base_damage() will set Z, the damage added onto the dice roll.
query_base_damage() will return the damage added onto the dice roll.
* There also needs to be a set of functions for weapon messages.

Striking:

The basic element of combat is striking at your opponent. There are two
types of strike--the swing and the called hit. Called hits are far simpler
than swings, but I will deal with swings first.

[ NOTE: THIS NEEDS SIMPLIFICATION ]
A race has a list of body parts. Those body parts have attached location
flags. Here is a rough map of where the location flags refer to:

 __0__     0 neck & head     1 left shoulder     2 right shoulder
1  |  2    3 left arm        4 torso             5 right arm
3  4  5    6 left hand       7 right hand        8 belly/waist
6  |  7    9 pelvis          10 left leg         11 right leg
   8       12 left knee      13 right knee       14 left foot
 / 9 \     15 right foot
10   11
12   13
14   15

Some races may not even use all these flags, others may use these flags
multiple times, but the approximate physical locations should be consistent
throughout. Note that some body parts (such as feet) are only hittable using
called hit strokes.

A stroke "right" on a very tall (2x height+) opponent can hit: ({ 7, 8, 11,
13 })
A stroke "right" can hit: ({ 0, 2, 5, 7, 8 })
A stroke "right" will not work on short (<1/2 height) opponents.

A stroke "left" on a very tall (2x height+) opponent can hit: ({ 6, 8, 10,
12 })
A stroke "left" can hit: ({ 0, 1, 3, 6, 8 })
A stroke "left" will not work on short (<1/2 height) opponents.

A stroke "stab" on a very tall opponent can hit: ({ 6, 7, 8, 9, 10, 11, 12,
13 })
A stroke "stab" can hit ({ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 })
A stroke "stab" on a short opponent can hit ({ 0, 1, 2, 3, 4, 5 })

Simple projectile weapons are treated as stab damage.

A stroke "crush" will not work on a very tall opponent.
A stroke "crush" can hit ({0, 1, 2}) for both normal and very short
opponents.

Using these rules, the race object should build up lists of 'hittable' parts
for each stroke, according to that race. There are a total of eight of these
lists. This list also uses the body part size as a determinant; if it has
size 5, it will appear in the list 5 times. That way, you merely have to
pick a flat random number for any location in the list and it will give you
the body part struck.

for each list of locations (for the strikes)
   strike_count = 0;
   for each body part
      if that body part appears in the location list
         loop from strike_count to (strike_count+body part size) - 1
            add the body part index to the hit calculation list
         add body part size to strike_count

Now we have a quick and easy method for determining which body part was
struck by the weapon, using stroke and height. There is one more
complication, however: If a body part you wish to strike is no longer
functioning, just follow the dependency chain backwards until you find one
that is functioning. If you reach the end of the dependency chain and the
body part is non-functioning, then the opponent is dealt a critical blow
which kills immediately.

The next problem is how to do something similar for called hits. Called hits
use only one table, and have the following rules:

* You cannot call a hit to any body part located at 0, 1, 2, or 4 for very
tall opponents.
* Called hits will use the following strokes (where the index is the body
part called):
({crush, crush, crush, left, stab, right, left, right, stab, stab, left,
right, stab, stab, stab, stab})
for descriptions.
* Standard hit calculations are performed, but with a negative to hit. If
you do not miss, then the hit is processed as normal from that point on. A
miss is reprocessed as a normal strike and a -2 to readyness, but if the
normal strike comes out with the same body part that was called it is
counted as a miss.
* Projectile weapons may call a hit to any body part regardless of size, and
are always
stab damage.

Now, whether your blow was a called hit or a stroke, you have enough
information to process the blow. However, we still haven't checked for
anything on the defensive end, with the other player. An automatic 'dodge'
is built into the hit/miss calculations, and a dodge message should be given
if the hit/miss comes close (but still misses). However, it is also possible
to parry, using a mobile piece of protective armor (such as a wrist brace or
a shield) or using a sword or similar to knock the blow aside. Parries can
only be performed using worn equipment (equipment is removed if the body
part or higher dependency body parts using that equipment are damaged below
critical). You can determine what is currently worn by scanning through the
player's inventory and querying all equipment which is wielded and capable
of parrying.

Item worn:
query_parry_chance() must return a number between 0 and 100, which is the
unadjusted percentage chance of parrying. If 100 is returned, the parry
succeeds automatically until the equipment breaks (this could only happen
with a severely magical item!). Anything else is adjusted by subtracting
(12-speed) * 2; Length could theoretically be related to parry chance, but
that is up to the weapon code to determine. Returning a 0 of course makes
parry impossible with that item.

If a parry succeeds, then the damage is diverted to the item with which you
parry, halved (because the blow was taken deliberately so impact would be
reduced).

Now that you have finally calculated all the information necessary, you can
deal the damage itself. The damage is either given to the parrying weapon
(if a parry was successful) or to the armor/body part combination which you
hit. At this point you use the hardness tables given above to determine the
damage distribution, and generate a message accordingly. Left & Right
strokes give normal damage, crush gives damage times 5/4, stab gives damage
times 3/2.

The final thing that needs to be done is readyness. The attacker loses
readyness as follows:

       missed parried hit
left     -5     -2     -3
right    -5     -2     -3
stab     -8     -4     -5
crush    -6     -3     -4

The target loses readyness as follows:

blow missed       -0
blow parried      -1
armour hit        -2
armour & self hit -4
hit w/o armour    -7
body part lost    -10

Combat messages:

Combat phrases are put together lego-like. In order to maintain variety in
the phrasing, sentences are constructed as a sort of "fill-in-the-blanks"
system. Also, varying phrases can be used, with the blanks filled in
different ways. Example:

~Name #WEAPON_STRIKE_LEFT##STRIKE_RESULT#.

You slash the blob with your sword, causing it to shriek wetly.
You slice at the dragon, but you miss terribly.
Your slash hits the dragon. It roars in pain.

weapon strike messages:
slash #ENEMY_NAME# with ~poss #WEAPON_NAME#
slice at #ENEMY_NAME#

parry:


just-miss: