예제 #1
0
void player::perform_special_attacks(game *g, monster *z, player *p,
                                     int &bash_dam, int &cut_dam, int &stab_dam)
{
 bool can_poison = false;
 int bash_armor = (z == NULL ? 0 : z->armor_bash());
 int cut_armor  = (z == NULL ? 0 : z->armor_cut());
 std::vector<special_attack> special_attacks = mutation_attacks(z, p);

 for (int i = 0; i < special_attacks.size(); i++) {
  bool did_damage = false;
  if (special_attacks[i].bash > bash_armor) {
   bash_dam += special_attacks[i].bash;
   did_damage = true;
  }
  if (special_attacks[i].cut > cut_armor) {
   cut_dam += special_attacks[i].cut - cut_armor;
   did_damage = true;
  }
  if (special_attacks[i].stab > cut_armor * .8) {
   stab_dam += special_attacks[i].stab - cut_armor * .8;
   did_damage = true;
  }

  if (!can_poison && one_in(2) &&
      (special_attacks[i].cut > cut_armor ||
       special_attacks[i].stab > cut_armor * .8))
   can_poison = true;

  if (did_damage)
   g->add_msg( special_attacks[i].text.c_str() );
 }

 if (can_poison && has_trait(PF_POISONOUS)) {
  if (z != NULL) {
   if (!is_npc() && !z->has_effect(ME_POISONED))
    g->add_msg("You poison the %s!", z->name().c_str());
   z->add_effect(ME_POISONED, 6);
  } else if (p != NULL) {
   if (!is_npc() && !p->has_disease(DI_POISON))
    g->add_msg("You poison %s!", p->name.c_str());
   p->add_disease(DI_POISON, 6, g);
  }
 }
}
예제 #2
0
void Character::spell_gas_breath (int sn, int lvl, void *vo)
{
  Character *vch;

  CharIter rch, next;
  for (rch = in_room->people.begin(); rch != in_room->people.end(); rch = next) {
    vch = *rch;
    next = ++rch;
    if (is_npc () ? !vch->is_npc () : vch->is_npc ()) {
      int hpch = std::max (10, hit);
      int dam = number_range (hpch / 16 + 1, hpch / 8);
      if (vch->saves_spell (lvl))
        dam /= 2;
      damage (this, vch, dam, sn);
    }
  }
  return;
}
예제 #3
0
파일: melee.cpp 프로젝트: Azrael/Cataclysm
void player::stumble(game *g)
{
 int stumble_pen = 2 * weapon.volume() + weapon.weight();
 if (has_trait(PF_DEFT))
  stumble_pen = int(stumble_pen * .3) - 10;
 if (stumble_pen < 0)
  stumble_pen = 0;
 if (stumble_pen > 0 && (str_cur >= 15 || dex_cur >= 21 ||
                         one_in(16 - str_cur) || one_in(22 - dex_cur)))
  stumble_pen = rng(0, stumble_pen);
 if (!is_npc()) {	// Only display messages if this is the player
  if (stumble_pen >= 60)
   g->add_msg("You miss and stumble with the momentum.");
  else if (stumble_pen >= 10)
   g->add_msg("You swing wildly and miss.");
  else
   g->add_msg("You miss.");
 }
 moves -= stumble_pen;
}
예제 #4
0
void Character::spell_call_lightning (int sn, int lvl, void *vo)
{
  Character *vch;

  if (!is_outside()) {
    send_to_char ("You must be out of doors.\r\n");
    return;
  }

  if (!g_world->is_raining()) {
    send_to_char ("You need bad weather.\r\n");
    return;
  }

  int dam = dice (lvl / 2, 8);

  send_to_char ("God's lightning strikes your foes!\r\n");
  act ("$n calls God's lightning to strike $s foes!",
    NULL, NULL, TO_ROOM);

  CharIter c, next;
  for (c = char_list.begin(); c != char_list.end(); c = next) {
    vch = *c;
    next = ++c;
    if (vch->in_room == NULL)
      continue;
    if (vch->in_room == in_room) {
      if (vch != this && (is_npc () ? !vch->is_npc () : vch->is_npc ()))
        damage (this, vch, vch->saves_spell (lvl) ? dam / 2 : dam, sn);
      continue;
    }

    if (vch->in_room->area == in_room->area && vch->is_outside()
      && vch->is_awake ())
      vch->send_to_char ("Lightning flashes in the sky.\r\n");
  }

  return;
}
예제 #5
0
void Character::spell_earthquake (int sn, int lvl, void *vo)
{
  send_to_char ("The earth trembles beneath your feet!\r\n");
  act ("$n makes the earth tremble and shiver.", NULL, NULL, TO_ROOM);

  CharIter c, next;
  for (c = char_list.begin(); c != char_list.end(); c = next) {
    Character* vch = *c;
    next = ++c;
    if (vch->in_room == NULL)
      continue;
    if (vch->in_room == in_room) {
      if (vch != this && (is_npc () ? !vch->is_npc () : vch->is_npc ()))
        damage (this, vch, lvl + dice (2, 8), sn);
      continue;
    }

    if (vch->in_room->area == in_room->area)
      vch->send_to_char ("The earth trembles and shivers.\r\n");
  }

  return;
}
예제 #6
0
void Character::spell_dispel_evil (int sn, int lvl, void *vo)
{
  Character *victim = (Character *) vo;

  if (!is_npc () && is_evil ())
    victim = this;

  if (victim->is_good ()) {
    act ("God protects $N.", NULL, victim, TO_ROOM);
    return;
  }

  if (victim->is_neutral ()) {
    act ("$N does not seem to be affected.", NULL, victim, TO_CHAR);
    return;
  }

  int dam = dice (lvl, 4);
  if (victim->saves_spell (lvl))
    dam /= 2;
  damage (this, victim, dam, sn);
  return;
}
예제 #7
0
파일: melee.cpp 프로젝트: Azrael/Cataclysm
bool player::hit_player(player &p, body_part &bp, int &hitdam, int &hitcut)
{
// TODO: Add bionics and other bonus (e.g. heat drain, shock, etc)
 if (!is_npc() && p.is_npc()) {
  npc *foe = dynamic_cast<npc*>(&p);
  foe->make_angry();
 }
 bool unarmed = unarmed_attack(), bashing = weapon.is_bashing_weapon(),
      cutting = weapon.is_cutting_weapon();
 int hitit = hit_roll() - p.dodge_roll();
 if (hitit < 0) {	// They dodged
  practice(sk_melee, rng(2, 4));
  if (unarmed)
   practice(sk_unarmed, 3);
  if (bashing)
   practice(sk_bashing, 1);
  if (cutting)
   practice(sk_cutting, 2);
  return false;
 }

 if (hitit >= 15)
  bp = bp_eyes;
 else if (hitit >= 12)
  bp = bp_mouth;
 else if (hitit >= 10)
  bp = bp_head;
 else if (hitit >= 6)
  bp = bp_torso;
 else if (hitit >= 2)
  bp = bp_arms;
 else
  bp = bp_legs;
 
 hitdam = base_damage();

 if (unarmed) {// Unarmed bonuses
  hitdam += rng(0, sklevel[sk_unarmed]);
  if (sklevel[sk_unarmed] >= 5)
   hitdam += rng(sklevel[sk_unarmed], 3 * sklevel[sk_unarmed]);
  if (has_trait(PF_TALONS))
   hitcut += 10;
  if (sklevel[sk_unarmed] >= 8 &&
      (one_in(3) || rng(5, 20) < sklevel[sk_unarmed]))
   hitdam *= rng(2, 3);
 }
// Weapon adds (melee_dam / 4) to (melee_dam)
 hitdam += rng(weapon.type->melee_dam / 4, weapon.type->melee_dam);
 if (bashing)
  hitdam += rng(0, sklevel[sk_bashing]) * sqrt(str_cur);

 hitdam += int(pow(1.5, sklevel[sk_melee]));
 hitcut = weapon.type->melee_cut;
 if (hitcut > 0)
  hitcut += int(sklevel[sk_cutting] / 3);
 if (hitdam < 0) hitdam = 0;
 if (hitdam > 0 || hitcut > 0) { // Practicing
  practice(sk_melee, rng(5, 10));
  if (unarmed)
   practice(sk_unarmed, rng(5, 10));
  if (bashing)
   practice(sk_bashing, rng(5, 10));
  if (cutting)
   practice(sk_cutting, rng(5, 10));
 } else { // Less practice if we missed
  practice(sk_melee, rng(2, 5));
  if (unarmed)
   practice(sk_unarmed, 2);
  if (bashing)
   practice(sk_bashing, 2);
  if (cutting)
   practice(sk_cutting, 3);
 }
 return true;
}
예제 #8
0
void player::perform_defensive_technique(
  technique_id technique, game *g, monster *z, player *p,
  body_part &bp_hit, int &side, int &bash_dam, int &cut_dam, int &stab_dam)

