{ Summary: Damage & experience routines ## $Id: fight.pas,v 1.7 2004/04/21 20:50:31 druid Exp $ } unit fight; interface uses Math, SysUtils, player, chars; { fighting constants } const FG_NONE = 0; FG_PUNCH = 1; FG_SLASH = 2; { gsn_slashing } FG_PIERCE = 3; { gsn_piercing } FG_CLEAVE = 4; { gsn_slashing } FG_BLAST = 5; FG_CRUSH = 6; { gsn_concussion } FG_BITE = 7; FG_CLAW = 8; FG_WHIP = 9; { gsn_whipping } FG_STAB = 10; { gsn_piercing } FG_GAZE = 11; { gaze from a spirit? } FG_BREATH = 12; { breath } FG_STING = 13; { sting (bee, fly) } FG_MAX = FG_STING; { damage types } const TYPE_UNDEFINED = 0; TYPE_SLAY = 1; TYPE_SILENT = 2; TYPE_HIT = 3; TYPE_OTHER = TYPE_HIT + FG_MAX + 1; const attack_table:array[FG_NONE..FG_STING,1..2] of string=(('nothing','nothings'), ('punch','punches'), ('slash','slashes'), ('pierce','pierces'), ('cleave','cleaves'), ('blast','blasts'), ('crush','crushes'), ('bite','bites'), ('claw','claws'), ('whip','whips'), ('stab','stabs'), ('gaze','gazes'), ('breath','breaths'), ('sting','stings')); procedure stopfighting(ch : GCharacter); procedure death_cry(ch, killer : GCharacter); procedure gain_xp(ch : GPlayer; xp : cardinal); function damage(ch, oppnt : GCharacter; dam : integer; dt : integer) : integer; function one_hit(ch, victim : GCharacter) : integer; function in_melee(ch, vict : GCharacter) : boolean; procedure multi_hit(ch, vict : GCharacter); procedure update_fighting; implementation uses timers, area, constants, skills, conns, mudsystem, commands, dtypes, console, util, Channels; var dual_flip : boolean; // Stop the fighting procedure stopfighting(ch : GCharacter); var vict : GCharacter; iterator : GIterator; begin iterator := char_list.iterator(); while (iterator.hasNext()) do begin vict := GCharacter(iterator.next()); if (vict.fighting = ch) then begin vict.fighting := nil; vict.state := STATE_IDLE; end; end; iterator.Free(); ch.fighting := nil; ch.state := STATE_IDLE; end; // Death cry procedure death_cry(ch, killer : GCharacter); var chance : integer; s_room : GRoom; vict : GCharacter; pexit : GExit; iterator_exit, iterator_char : GIterator; begin iterator_exit := ch.room.exits.iterator(); while (iterator_exit.hasNext()) do begin pexit := GExit(iterator_exit.next()); s_room := findRoom(pexit.vnum); iterator_char := s_room.chars.iterator(); while (iterator_char.hasNext()) do begin vict := GCharacter(iterator_char.next()); vict.sendBuffer('You hear a chilling death cry.'#13#10); end; iterator_char.Free(); end; iterator_exit.Free(); chance:=random(14); if (ch.room = killer.room) then case chance of 1,2,3: act(AT_REPORT,'$n falls to the ground... DEAD.',false,ch,nil,killer,TO_VICT); 4,5,6: act(AT_REPORT,'$n splatters blood on your armor.',false,ch,nil,killer,TO_VICT); 7,8,9: act(AT_REPORT,'$n screams out in terror and dies.',false,ch,nil,killer,TO_VICT); else act(AT_REPORT,'You hear $n''s death cry.',false,ch,nil,killer,TO_VICT); end; act(AT_REPORT,'You hear $n''s death cry.',false,ch,nil,killer,TO_NOTVICT); end; // Gain XP procedure gain_xp(ch : GPlayer; xp : cardinal); var hp_gain, mv_gain, ma_gain : integer; pracs_gain : integer; begin if (ch.IS_IMMORT) then exit; { no message here, could spam a level 500 off the floor - Grimlord } if (ch.level >= LEVEL_MAX) then exit; with GPlayer(ch) do begin inc(xptot,xp); dec(xptogo,xp); while (xptogo<=0) do begin pracs_gain := round(wis / 20) + random(6) - 1; // base prac gain on wis hp_gain := (con div 4)+random(6)-3; mv_gain := dex div 4; if odd(level) then ma_gain := round((int + wis) / 10) // base mana gain on int and wis else ma_gain := 0; inc(pracs, pracs_gain); max_hp := max_hp + hp_gain; max_mv := max_mv + mv_gain; max_mana := max_mana + ma_gain; hp := max_hp; mv := max_mv; level := level + 1; act(AT_WHITE,Format('>> You have advanced to level %d!', [level]),false,ch,nil,nil,TO_CHAR); act(AT_REPORT, 'You gain $B$1' + inttostr(hp_gain) + '$A$7 health, $B$1' + inttostr(mv_gain) + '$A$7 moves, $B$1' + inttostr(ma_gain) + '$A$7 mana and $B$1' + inttostr(pracs_gain) + '$A$7 practice sessions.', false, ch, nil, nil, TO_CHAR); if (level>=LEVEL_MAX) then begin level:=LEVEL_MAX; act(AT_WHITE,'>> You have achieved the maximum level possible!',false,ch,nil,nil,TO_CHAR); end; inc(xptogo, ch.calcxp2lvl); hitroll := UMax((level div 5)+50,100); ch.calcRank; end; end; end; function get_exp_worth(ch : GCharacter) : integer; begin get_exp_worth := longint(ch.level) * longint(ch.max_hp); end; // XP formula function xp_compute(ch, victim : GCharacter) : cardinal; var xp, range : cardinal; begin if (not ch.IS_NPC) and (not victim.IS_NPC) and (ch.IS_SAME_ALIGN(victim)) then begin xp_compute := 1; exit; end; if (ch.IS_IMMORT) or (ch.IS_NPC) or (ch = victim) then begin xp_compute := 0; exit; end; xp := get_exp_worth(victim); range := URange(1, (victim.level - ch.level) + 10,13); xp_compute := (xp*range) div 10; // xp_compute := UMin((victim.level - ch.level + 1) * 20, 1); end; // Find damage message function findDamage(dam : integer) : GDamMessage; var iterator : GIterator; dm : GDamMessage; begin Result := nil; iterator := dm_msg.iterator(); while (iterator.hasNext()) do begin dm := GDamMessage(iterator.next()); if (dam >= dm.min) and (dam <= dm.max) then begin Result := dm; break; end; end; iterator.Free(); end; // Handle damage function damage(ch, oppnt : GCharacter; dam : integer; dt : integer) : integer; var xp,r : integer; dm : GDamMessage; a : array[1..3] of string; s1, s2 : string; begin damage := RESULT_NONE; if (ch.CHAR_DIED) then begin damage := RESULT_CHARDIED; exit; end; if (oppnt.CHAR_DIED) then begin damage := RESULT_VICTDIED; exit; end; if (IS_SET(ch.aff_flags, AFF_BASHED) or IS_SET(ch.aff_flags, AFF_STUNNED)) then begin damage := RESULT_CHARBASHED; exit; { can't fight when bashed! } end; if (ch.room <> oppnt.room) and (dt <> TYPE_SILENT) then begin ch.fighting := nil; ch.state := STATE_IDLE; oppnt.fighting := nil; oppnt.state := STATE_IDLE; damage := RESULT_VICTDIED; exit; end; if (dam > 25) and (hasTimer(ch, 'cast') <> nil) then begin act(AT_FIGHT_HIT, '$B$4OUCH$7!$A$7 You just lost your concentration!',false,oppnt,nil,ch,TO_CHAR); unregisterTimer(oppnt, TIMER_ACTION); oppnt.state := STATE_FIGHTING; end; if (ch.state <> STATE_FIGHTING) and (oppnt <> ch) then begin unregisterTimer(oppnt, TIMER_ACTION); ch.state := STATE_FIGHTING; ch.position := POS_STANDING; ch.fighting := oppnt; end; if (oppnt.state <> STATE_FIGHTING) and (oppnt <> ch) then begin oppnt.state := STATE_FIGHTING; oppnt.position := POS_STANDING; oppnt.fighting := ch; end; (* if (dam>10) and (dt<>TYPE_UNDEFINED) then begin dameq := random(MAX_WEAR)+1; damobj := oppnt.getEQ(dameq); if (damobj <> nil) then begin { damage the object } dec(dam,5); end else inc(dam,5); end; *) { check for damage type } dm := findDamage(dam); if (dm <> nil) then begin a[1] := dm.msg[1]; a[2] := dm.msg[2]; a[3] := dm.msg[3]; end; if (dt = TYPE_UNDEFINED) then begin s1 := 'hit'; s2 := 'hits'; end else if (dt > TYPE_OTHER) then begin s1 := GSkill(pointer(dt)).dam_msg; s2 := GSkill(pointer(dt)).dam_msg; end else if (dt > TYPE_HIT) then begin s1 := attack_table[dt - TYPE_HIT, 1]; s2 := attack_table[dt - TYPE_HIT, 2]; end else if (dt = TYPE_SLAY) then begin s1 := 'cold breath'; s2 := 'breaths'; end else if (dt = TYPE_SILENT) then begin { nothing here } end else begin bugreport('damage', 'fight.pas', 'unknown damagetype ' + inttostr(dt)); exit; end; if (ch.CHAR_DIED) then begin damage:=RESULT_CHARDIED; exit; end; if (oppnt.CHAR_DIED) then begin damage:=RESULT_VICTDIED; exit; end; if (s1 <> '') and (s2 <> '') and (dt <> integer(gsn_backstab)) then begin r := pos('#w',a[1]); if (r <> 0) then begin delete(a[1], r, 2); insert(s1 + mudAnsi(AT_FIGHT_YOU), a[1], r); end; r:=pos('#W',a[1]); if r<>0 then begin delete(a[1],r,2); insert(s2 + mudAnsi(AT_FIGHT_YOU),a[1],r); end; r:=pos('#w',a[2]); if r<>0 then begin delete(a[2],r,2); insert(s1 + mudAnsi(AT_FIGHT_HIT),a[2],r); end; r:=pos('#W',a[2]); if r<>0 then begin delete(a[2],r,2); insert(s2 + mudAnsi(AT_FIGHT_HIT),a[2],r); end; r:=pos('#w',a[3]); if r<>0 then begin delete(a[3],r,2); insert(s1 + mudAnsi(AT_FIGHT),a[3],r); end; r:=pos('#W',a[3]); if r<>0 then begin delete(a[3],r,2); insert(s2 + mudAnsi(AT_FIGHT),a[3],r); end; act(AT_FIGHT_YOU,a[1],false,ch,nil,oppnt,TO_CHAR); act(AT_FIGHT_HIT,a[2],false,oppnt,nil,ch,TO_CHAR); act(AT_FIGHT,a[3],false,oppnt,nil,ch,TO_NOTVICT); end; { in a battleground, immortals should receive damage - Grimlord } if (not oppnt.IS_IMMORT) or ((not oppnt.IS_NPC) and oppnt.IS_IMMORT) and (GPlayer(oppnt).bg_status = BG_PARTICIPATE) then oppnt.hp := oppnt.hp - dam; { ermmz... you shouldn't receive xp when damaged by poison - Grimlord } if (oppnt <> ch) then xp:=round(3.1*dam)+10 else xp:=0; if (oppnt.CHAR_DIED) then begin damage := RESULT_VICTDIED; exit; end; if (not ch.CHAR_DIED) then begin if (not ch.IS_IMMORT) and (not ch.IS_NPC) then begin gain_xp(GPlayer(ch),xp); inc(GPlayer(ch).fightxp,xp); end; end else begin damage:=RESULT_CHARDIED; exit; end; if (oppnt.hp<0) then begin unregisterTimer(oppnt, TIMER_ACTION); unregisterTimer(oppnt, TIMER_COMBAT); oppnt.state := STATE_IDLE; ch.state := STATE_IDLE; stopfighting(oppnt); oppnt.fighting:=nil; ch.fighting:=nil; oppnt.bash_timer:=-2; death_cry(oppnt,ch); act(AT_KILLED,'You have been killed!',false,oppnt,nil,nil,TO_CHAR); act(AT_KILLED,'$N has been killed!',true,ch,nil,oppnt,TO_NOTVICT); if (ch<>oppnt) then begin act(AT_KILLED,'$N has been killed!',false,ch,nil,oppnt,TO_CHAR); xp := xp_compute(ch, oppnt); if (not ch.IS_NPC) then begin act(AT_REPORT,'You gain ' + inttostr(xp)+' XP for the kill and '+IntToStr(GPlayer(ch).fightxp)+' XP for fighting.',false,ch,nil,nil,TO_CHAR); gain_xp(GPlayer(ch),xp); end; end; if (oppnt.IS_NPC) then begin GNPC(oppnt).context.runSymbol('onDeath', [integer(oppnt), integer(ch)]); if (oppnt.snooped_by <> nil) and (GPlayer(oppnt.snooped_by).switching = oppnt) then interpret(oppnt, 'return sub'); oppnt.die(); if (not ch.IS_NPC) then begin if (IS_SET(GPlayer(ch).cfg_flags,CFG_AUTOLOOT)) then interpret(ch, 'get all ''corpse of ' + oppnt.name + ''''); if (IS_SET(GPlayer(ch).cfg_flags,CFG_AUTOSAC)) then interpret(ch, 'sac ''corpse of ' + oppnt.name + ''''); end; end else begin oppnt.die(); { Regain 10% of xp needed when killed by NPC, 4% when killed by PC - Grimlord} if (ch.IS_NPC) then begin inc(GPlayer(oppnt).xptogo, oppnt.calcxp2lvl div 10); ch.hunting := nil; REMOVE_BIT(GNPC(ch).act_flags, ACT_HUNTING); end else begin inc(ch.kills); { get a point when killing one in bg - Grimlord } if (GPlayer(ch).bg_status = BG_PARTICIPATE) then inc(GPlayer(ch).bg_points); if (GPlayer(oppnt).bg_status <> BG_PARTICIPATE) then inc(GPlayer(oppnt).xptogo, oppnt.calcxp2lvl div 4); if (IS_SET(GPlayer(ch).cfg_flags, CFG_AUTOSCALP)) then interpret(ch, 'scalp corpse of '+oppnt.name); end; if (oppnt.clan <> nil) then to_channel(oppnt, '*CLAN NOTIFY*: '+oppnt.name+' has been'+ ' killed by '+ch.name+'!',CHANNEL_CLAN,AT_WHITE); if (not ch.IS_NPC) then begin if (ch.clan<>nil) then to_channel(ch, '*CLAN NOTIFY*: '+ch.name+' has just'+ ' killed '+oppnt.name+'!',CHANNEL_CLAN,AT_WHITE); if not (ch.IS_SAME_ALIGN(oppnt)) then begin if (GPlayer(ch).taunt <> '') then act(AT_WHITE, '$N taunts: ' + GPlayer(ch).taunt, false, oppnt, nil, ch, TO_CHAR); // update_trophy(ch,oppnt); end; end; end; damage := RESULT_VICTDIED; exit; end; if (not oppnt.IS_NPC) then if (oppnt.hp <= GPlayer(oppnt).wimpy) then interpret(oppnt,'flee'); end; function one_hit(ch, victim : GCharacter) : integer; var chance,ds,dam:integer; wield : GObject; // fly_bonus, prof_gsn : integer; prof_bonus : integer; vict_ac : integer; hit_roll, roll : integer; begin one_hit := RESULT_NONE; if (ch.CHAR_DIED) then begin one_hit := RESULT_CHARDIED; exit; end; if (victim.CHAR_DIED) then begin one_hit := RESULT_VICTDIED; exit; end; if (ch.state <> STATE_FIGHTING) and (victim <> ch) then begin unregisterTimer(victim, TIMER_ACTION); ch.state := STATE_FIGHTING; ch.position := POS_STANDING; ch.fighting := victim; end; if (victim.state <> STATE_FIGHTING) and (victim <> ch) then begin victim.state := STATE_FIGHTING; victim.position := POS_STANDING; victim.fighting := ch; end; { get the weapon } wield := ch.getDualWield; if (wield <> nil) then begin if (not dual_flip) then begin dual_flip := true; wield := ch.getEQ('rightwield'); end else dual_flip := false; end; if (wield = nil) or (wield.item_type <> ITEM_WEAPON) then wield := ch.getWield(ITEM_WEAPON); if (wield = nil) then ds := FG_PUNCH else begin ds := wield.value[4]; if (ds = 0) then ds := FG_PUNCH; end; vict_ac := victim.ac; hit_roll := ch.hitroll; if (not ch.CAN_SEE(victim)) then { -10 penalty to hitroll when not able to see target } dec(hit_roll,10); // prof_bonus := get_prof_bonus(ch,wield,prof_gsn); prof_bonus := 0; dec(hit_roll,prof_bonus); dec(hit_roll,vict_ac); roll := rolldice(1,100); { undead or spirits: attack will go right through them if it's non-magical - Grimlord } if (victim.IS_NPC) and (IS_SET(GNPC(victim).act_flags, ACT_SPIRIT)) and (not ch.IS_AFFECT(AFF_ENCHANT)) then begin act(AT_REPORT,'Your attack goes right through your victim as if it was not there!', false,ch,nil,victim,TO_CHAR); act(AT_REPORT,'$n''s attack just doesn''t seem to have any effect!', false,ch,nil,victim,TO_ROOM); end else { mortals fighting immortals get this boney message - Grimlord } if (victim.IS_IMMORT) and (not ch.IS_IMMORT) then begin act(AT_REPORT, 'Your blow bounces off $N''s holy shield!',false,ch,nil,victim,TO_CHAR); act(AT_REPORT, '$n''s blow bounces off your holy shield!',false,ch,nil,victim,TO_VICT); act(AT_REPORT, '$n''s blow bounces off $N''s holy shield!',false,ch,nil,victim,TO_NOTVICT); end else if (roll <= hit_roll) then begin { check for dodge } if (number_percent <= victim.LEARNED(gsn_dodge) div 2) then begin improve_skill(victim,gsn_dodge); if number_percent<50 then begin act(AT_REPORT,'$N ducks left, dodging your attack.',false,ch,nil,victim,TO_CHAR); act(AT_REPORT,'You duck left, dodging $n''s attack.',false,ch,nil,victim,TO_VICT); act(AT_REPORT,'$N ducks left, dodging $n''s attack.',false,ch,nil,victim,TO_NOTVICT); end else begin act(AT_REPORT,'$N ducks right, dodging your attack.',false,ch,nil,victim,TO_CHAR); act(AT_REPORT,'You duck right, dodging $n''s attack.',false,ch,nil,victim,TO_VICT); act(AT_REPORT,'$N ducks right, dodging $n''s attack.',false,ch,nil,victim,TO_NOTVICT); end; exit; end; if (wield <> nil) then dam := rolldice(wield.value[2],wield.value[3]) else if (ch.damnumdie<>0) and (ch.damsizedie<>0) then dam := rolldice(ch.damnumdie,ch.damsizedie) else dam := rolldice(1,3); inc(dam,ch.apb); inc(dam,prof_bonus div 4); chance := ch.LEARNED(gsn_enhanced_damage); if (number_percent <= chance) then begin improve_skill(ch, gsn_enhanced_damage); inc(dam, 10); end; dam := (dam * ch.str) div 50; one_hit:=damage(ch,victim,dam, TYPE_HIT + ds); end else if (roll+((victim.dex-50) div 12)<=hit_roll) then begin if (victim.IS_FLYING) then begin act(AT_REPORT,'You swing wide as $N quickly flies out of the way.',false,ch,nil,victim,TO_CHAR); act(AT_REPORT,'$n swings wide as you quickly fly out of the way.',false,ch,nil,victim,TO_VICT); act(AT_REPORT,'$n swings wide as $N quickly flies out of the way.',false,ch,nil,victim,TO_NOTVICT); end else begin act(AT_REPORT,'You swing wide, and miss $N completely.',false,ch,nil,victim,TO_CHAR); act(AT_REPORT,'$n swings wide, and misses you completely.',false,ch,nil,victim,TO_VICT); act(AT_REPORT,'$n swings wide, and misses $N completely.',false,ch,nil,victim,TO_NOTVICT); end; end else begin act(AT_REPORT,'Your '+attack_table[ds,1]+' is completely absorbed by $N''s armor!', false,ch,nil,victim,TO_CHAR); act(AT_REPORT,'$n''s '+attack_table[ds,1]+' is completely absorbed by your armor!', false,ch,nil,victim,TO_VICT); act(AT_REPORT,'$n''s '+attack_table[ds,1]+' is completely absorbed by $N''s armor!', false,ch,nil,victim,TO_NOTVICT); end; end; function in_melee(ch, vict : GCharacter) : boolean; var iterator : GIterator; t : GCharacter; num : integer; begin in_melee := true; if (ch = vict.fighting) then exit; num := 0; iterator := char_list.iterator(); while (iterator.hasNext()) do begin t := GCharacter(iterator.next()); if (t.state = STATE_FIGHTING) and (t.fighting = vict) then begin inc(num); if (t = ch) then begin if (num > 4) then in_melee := false; exit; end; end; end; iterator.Free(); end; procedure multi_hit(ch, vict : GCharacter); var chance, dual_bonus : integer; begin if (ch.fighting <> vict) then begin ch.fighting := nil; ch.state := STATE_IDLE; bugreport('multi_hit', 'fight.pas', 'desync error: ch.fighting & vict not same'); writeConsole('System is unstable - prepare for a rough ride'); exit; end; if (ch.CHAR_DIED) then exit; if (vict.CHAR_DIED) then begin ch.fighting := nil; ch.state := STATE_IDLE; exit; end; if (IS_SET(ch.aff_flags, AFF_BASHED) or IS_SET(ch.aff_flags, AFF_STUNNED)) then exit; if (ch.state = STATE_FIGHTING) or (Assigned(ch.fighting)) then begin if (not in_melee(ch,vict)) then begin act(AT_REPORT,'$10- You are not in melee range! -',false,ch,nil,nil,TO_CHAR); exit; end; if (one_hit(ch, vict) <> RESULT_NONE) then exit; if (ch.getDualWield <> nil) then begin dual_bonus := ch.LEARNED(gsn_dual_wield) div 10; if skill_success(ch,gsn_dual_wield) then begin improve_skill(ch, gsn_dual_wield); if (one_hit(ch, vict) <> RESULT_NONE) then exit; end; end else dual_bonus := 0; if (ch.mv < 10) then dec(dual_bonus, 20); chance := ch.LEARNED(gsn_second_attack) + dual_bonus; if (number_percent <= chance) then begin improve_skill(ch, gsn_second_attack); if (one_hit(ch,vict) <> RESULT_NONE) then exit; end; chance := ch.LEARNED(gsn_third_attack) + dual_bonus; if (number_percent <= chance) then begin improve_skill(ch, gsn_third_attack); if (one_hit(ch,vict) <> RESULT_NONE) then exit; end; chance := ch.LEARNED(gsn_fourth_attack) + dual_bonus; if (number_percent <= chance) then begin improve_skill(ch, gsn_fourth_attack); if (one_hit(ch,vict) <> RESULT_NONE) then exit; end; chance := ch.LEARNED(gsn_fifth_attack) + dual_bonus; if (number_percent <= chance) then begin improve_skill(ch, gsn_fifth_attack); if (one_hit(ch,vict) <> RESULT_NONE) then exit; end; end; end; // Update the fighting procedure update_fighting(); var ch, vch, gch : GCharacter; iter_world, iter_room : GIterator; conn : GPlayerConnection; begin iter_world := char_list.iterator(); while (iter_world.hasNext()) do begin ch := GCharacter(iter_world.next()); if (ch.bash_timer > -2) then dec(ch.bash_timer); if (ch.bashing > -2) then dec(ch.bashing); if (ch.cast_timer > 0) then dec(ch.cast_timer); if (ch.bash_timer = 1) then begin if (IS_SET(ch.aff_flags, AFF_BASHED)) then begin REMOVE_BIT(ch.aff_flags, AFF_BASHED); act(AT_REPORT,'You recover from the bash and stand quickly.',false,ch,nil,nil,TO_CHAR); act(AT_REPORT,'$n recovers from the bash and stands quickly.',false,ch,nil,nil,TO_ROOM); end else if (IS_SET(ch.aff_flags, AFF_STUNNED)) then begin REMOVE_BIT(ch.aff_flags, AFF_STUNNED); act(AT_REPORT,'You shake your head and stand quickly.',false,ch,nil,nil,TO_CHAR); act(AT_REPORT,'$n recovers from the stun and stands quickly.',false,ch,nil,nil,TO_ROOM); end; end; vch := ch.fighting; if (ch.state = STATE_FIGHTING) or (Assigned(ch.fighting)) then begin multi_hit(ch,vch); if (vch.CHAR_DIED) or (ch.CHAR_DIED) then break; { Group members AUTO-assist other group members } iter_room := ch.room.chars.iterator(); while (iter_room.hasNext()) do begin gch := GCharacter(iter_room.next()); if (gch <> ch) and (gch.leader=ch.leader) and (gch.room = ch.room) then if (gch.fighting = nil) and (gch.state = STATE_IDLE) then if (gch.IS_NPC) or (IS_SET(GPlayer(gch).cfg_flags, CFG_ASSIST)) then begin if (vch.CHAR_DIED) then break; act(AT_REPORT,'You assist $N!',false,gch,nil,ch,TO_CHAR); act(AT_REPORT,'$n assists $N!',false,gch,nil,ch,TO_ROOM); gch.fighting := vch; gch.position := POS_STANDING; gch.state := STATE_FIGHTING; end; end; iter_room.Free(); { NPC's of same type assist each other } if (ch.IS_NPC) then begin iter_room := ch.room.chars.iterator(); while (iter_room.hasNext()) do begin gch := GCharacter(iter_room.next()); if (vch.CHAR_DIED) then break; if (gch <> ch) and (gch.IS_NPC) and (gch.IS_AWAKE) then if (gch.state = STATE_IDLE) and (GNPC(gch).npc_index.vnum = GNPC(ch).npc_index.vnum) then if (number_percent <= 25) then begin if (vch.CHAR_DIED) then break; gch.fighting := vch; gch.state := STATE_FIGHTING; // in_melee(gch,vch); act(AT_FIGHT,'$n charges into the battle against $N!',false,gch,nil,vch,TO_NOTVICT); act(AT_FIGHT_HIT,'$N charges into the battle against you!',false,vch,nil,gch,TO_CHAR); multi_hit(gch,vch); end; end; iter_room.Free(); end; end else if (ch.IS_NPC) then begin // aggress mode if (ch.hunting <> nil) and (ch.hunting.room = ch.room) then interpret(ch, 'kill '+ch.hunting.name); if (IS_SET(GNPC(ch).act_flags, ACT_AGGRESSIVE)) then begin vch := ch.room.findRandomChar; if (vch <> nil) and (not vch.IS_NPC) then interpret(ch, 'kill ' + vch.name); end; end; end; iter_world.Free(); iter_world := connection_list.iterator(); while (iter_world.hasNext()) do begin conn := GPlayerConnection(iter_world.next()); if (conn.isPlaying()) and (not conn.ch.in_command) then conn.ch.emptyBuffer(); end; iter_world.Free(); iter_world := char_list.iterator(); while (iter_world.hasNext()) do begin ch := GCharacter(iter_world.next()); if (ch.IS_NPC) and (ch.fighting <> nil) then GNPC(ch).context.runSymbol('onFight', [integer(ch.fighting), integer(ch)]) end; iter_world.Free(); end; begin dual_flip := false; end.