musicmud-2.1.6/data/
musicmud-2.1.6/data/help/
musicmud-2.1.6/data/policy/
musicmud-2.1.6/data/wild/
musicmud-2.1.6/data/world/
musicmud-2.1.6/doc/
musicmud-2.1.6/src/ident/
musicmud-2.1.6/src/lua/
musicmud-2.1.6/src/lua/include/
musicmud-2.1.6/src/lua/src/lib/
musicmud-2.1.6/src/lua/src/lua/
musicmud-2.1.6/src/lua/src/luac/
/*
 * MusicMUD - Combat Module
 * Copyright (C) 1998-2003 Abigail Brady
 * 
 * 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 "musicmud.h"
#include "verbs.h"
#include "misc.h"
#include "util.h"
#include "misc.h"
#include "rooms.h"
#include "pflags.h"
#include "death.h"
#include "trap.h"
#include "events.h"
#include "hooks.h"
#include "vsprintf.h"

#include "units.h"
#include "shared.h"
#include "match.h"
#include "move.h"
#include "nations.h"

/*
 * 
 *  NEW STYLE COMBAT ENGINE
 * 
 */

#define MODULE "combat"

static bool peaceful(MudObject *where)
{
  if (where->get_flag(FL_UNPEACEFUL)) return 0;
  if (where==mud) return 0;
  if (where->get_flag(FL_PEACEFUL)) return 1;
  return peaceful(where->owner);
}

static bool is_policed(MudObject *who) {
  if (peaceful(who->owner)) {
    if (streq(who->get("nation"), "evil"))
      return 0;
    return 1;
  }
  return 0;
}

static int
tourn() {
  return mud->get_int("tournament", 0);
}

static bool
verb_tournament(MudObject *who, int argc, const char **argv)
{
  MudObject *o;
  int i;
  if (argc>1) {
    int nowon = tourn();
    if (nowon && streq(argv[1], "off")) {
      if (IS_CAPTAIN(who) || who->ilc) {
	who->interpret2("announce Tournament off");
	mud->set("tjoin", 0);
	mud->unset("tournament");

	foreach(planet, o, i) {

	  if (is_player(o) && o->get("tourn")) {
	    o->unset("_fighting");
	  }

	  MudObject *f = o->get_object("_fighting");
	  if (f && is_player(f) && f->get("tourn")) {
	    o->unset("_fighting");
	  }

	}
      }
    }
    if (!nowon && streq(argv[1], "prepare")) {
      if (IS_CAPTAIN(who) || who->ilc) {
	who->interpret2("announce Tournament starting soon.");
	mud->set("tfrom", 1);
	foreach(players, o, i) {
	  o->unset("tourn");
	  o->unset("tplayers");
	  o->unset("tmobiles");
	  o->unset("tdiedp");
	  o->unset("tdiedm");
	}
      }
    } else if (!nowon && streq(argv[1], "begin")) {
      if (IS_CAPTAIN(who) || who->ilc) {
	mud->unset("tfrom");
	who->interpret2("announce Tournament on.");
	mud->set("tournament", now+(15*60));
      }
    }

    if (streq(argv[1], "join")) {
      if (strlen(who->id)!=4 || who->get_int("skill.combat",0)!=0) {
	who->printf("You can't join a tournament with your regular character. Quit then create a character with a 4 letter name.\n");
      } else {
	who->set("tourn", "yes");
	/// XXX make this a mud property
	MudObject *l2 = MUD_TOURNSTART;

	if (l2 && l2 != who->owner) {
	  who->oprintf("%#M vanishes.\n", who);
	  set_owner(who, l2);
	  who->oprintf("%#M appears.\n", who);
	  who->interpret("look"); 
	}
      }
    }
  }
  return true;
}

static bool is_immune(MudObject *who, MudObject *at) {
  if (who->get("tourn"))
    return 0;

  if (is_mobile(who) && tourn())
      return 0;

  if (dotrap(E_ONUNJUST, at, who, 0, 0))
    return 0;

  return is_policed(who) || who->get_priv(PFL_IMMORTAL);
}

static bool dropweapon(MudObject *who) {
  if (MudObject *w=who->get_object(KEY_WIELD)) {
    NewWorld ww;
    ww.add(*w);
    drop_obs(who, ww);
    return 1;
  }
  return 0;
}

static void damageroom(MudObject *who, int damage) {
  MudObject *room = who->owner;
  MudObject *o;
  int i;
  foreach(room->children, o, i) if (is_player(o) || is_mobile(o)) {
    set_strength(o, strength_of(o)-damage);
    /* XXX do death detection and corpse creation here */
  }
}



static bool verb_wield(MudObject *who, int argc, const char **argv) {
    if (argc < 2) {
	who->printf("Wield what?\n");
	return true;
    }
    if (who->get_flag(FL_SLEEPING)) {
        who->printf("You toss and turn in your sleep.\n");
        who->oprintf(cansee, "%#M attempts to grab you in %s sleep.\n",
		     who, his_or_her(who));   
        return true;
    }
    MudObject *what=0;
    NewWorld whatg = match(who, argc-1, argv+1, NULL, LOOK_INV|IGNORE_EXITS);
    if (whatg.getsize()>1) {
      who->printf("You can only wield one thing at a time.\n");
      return true;
    }
    if (whatg.getsize()) {
      what = whatg.get_nth(0);
    }

    if (!what) {
	who->printf("Wield what?\n", argv[1]);
	return true;
    }
    if (what->owner != who) {
	who->printf("You aren't carrying %Y.\n", what);
	return true;
    }

    set_prons(who, what);

    if (dotrap(E_BEFOREWIELD, who, what)) return true;
    /* ::: before_wield o1==weapon; before most things, return 1 to abort */

    if (what->get_int("damage")==-1 && iswornby(what, who)) {
	who->printf("%#M cannot be wielded whilst you are wearing %s.\n", what, it_them(what));
	return true;
    }

    if (mount(who) == what) {
      who->printf("You cannot wield your mount.\n");
      return true;
    }
    
    if (weapon(who) == what) {
      who->printf("You are already wielding it.\n");
      return true;
    }

    who->set(KEY_WIELD, what->id);

    who->printf("You wield %Y^n.\n", what);
    who->oprintf(cansee, "%#M wields %P^n.\n", who, what);
    what->printf("%#M wields you.\n", who);

    if (dotrap(E_AFTERWIELD, who, what)) return true;
    /* ::: after_wield o1==weapon; after everything */
    return true;
}

