/* Do not remove the headers from this file! see /USAGE for more info. */
inherit __DIR__ "limbs";
class diagnosis
{
int bruises;
int scratches;
int cuts;
int wounds;
int injuries;
int gashes;
int mutilations;
}
string number_word(int); /* M_GRAMMAR */
string number_of(int, string);
/* Value is an array of numbers, indicating the size of the wounds. Note
* that the sum of wounds[limb] should ALWAYS be health[limb]->max_health
* minus health[limb]->health.
*
* Limbs in perfect condition and disabled limbs are not in the mapping.
*/
private mapping wounds = ([]);
void set_max_limb_health(string limb, int x)
{
::set_max_limb_health(limb, x);
map_delete(wounds, limb);
if(!sizeof(wounds))
remove_call_out("health_periodic");
}
void disable_limb(string limb)
{
::disable_limb(limb);
map_delete(wounds, limb);
if(!sizeof(wounds))
remove_call_out("health_periodic");
}
varargs int hurt_us(int x, string limb)
{
int ret;
if(!limb)
limb = query_random_limb();
ret = ::hurt_us(x, limb);
/* ret is the amount of damage actually done; remember to check if we
disabled the limb */
if(ret && query_health(limb))
{
if(!sizeof(wounds))
call_out("health_periodic", 15);
if(wounds[limb])
{
int n = sizeof(wounds[limb]);
/* To prevent huge lists of wounds when chinking away with
small amounts of damage on a really large monster, we coalesce
wounds after about 5-10 of them */
if(n > random(5) + 5)
wounds[limb][random(n)] += ret;
else
wounds[limb] += ({ ret });
}
else
wounds[limb] = ({ ret });
}
return ret;
}
void heal_limb(string limb, int x)
{
int i = 0;
array tmp = wounds[limb];
int n = sizeof(tmp);
int left = x;
::heal_limb(limb, x);
if(n == 0)
return;
/* start healing with the first wound, and continue till we run out of
'healing' */
while(i < n)
{
if(left >= tmp[i])
{
left -= tmp[i];
i++;
}
else
{
tmp[i] -= left;
break;
}
}
if(i < n)
wounds[limb] = tmp[i..];
else map_delete(wounds, limb);
/* i == number of wounds healed; n == total number before */
if(i)
{
if(n == 1)
simple_action("The wound on $p " + limb + " has healed.\n");
else if(i == n)
simple_action("All of the wounds on $p " + limb + " have healed.\n");
else
simple_action(capitalize(number_word(i)) + " of the " + number_word(n) + " wounds on $p " + limb + " have healed.\n");
}
}
void heal_all()
{
wounds = ([]);
::heal_all();
}
void kill_us()
{
remove_call_out("health_periodic");
::kill_us();
}
// Unfortunately this can't be done lazily since it has side effects
void health_periodic()
{
string limb;
array w;
int i, n, severity, delta;
/* pick a random wound, and make it better or worse by up to 20%;
* the function is centered on zero, but remember that normal healing
* is causing us to heal at ~1 point per heal_rate seconds, so the
* overall effect is biased towards healing.
*/
if(sizeof(wounds) == 0)
return;
limb = choice(keys(wounds));
w = wounds[limb];
n = sizeof(w);
i = random(n);
severity = w[i];
delta = fuzzy_divide(random(2*severity+1) - severity, 5);
call_out("health_periodic", 15);
if(delta == 0)
return;
if(delta < 0)
{
if(n == 1)
simple_action("The wound on $p " + limb + " looks a bit worse.");
else
simple_action("One of the wounds on $p " + limb + " looks a bit worse.");
wounds[limb][i] += ::hurt_us(-delta, limb);
}
else
{
if(n == 1)
simple_action("The wound on $p " + limb + " looks a bit better.");
else
simple_action("One of the wounds on $p " + limb + " looks a bit better.");
wounds[limb][i] -= delta;
::heal_limb(limb, delta);
}
}
void reincarnate()
{
foreach(string s in query_vital_limbs())
map_delete(wounds, s);
::reincarnate();
health_periodic();
}
void health_status()
{
foreach (string limb in keys(wounds))
{
int h = query_health(limb);
int mh = query_max_health(limb);
if(wounds[limb])
printf("%-10s %3i (%3i%%) [%s]\n", limb, h, h * 100 / mh,
implode(map(wounds[limb], (: $1 + "" :)), ","));
}
}
string array query_all_wounds()
{
return keys(wounds);
}
array query_wounds(string limb)
{
return wounds[limb];
}
string diagnose_msg(string limb)
{
class diagnosis stuff = new(class diagnosis);
int array my_wounds = query_wounds(limb);
int max = query_max_health(limb);
string array types = ({ });
if(!query_health(limb))
return "$P " + limb + " is completely disabled!\n";
if(!my_wounds)
return "$P " + limb + " is in good condition.\n";
my_wounds = sort_array(my_wounds, 1);
foreach(int x in my_wounds)
{
switch(x * 100 / max)
{
case 0..10: stuff->bruises++; break;
case 11..20: stuff->scratches++; break;
case 21..30: stuff->cuts++; break;
case 31..50: stuff->wounds++; break;
case 51..71: stuff->injuries++; break;
case 72..85: stuff->gashes++; break;
case 86..100: stuff->mutilations++; break;
}
}
if(stuff->bruises)
types += ({ number_of(stuff->bruises, "bruise") });
if(stuff->scratches)
types += ({ number_of(stuff->scratches, "scratch") });
if(stuff->cuts)
types += ({ number_of(stuff->cuts, "cut") });
if(stuff->wounds)
types += ({ number_of(stuff->wounds, "wound") });
if(stuff->injuries)
types += ({ number_of(stuff->injuries, "injury") });
if(stuff->gashes)
types += ({ number_of(stuff->gashes, "gash") });
if(stuff->mutilations)
types += ({ number_of(stuff->mutilations, "mutilation") });
return "$P " + limb + " has sustained " + format_list(types, 0) + ".\n";
}
varargs string diagnose(string limb)
{
string array limbs = query_limbs();
string ret = "";
if(!limb)
{
foreach(string s in limbs)
ret += diagnose_msg(s);
}
else ret += diagnose_msg(limb);
return ret;
}