diff --git a/classes/Ammo_handler.lua b/classes/Ammo_handler.lua index 1c85ff3..a8a36fc 100644 --- a/classes/Ammo_handler.lua +++ b/classes/Ammo_handler.lua @@ -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 diff --git a/classes/Control_handler.lua b/classes/Control_handler.lua index 0971ef2..c94130b 100644 --- a/classes/Control_handler.lua +++ b/classes/Control_handler.lua @@ -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 diff --git a/classes/Gun-construct.lua b/classes/Gun-construct.lua index 322f8f8..28fbc8b 100644 --- a/classes/Gun-construct.lua +++ b/classes/Gun-construct.lua @@ -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) diff --git a/classes/Gun-methods.lua b/classes/Gun-methods.lua index e7796d2..c5bc67a 100644 --- a/classes/Gun-methods.lua +++ b/classes/Gun-methods.lua @@ -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 diff --git a/classes/Player_handler.lua b/classes/Player_handler.lua index 01db351..ad889aa 100644 --- a/classes/Player_handler.lua +++ b/classes/Player_handler.lua @@ -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() diff --git a/classes/Player_model_handler.lua b/classes/Player_model_handler.lua index b71fc3c..e7af669 100644 --- a/classes/Player_model_handler.lua +++ b/classes/Player_model_handler.lua @@ -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 diff --git a/default_controls.lua b/default_controls.lua index 5c04b04..8be3b1f 100644 --- a/default_controls.lua +++ b/default_controls.lua @@ -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 } diff --git a/init.lua b/init.lua index 2b34a3c..6aca802 100644 --- a/init.lua +++ b/init.lua @@ -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, diff --git a/misc_helpers.lua b/misc_helpers.lua index f72c258..773e1a0 100644 --- a/misc_helpers.lua +++ b/misc_helpers.lua @@ -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)) diff --git a/touch_support.lua b/touch_support.lua index 81dd39c..cab6dc3 100644 --- a/touch_support.lua +++ b/touch_support.lua @@ -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 \ No newline at end of file