static bool verb_unwield(MudObject *who, int argc, const char **argv) {
  MudObject *what = weapon(who);
  if (who->get_flag(FL_SLEEPING)) {
    who->printf("You toss and turn in your sleep.\n");
    who->oprintf(cansee, "%#M attempts to grab you in %s sleep.\n",
		 who, his_or_her(who));   
    return true;
  }
   
  if (argc >= 2) {


    NewWorld whatg = match(who, argc-1, argv+1, NULL, LOOK_INV|IGNORE_EXITS);
    if (whatg.getsize()>1) {
      who->printf("You can only unwield one thing at a time.\n");
      return true;
    }
    if (whatg.getsize()) {
      what = whatg.get_nth(0);
    }

    if (!what) {
	who->printf("Unwield what?\n", argv[1]);
	return true;
    }
  }

  if (!what) {
    who->printf("You aren't wielding anything.\n");
    return true;
  }
  
  set_prons(who, what);

  if (what->owner != who) {
    who->printf("You aren't carrying %Y.\n", what);
    return true;
  }
  
  if (weapon(who) != what) {
    who->printf("You aren't wielding it.\n");
    return true;
  }

  who->unset(KEY_WIELD);

  who->printf("You unwield %Y^n.\n", what);
  who->oprintf(cansee, "%#M unwields %P^n.\n", who, what);
  what->printf("%#M unwields you.\n", who);
  return true;
}

static int damage(MudObject *weapon)
{
  if (int x=weapon->get_int("damage", 0))
    return x;
  if (weapon->get_int("wtype")==-1) {
    int s = mass_in_grams(weapon) / HECTOGRAM;    
    int d = 0;
    if (s<2)
      d = 1;
    else if (s>30)
      d = 1;
    else
      d = abs(15-abs(s-15));
    return d;
  }
  return 0;
}

static bool iswielded(MudObject *what) {
	if (what && what->owner) {
		MudObject *o = weapon(what->owner);
		return o == what;
	}
	return false;
}

static bool isweapon(Object *what) {
	return what->get_int("damage") >= 0;
}

static int pdam(Object *what) {
	int dam = what->get_int("damage");
	if (dam>=0) return dam;
	return 10;
}

static void retaliate(MudObject *at, MudObject *vi) {
	if (!vi->get("_fighting")) {
		if (vi->get_flag(FL_SLEEPING) || vi->get_flag(FL_SITTING)) {
		  vi->interpret("stand");
		}
		vi->set("_fighting", at->id);
	}
	MudObject *o;
	int i;
	const char *n = vi->get("nation");
	if (!n)
	  return;

	foreach(vi->owner->children, o, i) {
	  if (!is_mobile(o))
	    continue;
	  if (o->get_object("_fighting"))
	    continue;
	  const char *m = o->get("nation");
	  if (streq(m, n)) {
	    if (dotrap(E_BEFOREJOININ, o, o, at))
		/* ::: before_joinin o1==mobile that wants to join in, o2==mobile it wants to attack; return true to not join in */
		   continue;
	    o->oprintf(cansee, "%#M %[joins/join] in against %Y.\n", o, at);
	    o->printf("You join in against %Y.\n", at);
	    o->set("_fighting", at->id);
	    continue;
	  }
	}
}

static bool isdead(MudObject *who) {
  if (who && who->owner && streq(who->owner->get("zone"), "dead"))
    return 1;
  return 0;
}

static bool
verb_tscores(MudObject *who, int argc, const char **argv) {
  MudObject *o;
  int i;
  who->printf("%-12s [ Kil PKs Mob ] [ Dth PKs Mob ]\n", "Player");
  foreach(players, o, i) {
    if (o->get("tourn")) {
      int pkills = o->get_int("tplayers", 0);
      int mkills = o->get_int("tmobiles", 0);
      int pdeaths = o->get_int("tdiedp", 0);
      int mdeaths = o->get_int("tdiedm", 0);
      
      int score = pkills * 100 + mkills * 30;
      score -= pdeaths * 70;
      score -= mdeaths * 15;

      who->printf("^p%#-12M^n [ %3i %3i %3i ] [ %3i %3i %3i ] %5i\n", o, 
		  pkills + mkills, pkills, mkills,
		  pdeaths + mdeaths, pdeaths, mdeaths, score);
    }
  }
  time_t when = mud->get_int("tournament", 0);
  if (when && when>=now) {
    when -= now;
    who->printf("Tournament to end in %i min %i sec.\n", when/60, when%60);
  }
  return true;
}

