Mimeon Weapon

#include <weapon_lib.h>
#include "../shifter.h"
#define MINSIZE MIN_WEAPON_SIZE
// How many swings per loss of a size
#define DAMAGE_SIZE 15
inherit OBJ+"base_mimeon_weapon";

int add_size(int x, int ignore_counter);
mixed d_types(string s);

int size;
int size_counter;
int ret_val;
int weight_bonus;
int explode;
int hand;

create() {
  ::create();
  set_sound("You hear nothing.\n");
  set_taste("You lick yourself and think you taste fine.\n");
  set_touch("You give yourself a good feel, and think the weapon feels perfect.\n");
  set_smell("You smell the weapon, and it smells like you need a bath.\n");
  set_identify("It was made by you!");
  set_material("rock");
}

int twohand(string s) {
  if (!s) return 0;
  s = lower_case(s);
  switch (s) {
    case "two handed axe"    : return 1;
    case "two handed sword"  : return 1;
  }
  return 0;
}

void set_wield_hand(int s) {
  hand = s;
}

int query_wield_hand() { return hand; }

void set_type(string s) {
  if (!s) return;
  set_weapon_type(s);
  set_twohanded(twohand(s));
  set_weapon(d_types(s));
  set_name(lower_case(s));
  set_alias(({"shifter_weapon",s}));
  set_short("a small "+lower_case(s));
  set_long("The "+lower_case(s)+" seems to be made of a shiny liquid metal.");
  add_size(MINSIZE,0);
  set_class(4);
  set_parry_ac(4);
}

mixed d_types(string s) {
  if (!s) return CLUB;
  s = lower_case(s);
  switch (s) {
    case "axe"        : return AXE;
    case "club"       : return CLUB;
    case "curved blade" : return CURVED;
    case "flail"      : return FLAIL;
    case "knife"      : return KNIFE;
    case "longsword"  : return LONGSWORD;
    case "polearm"    : return POLE_BLADE;
    case "rapier"     : return RAPIER;
    case "shortsword" : return SHORTSWORD;
    case "spear"      : return SPEAR;
    case "staff"      : return STAFF;
    case "two handed axe"    : return TWO_H_AXE;
    case "two handed sword"  : return TWO_H_SWORD;
  default : return CLUB;
 }
}

string a_an_string(string word) {
  string letter;
  if (!word) return word;
  letter = extract(word,0,0);
  if (letter == "a" || letter == "e" || 
      letter == "i" || letter == "o" ||
      letter == "u")
        return "an "+word;
  return "a "+word;
}

string short_size() {
  switch(size) {
    case 0..6 : return "small ";
    case 7..9 : return "";
    case 10..12 : return "medium-sized ";
    case 13..15 : return "large ";
    case 16..20 : return "huge ";
    case 21..40 : return "enormous ";
    default : return "unknown ";
  }
}

void fix_short() {
  set_short(a_an_string(short_size())+lower_case(query_weapon_type()));
  set_long("The "+short_size()+lower_case(query_weapon_type())+" seems to be made of a shiny liquid metal.");
}

int add_weight_bonus(int x) {
  weight_bonus += x;
  if (weight_bonus < 0) weight_bonus=0;
  set_weight(size/6+1+weight_bonus);
  TP->fix_carry();
}

int add_size(int x, int ignore_counter) {
  int lvl;
  int mwc;
  size += x;
  if (!ignore_counter) {
    size_counter += x*DAMAGE_SIZE;
  }
  if (size < MINSIZE)
    size=MINSIZE;
  if (size_counter < MINSIZE*DAMAGE_SIZE) 
    size_counter = MINSIZE*DAMAGE_SIZE;
  fix_short();
  ret_val = 0;
  set_weight(size/6+1+weight_bonus);
  mwc = this_object()->max_wc();
  if (size < mwc) {
    set_class(size);
  } else {
    set_class(mwc);
    ret_val = size - mwc;
  }
  if (TP)
    TP->fix_carry();
}

int query_hp_weight_cost(int i) {
  return WEAPON_HP_WEIGHT + i*2;
}

int query_hp_cost_eq(int s) {
  return 5+s*s/40;
}
int query_hp_cost() { return query_hp_cost_eq(size); }

int query_size() { return size; }
string query_form() { return ROCK3; }