{
 int junk;
 bool mon = (z != NULL);
 std::string You = (is_npc() ? name : "You");
 std::string your = (is_npc() ? (male ? "his" : "her") : "your");
 std::string target = (mon ? "the " + z->name() : p->name);
 bool u_see = (!is_npc() || g->u_see(posx, posy, junk));

 switch (technique) {
  case TEC_BLOCK:
  case TEC_BLOCK_LEGS: {
   if (technique == TEC_BLOCK) {
    bp_hit = bp_arms;
    if (hp_cur[hp_arm_l] >= hp_cur[hp_arm_r])
     side = 0;
    else
     side = 1;
   } else { // Blocking with our legs
    bp_hit = bp_legs;
    if (hp_cur[hp_leg_l] >= hp_cur[hp_leg_r])
     side = 0;
    else
     side = 1;
   }
   if (u_see)
    g->add_msg("%s block%s with %s %s.", You.c_str(), (is_npc() ? "s" : ""),
               your.c_str(), body_part_name(bp_hit, side).c_str());
   bash_dam *= .5;
   double reduction = 1.0;
// Special reductions for certain styles
   if (weapon.typeId() == "style_tai_chi")
    reduction -= double(0.08 * double(per_cur - 6));
   if (weapon.typeId() == "style_taekwondo")
    reduction -= double(0.08 * double(str_cur - 6));
   if (reduction > 1.0)
    reduction = 1.0;
   if (reduction < 0.3)
    reduction = 0.3;

   bash_dam *= reduction;
  } break;

  case TEC_WBLOCK_1:
  case TEC_WBLOCK_2:
  case TEC_WBLOCK_3:
// TODO: Cause weapon damage
   bash_dam = 0;
   cut_dam = 0;
   stab_dam = 0;
   if (u_see)
    g->add_msg("%s block%s with %s %s.", You.c_str(), (is_npc() ? "s" : ""),
               your.c_str(), weapon.tname().c_str());

  case TEC_COUNTER:
   break; // Handled elsewhere

  case TEC_DEF_THROW:
   if (u_see)
    g->add_msg("%s throw%s %s!", You.c_str(), (is_npc() ? "s" : ""),
               target.c_str());
   bash_dam = 0;
   cut_dam  = 0;
   stab_dam = 0;
   if (mon) {
    z->add_effect(ME_DOWNED, rng(1, 2));
    z->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1));
   } else {
    p->add_disease(DI_DOWNED, rng(1, 2), g);
    p->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1));
   }
   break;

  case TEC_DEF_DISARM:
   g->m.add_item(p->posx, p->posy, p->remove_weapon());