static bool
combat_kill(MudObject *attacker, MudObject *victim, const char *how2, const char *corpsedesc=0) {

  victim->unset("_fighting");
  attacker->unset("_fighting");

  if (is_player(victim) && victim->get("tourn") && tourn()) {
    if (is_player(attacker) && attacker->get("tourn"))
      victim->set("tdiedp", victim->get_int("tdiedp", 0)+1);
    else
      victim->set("tdiedm", victim->get_int("tdiedm", 0)+1);
  }
  if (is_player(attacker) && attacker->get("tourn") && tourn()) {
    if (is_player(victim) && victim->get("tourn"))
      attacker->set("tplayers", attacker->get_int("tplayers", 0)+1);
    else
      attacker->set("tmobiles", attacker->get_int("tmobiles", 0)+1);
  }
    
  set_strength (victim, -1);

  string how = "";
  if (strncmp(how2, "(fumble) ", 9)) {
    how = how2;
    how += " by ";
    how += attacker->get("name");
  } else {
    how = how2+10;
    how += " by self fighting ";
    how += attacker->get("name");
  }

  const char *howmsg = how.c_str();

  //  log(PFL_SEEINFO, LEV_INTERNAL, "combat", "%s slain by %s",
  //      victim->id,
  //  	attacker->id);

  if (is_player(attacker) && 
      (strength_of(attacker)<random_number(100) ||
      attacker->get_int("skill.combat")<victim->get_int("skill.combat"))) {
    attacker->printf("You are now more skilled at combat.\n");
    attacker->set("skill.combat", 
		  attacker->get_int("skill.combat", 0)+1);

    how += " (gaining a skillpoint)";
    howmsg = how.c_str();
  }

    victim->set(KEY_KILLEDBY, attacker->id);	/* remember who killed the mobile */

    if (is_mobile(victim)) {
      /* Kill mobile */

      MudObject *corpse = do_die(victim, 0, howmsg); /* do_die knows what to do */

      if (corpse) {
	static const char *descs[] = {
	  "brutally savaged", "completely massacred",
	  "obliterated", "slain", "fatally wounded",
	  "severely battered", "completely obliterated",
	  "hacked apart", "killed"
	};
	int corpseno = random_number(9);
	if (corpsedesc) {
	  corpse->setf("desc", "%#M %s.", victim, corpsedesc);  
	} else {
	  corpse->setf("desc", "%#M has been %s.", victim, descs[corpseno]);  
	}
      }

      if (const char *on_kill = victim->get("on_kill")) {
	attacker->ilc++;
	attacker->interpret(on_kill);
	attacker->ilc--;
      }

      dotrap(E_ONFIGHTOVER, attacker, attacker, 0, 0);
      /* ::: fight_over o1==attacker; after a fight has finished however */
      if (dotrap(E_AFTERSLAIN, attacker, victim, 0, 0)) return true;
      /* ::: after_slain o1==victim; after mobile killed in combat */
      
    } else if (victim->get_priv(PFL_IMMORTAL)) {

      victim->printf("If you were mortal, you would be dead.\n");
      dotrap(E_ONFIGHTOVER, attacker, attacker, 0, 0);

      return false;		/* Wizards don't die */
      
    } else {
      
      set_strength (victim, 40);

      do_die(victim, privs_of(victim)*20, howmsg); /* do_die knows what to die */
      dotrap(E_ONFIGHTOVER, attacker, attacker, 0, 0);
    }

    return true;

}

static bool
wound_player (MudObject* attacker, MudObject* victim, int damage)
{
  if (!attacker || !victim) {
    return false;
  }
  retaliate(attacker, victim);

  if (damage <= 0)
    return false;

  if (victim->get_priv(PFL_IMMORTAL))
    return 0;

  if (is_mobile(victim)) {
    set_strength (victim, strength_of (victim) - damage);
  } else if (privs_of (victim) < LEV_CAPTAIN) {
        if ((strength_of (victim) - damage) >= 0) {
          set_strength (victim, strength_of (victim) - damage);
	   victim ->printf( "^W[^nYour strength is now ^r%d^W]\n", strength_of(victim));
        } else {
	  set_strength(victim, -1);
	   victim ->printf ("The blow was too much for you to stand. You have died.\n");
        }
  }

  if (strength_of(victim)>0) {
    return 0;
  }

  return combat_kill(attacker, victim, "slain");
  /* Victim has died */

  return 1;
}

static int encumberance(MudObject *what) 
{
  int i;
  MudObject *o;
  int total = 0;

  foreach(what->children, o, i) {
    int size = mass_in_grams(o);
    total += size;
  }

  //  what->printf("t:%i/%i\n", total, what->get_int("capacity", 200*HECTOGRAM));

  total = (total * 100) / mass_capacity_in_grams(what, 20 * KILOGRAM);
  //  what->printf("tc:%i\n", total);
  total -= 50;

  if (total <= 0)
    return 0;
  
  if (total >= 50)
    total = 50;

  return total / 3;
}

#include "fumble.h"
#include "blow.h"