int hit(object att) {
  object env;
  object shape;
  int ret_bonus;
  size_counter--;
  env = environment();
  if (size_counter < MINSIZE*DAMAGE_SIZE) 
    size_counter = MINSIZE*DAMAGE_SIZE;
  if (size > size_counter/DAMAGE_SIZE) {
    shape = present(ROCK3+"_shape",env);
    if (shape) { // This should always be true... but just incase...
      shape->add_bonus_hp(-1*query_hp_cost());
    }
    CLASSOB(env)->add_learning("mweapon");
    add_size(-1,1);
  }
  if (!random(DAMAGE_SIZE) && size > MINSIZE)
    tell_room(environment(att),"Small pieces break off the "+lower_case(query_weapon_type())+".\n");
  ret_bonus = DAMAGE(ROCK3+"_weap",env,"mweapon",20,2,0);
  return ret_val+ret_bonus;
}

int max_wc() {
  return MOD_D->mimeon_max_wc(weapon_type,twohanded);
}

int query_hp_restore() { 
  int i;
  int x;
  for(i=size;i>MINSIZE;i--)
    x +=query_hp_cost_eq(i);
  for(i=weight_bonus;i>=0;i--)
    x += query_hp_weight_cost(i);
//return (size-(MINSIZE-1))*WEAPON_HP_SIZE;
  return x;
}
int query_weight_bonus() { return weight_bonus; }

// Exploding weapons!
// No ::init since we cant unwield or wield
void init() {
  add_action("do_spike","spike");
  if (TP==environment(this_object()))
    call_out("wieldme",0,TP);
}

void wieldme(object who) {
  object *inv;
  int i;
  int num;
  int found;

  inv = all_inventory(environment(this_object()));
  for(i=0;i<sizeof(inv);i++) {
    if (inv[i]->id(query_name()) && !found) {
      num++;
    }
    if (inv[i] == this_object()) found=1;
  }
  if (who != environment()) return;
  this_object()->un_wield();
  explode = 0;
  if (hand)
    this_object()->wield(query_name()+" "+num+" in "+who->query_secondary_hand_str()+" hand");
  else
    if (twohanded)
      this_object()->wield(query_name()+" "+num+" in both hands");
    else
      this_object()->wield(query_name()+" "+num);
}

int do_spike(string str) {
  object *inv;
  object ob;
  object weap;
  ob = CLASSOB(TP);
  if (!ob) return 0;
  if (ob->query_size("mimeon") < 4) return 0;
  inv = filter_array(all_inventory(this_player()),
    lambda( ({'x}),({#'call_other, 'x, "query_mimeon_shifter_item"}) )); //'
  weap = SXFUN->present_array(str,inv);
  if (weap != this_object()) {
    notify_fail("Spike what?\n");
    return 0;
  }
  if (explode) {
    notify_fail("You are already concentrating to shoot spikes from your "+query_name()+".\n");
    return 0;
  }
  explode = 1;
  this_player()->add_hook("damage_dealt",({"my_damage_dealt"}));
  write("You start concentrating on your weapon to shoot spikes out when it hits.\n");
  return 1;
}

void my_damage_dealt(int dam) {
  object env;
  object att;
  object shape;
  int size_mod;
  int amt,x,i;

  if (!explode) return; // Shouldnt ever happen.
  if (!this_object()->query_wielded()) return;
  if (!dam) return;
  env = environment(this_object());
  if (!env) return;
  att = env->query_attack();
  if (!att || att->query_dead()) return;

  explode = 0;

  x = 0;
//  for(i=size;i>MINSIZE && i>=size-2;i--)
//    x +=query_hp_cost_eq(i);
  if (size > MINSIZE)
    x+= query_hp_cost();
  if (weight_bonus)
    x+= query_hp_weight_cost(weight_bonus);

  amt = MOD_D->damage_function(ROCK3+"_spike",env,x,"mimeon","spike",10,1,dam);

  if (att->query_is_player()) amt *=2;
  env->remove_hook("damage_dealt");
  if (x)
    CLASSOB(env)->add_learning("spike");
  tell_object(env,"You shoot spikes from your "+query_name()+" into "+att->query_name()+".\n");
  tell_object(env,"The spikes break off as you pull the "+query_name()+" back.\n");
  tell_room(environment(env),get_f_string(env->query_name()+" causes spikes to shoot out of "+
       env->query_possessive()+" "+query_name()+"!",0,strlen(env->query_name())+1),({env,att}));
  tell_object(att,get_f_string("As "+env->query_name()+"'s "+query_name()+" hits you, spikes shoot out from it, snapping off inside you!",0,0));
  att->hit_player(amt);
  if (size > MINSIZE)
    add_size(-1,0);
  if (weight_bonus)
    add_weight_bonus(-1);
  shape = present(ROCK3+"_shape",env);
  if (shape) { // This should always be true... but just incase...
    shape->add_bonus_hp(-1*x);
  }
}

no_disarm() { return 1; }