switched guns bone transformations to allow for client IP, made player model handler changes, made changes that make firerate more consistent regardless of steprate and variation, fixed broken normalization of velocity recoil

This commit is contained in:
FatalErr42O 2024-12-09 18:54:26 -08:00
parent c44be0dff0
commit 25ea38c668
10 changed files with 106 additions and 45 deletions

View File

@ -113,6 +113,13 @@ function Ammo_handler:spend_round()
return bullet_spent
end
end
function Ammo_handler:can_spend_round()
local bullet_spent = self.ammo.next_bullet
if (self.ammo.total_bullets > 0) and (bullet_spent ~= "empty") then
return true
end
return false
end
function Ammo_handler:chamber_round()
self.ammo.next_bullet = Guns4d.math.weighted_randoms(self.ammo.loaded_bullets) or "empty"
end

View File

@ -105,7 +105,7 @@ function controls:update(dt)
end
end
for i, tbl in pairs(call_queue) do
tbl.control.func(self, tbl.active, tbl.interrupt, tbl.data, busy_list, gun, self.handler)
tbl.control.func(self, tbl.active, tbl.interrupt, tbl.data, busy_list, gun, self.handler, dt)
end
self.busy_list = {}
elseif self.busy_list then

View File

@ -1,6 +1,7 @@
local gun_default = Guns4d.gun
local mat4 = leef.math.mat4
--[[
*
@ -183,16 +184,19 @@ local function initialize_b3d_animation_data(self, props)
self.b3d_model.global_frames.arm_right = nil
end
if main then
--ATTENTION: this is broken, roll is somehow translating to yaw. How? fuck if I know, but I will have to fix this eventually.
--use -1 as it does not exist and thus will always go to the default resting pose
--we compose it by the inverse because we need to get the global CHANGE in rotation for the animation rotation offset. I really need to comment more often
--print(leef.b3d_nodes.get_node_rotation(nil, main, nil, -1))
local newvec = leef.b3d_nodes.get_node_rotation(nil, main, nil, target_frame)*leef.b3d_nodes.get_node_rotation(nil, main, nil, -1):inverse()
--used to use euler
table.insert(self.b3d_model.global_frames.rotation, newvec)
end
--we compose it by the inverse because we need to get the global offset in rotation for the animation rotation offset. I really need to comment more often
--print(leef.b3d_nodes.get_node_rotation(nil, main, nil, -1))
local newvec = leef.b3d_nodes.get_node_rotation(nil, main, nil, target_frame)*leef.b3d_nodes.get_node_rotation(nil, main, nil, -1):inverse()
--used to use euler
table.insert(self.b3d_model.global_frames.rotation, newvec)
end
local t, r = leef.b3d_nodes.get_node_global_transform(main, props.visuals.animations.loaded.x,1)
self.b3d_model.root_orientation_rest = mat4.new(t)
self.b3d_model.root_orientation_rest_inverse = mat4.invert(mat4.new(), t)
--[[local t2 = mat4.from_quaternion(leef.math.quat.new(unpack(main.rotation)))
self.b3d_model.root_orientation = mat4.new(t2)
self.b3d_model.root_orientation_inverse = mat4.invert(mat4.new(), t2)]]
local verts = {}
self.bones = {}
@ -200,7 +204,7 @@ local function initialize_b3d_animation_data(self, props)
for i, v in pairs(self.b3d_model.node_paths) do
if v.mesh then
--if there's a mesh present transform it's verts into global coordinate system, add add them to them to a big list.
local transform, _ = leef.b3d_nodes.get_node_global_transform(v, self.properties.visuals.animations.loaded.x, 1)
local transform, _ = leef.b3d_nodes.get_node_global_transform(v, props.visuals.animations.loaded.x, 1)
for _, vert in ipairs(v.mesh.vertices) do
vert.pos[4]=1
table.insert(verts, transform*vert.pos)

View File

@ -24,12 +24,11 @@ function gun_default:update(dt)
--player look rotation. I'm going to keep it real, I don't remember what this math does. Player handler just stores the player's rotation from MT in degrees, which is for some reason inverted
--timers
--it's set up like this so that if the gun is fired on auto and the RPM is very fast (faster then globalstep) we know how many rounds to let off.
if self.rechamber_time > 0 then
self.rechamber_time = self.rechamber_time - dt
else
self.rechamber_time = 0
end
self.time_since_creation = self.time_since_creation + dt
self.time_since_last_fire = self.time_since_last_fire + dt
@ -77,11 +76,16 @@ end
--manage burstfire
function gun_default:update_burstfire()
if self.rechamber_time <= 0 then
local success = self:attempt_fire()
if not success then
self.burst_queue = 0
else
self.burst_queue = self.burst_queue - 1
while true do
local success = self:attempt_fire()
if success then
self.burst_queue = self.burst_queue - 1
else
if not self.ammo_handler:can_spend_round() then
self.burst_queue = 0
end
break
end
end
end
end
@ -136,12 +140,13 @@ function gun_default:update_image_and_text_meta(meta)
end
function gun_default:attempt_fire()
assert(self.instance, "attempt to call object method on a class")
if self.rechamber_time <= 0 and not self.ammo_handler.ammo.magazine_psuedo_empty then
local props = self.properties
--check if there could have been another round fired between steps.
if ( (self.rechamber_time + (60/props.firerateRPM) < 0) or (self.rechamber_time <= 0) ) and (not self.ammo_handler.ammo.magazine_psuedo_empty) then
local spent_bullet = self.ammo_handler:spend_round()
if spent_bullet and spent_bullet ~= "empty" then
local dir = self.dir
local pos = self.pos
local props = self.properties
if not Guns4d.ammo.registered_bullets[spent_bullet] then
minetest.log("error", "unregistered bullet itemstring"..tostring(spent_bullet)..", could not fire gun (player:"..self.player:get_player_name()..")");
@ -170,10 +175,12 @@ function gun_default:attempt_fire()
fire_sound.pos = self.pos
self:play_sounds(fire_sound)
self.rechamber_time = 60/props.firerateRPM
--this should handle the firerate being faster than dt
self.rechamber_time = self.rechamber_time + (60/props.firerateRPM)
return true
end
end
return false
end
--[[function gun_default:damage()
assert(self.instance, "attempt to call object method on a class")
@ -199,7 +206,7 @@ function gun_default:recoil()
end
local length = math.sqrt(recoil.x^2+recoil.y^2)
if length > rprops.angular_velocity_max[axis] then
local co = rprops.angular_velocity_max[axis]*length
local co = rprops.angular_velocity_max[axis]/length
recoil.x = recoil.x*co
recoil.y = recoil.y*co
end
@ -332,7 +339,7 @@ function gun_default:get_pos(offset_pos, relative, ads, ignore_translations)
else
hud_pos = pos+handler:get_pos()
end]]
if minetest.get_player_by_name("fatal2") then
--if minetest.get_player_by_name("fatal2") then
--[[local hud = minetest.get_player_by_name("fatal2"):hud_add({
hud_elem_type = "image_waypoint",
text = "muzzle_flash2.png",
@ -344,7 +351,7 @@ function gun_default:get_pos(offset_pos, relative, ads, ignore_translations)
minetest.after(0, function(hud)
minetest.get_player_by_name("fatal2"):hud_remove(hud)
end, hud)]]
end
--end
--world pos, position of bone, offset of gun from bone (with added_pos)
return pos
@ -368,6 +375,10 @@ function gun_default:add_entity()
--obj:on_step()
--self:update_entity()
end
local mat4 = leef.math.mat4
local tmp_mat4_rot = mat4.identity()
local ip_time = Guns4d.config.gun_axial_interpolation_time
local ip_time2 = Guns4d.config.translation_interpolation_time
function gun_default:update_entity()
local obj = self.entity
local player = self.player
@ -380,17 +391,35 @@ function gun_default:update_entity()
visibility = false
end
--Irrlicht uses counterclockwise but we use clockwise.
local pos = self.gun_translation
local ads = props.ads.offset
local hip = props.hip.offset
local offset = self.total_offsets.gun_trans
local ip = Guns4d.math.smooth_ratio(Guns4d.math.clamp(handler.control_handler.ads_location*2,0,1))
local ip_inv = 1-ip
local pos = self.gun_translation --entity directly dictates the translation of the gun
pos.x = (ads.x*ip)+(hip.x*ip_inv)+offset.x
pos.y = (ads.y*ip)+(hip.y*ip_inv)+offset.y
pos.z = (ads.z*ip)+(hip.z*ip_inv)+offset.z
self.gun_translation = pos
obj:set_attach(player, handler.player_model_handler.bone_aliases.gun, {x=pos.x*10, y=pos.y*10, z=pos.z*10}, -axial_rot, visibility)
local scale = self.properties.visuals.scale
--some complicated math to get client interpolation to work. It doesn't really account for the root bone having an (oriented) parent bone currently... hopefully that's not an issue.
local b3d = self.b3d_model
local rot = tmp_mat4_rot:set_rot_luanti_entity(axial_rot.x*math.pi/180,axial_rot.y*math.pi/180, 0)
tmp_mat4_rot = mat4.mul(tmp_mat4_rot, {b3d.root_orientation_rest_inverse, rot, b3d.root_orientation_rest})
local xr,yr,zr = tmp_mat4_rot:get_rot_irrlicht_bone()
obj:set_attach(player, handler.player_model_handler.bone_aliases.gun, nil, nil, visibility)
obj:set_bone_override(self.consts.ROOT_BONE, {
position = {
vec = {x=pos.x/scale, y=pos.y/scale, z=pos.z/scale},
interpolation = ip_time2,
},
rotation = {
vec = {x=-xr,y=-yr,z=-zr},
interpolation = ip_time,
}
})
end
function gun_default:has_entity()
assert(self.instance, "attempt to call object method on a class")
@ -453,16 +482,14 @@ local e = 2.7182818284590452353602874713527 --I don't know how to find it otherw
function gun_default:update_recoil(dt)
for axis, _ in pairs(self.offsets.recoil) do
for _, i in pairs({"x","y"}) do
local recoil_vel = self.velocities.recoil[axis][i]
local recoil = self.offsets.recoil[axis][i]
local recoil_vel = Guns4d.math.clamp(self.velocities.recoil[axis][i],-self.properties.recoil.angular_velocity_max[axis],self.properties.recoil.angular_velocity_max[axis])
local old_recoil_vel = recoil_vel
recoil = recoil + recoil_vel
--this is modelled off a geometric sequence where the Y incercept of the sequence is set to recoil_vel.
if math.abs(recoil_vel) > 0.001 then
local r = (10*self.properties.recoil.velocity_correction_factor[axis])^-1
local vel_co = e^-( (self.time_since_last_fire^2)/(2*r^2) )
recoil_vel = self.velocities.init_recoil[axis][i]*vel_co
else
local r = (10*self.properties.recoil.velocity_correction_factor[axis])^-1
local vel_co = e^-( (self.time_since_last_fire^2)/(2*r^2) )
recoil_vel = self.velocities.init_recoil[axis][i]*vel_co
if math.abs(recoil_vel) < 0.0001 then
recoil_vel = 0
end
self.velocities.recoil[axis][i] = recoil_vel
@ -488,6 +515,7 @@ function gun_default:update_recoil(dt)
self.offsets.recoil[axis][i] = abs*sign
end
end
--print(self.velocities.recoil.player_axial.x, self.velocities.recoil.player_axial.y)
end
function gun_default:update_animation(dt)
local ent = self.entity

View File

@ -56,6 +56,7 @@ function player_handler:update(dt)
self.player_model_handler:update(dt)
player:set_eye_offset(self.gun.total_offsets.look_trans*10)
self.last_eye_offset = self.gun.total_offsets.look_trans
--this has to be checked after control handler
if TICK % 4 == 0 then
self.touching_ground = self:get_is_on_ground()

View File

@ -158,6 +158,8 @@ function player_model:update(dt)
self:update_arm_bones(dt)
end
local ip_time = Guns4d.config.player_axial_interpolation_time
local ip_time2 = Guns4d.config.translation_interpolation_time
function player_model:update_aiming(dt)
--gun bones:
local player = self.player
@ -183,12 +185,12 @@ function player_model:update_aiming(dt)
{
position = {
vec={x=pos.x, y=pos.y, z=pos.z},
absolute = true,
interpolation=.25
interpolation=ip_time2,
absolute = true
},
rotation = {
vec={x=-rot.x,y=-rot.y,z=0},
interpolation=.1,
interpolation=ip_time,
absolute = true
}
})
@ -198,6 +200,10 @@ function player_model:update_aiming(dt)
-- minetest.chat_send_all(dump(pos))
end
function player_model:update_look_offsets()
player:set_eye_offset(self.gun.total_offsets.look_trans*10)
self.last_eye_offset = self.gun.total_offsets.look_trans
end
--default arm code, compatible with MTG model.
function player_model:update_arm_bones(dt)
local player = self.player
@ -218,13 +224,15 @@ function player_model:update_arm_bones(dt)
player:set_bone_override(self.bone_aliases.arm_right, {
rotation = {
vec={x=math.pi/2, y=0, z=0}-right_rotation,
absolute = true
absolute = true,
interpolation = .1
}
})
player:set_bone_override(self.bone_aliases.arm_left, {
rotation = {
vec={x=math.pi/2, y=0, z=0}-left_rotation,
absolute = true
absolute = true,
interpolation = .1
}
})
end
@ -236,7 +244,8 @@ function player_model:update_head(dt)
player:set_bone_override(self.bone_aliases.head, {
rotation = {
vec={x=handler.look_rotation.x*math.pi/180,z=0,y=0},
absolute = true
absolute = true,
interpolation = .5
}
})
end

View File

@ -19,7 +19,12 @@ Guns4d.default_controls.auto = {
timer = 0,
func = function(self, active, interrupted, data, busy_list, gun, handler)
if gun.properties.firemodes[gun.current_firemode] == "auto" then
gun:attempt_fire()
while true do
local success = gun:attempt_fire()
if not success then
break
end
end
end
end
}

View File

@ -16,7 +16,10 @@ Guns4d.config = {
infinite_ammo_priv = "guns4d_infinite_ammo",
interpret_initial_wear_as_ammo = false,
punch_from_player_not_gun = true,
vertical_rotation_factor = 10,
vertical_rotation_factor = 25, --the rate at which the gun moves to the player's look vetically. With bone interpolation as of 5.9 this can be a lot higher now.
player_axial_interpolation_time = .05, --the time it takes for the gun to reach the new angle (around the player's eye) set by the server. This includes recoil, sway, or anything that shifts the gun without misaligning sights.
gun_axial_interpolation_time = .05, --the same as player_axial but for the rotation of the gun.
translation_interpolation_time = .09, --time it takes for nonrotation components (i.e. hip offset, aiming offset, bone location, etc) to interpolate.
simple_headshot = true, --holdover feature before a more complex system is implemented
simple_headshot_body_ratio = .75, --percentage of hitbox height that is body.
default_fov = 80,

View File

@ -116,7 +116,6 @@ end
-- in guns4d.math
--@section math
--all of the following is disgusting and violates the namespace because I got used to love2d.
function Guns4d.math.clamp(val, lower, upper)
if lower > upper then lower, upper = upper, lower end
return math.max(lower, math.min(upper, val))

View File

@ -45,6 +45,11 @@ touch.auto = table.copy(pc.auto)
touch.auto.conditions = {"LMB"}
touch.auto.func = function(active, interrupted, data, busy_list, gun, handler)
if (not handler.control_handler.player_pressed.sneak) and gun.properties.firemodes[gun.current_firemode] == "auto" then
gun:attempt_fire()
while true do
local success = gun:attempt_fire()
if not success then
break
end
end
end
end