/***************************************************************************
* Mud20 1.0 by Todd H. Johnson (Kregor) a derivative of the Open Gaming *
* License by Wizards of the Coast. All comments referring to D20, OGL, *
* and SRD refer to the System Reference Document for the Open Gaming *
* system. Any inclusion of these derivatives must include credit to the *
* Mud20 system, the full and complete Open Gaming License, and credit to *
* the respective authors. See ../doc/srd.txt for more information. *
* *
* Emud 2.2 by Igor van den Hoven, Michiel Lange, and Martin Bethlehem. *
* *
* MrMud 1.4 by David Bills, Dug Michael and Martin Gallwey *
* *
* Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael *
* Chastain, Michael Quan, and Mitchell Tse. *
* *
* Original Diku Mud copyright (C) 1990 1991 by Sebastian Hammer, *
* Michael Seifert, Hans Henrik St{rfeld, Tom Madsen, and Katje Nyboe. *
***************************************************************************/
/**************************************************************************
* File: combat_handler.c *
* Author: Midboss *
* Purpose: Core functionality for turn-based combat on ROM MUDs. *
* Modified by Kregor for the Mud20 System, Jan 2008 *
**************************************************************************/
#include "mud.h"
/*
* Guards come to the rescue of NPC citizens in distress.
* Note that putting this in combat_update does in fact mean that the more ACT_CITIZENs
* there are involved in the battle (this should include guards)
* The more often another guard will be summoned. This is the desired
* effect and should aid in area razing defense - Kregor
*/
void check_guard_assist( CHAR_DATA *ch, CHAR_DATA *attacker )
{
MOB_INDEX_DATA *mobIndex;
CHAR_DATA *guard;
push_call("check_guard_assist(%p)", ch);
if (IS_ACT(ch, ACT_CITIZEN) && ch->in_room->area == ch->pIndexData->area)
{
if (number_bits(3) == 0)
{
if ((mobIndex = get_mob_index(ch->pIndexData->area->guard)) == NULL)
log_build_printf(ch->pIndexData->vnum, "mobile_turn (%d): guard data for area invalid.");
else
{
guard = create_mobile(mobIndex);
char_to_room( guard, ch->in_room->vnum, TRUE );
guard->npcdata->sac_timer = 60;
RESTRING(guard->npcdata->sac_string, "$n departs the vicinity as the turmoil calms.");
act("$n comes to the aid of $N.", guard, NULL, ch, TO_ROOM);
if (attacker != NULL)
{
fight(guard, attacker);
}
}
}
}
pop_call();
return;
}
/*
* Updater for what few autonomous functions used in this combat system.
* Mostly just triggers mobile's turns and updates battlefield conditions.
* Also controls the automatic turn skip when someone tries to stall.
*/
void combat_update (void)
{
COMBAT_DATA * battle, * battle_next;
CHAR_DATA * ch;
push_call("combat_update()");
for (battle = battle_list; battle != NULL; battle = battle_next)
{
battle_next = battle->next;
if (battle->turn_list->roundtime >= 2000000000)
nuke_roundtime (battle);
ch = battle->turn_list->unit;
if (ch == NULL)
{
bug ("combat_update (): Turn with NULL character!", 0);
clean_combat (battle);
pop_call();
return;
}
if (!IS_NPC(ch) && ch->desc == NULL)
linkdead_turn (ch);
else if (!IS_AWAKE(ch)) // unconscious/mortal stay in combat, but only to lose affects and bleed
turn_end (battle->turn_list, 200 - ch->initiative);
else if (!mounted_combat_check(ch)) // failure to control mount in combat forfeits turn
turn_end (battle->turn_list, 200 - ch->initiative);
else if (IS_AFFECTED(ch, AFF2_DAZED))
{
act("You are too dazed to fight.", ch, NULL, NULL, TO_CHAR);
act("$n simply stands around in a daze.", ch, NULL, NULL, TO_ROOM);
turn_end (battle->turn_list, 200 - ch->initiative);
}
else if (is_affected(ch, gsn_irresistible_dance))
irresistible_dance(ch);
else if (IS_AFFECTED(ch, AFF2_CONFUSION)) // confusion can override attacks
confused_turn(ch);
else if (IS_NPC (ch) && !ch->desc)
mobile_turn (ch);
else
{
battle->turn_list->timer++;
}
if (battle->turn_list->timer >= 20) //two minutes to end a turn seems reasonable? Kregor
turn_end (battle->turn_list, 200 - ch->initiative);
if (IS_PLR(ch, PLR_WIZTIME))
{
if (ch->last_attacked != NULL)
ch_printf_color(ch, "{058}DEBUG: you last attacked %s.\n\r", get_name(ch->last_attacked));
}
check_victory (battle);
}
pop_call();
return;
}
/*
* Checks to see if a char has taken all the
* actions they can take in a turn, if so, then
* advance to the next turn - Kregor
*/
bool turn_complete( CHAR_DATA *ch )
{
TURN_DATA *turn;
push_call("turn_complete(%p)",ch);
if (!in_combat(ch))
{
pop_call();
return FALSE;
}
if (!is_active(ch))
{
pop_call();
return FALSE;
}
if ((turn = get_turn_char(ch)) == NULL)
{
pop_call();
return FALSE;
}
if (ch->casting || ch->concentrating)
{
pop_call();
return FALSE;
}
if (IS_SET(ch->action, ACTION_STANDARD) && IS_SET(ch->action, ACTION_MOVE))
{
turn_end (turn, 200 - ch->initiative);
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* Lets a mobile take its turn.
* All the AI functions happen here. All functions should control their
* own action space, and should all pass both ch and victim. The loop
* for deciding a target may not yet be the best. - Kregor
*/
void mobile_turn (CHAR_DATA * ch)
{
CHAR_DATA *victim, *vch = NULL, *list;
TURN_DATA *turn;
push_call("mobile_turn(%p)",ch);
if (!IS_NPC(ch))
{
pop_call();
return;
}
if ((turn = get_turn_char(ch)) == NULL)
{
pop_call();
return;
}
if (!IS_AWAKE(ch))
{
turn_end (turn, 200 - ch->initiative);
pop_call();
return;
}
if (!in_combat(ch))
{
pop_call();
return;
}
// hit percent prog triggers before aggression, overrides AI - Kregor
if ((victim = who_fighting(ch)) != NULL && mprog_hitprcnt_trigger(ch, victim))
{
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return;
}
// Select the target of aggression if not already focused on someone
if (!victim)
{
for (vch = ch, list = ch->in_battle->unit_list; list != NULL; list = list->next_in_battle)
{
if (!is_same_group(ch, list))
{
if (vch == ch)
vch = list;
else if (who_fighting(list) && who_fighting(list) == ch)
{
vch = list;
break;
}
else if (vch != ch && number_percent () < 45)
vch = list;
else
continue;
}
else if ((vch = who_fighting(list)) != NULL)
{
vch = list;
break;
}
}
}
if (vch == ch)
{
clean_combat (ch->in_battle);
pop_call();
return;
}
else if (victim)
{
vch = check_tanking(ch, victim);
}
if (IS_AWAKE(ch) && ch->position < POS_FIGHTING)
{
do_stand(ch, "");
}
if (!is_safe(ch, vch)) // if safe, end turn
{
// fight_prog takes over turn, overrides regular AI attack - Kregor
if (mprog_fight_trigger(ch, vch))
{
TAKE_ACTION(ch, ACTION_FULL);
}
else if (!IS_SET(ch->act, ACT_NOFIGHT)) // the AI functions for combat need to go here!
{
if (breath_weapon(ch, vch))
{
pop_call();
return;
}
if (gaze_attack(ch, vch))
{
pop_call();
return;
}
if (caster_AI_combat(ch, vch))
{
pop_call();
return;
}
if (warrior_AI_combat(ch, vch))
{
pop_call();
return;
}
else
{
do_say(ch, "Awwwwww! I'm outta options!");
}
}
}
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return;
}
/*
* Hacking mobile_turn to handle confused characters - Kregor
*/
void confused_turn(CHAR_DATA * ch)
{
CHAR_DATA *victim, *vch = NULL;
TURN_DATA *turn;
push_call("confused_turn(%p)",ch);
if ((turn = get_turn_char(ch)) == NULL)
{
pop_call();
return;
}
if (!IS_AWAKE(ch))
{
turn_end (turn, 200 - ch->initiative);
pop_call();
return;
}
if (!in_combat(ch))
{
pop_call();
return;
}
// Select the target of aggression if not already focused on someone
if ((victim = who_fighting(ch)) == NULL)
{
for (vch = ch->in_battle->unit_list ; vch ; vch = ch->next_in_battle)
{
if (is_master(vch, ch)) // because I'm nice, skip over pets - Kregor
continue;
if (vch->last_attacked && vch->last_attacked == ch)
{
break;
}
if (number_bits(1) == 0)
{
break;
}
}
}
// select collateral damage
if (vch == NULL)
{
for (vch = ch->in_room->first_person ; vch ; vch = vch->next)
{
if (is_master(vch, ch))
continue;
if (vch->last_attacked && vch->last_attacked == ch)
{
break;
}
if (number_bits(1) == 0)
{
break;
}
}
}
if (!victim)
victim = vch;
switch (number_range(1,4))
{
case 1:
attack(ch, vch);
attack(ch, vch);
break;
case 2:
act("$n stands around, babbling nonsense.", ch, NULL, NULL, TO_ROOM);
act("You stand around, babbling nonsense.", ch, NULL, NULL, TO_CHAR);
break;
case 3:
act("You swing wildly, hitting yourself in the process!", ch, NULL, NULL, TO_CHAR);
act("$n swings wildly, only hitting himself as a result!", ch, NULL, NULL, TO_ROOM);
damage(ch, ch, dice(1,8) + stat_bonus(TRUE, ch, APPLY_STR), TYPE_NOFIGHT, NULL);
break;
case 4:
attack(ch, vch);
attack(ch, vch);
break;
}
TAKE_ACTION(ch, ACTION_FULL);
pop_call();
return;
}
/*
* If link dead, remove from combat - Kregor
*/
void linkdead_turn (CHAR_DATA * ch)
{
push_call("linkdead_turn(%p)",ch);
char_from_combat (ch);
pop_call();
return;
}
/*
* This will end a combat, and notify the victorious party.
*/
void check_victory (COMBAT_DATA * battle)
{
CHAR_DATA *leader;
CHAR_DATA *unit;
bool victory = TRUE;
bool found = FALSE;
push_call("check_victory(%p)",battle);
//First look for PCs. Since mobs don't gain, if only mobs are left, we abort.
for (unit = battle->unit_list; unit != NULL; unit = unit->next_in_battle)
if (!IS_NPC (unit))
found = TRUE;
if (!found)
{
clean_combat (battle);
return;
}
for (leader = battle->unit_list; leader != NULL; leader = leader->next_in_battle)
{
if (!IS_AWAKE(leader))
continue;
//Once we find the first guy that isn't out, we look for another guy that
//isn't out, and isn't on his team. If one isn't found, battle's over.
for (unit = battle->unit_list; unit != NULL; unit = unit->next_in_battle)
{
if ((!IS_AWAKE(unit) && !is_same_group (leader, unit))
|| is_same_group (leader, unit) || leader == unit)
continue;
else
{
victory = FALSE;
break;
}
}
if (!victory)
continue;
else
{
for (unit = battle->unit_list; unit != NULL; unit = unit->next_in_battle)
{
if (is_same_group (leader, unit))
act ("{138}You have won the battle!", unit, NULL, NULL, TO_CHAR);
else
act ("{138}You have lost the battle!", unit, NULL, NULL, TO_CHAR);
}
clean_combat (battle);
pop_call();
return;
}
}
pop_call();
return;
}
/*
* True if ch is part of any combat.
*/
bool in_combat (CHAR_DATA * ch)
{
push_call("in_combat(%p)",ch);
if (ch->in_battle)
{
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* True if ch and victim are both in same combat.
*/
bool in_same_combat (CHAR_DATA * ch, CHAR_DATA *victim)
{
push_call("in_combat(%p)",ch);
if (ch == NULL || victim == NULL)
{
pop_call();
return FALSE;
}
if (!ch->in_battle || victim->in_battle)
{
pop_call();
return FALSE;
}
if (ch->in_battle != victim->in_battle)
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
* True if ch may take a turn right now.
*/
bool is_active (CHAR_DATA * ch)
{
push_call("is_active(%p)",ch);
if (!in_combat (ch))
{
pop_call();
return FALSE;
}
if (ch->turn == ch->in_battle->turn_list)
{
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* initiative per D20 SRD - Kregor
*/
void roll_initiative( CHAR_DATA *ch )
{
int init;
push_call("roll_initiative(%p)", ch);
if (arcane_mastery(ch, SCHOOL_DIVINATION))
init = 20 + stat_bonus(TRUE, ch, APPLY_DEX);
else
init = dex_roll(ch);
if (learned(ch, gsn_imp_initiative))
init += 4;
if (learned(ch, gsn_alertness))
init += 2;
if (learned(ch, gsn_first_strike) && armor_type_worn(ch) == ARMOR_NONE
&& get_eq_char(ch, WEAR_SHIELD) == NULL)
{
init += multi_class_level(ch, gsn_first_strike) / 2;
}
if (get_monk_style(ch) == STYLE_INVISIBLE_EYE && class_level(ch, CLASS_MONK) >= 12)
init += 2;
if (IS_AFFECTED(ch, AFF2_HALLUCINATE))
init -= 4;
ch->initiative = UMAX(1, init);
if (IS_PLR(ch, PLR_WIZTIME))
ch_printf_color(ch, "DEBUG: You score %d for initiative.\n\r", ch->initiative);
pop_call();
return;
}
/*
* Inserts a character into combat and adds their turn to the list.
*/
void char_to_combat (CHAR_DATA * ch, COMBAT_DATA * combat)
{
TURN_DATA * turn;
AFFECT_DATA af;
push_call("char_to_combat(%p,%p)",ch,combat);
if (combat->unit_list == NULL)
combat->unit_list = ch;
else
{
ch->next_in_battle = combat->unit_list;
combat->unit_list = ch;
}
ch->in_battle = combat;
roll_initiative(ch);
turn = new_turn ();
turn->unit = ch;
turn->in_battle = combat;
//So people don't end up getting a dozen turns by joining in.
if (combat->turn_count > 0)
turn->roundtime = (combat->turn_list->roundtime + 200) - ch->initiative;
else
turn->roundtime = 500 - ch->initiative;
ch->turn = turn;
ch->action = 0;
if (IS_STAGGERED(ch))
ch->action = ACTION_MOVE;
if (IS_FLYING(ch))
TAKE_ACTION(ch, ACTION_MOVE);
ch->attack = 0;
ch->attack_part = -1;
insert_turn (combat, turn);
// check racial abilities that only happen once in a fight
frightful_presence(ch);
check_aura(ch, gsn_aura_of_menace);
check_aura(ch, gsn_babble);
if (learned(ch, gsn_daring_charge))
{
af.type = gsn_daring_charge;
af.level = multi_class_level(ch, gsn_daring_charge);
af.duration = 1;
af.bittype = AFFECT_TO_NONE;
af.bitvector = AFF_NONE;
af.location = APPLY_MOR_TOHIT;
af.modifier = af.level;
affect_join( ch, ch, &af);
af.bitvector = AFF_NONE;
af.location = APPLY_MOR_DAMG;
af.modifier = af.level;
affect_join( ch, ch, &af);
ch->uses[gsn_daring_charge]++;
}
pop_call();
return;
}
/*
* Removes a ch and their turn entry from combat.
*/
void char_from_combat (CHAR_DATA * ch)
{
CHAR_DATA * prev;
push_call("char_from_combat(%p)",ch);
if (ch == NULL)
{
bug ("char_from_combat() -- null ch!?", 0);
pop_call();
return;
}
stop_fighting(ch, TRUE);
if (!in_combat(ch))
{
pop_call();
return;
}
if (ch->in_battle->unit_list == ch)
{
ch->in_battle->unit_list = ch->next_in_battle;
ch->next_in_battle = NULL;
}
else
{
for (prev = ch->in_battle->unit_list; prev != NULL; prev = prev->next_in_battle)
{
if (prev->next_in_battle == ch)
{
prev->next_in_battle = ch->next_in_battle;
ch->next_in_battle = NULL;
break;
}
}
if (prev == NULL)
{
bug ("char_from_combat() -- ch not found.", 0);
pop_call();
return;
}
}
remove_turn (ch->turn);
ch->turn = NULL;
ch->in_battle = NULL;
// strip all effects that only last until end of fight
if (is_affected(ch, gsn_barbarian_rage))
{
affect_strip(ch, gsn_barbarian_rage);
if (!learned(ch, gsn_tireless_rage))
{
int cost = get_max_move(ch) / 2;
move_loss(ch, NULL, cost);
}
}
if (is_affected(ch, gsn_blood_rage))
affect_strip(ch, gsn_blood_rage);
if (is_affected(ch, gsn_smite))
affect_strip(ch, gsn_smite);
if (is_affected(ch, gsn_retributive_strike))
affect_strip(ch, gsn_retributive_strike);
stop_fighting(ch, TRUE);
pop_call();
return;
}
/*
* Returns the data for ch's next/current turn.
*/
TURN_DATA * get_turn_char (CHAR_DATA * ch)
{
TURN_DATA * turn;
push_call("get_turn_char(%p)",ch);
if (!in_combat (ch))
{
pop_call();
return NULL;
}
for (turn = ch->in_battle->turn_list; turn != NULL; turn = turn->next)
{
if (turn->unit == ch)
{
pop_call();
return turn;
}
}
pop_call();
return NULL;
}
/*
* Inserts a turn into its proper position in the specified list.
*/
void insert_turn (COMBAT_DATA * combat, TURN_DATA * nTurn)
{
push_call("insert_turn(%p,%p)",combat,nTurn);
if (combat->turn_list == NULL)
{
combat->turn_list = nTurn;
nTurn->in_battle = combat;
}
else
{
TURN_DATA * prev;
if (combat->turn_list->roundtime > nTurn->roundtime)
{
nTurn->next = combat->turn_list;
combat->turn_list = nTurn;
nTurn->in_battle = combat;
}
else
{
for (prev = combat->turn_list; prev != NULL; prev = prev->next)
{
if (prev->next != NULL && prev->next->roundtime > nTurn->roundtime)
{
nTurn->next = prev->next;
prev->next = nTurn;
nTurn->in_battle = combat;
break;
}
else if (prev->next == NULL)
{
prev->next = nTurn;
nTurn->in_battle = combat;
break;
}
}
}
}
pop_call();
return;
}
/*
* Removes a turn from the list without freeing it.
* It isn't freed here because you have to use this every time you move a turn
* to a new position in the list.
*/
void remove_turn (TURN_DATA * turn)
{
TURN_DATA * prev;
push_call("remove_turn(%p)",turn);
if (turn == NULL)
{
bug ("remove_turn() -- null turn!?", 0);
pop_call();
return;
}
if (turn == turn->in_battle->turn_list)
{
turn->in_battle->turn_list = turn->next;
turn->next = NULL;
}
else
{
for (prev = turn->in_battle->turn_list; prev != NULL; prev = prev->next)
{
if (prev->next == turn)
{
prev->next = turn->next;
turn->next = NULL;
break;
}
}
}
pop_call();
return;
}
/*
* Similar to roundtime (), but used specifically at the end of each action.
*/
void turn_end (TURN_DATA * turn, int roundtime)
{
CHAR_DATA *grappled;
CHAR_DATA *caster;
CHAR_DATA *ch, *ch_next;
int lvl;
push_call("turn_end(%p,%p)",turn,roundtime);
turn->timer = 0;
turn->roundtime += roundtime;
ch = turn->unit;
ch_next = turn->next->unit;
if (!IS_NPC(ch))
ch->pcdata->last_combat = mud->current_time;
if (IS_SET(ch->in_room->progtypes, FIGHT_PROG))
rprog_fight_trigger(ch);
// If grappling at end of affected char's turn, do damage - Kregor
if ((grappled = who_grappling(ch)) != NULL)
{
if ((lvl = get_affect_level(ch, gsn_fire_shield)) > 0 && !save_resist(ch, grappled, gsn_fire_shield, lvl))
{
damage(ch, grappled, dice(1,6) + UMIN(lvl,15), gsn_fire_shield, NULL);
}
else if ((lvl = get_affect_level(ch, gsn_thorn_body)) > 0 && !save_resist(ch, grappled, gsn_thorn_body, lvl))
{
damage(ch, grappled, dice(1,6) + UMIN(lvl,15), gsn_thorn_body, NULL);
}
else if ((lvl = race_skill(ch, gsn_barbed_touch)) > 0)
{
damage(ch, grappled, dice(1,lvl), gsn_barbed_touch, NULL);
}
else if ((lvl = race_skill(ch, gsn_fire_touch)) > 0)
{
damage(ch, grappled, dice(1,lvl), gsn_fire_touch, NULL);
}
else if ((lvl = race_skill(ch, gsn_frost_touch)) > 0)
{
damage(ch, grappled, dice(1,lvl), gsn_frost_touch, NULL);
}
else if ((lvl = race_skill(ch, gsn_shock_touch)) > 0)
{
damage(ch, grappled, dice(1,lvl), gsn_shock_touch, NULL);
}
else if ((lvl = race_skill(ch, gsn_acid_touch)) > 0)
{
damage(ch, grappled, dice(1,lvl), gsn_acid_touch, NULL);
}
}
act( "You have ended your turn.", ch, NULL, NULL, TO_CHAR);
act( "$n has ended $s turn.", ch, NULL, NULL, TO_ROOM);
damage_update(ch);
affect_update(ch);
obj_char_update(ch);
act( "It is now your turn.", ch_next, NULL, NULL, TO_CHAR);
// update cleric with status of his comrades.
if (is_affected(ch_next, gsn_status))
{
show_party_line(ch_next);
}
check_guard_assist(ch, NULL);
ch_next->action = 0;
if (IS_STAGGERED(ch_next))
{
act("You are staggered and can only take a partial action.", ch_next, NULL, NULL, TO_CHAR);
ch_next->action = ACTION_MOVE;
}
if (is_mounting(ch_next) && !learned(ch_next->mounting, gsn_combat_rearing))
{
act("You take a move action to control your mount.", ch_next, NULL, NULL, TO_CHAR);
TAKE_ACTION(ch_next, ACTION_MOVE);
}
if (IS_FLYING(ch_next))
{
act("You take a move action to remain airborne.", ch_next, NULL, NULL, TO_CHAR);
TAKE_ACTION(ch_next, ACTION_MOVE);
}
ch_next->attack = 0;
ch_next->attack_part = -1;
if (is_affected(ch_next, gsn_crushing_hand))
{
if (IS_AFFECTED(ch_next, AFF_FREEDOM) || (caster = get_caster(ch_next, gsn_crushing_hand)) == NULL || combat_maneuver_check(caster, ch_next, gsn_crushing_hand) < 0)
{
act("{038}You break free from the hand's grasp!", ch, NULL, NULL, TO_CHAR);
act("{038}$n breaks free from the hand's grasp!", ch, NULL, NULL, TO_ROOM);
affect_strip(ch_next, gsn_crushing_hand);
}
else
{
damage(caster, ch_next, dice(2,6)+12, gsn_crushing_hand, NULL);
}
TAKE_ACTION(ch_next, ACTION_MOVE);
}
else if (is_affected(ch_next, gsn_grasping_hand))
{
if (IS_AFFECTED(ch_next, AFF_FREEDOM) || (caster = get_caster(ch_next, gsn_grasping_hand)) == NULL || combat_maneuver_check(caster, ch_next, gsn_grasping_hand) < 0)
{
act("{038}You break free from the hand's grasp!", ch, NULL, NULL, TO_CHAR);
act("{038}$n breaks free from the hand's grasp!", ch, NULL, NULL, TO_ROOM);
affect_strip(ch_next, gsn_grasping_hand);
}
else
{
act("{118}The giant hand of force holds you fast!", ch, NULL, NULL, TO_CHAR);
act("{118}The giant hand of force holds $n fast!", ch, NULL, NULL, TO_ROOM);
}
TAKE_ACTION(ch_next, ACTION_MOVE);
}
else if (IS_ENTANGLED(ch_next))
{
int diceroll;
if (learned(ch_next, gsn_escape_artist))
diceroll = escape_artist_roll(ch_next);
else
diceroll = str_roll(ch_next);
if (diceroll < 15 && domain_apotheosis(ch_next, DOMAIN_STRENGTH))
diceroll = UMAX(diceroll, str_roll(ch_next));
if (diceroll >= 15)
{
act("$tYou break free from your entanglement!", ch_next, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), NULL, TO_CHAR);
act("$t$n breaks free from $s entanglement!", ch_next, get_color_string(ch, COLOR_YOU_HIT, VT102_BOLD), NULL, TO_ROOM);
AFFECT_STRIP(ch_next, AFF2_ENTANGLED);
}
else
{
act("$tYou fail to escape your entanglement!", ch_next, get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD), NULL, TO_CHAR);
act("$t$n fails to escape $s entanglement!", ch_next, get_color_string(ch, COLOR_YOU_ARE_HIT, VT102_BOLD), NULL, TO_ROOM);
}
TAKE_ACTION(ch_next, ACTION_MOVE);
}
else if ((grappled = ch_next->grappled_by) != NULL)
{
if (combat_maneuver_check(ch_next, grappled, gsn_grapple) >= 0)
{
stop_grapple(grappled);
}
else
{
act("$tYou try to break $N's hold, but fail.", ch_next, get_color_string(ch_next, COLOR_YOU_ARE_HIT, VT102_BOLD), grappled, TO_CHAR);
act("$t$n tries to break $N's hold, but fails.", ch_next, "{178}", grappled, TO_NOTVICT);
act("$t$n tries to break your hold, but fails.", ch_next, get_color_string(grappled, COLOR_YOU_HIT, VT102_BOLD), grappled, TO_VICT);
}
TAKE_ACTION(ch_next, ACTION_MOVE);
}
else if ((grappled = ch_next->grappling) != NULL)
{
if (combat_maneuver_check(ch_next, grappled, gsn_grapple) < 0)
{
stop_grapple(grappled);
}
else
{
act("$tYou maintain your hold on $N.", ch_next, get_color_string(ch_next, COLOR_YOU_HIT, VT102_BOLD), grappled, TO_CHAR);
act("$t$n maintains $s grapple on $N.", ch_next, "{178}", grappled, TO_NOTVICT);
act("$t$n maintains $s grapple on you.", ch_next, get_color_string(grappled, COLOR_YOU_ARE_HIT, VT102_BOLD), grappled, TO_VICT);
}
TAKE_ACTION(ch_next, ACTION_MOVE);
}
check_aura(ch_next, gsn_aura_of_menace);
check_aura(ch_next, gsn_babble);
remove_turn(turn);
insert_turn(turn->in_battle, turn);
turn->in_battle->turn_count++;
pop_call();
return;
}
/*
* Moves the turn down the list a bit.
*/
void skip_turn (TURN_DATA * turn, bool fWilling)
{
push_call("skip_turn(%p,%p)",turn,fWilling);
turn->roundtime = turn->next->roundtime;
remove_turn (turn);
insert_turn (turn->in_battle, turn);
if (fWilling)
turn->in_battle->turn_count++;
pop_call();
return;
}
/*
* Adds to the roundtime of a turn, then replaces it in the order.
*/
void roundtime (TURN_DATA * turn, int roundtime)
{
push_call("roundtime(%p,%p)",turn,roundtime);
turn->roundtime += roundtime;
remove_turn (turn);
insert_turn (turn->in_battle, turn);
pop_call();
return;
}
/*
* Swaps the placement of two turns, by switching roundtimes and reordering.
*/
void swap_turn (TURN_DATA * aTurn, TURN_DATA * bTurn)
{
COMBAT_DATA * battle = aTurn->in_battle;
int rt = aTurn->roundtime;
push_call("swap_turn(%p,%p)",aTurn,bTurn);
aTurn->roundtime = bTurn->roundtime;
bTurn->roundtime = rt;
remove_turn (aTurn);
remove_turn (bTurn);
insert_turn (battle, aTurn);
insert_turn (battle, bTurn);
pop_call();
return;
}
/*
* Just in case some newb with a trigger leaves a battle against a training
* dummy running for weeks on end with trigs, or something. You can never
* have too much protection, y'know. -- Midboss
*/
void nuke_roundtime (COMBAT_DATA * battle)
{
TURN_DATA * list;
push_call("nuke_rountime(%p)",battle);
for (list = battle->turn_list; list != NULL; list = list->next)
list->roundtime -= 2000000000;
pop_call();
return;
}
/*
* Creates a new battle between two parties.
* Recycles the data if there somehow aren't enough people.
*/
void initiate_combat (CHAR_DATA * ch, CHAR_DATA * victim)
{
CHAR_DATA * och;
TURN_DATA * turn;
COMBAT_DATA * battle;
int acount = 0, bcount = 0;
push_call("initiate_combat(%p,%p)",ch,victim);
ALLOCMEM(battle, COMBAT_DATA, 1);
if (battle_list == NULL)
battle_list = battle;
else
{
battle->next = battle_list;
battle_list = battle;
}
set_fighting(ch, victim);
set_fighting(victim, ch);
for (och = ch->in_room->first_person; och != NULL; och = och->next_in_room)
{
if (is_same_group (ch, och) && !IS_ACT(och, ACT_NOASSIST))
{
char_to_combat (och, battle);
set_fighting(och, victim);
}
if (is_same_group (victim, och) && !IS_ACT(och, ACT_NOASSIST))
{
char_to_combat (och, battle);
set_fighting(och, ch);
}
}
for (turn = battle->turn_list; turn != NULL; turn = turn->next)
{
if (is_same_group (ch, turn->unit))
acount++;
else
bcount++;
}
if (acount < 1 || bcount < 1)
clean_combat (battle);
pop_call();
return;
}
/*
* is ch dead or nonexistent?
*/
bool valid_victim( CHAR_DATA *ch )
{
push_call("valid_victim(%p)",ch);
if (ch == NULL)
{
pop_call();
return FALSE;
}
if (ch->position == POS_DEAD || IS_PLR(ch, PLR_DEAD))
{
pop_call();
return FALSE;
}
if (ch->in_room && ch->in_room->vnum == ROOM_VNUM_DEATH)
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
* is ch REALLY fighting victim?
*/
bool valid_fight( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("valid_fight(%p,%p)",ch,victim);
/* Keep from fighting thin air... */
if (ch == NULL)
{
pop_call();
return FALSE;
}
if (victim == NULL)
{
pop_call();
return FALSE;
}
/* keep SuperMob from fighting */
if (victim == supermob || ch == supermob)
{
pop_call();
return FALSE;
}
// if either party died, it's not valid fight
if (victim->position == POS_DEAD)
{
pop_call();
return FALSE;
}
if (victim->in_room)
{
switch (victim->in_room->vnum)
{
case ROOM_VNUM_TEMPLE:
case ROOM_VNUM_SCHOOL:
case ROOM_VNUM_DEATH:
pop_call();
return FALSE;
default:
break;
}
}
if (ch->in_room)
{
switch (ch->in_room->vnum)
{
case ROOM_VNUM_TEMPLE:
case ROOM_VNUM_SCHOOL:
case ROOM_VNUM_DEATH:
pop_call();
return FALSE;
default:
break;
}
}
// ch can't attack if he's out cold.
if (ch->position <= POS_SLEEPING)
{
pop_call();
return FALSE;
}
// /* prevent melee in separate rooms */
// if (ch->in_room != victim->in_room)
// {
// pop_call();
// return FALSE;
// }
pop_call();
return TRUE;
}
/*
* What person is ch actually fighting
*/
CHAR_DATA *who_fighting( CHAR_DATA *ch )
{
push_call("who_fighting(%p)",ch);
//
// pop_call();
// return NULL;
//
if (ch == NULL || ch->last_attacked == NULL)
{
pop_call();
return NULL;
}
if (!is_string(ch->last_attacked->name))
{
log_printf("who_fighting: who->name == NULL for %s", ch->name);
ch->last_attacked = NULL;
dump_stack();
pop_call();
return NULL;
}
pop_call();
return ch->last_attacked;
}
/*
* Is CH grappling, or grappled by someone
*/
CHAR_DATA *who_grappling( CHAR_DATA *ch )
{
CHAR_DATA *grappled;
push_call("who_grappling(%p)",ch);
if ((grappled = ch->grappling) == NULL)
{
if ((grappled = ch->grappled_by) == NULL)
{
pop_call();
return NULL;
}
}
if (!is_string(grappled->name))
{
log_printf("who_grappling: who->name == NULL for %s", ch->name);
if (ch->grappling)
stop_grapple(ch);
if (ch->grappled_by)
stop_grapple(ch->grappled_by);
dump_stack();
pop_call();
return NULL;
}
pop_call();
return grappled;
}
/*
* Choose a new target for aggression
* choose new victim fighting CH first,
* else pick a member of victim's group - Kregor 1/5/08
*/
CHAR_DATA *get_next_fighting(CHAR_DATA *ch, CHAR_DATA *victim)
{
CHAR_DATA *rch, *rch_next;
push_call("get_next_fighting(%p)",ch);
for (rch = ch->in_room->first_person ; rch ; rch = rch_next)
{
rch_next = rch->next_in_room;
if (rch == victim)
continue;
if (!IS_AWAKE(rch))
continue;
if (who_fighting(rch) == NULL)
continue;
if (who_fighting(rch) == ch)
{
pop_call();
return rch;
}
if (is_same_group(who_fighting(rch),ch))
{
pop_call();
return rch;
}
}
pop_call();
return NULL;
}
/*
Start ch fighting against victim.
*/
void set_fighting( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("set_fighting(%p,%p)",ch,victim);
if (ch == victim)
{
pop_call();
return;
}
if (ch->in_room != victim->in_room)
{
pop_call();
return;
}
if (IS_AFFECTED(ch, AFF_SLEEP))
{
affect_strip(ch, gsn_sleep);
}
if (ch->position == POS_STANDING)
{
ch->position = POS_FIGHTING;
}
ch->last_attacked = victim;
if (!IS_NPC(ch) && is_string(ch->pcdata->pose))
STRFREE(ch->pcdata->pose);
if (!IS_NPC(victim) && is_string(victim->pcdata->pose))
STRFREE(victim->pcdata->pose);
if (!IS_NPC(ch))
{
ch->pcdata->last_combat = mud->current_time;
}
if (!IS_NPC(victim))
{
victim->pcdata->last_combat = mud->current_time;
}
update_pos(ch,-1);
pop_call();
return;
}
/*
And stop ch from fighting, and alternately those fighting ch.
*/
void stop_fighting( CHAR_DATA *ch, bool fBoth )
{
CHAR_DATA *victim, *och;
push_call("stop_fighting(%p,%p)",ch,fBoth);
free_unit( ch );
if (!fBoth)
{
pop_call();
return;
}
for (och = mud->f_char ; och ; och = mud->update_wch)
{
mud->update_wch = och->next;
if (och->last_attacked && och->last_attacked == ch)
{
if ((victim = get_next_fighting(och, ch)) == NULL)
{
free_unit (och);
continue;
}
set_fighting(och, victim);
}
}
pop_call();
return;
}
void free_unit( CHAR_DATA *ch )
{
push_call("free_unit(%p)",ch);
ch->last_attacked = NULL;
ch->last_attacker = NULL;
ch->position = POS_STANDING;
ch->attack_part = -1;
ch->initiative = 0;
ch->attack = 0;
ch->action = 0;
if (ch->grappled_by)
{
stop_grapple(ch->grappled_by);
}
if (ch->grappling)
{
stop_grapple(ch);
}
update_pos(ch,-1);
pop_call();
return;
}
/*
* Frees combat data at the end of battle.
*/
void clean_combat (COMBAT_DATA * battle)
{
COMBAT_DATA *list;
CHAR_DATA *unit, *unit_next;
push_call("clean_combat(%p)",battle);
for (unit = battle->unit_list; unit != NULL; unit = unit_next)
{
unit_next = unit->next_in_battle;
char_from_combat (unit);
//Perhaps call raw_kill() here on KOd mobiles?
if (unit->position == POS_DEAD)
raw_kill(unit, -1);
}
if (battle == battle_list)
battle_list = battle->next;
else
for (list = battle_list; list != NULL; list = list->next)
{
if (list->next == battle)
list->next = battle->next;
}
battle->next = NULL;
free_combat (battle);
pop_call();
return;
}
TURN_DATA *new_turn (void)
{
TURN_DATA *turn;
push_call("new_turn()");
ALLOCMEM(turn, TURN_DATA, 1);
turn->next = NULL;
pop_call();
return turn;
}
void free_turn (TURN_DATA * turn)
{
push_call("free_turn(%p)",turn);
turn = turn->next;
FREEMEM(turn);
pop_call();
return;
}
void free_combat (COMBAT_DATA * battle)
{
push_call("free_combat(%p)",battle);
battle = battle->next;
FREEMEM(battle);
pop_call();
return;
}
/*
* Move action, only here for testing purposes.
*/
void do_move (CHAR_DATA *ch, char *argument)
{
push_call("do_move(%p)",ch);
if (!in_combat (ch))
{
send_to_char ("You're not fighting!\n\r", ch);
pop_call();
return;
}
act( "You execute a move action.", ch, NULL, NULL, TO_CHAR);
act( "$n executes a move action.", ch, NULL, NULL, TO_ROOM);
TAKE_ACTION(ch, ACTION_MOVE);
pop_call();
return;
}
/*
* Withdraws a character from combat with check for victory.
*/
void withdraw_combat(CHAR_DATA *ch)
{
COMBAT_DATA * battle;
push_call("withdraw_combat(%p)",ch);
if (!in_combat (ch))
{
pop_call();
return;
}
battle = ch->in_battle;
char_from_combat (ch);
check_victory (battle);
pop_call();
return;
}
/*
* This is no longer supported in code, but left
* the old function deprecated here - Kregor
*/
void add_to_victory_list( CHAR_DATA *ch, CHAR_DATA *victim)
{
int i;
char buf[MAX_STRING_LENGTH];
push_call("add_to_victory_list(%p,%p)",ch,victim);
for (i = 1 ; i < VICTORY_LIST_SIZE ; i++)
{
STRFREE(victory_list[i-1]);
victory_list[i-1] = STRALLOC(victory_list[i]);
}
sprintf(buf, "Z%12s (Level %2d) %12s (Level %2d) %s", ch->name, ch->level, victim->name, victim->level, get_time_string(mud->current_time));
RESTRING(victory_list[i-1], buf);
save_victors();
pop_call();
return;
}
/*
* Qualifiers in the check_murder function - Kregor
*/
bool can_attack( CHAR_DATA *ch, CHAR_DATA *victim)
{
push_call("can_attack(%p,%p)",ch,victim);
if (victim == NULL)
{
pop_call();
return TRUE;
}
// I am god, and I can kill you!
if (IS_GOD(ch))
{
pop_call();
return TRUE;
}
// This means it's not murder.
if (IS_NPC(ch) || IS_NPC(victim))
{
pop_call();
return TRUE;
}
// Anyone in the arena is fair game.
if (in_area(ch, ROOM_VNUM_ARENA) && in_area(victim, ROOM_VNUM_ARENA))
{
pop_call();
return TRUE;
}
// PKill is restricted by character level
if ((!IS_NPC(ch) && ch->level < 3) || (!IS_NPC(victim) && victim->level < 3))
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
* New function to check for pkill allowed,
* control murder restrictions. - Kregor 1/5/08
*/
bool check_murder(CHAR_DATA *ch, CHAR_DATA *victim)
{
bool fMurder = FALSE;
push_call("check_murder(%p,%p)",ch,victim);
if (victim == NULL)
{
pop_call();
return TRUE;
}
if (IS_NPC(victim) && IS_SET(victim->act, ACT_PET) && victim->master)
{
if (victim->master == ch)
{
act( "Why not just DISCHARGE $M instead?", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
if (!in_area(ch, ROOM_VNUM_ARENA))
fMurder = TRUE;
}
if (!IS_NPC(ch) || IS_SET(ch->act, ACT_PET) || ch->desc != NULL)
{
if (!IS_NPC(victim) && !in_area(ch, ROOM_VNUM_ARENA))
fMurder = TRUE;
}
if (!can_attack(ch, victim))
{
pop_call();
return FALSE;
}
if (fMurder)
{
if (ch == victim)
{
pop_call();
return TRUE;
}
if (victim->pcdata->just_died_ctr > 0)
{
send_to_char("That person is currently protected by their god.\n\r", ch);
pop_call();
return FALSE;
}
if (!IS_SET(victim->act, PLR_KILLER) && !IS_SET(victim->act, PLR_THIEF))
{
if (ch->pcdata->last_connect + 120 > mud->current_time)
{
ch_printf_color(ch, "You must wait %d seconds before you can murder anyone.\n\r", ch->pcdata->last_connect + 120 - mud->current_time);
pop_call();
return FALSE;
}
}
if (!IS_NPC(victim) && victim->pcdata->corpse != NULL)
{
act("OOC: $N has been killed quite recently.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
if (!check_add_attack(ch, victim))
{
act("OOC: $N has been attacked too many times recently.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
check_killer( ch, victim );
ch->pcdata->just_died_ctr = 0;
clear_attack_list(ch);
log_printf("ROOM [%u] MURDER %s attacking %s", ch->in_room->vnum, ch->name, victim->name);
}
pop_call();
return TRUE;
}
/*
true if victim is a legal target for a mass attack - Scandum 27/05/2002
*/
bool can_mass_attack( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("can_mass_attack(%p,%p)",ch,victim);
if (victim == ch)
{
pop_call();
return FALSE;
}
if (victim == supermob)
{
pop_call();
return FALSE;
}
// anyone fighting CH is fair game!
if (who_fighting(victim) && who_fighting(victim) == ch)
{
pop_call();
return TRUE;
}
// anyone attacking someone in your group is also fair game!
if (who_fighting(victim) && is_same_group(ch, who_fighting(victim)))
{
pop_call();
return TRUE;
}
if (IS_ACT(victim, ACT_MOBINVIS))
{
pop_call();
return FALSE;
}
if (is_same_group(ch, victim))
{
pop_call();
return FALSE;
}
if (is_master(victim, ch))
{
pop_call();
return FALSE;
}
if (in_area(ch, ROOM_VNUM_ARENA))
{
pop_call();
return TRUE;
}
// Should prevent some nasty AOE kills from mobiles against friendlies
if (IS_NPC(ch) && who_fighting(ch) && !is_same_group(ch, who_fighting(ch)))
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
Rewrote this function to put all the no fighting crap in one
neat function, charmed and possessed mobs attack like players.
Scandum 18/02/02
Revised - Kregor
Rewrote santuary affect per d20 rules. This is separate
from can_melee_attack in that this checks for ability to
do ANY hostile action, not just a physical attack.
*/
bool is_safe( CHAR_DATA *ch, CHAR_DATA *victim)
{
push_call("is_safe(%p,%p)",ch,victim);
if (IS_SET(ch->in_room->room_flags, ROOM_SAFE))
{
send_to_char( "Wards of peace prevent you from doing that.\n\r", ch);
pop_call();
return TRUE;
}
// should assure no auto actions happen from people who are down
if (ch->position < POS_FIGHTING)
{
act("You are in no position to do anything aggressive.", ch, NULL, NULL, TO_CHAR);
pop_call();
return TRUE;
}
// These are only needed for auto actions, commands are snagged in interp.c
if (IS_AFFECTED(ch, AFF2_PETRIFICATION) || (IS_AFFECTED(ch, AFF2_PARALYSIS) && !IS_AFFECTED(ch, AFF_FREEDOM)))
{
pop_call();
return TRUE;
}
if (IS_AFFECTED(ch, AFF2_DAZED))
{
act( "You are too dazed to act.", ch, NULL, NULL, TO_CHAR);
act( "$n simply stands in a daze.", ch, NULL, NULL, TO_ROOM);
pop_call();
return TRUE;
}
if (IS_AFFECTED(ch, AFF2_NAUSEATED))
{
act( "You are too nauseous to act.", ch, NULL, NULL, TO_CHAR);
act( "$n struggles against wretching.", ch, NULL, NULL, TO_ROOM);
pop_call();
return TRUE;
}
if (IS_AFFECTED(ch, AFF2_STUNNED))
{
act( "You are too stunned to act.", ch, NULL, NULL, TO_CHAR);
pop_call();
return TRUE;
}
if (victim == NULL)
{
pop_call();
return FALSE;
}
if (!IS_NPC(ch) && !IS_NPC(victim) && IS_SET(ch->in_room->area->flags, AFLAG_NO_PVP))
{
send_to_char( "You are currently in a no-pvp area.\n\r", ch);
pop_call();
return TRUE;
}
if (victim == supermob)
{
send_to_char( "You cannot attack the SuperMob!\n\r", ch);
pop_call();
return TRUE;
}
if (is_affected(victim, gsn_time_stop))
{
act("You cannot affect $N while $E is frozen in time.", ch, NULL, victim, TO_CHAR);
pop_call();
return TRUE;
}
if (!can_attack( ch, victim ))
{
act( "You may not do that against $N.", ch, NULL, victim, TO_CHAR);
pop_call();
return TRUE;
}
//hostile action removes fascination.
if (IS_AFFECTED(victim, AFF2_FASCINATED))
{
AFFECT_STRIP(victim, AFF2_FASCINATED);
act("Your fascination is broken due to $N's actions.", victim, NULL, ch, TO_CHAR);
}
if (IS_AFFECTED(ch, AFF2_CHARMED))
{
if ((is_affected(ch, gsn_charm_monster) && get_caster(ch, gsn_charm_monster) == victim)
|| (is_affected(ch, gsn_charm_person) && get_caster(ch, gsn_charm_person) == victim)
|| (is_affected(ch, gsn_charm_plant) && get_caster(ch, gsn_charm_plant) == victim)
|| (is_affected(ch, gsn_charm_animal) && get_caster(ch, gsn_charm_animal) == victim))
{
act( "You like $N too much to attack $M.", ch, NULL, victim, TO_CHAR);
pop_call();
return TRUE;
}
}
// already fighting, sanctuary doesn't matter - Kregor
if (who_fighting(ch) == victim)
{
pop_call();
return FALSE;
}
if (who_fighting(victim) == ch)
{
pop_call();
return FALSE;
}
if (IS_AFFECTED(victim, AFF_SANCTUARY))
{
if (!save_resist(ch, victim, gsn_sanctuary, get_affect_level(victim, gsn_sanctuary)))
{
send_to_char( "A magical force stays your hand.\n\r", ch);
pop_call();
return TRUE;
}
}
if (IS_AFFECTED(victim, gsn_undeath_ward) && IS_UNDEAD(ch))
{
if (!save_resist(ch, victim, gsn_undeath_ward, get_affect_level(victim, gsn_undeath_ward)))
{
send_to_char( "A powerful ward stays your hand.\n\r", ch);
pop_call();
return TRUE;
}
}
if (IS_AFFECTED(ch, AFF_DOMINATE) && (is_master(ch, victim) || is_same_group(ch, victim->master)))
{
act( "You may not attack your master.", ch, NULL, victim, TO_CHAR);
pop_call();
return TRUE;
}
if (in_area(ch, ROOM_VNUM_ARENA))
{
pop_call();
return FALSE;
}
if (IS_NPC(ch) && !in_combat(ch) && ch->hit < get_max_hit(ch) / 2 && IS_SET(ch->act, ACT_WIMPY))
{
if (!IS_AFFECTED(ch, AFF_DOMINATE) && !IS_SET(ch->act, ACT_PET) && ch->desc == NULL)
{
pop_call();
return TRUE;
}
}
if (IS_AFFECTED(ch, AFF_DOMINATE) && ch->master == victim )
{
act( "$N is your beloved master.", ch, NULL, victim, TO_CHAR);
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* New function to determine whether an aggressive action
* can be performed outside of combat or out of turn - Kregor 01/2008
* Make sure this is the LAST check before the action, so
* that AoO isn't wasted by other conditions!
*/
bool can_surprise(CHAR_DATA *ch, CHAR_DATA *victim)
{
push_call("can_surprize(%p,%p)",ch,victim);
if (ch == NULL || victim == NULL)
{
pop_call();
return FALSE;
}
if (ch == victim)
{
act("Attack yourself?", ch, NULL, NULL, TO_CHAR);
pop_call();
return FALSE;
}
if (in_combat(ch))
{
if (!is_active(ch))
{
if (!has_opportunity(ch, victim))
{
send_to_char ("Just wait your turn.\n\r", ch);
pop_call();
return FALSE;
}
else if (check_murder(ch, victim))
{
take_opportunity(ch, victim);
pop_call();
return TRUE;
}
}
else
{
pop_call();
return TRUE;
}
}
else if (has_opportunity(ch, victim))
{
if (check_murder(ch, victim))
{
take_opportunity(ch, victim);
pop_call();
return TRUE;
}
}
else if (!can_see(victim, ch) || !IS_AWAKE(victim) || multi(ch, gsn_backstab) != -1)
{
if (check_murder(ch, victim))
{
if (IS_AFFECTED(ch, AFF_HIDE))
{
AFFECT_STRIP(ch, AFF_HIDE);
act( "$n steps out from $s hiding spot.", ch, NULL, NULL, TO_ROOM );
act( "You step out from your hiding spot.", ch, NULL, NULL, TO_CHAR );
}
if (IS_AFFECTED(ch, AFF_INVISIBLE))
{
affect_strip(ch, gsn_invis);
REMOVE_BIT(ch->affected_by, AFF_INVISIBLE);
}
act( "$n takes you by surprise!", ch, NULL, victim, TO_VICT);
act( "You takes $N by surprise!", ch, NULL, victim, TO_CHAR);
act( "$n takes $N by surprise!", ch, NULL, victim, TO_NOTVICT);
pop_call();
return TRUE;
}
}
act ("$N is too wary to be taken by surprise. Try starting a FIGHT first.", ch, NULL, victim, TO_CHAR);
pop_call();
return FALSE;
}
bool check_recently_fought( CHAR_DATA *ch, CHAR_DATA *victim )
{
CHAR_DATA *ch_ld;
int pvnum_ld;
push_call("check_recently_fought(%p,%p)",ch,victim);
if (!IS_NPC(victim))
{
pop_call();
return FALSE;
}
if (victim->npcdata->pvnum_last_hit == 0)
{
pop_call();
return FALSE;
}
if (MP_VALID_MOB(ch))
{
pop_call();
return FALSE;
}
ch_ld = ch;
if (IS_NPC(ch) && ch->master)
{
ch_ld = ch_ld->master;
}
if (ch_ld->leader != NULL)
{
ch_ld = ch_ld->leader;
}
if (!IS_NPC(ch_ld))
{
pvnum_ld = ch_ld->pcdata->pvnum;
}
else
{
pvnum_ld = 0;
}
if (ch_ld == NULL || IS_NPC(ch_ld))
{
pop_call();
return TRUE;
}
if (pvnum_ld != victim->npcdata->pvnum_last_hit)
{
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
See if an attack justifies a KILLER flag.
*/
void check_killer( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("check_killer(%p,%p)",ch,victim);
if (IS_PLR(victim, PLR_KILLER))
{
pop_call();
return;
}
if (!IS_PLR(ch, PLR_KILLER))
{
send_to_char("*** PLAYER KILLER status recorded. ***\n\r", ch);
}
SET_BIT(ch->act, PLR_KILLER);
ch->pcdata->killer_played = ch->pcdata->played;
pop_call();
return;
}
/*
Code for total and max pvnum attacks - Chaos 4/20/99
*/
bool check_add_attack( CHAR_DATA *ch, CHAR_DATA *victim )
{
push_call("check_add_attack(%p,%p)",ch,victim);
/*
Killers and Thieves can always be attacked - Scandum 07/02/02
*/
if (!IS_NPC(ch) && !IS_NPC(victim))
{
int oldest_attack, total_pvnum_attacks, attacks_today, cnt;
attacks_today = 0;
oldest_attack = 0;
total_pvnum_attacks = 0;
for (cnt = 0 ; cnt < MAX_PK_ATTACKS ; cnt++)
{
if (victim->pcdata->last_pk_attack_time[cnt] == 0)
{
oldest_attack = cnt;
}
else if (victim->pcdata->last_pk_attack_time[oldest_attack] > victim->pcdata->last_pk_attack_time[cnt])
{
oldest_attack = cnt;
}
if (victim->pcdata->last_pk_attack_time[cnt] > mud->current_time)
{
attacks_today++;
}
if (ch->pcdata->pvnum == victim->pcdata->last_pk_attack_pvnum[cnt]
&& victim->pcdata->last_pk_attack_time[cnt] > mud->current_time)
{
total_pvnum_attacks++;
}
}
if (!IS_SET(victim->act, PLR_KILLER) && !IS_SET(victim->act, PLR_THIEF))
{
if (attacks_today >= MAX_PK_ATTACKS)
{
pop_call();
return( FALSE );
}
if (total_pvnum_attacks >= 5)
{
pop_call();
return( FALSE );
}
victim->pcdata->last_pk_attack_time[oldest_attack] = mud->current_time + 60 * 60 * 6;
victim->pcdata->last_pk_attack_pvnum[oldest_attack] = ch->pcdata->pvnum;
STRFREE(victim->pcdata->last_pk_attack_name[oldest_attack]);
victim->pcdata->last_pk_attack_name[oldest_attack] = STRALLOC(ch->name);
}
}
pop_call();
return( TRUE );
}
/*
Scandum 14-01-2003
*/
void clear_attack_list( CHAR_DATA *ch )
{
int cnt;
push_call("clear_attack_list(%p)",ch);
for (cnt = 0 ; cnt < MAX_PK_ATTACKS ; cnt++)
{
ch->pcdata->last_pk_attack_time[cnt] = 0;
STRFREE(ch->pcdata->last_pk_attack_name[cnt]);
ch->pcdata->last_pk_attack_name[cnt] = STRDUPE(str_empty);
}
pop_call();
return;
}
void spam_attack_list( CHAR_DATA *ch )
{
int cnt;
push_call("spam_attack_list(%p)",ch);
for (cnt = 0 ; cnt < MAX_PK_ATTACKS ; cnt++)
{
ch->pcdata->last_pk_attack_time[cnt] = mud->current_time + 60 * 60 * 24;
ch->pcdata->last_pk_attack_pvnum[cnt] = 0;
STRFREE(ch->pcdata->last_pk_attack_name[cnt]);
ch->pcdata->last_pk_attack_name[cnt] = STRDUPE(str_empty);
}
pop_call();
return;
}
/*
* Set position of a victim.
* Updated to include D20 guidelines
* for stunning and unconsciousness - Kregor 10/07
*/
void update_pos( CHAR_DATA *victim, int pos )
{
CHAR_DATA *gch;
push_call("update_pos(%p)",victim);
// really, can't get any deader.
if (victim->position == POS_DEAD)
{
free_cast(victim);
free_skill(victim);
pop_call();
return;
}
if (in_combat(victim) && in_camp(victim))
{
for (gch = victim->in_room->first_person ; gch ; gch = gch->next_in_room)
{
if (is_same_group(victim, gch) && IS_AFFECTED(gch, AFF2_CAMPING))
{
send_to_char( "Your camp is destroyed!\n\r", victim );
REMOVE_AFFECT(gch, AFF2_CAMPING);
break;
}
}
}
if (in_combat(victim) && victim->rest > 0)
{
send_to_char( "Your rest is spoiled!\n\r", victim );
victim->rest = 0;
}
if ((!CAN_CRITICAL(victim) && victim->hit <= 0)
|| victim->hit <= -10)
{
victim->position = POS_DEAD;
free_cast(victim);
free_skill(victim);
pop_call();
return;
}
if (victim->level + get_apply(victim, APPLY_LEVEL) <= 0)
{
act( "$tYou succumb to energy drain!", victim, get_color_code(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), NULL, TO_CHAR);
act( "$t$n succumbs to energy drain!", victim, get_color_code(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), NULL, TO_ROOM);
victim->position = POS_DEAD;
free_cast(victim);
free_skill(victim);
pop_call();
return;
}
if (get_curr_con(victim) == 0)
{
act( "$tYou succumb to consitution loss!", victim, get_color_code(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), NULL, TO_CHAR);
victim->position = POS_DEAD;
free_cast(victim);
free_skill(victim);
pop_call();
return;
}
if (pos != -1)
{
victim->position = URANGE(0, pos, POS_STANDING);
}
if (victim->position < POS_FIGHTING)
{
if (is_affected(victim, gsn_defensive_stance))
{
affect_strip(victim, gsn_defensive_stance);
}
}
if (victim->hit > 0 && victim->nonlethal <= victim->hit)
{
if (get_curr_wis(victim) == 0 || get_curr_int(victim) == 0 || get_curr_cha(victim) == 0)
{
if (victim->position > POS_INCAP)
{
act( "$tYou collapse into a catatonic state!", victim, get_color_code(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), NULL, TO_CHAR);
}
victim->position = POS_INCAP;
}
else if (get_curr_str(victim) == 0 || get_curr_dex(victim) == 0)
{
if (victim->position > POS_INCAP)
{
act( "$tYou fall paralyzed to the ground!", victim, get_color_code(victim, COLOR_YOU_ARE_HIT, VT102_BOLD), NULL, TO_CHAR);
}
victim->position = POS_INCAP;
}
else if (IS_AFFECTED(victim, AFF2_UNCONSCIOUS))
{
victim->position = POS_STUNNED;
}
else if (drunk_level(victim) >= DRUNK_TOTALLED)
{
victim->position = POS_STUNNED;
}
else if (victim->position <= POS_STUNNED)
{
act( "You recover consciousness.", victim, NULL, NULL, TO_CHAR);
act( "$n recovers consciousness.", victim, NULL, NULL, TO_ROOM);
victim->position = POS_RESTING;
}
// stunned chars drop their wielded weapons - Kregor
if (IS_AFFECTED(victim, AFF2_STUNNED) || victim->position <= POS_STUNNED)
{
OBJ_DATA *obj;
if ((obj = get_wield(victim, FALSE)) != NULL)
{
act( "$p slips out of your grasp.", victim, obj, NULL, TO_CHAR );
act( "$p slips out of $n's grasp.", victim, obj, NULL, TO_ROOM );
unequip_char(victim, obj, FALSE);
}
if ((obj = get_wield(victim, TRUE)) != NULL)
{
act( "$p slips out of your grasp.", victim, obj, NULL, TO_CHAR );
act( "$p slips out of $n's grasp.", victim, obj, NULL, TO_ROOM );
unequip_char(victim, obj, FALSE);
}
}
if (victim->position <= POS_SLEEPING)
{
free_cast(victim);
free_skill(victim);
}
pop_call();
return;
}
if (victim->position != POS_INCAP)
{
if (victim->hit <= -1)
{
if (!race_skill(victim, gsn_ferocity) || victim->uses[gsn_ferocity])
{
if (!learned(victim, gsn_diehard) || !will_save(victim, NULL, 10 - (victim->hit * 2), -1))
{
victim->position = POS_MORTAL;
}
}
}
else if (victim->hit == 0 || !domain_apotheosis(victim, DOMAIN_SUFFERING))
{
victim->position = POS_STUNNED;
}
}
if (victim->position <= POS_SLEEPING)
{
free_cast(victim);
free_skill(victim);
}
pop_call();
return;
}
void group_gain( CHAR_DATA *ch, CHAR_DATA *victim )
{
CHAR_DATA *gch;
int xp, group_bonus, group_count, bonus;
push_call("group_gain(%p,%p)",ch,victim);
if (IS_AFFECTED(victim, AFF_DOMINATE) || IS_ACT(victim, ACT_PET))
{
pop_call();
return;
}
if (IS_NPC(victim) && victim->pIndexData->vnum == 9901) /* No Demons */
{
pop_call();
return;
}
group_bonus = group_count = 0;
for (gch = ch->in_room->first_person ; gch ; gch = gch->next_in_room)
{
if (is_same_group(gch, ch))
{
group_count += 1;
if (IS_NPC(gch))
continue;
group_bonus += 1;
gch->alignment -= victim->alignment / 50;
gch->ethos -= victim->ethos / 50;
gch->alignment = URANGE(-1000, gch->alignment, 1000);
gch->ethos = URANGE(-1000, gch->ethos, 1000);
}
}
bonus = group_bonus * 10;
bonus += 100;
for (gch = ch->in_room->first_person ; gch ; gch = gch->next_in_room)
{
if (!is_same_group(gch, ch) || IS_NPC(gch))
{
continue;
}
xp = xp_compute(gch, victim);
if (group_count > 1)
xp = xp * bonus / 100;
wiz_printf("group_gain(%s) - bonus xp: %d", gch->name, xp);
xp /= group_count;
wiz_printf("group_gain(%s) - share: %d", gch->name, xp);
ch_printf_color(gch, "You receive %d experience points.", xp);
ch_printf_color(gch, "\n\r");
gain_exp(gch, xp);
if (!gain_favor(gch, DOMAIN_WAR, 1))
if (!gain_favor(gch, DOMAIN_DESTRUCTION, 1))
if (!gain_favor(gch, DOMAIN_RETRIBUTION, 1))
if (!gain_favor(gch, DOMAIN_SUFFERING, 1))
gain_favor(gch, DOMAIN_WRATH, 1);
if (faith_enemies(gch, victim))
gain_favor(gch, -1, 1);
if (which_god(gch) == which_god(victim))
gain_favor(gch, -1, -5);
check_zap(gch, TRUE);
}
pop_call();
return;
}
/*
* Calculate how much experience a character is worth.
* Formula derived from the OSRIC ruleset, and thus does not
* infringe upon non-Open Gaming content from WotC - Kregor
*/
int get_exp_worth( CHAR_DATA *ch )
{
int exp;
int level = UMIN(ch->level, 21);
int sn;
push_call("get_exp_worth(%p)",ch);
//base XP is based on hit dice
exp = (level + level - 1) * 10;
wiz_printf("get_exp_worth(%s) - base xp: %d", ch->name, exp);
//add 1 xp per hit point, per level of victim
exp += get_max_hit(ch) * level;
// each exceptional race attack grants +50% of base xp
for (sn = 0 ; *skill_table[sn].name != '\0' ; sn++)
{
switch (skill_table[sn].skilltype)
{
default:
break;
case FSKILL_RACEATTACK:
if (race_skill(ch, sn))
exp += (level + level - 1) * 5;
break;
case FSKILL_SPELL:
if (race_skill(ch, sn))
{
if (skill_table[sn].native_level <= 3)
exp += (level + level - 1) * 5 / 2; // spell-like abilities of 3rd or below only +25%
else
exp += (level + level - 1) * 5;
}
break;
}
}
wiz_printf("get_exp_worth(%s) - final xp: %d", ch->name, exp);
pop_call();
return exp;
}
/*
* Adjust XP awarded to a char based on
* level difference with victim - Kregor
*/
int xp_compute( CHAR_DATA *gch, CHAR_DATA *victim )
{
int xp;
int spread;
push_call("xp_compute(%p,%p)",gch,victim);
if (gch->in_room == NULL)
{
pop_call();
return(0);
}
if (IS_SET(gch->in_room->room_flags, ROOM_RIP))
{
pop_call();
return(0);
}
xp = get_exp_worth(victim);
if (!IS_NPC(victim))
{
xp = 0;
}
spread = 10 + gch->level / 10;
if (gch->level > victim->level)
{
xp = xp * spread / (gch->level - victim->level + spread);
}
pop_call();
return UMAX(1, xp);
}
/*
* Calc the experience worth based on a stated
* Challenge Rating per OSRIC calculations - Kregor
*/
int xp_compute_cr( CHAR_DATA *gch, int challenge )
{
int xp, spread;
push_call("xp_compute(%p,%p)",gch,challenge);
xp = (challenge + challenge - 1) * 10;
xp += 5 * challenge;
spread = 10 + gch->level / 10;
if (gch->level > challenge)
{
xp = xp * spread / (gch->level - challenge + spread);
}
pop_call();
return UMAX(1, xp);
}
/*
* The SPAM stat line
* could use for the Status spell
*/
void show_party_line( CHAR_DATA *ch )
{
CHAR_DATA *fch;
char buf[MAX_STRING_LENGTH], buf2[200], buf3[200];
push_call("show_party_line(%p)",ch);
buf[0]='\0';
for (fch = ch->in_room->first_person; fch != NULL; fch = fch->next_in_room)
{
if (fch != ch && is_same_group(ch, fch))
{
strcpy(buf3, !IS_NPC(fch) ? capitalize(fch->name) : capitalize(fch->short_descr));
buf3[6]='\0';
sprintf(buf2, "%s:%d/%d ", buf3, fch->hit, get_max_hit(fch));
strcat(buf, buf2);
}
}
if (buf[0] != '\0')
{
ch_printf_color(ch, "%s%s\n\r", get_color_string(ch,COLOR_PROMPT,VT102_DIM), buf);
}
pop_call();
return;
}
bool is_light_weapon(CHAR_DATA *ch, OBJ_DATA *obj)
{
push_call("is_light_weapon(%p)",ch,obj);
if (obj == NULL || !IS_OBJ_TYPE(obj, ITEM_WEAPON))
{
pop_call();
return FALSE;
}
if (get_size(ch) > obj->size)
{
pop_call();
return TRUE;
}
else if (get_size(ch) == obj->size && WSPEC(obj, WSPEC_LIGHT))
{
pop_call();
return TRUE;
}
else
{
pop_call();
return FALSE;
}
}
/*
* Find the range between ch and victim for ranged attack
*/
int get_range(CHAR_DATA *ch, CHAR_DATA *victim)
{
int range;
push_call("get_range(%p,%p)",ch,victim);
if (ch == NULL || victim == NULL)
{
bug("get_range: NULL char!",0);
pop_call();
return 0;
}
for (range = 0 ; range < MAX_VNUM ; range++)
{
if (findpath_search_victim(ch, victim, range) == -1)
continue;
wiz_printf_room(ch, "get_range(%s,%s): range: %d\n\r", get_name(ch), get_name(victim), range);
wiz_printf_room(victim, "get_range(%s,%s): range: %d\n\r", get_name(ch), get_name(victim), range);
pop_call();
return range;
}
pop_call();
return -1;
}
/* returns weapon proficiency for a wielded weapon - Kregor 2/18/07 */
int weapon_skill( CHAR_DATA *ch, OBJ_DATA *wield )
{
int prof;
push_call("weapon_skill(%p,%p)",ch,wield);
/* needs a safety in here */
if (wield != NULL && !IS_WEAPON(wield) && !IS_AMMO(wield))
{
pop_call();
return 0;
}
// treat NULL as unarmed strike - Kregor
if (wield == NULL)
{
prof = learned(ch, gsn_unarmed_strike);
}
else
{
switch (wield->value[0])
{
default:
bug("weapon_skill: bad weapon type", 0);
prof = 0;
break;
case WEAPON_TYPE_AXE_DOUBLE:
prof = learned(ch,gsn_weapon_double_axe);
if ((ch->race == RACE_ORC || ch->race == RACE_HALFORC) && learned(ch, gsn_weapon_prof_martial))
{
SET_BIT(prof, WSKILL_PROFICIENT);
}
break;
case WEAPON_TYPE_HANDAXE:
prof = (learned(ch,gsn_weapon_handaxe));
break;
case WEAPON_TYPE_AXE_THROWING:
prof = (learned(ch,gsn_weapon_throwing_axe));
break;
case WEAPON_TYPE_BATTLEAXE:
prof = (learned(ch,gsn_weapon_battleaxe));
break;
case WEAPON_TYPE_BOLA:
prof = (learned(ch,gsn_weapon_bola));
break;
case WEAPON_TYPE_CHAIN_SPIKED:
prof = (learned(ch,gsn_weapon_spiked_chain));
break;
case WEAPON_TYPE_CLUB:
prof = (learned(ch,gsn_weapon_club));
break;
case WEAPON_TYPE_CROSSBOW_HAND:
prof = (learned(ch,gsn_weapon_crossbow_hand));
break;
case WEAPON_TYPE_CROSSBOW_HEAVY:
prof = (learned(ch,gsn_weapon_crossbow_heavy));
break;
case WEAPON_TYPE_CROSSBOW_LIGHT:
prof = (learned(ch,gsn_weapon_crossbow_light));
break;
case WEAPON_TYPE_DAGGER:
case WEAPON_TYPE_KNIFE:
prof = (learned(ch,gsn_weapon_short_blade));
break;
case WEAPON_TYPE_DART:
prof = (learned(ch,gsn_weapon_dart));
break;
case WEAPON_TYPE_FALCHION:
prof = (learned(ch,gsn_weapon_falchion));
break;
case WEAPON_TYPE_FLAIL:
case WEAPON_TYPE_FLAIL_HEAVY:
prof = (learned(ch,gsn_weapon_flail));
break;
case WEAPON_TYPE_FLAIL_DIRE:
prof = (learned(ch,gsn_weapon_flail_dire));
break;
case WEAPON_TYPE_GLAIVE:
prof = (learned(ch,gsn_weapon_glaive));
break;
case WEAPON_TYPE_GREATAXE:
prof = (learned(ch,gsn_weapon_greataxe));
break;
case WEAPON_TYPE_GREATCLUB:
prof = (learned(ch,gsn_weapon_greatclub));
break;
case WEAPON_TYPE_GREATSWORD:
prof = (learned(ch,gsn_weapon_greatsword));
break;
case WEAPON_TYPE_GUISARME:
prof = (learned(ch,gsn_weapon_guisarme));
break;
case WEAPON_TYPE_HALBERD:
prof = (learned(ch,gsn_weapon_halberd));
break;
case WEAPON_TYPE_HAMMER_LIGHT:
case WEAPON_TYPE_WARHAMMER:
prof = (learned(ch,gsn_weapon_hammer));
break;
case WEAPON_TYPE_JAVELIN:
prof = (learned(ch,gsn_weapon_javelin));
break;
case WEAPON_TYPE_KAMA:
prof = (learned(ch,gsn_weapon_kama));
break;
case WEAPON_TYPE_KUKRI:
prof = (learned(ch,gsn_weapon_kukri));
break;
case WEAPON_TYPE_LANCE:
prof = (learned(ch,gsn_weapon_lance));
break;
case WEAPON_TYPE_LONGBOW:
prof = (learned(ch,gsn_weapon_longbow));
break;
case WEAPON_TYPE_LONGBOW_COMPOSITE:
prof = (learned(ch,gsn_weapon_longbow_composite));
break;
case WEAPON_TYPE_LONGSPEAR:
case WEAPON_TYPE_SHORTSPEAR:
case WEAPON_TYPE_SPEAR:
prof = (learned(ch,gsn_weapon_spear));
break;
case WEAPON_TYPE_MACE_HEAVY:
case WEAPON_TYPE_MACE_LIGHT:
prof = (learned(ch,gsn_weapon_mace));
break;
case WEAPON_TYPE_MORNINGSTAR:
prof = (learned(ch,gsn_weapon_morningstar));
break;
case WEAPON_TYPE_NUNCHAKU:
prof = (learned(ch,gsn_weapon_nunchaku));
break;
case WEAPON_TYPE_PICK_HEAVY:
case WEAPON_TYPE_PICK_LIGHT:
prof = (learned(ch,gsn_weapon_pick));
break;
case WEAPON_TYPE_QUARTERSTAFF:
prof = (learned(ch,gsn_weapon_quarterstaff));
break;
case WEAPON_TYPE_RANSEUR:
prof = (learned(ch,gsn_weapon_ranseur));
break;
case WEAPON_TYPE_RAPIER:
prof = (learned(ch,gsn_weapon_rapier));
break;
case WEAPON_TYPE_SAI:
prof = (learned(ch,gsn_weapon_sai));
break;
case WEAPON_TYPE_SAP:
prof = (learned(ch,gsn_weapon_sap));
break;
case WEAPON_TYPE_SCIMITAR:
prof = (learned(ch,gsn_weapon_scimitar));
break;
case WEAPON_TYPE_SCYTHE:
prof = (learned(ch,gsn_weapon_scythe));
break;
case WEAPON_TYPE_SHORTBOW:
prof = (learned(ch,gsn_weapon_shortbow));
break;
case WEAPON_TYPE_SHORTBOW_COMPOSITE:
prof = (learned(ch,gsn_weapon_shortbow_composite));
break;
case WEAPON_TYPE_SHURIKEN:
prof = (learned(ch,gsn_weapon_shuriken));
break;
case WEAPON_TYPE_CHAKRAM:
prof = (learned(ch,gsn_weapon_chakram));
break;
case WEAPON_TYPE_SIANGHAM:
prof = (learned(ch,gsn_weapon_siangham));
break;
case WEAPON_TYPE_SICKLE:
prof = (learned(ch,gsn_weapon_sickle));
break;
case WEAPON_TYPE_SLING:
prof = (learned(ch,gsn_weapon_sling));
break;
case WEAPON_TYPE_SWORD_BASTARD:
prof = (learned(ch,gsn_weapon_sword_bastard));
break;
case WEAPON_TYPE_SWORD_LONG:
prof = (learned(ch,gsn_weapon_sword_long));
break;
case WEAPON_TYPE_SWORD_SHORT:
prof = (learned(ch,gsn_weapon_sword_short));
break;
case WEAPON_TYPE_SWORD_TWO_BLADED:
prof = (learned(ch,gsn_weapon_sword_double));
break;
case WEAPON_TYPE_TRIDENT:
prof = (learned(ch,gsn_weapon_trident));
break;
case WEAPON_TYPE_URGROSH_DWARVEN:
prof = (learned(ch,gsn_weapon_urgrosh));
if (IS_RACE(ch, RACE_DWARF) && learned(ch, gsn_weapon_prof_martial))
{
SET_BIT(prof, WSKILL_PROFICIENT);
}
break;
case WEAPON_TYPE_ELVEN_THINBLADE:
prof = (learned(ch,gsn_weapon_thinblade));
if (IS_RACE(ch, RACE_ELF) && learned(ch, gsn_weapon_prof_martial))
{
SET_BIT(prof, WSKILL_PROFICIENT);
}
break;
case WEAPON_TYPE_WARAXE_DWARVEN:
prof = (learned(ch,gsn_weapon_waraxe_dwarven));
if (IS_RACE(ch, RACE_DWARF) && learned(ch, gsn_weapon_prof_martial))
{
SET_BIT(prof, WSKILL_PROFICIENT);
}
break;
case WEAPON_TYPE_WHIP:
prof = (learned(ch,gsn_weapon_whip));
break;
}
/*
* cleric gains weapon proficiency in deity's weapon of choice
* cleric of War domain gets specialization in weapon - Kregor
*/
if (class_level(ch, CLASS_CLERIC) && ch->god && god_table[ch->god].favored_weapon == wield->value[0])
{
SET_BIT(prof, WSKILL_PROFICIENT);
if (has_domain(ch, DOMAIN_WAR))
{
SET_BIT(prof, WSKILL_SPECIALIZED);
}
}
}
pop_call();
return prof;
}
/*
Find the ac value of armor worn. Magic plusses are added by affects - Kregor
*/
int apply_ac( CHAR_DATA *ch )
{
OBJ_DATA *obj;
int ac;
push_call("apply_ac(%p,%p)",ch);
// added automatic armor for NPCs
if (IS_NPC(ch) && ch->npcdata->armor_type)
{
pop_call();
return armor_table[ch->npcdata->armor_type].ac_bonus;
}
for (ac = 0, obj = ch->first_carrying ; obj ; obj = obj->next_content)
{
if (!IS_WORN(obj))
continue;
if ( obj->item_type != ITEM_ARMOR )
continue;
if ( obj->value[0] == ARMOR_TYPE_CLOTH )
continue;
// if (get_layer(obj) != LAYER_ARMOR)
// continue;
//
switch ( obj->wear_loc )
{
case WEAR_BODY:
ac += 30 * armor_table[obj->value[0]].ac_bonus;
break;
case WEAR_HEAD:
ac += 20 * armor_table[obj->value[0]].ac_bonus;
break;
case WEAR_LEGS:
ac += 20 * armor_table[obj->value[0]].ac_bonus;
break;
case WEAR_FEET:
ac += 5 * armor_table[obj->value[0]].ac_bonus;
break;
case WEAR_ARMS:
ac += 20 * armor_table[obj->value[0]].ac_bonus;
break;
case WEAR_HANDS:
ac += 5 * armor_table[obj->value[0]].ac_bonus;
break;
default:
ac += 0;
break;
}
}
ac /= 100;
pop_call();
return ac;
}
/*
* Calculate base AC of ch
* Added argument for touch attacks
*/
int GET_AC(CHAR_DATA *ch, bool touch)
{
int AC, armor, deflect, natural;
push_call("GET_AC(%p)",ch);
AC = armor = deflect = natural = 0;
if (!touch)
{
armor = UMAX(get_apply(ch, APPLY_ARMOR), ch->armor);
if (!IS_NPC(ch) || (IS_NPC(ch) && !ch->race))
natural = race_table[get_race(ch)].nat_armor;
else if (IS_NPC(ch))
natural = ch->pIndexData->nat_armor;
natural += ch->nat_armor;
natural = UMAX(get_apply(ch, APPLY_NATURAL_AC), natural);
}
deflect = UMAX(get_apply(ch, APPLY_DEFLECT), race_table[get_race(ch)].deflection);
if (domain_apotheosis(ch, DOMAIN_PROTECTION))
deflect = stat_bonus(TRUE, ch, APPLY_WIS);
// incorporeals gain deflection AC == their CHA bonus - Kregor
if (IS_INCORPOREAL(ch))
deflect = UMAX(deflect, stat_bonus(TRUE, ch, APPLY_CHA));
AC = armor;
AC += deflect;
AC += natural;
AC += get_apply(ch, APPLY_ENHANCE_AC); // takes greater of magic armor vs. Magic Vestment affect - Kregor
if (class_level(ch, CLASS_MONK) && armor_type_worn(ch) == ARMOR_NONE
&& get_eq_char(ch, WEAR_SHIELD) == NULL && !IS_ENCUMBERED(ch) && !IS_HELPLESS(ch))
{
AC += class_level(ch, CLASS_MONK) / 5;
AC += stat_bonus(TRUE, ch, APPLY_WIS);
}
if (is_affected(ch, gsn_defensive_stance) && IS_AWAKE(ch))
{
AC += stat_bonus(TRUE, ch, APPLY_CON);
}
switch (get_size(ch))
{
case SIZE_FINE:
AC += 8;
break;
case SIZE_DIMINUTIVE:
AC += 4;
break;
case SIZE_TINY:
AC += 2;
break;
case SIZE_SMALL:
AC += 1;
break;
case SIZE_LARGE:
AC -= 1;
break;
case SIZE_HUGE:
AC -= 2;
break;
case SIZE_GARGANTUAN:
AC -= 4;
break;
case SIZE_COLOSSAL:
AC -= 8;
break;
}
if (IS_AFFECTED(ch, AFF2_STUNNED))
AC -= 2;
// AC penalty for a charge
if (IS_SET(ch->attack, ATTACK_CHARGE))
AC -= 2;
pop_call();
return(AC+10);
}
/*
* adds shield bonus against immediate opponent
*/
int shield_bonus( CHAR_DATA *ch )
{
int shield;
OBJ_DATA *obj;
push_call("shield_bonus(%p)",ch);
if (IS_HELPLESS(ch))
{
pop_call();
return get_apply(ch, APPLY_SHIELD);
}
if (is_affected(ch, gsn_irresistible_dance))
{
pop_call();
return get_apply(ch, APPLY_SHIELD);
}
shield = 0;
if ((obj = get_eq_char(ch, WEAR_SHIELD)) != NULL && IS_OBJ_TYPE(obj, ITEM_ARMOR))
{
shield = armor_table[obj->value[0]].ac_bonus;
shield += obj->apply[APPLY_ENHANCE_AC];
if (learned(ch, gsn_shield_specialization))
{
if (learned(ch, gsn_active_shield_defense)
&& COMBATMODE(ch, COMBAT_DEFENSIVE) && IS_SET(ch->attack, ATTACK_MELEE))
shield += 4;
else
shield += 1;
}
if (IS_SET(ch->attack, ATTACK_SHIELD_BASH) && !learned(ch, gsn_imp_shield_bash))
shield = 0;
}
else if (get_wield(ch, TRUE) != NULL && learned(ch, gsn_two_weapon_defense))
{
if (IS_SET(ch->combatmode, COMBAT_DEFENSIVE) && IS_SET(ch->attack, ATTACK_MELEE))
{
shield = 2;
}
else
{
shield = 1;
}
}
shield = UMAX(get_apply(ch, APPLY_SHIELD), shield);
pop_call();
return(shield);
}
/*
* Dodge bonuses applied if can_dodge
*/
int dodge_bonus( CHAR_DATA *ch )
{
int dodge = 0;
int dex_max = dex_bonus_max(ch);
push_call("dodge_bonus(%p)",ch);
dodge = stat_bonus(TRUE, ch, APPLY_DEX);
if (dex_max != -1)
dodge = UMIN(dodge, dex_max);
dodge += get_apply(ch, APPLY_DODGE);
dodge += get_apply(ch, APPLY_INS_AC);
if (learned(ch, gsn_dodge))
{
dodge += 1;
if (get_monk_style(ch) == STYLE_COBRA_STRIKE && class_level(ch, CLASS_MONK) >= 12)
dodge += 1;
}
if (learned(ch, gsn_canny_defense) && armor_type_worn(ch) == ARMOR_NONE
&& get_eq_char(ch, WEAR_SHIELD) == NULL)
{
dodge += UMIN(stat_bonus(TRUE, ch, APPLY_INT), multi_class_level(ch, gsn_canny_defense));
}
if (is_mounted(ch) && learned(ch->master, gsn_mounted_defense))
dodge += URANGE(0, learned(ch->master, gsn_mount), stat_bonus(TRUE, ch->master, APPLY_DEX));
if (IS_SET(ch->combatmode, COMBAT_DEFENSIVE) && !MISSILE_WIELD(ch) && IS_SET(ch->attack, ATTACK_MELEE))
{
dodge += (learned(ch, gsn_combat_expertise) ? UMIN(stat_bonus(TRUE, ch, APPLY_DEX), base_attack(ch)) : 2);
if (learned(ch, gsn_tumble) >= 5)
{
dodge += 1;
if (learned(ch, gsn_combat_expertise))
dodge += 1;
}
if (learned(ch, gsn_elaborate_defense))
dodge += multi_class_level(ch, gsn_elaborate_defense) / 2;
}
pop_call();
return dodge;
}
/*
* Dodge penalties still apply if !can_dodge
*/
int dodge_penalty( CHAR_DATA *ch )
{
int dodge = 0;
push_call("dodge_penalty(%p)",ch);
dodge += stat_bonus(TRUE, ch, APPLY_DEX);
dodge += get_apply(ch, APPLY_DODGE);
pop_call();
return UMIN(dodge, 0);
}
/*
* Does the character have reach weapon? - Kregor
*/
bool has_reach( CHAR_DATA *ch, OBJ_DATA *wield )
{
push_call("has_reach(%p,%p)",ch,wield);
if (!wield)
{
pop_call();
return FALSE;
}
if (WSPEC(wield, WSPEC_REACH))
{
pop_call();
return TRUE;
}
pop_call();
return FALSE;
}
/*
* Qualifiers for evasion or imp. evasion feat
*/
bool can_evade( CHAR_DATA *ch )
{
push_call("can_evade(%p)",ch);
if (!IS_HELPLESS(ch))
{
pop_call();
return FALSE;
}
if (!learned(ch, gsn_evasion))
{
if (!learned(ch, gsn_unstoppable_rage) || !IS_AFFECTED(ch, AFF2_BERSERK))
{
pop_call();
return FALSE;
}
}
if (armor_type_worn(ch) >= ARMOR_MEDIUM)
{
pop_call();
return FALSE;
}
if (IS_AFFECTED(ch, AFF2_STUNNED))
{
pop_call();
return FALSE;
}
if (IS_FLATFOOTED(ch) && !learned(ch,gsn_uncanny_dodge))
{
pop_call();
return FALSE;
}
if (!IS_AWAKE(ch))
{
pop_call();
return FALSE;
}
if (IS_OVERLOADED(ch))
{
pop_call();
return FALSE;
}
pop_call();
return TRUE;
}
/*
* Returns the total Base Attack bonus for all classes - Kregor 11/26/06
*/
int base_attack( CHAR_DATA *ch )
{
int bab, lvl, cnt, type;
push_call("base_attack(%p)",ch);
lvl = ch->level;
bab = 0;
if (is_affected(ch, gsn_transformation) || is_affected(ch, gsn_divine_power))
{
pop_call();
return lvl;
}
if (!IS_CLASSED(ch))
{
type = race_type(ch);
if (race_type_table[type].base_attack == BAB_BEST)
bab += lvl;
else if (race_type_table[type].base_attack == BAB_POOR)
bab += lvl / 2;
else
bab += lvl * 3 / 4;
}
else
{
float bab_hi, bab_md, bab_lo;
for (cnt = bab_lo = bab_md = bab_hi = 0 ; cnt < MAX_CLASS ; cnt++)
{
if (lvl <= 0)
break;
if (class_level(ch, cnt) > 0)
{
if (cnt == CLASS_MONSTER)
{
type = race_type(ch);
if (race_type_table[type].base_attack == BAB_BEST)
bab_hi += UMIN(class_level(ch, cnt), lvl);
else if (race_type_table[type].base_attack == BAB_POOR)
bab_lo += UMIN(class_level(ch, cnt), lvl);
else
bab_md += UMIN(class_level(ch, cnt), lvl);
}
else if (cnt == CLASS_CLERIC && domain_apotheosis(ch, DOMAIN_WAR))
bab_hi += UMIN(class_level(ch, cnt), lvl);
else if (class_table[cnt].base_attack == BAB_POOR)
bab_lo += UMIN(class_level(ch, cnt), lvl);
else if (class_table[cnt].base_attack == BAB_GOOD)
bab_md += UMIN(class_level(ch, cnt), lvl);
else if (class_table[cnt].base_attack == BAB_BEST)
bab_hi += UMIN(class_level(ch, cnt), lvl);
lvl -= UMIN(class_level(ch, cnt), lvl);
}
}
bab = 0;
if (bab_hi > 0)
bab += bab_hi;
if (bab_md > 0)
bab += bab_md * 3 / 4;
if (bab_lo > 0)
bab += bab_lo / 2;
}
pop_call();
return(bab);
}