pvp_revamped/init.lua
2020-07-29 20:00:20 -05:00

1084 lines
39 KiB
Lua

local head_height = 1.35
local torso_height = 0.75
local leg_height = 0.45
local knee_height = 0.375
local block_duration = 100000
local dodge_duration = 350000
local dodge_cooldown = 1500000
local dash_cooldown = 1500000
local dodge_aerial_cooldown = 3000000
local dash_aerial_cooldown = 3000000
local dash_speed = 9.2
local disarm_chance_mul = 2
local leg_stagger_mul = 0.8
local knee_stagger_mul = 1.5
local block_duration_mul = 100000
local block_interval_mul = 0.15
local block_pool_mul = 2
local shield_pool_mul = 4
local block_wear_mul = 9000
local shield_axe_dmg_mul = 20
local head_dmg_mul = 1.2
local torso_dmg_mul = 1.0
local arm_dmg_mul = 0.6
local leg_dmg_mul = 0.7
local front_dmg_mul = nil
local side_dmg_mul = 1.05
local back_dmg_mul = 1.1
local elevated_dmg_mul = 1.5
local equal_height_dmg_mul = nil
local lower_elevation_dmg_mul = 0.9
local velocity_dmg_mul = 0.15
local optimal_distance_dmg_mul = 0.2
local maximum_distance_dmg_mul = 0.1
local optimal_distance_mul = 0.5
local projectile_full_throw_mul = 2
local projectile_speed_mul = 3
local projectile_gravity = -10
local projectile_dmg_mul = 0.5
local projectile_velocity_dmg_mul = 0.1
local projectile_step = 0.15
local projectile_dist = 5
local projectile_throw_style_dip = 1
local projectile_throw_style_spinning = 2
local lag = 0
local player_data = {}
local hit_points = {{x = 0.3, y = 1.2, z = 0, part = 1},
{x = 0, y = 1.2, z = 0, part = 0},
{x = -0.3, y = 1.2, z = 0, part = 1}}
local registered_tools = minetest.registered_tools
local raycast = minetest.raycast
local get_us_time = minetest.get_us_time
local get_player_by_name = minetest.get_player_by_name
local get_player_information = minetest.get_player_information
local serialize = minetest.serialize
local deserialize = minetest.deserialize
local add_item = minetest.add_item
local add_entity = minetest.add_entity
local maxn = table.maxn
local add = vector.add
local multiply = vector.multiply
local subtract = vector.subtract
local distance = vector.distance
local normalize = vector.normalize
local cos = math.cos
local sin = math.sin
local abs = math.abs
local atan = math.atan
local random = math.random
local max = math.max
local min = math.min
local floor = math.floor
local asin = math.asin
local pi = math.pi
local rad45 = pi * 0.25
local rad90 = pi * 0.5
local rad360 = pi * 2
-- Entity for thrown items.
minetest.register_entity("pvp_revamped:projectile", {
initial_properties = {
collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
visual_size = {x = 0.4, y = 0.4, z = 0.4},
visual = "wielditem",
textures = {""},
physical = true,
collide_with_objects = false,
static_save = false,
is_visible = false
},
timer = 0,
spin_rate = 0,
throw_style = 0,
itemstring = "",
owner = "",
tool_capabilities = nil,
set_item = function(self, owner, item)
-- Get the stack from item or itemstring.
local stack = ItemStack(item or self.itemstring)
self.itemstring = stack:to_string()
if self.itemstring == "" then
return
end
local itemname = stack:is_known() and stack:get_name() or "unknown"
-- Get the name of the stack item.
local tool_capabilities = registered_tools[stack:get_name()].tool_capabilities
local max_count = stack:get_stack_max()
local count = math.min(stack:get_count(), max_count)
local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3)
-- Set the entity properties.
self.object:set_properties({
is_visible = true,
visual = "wielditem",
textures = {itemname},
visual_size = {x = size, y = size},
collisionbox = {-size, -size, -size, size, size, size}
})
if owner then
self.owner = owner
end
self.tool_capabilities = tool_capabilities
end,
throw = function(self, user, speed, acceleration, damage, throw_style, spin_rate)
local obj = self.object
local pos = obj:get_pos()
local velocity = multiply(user:get_look_dir(), speed)
-- Set the entities speed and gravity.
obj:set_velocity(velocity)
obj:set_acceleration(acceleration)
obj:set_rotation({x = 0, y = user:get_look_horizontal() + rad90, z = asin(-normalize(velocity).y) + rad45})
-- If no damage value, auto set the damage times projectile_dmg_mul.
if not damage and self.tool_capabilities and self.tool_capabilities.damage_groups and self.tool_capabilities.damage_groups.fleshy then
self.tool_capabilities.damage_groups.fleshy = self.tool_capabilities.damage_groups.fleshy * projectile_dmg_mul
elseif damage and self.tool_capabilities and self.tool_capabilities.damage_groups and self.tool_capabilities.damage_groups.fleshy then
self.tool_capabilities.damage_groups.fleshy = damage
end
if spin_rate then
self.spin_rate = spin_rate
end
self.throw_style = throw_style
self.timer = -1 / speed
end,
get_staticdata = function(self)
return serialize({
itemstring = self.itemstring,
owner = self.owner,
tool_capabilities = self.tool_capabilities,
throw_style = self.throw_style,
spin_rate = self.spin_rate
})
end,
on_activate = function(self, staticdata)
self.object:set_armor_groups({immortal = 1})
local data = deserialize(staticdata)
if data and type(data) == "table" then
self.itemstring = data.itemstring
self.owner = data.owner
self.tool_capabilities = data.tool_capabilities
self.throw_style = data.throw_style
self.spin_rate = data.spin_rate
end
self.timer = 0
self:set_item()
end,
on_step = function(self, dtime)
local tool_capabilities = self.tool_capabilities
local throw_style = self.throw_style
local object = self.object
self.timer = self.timer + dtime
-- Two different throwing styles.
if throw_style and throw_style == projectile_throw_style_spinning then
-- This style has the item spining at a fixed rate.
local old_rotation = object:get_rotation()
object:set_rotation({x = old_rotation.x, y = old_rotation.y, z = old_rotation.z + self.spin_rate})
elseif throw_style and throw_style == projectile_throw_style_dip then
-- This style gives the item a bullet drop effect.
local old_rotation = object:get_rotation()
object:set_rotation({x = old_rotation.x, y = old_rotation.y, z = asin(-normalize(object:get_velocity()).y) + rad45})
end
if self.timer >= projectile_step then
local velocity = object:get_velocity()
-- If there is no velocity then drop the item.
if velocity.y == 0 or velocity.x == 0 and velocity.z == 0 then
self:die()
return
end
local dir = normalize(velocity)
local pos = object:get_pos()
local p1 = add(pos, dir)
local p2 = add(pos, multiply(dir, projectile_dist))
local ray = raycast(p1, p2)
for pointed_thing in ray do
if pointed_thing.type == "object" then
local obj = pointed_thing.ref
if obj:get_armor_groups().fleshy then
-- Add up the velocity damage.
if projectile_velocity_dmg_mul and tool_capabilities.damage_groups and tool_capabilities.damage_groups.fleshy then
local vv = abs(velocity.x) + abs(velocity.y) + abs(velocity.z)
if vv > 0 then
-- Give a damage bonus based on the tool's velocity.
tool_capabilities.damage_groups.fleshy = tool_capabilities.damage_groups.fleshy + vv * projectile_velocity_dmg_mul
end
end
obj:punch(get_player_by_name(self.owner), nil, tool_capabilities)
self:die()
return
end
end
end
self.timer = 0
end
end,
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
-- If this item was punched drop it.
if puncher and puncher:is_player() and puncher:get_player_name() ~= self.owner then
self:die()
end
end,
die = function(self, pos)
-- On death drop the item.
if not pos then
pos = self.object:get_pos()
end
local obj = add_item(pos, self.itemstring)
if obj then
obj:get_luaentity().collect = true
obj:set_velocity(self.object:get_velocity())
end
self.object:remove()
end
})
minetest.register_on_mods_loaded(function()
local max_armor_use
for k, v in pairs(registered_tools) do
if v.groups and v.groups.armor_use then
if not max_armor_use or max_armor_use < v.groups.armor_use then
-- Get the max armor_use.
max_armor_use = v.groups.armor_use
end
end
if v.tool_capabilities and v.tool_capabilities.groupcaps and v.tool_capabilities.groupcaps.choppy then
-- Compute the damage an axe would do to a shield.
local tool_capabilities = v.tool_capabilities
local choppy = tool_capabilities.groupcaps.choppy
local uxml = choppy.uses * choppy.maxlevel
tool_capabilities.damage_groups.shield_dmg = uxml * shield_axe_dmg_mul
minetest.override_item(k, {tool_capabilities = tool_capabilities})
end
if v.tool_capabilities and v.tool_capabilities.full_punch_interval then
-- Calculate the time it takes to fully throw an item at max velocity and damage.
local tool_capabilities = v.tool_capabilities
tool_capabilities.full_throw = (v.tool_capabilities.full_punch_interval * projectile_full_throw_mul) * 1000000
minetest.override_item(k, {tool_capabilities = tool_capabilities})
end
if v.tool_capabilities then
-- Calculate the item throw speed.
local tool_capabilities = v.tool_capabilities
local range = 4
if v.tool_capabilities.range then
range = v.tool_capabilities.range
end
tool_capabilities.throw_speed = range * projectile_speed_mul
minetest.override_item(k, {tool_capabilities = tool_capabilities})
end
end
for k, v in pairs(registered_tools) do
if not (max_armor_use and v.groups and v.groups.armor_shield) and v.tool_capabilities and v.tool_capabilities.damage_groups.fleshy and v.tool_capabilities.full_punch_interval then
-- Block feature for tools with combat ability.
local tool_capabilities = v.tool_capabilities
local full_punch_interval = tool_capabilities.full_punch_interval
local punch_number = max(tool_capabilities.damage_groups.fleshy - full_punch_interval, 0.1)
local block_pool = punch_number * block_pool_mul
local full_block_interval = (full_punch_interval * block_interval_mul) * 1000000
local duration = block_duration + (punch_number * block_duration_mul)
local old_on_secondary_use = v.on_secondary_use
local old_on_place = v.on_place
local old_on_drop = v.on_drop
if block_pool > 0 then
-- Allow the tool to block damage.
local block_action = function(user)
local time = get_us_time()
local name = user:get_player_name()
local data = player_data[name].block
-- Prevent spam blocking.
if not data or time - data.time > full_block_interval then
data = {pool = block_pool, time = time, duration = duration}
end
-- Disable the damage texture modifier on tool block.
user:set_properties{damage_texture_modifier = ""}
player_data[name].block = data
end
minetest.override_item(k, {on_secondary_use = function(itemstack, user, pointed_thing)
block_action(user)
return old_on_secondary_use(itemstack, user, pointed_thing)
end, on_place = function(itemstack, placer, pointed_thing)
block_action(placer)
return old_on_place(itemstack, placer, pointed_thing)
end, on_drop = function(itemstack, dropper, pos)
local name = itemstack:get_name()
local player_name = dropper:get_player_name()
local control_bits = dropper:get_player_control_bits()
local throw_data = player_data[player_name].throw
-- If in the process of throwing, either LMB, RMB, or item name is not the same then return the old function.
if throw_data or dropper:get_wielded_item():get_name() ~= name or (floor(control_bits / 128) % 2 ~= 1 and floor(control_bits / 256) % 2 ~= 1) then
return old_on_drop(itemstack, dropper, pos)
end
throw_data = {name = name, time = get_us_time(), item = itemstack:take_item(), tool_capabilities = registered_tools[name].tool_capabilities}
player_data[player_name].throw = throw_data
return itemstack
end})
end
elseif max_armor_use and v.groups and v.groups.armor_shield then
-- Block feature for shields.
local armor_heal = v.groups.armor_heal or 0
local armor_use = v.groups.armor_use or 0
local armor_shield = v.groups.armor_shield or 1
local old_on_secondary_use = v.on_secondary_use
local old_on_place = v.on_place
local fleshy = 1
if v.armor_groups and v.armor_groups.fleshy then
fleshy = v.armor_groups.fleshy
end
local block_pool = (max_armor_use - armor_use + armor_heal + armor_shield + fleshy) * shield_pool_mul
if block_pool > 0 then
-- Allow the shield to block damage.
local block_action = function(user)
local name = user:get_player_name()
local data = player_data[name].shield
data = {name = k, pool = block_pool}
-- Disable the damage texture modifier on shield block.
user:set_properties{damage_texture_modifier = ""}
player_data[name].shield = data
end
minetest.override_item(k, {on_secondary_use = function(itemstack, user, pointed_thing)
block_action(user)
return old_on_secondary_use(itemstack, user, pointed_thing)
end, on_place = function(itemstack, placer, pointed_thing)
block_action(placer)
return old_on_place(itemstack, placer, pointed_thing)
end})
end
end
end
end)
minetest.register_globalstep(function(dtime)
lag = dtime * 1000000
for k, v in pairs(player_data) do
local server_lag = lag + get_player_information(k).avg_jitter * 1000000
local player = get_player_by_name(k)
if v.block then
-- Check if the player is holding down the RMB key.
if floor(player:get_player_control_bits() / 256) % 2 == 1 then
-- Update the block time.
player_data[k].block.time = get_us_time()
end
local block = v.block
-- Remove the block table if it's past duration.
if block.time + block.duration + server_lag < get_us_time() then
-- Revert the damage texture modifier.
player:set_properties{damage_texture_modifier = player_data[k].damage_texture_modifier}
player_data[k].block = nil
end
end
if v.throw then
local control_bits = player:get_player_control_bits()
-- If neither LMB or RMB is down then throw the item.
if floor(control_bits / 128) % 2 ~= 1 and floor(control_bits / 256) % 2 ~= 1 then
local pos = player:get_pos()
pos.y = pos.y + player:get_properties().eye_height
local obj = add_entity(pos, "pvp_revamped:projectile")
local ent = obj:get_luaentity()
if ent then
local name = player:get_player_name()
local throw_style = player_data[name].throw_style
local throw_data = v.throw
local tool_capabilities = throw_data.tool_capabilities
local throw_speed = tool_capabilities.throw_speed
local damage = tool_capabilities.damage_groups.fleshy
local time = get_us_time()
local full_throw = throw_data.time + tool_capabilities.full_throw
local spin
if full_throw > time then
local re = (full_throw - time) / 200000
if re > 0.5 then
damage = tool_capabilities.damage_groups.fleshy - re
throw_speed = throw_speed - re
end
end
if throw_style == projectile_throw_style_spinning then
spin = throw_speed
end
ent:set_item(name, throw_data.item)
ent:throw(player, throw_speed, {x = 0, y = projectile_gravity, z = 0}, max(damage * projectile_dmg_mul, 0.1), throw_style, spin)
end
player_data[k].throw = nil
end
end
if v.stagger then
local stagger = v.stagger
-- Check if the stagger duration expired.
if stagger.time + stagger.value + server_lag < get_us_time() then
-- Restore the player's physics.
get_player_by_name(k):set_physics_override({speed = 1, jump = 1})
player_data[k].stagger = nil
end
end
if v.dodge then
local active_dodges = 0
-- Process the player's dodge table cooldown.
for j, l in pairs(v.dodge) do
-- Find if it's aerial or not.
if j > 4 and l + dodge_aerial_cooldown + server_lag < get_us_time() then
player_data[k].dodge[j] = nil
elseif j < 5 and l + dodge_cooldown + server_lag < get_us_time() then
player_data[k].dodge[j] = nil
elseif l + dodge_duration + server_lag > get_us_time() then
active_dodges = active_dodges + 1
end
end
if active_dodges < 1 and player:get_properties().damage_texture_modifier == "" then
-- Revert the damage texture modifier.
player:set_properties{damage_texture_modifier = player_data[k].damage_texture_modifier}
end
-- Store the dodge amount for later use.
player_data[k].active_dodges = active_dodges
-- If this table contains no more dodges remove it.
if maxn(player_data[k].dodge) < 1 then
player_data[k].dodge = nil
end
end
if v.dash then
-- Process the player's dash table cooldown.
for j, l in pairs(v.dash) do
-- Find if it's aerial or not.
if j > 4 and l + dash_aerial_cooldown + server_lag < get_us_time() then
player_data[k].dash[j] = nil
elseif j < 5 and l + dash_cooldown + server_lag < get_us_time() then
player_data[k].dash[j] = nil
end
end
-- If this table contains no more dashes remove it.
if maxn(player_data[k].dash) < 1 then
player_data[k].dash = nil
end
end
end
end)
if sscsm then
-- Register a sscsm for dodging and dashing.
sscsm.register({name = "pvp_revamped:movement",
file = minetest.get_modpath("pvp_revamped") .. "/movement.lua"})
-- Helper function to check and set the dodge cooldown.
local function dodge(name, player, number)
local dodge_data = player_data[name]
if not dodge_data.dodge then
dodge_data.dodge = {[number] = get_us_time()}
dodge_data.active_dodges = dodge_data.active_dodges + 1
player:set_properties{damage_texture_modifier = ""}
elseif dodge_data.dodge and not dodge_data.dodge[number] then
dodge_data.dodge[number] = get_us_time()
dodge_data.active_dodges = dodge_data.active_dodges + 1
player:set_properties{damage_texture_modifier = ""}
end
end
-- Channel for dodge request.
sscsm.register_on_com_receive("pvp_revamped:dodge", function(name, msg)
if msg and type(msg) == "string" then
local player = get_player_by_name(name)
local velocity = player:get_player_velocity().y
local aerial_points = 0
if velocity < 0.0 or velocity > 0.0 then
aerial_points = 4
end
if msg == "dodge_l" then
dodge(name, player, 1 + aerial_points)
elseif msg == "dodge_u" then
dodge(name, player, 2 + aerial_points)
elseif msg == "dodge_r" then
dodge(name, player, 3 + aerial_points)
elseif msg == "dodge_d" then
dodge(name, player, 4 + aerial_points)
else
return false
end
end
end)
-- Channel for dash request.
sscsm.register_on_com_receive("pvp_revamped:dash", function(name, msg)
if msg and type(msg) == "string" then
local player = get_player_by_name(name)
local yaw = player:get_look_horizontal()
local y = dash_speed * 0.5
local aerial_points = 0
local dash_key = 0
local x = 0
local z = 0
local velocity = player:get_player_velocity().y
if velocity < 0.0 or velocity > 0.0 then
aerial_points = 4
end
if msg == "dash_l" then
x = -dash_speed
dash_key = 1 + aerial_points
elseif msg == "dash_u" then
z = dash_speed
dash_key = 2 + aerial_points
elseif msg == "dash_r" then
x = dash_speed
dash_key = 3 + aerial_points
elseif msg == "dash_d" then
z = -dash_speed
dash_key = 4 + aerial_points
else
return false
end
local dash_data = player_data[name]
local function dash()
local co = cos(yaw)
local si = sin(yaw)
local re_x = co * x - si * z
local re_z = si * x + co * z
player:add_player_velocity({x = re_x, y = y, z = re_z})
end
if not dash_data.dash then
dash()
dash_data.dash = {[dash_key] = get_us_time()}
elseif dash_data.dash and not dash_data.dash[dash_key] then
dash()
dash_data.dash[dash_key] = get_us_time()
end
end
end)
end
-- Create an empty data sheet for the player.
minetest.register_on_joinplayer(function(player)
player_data[player:get_player_name()] = {damage_texture_modifier = player:get_properties().damage_texture_modifier,
active_dodges = 0}
end)
-- Helper function to drop an item.
local function drop(player, item, pos)
if not pos then
pos = player:get_pos()
end
local obj = add_item(pos, item)
if obj then
obj:get_luaentity().collect = true
end
end
-- Clear up memory if the player leaves.
-- Drop any item the player is about to throw on leave.
minetest.register_on_leaveplayer(function(player)
local name = player:get_player_name()
local throw_data = player_data[name].throw
if throw_data then
drop(player, throw_data.item)
player_data[name].throw = nil
end
player_data[name] = nil
end)
-- Drop any item the player is about to throw on death.
minetest.register_on_dieplayer(function(player)
local name = player:get_player_name()
local throw_data = player_data[name].throw
if throw_data then
drop(player, throw_data.item)
player_data[name].throw = nil
end
end)
-- Drop any item the player is about to throw on shutdown.
minetest.register_on_shutdown(function()
for k, v in pairs(player_data) do
local throw_data = v.throw
if throw_data then
drop(player, throw_data.item)
player_data[k].throw = nil
end
end
end)
-- Do the damage calculations when the player gets hit.
minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
local name = player:get_player_name()
-- If the player is dodging return true.
if player_data[name].active_dodges > 0 then
return true
end
local hitter_name = hitter:get_player_name()
-- Cancel any attack if the hitter is in dodge mode.
if player_data[hitter_name].active_dodges > 0 then
return true
end
local pos1 = hitter:get_pos()
local pos2 = player:get_pos()
local hitter_pos = {x = pos1.x, y = pos1.y, z = pos1.z}
local item = registered_tools[hitter:get_wielded_item():get_name()]
local range = 4
local yaw = player:get_look_horizontal()
local front
local side
local arm
local leg
local knee
local re_yaw
local full_punch
local full_punch_interval = 1.4
if item and item.range then
range = item.range
end
-- Get whether this is a full punch.
if tool_capabilities and time_from_last_punch >= tool_capabilities.full_punch_interval then
full_punch = true
full_punch_interval = tool_capabilities.full_punch_interval
elseif tool_capabilities and tool_capabilities.full_punch_interval then
full_punch_interval = tool_capabilities.full_punch_interval
end
-- Raise the position to eye height.
hitter_pos.y = hitter_pos.y + hitter:get_properties().eye_height
-- Get the second position from the direction of the hitter.
local _dir = hitter:get_look_dir()
local hit_pos1 = add(hitter_pos, _dir)
local hit_pos2 = add(hit_pos1, multiply(_dir, range))
local ray = raycast(hit_pos1, hit_pos2)
for pointed_thing in ray do
if pointed_thing.type == "object" and pointed_thing.ref:is_player() and pointed_thing.ref:get_player_name() == name then
local hit_point = pointed_thing.intersection_point
local newpos = subtract(hit_point, pos2)
local y1 = hit_point.y
local y2 = pos2.y
if head_height and head_dmg_mul and y1 > y2 + head_height then
-- If the player was hit in the head add extra damage.
damage = damage * head_dmg_mul
elseif torso_height and y1 > y2 + torso_height then
-- Find if the player was hit in the torso or arm.
local near_part = 0
local past_distance = -1
for _, point in pairs(hit_points) do
local x = point.x
local y = point.y
local z = point.z
local co = cos(yaw)
local si = sin(yaw)
local re_x = co * x - si * z
local re_z = si * x + co * z
local dist = distance(newpos, {x = re_x, y = y, z = re_z})
if dist < past_distance or past_distance == -1 then
past_distance = dist
near_part = point.part
end
end
if near_part == 1 then
-- Hit in the arm.
arm = true
if arm_dmg_mul then
damage = damage * arm_dmg_mul
end
elseif torso_dmg_mul then
-- Hit in the torso.
damage = damage * torso_dmg_mul
end
elseif leg_height and y1 > y2 + leg_height then
-- Hit in the leg.
leg = true
if leg_dmg_mul then
damage = damage * leg_dmg_mul
end
elseif knee_height and y1 > y2 + knee_height then
-- Hit in the knee.
knee = true
if leg_dmg_mul then
damage = damage * leg_dmg_mul
end
else
-- Hit in the lower leg.
leg = true
if leg_dmg_mul then
damage = damage * leg_dmg_mul
end
end
local dist = distance(hitter_pos, pos2)
local optimal_range = range * optimal_distance_mul
local dist_rounded = dist + 0.5 - (dist + 0.5) % 1
-- If the distance rounded is outside the range skip.
if dist_rounded <= range + 1 then
-- Add or remove damage based on the distance.
-- Full punches are not affected by maximum distance.
if not full_punch and optimal_distance_mul and maximum_distance_dmg_mul and dist_rounded > optimal_range then
damage = damage - range * maximum_distance_dmg_mul
elseif optimal_distance_mul and optimal_distance_dmg_mul and dist_rounded < optimal_range then
damage = damage + optimal_range - dist_rounded * optimal_distance_dmg_mul
end
end
-- Get the yaw from both the player and intersection point.
local yaw2 = atan(newpos.z / newpos.x) + rad90
if hit_point.x >= pos2.x then
yaw2 = yaw2 + pi
end
re_yaw = rad360 - (yaw - yaw2)
if re_yaw < 0 then
re_yaw = rad360 - re_yaw
end
if re_yaw > rad360 then
re_yaw = re_yaw - rad360
end
if (re_yaw <= 0.7853982 and re_yaw >= 0) or (re_yaw <= 6.283185 and re_yaw >= 5.497787) then
-- Hit on the front.
front = true
if front_dmg_mul then
damage = damage * front_dmg_mul
end
elseif re_yaw <= 2.356194 then
-- Hit on the left-side.
side = true
if side_dmg_mul then
damage = damage * side_dmg_mul
end
elseif back_dmg_mul and re_yaw <= 3.926991 then
-- Hit on the back-side.
damage = damage * back_dmg_mul
else
-- Hit on the right-side.
side = true
if side_dmg_mul then
damage = damage * side_dmg_mul
end
end
-- You can only hit the knee-caps in the front.
if side and knee then
knee = nil
leg = true
end
-- End the for loop we got what we come for.
break
end
end
if elevated_dmg_mul and pos1.y > pos2.y then
-- Give a damage bonus or drawback to the aggressor if they are above the victim.
damage = damage * elevated_dmg_mul
elseif lower_elevation_dmg_mul and pos1.y < pos2.y then
-- Give a damage bonus or drawback to the aggressor if they are below the victim.
damage = damage * lower_elevation_dmg_mul
elseif equal_height_dmg_mul then
-- Give a damage bonus or drawback to the aggressor if they are equal footing with the victim.
damage = damage * equal_height_dmg_mul
end
-- This damage bonus can only be used if this is a full interval punch.
if full_punch and velocity_dmg_mul then
local v1 = hitter:get_player_velocity()
local vv
if front then
-- Ignore the victim's speed if you hit them in the front.
vv = abs(v1.x) + abs(v1.y) + abs(v1.z)
else
-- Subtract the victim's velocity from the aggressor if they where not hit in the front.
local v2 = player:get_player_velocity()
vv = abs(v1.x) - abs(v2.x) + abs(v1.y) - abs(v2.y) + abs(v1.z) - abs(v2.z)
end
if vv > 0 then
-- Give a damage bonus to the aggressor based on how fast they are running.
damage = damage + vv * velocity_dmg_mul
end
end
-- If damage is at or below zero set it to a default value.
damage = max(damage, 0.1)
-- Remove the hitter's blocking data.
player_data[hitter_name].block = nil
player_data[hitter_name].shield = nil
local data_block = player_data[name].block
local hp = player:get_hp()
local wielded_item = player:get_wielded_item()
local item_name = wielded_item:get_name()
-- Process if the player is blocking with a tool or not.
if front and data_block and data_block.pool > 0 and data_block.time + data_block.duration + lag + max(get_player_information(name).avg_jitter - get_player_information(hitter_name).avg_jitter, 0) * 1000000 > time then
-- Block the damage and add it as wear to the tool.
wielded_item:add_wear(((damage - full_punch_interval) / 75) * block_wear_mul)
player:set_wielded_item(wielded_item)
data_block.pool = data_block.pool - damage
-- Remove block table if pool is zero or below.
if data_block.pool <= 0 then
player_data[name].block = nil
return true
end
player_data[name].block = data_block
return true
elseif data_block then
-- Block attempt failed.
player_data[name].block = nil
end
local data_shield = player_data[name].shield
-- Process if the player is blocking with a shield or not.
if data_shield and data_shield.pool > 0 and data_shield.name == item_name and (front or side) then
-- Block the damage and add it as wear to the tool.
local axe_wear = 0
if tool_capabilities and tool_capabilities.damage_groups and tool_capabilities.damage_groups.shield_dmg then
axe_wear = tool_capabilities.damage_groups.shield_dmg
end
wielded_item:add_wear((((damage - full_punch_interval) / 75) * block_wear_mul) + axe_wear)
player:set_wielded_item(wielded_item)
data_shield.pool = data_shield.pool - (damage + axe_wear)
-- Remove shield table if pool is zero or below.
if data_shield.pool <= 0 then
player_data[name].shield = nil
return true
end
player_data[name].shield = data_shield
return true
elseif data_shield then
-- Shield block attempt failed.
player_data[name].shield = nil
end
-- Process if the player was hit in the arm.
if arm then
local item2 = registered_tools[item_name]
local chance
if item2 and not data_shield and not data_block and item2.tool_capabilities and item2.tool_capabilities.damage_groups.fleshy and item2.tool_capabilities.full_punch_interval then
-- Compute the chance to disarm by the victim's hp and tool stats.
chance = random(0, ((hp + (item2.tool_capabilities.damage_groups.fleshy - item2.tool_capabilities.full_punch_interval) * disarm_chance_mul) - damage) + 1)
elseif not item2 and not data_shield and not data_block then
-- Compute the chance to disarm by the victim's hp.
chance = random(0, (hp - damage) + 1)
end
-- Disarm the player if chance equals zero.
if chance and chance <= 0 then
local drop_item = wielded_item:take_item()
local obj = add_item(pos2, drop_item)
if obj then
obj:get_luaentity().collect = true
end
player:set_wielded_item(wielded_item)
end
end
local function set_stagger_data(speed)
player:set_physics_override({speed = speed, jump = speed})
data_stagger = {}
data_stagger.time = get_us_time()
data_stagger.value = (1 / speed) * 500000
player_data[name].stagger = data_stagger
end
-- Process if the player was hit in the leg.
if leg then
-- Stagger the player.
local speed = min(1 / max(damage - hp, 1) * leg_stagger_mul, 0.1)
local data_stagger = player_data[name].stagger
if not data_stagger or data_stagger.value > speed then
set_stagger_data(speed)
end
elseif knee then
-- Stagger the player.
local speed = min(1 / max(damage - hp, 1.5) * knee_stagger_mul, 0.1)
local data_stagger = player_data[name].stagger
if data_stagger then
-- Add the original value and update all stagger data.
speed = min(abs(speed - data_stagger.value), 0.1)
set_stagger_data(speed)
else
set_stagger_data(speed)
end
end
if player:get_properties().damage_texture_modifier == "" then
-- Revert the damage texture modifier.
player:set_properties{damage_texture_modifier = player_data[name].damage_texture_modifier}
end
-- Damage the player.
player:set_hp(hp - damage, "punch")
return true
end)
-- Cmd for changing the way you throw items.
minetest.register_chatcommand("throw_style", {
params = "[<style>]: Change how you throw an item.",
description = "Change how you throw an item. Accepted values are [none|spin|dip]",
privs = {
interact = true,
},
func = function(name, param)
-- Check the given param.
if param == "none" then
-- Set the style to none.
player_data[name].throw_style = nil
return true, "Throw style set to none."
elseif param == "spin" then
-- Give the item a little spin.
player_data[name].throw_style = projectile_throw_style_spinning
return true, "Throw style set to spin."
elseif param == "dip" then
-- Bullet drop.
player_data[name].throw_style = projectile_throw_style_dip
return true, "Throw style set to dip."
end
-- throw_style cmd help.
return false, "Only parameters: 'none', 'spin', and 'dip' are accepted."
end
})