# Author: Craig Smith # # This source code copyright (C) 2009 Craig Smith # All rights reserved. # # Released under the terms of the TeensyMUD Public License # See LICENSE file for additional information. # require 'gettext' require 'utility/log' require 'combat/damage' require 'combat/miss' require 'combat/criticaldamage' require 'combat/criticalmiss' # This class is the combat system # There is one roll to see if the attacker hit the victim # Second determines if there was a critical strike class Combat include GetText bindtextdomain("core") logger 'DEBUG' attr_accessor :numberofattacks # Initialize an attack # [+attackerid+] id of attacker # [+defenderid+] id of defender def initialize(attackerid, defenderid) @a = get_object(attackerid) @d = get_object(defenderid) @damagetype = nil @hitlocationid = nil @attackweaponid = nil @numberofattacks = 1 srand if not @a or not @d log.error "Attacker defender pair did not load for attackerid=#{attackerid} defenderid=#{defenderid}" end end # Rolls dice and returns a number between low and high # [+low+] lowest result in the roll # [+high+] highest result in the roll # [+return+] a random number between low and high def roll(low, high) return rand(high) + low end # get_object taken from Root.rb def get_object(oid) Engine.instance.db.get(oid) end # Checks to see if player is stunned and sends a Msg # [+return+] True if stunned def playerStunned? if @a.has_attribute? :stunned @a.sendto _("You are STUNNED!") msg = Msg.new("^p1 is STUNNED!") msg.p1 = @a.name @a.sendroom(msg) return true end return false end # Determine what the attacker is attacking with # [+return+] Undefined def attack_method if @a.has_attribute? :zombie or @a.is_a? Zombie @attackweaponid = nil @damagetype = :zombie return end weapons = @a.body.wielding? if weapons.size == 1 # Attacher is weilding something, use it weapon = get_object(weapons[0]) @attackweaponid = weapons[0] if weapon.has_val? :damagetype dtype = weapon.has_val?(:damagetype) dtype = dtype.to_sym if dtype.is_a? String @damagetype = dtype else @damagetype = :bludgeon end elsif weapons.size > 1 # Attacker has more than one possible weapon # Todo: Possibly add more attacks per weapon... weapon1 = get_object(weapons[0]) weapon2 = get_object(weapons[1]) if weapon1.has_val? :damagetype and weapon2.has_val? :damagetype # Both can deal some type of damage..pick a random one # TODO: Check for fuel or Ammo for this decission @attackweaponid = weapons[rand(weapons.size)] elsif not weapon1.has_val? :damagetype and not weapon2.has_val? :damagetype # Neither weapon has a special damage type, pick the heavier item if weapon1.weight > weapon2.weight @attackweaponid = weapon1.id elsif weapon2.weight > weapon1.weight @attackweaponid = weapon2.id else # Equal weight @attackweaponid = weapons[rand(weapons.size)] end else # One weapon has a damage type and one doesn't, pick the one that does @attackweaponid = weapon1.id if weapon1.has_val? :damagetype @attackweaponid = weapon2.id if weapon2.has_val? :damagetype end weapon = get_object(@attackweaponid) if weapon.has_val? :damagetype dtype = weapon.has_val? :damagetype dtype = dtype.to_sym if dtype.is_a? String @damagetype = dtype else @damagetype = :bludgeon end else # Check for Race/Mobile special attack types if @a.has_val? :damagetype dtype = @a.has_val? :damagetype dtype = dtype.to_sym if dtype.is_a? String @damagetype = dtype else @damagetype = :melee end end end # Determine the targeted hit location # [+return+] The bodypart location id def get_hit_location @hitlocationid = @d.body.bodyparts[rand(@d.body.bodyparts.size)] end # Determine success of attack # [+return+] true if attacker hit defender def attacker_successful? chancetohit = 50 # Harder to hit if your head is crippled chancetohit -= 20 if get_object(@a.body.getbodyid("head")[0]).crippled # Subtract 10 % if in the dark and can't see chancetohit -= 10 if not @a.cansee? :dark # Apply Accuracy modifiers if @attackweaponid weapon = get_object(@attackweaponid) if weapon.has_modifier? :accuracy # Fuel based weapons... if weapon.is_a? Container chancetohit += weapon.get_modifier :accuracy if weapon.has_power? else chancetohit += weapon.get_modifier :accuracy end end end if roll(1,100) < chancetohit return true end false end # If the attacker hit was it a critical attack? # [+return+] true if critical hit def critical_hit? chance4critical = 3 # Apply Accuracy modifiers if @attackweaponid weapon = get_object(@attackweaponid) if weapon.has_modifier? :critical # Fuel based weapons... if weapon.is_a? Container chance4critical += weapon.get_modifier :critical if weapon.has_power? else chance4critical += weapon.get_modifier :critical end end end if roll(1,100) < chance4critical return true end false end # Was the attack a critical miss? # [+return+] true if critical miss def critical_miss? chance4critical = 3 # Apply Accuracy modifiers if @attackweaponid weapon = get_object(@attackweaponid) if weapon.has_modifier? :critical # Fuel based weapons... if weapon.is_a? Container chance4critical += weapon.get_modifier :critical if weapon.has_power? else chance4critical += weapon.get_modifier :critical end end end if roll(1,100) < chance4critical return true end false end # Determines the amount of damage dealt # [+return+] false if damage was prevented (ie: armor) def deal_damage # TODO: Armor checks pain = Damage.new(@a, @d, @damagetype, @hitlocationid, @attackweaponid) pain.deal(roll(1,100)) true end # Determines what type of critical strike occured def deal_critical_hit pain = CriticalDamage.new(@a, @d, @damagetype, @hitlocationid, @attackweaponid) pain.deal(roll(1,100)) end # Determines what type of critical miss occured def deal_critical_miss pain = CriticalMiss.new(@a, @d, @damagetype, @hitlocationid, @attackweaponid) pain.deal(roll(1,100)) end # Describes the results of a miss def describe_miss missmsg = Miss.new(@a, @d, @damagetype, @hitlocationid, @attackweaponid) missmsg.deal(roll(1,100)) end # Ensures that the person hit is fighting them def add_combatant @d.combatants << @a.id if not @d.combatants.include? @a.id end # Execute an attack on defender def attack hit = false # If either part is already dead, return return if @a.health < 1 or @d.health < 1 # If attacker is stunned then they can not attack return if playerStunned? # Determine how the attacker is attacking attack_method # Determine where the attack might hit the defender get_hit_location # Did attacker hit defender? if attacker_successful? if critical_hit? deal_damage deal_critical_hit hit = true else hit = deal_damage end add_combatant elsif critical_miss? deal_critical_miss hit = false else describe_miss hit = false end hit end end