// Re-roll damage, without our weapon
   bash_dam = p->roll_bash_damage(NULL, false);
   cut_dam  = p->roll_cut_damage(NULL, false);
   stab_dam = p->roll_stab_damage(NULL, false);
   if (u_see)
    g->add_msg("%s disarm%s %s!", You.c_str(), (is_npc() ? "s" : ""),
                                  target.c_str());
   break;

 } // switch (technique)
}
예제 #9
0
void player::perform_technique(technique_id technique, game *g, monster *z,
                               player *p, int &bash_dam, int &cut_dam,
                               int &stab_dam, int &pain)
{
 bool mon = (z != NULL);
 std::string You = (is_npc() ? name : "You");
 std::string target = (mon ? "the " + z->name() :
                       (p->is_npc() ? p->name : "you"));
 std::string s = (is_npc() ? "s" : "");
 int tarx = (mon ? z->posx : p->posx), tary = (mon ? z->posy : p->posy);

 int junk;
 bool u_see = (!is_npc() || g->u_see(posx, posy, junk));

 if (technique == TEC_RAPID) {
  moves += int( attack_speed(*this, false) / 2);
  return;
 }
 if (technique == TEC_BLOCK) {
  bash_dam *= .7;
  return;
 }
// The rest affect our target, and thus depend on z vs. p
 switch (technique) {

 case TEC_SWEEP:
  if (z != NULL && !z->has_flag(MF_FLIES)) {
   z->add_effect(ME_DOWNED, rng(1, 2));
   bash_dam += z->fall_damage();
  } else if (p != NULL && p->weapon.typeId() != "style_judo") {
   p->add_disease(DI_DOWNED, rng(1, 2), g);
   bash_dam += 3;
  }
  break;

 case TEC_PRECISE:
  if (z != NULL)
   z->add_effect(ME_STUNNED, rng(1, 4));
  else if (p != NULL)
   p->add_disease(DI_STUNNED, rng(1, 2), g);
  pain += rng(5, 8);
  break;

 case TEC_BRUTAL:
  if (z != NULL) {
   z->add_effect(ME_STUNNED, 1);
   z->knock_back_from(g, posx, posy);
  } else if (p != NULL) {
   p->add_disease(DI_STUNNED, 1, g);
   p->knock_back_from(g, posy, posy);
  }
  break;

 case TEC_THROW:
// Throws are less predictable than brutal strikes.
// We knock them back from a tile adjacent to us!
  if (z != NULL) {
   z->add_effect(ME_DOWNED, rng(1, 2));
   z->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1));
  } else if (p != NULL) {
   p->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1));
   if (p->weapon.typeId() != "style_judo")
    p->add_disease(DI_DOWNED, rng(1, 2), g);
  }
  break;

 case TEC_WIDE: {
  int count_hit = 0;
  for (int x = posx - 1; x <= posx + 1; x++) {
   for (int y = posy - 1; y <= posy + 1; y++) {
    if (x != tarx || y != tary) { // Don't double-hit our target
     int mondex = g->mon_at(x, y);
     if (mondex != -1 && hit_roll() >= rng(0, 5) + g->z[mondex].dodge_roll()) {
      count_hit++;
      int dam = roll_bash_damage(&(g->z[mondex]), false) +
                roll_cut_damage (&(g->z[mondex]), false);
      g->z[mondex].hurt(dam);
      if (u_see)
       g->add_msg("%s hit%s %s for %d damage!", You.c_str(), s.c_str(),
                                                target.c_str(), dam);
     }
     int npcdex = g->npc_at(x, y);
     if (npcdex != -1 &&
         hit_roll() >= rng(0, 5) + g->active_npc[npcdex].dodge_roll(g)) {
      count_hit++;
      int dam = roll_bash_damage(NULL, false);
      int cut = roll_cut_damage (NULL, false);
      g->active_npc[npcdex].hit(g, bp_legs, 3, dam, cut);
      if (u_see)
       g->add_msg("%s hit%s %s for %d damage!", You.c_str(), s.c_str(),
                  g->active_npc[npcdex].name.c_str(), dam + cut);
     }
    }
   }
  }
  if (!is_npc())
   g->add_msg("%d enemies hit!", count_hit);
 } break;

 case TEC_DISARM:
  g->m.add_item(p->posx, p->posy, p->remove_weapon());
  if (u_see)
   g->add_msg("%s disarm%s %s!", You.c_str(), s.c_str(), target.c_str());
  break;

 } // switch (tech)
}
예제 #10
0
std::vector<special_attack> player::mutation_attacks(monster *z, player *p)
{
 std::vector<special_attack> ret;

 if (z == NULL && p == NULL)
  return ret;

 bool mon = (z != NULL);
 bool is_u = (!is_npc());// Affects how we'll display messages
 std::string You  = (is_u ? "You"  : name);
 std::string Your = (is_u ? "Your" : name + "'s");
 std::string your = (is_u ? "your" : (male ? "his" : "her"));
 std::string target = (mon ? "the " + z->name() : p->name);

 std::stringstream text;

 if (has_trait(PF_FANGS) && !wearing_something_on(bp_mouth) &&
     one_in(20 - dex_cur - skillLevel("unarmed"))) {
  special_attack tmp;
  text << You << " sink" << (is_u ? " " : "s ") << your << " fangs into " <<
          target << "!";
  tmp.text = text.str();
  tmp.stab = 20;
  ret.push_back(tmp);
 }

 if (has_trait(PF_MANDIBLES) && one_in(22 - dex_cur - skillLevel("unarmed"))) {
  special_attack tmp;
  text << You << " slice" << (is_u ? " " : "s ") << target << " with " <<
          your << " mandibles!";
  tmp.text = text.str();
  tmp.cut = 12;
  ret.push_back(tmp);
 }

 if (has_trait(PF_BEAK) && one_in(15 - dex_cur - skillLevel("unarmed"))) {
  special_attack tmp;
  text << You << " peck" << (is_u ? " " : "s ") << target << "!";
  tmp.text = text.str();
  tmp.stab = 15;
  ret.push_back(tmp);
 }

 if (has_trait(PF_HOOVES) && one_in(25 - dex_cur - 2 * skillLevel("unarmed"))) {
  special_attack tmp;
  text << You << " kick" << (is_u ? " " : "s ") << target << " with " <<
          your << " hooves!";
  tmp.text = text.str();
  tmp.bash = str_cur * 3;
  if (tmp.bash > 40)
   tmp.bash = 40;
  ret.push_back(tmp);
 }

 if (has_trait(PF_HORNS) && one_in(20 - dex_cur - skillLevel("unarmed"))) {
  special_attack tmp;
  text << You << " headbutt" << (is_u ? " " : "s ") << target << " with " <<
          your << " horns!";
  tmp.text = text.str();
  tmp.bash = 3;
  tmp.stab = 3;
  ret.push_back(tmp);
 }

 if (has_trait(PF_HORNS_CURLED) && one_in(20 - dex_cur - skillLevel("unarmed"))) {
  special_attack tmp;
  text << You << " headbutt" << (is_u ? " " : "s ") << target << " with " <<
          your << " curled horns!";
  tmp.text = text.str();
  tmp.bash = 14;
  ret.push_back(tmp);
 }

 if (has_trait(PF_HORNS_POINTED) && one_in(22 - dex_cur - skillLevel("unarmed"))){
  special_attack tmp;
  text << You << " stab" << (is_u ? " " : "s ") << target << " with " <<
          your << " pointed horns!";
  tmp.text = text.str();
  tmp.stab = 24;
  ret.push_back(tmp);
 }

 if (has_trait(PF_ANTLERS) && one_in(20 - dex_cur - skillLevel("unarmed"))) {
  special_attack tmp;
  text << You << " butt" << (is_u ? " " : "s ") << target << " with " <<
          your << " antlers!";
  tmp.text = text.str();
  tmp.bash = 4;
  ret.push_back(tmp);
 }

 if (has_trait(PF_TAIL_STING) && one_in(3) && one_in(10 - dex_cur)) {
  special_attack tmp;
  text << You << " sting" << (is_u ? " " : "s ") << target << " with " <<
          your << " tail!";
  tmp.text = text.str();
  tmp.stab = 20;
  ret.push_back(tmp);
 }

 if (has_trait(PF_TAIL_CLUB) && one_in(3) && one_in(10 - dex_cur)) {
  special_attack tmp;
  text << You << " hit" << (is_u ? " " : "s ") << target << " with " <<
          your << " tail!";
  tmp.text = text.str();
  tmp.bash = 18;
  ret.push_back(tmp);
 }

 if (has_trait(PF_ARM_TENTACLES) || has_trait(PF_ARM_TENTACLES_4) ||
     has_trait(PF_ARM_TENTACLES_8)) {
  int num_attacks = 1;
  if (has_trait(PF_ARM_TENTACLES_4))
   num_attacks = 3;
  if (has_trait(PF_ARM_TENTACLES_8))
   num_attacks = 7;
  if (weapon.is_two_handed(this))
   num_attacks--;

  for (int i = 0; i < num_attacks; i++) {
   if (one_in(18 - dex_cur - skillLevel("unarmed"))) {
    special_attack tmp;
    text.str("");
    text << You << " slap" << (is_u ? " " : "s ") << target << " with " <<
            your << " tentacle!";
    tmp.text = text.str();
    tmp.bash = str_cur / 2;
    ret.push_back(tmp);
   }
  }
 }

 return ret;
}
예제 #11
0
void player::melee_special_effects(game *g, monster *z, player *p, bool crit,
                                   int &bash_dam, int &cut_dam, int &stab_dam)
{
 if (z == NULL && p == NULL)
  return;
 bool mon = (z != NULL);
 int junk;
 bool is_u = (!is_npc());
 bool can_see = (is_u || g->u_see(posx, posy, junk));
 std::string You = (is_u ? "You" : name);
 std::string Your = (is_u ? "Your" : name + "'s");
 std::string your = (is_u ? "your" : name + "'s");
 std::string target = (mon ? "the " + z->name() :
                       (p->is_npc() ? p->name : "you"));
 std::string target_possessive = (mon ? "the " + z->name() + "'s" :
                                  (p->is_npc() ? p->name + "'s" : your));
 int tarposx = (mon ? z->posx : p->posx), tarposy = (mon ? z->posy : p->posy);

// Bashing effecs
 if (mon)
  z->moves -= rng(0, bash_dam * 2);
 else
  p->moves -= rng(0, bash_dam * 2);

// Bashing crit
 if (crit && !unarmed_attack()) {
  int turns_stunned = int(bash_dam / 20) + rng(0, int(skillLevel("bashing") / 2));
  if (turns_stunned > 6)
   turns_stunned = 6;
  if (turns_stunned > 0) {
   if (mon)
    z->add_effect(ME_STUNNED, turns_stunned);
   else
    p->add_disease(DI_STUNNED, 1 + turns_stunned / 2, g);
  }
 }

// Stabbing effects
 int stab_moves = rng(stab_dam / 2, stab_dam * 1.5);
 if (crit)
  stab_moves *= 1.5;
 if (stab_moves >= 150) {
  if (can_see)
   g->add_msg("%s force%s the %s to the ground!", You.c_str(),
              (is_u ? "" : "s"), target.c_str());
  if (mon) {
   z->add_effect(ME_DOWNED, 1);
   z->moves -= stab_moves / 2;
  } else {
   p->add_disease(DI_DOWNED, 1, g);
   p->moves -= stab_moves / 2;
  }
 } else if (mon)
  z->moves -= stab_moves;
 else
  p->moves -= stab_moves;

// Bonus attacks!
 bool shock_them = (has_bionic("bio_shock") && power_level >= 2 &&
                    unarmed_attack() && (!mon || !z->has_flag(MF_ELECTRIC)) &&
                    one_in(3));

 bool drain_them = (has_bionic("bio_heat_absorb") && power_level >= 1 &&
                    !is_armed() && (!mon || z->has_flag(MF_WARM)));

 if (drain_them)
  power_level--;
 drain_them &= one_in(2);	// Only works half the time

 if (shock_them) {
  power_level -= 2;
  int shock = rng(2, 5);
  if (mon) {
   z->hurt( shock * rng(1, 3) );
   z->moves -= shock * 180;
   if (can_see)
    g->add_msg("%s shock%s %s!", You.c_str(), (is_u ? "" : "s"),
               target.c_str());
  } else {
   p->hurt(g, bp_torso, 0, shock * rng(1, 3));
   p->moves -= shock * 80;
  }
 }

 if (drain_them) {
  charge_power(rng(0, 4));
  if (can_see)
   g->add_msg("%s drain%s %s body heat!", You.c_str(), (is_u ? "" : "s"),
               target_possessive.c_str());
  if (mon) {
   z->moves -= rng(80, 120);
   z->speed -= rng(4, 6);
  } else
   p->moves -= rng(80, 120);
 }

 bool conductive = !wearing_something_on(bp_hands) && weapon.conductive();

 if (mon && z->has_flag(MF_ELECTRIC) && conductive) {
  hurtall(rng(0, 1));
  moves -= rng(0, 50);
  if (is_u)
   g->add_msg("Contact with the %s shocks you!", z->name().c_str());
 }

// Glass weapons shatter sometimes
 if (weapon.made_of(GLASS) &&
     rng(0, weapon.volume() + 8) < weapon.volume() + str_cur) {
  if (can_see)
   g->add_msg("%s %s shatters!", Your.c_str(), weapon.tname(g).c_str());
  g->sound(posx, posy, 16, "");
// Dump its contents on the ground
  for (int i = 0; i < weapon.contents.size(); i++)
   g->m.add_item(posx, posy, weapon.contents[i]);
  hit(g, bp_arms, 1, 0, rng(0, weapon.volume() * 2));// Take damage
  if (weapon.is_two_handed(this))// Hurt left arm too, if it was big
   hit(g, bp_arms, 0, 0, rng(0, weapon.volume()));
  cut_dam += rng(0, 5 + int(weapon.volume() * 1.5));// Hurt the monster extra
  remove_weapon();
 }

// Getting your weapon stuck
 int cutting_penalty = roll_stuck_penalty(z, stab_dam > cut_dam);
 if (weapon.has_flag(IF_MESSY)) { // e.g. chainsaws
  cutting_penalty /= 6; // Harder to get stuck
  for (int x = tarposx - 1; x <= tarposx + 1; x++) {
   for (int y = tarposy - 1; y <= tarposy + 1; y++) {
    if (!one_in(3)) {
     if (g->m.field_at(x, y).type == fd_blood &&
         g->m.field_at(x, y).density < 3)
      g->m.field_at(x, y).density++;
     else
      g->m.add_field(g, x, y, fd_blood, 1);
    }
   }
  }
 }
 if (!unarmed_attack() && cutting_penalty > dice(str_cur * 2, 20)) {
  if (is_u)
   g->add_msg("Your %s gets stuck in %s, pulling it out of your hands!",
              weapon.tname().c_str(), target.c_str());
  if (mon) {
   if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB))
    z->speed *= .7;
   else
    z->speed *= .85;
   z->add_item(remove_weapon());
  } else
   g->m.add_item(posx, posy, remove_weapon());
 } else {
  if (mon && (cut_dam >= z->hp || stab_dam >= z->hp)) {
   cutting_penalty /= 2;
   cutting_penalty -= rng(skillLevel("cutting"), skillLevel("cutting") * 2 + 2);
  }
  if (cutting_penalty > 0)
   moves -= cutting_penalty;
  if (cutting_penalty >= 50 && is_u)
   g->add_msg("Your %s gets stuck in %s, but you yank it free.",
              weapon.tname().c_str(), target.c_str());
  if (mon && (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)))
   z->speed *= .9;
 }

