/* ************************************************************************ * File: fight.c EmpireMUD AD 1.0 * * Usage: Combat system * * * * All rights reserved. See license.doc for complete information. * * * * Code base by Paul Clarke. EmpireMUD Project, a tbgMUD Production. * * Based upon CircleMUD 3.0, beta patch level 17, by Jeremy Elson. * * * * Copyright (C) 1993, 94 by the Trustees of the Johns Hopkins University * * CircleMUD is based on DikuMUD, Copyright (C) 1990, 1991. * ************************************************************************ */ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "handler.h" #include "interpreter.h" #include "db.h" #include "skills.h" #include "vnums.h" #include "empire.h" /* Structures */ Creature combat_list = NULL; /* head of l-list of fighting chars */ Creature next_combat_list = NULL; /* External structures */ extern struct message_list fight_messages[MAX_MESSAGES]; extern int rev_dir[]; /* External procedures */ extern char *fread_action(FILE * fl, int nr); ACMD(do_flee); /* Weapon attack texts */ struct attack_hit_type attack_hit_text[] = { { "hit", "hits" }, /* 0 */ { "slash", "slashes" }, { "slice", "slices" }, { "jab", "jabs" }, { "stab", "stabs" }, { "pound", "pounds" }, /* 5 */ { "hammer", "hammers" }, { "whip", "whips" }, { "jab", "jabs" }, { "bite", "bites" }, { "claw", "claws" }, /* 10 */ { "kick", "kicks" }, { "burn", "burns" }, { "claw", "claws" }, { "slash", "slashes" }, { "hit", "hits" } }; #define IS_WEAPON(type) (((type) >= TYPE_RESERVED) && ((type) < TYPE_SUFFERING)) /* The Fight related routines */ /* * can_fight() * Controls player-killing. It sends its own message, so use it * appropriately. Also, it stops people from fighting, so they * aren't spammed. */ bool can_fight(Creature ch, Creature victim) { extern struct time_info_data *real_time_passed(time_t t2, time_t t1); struct empire_political_data *emp_pol; extern bitvector_t pk_ok; struct time_info_data playing_time, playing_time2; if (!ch || !victim) { syslog(0, TRUE, "SYSERR: can_fight() called without ch or victim"); if (ch && FIGHTING(ch)) stop_fighting(ch); return FALSE; } if (ch == victim) return TRUE; /* charmies don't attack people */ if (AFF_FLAGGED(ch, AFF_CHARM) && !IS_NPC(victim)) return FALSE; /* Already fighting */ if (FIGHTING(victim) == ch) return TRUE; if (AFF_FLAGGED(victim, AFF_NO_ATTACK) || AFF_FLAGGED(ch, AFF_NO_ATTACK)) return FALSE; if (DSC_FLAGGED(victim, DSC_MAJESTY) && ww_dice(GET_COURAGE(ch), GET_CHARISMA(victim) + GET_INTIMIDATION(victim)) < 1) return FALSE; if (GET_RIDING(ch) == victim || GET_RIDDEN_BY(ch) == victim) { msg_to_char(ch, "You can't attack your mount!\r\n"); return FALSE; } if (IS_NPC(ch) || IS_NPC(victim)) return TRUE; /* calculate play time: 1 day minimum to pk */ playing_time = *real_time_passed((time(0) - ch->player.time.logon) + ch->player.time.played, 0); playing_time2 = *real_time_passed((time(0) - victim->player.time.logon) + victim->player.time.played, 0); // let em kill eachother if (playing_time.day > 0 || playing_time2.day > 0){ return TRUE; } else { return TRUE; } if (DSC_FLAGGED(victim, DSC_BITUMENOUS_FLESH)) return FALSE; if (SECT(victim->in_room) == SECT_TOWER_OF_SOULS) { msg_to_char(ch, "You can't attack anyone here!\r\n"); return FALSE; } if ((IS_GOD(ch) && !IS_GOD(victim)) || (IS_GOD(victim) && !IS_GOD(ch))) return FALSE; if (IS_IMMORTAL(ch) || IS_IMMORTAL(victim)) return FALSE; if ((IS_HARDCORE(ch) && !IS_HARDCORE(victim)) || (IS_HARDCORE(victim) && !IS_HARDCORE(ch))) return FALSE; if (IS_SET(pk_ok, PK_FULL)) return TRUE; if (IS_SET(pk_ok, PK_WAR)) { if ((emp_pol = find_relation(real_empire(GET_LOYALTY(ch)), real_empire(GET_LOYALTY(victim))))) if (IS_SET(emp_pol->type, DIPL_WAR)) return TRUE; /* owned territory */ if (CAN_USE_ROOM(ch, ch->in_room, 1) && !CAN_USE_ROOM(victim, victim->in_room, 0)) return TRUE; } /* Oops... we shouldn't be fighting */ if (FIGHTING(ch) == victim) stop_fighting(ch); if (FIGHTING(victim) == ch) stop_fighting(victim); return FALSE; } void appear(Creature ch) { REMOVE_BIT(AFF_FLAGS(ch), AFF_HIDE | AFF_INVISIBLE); REMOVE_BIT(DSC_FLAGS(ch), DSC_UNSEEN_PRESENCE); if (GET_LEVEL(ch) < LVL_GOD) { act("$n slowly fades into existence.", FALSE, ch, 0, 0, TO_ROOM); msg_to_char(ch, "You fade back into view.\r\n"); } else act("You feel a strange presence as $n appears, seemingly from nowhere.", FALSE, ch, 0, 0, TO_ROOM); } void load_messages(void) { FILE *fl; int i, type; struct message_type *messages; char chk[128]; if (!(fl = fopen(MESS_FILE, "r"))) { log("SYSERR: Error reading combat message file %s: %s", MESS_FILE, strerror(errno)); exit(1); } for (i = 0; i < MAX_MESSAGES; i++) { fight_messages[i].a_type = 0; fight_messages[i].number_of_attacks = 0; fight_messages[i].msg = 0; } fgets(chk, 128, fl); while (!feof(fl) && (*chk == '\n' || *chk == '*')) fgets(chk, 128, fl); while (*chk == 'M') { fgets(chk, 128, fl); sscanf(chk, " %d\n", &type); for (i = 0; (i < MAX_MESSAGES) && (fight_messages[i].a_type != type) && (fight_messages[i].a_type); i++); if (i >= MAX_MESSAGES) { log("SYSERR: Too many combat messages. Increase MAX_MESSAGES and recompile."); exit(1); } CREATE(messages, struct message_type, 1); fight_messages[i].number_of_attacks++; fight_messages[i].a_type = type; messages->next = fight_messages[i].msg; fight_messages[i].msg = messages; messages->die_msg.attacker_msg = fread_action(fl, i); messages->die_msg.victim_msg = fread_action(fl, i); messages->die_msg.room_msg = fread_action(fl, i); messages->miss_msg.attacker_msg = fread_action(fl, i); messages->miss_msg.victim_msg = fread_action(fl, i); messages->miss_msg.room_msg = fread_action(fl, i); messages->hit_msg.attacker_msg = fread_action(fl, i); messages->hit_msg.victim_msg = fread_action(fl, i); messages->hit_msg.room_msg = fread_action(fl, i); messages->god_msg.attacker_msg = fread_action(fl, i); messages->god_msg.victim_msg = fread_action(fl, i); messages->god_msg.room_msg = fread_action(fl, i); fgets(chk, 128, fl); while (!feof(fl) && (*chk == '\n' || *chk == '*')) fgets(chk, 128, fl); } fclose(fl); } void update_pos(Creature victim) { if (GET_DAMAGE(victim) < 7 && GET_POS(victim) > POS_STUNNED) return; else if (GET_DAMAGE(victim) == 7) GET_POS(victim) = POS_INCAP; else if (GET_DAMAGE(victim) >= 8) GET_POS(victim) = POS_DEAD; else GET_POS(victim) = POS_STUNNED; } /* start one char fighting another (yes, it is horrible, I know... ) */ void set_fighting(Creature ch, Creature vict, byte mode) { if (ch == vict) return; if (FIGHTING(ch)) return; ch->next_fighting = combat_list; combat_list = ch; FIGHTING(ch) = vict; FIGHT_MODE(ch) = mode; if (mode == FMODE_WAITING) FIGHT_WAIT(ch) = MAX(1, MIN(3, 10 - GET_DEXTERITY(ch))); GET_POS(ch) = POS_FIGHTING; } /* remove a char from the list of fighting chars */ void stop_fighting(Creature ch) { Creature temp; if (ch == next_combat_list) next_combat_list = ch->next_fighting; REMOVE_FROM_LIST(ch, combat_list, next_fighting); ch->next_fighting = NULL; FIGHTING(ch) = NULL; GET_POS(ch) = POS_STANDING; update_pos(ch); } Object make_corpse(Creature ch) { Object corpse, o, next_o; int i; corpse = read_object(o_CORPSE, VIRTUAL); GET_OBJ_WEIGHT(corpse) = 100 + IS_CARRYING_W(ch); GET_OBJ_VAL(corpse, 0) = IS_NPC(ch) ? GET_MOB_VNUM(ch) : -1; GET_OBJ_VAL(corpse, 1) = IS_NPC(ch) ? MOB_SKIN(ch) : 100; GET_OBJ_VAL(corpse, 2) = 0; /* transfer character's inventory to the corpse */ corpse->contains = ch->carrying; for (o = corpse->contains; o != NULL; o = o->next_content) o->in_obj = corpse; object_list_new_owner(corpse, NULL); /* check for large objects and put them in the room */ if (ch->in_room != NOWHERE) for (o = corpse->contains; o != NULL; o = next_o) { next_o = o->next_content; if (IS_OBJ_STAT(o, ITEM_LARGE)) obj_to_room(o, ch->in_room); } /* transfer character's equipment to the corpse */ for (i = 0; i < NUM_WEARS; i++) if (GET_EQ(ch, i)) obj_to_obj(unequip_char(ch, i), corpse); ch->carrying = NULL; IS_CARRYING_N(ch) = 0; IS_CARRYING_W(ch) = 0; return corpse; } void death_cry(Creature ch) { int door; act("Your blood freezes as you hear $n's death cry.", FALSE, ch, 0, 0, TO_ROOM); if ((SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_INSIDE) && (IS_COMPLETE(ch->in_room) || ALWAYS_CLOSED(ch->in_room))) { for (door = 0; door < NUM_OF_DIRS; door++) if (CAN_GO(ch, door)) send_to_room("Your blood freezes as you hear someone's death cry.\r\n", world[ch->in_room].dir_option[door]->to_room); } for (door = 0; door < NUM_2D_DIRS; door++) send_to_room("Your blood freezes as you hear someone's death cry.\r\n", real_shift(ch->in_room, shift_dir[door][0], shift_dir[door][1])); } void die(Creature ch) { void delete_empire(int rnum); extern const char *hardcore_slain_msg; int emp; /* Free restore */ if (!IS_NPC(ch)) { if (GET_COND(ch, FULL) > 0) GET_COND(ch, FULL) = 0; if (GET_COND(ch, DRUNK) > 0) GET_COND(ch, DRUNK) = 0; if (GET_COND(ch, THIRST) > 0) GET_COND(ch, THIRST) = 0; if (GET_COND(ch, TIRED) > 0) GET_COND(ch, TIRED) = 0; } INJURY_FLAGS(ch) = 0; /* Restore Health */ GET_DAMAGE(ch) = 0; GET_AGG_DAMAGE(ch) = 0; GET_BLOOD(ch) = GET_MAX_BLOOD(ch); if (FIGHTING(ch)) stop_fighting(ch); while (ch->affected) affect_remove(ch, ch->affected); perform_morph(ch, MORPH_NONE); death_cry(ch); if (MOB_FLAGGED(ch, MOB_UNDEAD)) { while (ch->carrying) obj_to_room(ch->carrying, ch->in_room); } else obj_to_room(make_corpse(ch), ch->in_room); if (!IS_NPC(ch) && IS_HARDCORE(ch) && !IS_GOD(ch) && !IS_IMMORTAL(ch)) { SET_BIT(PLR_FLAGS(ch), PLR_SLAIN); msg_to_char(ch, hardcore_slain_msg); /* Check that empire */ if ((emp = real_empire(GET_LOYALTY(ch))) != -1) { GET_LOYALTY(ch) = 0; GET_RANK(ch) = 0; empire[emp].members--; if (empire[emp].members == 0) delete_empire(emp); } } save_char(ch, NOWHERE); extract_char(ch); } char *replace_string(const char *str, const char *weapon_singular, const char *weapon_plural) { static char buf[256]; char *cp = buf; for (; *str; str++) { if (*str == '#') { switch (*(++str)) { case 'W': for (; *weapon_plural; *(cp++) = *(weapon_plural++)); break; case 'w': for (; *weapon_singular; *(cp++) = *(weapon_singular++)); break; default: *(cp++) = '#'; break; } } else *(cp++) = *str; *cp = 0; } return (buf); } /* message for doing damage with a weapon */ void dam_message(int dam, Creature ch, Creature victim, int w_type) { char *buf; int msgnum; static struct dam_weapon_type { const char *to_room; const char *to_char; const char *to_victim; } dam_weapons[] = { /* use #w for singular (i.e. "slash") and #W for plural (i.e. "slashes") */ { "$n tries to #w $N, but misses.", /* 0: 0 */ "You try to #w $N, but miss.", "$n tries to #w you, but misses." }, { "$n tickles $N as $e #W $M.", /* 1 */ "You tickle $N as you #w $M.", "$n tickles you as $e #W you." }, { "$n barely #W $N.", /* 2 */ "You barely #w $N.", "$n barely #W you." }, { "$n #W $N.", /* 3 */ "You #w $N.", "$n #W you." }, { "$n #W $N hard.", /* 4 */ "You #w $N hard.", "$n #W you hard." }, { "$n #W $N very hard.", /* 5 */ "You #w $N very hard.", "$n #W you very hard." }, { "$n #W $N extremely hard.", /* 6 */ "You #w $N extremely hard.", "$n #W you extremely hard." }, { "$n massacres $N to small fragments with $s #w.", /* 7 */ "You massacre $N to small fragments with your #w.", "$n massacres you to small fragments with $s #w." }, { "$n OBLITERATES $N with $s deadly #w!!", /* 8 */ "You OBLITERATE $N with your deadly #w!!", "$n OBLITERATES you with $s deadly #w!!" } }; if (dam < 8) msgnum = dam; else msgnum = 8; /* damage message to onlookers */ buf = replace_string(dam_weapons[msgnum].to_room, attack_hit_text[w_type].singular, attack_hit_text[w_type].plural); act(buf, FALSE, ch, NULL, victim, TO_NOTVICT); /* damage message to damager */ if (ch->desc) { send_to_char("&3", ch); buf = replace_string(dam_weapons[msgnum].to_char, attack_hit_text[w_type].singular, attack_hit_text[w_type].plural); act(buf, FALSE, ch, NULL, victim, TO_CHAR); send_to_char("&0", ch); } /* damage message to damagee */ if (victim->desc) { send_to_char("&1", victim); buf = replace_string(dam_weapons[msgnum].to_victim, attack_hit_text[w_type].singular, attack_hit_text[w_type].plural); act(buf, FALSE, ch, NULL, victim, TO_VICT | TO_SLEEP); send_to_char("&0", victim); } } /* * message for doing damage with a spell or skill * C3.0: Also used for weapon damage on miss and death blows */ int skill_message(int dam, Creature ch, Creature vict, int attacktype) { int i, j, nr; struct message_type *msg; Object weap = GET_EQ(ch, WEAR_WIELD); for (i = 0; i < MAX_MESSAGES; i++) { if (fight_messages[i].a_type == attacktype) { nr = dice(1, fight_messages[i].number_of_attacks); for (j = 1, msg = fight_messages[i].msg; (j < nr) && msg; j++) msg = msg->next; if (!IS_NPC(vict) && (IS_IMMORTAL(vict) || (IS_GOD(vict) && !IS_GOD(ch)))) { act(msg->god_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR); act(msg->god_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT); act(msg->god_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT); } else if (dam != 0) { if (GET_POS(vict) == POS_DEAD) { send_to_char("&3", ch); act(msg->die_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR); send_to_char("&0", ch); send_to_char("&1", vict); act(msg->die_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP); send_to_char("&0", vict); act(msg->die_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT); } else { send_to_char("&3", ch); act(msg->hit_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR); send_to_char("&0", ch); send_to_char("&1", vict); act(msg->hit_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP); send_to_char("&0", vict); act(msg->hit_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT); } } else if (ch != vict) { /* Dam == 0 */ send_to_char("&3", ch); act(msg->miss_msg.attacker_msg, FALSE, ch, weap, vict, TO_CHAR); send_to_char("&0", ch); send_to_char("&1", vict); act(msg->miss_msg.victim_msg, FALSE, ch, weap, vict, TO_VICT | TO_SLEEP); send_to_char("&0", vict); act(msg->miss_msg.room_msg, FALSE, ch, weap, vict, TO_NOTVICT); } return (1); } } return (0); } void death_log(Creature ch, Creature killer, int type) { char *msg; char output[MAX_STRING_LENGTH]; /* Follow the pattern. If there IS a killer, i needs to be 1 */ bool i = 0; switch (type) { case ATTACK_GUARD_TOWER: msg = "%s has been killed by a guard tower at (%d, %d)"; break; case TYPE_SUFFERING: msg = "%s has died at (%d, %d)"; break; case ATTACK_EXECUTE: msg = "%s has been executed by %s at (%d, %d)"; i++; break; default: msg = "%s has been killed at (%d, %d)"; break; } if (ch == killer && i) { free(msg); syslog(0, TRUE, "Unusual error in death_log(): ch == killer, but combat attack type passed"); msg = "%s has been killed at (%d, %d)"; i = 0; } if (i) { strcpy(buf, PERS(killer, killer, 1)); sprintf(output, msg, PERS(ch, ch, 1), buf, X_COORD(ch->in_room), Y_COORD(ch->in_room)); } else sprintf(output, msg, PERS(ch, ch, 1), X_COORD(ch->in_room), Y_COORD(ch->in_room)); mortlog(0, output); syslog(0, TRUE, "DEATH: %s", output); } void perform_execute(Creature ch, Creature victim, int attacktype, int damtype) { char *to_char, *to_vict, *to_room; bool ok = FALSE, decap = FALSE, vampire = FALSE, werewolf = FALSE; bool revert = TRUE; Creature m; Object weapon; /* stop_fighting() is split around here to help with exp */ /* Probably sent here by damage() */ if (ch == victim) ok = TRUE; /* Sent here by damage() */ if (attacktype > NUM_ATTACK_TYPES || (IS_NPC(ch) && (MOB_TYPE(ch) == MTYPE_HUMAN || MOB_FLAGGED(ch, MOB_AGGRESSIVE))) || IS_NPC(victim) || (!IS_NPC(ch) && PRF_FLAGGED(ch, PRF_AUTOKILL))) ok = TRUE; /* Sent here by do_execute() */ if (attacktype == -1) ok = TRUE; if (!ok) { if (FIGHTING(victim)) stop_fighting(victim); if (FIGHTING(ch) == victim) stop_fighting(ch); /* knock 'em out */ GET_DAMAGE(victim) = 7; GET_POS(victim) = POS_INCAP; act("$n is knocked unconscious!", FALSE, victim, 0, 0, TO_ROOM); msg_to_char(victim, "You are knocked unconscious.\r\n"); return; } /* We will TRY to find a slicing/slashing weapon */ weapon = GET_EQ(ch, WEAR_WIELD); if ((!weapon || (GET_OBJ_VAL(weapon, 2) != TYPE_SLASH && GET_OBJ_VAL(weapon, 2) != TYPE_SLICE)) && GET_EQ(ch, WEAR_HOLD)) { weapon = GET_EQ(ch, WEAR_HOLD); if (GET_OBJ_TYPE(weapon) != ITEM_WEAPON || (GET_OBJ_VAL(weapon, 2) != TYPE_SLASH && GET_OBJ_VAL(weapon, 2) != TYPE_SLICE)) weapon = GET_EQ(ch, WEAR_WIELD); } /* We have now got a slicing/slashing weapon OR the weapon in the right hand */ if (weapon && (GET_OBJ_VAL(weapon, 2) == TYPE_SLASH || GET_OBJ_VAL(weapon, 2) == TYPE_SLICE)) decap = TRUE; /* This is a special case, to help hide vampires */ if (IS_VAMPIRE(victim) && !decap && damtype != DAM_AGGRAVATED) { vampire = TRUE; revert = FALSE; } /* This case will help with werewolves */ if (IS_WEREWOLF(victim)) switch (damtype) { case DAM_BASHING: revert = FALSE; case DAM_LETHAL: werewolf = TRUE; break; } /* Now, we can do it and have handled knock-outs.. */ switch (weapon ? GET_OBJ_VAL(weapon, 2) : TYPE_HIT) { case TYPE_STING: case TYPE_STAB: to_char = "You jab $M through the heart!"; to_vict = "$n jabs you through the heart!"; to_room = "$n jabs $N through the heart!"; break; case TYPE_WHIP: to_char = "You wrap your weapon around $S neck and strangle $M!"; to_vict = "$n wraps $s weapon around your neck and strangles you!"; to_room = "$n wraps $s weapon around $N and strangles $M!"; break; case TYPE_SLASH: case TYPE_SLICE: to_char = "You swing your weapon hard and decapitate $M!"; to_vict = "$n swings $s weapon hard, and that's the last thing you see!"; to_room = "$n swings $s weapon hard, decapitating $N!"; break; case TYPE_POUND: to_char = "You hit $M hard upside the head with your weapon!"; to_vict = "$n hits you hard upside the head with $s weapon... You black out.\r\n"; to_room = "$n hits $N hard upside the head with $s weapon!"; break; default: to_char = "You grab $M by the head and twist sharply!"; to_vict = "$n grabs you by the head and twists sharply!"; to_room = "$n grabs $N by the head and twists sharply!"; break; } if (ch != victim) { act(to_char, FALSE, ch, 0, victim, TO_CHAR); act(to_vict, FALSE, ch, 0, victim, TO_VICT); act(to_room, FALSE, ch, 0, victim, TO_NOTVICT); } if (revert && !IS_NPC(victim) && GET_MORPH(victim) != GET_BREED_FORM(victim)) { sprintf(buf, "%s reverts into $n!", PERS(victim, victim, 0)); perform_morph(victim, GET_BREED_FORM(victim)); act(buf, TRUE, victim, 0, 0, TO_ROOM); } act("$n is dead! R.I.P.", FALSE, victim, 0, 0, TO_ROOM); if (vampire || werewolf) { if (FIGHTING(victim)) stop_fighting(victim); if (FIGHTING(ch) == victim) stop_fighting(ch); GET_DAMAGE(victim) = 7; GET_POS(victim) = POS_INCAP; if (vampire) msg_to_char(victim, "You are knocked unconscious, though this is not Final Death...\r\n"); else if (werewolf) msg_to_char(victim, "You are knocked unconscious!\r\n"); } else { /* logs */ if (!IS_NPC(victim)) { if (ch != victim) death_log(victim, ch, ATTACK_EXECUTE); else death_log(victim, ch, TYPE_SUFFERING); } /* Actually killed him! */ if (victim != ch && !IS_NPC(victim)) { for (m = world[victim->in_room].people; m; m = m->next_in_room) if (FIGHTING(m) == victim && !IS_NPC(m)) stop_fighting(m); if (!IS_NPC(ch)) gain_experience(victim, -5); } if (FIGHTING(victim)) stop_fighting(victim); if (FIGHTING(ch) == victim) stop_fighting(ch); msg_to_char(victim, "You are dead! Sorry...\r\n"); if (!IS_NPC(victim) && !IS_NPC(ch)) { if (ch == victim) { if (attacktype == ATTACK_GUARD_TOWER) add_lore(ch, LORE_TOWER_DEATH, 0); else add_lore(ch, LORE_DEATH, 0); } else { add_lore(ch, LORE_PLAYER_KILL, GET_IDNUM(victim)); add_lore(victim, LORE_PLAYER_DEATH, GET_IDNUM(ch)); } } die(victim); } } /* * < 0 Victim died. * = 0 No damage. * > 0 How much damage done. */ int damage(Creature ch, Creature victim, int dam, int attacktype, byte damtype) { byte soak_difficulty = 6; if (GET_POS(victim) <= POS_DEAD) { log("SYSERR: Attempt to damage corpse '%s' in room #%d by '%s'.", GET_NAME(victim), GET_ROOM_VNUM(IN_ROOM(victim)), GET_NAME(ch)); die(victim); return (-1); /* -je, 7/7/92 */ } /* You can't damage an immortal! */ if (!IS_NPC(victim) && (IS_IMMORTAL(victim) || (IS_GOD(victim) && !IS_GOD(ch)))) dam = 0; if (!can_fight(ch, victim)) return 0; /* Only damage to self (sun) still hurts */ if (AFF_FLAGGED(victim, AFF_IMMUNE_PHYSICAL) && !(attacktype == ATTACK_SUNBURN || attacktype == TYPE_FIRE)) dam = 0; if (victim != ch) { /* Start the attacker fighting the victim */ if (GET_POS(ch) > POS_STUNNED && (FIGHTING(ch) == NULL)) set_fighting(ch, victim, FMODE_MELEE); /* Start the victim fighting the attacker */ if (GET_POS(victim) > POS_STUNNED && (FIGHTING(victim) == NULL)) set_fighting(victim, ch, FMODE_MELEE); } /* If you attack a pet, it hates your guts */ if (victim->master == ch) stop_follower(victim); /* If the attacker is invisible, he becomes visible */ if (SHOULD_APPEAR(ch)) appear(ch); /* Roll soak: */ /* Humans can soak bashing damage at 6, lethal at 8 */ if (IS_HUMAN(ch) && (!IS_WEREWOLF(ch) || (GET_MORPH(ch) == GET_BREED_FORM(ch) && GET_MORPH(ch) != MORPH_CRINOS)) && damtype >= DAM_LETHAL) soak_difficulty = 8; /* Aggravated damage is ONLY soaked with Fortitude */ if (damtype != DAM_AGGRAVATED) dam -= MAX(0, ww_dice(GET_STAMINA(victim) + GET_SOAK(victim) + GET_FORTITUDE(victim), soak_difficulty)); else if (IS_WEREWOLF(victim) && (GET_MORPH(victim) != GET_BREED_FORM(victim) || GET_MORPH(victim) == MORPH_CRINOS)) dam -= MAX(0, ww_dice(GET_STAMINA(victim) + GET_FORTITUDE(victim), soak_difficulty)); else if (GET_FORTITUDE(victim) >= 1) dam -= MAX(0, ww_dice(GET_FORTITUDE(victim), soak_difficulty)); /* Bashing damage is halved against vampires, AFTER soak */ if (damtype == DAM_BASHING && IS_VAMPIRE(ch)) dam /= 2; /* Minimum damage of 0.. can't do negative */ dam = MAX(0, dam); /* Add Damage */ GET_DAMAGE(victim) += dam; if (damtype == DAM_AGGRAVATED) GET_AGG_DAMAGE(victim) += dam; /* Error checking */ GET_AGG_DAMAGE(victim) = MIN(GET_AGG_DAMAGE(victim), GET_DAMAGE(victim)); /* Blood-stealing tongue of the asp */ if (dam > 0 && attacktype == TYPE_SERPENTIS_TONGUE && IS_VAMPIRE(ch) && GET_BLOOD(ch) < GET_MAX_BLOOD(ch) && GET_BLOOD(victim) > 0) { if (GET_BLOOD(victim) > 0) GET_BLOOD(victim) -= 1; if (GET_BLOOD(ch) < GET_MAX_BLOOD(ch)) GET_BLOOD(ch) += 1; } update_pos(victim); /* * skill_message sends a message from the messages file in lib/misc. * dam_message just sends a generic "You hit $n extremely hard.". * skill_message is preferable to dam_message because it is more * descriptive. * * If we are _not_ attacking with a weapon (i.e. a spell), always use * skill_message. If we are attacking with a weapon: If this is a miss or a * death blow, send a skill_message if one exists; if not, default to a * dam_message. Otherwise, always send a dam_message. */ if (!IS_WEAPON(attacktype)) skill_message(dam, ch, victim, attacktype); else { if (dam == 0 || ch == victim || (GET_POS(victim) == POS_DEAD && !(!IS_NPC(ch) && !IS_NPC(victim) && !PRF_FLAGGED(ch, PRF_AUTOKILL)))) { if (!skill_message(dam, ch, victim, attacktype)) dam_message(dam, ch, victim, attacktype); } else dam_message(dam, ch, victim, attacktype); } /* Use send_to_char -- act() doesn't send message if you are DEAD. */ switch (GET_POS(victim)) { case POS_MORTALLYW: act("$n is mortally wounded, and will die soon, if not aided.", TRUE, victim, 0, 0, TO_ROOM); send_to_char("You are mortally wounded, and will die soon, if not aided.\r\n", victim); break; case POS_INCAP: act("$n is incapacitated and will slowly die, if not aided.", TRUE, victim, 0, 0, TO_ROOM); send_to_char("You are incapacitated an will slowly die, if not aided.\r\n", victim); break; case POS_STUNNED: act("$n is stunned, but will probably regain consciousness again.", TRUE, victim, 0, 0, TO_ROOM); send_to_char("You're stunned, but will probably regain consciousness again.\r\n", victim); break; case POS_DEAD: /* These messages will be handled in perform_execute */ break; default: /* >= POSITION SLEEPING */ if (dam > 2) send_to_char("&1That really did HURT!&0\r\n", victim); if (GET_DAMAGE(victim) >= 6) msg_to_char(victim, "&1You wish that your wounds would stop BLEEDING so much!&0\r\n"); break; } /* Help out poor linkless people who are attacked */ if (!IS_NPC(victim) && !(victim->desc) && GET_POS(victim) > POS_STUNNED) do_flee(victim, NULL, 0, 0); /* stop someone from fighting if they're stunned or worse */ if ((GET_POS(victim) <= POS_STUNNED) && (FIGHTING(victim) != NULL)) stop_fighting(victim); /* Uh oh. Victim died. */ if (GET_POS(victim) == POS_DEAD) { perform_execute(ch, victim, attacktype, damtype); return -1; } return dam; } int hit(Creature ch, Creature victim) { extern const int dam_type[]; Object wielded = GET_EQ(ch, WEAR_WIELD); int w_type, to_hit, to_dodge = 0, dam; byte damtype; if (!victim || !ch) return -1; /* Do some sanity checking, in case someone flees, etc. */ if (ch->in_room != victim->in_room) { if (FIGHTING(ch) && FIGHTING(ch) == victim) stop_fighting(ch); return -1; } /* Find the weapon type (for display purposes only) */ if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON) w_type = GET_OBJ_VAL(wielded, 2); else { if (DSC_FLAGGED(ch, DSC_TALONS_OF_THE_BEAST)) w_type = TYPE_PROTEAN_CLAWS; else if (DSC_FLAGGED(ch, DSC_TONGUE_OF_THE_ASP)) w_type = TYPE_SERPENTIS_TONGUE; else if (IS_NPC(ch) && (MOB_ATTACK_TYPE(ch) != 0)) w_type = MOB_ATTACK_TYPE(ch); else if (!IS_NPC(ch) && GET_MORPH(ch) != MORPH_NONE) w_type = get_morph_attack_type(ch); else w_type = TYPE_HIT; } /* to_hit is based on dexterity + melee/brawl */ if (wielded) to_hit = ww_dice(GET_DEXTERITY(ch) + GET_MELEE(ch), 6 + GET_BLOCK(victim)); else to_hit = ww_dice(GET_DEXTERITY(ch) + GET_BRAWL(ch), 6 + GET_BLOCK(victim)); if (AWAKE(victim) && CAN_SEE(victim, ch)) to_dodge = ww_dice(GET_DEXTERITY(victim) + GET_DODGE(victim), 8); /* decide whether this is a hit or a miss */ if (to_hit BOTCHED) return -1; else if (to_hit <= to_dodge) { /* the attacker missed the victim */ return damage(ch, victim, 0, w_type, DAM_BASHING); } else { /* okay, we know the guy has been hit. now calculate damage. */ /* Melee attacks are based upon strength.. */ dam = GET_STRENGTH(ch); if (wielded && GET_OBJ_TYPE(wielded) == ITEM_WEAPON) { /* Add weapon-based damage if a weapon is being wielded */ dam += GET_OBJ_VAL(wielded, 1); } else if (IS_NPC(ch)) dam = MOB_DAMAGE(ch); if (w_type == TYPE_PROTEAN_CLAWS) dam += 2; dam += to_hit - to_dodge - 1; dam = ww_dice(dam, 6); dam += GET_POTENCE(ch); damtype = dam_type[w_type]; if (wielded && GET_OBJ_VAL(wielded, 0)) { damtype = DAM_AGGRAVATED; if (GET_OBJ_VAL(wielded, 0) > 0) GET_OBJ_VAL(wielded, 0) -= 1; } dam = MAX(0, dam); /* Fortitude 6 lets us shatter weapons */ if (wielded && DSC_FLAGGED(victim, DSC_ARMOR_OF_VITALITY) && GET_BLOOD(victim) > 1) { GET_BLOOD(victim) -= 1; if (ww_dice(GET_FORTITUDE(victim), 7) > 0) { act("You strike $N with $p, which shatters against $S flesh!", FALSE, ch, wielded, victim, TO_CHAR); act("$n strikes $N with $p, which shatters against $S flesh!", FALSE, ch, wielded, victim, TO_NOTVICT); act("$n strikes you with $p, which shatters against your flesh!", FALSE, ch, wielded, victim, TO_VICT); extract_obj(wielded); return 1; } } return damage(ch, victim, dam, w_type, damtype); } } void perform_violence_melee(Creature ch) { Object main_weapon, second_weapon; int attacks; for (attacks = 0; attacks < MAX(1 + ATTACK_BONUS(ch), number(1, 2)); attacks++) { main_weapon = GET_EQ(ch, WEAR_WIELD); second_weapon = GET_EQ(ch, WEAR_HOLD); if (second_weapon && GET_OBJ_TYPE(second_weapon) != ITEM_WEAPON) second_weapon = NULL; if (!IS_NPC(ch) || !second_weapon || number(0, 3)) { if (hit(ch, FIGHTING(ch)) < 0) return; } else { GET_EQ(ch, WEAR_WIELD) = second_weapon; if (hit(ch, FIGHTING(ch)) < 0) { GET_EQ(ch, WEAR_WIELD) = main_weapon; return; } GET_EQ(ch, WEAR_WIELD) = main_weapon; } } } void perform_violence_missile(Creature ch) { Object arrow, bow, quiver; int attacks, dam = 0; int to_hit, to_dodge = 0; byte dam_type = DAM_LETHAL; if (!(bow = GET_EQ(ch, WEAR_HOLD)) || GET_OBJ_TYPE(bow) != ITEM_MISSILE_WEAPON) { msg_to_char(ch, "You don't have a ranged weapon to shoot with!\r\n"); if (FIGHT_MODE(FIGHTING(ch)) == FMODE_MISSILE) { FIGHT_MODE(ch) = FMODE_WAITING; FIGHT_WAIT(ch) = 2; } else FIGHT_MODE(ch) = FMODE_MELEE; return; } if (!(quiver = GET_EQ(ch, WEAR_QUIVER)) || GET_OBJ_TYPE(quiver) != ITEM_CONTAINER) { msg_to_char(ch, "You need a quiver full of arrows to shoot!\r\n"); if (FIGHT_MODE(FIGHTING(ch)) == FMODE_MISSILE) { FIGHT_MODE(ch) = FMODE_WAITING; FIGHT_WAIT(ch) = 2; } else FIGHT_MODE(ch) = FMODE_MELEE; return; } for (attacks = 0; attacks < GET_OBJ_VAL(bow, 0); attacks++) { for (arrow = quiver->contains; arrow; arrow = quiver->next_content) if (GET_OBJ_TYPE(arrow) == ITEM_ARROW && GET_OBJ_VAL(arrow, 2) == GET_OBJ_VAL(bow, 2)) break; if (!arrow) { msg_to_char(ch, "You don't have anything that you can shoot!\r\n"); if (FIGHT_MODE(FIGHTING(ch)) == FMODE_MISSILE) { FIGHT_MODE(ch) = FMODE_WAITING; FIGHT_WAIT(ch) = 2; } else FIGHT_MODE(ch) = FMODE_MELEE; return; } /* to_hit is based upon dexterity + archery, difficulty 7 */ to_hit = ww_dice(GET_DEXTERITY(ch) + GET_ARCHERY(ch), 7); /* to_dodge is based upon dexterity + dodge, difficulty 6 */ if (AWAKE(FIGHTING(ch)) && CAN_SEE(FIGHTING(ch), ch)) to_dodge = ww_dice(GET_DEXTERITY(FIGHTING(ch)) + GET_DODGE(FIGHTING(ch)), 8); /* Damage is based ONLY on the weapons, not on strength */ if (to_hit > to_dodge) { dam = GET_OBJ_VAL(bow, 1) + GET_OBJ_VAL(arrow, 1) + to_hit - to_dodge; dam = ww_dice(dam, 6); } if (GET_OBJ_VAL(arrow, 0)) dam_type = DAM_AGGRAVATED; extract_obj(arrow); if (to_dodge >= to_hit) damage(ch, FIGHTING(ch), 0, ATTACK_ARROW, dam_type); else if (damage(ch, FIGHTING(ch), dam, ATTACK_ARROW, dam_type) < 0) return; } } /* control the fights going on. Called every 2 seconds from comm.c. */ void perform_violence(void) { void diag_char_to_char(Creature i, Creature ch); Creature ch, vict; for (ch = combat_list; ch; ch = next_combat_list) { next_combat_list = ch->next_fighting; if (FIGHTING(ch) == NULL || ch->in_room != FIGHTING(ch)->in_room) { stop_fighting(ch); continue; } if (!IS_NPC(ch) && !IS_NPC(FIGHTING(ch))) { GET_QUIT_TIMER(ch) = 45; GET_QUIT_TIMER(FIGHTING(ch)) = 45; } /* Auto-assist */ if (AFF_FLAGGED(ch, AFF_PARTY) && FIGHTING(ch)) for (vict = world[ch->in_room].people; vict; vict = vict->next_in_room) if (AFF_FLAGGED(vict, AFF_PARTY) && ((vict->master ? vict->master : vict) == (ch->master ? ch->master : ch)) && FIGHTING(ch) != vict && !FIGHTING(vict)) { act("You jump to $N's aide!", FALSE, vict, 0, ch, TO_CHAR); act("$n jumps to your aide!", FALSE, vict, 0, ch, TO_VICT); act("$n jumps to $N's aide!", FALSE, vict, 0, ch, TO_NOTVICT); set_fighting(vict, FIGHTING(ch), FMODE_WAITING); } if (IS_NPC(ch)) { if (GET_MOB_WAIT(ch) > 0) { GET_MOB_WAIT(ch) -= PULSE_VIOLENCE; continue; } GET_MOB_WAIT(ch) = 0; if (GET_POS(ch) < POS_FIGHTING) { GET_POS(ch) = POS_FIGHTING; act("$n scrambles to $s feet!", TRUE, ch, 0, 0, TO_ROOM); } } if (GET_POS(ch) < POS_FIGHTING) { send_to_char("You can't fight while sitting!!\r\n", ch); continue; } /* These functions tend to kill people, so they must be last in the loop. */ switch (FIGHT_MODE(ch)) { case FMODE_MELEE: perform_violence_melee(ch); break; case FMODE_MISSILE: perform_violence_missile(ch); break; case FMODE_WAITING: if (IS_NPC(ch) && MOB_TYPE(ch) == MTYPE_ANIMAL && !number(0, 10)) do_flee(ch, "", 0, 0); if (FIGHTING(ch) && ch->in_room == FIGHTING(ch)->in_room) { act("You run toward $N!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR); act("$n runs toward you!", FALSE, ch, 0, FIGHTING(ch), TO_VICT); if ((FIGHT_WAIT(ch)--) <= 0 || FIGHT_MODE(FIGHTING(ch)) != FMODE_MISSILE) { act("You engage $M in melee combat!", FALSE, ch, 0, FIGHTING(ch), TO_CHAR); act("$e engages you in melee combat!", FALSE, ch, 0, FIGHTING(ch), TO_VICT); FIGHT_MODE(ch) = FMODE_MELEE; /* Only change modes if he's fighting us, too */ if (FIGHTING(FIGHTING(ch)) == ch) FIGHT_MODE(FIGHTING(ch)) = FMODE_MELEE; } } break; default: FIGHT_MODE(ch) = FMODE_MELEE; break; } } /* Diagnose them all */ for (ch = combat_list; ch; ch = ch->next_fighting) if (ch->desc && FIGHTING(ch) && FIGHTING(ch)->in_room == ch->in_room) diag_char_to_char(FIGHTING(ch), ch); } ACMD(do_assist) { Creature helpee, opponent; if (FIGHTING(ch)) { send_to_char("You're already fighting! How can you assist someone else?\r\n", ch); return; } one_argument(argument, arg); if (!*arg) send_to_char("Whom do you wish to assist?\r\n", ch); else if (!(helpee = get_char_vis(ch, arg, FIND_CHAR_ROOM))) send_to_char(NOPERSON, ch); else if (helpee == ch) send_to_char("You can't help yourself any more than this!\r\n", ch); else { /* * Hit the same enemy the person you're helping is. */ if (FIGHTING(helpee)) opponent = FIGHTING(helpee); else for (opponent = world[ch->in_room].people; opponent && (FIGHTING(opponent) != helpee); opponent = opponent->next_in_room); if (!opponent) act("But nobody is fighting $M!", FALSE, ch, 0, helpee, TO_CHAR); else if (!CAN_SEE(ch, opponent)) act("You can't see who is fighting $M!", FALSE, ch, 0, helpee, TO_CHAR); else if (FIGHT_MODE(opponent) != FMODE_MELEE) msg_to_char(ch, "You can't attack until they've engaged in melee combat.\r\n"); else if (can_fight(ch, opponent)) { send_to_char("You join the fight!\r\n", ch); act("$N assists you!", 0, helpee, 0, ch, TO_CHAR); act("$n assists $N.", FALSE, ch, 0, helpee, TO_NOTVICT); hit(ch, opponent); } else act("You can't attack $N!", FALSE, ch, 0, opponent, TO_CHAR); } } ACMD(do_hit) { Creature vict; one_argument(argument, arg); if (!*arg) send_to_char("Hit who?\r\n", ch); else if (!(vict = get_char_vis(ch, arg, FIND_CHAR_ROOM))) send_to_char("They don't seem to be here.\r\n", ch); else if (vict == ch) { send_to_char("You hit yourself...OUCH!.\r\n", ch); act("$n hits $mself, and says OUCH!", FALSE, ch, 0, vict, TO_ROOM); } else if (AFF_FLAGGED(ch, AFF_CHARM) && (ch->master == vict)) act("$N is just such a good friend, you simply can't hit $M.", FALSE, ch, 0, vict, TO_CHAR); else if (vict == FIGHTING(ch) && FIGHT_MODE(ch) == FMODE_MISSILE) { if (FIGHT_MODE(vict) == FMODE_MISSILE) { act("You run at $M!", FALSE, ch, 0, vict, TO_CHAR); FIGHT_MODE(ch) = FMODE_WAITING; FIGHT_WAIT(ch) = 2; WAIT_STATE(ch, PULSE_VIOLENCE + 2); } else FIGHT_MODE(ch) = FMODE_MELEE; } else if (can_fight(ch, vict)) { if (AFF_FLAGGED(ch, AFF_CHARM) && !IS_NPC(ch->master) && !IS_NPC(vict)) return; if (FIGHTING(ch) || vict == FIGHTING(ch) || GET_POS(ch) < POS_STANDING) msg_to_char(ch, "You do the best that you can!\r\n"); else if (FIGHTING(vict) && FIGHT_MODE(vict) == FMODE_MISSILE) { set_fighting(ch, vict, FMODE_WAITING); act("You run at $M!", FALSE, ch, 0, vict, TO_CHAR); WAIT_STATE(ch, PULSE_VIOLENCE + 2); } else { hit(ch, vict); WAIT_STATE(ch, PULSE_VIOLENCE + 2); } } else act("You can't attack $N!", FALSE, ch, 0, vict, TO_CHAR); } ACMD(do_shoot) { Creature vict; one_argument(argument, arg); if (!*arg) msg_to_char(ch, "Shoot whom?\r\n"); else if (!(vict= get_char_vis(ch, arg, FIND_CHAR_ROOM))) msg_to_char(ch, NOPERSON); else if (vict == ch) msg_to_char(ch, "Shooting yourself in the foot will do you no good now.\r\n"); else if (AFF_FLAGGED(ch, AFF_CHARM) && ch->master == vict) act("$N is just such a good friend, you simply can't hit $M.", FALSE, ch, 0, vict, TO_CHAR); else if (!GET_EQ(ch, WEAR_HOLD) || GET_OBJ_TYPE(GET_EQ(ch, WEAR_HOLD)) != ITEM_MISSILE_WEAPON) msg_to_char(ch, "You don't have anything to shoot!\r\n"); else if (FIGHTING(ch)) msg_to_char(ch, "You're already fighting for your life!\r\n"); else if (can_fight(ch, vict)) { if (AFF_FLAGGED(ch, AFF_CHARM) && !IS_NPC(ch->master) && !IS_NPC(vict)) return; msg_to_char(ch, "You take aim.\r\n"); act("$n takes aim.", TRUE, ch, 0, 0, TO_ROOM); set_fighting(ch, vict, FMODE_MISSILE); if (!FIGHTING(vict) && GET_POS(vict) == POS_STANDING) { if (GET_EQ(vict, WEAR_HOLD) && GET_OBJ_TYPE(GET_EQ(vict, WEAR_HOLD)) == ITEM_MISSILE_WEAPON) set_fighting(vict, ch, FMODE_MISSILE); else set_fighting(vict, ch, FMODE_WAITING); } WAIT_STATE(ch, PULSE_VIOLENCE + 2); } else act("You can't shoot $N!", FALSE, ch, 0, vict, TO_CHAR); } ACMD(do_slay) { Creature vict; one_argument(argument, arg); if (!*arg) send_to_char("Slay whom?\r\n", ch); else { if (!(vict = get_char_vis(ch, arg, FIND_CHAR_ROOM))) send_to_char("They aren't here.\r\n", ch); else if (ch == vict) send_to_char("Your mother would be so sad.. :(\r\n", ch); else { act("You chop $M to pieces! Ah! The blood!", FALSE, ch, 0, vict, TO_CHAR); act("$N chops you to pieces!", FALSE, vict, 0, ch, TO_CHAR); act("$n brutally slays $N!", FALSE, ch, 0, vict, TO_NOTVICT); die(vict); } } } ACMD(do_order) { char name[MAX_INPUT_LENGTH], message[MAX_INPUT_LENGTH]; bool found = FALSE; room_rnum org_room; Creature vict; struct follow_type *k; half_chop(argument, name, message); if (!*name || !*message) send_to_char("Order who to do what?\r\n", ch); else if (!(vict = get_char_vis(ch, name, FIND_CHAR_ROOM)) && !is_abbrev(name, "followers")) send_to_char("That person isn't here.\r\n", ch); else if (ch == vict) send_to_char("You obviously suffer from skitzofrenia.\r\n", ch); else { if (AFF_FLAGGED(ch, AFF_CHARM)) { send_to_char("Your superior would not aprove of you giving orders.\r\n", ch); return; } if (vict) { sprintf(buf, "$N orders you to '%s'", message); act(buf, FALSE, vict, 0, ch, TO_CHAR); act("$n gives $N an order.", FALSE, ch, 0, vict, TO_ROOM); if ((vict->master != ch) || (!AFF_FLAGGED(vict, AFF_CHARM) && !DSC_FLAGGED(vict, DSC_ENTRANCEMENT))) act("$n has an indifferent look.", FALSE, vict, 0, 0, TO_ROOM); else { send_to_char(OK, ch); command_interpreter(vict, message); } } else { /* This is order "followers" */ sprintf(buf, "$n issues the order '%s'.", message); act(buf, FALSE, ch, 0, vict, TO_ROOM); org_room = ch->in_room; for (k = ch->followers; k; k = k->next) { if (org_room == k->follower->in_room) if (AFF_FLAGGED(k->follower, AFF_CHARM) || DSC_FLAGGED(k->follower, DSC_ENTRANCEMENT)) { found = TRUE; command_interpreter(k->follower, message); } } if (found) send_to_char(OK, ch); else send_to_char("Nobody here is a loyal subject of yours!\r\n", ch); } } } ACMD(do_flee) { extern int perform_move(Creature ch, int dir, int need_specials_check, byte mode); int i, attempt, to_room; Creature was_fighting; if (GET_POS(ch) < POS_FIGHTING) { send_to_char("You are in pretty bad shape, unable to flee!\r\n", ch); return; } for (i = 0; i < NUM_2D_DIRS; i++) { attempt = number(0, NUM_2D_DIRS - 1); /* Select a random direction, not up/down */ to_room = real_shift(ch->in_room, shift_dir[attempt][0], shift_dir[attempt][1]); if (((SECT(ch->in_room) == SECT_INSIDE || SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_BUILDING) && CAN_GO(ch, attempt)) || (((SECT(to_room) != SECT_MOUNTAIN && SECT(to_room) != SECT_OCEAN && SECT(to_room) != SECT_RIVER) || GET_RIDING(ch)) && SECT(to_room) != SECT_BUILDING && SECT(to_room) != SECT_MULTI)) { act("$n panics, and attempts to flee!", TRUE, ch, 0, 0, TO_ROOM); was_fighting = FIGHTING(ch); if (perform_move(ch, attempt, TRUE, 0)) send_to_char("You flee head over heels.\r\n", ch); else { act("$n tries to flee, but can't!", TRUE, ch, 0, 0, TO_ROOM); send_to_char("PANIC! You couldn't escape!\r\n", ch); } return; } } send_to_char("PANIC! You couldn't escape!\r\n", ch); } ACMD(do_bash) { Creature vict; byte to_hit, to_dodge = 0, dam; one_argument(argument, arg); if (IS_NPC(ch)) { send_to_char("You have no idea how.\r\n", ch); return; } if (!GET_EQ(ch, WEAR_WIELD)) { send_to_char("You need to wield a weapon to make it a success.\r\n", ch); return; } if (!(vict = get_char_vis(ch, arg, FIND_CHAR_ROOM))) { if (FIGHTING(ch) && ch->in_room == FIGHTING(ch)->in_room) vict = FIGHTING(ch); else { send_to_char("Bash who?\r\n", ch); return; } } if (vict == ch) { send_to_char("Aren't we funny today...\r\n", ch); return; } if (FIGHT_MODE(vict) == FMODE_MISSILE || FIGHT_MODE(ch) == FMODE_MISSILE) { msg_to_char(ch, "You aren't close enough.\r\n"); return; } if (!can_fight(ch, vict)) { act("You can't attack $N!", FALSE, ch, 0, vict, TO_CHAR); return; } /* to_hit is based on dexterity + brawl */ to_hit = ww_dice(GET_DEXTERITY(ch) + GET_BRAWL(ch), 6 + GET_BLOCK(vict)); if (AWAKE(vict) && CAN_SEE(vict, ch)) to_dodge = ww_dice(GET_DEXTERITY(vict) + GET_DODGE(vict), 8); if (AFF_FLAGGED(vict, AFF_IMMUNE_BASH) || to_dodge >= to_hit || to_hit BOTCHED) { damage(ch, vict, 0, ATTACK_BASH, DAM_BASHING); GET_POS(ch) = POS_SITTING; } else { /* * If we bash a player and they wimp out, they will move to the previous * room before we set them sitting. If we try to set the victim sitting * first to make sure they don't flee, then we can't bash them! So now * we only set them sitting if they didn't flee. -gg 9/21/98 */ dam = ww_dice(GET_STRENGTH(ch) + to_hit - to_dodge, 6) + GET_POTENCE(ch); if (damage(ch, vict, dam, ATTACK_BASH, DAM_BASHING) > 0) { /* -1 = dead, 0 = miss */ WAIT_STATE(vict, PULSE_VIOLENCE); if (ch->in_room == vict->in_room) GET_POS(vict) = POS_SITTING; } } WAIT_STATE(ch, PULSE_VIOLENCE * 2); } ACMD(do_rescue) { Creature vict, tmp_ch; if (IS_NPC(ch)) { send_to_char("You have no idea how to do that.\r\n", ch); return; } one_argument(argument, arg); if (!(vict = get_char_vis(ch, arg, FIND_CHAR_ROOM))) { send_to_char("Whom do you wish to rescue?\r\n", ch); return; } if (vict == ch) { send_to_char("What about fleeing instead?\r\n", ch); return; } if (FIGHTING(ch) == vict) { send_to_char("How can you rescue someone you are trying to kill?\r\n", ch); return; } for (tmp_ch = world[ch->in_room].people; tmp_ch && (FIGHTING(tmp_ch) != vict); tmp_ch = tmp_ch->next_in_room); if (!tmp_ch) { act("But nobody is fighting $M!", FALSE, ch, 0, vict, TO_CHAR); return; } if (FIGHT_MODE(tmp_ch) != FMODE_MELEE) { msg_to_char(ch, "You may only rescue someone engaged in melee combat.\r\n"); return; } if (!can_fight(ch, tmp_ch)) { act("You can't attack $N!", FALSE, ch, 0, tmp_ch, TO_CHAR); return; } /* Rescue is based on melee for unobvious reasons */ if (ww_dice(GET_MANIPULATION(ch) + GET_MELEE(ch), 6)) { send_to_char("You fail the rescue!\r\n", ch); return; } send_to_char("Banzai! To the rescue...\r\n", ch); act("You are rescued by $N, you are confused!", FALSE, vict, 0, ch, TO_CHAR); act("$n heroically rescues $N!", FALSE, ch, 0, vict, TO_NOTVICT); if (FIGHTING(vict) == tmp_ch) stop_fighting(vict); if (FIGHTING(tmp_ch)) stop_fighting(tmp_ch); if (FIGHTING(ch)) stop_fighting(ch); set_fighting(ch, tmp_ch, FMODE_MELEE); set_fighting(tmp_ch, ch, FMODE_MELEE); WAIT_STATE(vict, 2 * PULSE_VIOLENCE); } ACMD(do_kick) { Creature vict; byte to_hit, to_dodge = 0, dam; one_argument(argument, arg); if (!(vict = get_char_vis(ch, arg, FIND_CHAR_ROOM))) { if (FIGHTING(ch) && ch->in_room == FIGHTING(ch)->in_room) vict = FIGHTING(ch); else { send_to_char("Kick who?\r\n", ch); return; } } /* to_hit is based on dexterity + brawl */ to_hit = ww_dice(GET_DEXTERITY(ch) + GET_BRAWL(ch), 6 + GET_BLOCK(vict)); if (AWAKE(vict) && CAN_SEE(vict, ch)) to_dodge = ww_dice(GET_DEXTERITY(vict) + GET_DODGE(vict), 8); if (IS_NPC(ch)) send_to_char("You have no idea how.\r\n", ch); else if (vict == ch) send_to_char("Aren't we funny today...\r\n", ch); else if (FIGHT_MODE(vict) == FMODE_MISSILE || FIGHT_MODE(ch) == FMODE_MISSILE) msg_to_char(ch, "You aren't close enough.\r\n"); else if (!can_fight(ch, vict)) act("You can't attack $N!", FALSE, ch, 0, vict, TO_CHAR); else if (AFF_FLAGGED(vict, AFF_IMMUNE_BASH) || to_dodge >= to_hit || to_hit BOTCHED) { damage(ch, vict, 0, ATTACK_KICK, DAM_BASHING); WAIT_STATE(ch, PULSE_VIOLENCE * 3); } else { dam = ww_dice(GET_STRENGTH(ch) + 2 + to_hit - to_dodge, 6) + GET_POTENCE(ch); damage(ch, vict, dam, ATTACK_KICK, DAM_BASHING); WAIT_STATE(ch, PULSE_VIOLENCE * 3); } } ACMD(do_throw) { extern const char *dirs[]; int dir = -1; Object obj = NULL; room_rnum to_room = NOWHERE; two_arguments(argument, arg, buf); if (!*arg || !*buf) msg_to_char(ch, "What would you like to throw, and which direction?\r\n"); else if (!(obj = get_obj_in_list_vis(ch, arg, ch->carrying))) msg_to_char(ch, "You don't have anything like that.\r\n"); else if ((dir = parse_direction(buf)) < 0) msg_to_char(ch, "Which way did you want to throw it?\r\n"); else if (SECT(ch->in_room) == SECT_INSIDE || SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_MULTI) { if (!EXIT(ch, dir) || EXIT(ch, dir)->to_room == NOWHERE) msg_to_char(ch, "You can't throw it that way.\r\n"); else if (IS_SET(EXIT(ch, dir)->exit_info, EX_CLOSED)) msg_to_char(ch, "You can't throw it through a closed door!\r\n"); else to_room = EXIT(ch, dir)->to_room; } else { if (SECT(real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == SECT_MOUNTAIN) msg_to_char(ch, "You can't throw it into the mountains.\r\n"); else if (SECT(real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == SECT_BUILDING || SECT(real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == SECT_MONUMENT_CLOSED || SECT(real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == SECT_MULTI) { if (world[real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])].building_entrance != dir) msg_to_char(ch, "You can only throw it through the entrance.\r\n"); else to_room = real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1]); } else to_room = real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1]); } /* If we came up with a room, lets throw! */ if (to_room == NOWHERE) return; sprintf(buf, "You throw $p %s as hard as you can!", dirs[dir]); sprintf(buf1, "$n throws $p %s as hard as $e can!", dirs[dir]); act(buf, FALSE, ch, obj, 0, TO_CHAR); act(buf1, TRUE, ch, obj, 0, TO_ROOM); obj_to_room(obj, to_room); sprintf(buf, "$p is hurled in from the %s and falls to the ground at your feet!", dirs[rev_dir[dir]]); if (world[obj->in_room].people) act(buf, FALSE, 0, obj, 0, TO_ROOM); } ACMD(do_catapult) { void disperse_resources(room_rnum room); void disassociate_building(room_rnum room); extern const char *dirs[]; extern const int max_damage[]; Creature c, next_c; Object catapult, o, next_o; struct empire_political_data *emp_pol = NULL; int dir, e; Resource rocks[2] = { {o_ROCK, 12}, END_RESOURCE_LIST }; room_rnum to_room, rm; /* Find a 'pult */ for (catapult = world[ch->in_room].contents; catapult; catapult = catapult->next_content) if (GET_OBJ_TYPE(catapult) == ITEM_CART && GET_OBJ_VAL(catapult, 2)) break; skip_spaces(&argument); if (!catapult) msg_to_char(ch, "You don't even have a catapult.\r\n"); else if (!*argument) msg_to_char(ch, "Which direction would you like to shoot?\r\n"); else if (!has_resources(ch, rocks, FALSE)) { /* This line intentionally left blank */ } else if ((dir = parse_direction(argument)) == -1) msg_to_char(ch, "Which direction is that?\r\n"); else if ((to_room = real_shift(ch->in_room, shift_dir[dir][0], shift_dir[dir][1])) == NOWHERE) msg_to_char(ch, "Unknown error.\r\n"); else if (GET_OBJ_VAL(catapult, 2) > 1) msg_to_char(ch, "You must wait before shooting the catapult again.\r\n"); else if (SECT(ch->in_room) == SECT_FOREST_3 || SECT(ch->in_room) == SECT_FOREST_4) msg_to_char(ch, "You can't aim through the trees!\r\n"); else if (SECT(ch->in_room) == SECT_BUILDING || SECT(ch->in_room) == SECT_MULTI || SECT(ch->in_room) == SECT_MONUMENT_CLOSED || SECT(ch->in_room) == SECT_INSIDE) msg_to_char(ch, "You can't shoot from indoors.\r\n"); else if (SECT(ch->in_room) == SECT_BARRIER) msg_to_char(ch, "You can't shoot from this close to the barrier.\r\n"); else { if ((e = real_empire(world[to_room].owner)) != -1) emp_pol = find_relation(real_empire(GET_LOYALTY(ch)), e); if (e > 0 && (!emp_pol || !IS_SET(emp_pol->type, DIPL_WAR))) { msg_to_char(ch, "You can't attack that acre!\r\n"); return; } extract_resources(ch, rocks, FALSE); sprintf(buf, "You shoot $p %s!", dirs[dir]); act(buf, FALSE, ch, catapult, 0, TO_CHAR); sprintf(buf, "$n shoots $p %s!", dirs[dir]); act(buf, FALSE, ch, catapult, 0, TO_ROOM); GET_OBJ_VAL(catapult, 2) = 2; /* shot timer, 1 = ready to shoot */ if (SHOULD_APPEAR(ch)) appear(ch); if (SECT(to_room) == SECT_MULTI || SECT(to_room) == SECT_BUILDING) { if (++(world[to_room].damage) > (SECT(to_room) == SECT_MULTI ? 5 : max_damage[(int) world[to_room].type])) { disperse_resources(to_room); disassociate_building(to_room); if (world[to_room].people) act("The building is hit and crumbles!", FALSE, world[to_room].people, 0, 0, TO_CHAR | TO_ROOM); } else { if (world[to_room].people) act("The building is hit by something and shakes violently!", FALSE, world[to_room].people, 0, 0, TO_CHAR | TO_ROOM); for (rm = MAP_SIZE; rm <= top_of_world; rm++) if (HOME_ROOM(rm) == to_room && world[rm].people) act("The building is hit by something and shakes violently!", FALSE, world[rm].people, 0, 0, TO_CHAR | TO_ROOM); return; } } else { if (SECT(to_room) == SECT_WELL || SECT(to_room) == SECT_BARRIER || SECT(to_room) == SECT_FOUNTAIN) { disperse_resources(to_room); disassociate_building(to_room); } sprintf(buf, "A huge object is hurled in from %s!", dirs[rev_dir[dir]]); if (world[to_room].people) act(buf, FALSE, world[to_room].people, 0, 0, TO_CHAR | TO_ROOM); } /* if we got this far, we need to hurt some people */ for (c = world[to_room].people; c; c = next_c) { next_c = c->next_in_room; if (!number(0, 1)) msg_to_char(c, "You leap out of the way!\r\n"); else { msg_to_char(c, "You are hit and killed!\r\n"); if (!IS_NPC(c)) { mortlog(0, "%s has been killed by a catapult at (%d, %d)!", PERS(c, c, 1), X_COORD(c->in_room), Y_COORD(c->in_room)); syslog(0, TRUE, "DEATH: %s has been killed by a catapult at (%d, %d)", GET_NAME(c), X_COORD(c->in_room), Y_COORD(c->in_room)); } die(c); } } for (o = world[to_room].contents; o; o = next_o) { next_o = o->next_content; if (!number(0, 1)) extract_obj(o); } WAIT_STATE(ch, 5 RL_SEC); } } ACMD(do_execute) { Creature victim; one_argument(argument, arg); if (!*arg) msg_to_char(ch, "Execute whom?\r\n"); else if (!(victim = get_char_vis(ch, arg, FIND_CHAR_ROOM))) msg_to_char(ch, NOPERSON); else if (victim == ch) msg_to_char(ch, "Seek professional help.\r\n"); else if (IS_NPC(victim)) msg_to_char(ch, "You can only execute people.\r\n"); else if (GET_POS(victim) >= POS_SLEEPING && !IS_INJURED(victim, INJ_TIED)) act("You need to knock $M out or tie $M up.", FALSE, ch, 0, victim, TO_CHAR); else perform_execute(ch, victim, -1, DAM_BASHING); } ACMD(do_stake) { Creature victim; Object stake; one_argument(argument, arg); if (!*arg) msg_to_char(ch, "%stake whom?\r\n", subcmd ? "Uns" : "S"); else if (!(victim = get_char_vis(ch, arg, FIND_CHAR_ROOM))) msg_to_char(ch, NOPERSON); else if (IS_GOD(victim) || IS_IMMORTAL(victim)) msg_to_char(ch, "You can't tie up a god!\r\n"); else if (!subcmd && IS_INJURED(victim, INJ_STAKED)) act("$E is already staked!", FALSE, ch, 0, victim, TO_CHAR); else if (subcmd && !IS_INJURED(victim, INJ_STAKED)) act("$E isn't even staked!", FALSE, ch, 0, victim, TO_CHAR); else if (IS_INJURED(victim, INJ_STAKED)) { act("You unstake $N.", FALSE, ch, 0, victim, TO_CHAR); act("$n unstakes you!", FALSE, ch, 0, victim, TO_VICT | TO_SLEEP); act("$n unstakes $N.", FALSE, ch, 0, victim, TO_NOTVICT); if (GET_DAMAGE(victim) < 7) GET_POS(victim) = POS_RESTING; REMOVE_BIT(INJURY_FLAGS(victim), INJ_STAKED); obj_to_char(read_object(o_STAKE, VIRTUAL), ch); } else if (!can_fight(ch, victim)) act("You can't stake $M!", FALSE, ch, 0, victim, TO_CHAR); else if (!(stake = get_obj_in_list_num(real_object(o_STAKE), ch->carrying))) msg_to_char(ch, "You don't have a stake!\r\n"); else { WAIT_STATE(ch, PULSE_VIOLENCE * 2); if (AWAKE(victim)) { int to_hit, to_dodge = 0; to_hit = ww_dice(GET_DEXTERITY(ch) + GET_MELEE(ch), 6 + GET_BLOCK(victim)); to_dodge = ww_dice(GET_DEXTERITY(victim) + GET_DODGE(victim), 6); /* decide whether this is a hit or a miss */ if (to_hit <= to_dodge) { act("You lunge at $N with $p, but miss!", FALSE, ch, stake, victim, TO_CHAR); act("$n lunges at you with $p, but misses!", FALSE, ch, stake, victim, TO_VICT); act("$n lunges at $N with $p, but misses!", FALSE, ch, stake, victim, TO_NOTVICT); return; } } act("You jab $p through $N's heart!", FALSE, ch, stake, victim, TO_CHAR); act("$n jabs $p through your heart!", FALSE, ch, stake, victim, TO_VICT | TO_SLEEP); act("$n jabs $p through $N's heart!", FALSE, ch, stake, victim, TO_NOTVICT); if (GET_DISC(victim, DISC_PROTEAN) >= 8 && GET_BLOOD(victim) > 3) { GET_BLOOD(victim) -= 2; if (ww_dice(GET_WILLPOWER(ch), 8) > 0) { act("The stake ejects violently from $n's chest!", FALSE, victim, 0, 0, TO_ROOM); msg_to_char(victim, "You eject the stake violently from your chest!\r\n"); msg_to_char(ch, "You are momentarily stunned!\r\n"); WAIT_STATE(ch, PULSE_VIOLENCE * 4); return; } } SET_BIT(INJURY_FLAGS(victim), INJ_STAKED); if (GET_DAMAGE(victim) >= 7) { GET_DAMAGE(victim) = 6; GET_POS(victim) = POS_STUNNED; } extract_obj(stake); if (!IS_VAMPIRE(victim)) { if (!IS_NPC(victim)) { death_log(victim, ch, ATTACK_EXECUTE); add_lore(ch, LORE_PLAYER_KILL, GET_IDNUM(victim)); add_lore(victim, LORE_PLAYER_DEATH, GET_IDNUM(ch)); } die(victim); } } }