static void do_combat(MudObject *attacker, MudObject *victim) {

  wtype_t wtype = W_UNARMED;

#ifdef DEBUG_COMBAT
  printf("::%s attacks %s\n", attacker->id, victim->id);
#endif

  const char *skillfor[] = 
  {"skill.unarmed",
   "skill.impact",
   "skill.cutting",
   "skill.energy",
   "skill.stunning",
  "skill.stabbing"};

	if (attacker->owner != victim->owner) {
		attacker->unset("_fighting");
		victim->unset("_fighting");
		dotrap(E_ONFIGHTOVER, attacker, attacker, 0, 0);
		//		attacker->oprintf(cansee, "%#M visibly relaxes.\n");
		return;
	}

	//	attacker->printf("%20s rounds : %i\n", "you", attacker->get_int("!rounds", 0));
	//	attacker->oprintf(cansee, "%#20M rounds : %i\n", attacker, attacker->get_int("!rounds", 0));

	if (attacker->get_int("!rounds", 0)>0) {
	  attacker->set("!rounds", attacker->get_int("!rounds", 0)-1);
	  attacker->printf("You can't attack yet...\n");
	  //	  attacker->oprintf(cansee, "%#M can't attack yet.\n", attacker);
	  return;
	} else {
	  attacker->unset("!rounds");
	}
	
	int d = 0;
	
	MudObject *w = weapon(attacker);
	
	if (w && w->owner != attacker) {
	  attacker->printf("You belatedly realise you don't have %Y,\n"
			   "and are forced to use your hands instead.\n", w);
	    attacker->unset(KEY_WIELD);
	    w = 0;
	}
	
	{
	  if (w && w->owner==attacker && iswielded(w) && isweapon(w)) {
	    d = w->get_int("damage", 0);
	  }
	  d += pdam(attacker);
	}
#ifdef WORNFROM
	MudObject *wornfrom = archetype(attacker);
 	if (attacker->get("wornfrom")) {
	  wornfrom = attacker->get_object("wornfrom");
	}
#endif
	if (w) {
	  wtype = (wtype_t)(w->get_int("wtype"));
	}

	if (wtype==-1) {
	  int s = mass_in_grams(w) / HECTOGRAM;

	  if (s<2)
	    d = 1;
	  else if (s>30)
	    d = 1;
	  else
	    d = abs(15-abs(s-15));

	  wtype = W_IMPACT;
	}
#ifdef WORNFROM
	if (!w && wornfrom && wornfrom->get_object(KEY_WIELD)) {
	  w = wornfrom->get_object(KEY_WIELD);
	  wtype = (wtype_t)(w->get_int("wtype"));
	}
#endif
	int roll = random_number(100);
	
	int mroll = roll;

	mroll += encumberance(victim); /* encumberance of defender */
	int skil = (attacker->get_int(skillfor[wtype])/5,
		    attacker->get_int("skill.combat")/5) + (is_player(attacker)?20:0);
	mroll += skil; 
	/* skill at this weapon */
	mroll += attacker->get_int("att.str", is_player(attacker)?15:10); 
	/* strength of attacker */
	
	mroll -= encumberance(attacker); /* encumberance of attacker */
	mroll -= victim->get_int("att.dex", is_player(victim)?30:10); /* dexterity of defender */

	//	int vdex = victim->get_int("att.dex", is_player(victim)?30:10);
	// should we be using the victims dexterity???

	//        attacker->printf("The bias is %i, skill bonus is %i.\n", mroll-roll, skil);

	int delay = 1;
    	int victimdelay = 0;

	if (w) 
	  delay += w->get_int("delay", 0);

	delay += attacker->get_int("delay", 0);

	bool DONTKILL = 0;

	if (is_player(attacker) && is_immune(victim, attacker) /*&& !IS_CAPTAIN(attacker)*/) {
	  //	  mroll = 1;
	  //	  roll = 1;
	  DONTKILL = 1;
	  /* 	  delay += 2; */
	  
	}

	bool DONTDIE = 0;
	
	if (is_player(victim) && is_immune(attacker, victim)) {
	  DONTDIE = 1;
	}

	int selfdamage = 0;


	      const char *verb = "hit";
	      const char *verb_s = "hits";
	      switch (wtype) {
	      case W_UNARMED: break;
	      case W_CUTTING: verb = "slash"; verb_s = "slashes"; break;
	      case W_IMPACT: verb = "bash";   verb_s = "bashes"; break;
	      case W_ENERGY: verb = "shoot";  verb_s = "shoots"; break;
	      case W_STABBING: verb = "stab"; verb_s = "stabs"; break;
	      }


	      int deathblow = 0;

	      retaliate(attacker, victim);

        int cantfumble=0;
	if (!streq(body(attacker), "human"))
	  cantfumble = 1;

	if (roll<=0 || mroll<=0 && !DONTDIE && !cantfumble) {

	  /* Fumbles */

	  int fumblenum = -random_number(100) + skil;
	  /*	  attacker->printf("You fumble (%i).\n", fumblenum);
		  attacker->oprintf(cansee, "%#M fumbles (%i)\n", attacker, fumblenum); */

	  switch (wtype) {
	    
	  case W_UNARMED:
	    unarmed_do_fumble(attacker, victim, w, selfdamage, delay, fumblenum);
	    break;

	  case W_IMPACT:  
	    impact_do_fumble(attacker, victim, w, selfdamage, delay, fumblenum);
	    break;

	  case W_CUTTING: 
	  case W_STABBING:
	    cutting_do_fumble(attacker, victim, w, selfdamage, delay, fumblenum);
	    break;

	  case W_ENERGY:
	    energy_do_fumble(attacker, victim, w, selfdamage, delay, fumblenum);
	    break;

	  }

	} else if (mroll < 20) {

	  /* Miss! */

	  attacker->printf("You take a swing at %M but miss.\n", victim);
	  attacker->oprintf(cansee && avoid(victim), "%#M takes a swing at %M but misses.\n", attacker, victim);
	  victim->printf("%#M takes a swing at you but misses.\n", attacker);

	} else {
	  
	  int roll2 = random_number(d?d:1);

	  /* Regular hits */

	  if (wtype != W_ENERGY) {
	    roll2 += attacker->get_int("att.str", 10);
	    roll2 += (mroll-20) / 20;

	    roll2 -= random_number(player_armour( victim )+parmour(victim));

	  }

	    if (roll2 <= 0)
	      roll2 = 0;

	  if (DONTKILL) {
	    if (roll2 > (strength_of(victim)/2)) {
	      roll2 = strength_of(victim)/2;
	    }
	  }

	  if (streq(body(victim), "droid") && 
	      (wtype==W_CUTTING || wtype==W_STABBING)) {
	    roll2 /= 10;
	    wtype = W_IMPACT;
	  }

	  if (victim->owner->get_flag(FL_UNDERWATER)) {
	    if (wtype==W_CUTTING) {
	      roll2 /= 2;
	    }
	    if (wtype==W_IMPACT) {
	      roll2 /= 3;
	    }
	  }

	  if (roll2 > strength_of(victim)) {
	    deathblow = 1;
	  } else {
	    
	    newblow(attacker, victim, roll2, w, wtype);
	    
	    if (!isdead(victim)) 
	      wound_player(attacker, victim, roll2);
	    
	  }
	}


	if (deathblow) {
	    newblow(attacker, victim, -100, w, wtype);
	}

    victimdelay += victim->get_int("delay", 0);
    victim->set("!rounds", victimdelay);

	attacker->set("!rounds", delay);
	if (selfdamage && !isdead(attacker))
	  wound_player(victim, attacker, selfdamage);


	/* Combat algorithm

	     Calculate where we hit, and with what force

             (Where is determined by)

               Rolling at random.

             (Force is determined by)

               + encumberance of defender
               + skill of attacker at this weapon type
               + strength of attacker
               (should current state of health come into this? ratio of health:maxhealth)

               - encumberance of attacker
               - dexterity of victim

               If we roll really badly here we could get a fumble.

               If the _unmodified_ hit roll is exceptionaly high (natural 99? asuming rand(100) is 0->99)
                  -We get a 'superhit' (this gives even weak attackers a tiny chance of fancy stuff)
                  -we dont carry on and calculate damage as well as the superhit.

             Calculate damage actually done

               - armour of victim

               + weapon damage
               + strength of blow

             If the damage caused after calculation would kill the victim
                -We get a deathblow _instead_

             else If the _modified_ damage roll is very high (exact number unknown yet)
                -We get a superhit _instead_ of normal damage
                -must make sure all superhits do more damage than the best normal hit...
                -what do we do if the super-hit kills someone, but it is a weak one, like scratching of the face, should we
                 look at the potential damage of the super hit, and if that would kill, do a deathblow instead?


	*/
		
	//	if (is_player(attacker)) attacker->interpret("calc_level");
	//	if (is_player(victim)) victim->interpret("calc_level");
}