// Finally, some special effects for martial arts
 if(weapon.typeId() == "style_karate"){
   dodges_left++;
   blocks_left += 2;
 } else if(weapon.typeId() == "style_aikido"){
   bash_dam /= 2;
 } else if(weapon.typeId() == "style_capoeira"){
   add_disease(DI_DODGE_BOOST, 2, g, 2);
 } else if(weapon.typeId() == "style_muay_thai"){
   if ((mon && z->type->size >= MS_LARGE) || (!mon && p->str_max >= 12))
    bash_dam += rng((mon ? z->type->size : (p->str_max - 8) / 4),
                    3 * (mon ? z->type->size : (p->str_max - 8) / 4));
 } else if(weapon.typeId() == "style_tiger"){
   add_disease(DI_DAMAGE_BOOST, 2, g, 2, 10);
 } else if(weapon.typeId() == "style_centipede"){
   add_disease(DI_SPEED_BOOST, 2, g, 4, 40);
 } else if(weapon.typeId() == "style_venom_snake"){
   if (has_disease(DI_VIPER_COMBO)) {
    if (disease_intensity(DI_VIPER_COMBO) == 1) {
     if (is_u)
      g->add_msg("Snakebite!");
     int dambuf = bash_dam;
     bash_dam = stab_dam;
     stab_dam = dambuf;
     add_disease(DI_VIPER_COMBO, 2, g, 1, 2); // Upgrade to Viper Strike
    } else if (disease_intensity(DI_VIPER_COMBO) == 2) {
     if (hp_cur[hp_arm_l] >= hp_max[hp_arm_l] * .75 &&
         hp_cur[hp_arm_r] >= hp_max[hp_arm_r] * .75   ) {
      if (is_u)
       g->add_msg("Viper STRIKE!");
      bash_dam *= 3;
     } else if (is_u)
      g->add_msg("Your injured arms prevent a viper strike!");
     rem_disease(DI_VIPER_COMBO);
    }
   } else if (crit) {
    if (is_u)
     g->add_msg("Tail whip!  Viper Combo Intiated!");
    bash_dam += 5;
    add_disease(DI_VIPER_COMBO, 2, g, 1, 2);
   }
 } else if(weapon.typeId() == "style_scorpion"){
   if (crit) {
    if (!is_npc())
     g->add_msg("Stinger Strike!");
    if (mon) {
     z->add_effect(ME_STUNNED, 3);
     int zposx = z->posx, zposy = z->posy;
     z->knock_back_from(g, posx, posy);
     if (z->posx != zposx || z->posy != zposy)
      z->knock_back_from(g, posx, posy); // Knock a 2nd time if the first worked
    } else {
     p->add_disease(DI_STUNNED, 2, g);
     int pposx = p->posx, pposy = p->posy;
     p->knock_back_from(g, posx, posy);
     if (p->posx != pposx || p->posy != pposy)
      p->knock_back_from(g, posx, posy); // Knock a 2nd time if the first worked
    }
   }
 } else if(weapon.typeId() == "style_zui_quan"){
   dodges_left = 50; // Basically, unlimited.
 }
}
예제 #12
0
void player::consume_effects( const item &food )
{
    if( !food.is_comestible() ) {
        debugmsg( "called player::consume_effects with non-comestible" );
        return;
    }
    const auto &comest = *food.type->comestible;

    const int capacity = stomach_capacity();
    if( has_trait( trait_id( "THRESH_PLANT" ) ) && food.type->can_use( "PLANTBLECH" ) ) {
        // Just keep nutrition capped, to prevent vomiting
        cap_nutrition_thirst( *this, capacity, true, true );
        return;
    }
    if( ( has_trait( trait_id( "HERBIVORE" ) ) || has_trait( trait_id( "RUMINANT" ) ) ) &&
        food.has_any_flag( herbivore_blacklist ) ) {
        // No good can come of this.
        return;
    }

    // Rotten food causes health loss
    const float relative_rot = food.get_relative_rot();
    if( relative_rot > 1.0f && !has_trait( trait_id( "SAPROPHAGE" ) ) &&
        !has_trait( trait_id( "SAPROVORE" ) ) && !has_bionic( bio_digestion ) ) {
        const float rottedness = clamp( 2 * relative_rot - 2.0f, 0.1f, 1.0f );
        // ~-1 health per 1 nutrition at halfway-rotten-away, ~0 at "just got rotten"
        // But always round down
        int h_loss = -rottedness * comest.nutr;
        mod_healthy_mod( h_loss, -200 );
        add_msg( m_debug, "%d health from %0.2f%% rotten food", h_loss, rottedness );
    }

    const auto nutr = nutrition_for( food );
    mod_hunger( -nutr );
    mod_thirst( -comest.quench );
    mod_stomach_food( nutr );
    mod_stomach_water( comest.quench );
    if( comest.healthy != 0 ) {
        // Effectively no cap on health modifiers from food
        mod_healthy_mod( comest.healthy, ( comest.healthy >= 0 ) ? 200 : -200 );
    }

    if( comest.stim != 0 &&
        ( abs( stim ) < ( abs( comest.stim ) * 3 ) ||
          sgn( stim ) != sgn( comest.stim ) ) ) {
        if( comest.stim < 0 ) {
            stim = std::max( comest.stim * 3, stim + comest.stim );
        } else {
            stim = std::min( comest.stim * 3, stim + comest.stim );
        }
    }
    add_addiction( comest.add, comest.addict );
    if( addiction_craving( comest.add ) != MORALE_NULL ) {
        rem_morale( addiction_craving( comest.add ) );
    }

    // Morale is in minutes
    int morale_time = HOURS( 2 ) / MINUTES( 1 );
    if( food.has_flag( "HOT" ) && food.has_flag( "EATEN_HOT" ) ) {
        morale_time = HOURS( 3 ) / MINUTES( 1 );
        int clamped_nutr = std::max( 5, std::min( 20, nutr / 10 ) );
        add_morale( MORALE_FOOD_HOT, clamped_nutr, 20, morale_time, morale_time / 2 );
    }

    std::pair<int, int> fun = fun_for( food );
    if( fun.first < 0 ) {
        add_morale( MORALE_FOOD_BAD, fun.first, fun.second, morale_time, morale_time / 2, false,
                    food.type );
    } else if( fun.first > 0 ) {
        add_morale( MORALE_FOOD_GOOD, fun.first, fun.second, morale_time, morale_time / 2, false,
                    food.type );
    }

    const bool hibernate = has_active_mutation( trait_id( "HIBERNATE" ) );
    if( hibernate ) {
        if( ( nutr > 0 && get_hunger() < -60 ) || ( comest.quench > 0 && get_thirst() < -60 ) ) {
            //Tell the player what's going on
            add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) );
            if( one_in( 2 ) ) {
                //50% chance of the food tiring you
                mod_fatigue( nutr );
            }
        }
        if( ( nutr > 0 && get_hunger() < -200 ) || ( comest.quench > 0 && get_thirst() < -200 ) ) {
            //Hibernation should cut burn to 60/day
            add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) );
            if( one_in( 2 ) ) {
                //And another 50%, intended cumulative
                mod_fatigue( nutr );
            }
        }

        if( ( nutr > 0 && get_hunger() < -400 ) || ( comest.quench > 0 && get_thirst() < -400 ) ) {
            add_msg_if_player(
                _( "Mmm.  You can still fit some more in...but maybe you should get comfortable and sleep." ) );
            if( !one_in( 3 ) ) {
                //Third check, this one at 66%
                mod_fatigue( nutr );
            }
        }
        if( ( nutr > 0 && get_hunger() < -600 ) || ( comest.quench > 0 && get_thirst() < -600 ) ) {
            add_msg_if_player( _( "That filled a hole!  Time for bed..." ) );
            // At this point, you're done.  Schlaf gut.
            mod_fatigue( nutr );
        }
    }

    // Moved here and changed a bit - it was too complex
    // Incredibly minor stuff like this shouldn't require complexity
    if( !is_npc() && has_trait( trait_id( "SLIMESPAWNER" ) ) &&
        ( get_hunger() < capacity + 40 || get_thirst() < capacity + 40 ) ) {
        add_msg_if_player( m_mixed,
                           _( "You feel as though you're going to split open!  In a good way?" ) );
        mod_pain( 5 );
        std::vector<tripoint> valid;
        for( const tripoint &dest : g->m.points_in_radius( pos(), 1 ) ) {
            if( g->is_empty( dest ) ) {
                valid.push_back( dest );
            }
        }
        int numslime = 1;
        for( int i = 0; i < numslime && !valid.empty(); i++ ) {
            const tripoint target = random_entry_removed( valid );
            if( monster *const slime = g->summon_mon( mon_player_blob, target ) ) {
                slime->friendly = -1;
            }
        }
        mod_hunger( 40 );
        mod_thirst( 40 );
        //~slimespawns have *small voices* which may be the Nice equivalent
        //~of the Rat King's ALL CAPS invective.  Probably shared-brain telepathy.
        add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) );
    }

    // Last thing that happens before capping hunger
    if( get_hunger() < capacity && has_trait( trait_id( "EATHEALTH" ) ) ) {
        int excess_food = capacity - get_hunger();
        add_msg_player_or_npc( _( "You feel the %s filling you out." ),
                               _( "<npcname> looks better after eating the %s." ),
                               food.tname().c_str() );
        // Guaranteed 1 HP healing, no matter what.  You're welcome.  ;-)
        if( excess_food <= 5 ) {
            healall( 1 );
        } else {
            // Straight conversion, except it's divided amongst all your body parts.
            healall( excess_food /= 5 );
        }

        // Note: We want this here to prevent "you can't finish this" messages
        set_hunger( capacity );
    }

    cap_nutrition_thirst( *this, capacity, nutr > 0, comest.quench > 0 );
}
예제 #13
0
void player::consume_effects( item &food, bool rotten )
{
    if( !food.is_food() ) {
        debugmsg( "called player::consume_effects with non-comestible" );
        return;
    }
    const auto comest = food.type->comestible.get();

    const int capacity = stomach_capacity();
    if( has_trait( "THRESH_PLANT" ) && food.type->can_use( "PLANTBLECH" ) ) {
        // Just keep nutrition capped, to prevent vomiting
        cap_nutrition_thirst( *this, capacity, true, true );
        return;
    }
    if( ( has_trait( "HERBIVORE" ) || has_trait( "RUMINANT" ) ) &&
        food.has_any_flag( herbivore_blacklist ) ) {
        // No good can come of this.
        return;
    }
    float factor = 1.0f;
    float hunger_factor = 1.0f;
    bool unhealthy_allowed = true;

    if( has_trait( "GIZZARD" ) ) {
        factor *= 0.6f;
    }

    if( has_trait( "CARNIVORE" ) && food.has_flag( "CARNIVORE_OK" ) ) {
        // At least partially edible
        if( food.has_any_flag( carnivore_blacklist ) ) {
            // Other things are in it, we only get partial benefits
            add_msg_if_player( _( "You pick out the edible parts and throw away the rest." ) );
            factor *= 0.5f;
        } else {
            // Carnivores don't get unhealthy off pure meat diets
            unhealthy_allowed = false;
        }
    }
    // Saprophages get full nutrition from rotting food
    if( rotten && !has_trait( "SAPROPHAGE" ) ) {
        // everyone else only gets a portion of the nutrition
        hunger_factor *= rng_float( 0, 1 );
        // and takes a health penalty if they aren't adapted
        if( !has_trait( "SAPROVORE" ) && !has_bionic( "bio_digestion" ) ) {
            mod_healthy_mod( -30, -200 );
        }
    }

    // Bio-digestion gives extra nutrition
    if( has_bionic( "bio_digestion" ) ) {
        hunger_factor += rng_float( 0, 1 );
    }

    const auto nutr = nutrition_for( food.type );
    mod_hunger( -nutr * factor * hunger_factor );
    mod_thirst( -comest->quench * factor );
    mod_stomach_food( nutr * factor * hunger_factor );
    mod_stomach_water( comest->quench * factor );
    if( unhealthy_allowed || comest->healthy > 0 ) {
        // Effectively no cap on health modifiers from food
        mod_healthy_mod( comest->healthy, ( comest->healthy >= 0 ) ? 200 : -200 );
    }

    if( comest->stim != 0 &&
        ( abs( stim ) < ( abs( comest->stim ) * 3 ) ||
          sgn( stim ) != sgn( comest->stim ) ) ) {
        if( comest->stim < 0 ) {
            stim = std::max( comest->stim * 3, stim + comest->stim );
        } else {
            stim = std::min( comest->stim * 3, stim + comest->stim );
        }
    }
    add_addiction( comest->add, comest->addict );
    if( addiction_craving( comest->add ) != MORALE_NULL ) {
        rem_morale( addiction_craving( comest->add ) );
    }
    if( food.has_flag( "HOT" ) && food.has_flag( "EATEN_HOT" ) ) {
        add_morale( MORALE_FOOD_HOT, 5, 10 );
    }
    auto fun = comest->fun;
    if( food.has_flag( "COLD" ) && food.has_flag( "EATEN_COLD" ) && fun > 0 ) {
        if( fun > 0 ) {
            add_morale( MORALE_FOOD_GOOD, fun * 3, fun * 3, 60, 30, false, food.type );
        } else {
            fun = 1;
        }
    }

    const bool gourmand = has_trait( "GOURMAND" );
    const bool hibernate = has_active_mutation( "HIBERNATE" );
    if( gourmand ) {
        if( fun < -2 ) {
            add_morale( MORALE_FOOD_BAD, fun * 0.5, fun, 60, 30, false, food.type );
        } else if( fun > 0 ) {
            add_morale( MORALE_FOOD_GOOD, fun * 3, fun * 6, 60, 30, false, food.type );
        }
    } else if( fun < 0 ) {
        add_morale( MORALE_FOOD_BAD, fun, fun * 6, 60, 30, false, food.type );
    } else if( fun > 0 ) {
        add_morale( MORALE_FOOD_GOOD, fun, fun * 4, 60, 30, false, food.type );
    }

    if( hibernate ) {
        if( ( nutr > 0 && get_hunger() < -60 ) || ( comest->quench > 0 && get_thirst() < -60 ) ) {
            //Tell the player what's going on
            add_msg_if_player( _( "You gorge yourself, preparing to hibernate." ) );
            if( one_in( 2 ) ) {
                //50% chance of the food tiring you
                mod_fatigue( nutr );
            }
        }
        if( ( nutr > 0 && get_hunger() < -200 ) || ( comest->quench > 0 && get_thirst() < -200 ) ) {
            //Hibernation should cut burn to 60/day
            add_msg_if_player( _( "You feel stocked for a day or two. Got your bed all ready and secured?" ) );
            if( one_in( 2 ) ) {
                //And another 50%, intended cumulative
                mod_fatigue( nutr );
            }
        }

        if( ( nutr > 0 && get_hunger() < -400 ) || ( comest->quench > 0 && get_thirst() < -400 ) ) {
            add_msg_if_player(
                _( "Mmm.  You can still fit some more in...but maybe you should get comfortable and sleep." ) );
            if( !one_in( 3 ) ) {
                //Third check, this one at 66%
                mod_fatigue( nutr );
            }
        }
        if( ( nutr > 0 && get_hunger() < -600 ) || ( comest->quench > 0 && get_thirst() < -600 ) ) {
            add_msg_if_player( _( "That filled a hole!  Time for bed..." ) );
            // At this point, you're done.  Schlaf gut.
            mod_fatigue( nutr );
        }
    }

    // Moved here and changed a bit - it was too complex
    // Incredibly minor stuff like this shouldn't require complexity
    if( !is_npc() && has_trait( "SLIMESPAWNER" ) &&
        ( get_hunger() < capacity + 40 || get_thirst() < capacity + 40 ) ) {
        add_msg_if_player( m_mixed,
                           _( "You feel as though you're going to split open!  In a good way?" ) );
        mod_pain( 5 );
        std::vector<tripoint> valid;
        for( const tripoint &dest : g->m.points_in_radius( pos(), 1 ) ) {
            if( g->is_empty( dest ) ) {
                valid.push_back( dest );
            }
        }
        int numslime = 1;
        for( int i = 0; i < numslime && !valid.empty(); i++ ) {
            const tripoint target = random_entry_removed( valid );
            if( g->summon_mon( mon_player_blob, target ) ) {
                monster *slime = g->monster_at( target );
                slime->friendly = -1;
            }
        }
        mod_hunger( 40 );
        mod_thirst( 40 );
        //~slimespawns have *small voices* which may be the Nice equivalent
        //~of the Rat King's ALL CAPS invective.  Probably shared-brain telepathy.
        add_msg_if_player( m_good, _( "hey, you look like me! let's work together!" ) );
    }

    // Last thing that happens before capping hunger
    if( get_hunger() < capacity && has_trait( "EATHEALTH" ) ) {
        int excess_food = capacity - get_hunger();
        add_msg_player_or_npc( _( "You feel the %s filling you out." ),
                               _( "<npcname> looks better after eating the %s." ),
                               food.tname().c_str() );
        // Guaranteed 1 HP healing, no matter what.  You're welcome.  ;-)
        if( excess_food <= 5 ) {
            healall( 1 );
        } else {
            // Straight conversion, except it's divided amongst all your body parts.
            healall( excess_food /= 5 );
        }

        // Note: We want this here to prevent "you can't finish this" messages
        set_hunger( capacity );
    }

    cap_nutrition_thirst( *this, capacity, nutr > 0, comest->quench > 0 );
}
예제 #14
0
edible_rating player::can_eat( const item &food, bool interactive, bool force ) const
{
    if( is_npc() || force ) {
        // Just to be sure
        interactive = false;
    }

    const std::string &itname = food.tname();
    // Helper to avoid ton of `if( interactive )`
    // Prints if interactive is true, does nothing otherwise
    const auto maybe_print = [interactive, &itname]
    ( game_message_type type, const char *str ) {
        if( interactive ) {
            add_msg( type, str, itname.c_str() );
        }
    };
    // As above, but for queries
    // Asks if interactive and not force
    // Always true if force
    // Never true otherwise
    const auto maybe_query = [force, interactive, &itname, this]( const char *str ) {
        if( force ) {
            return true;
        } else if( !interactive ) {
            return false;
        }

        return query_yn( str, itname.c_str() );
    };

    const auto comest = food.type->comestible.get();
    if( comest == nullptr ) {
        maybe_print( m_info, _( "That doesn't look edible." ) );
        return INEDIBLE;
    }

    if( comest->tool != "null" ) {
        bool has = has_amount( comest->tool, 1 );
        if( item::count_by_charges( comest->tool ) ) {
            has = has_charges( comest->tool, 1 );
        }
        if( !has ) {
            if( interactive ) {
                add_msg_if_player( m_info, _( "You need a %s to consume that!" ),
                                   item::nname( comest->tool ).c_str() );
            }
            return NO_TOOL;
        }
    }

    if( is_underwater() ) {
        maybe_print( m_info, _( "You can't do that while underwater." ) );
        return INEDIBLE;
    }
    // For all those folks who loved eating marloss berries.  D:< mwuhahaha
    if( has_trait( "M_DEPENDENT" ) && food.type->id != "mycus_fruit" ) {
        maybe_print( m_info, _( "We can't eat that.  It's not right for us." ) );
        return INEDIBLE_MUTATION;
    }

    const bool drinkable = comest->comesttype == "DRINK" && !food.has_flag( "USE_EAT_VERB" );
    // Here's why PROBOSCIS is such a negative trait.
    if( has_trait( "PROBOSCIS" ) && !drinkable ) {
        maybe_print( m_info, _( "Ugh, you can't drink that!" ) );
        return INEDIBLE_MUTATION;
    }

    int capacity = stomach_capacity();

    // TODO: Move this cache to a structure and pass it around
    // to speed up checking entire inventory for edibles
    const bool gourmand = has_trait( "GOURMAND" );
    const bool hibernate = has_active_mutation( "HIBERNATE" );
    const bool eathealth = has_trait( "EATHEALTH" );
    const bool slimespawner = has_trait( "SLIMESPAWNER" );
    const int nutr = nutrition_for( food.type );
    const int quench = comest->quench;
    bool spoiled = food.rotten();

    const int temp_hunger = get_hunger() - nutr;
    const int temp_thirst = get_thirst() - quench;

    const bool overeating = get_hunger() < 0 && nutr >= 5 && !gourmand && !eathealth && !slimespawner &&
                            !hibernate;

    if( interactive && hibernate &&
        ( get_hunger() >= -60 && get_thirst() >= -60 ) &&
        ( temp_hunger < -60 || temp_thirst < -60 ) ) {
        if( !maybe_query( _( "You're adequately fueled. Prepare for hibernation?" ) ) ) {
            return TOO_FULL;
        }
    }

    const bool carnivore = has_trait( "CARNIVORE" );
    if( carnivore && nutr > 0 &&
        food.has_any_flag( carnivore_blacklist ) && !food.has_flag( "CARNIVORE_OK" ) ) {
        maybe_print( m_info, _( "Eww.  Inedible plant stuff!" ) );
        return INEDIBLE_MUTATION;
    }

    if( ( has_trait( "HERBIVORE" ) || has_trait( "RUMINANT" ) ) &&
        food.has_any_flag( herbivore_blacklist ) ) {
        // Like non-cannibal, but more strict!
        maybe_print( m_info, _( "The thought of eating that makes you feel sick.  You decide not to." ) );
        return INEDIBLE_MUTATION;
    }

    if( food.has_flag( "CANNIBALISM" ) ) {
        if( !has_trait_flag( "CANNIBAL" ) &&
            !maybe_query( _( "The thought of eating that makes you feel sick.  Really do it?" ) ) ) {
            return CANNIBALISM;
        }
    }

    if( is_allergic( food ) &&
        !maybe_query( _( "Really eat that %s?  Your stomach won't be happy." ) ) ) {
        return ALLERGY;
    }

    if( carnivore && food.has_flag( "ALLERGEN_JUNK" ) && !food.has_flag( "CARNIVORE_OK" ) &&
        !maybe_query( _( "Really eat that %s?  Your stomach won't be happy." ) ) ) {
        return ALLERGY;
    }

    const bool saprophage = has_trait( "SAPROPHAGE" );
    // The item is solid food
    const bool chew = comest->comesttype == "FOOD" || food.has_flag( "USE_EAT_VERB" );
    if( spoiled ) {
        if( !saprophage && !has_trait( "SAPROVORE" ) &&
            !maybe_query( _( "This %s smells awful!  Eat it?" ) ) ) {
            return ROTTEN;
        }
    } else if( saprophage && chew && !food.has_flag( "FERTILIZER" ) &&
               !maybe_query( _( "Really eat that %s?  Your stomach won't be happy." ) ) ) {
        // Note: We're allowing all non-solid "food". This includes drugs
        // Hardcoding fertilizer for now - should be a separate flag later
        //~ No, we don't eat "rotten" food. We eat properly aged food, like a normal person.
        //~ Semantic difference, but greatly facilitates people being proud of their character.
        maybe_print( m_info, _( "It's too fresh, let it age a little first." ) );
        return ROTTEN;
    }

    // Print at most one of those
    bool overfull = false;
    if( overeating ) {
        overfull = !maybe_query( _( "You're full.  Force yourself to eat?" ) );
    } else if( ( ( nutr > 0 && temp_hunger < capacity ) ||
                 ( comest->quench > 0 && temp_thirst < capacity ) ) &&
               !eathealth && !slimespawner ) {
        overfull = !maybe_query( _( "You will not be able to finish it all.  Consume it?" ) );
    }

    if( overfull ) {
        return TOO_FULL;
    }

    // All checks ended, it's edible (or we're pretending it is)
    return EDIBLE;
}