Skip to content
Snippets Groups Projects
Combat.lua 5.32 KiB
Newer Older
  • Learn to ignore specific revisions
  • dg's avatar
    dg committed
    require "engine.class"
    
    dg's avatar
    dg committed
    local DamageType = require "engine.DamageType"
    
    local Map = require "engine.Map"
    
    dg's avatar
    dg committed
    local Target = require "engine.Target"
    
    dg's avatar
    dg committed
    
    --- Interface to add ToME combat system
    module(..., package.seeall, class.make)
    
    
    --- Checks what to do with the target
    -- Talk ? attack ? displace ?
    function _M:bumpInto(target)
    	local reaction = self:reactionToward(target)
    	if reaction < 0 then
    		return self:attackTarget(target)
    	elseif reaction >= 0 then
    		-- Talk ?
    		if self.player and target.can_talk then
    			-- TODO: implement !
    		elseif target.player and self.can_talk then
    
    dg's avatar
    dg committed
    			-- TODO: implement! request the player to talk
    
    dg's avatar
    dg committed
    		elseif self.move_others then
    
    			-- Displace
    			game.level.map:remove(self.x, self.y, Map.ACTOR)
    			game.level.map:remove(target.x, target.y, Map.ACTOR)
    			game.level.map(self.x, self.y, Map.ACTOR, target)
    			game.level.map(target.x, target.y, Map.ACTOR, self)
    			self.x, self.y, target.x, target.y = target.x, target.y, self.x, self.y
    		end
    	end
    end
    
    --- Makes the death happen!
    
    dg's avatar
    dg committed
    --[[
    The ToME combat system has the following attributes:
    
    dg's avatar
    dg committed
    - attack: increases chances to hit against high defence
    
    dg's avatar
    dg committed
    - defence: increases chances to miss against high attack power
    - armor: direct reduction of damage done
    - armor penetration: reduction of target's armor
    - damage: raw damage done
    ]]
    function _M:attackTarget(target)
    
    dg's avatar
    dg committed
    	local speed = nil
    
    	-- All weaponsin main hands
    	if self:getInven(self.INVEN_MAINHAND) then
    		for i, o in ipairs(self:getInven(self.INVEN_MAINHAND)) do
    			if o.combat then
    				local s = self:attackTargetWith(target, o.combat)
    				speed = math.max(speed or 0, s)
    			end
    		end
    	end
    	-- All wpeaons in off hands
    	if self:getInven(self.INVEN_OFFHAND) then
    		for i, o in ipairs(self:getInven(self.INVEN_OFFHAND)) do
    			if o.combat then
    				local s = self:attackTargetWith(target, o.combat)
    				speed = math.max(speed or 0, s)
    			end
    		end
    	end
    
    	-- Barehanded ?
    	if not speed then
    		speed = self:attackTargetWith(target, self.combat)
    	end
    
    	-- We use up our own energy
    	if speed then
    		self:useEnergy(game.energy_to_act * speed)
    		self.did_energy = true
    	end
    end
    
    
    dg's avatar
    dg committed
    --- Computes a logarithmic chance to hit, opposing chance to hit to chance to miss
    -- This will be used for melee attacks, physical and spell resistance
    function _M:checkHit(atk, def, min, max, facotr)
    print("checkHit", atk, def)
    	if atk == 0 then atk = 1 end
    	local hit = nil
    	factor = factor or 5
    	if atk > def then
    		hit = math.log10(1 + 5 * (atk - def) / atk) * 100 + 50
    	else
    		hit = -math.log10(1 + 5 * (def - atk) / atk) * 100 + 50
    	end
    	hit = util.bound(hit, min or 5, max or 95)
    print("=> chance to hit", hit)
    	return rng.percent(hit)
    end
    
    
    dg's avatar
    dg committed
    --- Attacks with one weapon
    function _M:attackTargetWith(target, weapon)
    
    dg's avatar
    dg committed
    	local damtype = DamageType.PHYSICAL
    
    dg's avatar
    dg committed
    
    
    dg's avatar
    dg committed
    	-- Does the blow connect? yes .. complex :/
    	local atk, def = self:combatAttack(weapon), target:combatDefense()
    	local dam, apr, armor = self:combatDamage(weapon), self:combatAPR(weapon), target:combatArmor()
    
    dg's avatar
    dg committed
    
    	-- If hit is over 0 it connects, if it is 0 we still have 50% chance
    
    dg's avatar
    dg committed
    	if self:checkHit(atk, def) then
    
    dg's avatar
    dg committed
    		local dam = dam - math.max(0, armor - apr)
    
    dg's avatar
    dg committed
    		dam = self:physicalCrit(dam, weapon)
    
    dg's avatar
    dg committed
    		DamageType:get(damtype).projector(self, target.x, target.y, damtype, math.max(0, dam))
    
    dg's avatar
    dg committed
    	else
    		game.logSeen(target, "%s misses %s.", self.name:capitalize(), target.name)
    	end
    
    dg's avatar
    dg committed
    
    	return self:combatSpeed(weapon)
    end
    
    --- Gets the defense
    function _M:combatDefense()
    	return self.combat_def + self:getDex() - 10
    end
    
    --- Gets the armor
    function _M:combatArmor()
    	return self.combat_armor
    end
    
    --- Gets the attack
    function _M:combatAttack(weapon)
    	weapon = weapon or self.combat
    
    dg's avatar
    dg committed
    	return self.combat_atk + (weapon.atk or 0) + (self:getStr(50) - 5) + (self:getDex(50) - 5)
    
    dg's avatar
    dg committed
    end
    
    --- Gets the armor penetration
    function _M:combatAPR(weapon)
    	weapon = weapon or self.combat
    
    dg's avatar
    dg committed
    	return self.combat_apr + (weapon.apr or 0)
    
    dg's avatar
    dg committed
    end
    
    --- Gets the weapon speed
    function _M:combatSpeed(weapon)
    	weapon = weapon or self.combat
    	return self.combat_physspeed + (weapon.physspeed or 1)
    end
    
    --- Gets the crit rate
    function _M:combatCrit(weapon)
    	weapon = weapon or self.combat
    	return self.combat_physcrit + (self:getCun() - 10) * 0.3 + (weapon.physcrit or 1)
    end
    
    --- Gets the damage
    function _M:combatDamage(weapon)
    	weapon = weapon or self.combat
    	local add = 0
    	if weapon.dammod then
    		for stat, mod in pairs(weapon.dammod) do
    			add = add + (self:getStat(stat) - 10) * mod
    		end
    	end
    
    dg's avatar
    dg committed
    	return self.combat_armor + (weapon.dam or 1) + add
    
    dg's avatar
    dg committed
    end
    
    --- Gets spellpower
    function _M:combatSpellpower(mod)
    	mod = mod or 1
    	return (self.combat_spellpower + self:getMag()) * mod
    end
    
    --- Gets spellcrit
    function _M:combatSpellCrit()
    	return self.combat_spellcrit + 1
    end
    
    --- Gets spellspeed
    function _M:combatSpellSpeed()
    	return self.combat_spellspeed + (self:getCun() - 10) * 0.3 + 1
    
    dg's avatar
    dg committed
    end
    
    dg's avatar
    dg committed
    
    --- Computes physical crit for a damage
    function _M:physicalCrit(dam, weapon)
    	local chance = self:combatCrit(weapon)
    	if rng.percent(chance) then
    		dam = dam * 2
    	end
    	return dam
    end
    
    --- Computes spell crit for a damage
    function _M:spellCrit(dam)
    	local chance = self:combatSpellCrit()
    	if rng.percent(chance) then
    		dam = dam * 2
    	end
    	return dam
    end
    
    dg's avatar
    dg committed
    
    --- Computes physical resistance
    function _M:combatPhysicalResist()
    	return self.combat_physresist + (self:getCon() + self:getStr()) * 0.5
    end
    
    --- Computes spell resistance
    function _M:combatSpellResist()
    	return self.combat_spellresist + (self:getMag() + self:getWil()) * 0.5
    end