static MudObject *random_exit(MudObject *player) {
  MudObject *what = 0;
  MudObject *o;
  int i;
  NewWorld exits;
  foreach(player->owner->children, o, i) {
    if (MudObject *l=o->get_object("link")) {
      if (is_mobile(player) && l->get_flag(FL_NOMOBILES))
	continue;
      exits.add(*o);
    }
  }
  if (exits.getsize()) {
    what = exits.get_nth(random_number(exits.getsize()));
  }
  return what;
}

static void docombat(MudObject *who) {
#ifdef DEBUG_COMBAT
  printf("::docombat(%s)\n", who->id);  
#endif
    if (who->get("_fighting")) {
	MudObject *victim = who->get_object("_fighting");
	if (!victim) {
	    who->unset("_fighting");
  	    dotrap(E_ONFIGHTOVER, who, who, 0, 0);
	} else {

	  if (who->get_int("wimpy")>strength_of(who)) {
	    if (who->get("$camefrom")) {
	      who->interpretf("flee %s", who->get("$camefrom"));
	    } else {
	      MudObject *re = random_exit(who);
	      if (re) {
		who->interpretf("flee %s", re->get("short"));
	      }
	    }
	  }

	  do_combat(who, victim);
	}
    }
    return;
}

static bool verb_arrest(MudObject *who, int argc, const char **argv)
{  /* can we use this for non lethal stun combat as well?*/
    if (argc==1) {
	who->printf("syntax : arrest <who>\n");
	return true;
    }

    if (!who->get_flag(FL_POLICE)) {
      who->printf("You aren't a police officer.\n");
      return true;
    }

    NewWorld whatg = match(who, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME);
    MudObject *what = 0;
    if (whatg.getsize()>1) {
      who->printf("You can only do one arrest at a time!\n");
      return true;
    } else if (whatg.getsize()) {
      what = whatg.get_nth(0);
    }
    if (!what) what = planet->get(argv[1]);

    if (!what) {
      who->printf("Arrest who?\n");
      return true;
    }

    if (who->owner != what->owner) {
      who->interpretf(lang("say We'll catch %M, don't you worry.", who), what);
      who->interpretf("tell %s %s", what->id, lang("You can run but you can't hide!", what));
    } else {      
      who->interpretf(lang("say %M, you're nicked!", who), what);
    }
    
    MudObject *fighter = what->get_object("_fighting");
    if (fighter) {
      fighter->unset("_fighting");
    }
    what->unset("_fighting");
    what->set("arrested", 1);

    return true;
}


static bool iscopper(MudObject *ob) {
  return (ob->get_flag(FL_MOBILE) && ob->get_flag(FL_POLICE) && !ob->get_flag(FL_SITTING));
}

