/* 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