/*
Fighter.m Class definition.
Copyright (C) 1995 David Flater.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "cheezmud.h"
@implementation Fighter
+ new
{
self = [super new];
enemies = [OrdCltn new];
stamina = maxstamina = 30.0;
heartbeat_kill = panic = 0;
ftype = warmonger;
strcpy (deadname, "corpse");
strcpy (deaddef, "the corpse");
strcpy (deadindef, "a corpse");
strcpy (deaddesc, "This is some anonymous dead thing.");
return self;
}
- free
{
/* 1996-04-15 New libobjects demands that I use dealloc instead */
/* of free. */
/* NSDeallocateObject (enemies); */
[enemies free];
return [super free];
}
- kill: who: dobj
{
if ([dobj isdead])
return self;
/* We have to prevent people from getting in extra hits by typing kill */
/* over and over. We only really want to hit if this function was called */
/* by the heartbeat. The heartbeat is nice enough to set a flag for us, */
/* since otherwise it would be hard to tell. */
if ([who checkkillflag]) {
if (dobj == who) {
[who clue: who];
return self;
}
if (++beatcount == 4) {
beatcount = 0;
[[who getlocation] emote: who: "attack": "attacks": dobj: ""];
[dobj hit: who: (float) (random() % 3)];
[location theres_a_fight_going_on];
}
} else {
if (dobj == who) {
[who echo: "If you want to delete your account, e-mail the mud admin!"];
[who clue: who];
return self;
}
[[who enemies] addIfAbsent: dobj];
}
return self;
}
- (float) priority: (char *) action: (int) numargs
{
if (numargs == 2) {
if (!strcmp (action, "kill"))
return 2.0;
}
/* Not supported */
return -1.0;
}
- hit: fromwho: (float) damage
{
if (dead)
return self;
[enemies addIfAbsent: fromwho];
/* Figure in level differences and armor. */
if ([fromwho level] == -1)
damage = damage + 20.0; /* Mud admin always gets a big advantage */
else {
int i,m;
float ac = 0.0;
damage = damage + (float)([fromwho level]) - (float)([self level]);
for(i=0,m=[contents size];i<m;i++) {
id whatever = [contents at:i];
/* Armor is not cumulative! */
if ([whatever isKindOf: [Armor class]])
if ([whatever protection] > ac)
ac = [whatever protection];
}
damage -= ac;
}
if (damage <= 0.0) {
[location emote: self: "suffer": "suffers": " no damage"];
if (stamina <= panic)
[self runaway];
} else if (stamina > damage) {
float beforehealth, afterhealth;
beforehealth = [self health];
stamina -= damage;
afterhealth = [self health];
if (afterhealth <= 0.33 && beforehealth > 0.33)
[location emote: self: "are": "is": " on the brink of death"];
else if (afterhealth <= 0.66 && beforehealth > 0.66)
[location emote: self: "are": "is": " hurting real bad"];
else if (damage <= 2.0)
[location emote: self: "are": "is": " hurt"];
else if (damage <= 7.0)
[location emote: self: "are": "is": " injured"];
else if (damage <= 15.0)
[location emote: self: "are": "is": " wounded"];
else
[location emote: self: "are": "is": " seriously wounded"];
if (stamina <= panic)
[self runaway];
} else {
int templevel;
float tempstamina;
if ([self isKindOf: [Player class]] &&
[fromwho isKindOf: [Player class]] ) {
char temp[80];
sprintf (temp, "PK: %s killed %s", [fromwho mudname], [self mudname]);
cheezlog (temp);
}
/* Make the gain-a-level message come _after_ the dies message. */
templevel = [self level];
tempstamina = [self maxstamina];
[self die];
if (templevel)
[fromwho get_experience:
(unsigned long) (tempstamina * (templevel + 1)) >> 1];
else
[fromwho get_experience: (unsigned long) tempstamina];
}
return self;
}
- runaway
{
int result=0, choice, loop;
for (loop=0;loop<12;loop++) {
choice = random() % 6;
switch (choice) {
case 0:
result = [self act: "n"];
break;
case 1:
result = [self act: "s"];
break;
case 2:
result = [self act: "e"];
break;
case 3:
result = [self act: "w"];
break;
case 4:
result = [self act: "u"];
break;
case 5:
result = [self act: "d"];
break;
default:
assert (0);
}
if (result)
return self;
}
return self;
}
- die
{
id c;
dead = 1;
[location emote: self: "die": "dies": ""];
/* Kluge: theres_a_fight_going_on doesn't work if you get killed in */
/* one swipe, since you are not there for your buddies to query on who */
/* just killed you. This works around that by calling it special just */
/* before you die. */
[location theres_a_fight_going_on];
c = [[[Corpse new] describe: deadname: deadindef: deaddef: deaddesc]
setlocation: location];
[contents elementsPerform: @selector(setlocation:) with: c];
[self logout];
return self;
}
/* This does the killing on each heartbeat. */
- killagain
{
id victim = NULL;
while (![enemies isEmpty]) {
victim = get_random_member (enemies);
if ([contents includes: victim])
if (![victim isdead])
break;
if ([[location contents] includes: victim])
if (![victim isdead])
break;
[self clue: victim];
victim = NULL;
}
if (victim) {
id t;
if ((t = [self resolve_action: "kill": 2])) {
heartbeat_kill = 1;
[t kill: self: victim];
} else
assert (0);
}
return self;
}
- (void) heartbeat
{
/* Beatcount is managed in kill. */
if (dead)
return;
if (stamina < [self maxstamina])
stamina *= 1.001;
if (stamina > [self maxstamina])
stamina = [self maxstamina];
[self killagain];
}
- clue: dontkillme
{
while ([enemies find:dontkillme]) {
[enemies remove:dontkillme];
}
return self;
}
- unclue: killme
{
[enemies addIfAbsent: killme];
return self;
}
- (int) checkkillflag
{
int t;
t = heartbeat_kill;
heartbeat_kill = 0;
return t;
}
- theres_a_fight_going_on
{
id c;
int i,m;
if (dead)
return self;
if (ftype == bystander || (![enemies isEmpty]))
return self;
if (ftype == pacifist)
return [self runaway];
/* Kill 'em all! */
c = [location contents];
for(i=0,m=[c size];i<m;i++) {
id whatever = [c at:i];
if (whatever == self)
continue;
if ([whatever isKindOf: [Fighter class]] &&
(![whatever isKindOf: [Player class]])) {
int j,mm=[[whatever enemies] size];
for(j=0;j<mm;j++) {
id w2 = [[whatever enemies] at:j];
[enemies addIfAbsent:w2];
}
}
}
return self;
}
- enemies
{
return enemies;
}
- empty: who
{
char temp[80];
sprintf (temp, "%s is not carrying anything.", capitalize ([self def]));
[who echo: temp];
return self;
}
- nonempty: who
{
char temp[80];
sprintf (temp, "%s is carrying:", capitalize ([self def]));
[who echo: temp];
return self;
}
- (float) health
{
return stamina / [self maxstamina];
}
- (float) stamina
{
return stamina;
}
- (float) maxstamina
{
return maxstamina;
}
- (void) heal
{
stamina = [self maxstamina];
}
- get_experience: (unsigned long) exp
{
return self;
}
@end