static bool verb_attack(MudObject *who , int argc, const char **argv) {

    int unjust = 0;
    MudObject *coproom = 0;
    
    if (!is_player(who) && !is_mobile(who)) {
	who->printf("Sorry, you're not even a mobile! :)\n");
	return true;
    }

    if (who->get_flag(FL_SLEEPING)) {
        who->printf("You wave your hands about in your sleep.\n");
        who->oprintf(cansee, "%#M is obviously dreaming in %s sleep.\n", who, his_or_her(who));   
        return true;
    }

    if (wf_wears_with_flag(who, FL_HANDSTIED)) {
        who->printf("Your hands are tied!\n");
        return true;
    }

    if (argc==1) {
	who->printf("Attack what?\n");
	return true;
    }

    if (streq(argv[1], "myself") && argc==2) {
      MudObject *cor=do_die(who, privs_of(who)*20, "committed suicide", 1);
      if (cor) {
	cor->setf("desc", "%#M commited suicide.\n", who);
      }
      return true;
    }
    
    NewWorld whatg = match(who, argc-1, argv+1, 0, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME);
    MudObject *what = 0;
    if (whatg.getsize()>1) {
      who->printf("You can only do one attack at a time!\n");
      return true;
    } else if (whatg.getsize()) {
      what = whatg.get_nth(0);
    }
    
    NewWorld withg = match(who, argc-1, argv+1, 0, LOOK_INV|IGNORE_EXITS, "with");
    MudObject *with=0;
    if (withg.getsize()>1) {
      who->printf("You can only attack with one thing at a time!\n");
      return true;
    } else if (withg.getsize()) {
      with = withg.get_nth(0);
    }

    if (!what) 
      	what = planet->get(argv[1]);
	
    if (!what) {
	who->printf("Cannot find %s.\n", whatg.txt.c_str());
	return true;
    }
    
    set_prons(who, what);

    if (what->get_object("_fighting")==who) {
      if (who->get_object("_fighting")==what) {
	who->printf("You are already fighting %M.\n", what);
	return true;
      }
      who->printf("You shift your attention to %M.\n", what);
      who->set("_fighting", what->id);
      what->printf("%#M %[has/have] shifted %s attention to you.", who, his_or_her(who));
      who->set("!rounds", who->get_int("!rounds",0)+2);
      return true;
    }

    who->set("!rounds", 0);

    if (who->get_object("_fighting")) {
	who->printf("You are already fighting someone...\n");
	return true;
    }

    if (what == who) {
	who->printf("Are you sure? If so, 'kill myself'.\n");
	return true;
    }

    MudObject *where = who->owner;
    MudObject *zon = where?where->owner:NULL;

    if (zon && zon->get_flag(FL_SANCTUARY)) {
      who->printf("Something interferes with your attack.\n");
      return true;
    }

    if (where && where->get_flag(FL_SANCTUARY) && who->get("tourn")) {
      who->printf("This room is not part of the tournament.\n");
      return true;
    }

    if (mud->get_int("tfrom", 0) && who->get("tourn")) {
      who->printf("Not until the tournament has formally started.\n");
      return true;
    }

    if (!tourn() || !who->get("tourn") || (is_player(what) && !what->get("tourn"))) {
      if (is_player(what) || is_mobile(what)) {
	if (peaceful(who->owner)) {
	  
	  coproom = find_coproom(who->owner);
	  
	  unjust = 1;
	  if (who->get_flag(FL_POLICE))
	    unjust = 0;
	  
	  if (streq(what->get("nation"), "evil"))
	    unjust = 0;
	}
      }
    }

    if (unjust && !coproom && !who->owner->get_flag(FL_SHIP)) {
      who->printf("It is too peaceful here.\n");
      return true;
    }

    if (what->get_priv(PFL_NOHASSLE) && !who->get_flag(FL_MOBILE)) {
	who->printf("Something interferes with your attack.\n");
	return true;
    }

    if (what->get_priv(PFL_NOHASSLE)) {
    	who->printf("No point.\n");
    	what->printf("%#M would normally attack you now.\n", who);
    	return true;
    }
    
    if (dotrap(E_ONKILL, who, what, with, 0)) return true;
    /* ::: kill o1==victim; before fight started. o1 can be an object. */
    
    if (what->get_flag(FL_FRAGILE)) {
        who->printf("You smash %Y to bits.\n", what);
        who->oprintf(avoid(what) && cansee, "%#M %[smashes/smash] %Y to bits.\n", who, what);
        what->printf("%#M %[smashes/smash] you to bits.\n", who);
	vanish(what);
	return true;
    }

    if ((who->ilc || IS_CAPTAIN(who)) && what->get_flag(FL_CANOPEN) && what->get_object("other") && what->get_int(KEY_STATE, 0)!=0) {
	MudObject *oth = what->get_object("other");
        who->printf("You bash %Y down.\n", what);
        who->oprintf(cansee, "%#M %[bashes/bash] %P down.\n", who, what);
	what->set(KEY_STATE, 0);
	oth->set(KEY_STATE, 0);
	oth->oprintf(cansee, "%#M %[is/are] bashed down.\n", oth);
	return true;
    }

    if (!is_player(what) && !is_mobile(what)) {
	who->printf("You can't attack %Y.\n", what);
	return true;
    }
    
    if (what->owner == who) {
	who->printf("%#M %[escapes/escape] your grasp.\n", what);
	set_owner(what, who->owner);
    }

    if (what->owner != who->owner) {
	who->printf("%#M %[is/are] not here.\n", what);
	return true;
    }

    if (what->get_object("_fighting")==who)
      unjust = 0;

    who->set("_fighting", what->id);
    what->set("_fighting", who->id); /* added 07042002 */
    who->printf("You unleash an attack on %M!\n", what);
    if (what->get_flag(FL_SLEEPING)) {
       what->printf("You clamber dozily to your feet as %#M %[unleashes/unleash] an attack on you!\n", who);
       what->set_flag(FL_SLEEPING, 0);
    } else if (what->get_flag(FL_SITTING)) {
       what->printf("You clamber to your feet as %#M %[unleashes/unleash] an attack on you!\n", who);
       what->set_flag(FL_SITTING, 0);
    } else {
       what->printf("%#M %[unleashes/unleash] an attack on you.\n", who);
    }
    who->oprintf(cansee && avoid(what), "%#M %[unleashes/unleash] an attack on %M!\n", who, what);
    
    if (!is_player(what) && dotrap(E_ONUNJUST, who, what, 0, 0)) {
    /* ::: unjust o1==victim; return 1 if player killing o1 would be ok */
      unjust = 0;
    }

    if (is_mobile(who) && is_player(what)) {
      unjust = 0;
    }

    if (unjust && !what->get_flag(FL_CANTTALK) && !what->get_flag(FL_DUMB)) 
      {
	int po = 0;

	if (what->get_flag(FL_POLICE)) {
	  po = 1;
	}

	if (who->owner->get_flag(FL_SHIP)) {
	  what->interpretf(lang("%s Help! %M is commiting piracy!", what), 
			   MUD_HELPCHAN, who);
	  fine(who, 500, ssprintf("Piracy", what).c_str());
	} else {
	  if (!po) {
	    what->interpretf(lang("%s Help! I'm being attacked by %M!", 
				  what), MUD_HELPCHAN, who);
	    if (streq(body(what),"droid"))
	      fine(who, 50, ssprintf("Vandalism upon %M", what).c_str());
	    else
	      fine(who, 100, ssprintf("Assaulting %M", what).c_str());
	  } else {
	    if (!coproom || coproom->get_object("interview") != who->owner)
	      what->interpretf(lang("%s %M is attacking an officer of the law!", what), MUD_HELPCHAN, who);
	    else
	      coproom = 0;
	    fine(who, 150, "Assaulting an officer of the law.");
	  }
	}

	if (coproom) {
	  MudObject *plod = choose_random_object_from(coproom, iscopper);
	  if (plod) {
	    if (streq(what->get("nation"), "evil")) {
	      plod->interpretf("%s Good!", MUD_HELPCHAN);
	      return true;
	    }
	  }
	  
	  if (plod) {
	    plod->setf("!plan", "1 goto %s;0 say %s;0 arrest %s;1 goin %s;0 summon %s;0 heal %s;2 goto", 
		       who->id, 
		       lang("What's going on here then?", plod),
		       who->id, 
		       coproom->get("interview"),
		       who->id,
		       what->id);
	    set_owner(plod, "empty");
	    plod->set("!when", 1);
	    plod->setf("!what", "%s %s", MUD_HELPCHAN, lang("On my way.", plod));
 	    plod->set_bflag(FL_TIMER, 1);
	    plod->set_bflag(FL_REFUSEMOVE, 1);
	  }
	}
      }

    docombat(who);

    return true;
}

bool is_person(const TeleObject &t)
{
  return is_person(t.what);
}

static bool verb_consider(MudObject *who, int argc, const char **argv)
{
  int unjust = 0;
  MudObject *coproom = 0;
  
  NewWorld whatg = match(who, argc-1, argv+1, is_person, LOOK_BOTH|IGNORE_EXITS|ALLOW_ME);
  MudObject *what = 0;
  if (whatg.getsize()>1) {
    who->printf("You can only consider one thing at a time!\n");
    return true;
  } else if (whatg.getsize()) {
    what = whatg.get_nth(0);
  }

  if (!what) {
    who->printf("Consider who or what?\n");
    return true;
  }

  set_prons(who, whatg);  

  if (!is_person(what)) {
    string ab = abilities(what);
    if (ab.length())
      who->printf("%#M: %s\n", what, ab.c_str());
    else
      who->printf("You note nothing out of the ordinary.\n");
    return true;
  }

  if (what==who) {
    who->printf("If it came to it, you could probably kill yourself.\n");
    return true;
  }

  if (!tourn() || !who->get("tourn") || (is_player(what) && !what->get("tourn"))) {
    
    if (is_player(what) || is_mobile(what)) {
      if (peaceful(who->owner)) {
	  
	  coproom = find_coproom(who->owner);
	  
	  unjust = 1;
	  if (who->get_flag(FL_POLICE))
	    unjust = 0;
	  
	  if (streq(what->get("nation"), "evil"))
	    unjust = 0;
      }
    }      
  }

  if (!is_player(what) && dotrap(E_ONUNJUST, who, what, 0, 0)) {
    unjust = 0;
  }

  if (unjust && coproom)
    who->printf("You'd get arrested by %s police.\n", jname(who->owner));
  else if (unjust && who->owner->get_flag(FL_SHIP))
    who->printf("You'd get done for piracy the next dock you went to under %s law.\n", jname(who->owner));
  else if (unjust)
    who->printf("You can't attack %s.\n", him_or_her(what));
  else
    who->printf("You can feel free to attack %s.\n", him_or_her(what));
  
  if (!unjust) {

  int skildif = who->get_int("skill.combat",0) - 
    what->get_int("fakeskill.combat", 
		  what->get_int("skill.combat",0));

  skildif += random_number(5) - 2;
  skildif += random_number(5) - 2;

  if (skildif > 20)
    who->printf("You are much more skilled,");
  else if (skildif > 10)
    who->printf("You are more skilled,");
  else if (skildif > 0)
    who->printf("You have a slight edge in skill,");
  else if (skildif == 0)
    who->printf("You are about equally skilled,");
  else if (skildif > -10)
    who->printf("%#s has a slight edge in skill,", he_or_she(what));
  else if (skildif > -20)
    who->printf("%#s %s more skilled than you,", he_or_she(what), is_are(what));
  else if (skildif > -40)
    who->printf("%#s %s much more skilled than you,", he_or_she(what), is_are(what));
  else
    who->printf("%#s %s you greatly,", he_or_she(what), what->get_flag(FL_PLURAL)?"outclass":"outclasses");

  MudObject *w = weapon(what);
  int dam = pdam(what);
  if (w) dam += damage(w);

  dam += random_number(3) - 1;
  dam += random_number(3) - 1;

  int fd=what->get_int("fakedamage");
  if (fd != -1) dam = fd;

  const char *damstr = "minimal damage";
  if (dam>5) damstr = "some damage";
  if (dam>10) damstr = "a fair bit of damage";
  if (dam>15) damstr = "quite a bit of damage";
  if (dam>20) damstr = "a lot of damage";
  if (dam>25) damstr = "lots of damage";
  
  who->printf(" %s %s will do %s.\n", (skildif>0&&dam>10)?"but":"and", he_or_she(what), damstr);

  }

  int hp = (strength_of(what) * 100) / what->get_int("maxstrength", 200);

  hp += random_number(5) - 2;
  hp += random_number(5) - 2;

  const char *cond = "in excellent condition";
  if (hp < 90) cond = "in very good condition";
  if (hp < 80) cond = "in pretty good condition";
  if (hp < 70) cond = "in good condition";
  if (hp < 60) cond = "in fair condition";
  if (hp < 50) cond = "starting to feel the pain";
  if (hp < 40) cond = "not so good";
  if (hp < 30) cond = "pretty bad";
  if (hp < 20) cond = "very bad";
  if (hp < 10) cond = "close to death";

  who->printf("%#s %s %s.\n", he_or_she(what), is_are(what), cond);

  return true;
}

static bool verb_flee(MudObject *who, int, const char **argv) {
    if (!argv[1]) {
        who->printf("You must specify a direction to flee.\n");
        return false;
    }
    
    if (who->get_flag(FL_SITTING)) {
      who->printf("You are sitting down.\n");
      return true;
    }

   switch (argv[1][0]) {
         case 'n' : case 'N': argv[1] = "north"; break;
         case 's' : case 'S':
           if (who->owner->get_flag(FL_SHIP))
             argv[1] = "starboard";
           else
             argv[1] = "south";
         break;
         case 'e' : case 'E': argv[1] = "east";break;
         case 'f' : case 'F': argv[1] = "fore";break;
         case 'w' : case 'W': argv[1] = "west";break;
         case 'u' : case 'U': argv[1] = "up";break;
         case 'd' : case 'D': argv[1] = "down";break;
         case 'r' : case 'R': argv[1] = "rimwards"; break;
         case 'p' : case 'P': argv[1] = "port"; break;
         case 'h' : case 'H': argv[1] = "hubwards";break;
         case 'a' : case 'A':
           if (who->owner->get_flag(FL_SHIP))
             argv[1] = "aft";
           else
             argv[1] = "anticlockwise";
           break;
         case 'c' : case 'C': argv[1] = "clockwise";break;
         case 'o' : case 'O': argv[1] = "out";break;
         case 'i' : case 'I': argv[1] = "in";break;
        }

    MudObject *what = find_exit(who->owner, argv[1]);
    MudObject *mob = who->get_object("_fighting");

    if (what && (what->get_flag(FL_EXIT) || what->get_flag(FL_PARTEXIT))) {
      MudObject *o;
      int i;
      if (mob) who->printf("You hurry away from %M.\n", mob);
      if (!trigger_mobiles(who->owner, who, what, 1)) {
	return true;
      }
      
      NewWorld todrop;
      foreach(who->children, o, i) {
	if (!iswornby(o, who) && !iswielded(o) && !o->get(KEY_ACCEPTER)) {
	  todrop.add(*o);
	}
      }
      if (todrop.getsize()) {
	drop_obs(who, todrop);
      }
      
      MudObject *wasin = who->owner;
      who->unset("_fighting");
      traverse(who, what, "flee");
      if (wasin == who->owner && mob) {
	who->set("_fighting", mob->id);
      }
      if (mob)
	mob->unset("_fighting");
      return false;
    }
    who->printf("You can't go ^d%s^n.\n", argv[1]);;
    return true;
}

#define CLEANUP COMBAT_ROUTINE = 0;

#include "verbmodule.h"

void startup() {

AUTO_VERB(arrest, 4, 0, PFL_NONE);

AUTO_VERB(flee, 4, 0, PFL_AWAKE);

AUTO_VERB(consider, 4, 0, PFL_NONE);

AUTO_VERB(attack, 4, 0, PFL_NONE);
ADD_ALIAS(kill, 1, attack);
ADD_ALIAS(hit, 2, attack);
ADD_ALIAS(smash, 2, attack);

AUTO_VERB(wield, 4, 0, PFL_NONE);

AUTO_VERB(unwield, 4, 0, PFL_NONE);
ADD_ALIAS(lower, 4, unwield);

AUTO_VERB(tscores, 2, 0, PFL_NONE);
AUTO_VERB(tournament, 4, 0, PFL_NONE);

AUTO_VERB(cmsg, 4, 0, PFL_NONE);

COMBAT_ROUTINE = docombat;

}