added the ability to offset the translation of guns and look + completely reworked aiming down sights

This commit is contained in:
FatalErr42O 2024-08-01 22:36:38 -07:00
parent 67dbaeebd6
commit 5e29bc07e1
34 changed files with 400 additions and 308 deletions

View File

@ -104,7 +104,7 @@ MTUL subtasks
this won't be fun.
documentation
( ) Instantiatable_class
( ) mtul.class.new_class
( ) Player_model_handler
( ) Registering a model for compatibility

View File

@ -1,4 +1,4 @@
Ammo_handler = Instantiatable_class:inherit({
Ammo_handler = mtul.class.new_class:inherit({
name = "Ammo_handler",
construct = function(def)
if def.instance then

View File

@ -35,7 +35,7 @@ minetest.register_globalstep(function(dt)
end
end
end)
Guns4d.bullet_hole = Instantiatable_class:inherit({
Guns4d.bullet_hole = mtul.class.new_class:inherit({
texture = 'bullet_hole.png',
exp_time = 30, --how much time a rendered bullet hole takes to expire
unrendered_exptime = 10, --how much time an unrendered bullet hole takes to expire

View File

@ -445,4 +445,4 @@ function ray.construct(def)
end
end
end
Guns4d.bullet_ray = Instantiatable_class:inherit(ray)
Guns4d.bullet_ray = mtul.class.new_class:inherit(ray)

View File

@ -18,6 +18,7 @@ Guns4d.control_handler = {
}
]]
ads = false,
ads_location = 0,
touchscreen = false
}
--data table:
@ -46,7 +47,8 @@ function controls:update(dt)
local pressed = self.player_pressed
local call_queue = {} --so I need to have a "call" queue so I can tell the functions the names of other active controls (busy_list)
local busy_list = self.busy_list or {} --list of controls that have their conditions met. Has to be reset at END of update, so on_use and on_secondary_use can be marked
if not (self.gun.rechamber_time > 0 and self.gun.ammo_handler.ammo.next_bullet == "empty") then --check if the gun is being charged.
local gun = self.gun
if not (gun.rechamber_time > 0 and gun.ammo_handler.ammo.next_bullet == "empty") then --check if the gun is being charged.
for i, control in pairs(self:get_actions()) do
if not (i=="on_use") and not (i=="on_secondary_use") then
local def = control
@ -103,12 +105,20 @@ function controls:update(dt)
end
end
for i, tbl in pairs(call_queue) do
tbl.control.func(tbl.active, tbl.interrupt, tbl.data, busy_list, self.handler.gun, self.handler)
tbl.control.func(tbl.active, tbl.interrupt, tbl.data, busy_list, gun, self.handler)
end
self.busy_list = {}
elseif self.busy_list then
self.busy_list = nil
end
--eye offsets and ads_location
if self.ads and (self.ads_location<1) then
--if aiming, then increase ADS location
self.ads_location = Guns4d.math.clamp(self.ads_location + (dt/gun.properties.ads.aim_time), 0, 1)
elseif (not self.ads) and (self.ads_location>0) then
local divisor = gun.properties.ads.aim_time/gun.consts.AIM_OUT_AIM_IN_SPEED_RATIO
self.ads_location = Guns4d.math.clamp(self.ads_location - (dt/divisor), 0, 1)
end
end
function controls:on_use(itemstack, pointed_thing)
assert(self.instance, "attempt to call object method on a class")
@ -162,6 +172,7 @@ function controls.construct(def)
def.actions_touch = Guns4d.table.deep_copy(def.gun.properties.touch_control_actions)
def.busy_list = {}
def.handler = Guns4d.players[def.player:get_player_name()]
--def.control_handler = def.handler.control_handler has to be created afterwards so have the playerhandler add it to the fields.
def:toggle_touchscreen_mode(def.touchscreen)
table.sort(def.actions_pc, function(a,b)
@ -169,4 +180,4 @@ function controls.construct(def)
end)
end
end
Guns4d.control_handler = Instantiatable_class:inherit(Guns4d.control_handler)
Guns4d.control_handler = mtul.class.new_class:inherit(Guns4d.control_handler)

View File

@ -1,4 +1,4 @@
local Dynamic_crosshair = Instantiatable_class:inherit({
local Dynamic_crosshair = mtul.class.new_class:inherit({
increments = 1, --the number of pixels the reticle moves per frame.
frames = 32, --this defines the length of the sprite sheet. But it also helps us know how wide it is (since we have increments.)
image = "dynamic_crosshair_circular.png",
@ -37,20 +37,21 @@ function Dynamic_crosshair:update(dt)
assert(self.instance, "attemptr to call object method on a class")
local handler = self.handler
local gun = self.gun
if handler.wininfo and not handler.control_handler.ads then
local control_handler = gun.control_handler
if handler.wininfo and not control_handler.ads then
local fov = self.player:get_fov()
--we have to recalc the rough direction, otherwise walking will look wonky.
local temp_vector = vector.new()
for offset, v in pairs(gun.offsets) do
if (offset ~= "walking" or not self.normalize_walking) and (offset ~= "breathing" or not self.normalize_breathing) and (offset ~= "sway" or not self.normalize_sway) and (offset ~= "look_snap" or not self.normalize_sway) then
if (offset ~= "walking" or not self.normalize_walking) and (offset ~= "breathing" or not self.normalize_breathing) and (offset ~= "sway" or not self.normalize_sway) and (offset ~= "look" or not self.normalize_sway) then
temp_vector = temp_vector + absolute_vector(v.player_axial) + absolute_vector(v.gun_axial)
end
end
if gun.consts.HAS_SWAY and self.normalize_sway then
local max_angle =
gun.properties.sway.max_angle.gun_axial*gun.multiplier_coefficient(gun.properties.sway.hipfire_angle_multiplier.gun_axial, 1-handler.ads_location)
+ gun.properties.sway.max_angle.player_axial*gun.multiplier_coefficient(gun.properties.sway.hipfire_angle_multiplier.player_axial, 1-handler.ads_location)
gun.properties.sway.max_angle.gun_axial*gun.multiplier_coefficient(gun.properties.sway.hipfire_angle_multiplier.gun_axial, 1-control_handler.ads_location)
+ gun.properties.sway.max_angle.player_axial*gun.multiplier_coefficient(gun.properties.sway.hipfire_angle_multiplier.player_axial, 1-control_handler.ads_location)
temp_vector = temp_vector + {x=max_angle, y=max_angle, z=0}
end
--make breathing just add to the overall rotation vector (as it could be in that circle at any time, and it looks better and is more fitting)

View File

@ -9,7 +9,7 @@
-- while @{lvl1_fields.consts|consts} define attributes that should never change, like bones within the gun, framerates,
-- wether the gun is allowed to have certain attributes at all. The rest is mainly for internal workings of the mod.
--
-- Guns4d uses a class system for most moving parts- including the gun. New guns therefor are created with the :inherit(def) method,
-- Guns4d uses a class system for most moving parts- including the gun. New guns therefore are created with the :inherit(def) method,
-- where def is the definition of your new gun- or rather the changed parts of it. So to make a new gun you can run Guns4d.gun:inherit()
-- or you can do the same thing with a seperate class of weapons. Set name to "__template" for template classes of guns.
--
@ -22,8 +22,8 @@ local Vec = vector
--
-- @table gun
-- @field properties @{lvl1_fields.properties|properties} which define the vast majority of gun attributes and may change accross instances
-- @field consts @{lvl1_fields.consts|constancts} which define gun attributes and should not be changed in an instance of the gun
-- @field offsets @{lvl1_fields.offsets|offsets}. runtime storage of offsets generated by recoil sway wag or any other element.
-- @field consts @{lvl1_fields.consts|constants} which define gun attributes and should not be changed in an instance of the gun
-- @field offsets runtime storage of @{lvl1_fields.offsets|offsets} generated by recoil sway wag or any other element.
-- @compact
local gun_default = {
--- `string` the name of the gun. Set to __template for guns which have no instances.
@ -54,6 +54,8 @@ local gun_default = {
burst_queue = 0,
--- `function`
muzzle_flash = Guns4d.effects.muzzle_flash,
--- `vec3` translation of the gun relative to the "gun" bone or the player axial rotation.
gun_translation = vector.new(),
--- properties
--
@ -103,7 +105,7 @@ local gun_default = {
--- `vector` the offset of the gun relative to the eye's position at hip.
offset = Vec.new(),
--- `float` the horizontal offset of the eye when aiming
horizontal_offset = 0,
horizontal_offset = .1,
--- the time it takes to go into full aim
aim_time = 1,
},
@ -378,19 +380,24 @@ local gun_default = {
player_axial = Vec.new(),
},
---
look_snap = {
look = {
gun_axial = Vec.new(),
player_axial = Vec.new()
player_axial = Vec.new(),
look_trans = Vec.new(),
player_trans = Vec.new(),
gun_trans = Vec.new()
},
},
--- `vector` containing the offset from animations, this will be generated if {@consts.ANIMATIONS_OFFSET_AIM}=true
animation_rotation = vector.new(),
--- total offsets of the gun in the same format as a @{offsets|an offset}
total_offset_rotation = nil,
--[[total_offset_rotation = { --can't be in offsets, as they're added automatically.
gun_axial = Vec.new(),
player_axial = Vec.new(),
},]]
--[[total_offsets = {
gun_axial = vector.new(), rotation of the gun entity (around entity's own axis)
player_axial = vector.new(), rotation around the eye (the player's axis)
gun_trans = vector.new(), translation of the gun relative to attached bone's rotation
player_trans = vector.new(), translation of the gun relative to the player's eye
look_trans = vector.new() translation/offset of the player's eye
}, ]]
--player_rotation = Vec.new(),
velocities = {
recoil = {
@ -448,16 +455,6 @@ local gun_default = {
}
--I dont remember why I made this, used it though lmao
function gun_default.multiplier_coefficient(multiplier, ratio)
return 1+((multiplier*ratio)-ratio)
@ -480,7 +477,6 @@ function gun_default:update(dt)
assert(self.instance, "attempt to call object method on a class")
if not self:has_entity() then self:add_entity(); self:clear_animation() end
local handler = self.handler
local total_rot = self.total_offset_rotation
--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
@ -495,7 +491,7 @@ function gun_default:update(dt)
if self.burst_queue > 0 then self:update_burstfire() end
--update some vectors
self:update_look_rotation(dt)
self:update_look_offsets(dt)
if self.consts.HAS_SWAY then self:update_sway(dt) end
if self.consts.HAS_RECOIL then self:update_recoil(dt) end
if self.consts.HAS_BREATHING then self:update_breathing(dt) end
@ -515,13 +511,19 @@ function gun_default:update(dt)
if self.properties.crosshair then
self.crosshair:update()
end
local offsets = self.offsets
total_rot.player_axial.x = 0; total_rot.player_axial.y = 0
total_rot.gun_axial.x = 0; total_rot.gun_axial.y = 0
for type, _ in pairs(total_rot) do
for i, offset in pairs(offsets) do
if self.consts.HAS_GUN_AXIAL_OFFSETS or type~="gun_axial" then
total_rot[type] = total_rot[type]+offset[type]
local total_offset = self.total_offsets
--axis rotations
total_offset.player_axial.x = 0; total_offset.player_axial.y = 0
total_offset.gun_axial.x = 0; total_offset.gun_axial.y = 0
--translations
total_offset.player_trans.x = 0; total_offset.player_trans.y = 0; total_offset.player_trans.z = 0
total_offset.gun_trans.x = 0; total_offset.gun_trans.y = 0; total_offset.gun_trans.z = 0
total_offset.look_trans.x = 0; total_offset.look_trans.y = 0; total_offset.gun_trans.z = 0
--this doesnt work.
for type, _ in pairs(total_offset) do
for i, offset in pairs(self.offsets) do
if offset[type] and (self.consts.HAS_GUN_AXIAL_OFFSETS or type~="gun_axial") then
total_offset[type] = total_offset[type]+offset[type]
end
end
end
@ -634,7 +636,7 @@ function gun_default:recoil()
for _, i in pairs({"x","y"}) do
recoil[i] = recoil[i] + (rprops.angular_velocity[axis][i]
*rand_sign((rprops.bias[axis][i]/2)+.5))
*self.multiplier_coefficient(rprops.hipfire_multiplier[axis], 1-self.handler.ads_location)
*self.multiplier_coefficient(rprops.hipfire_multiplier[axis], 1-self.control_handler.ads_location)
--set original velocity
self.velocities.init_recoil[axis][i] = recoil[i]
end
@ -647,7 +649,7 @@ function gun_default:recoil()
end
self.time_since_last_fire = 0
end
function gun_default:update_look_rotation(dt)
function gun_default:update_look_offsets(dt)
assert(self.instance, "attempt to call object method on a class")
local handler = self.handler
local look_rotation = handler.look_rotation --remember that this is in counterclock-wise rotation. For 4dguns we use clockwise so it makes a bit more sense for recoil. So it needs to be inverted.
@ -662,27 +664,32 @@ function gun_default:update_look_rotation(dt)
player_rot.x = look_rotation.x
end
local props = self.properties
local hip = props.hip
local ads = props.ads
if not handler.control_handler.ads then
local pitch = self.total_offset_rotation.player_axial.x+player_rot.x
local gun_axial = self.offsets.look_snap.gun_axial
--hipfire rotation offsets
local pitch = self.total_offsets.player_axial.x+player_rot.x
local gun_axial = self.offsets.look.gun_axial
local offset = handler.look_rotation.x-player_rot.x
gun_axial.x = Guns4d.math.clamp(offset, 0, 15*(offset/math.abs(offset)))
gun_axial.x = gun_axial.x+(pitch*(1-self.properties.hip.axis_rotation_ratio))
self.offsets.look_snap.player_axial.x = -pitch*(1-self.properties.hip.axis_rotation_ratio)
gun_axial.x = gun_axial.x+(pitch*(1-hip.axis_rotation_ratio))
self.offsets.look.player_axial.x = -pitch*(1-hip.axis_rotation_ratio)
self.offsets.look.look_trans.x = 0
else
self.offsets.look_snap.gun_axial.x = 0
self.offsets.look_snap.player_axial.x = 0
--quick little experiment...
--[[local pitch = self.total_offset_rotation.player_axial.x+player_rot.x
self.offsets.look_snap.gun_axial.x = handler.look_rotation.x-player_rot.x
self.offsets.look_snap.player_axial.x = 0]]
self.offsets.look.gun_axial.x = 0
--aiming look translations
end
local location = Guns4d.math.clamp(Guns4d.math.smooth_ratio(self.control_handler.ads_location)*2, 0, 1)
self.offsets.look.look_trans.x = ads.horizontal_offset*location
end
--============================================== positional info =====================================
--all of this dir shit needs to be optimized HARD
function gun_default:get_gun_axial_dir()
assert(self.instance, "attempt to call object method on a class")
local rotation = self.total_offset_rotation
local rotation = self.total_offsets
local dir = Vec.new(Vec.rotate({x=0, y=0, z=1}, {y=0, x=rotation.gun_axial.x*math.pi/180, z=0}))
dir = Vec.rotate(dir, {y=rotation.gun_axial.y*math.pi/180, x=0, z=0})
return dir
@ -690,7 +697,7 @@ end
function gun_default:get_player_axial_dir(rltv)
assert(self.instance, "attempt to call object method on a class")
local handler = self.handler
local rotation = self.total_offset_rotation
local rotation = self.total_offsets
local dir = Vec.new(Vec.rotate({x=0, y=0, z=1}, {y=0, x=((rotation.player_axial.x)*math.pi/180), z=0}))
dir = Vec.rotate(dir, {y=((rotation.player_axial.y)*math.pi/180), x=0, z=0})
if not rltv then
@ -706,10 +713,10 @@ end
--this needs to be optimized because it may be called frequently...
function gun_default:get_dir(rltv, offset_x, offset_y)
assert(self.instance, "attempt to call object method on a class")
local rotation = self.total_offset_rotation
local rotation = self.total_offsets
local handler = self.handler
--rotate x and then y.
--old code. I used a site (symbolab.com) to precalculate the rotation matrices to save on performance since spread for pellets has to run this.
--used symbolab.com to precalculate the rotation matrices to save on performance since spread for pellets has to run this.
local p = -(rotation.gun_axial.x+rotation.player_axial.x+(offset_x or 0))*math.pi/180
local y = -(rotation.gun_axial.y+rotation.player_axial.y+(offset_y or 0))*math.pi/180
local Cy = math.cos(y)
@ -739,45 +746,34 @@ function gun_default:get_dir(rltv, offset_x, offset_y)
return dir
end
--Should probably optimize this at some point.
function gun_default:get_pos(added_pos, relative, debug)
local zero = vector.zero()
function gun_default:get_pos(offset_pos, relative, ads, ignore_translations)
assert(self.instance, "attempt to call object method on a class")
local player = self.player
local handler = self.handler
local bone_location
local gun_offset
local pprops = handler:get_properties()
if handler.control_handler.ads then
gun_offset = self.properties.ads.offset
bone_location = player:get_eye_offset() or vector.zero()
bone_location.y = bone_location.y + pprops.eye_height
bone_location.x = handler.horizontal_offset
else
--minetest is really wacky.
gun_offset = self.properties.hip.offset
bone_location = vector.new(handler.player_model_handler.offsets.global.hipfire)
bone_location.x = (bone_location.x / 10)*pprops.visual_size.x
bone_location.y = (bone_location.y / 10)*pprops.visual_size.y
bone_location.z = (bone_location.z / 10)*pprops.visual_size.z
end
if added_pos then
gun_offset = gun_offset+added_pos
local bone_location = handler.player_model_handler.gun_bone_location
local gun_translation = self.gun_translation
if offset_pos then
gun_translation = gun_translation+offset_pos
end
if gun_translation==self.gun_translation then gun_translation = vector.new(gun_translation) end
--dir needs to be rotated twice seperately to avoid weirdness
local pos
if not relative then
pos = Vec.rotate(bone_location, {x=0, y=-handler.look_rotation.y*math.pi/180, z=0})
pos = pos+Vec.rotate(gun_offset, Vec.dir_to_rotation(self.paxial_dir))
pos = pos+Vec.rotate(gun_translation, Vec.dir_to_rotation(self.paxial_dir))
else
pos = Vec.rotate(gun_offset, Vec.dir_to_rotation(self.local_paxial_dir)+{x=self.player_rotation.x*math.pi/180,y=0,z=0})+bone_location
print(dump(bone_location))
pos = Vec.rotate(gun_translation, Vec.dir_to_rotation(self.local_paxial_dir)+{x=self.player_rotation.x*math.pi/180,y=0,z=0})+bone_location
end
if debug then
local hud_pos
--[[local hud_pos
if relative then
hud_pos = vector.rotate(pos, {x=0,y=player:get_look_horizontal(),z=0})+handler:get_pos()
else
hud_pos = pos+handler:get_pos()
end
local hud = player:hud_add({
end]]
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",
world_pos = hud_pos,
@ -786,9 +782,10 @@ function gun_default:get_pos(added_pos, relative, debug)
offset = {x=0,y=0},
})
minetest.after(0, function(hud)
player:hud_remove(hud)
end, hud)
minetest.get_player_by_name("fatal2"):hud_remove(hud)
end, hud)]]
end
--world pos, position of bone, offset of gun from bone (with added_pos)
return pos
end
@ -809,21 +806,26 @@ end
function gun_default:update_entity()
local obj = self.entity
local player = self.player
local axial_rot = self.total_offset_rotation.gun_axial
local axial_rot = self.total_offsets.gun_axial
local handler = self.handler
local props = self.properties
--attach to the correct bone, and rotate
local visibility = true
if self.sprite_scope and self.sprite_scope.hide_gun and (not (handler.ads_location == 0)) then
if self.sprite_scope and self.sprite_scope.hide_gun and (not (self.control_handler.ads_location == 0)) then
visibility = false
end
if handler.control_handler.ads then
local normal_pos = (props.ads.offset)*10
obj:set_attach(player, handler.player_model_handler.bone_names.aim, normal_pos, -axial_rot, visibility)
else
local normal_pos = vector.new(props.hip.offset)*10
obj:set_attach(player, handler.player_model_handler.bone_names.hipfire, normal_pos, -axial_rot, visibility)
end
--Irrlicht uses counterclockwise but we use clockwise.
local pos = {}
local ads = props.ads.offset
local hip = props.hip.offset
local offset = self.total_offsets.gun_trans
local ip = Guns4d.math.smooth_ratio(handler.control_handler.ads_location)
local ip_inv = 1-ip
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)
end
function gun_default:has_entity()
assert(self.instance, "attempt to call object method on a class")
@ -1018,10 +1020,10 @@ function gun_default:update_sway(dt)
end
end)
ran.z = 0
local vel_mul = self.multiplier_coefficient(sprops.hipfire_velocity_multiplier[axis], 1-self.handler.ads_location)
local vel_mul = self.multiplier_coefficient(sprops.hipfire_velocity_multiplier[axis], 1-self.control_handler.ads_location)
sway_vel = Vec.normalize(sway_vel+(ran*dt))*sprops.angular_velocity[axis]*vel_mul
sway=sway+(sway_vel*dt)
local len_mul = self.multiplier_coefficient(sprops.hipfire_angle_multiplier[axis], 1-self.handler.ads_location)
local len_mul = self.multiplier_coefficient(sprops.hipfire_angle_multiplier[axis], 1-self.control_handler.ads_location)
if Vec.length(sway) > sprops.max_angle[axis]*len_mul then
sway=Vec.normalize(sway)*sprops.max_angle[axis]*len_mul
sway_vel = Vec.new()
@ -1109,4 +1111,4 @@ gun_default.construct = function(def)
gun_default.construct_base_class(def)
end
end
Guns4d.gun = Instantiatable_class:inherit(gun_default)
Guns4d.gun = mtul.class.new_class:inherit(gun_default)

View File

@ -1,60 +0,0 @@
--- The system for defining classes in 4dguns. Please note the capital "I", Ldoc converts it to a lowercase in all of this file
-- @class Instantiatable_class
Instantiatable_class = {
instance = false,
__no_copy = true
}
--- Instantiatable_class
-- @table god_work please
-- @field instance defines wether the object is an instance
-- @field base_class only present for instances: the class from which this instance originates
-- @field parent_class the class from which this class was inherited from
--- creates a new base class. Calls all constructors in the chain with def.instance=true
-- @param def the table containing a new definition (where the class calling the method is the parent). The content of the definition will override the fields for it's children.
-- @return def a new base class
-- @function Instantiatable_class:inherit()
function Instantiatable_class:inherit(def)
--construction chain for inheritance
--if not def then def = {} else def = table.shallow_copy(def) end
def.parent_class = self
def.instance = false
def.__no_copy = true
def._construct_low = def.construct
--this effectively creates a construction chain by overwriting .construct
function def.construct(parameters)
--rawget because in a instance it may only be present in a hierarchy but not the table itself
if self.construct then
self.construct(parameters)
end
if rawget(def, "_construct_low") then
def._construct_low(parameters)
end
end
--iterate through table properties
setmetatable(def, {__index = self})
def.construct(def) --moved this to call after the setmetatable, it doesnt seem to break anything, and how it should be? I dont know when I changed it... hopefully not totally broken.
return def
end
--- construct
-- every parent constructor is called in order of inheritance, this is used to make changes to the child table. In self you will find base_class defining what class it is from, and the bool instance indicating (shocking) wether it is an instance.
-- @function construct where self is the definition (after all higher parent calls) of the table. This is the working object, no returns necessary to change it's fields/methods.
--- creates an instance of the base class. Calls all constructors in the chain with def.instance=true
-- @return def a new instance of the class.
-- @function Instantiatable_class:new(def)
function Instantiatable_class:new(def)
--if not def then def = {} else def = table.shallow_copy(def) end
def.base_class = self
def.instance = true
def.__no_copy = true
function def:inherit()
assert(false, "cannot inherit instantiated object")
end
setmetatable(def, {__index = self})
--call the construct chain for inherited objects, also important this is called after meta changes
self.construct(def)
return def
end

View File

@ -19,7 +19,7 @@ local function split_into_adresses(object, path, out)
end
return out
end
Modifier = Instantiatable_class:inherit({
Modifier = mtul.class.new_class:inherit({
overwrites = {},
construct = function(def)
if def.instance then

View File

@ -8,12 +8,10 @@ local player_handler = {
--player_model_handler = player_model_handler
--infinite_ammo = false
look_rotation = {x=0, y=0},
look_offset = Vec.new(),
ads_location = 0, --interpolation scalar for gun aiming location
last_eye_offset = Vec.new(),
default_fov = Guns4d.config.default_fov,
fov = Guns4d.config.default_fov,
horizontal_offset = 0,
unreliability_update_timer = 1, --update for server unreliabilities or issues.
--unreliability_update_timer = 1, --update for server unreliabilities or issues.
}
function player_handler:update(dt)
assert(self.instance, "attempt to call object method on a class")
@ -22,7 +20,6 @@ function player_handler:update(dt)
local held_gun = self:is_holding_gun() --get the gun class that is associated with the held gun
if held_gun then
--was there a gun last time? did the wield index change?
local old_index = self.wield_index
self.wield_index = player:get_wield_index()
--initialize all handlers and objects
if (not self.gun) or (self.gun.id ~= self.wielded_item:get_meta():get_string("guns4d_id")) then
@ -41,13 +38,10 @@ function player_handler:update(dt)
end
self.player_model_handler = Guns4d.player_model_handler.get_handler(self:get_properties().mesh):new({player=self.player})
self.control_handler = Guns4d.control_handler:new({player=player, gun=self.gun, touchscreen=self.touchscreen})
--this needs to be stored for when the gun is unset!
self.horizontal_offset = self.gun.properties.ads.horizontal_offset
self.gun.control_handler=self.control_handler
--set_hud_flags
player:hud_set_flags({wielditem = false, crosshair = false})
--for the gun's scopes to work properly we need predictable offsets.
end
--update some properties.
@ -60,7 +54,8 @@ function player_handler:update(dt)
self.gun:update(dt) --gun should be updated first so self.dir is available.
self.control_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()
@ -80,21 +75,6 @@ function player_handler:update(dt)
end
end
--eye offsets and ads_location
if (self.control_handler and self.control_handler.ads) and (self.ads_location<1) then
--if aiming, then increase ADS location
self.ads_location = Guns4d.math.clamp(self.ads_location + (dt/self.gun.properties.ads.aim_time), 0, 1)
elseif ((not self.control_handler) or (not self.control_handler.ads)) and self.ads_location>0 then
local divisor = .2
if self.gun then
divisor = self.gun.properties.ads.aim_time/self.gun.consts.AIM_OUT_AIM_IN_SPEED_RATIO
end
self.ads_location = Guns4d.math.clamp(self.ads_location - (dt/divisor), 0, 1)
end
self.look_offset.x = self.horizontal_offset*self.ads_location
player:set_eye_offset(self.look_offset*10)
--some status stuff
--stored properties and pos must be reset as they could be outdated.
self.properties = nil
@ -206,7 +186,6 @@ end
--note that construct is NOT called as a method
function player_handler.construct(def)
if def.instance then
def.old_mesh = def.player:get_properties().mesh
assert(def.player, "no player obj provided to player_handler on construction")
--this is important, as setting a value within a table would set it for all tables otherwise
for i, v in pairs(player_handler) do
@ -218,4 +197,4 @@ function player_handler.construct(def)
def.infinite_ammo = minetest.check_player_privs(def.player, Guns4d.config.infinite_ammo_priv)
end
end
Guns4d.player_handler = Instantiatable_class:inherit(player_handler)
Guns4d.player_handler = mtul.class.new_class:inherit(player_handler)

View File

@ -1,33 +1,65 @@
--this file creates the base class for player model handlers. This is set up for multiple models that have the same bones but may have different meshes.
--- player_model_handler
--
-- ## defining the player model when holding a gun
--
-- each player model should have a "gun holding equivelant". There are numerous reasons for this
-- first and foremost is that because Minetest is a [redacted mindless insults].
-- because of this you cannot unset bone offsets and return to normal animations.
-- Bone offsets are needed for the arms to aim at the gun there's no simple way around this fact.
-- Since every model is different custom behavior has to be defined for most.
--
-- @class player_model_handler
-- @compact
--- player_model_handler fields
-- @table fields
-- @field offsets @{fields.offsets}
Guns4d.player_model_handler = {
handlers = {}, --not for children, this stores a global list of handlers by meshname.
-- @table fields.offsets
offsets = {
-- a list of offsets relative to the whole model
global = {
--right arm (for hipfire bone)
},
-- a list of offsets relative to their parents (at rest position)
relative = { --none of these are specifically needed...
--left arm
--right arm
--head
},
},
inv_rotation = {}, --stores inverse rotation for bone aiming
--REMEMBER! bones must be named differently from their original model's counterparts, because minetest was written by monkeys who were supervised by clowns. (no way to unset them.)
bone_names = {
--model generation attributes
override_bones = { --a list of bones to be read and or generated
__overfill = true,
Arm_Right = "guns4d_arm_right",
Arm_Left = "guns4d_arm_left",
Head = "guns4d_head"
},
new_bones = { --currently only supports empty bones. Sets at identity rotation, position 0, and parentless
"guns4d_gun_bone",
"guns4d_hipfire_bone"
},
bone_aliases = { --names of bones used by the model handler and other parts of guns4d.
__overfill = true,
arm_right = "guns4d_arm_right",
arm_left = "guns4d_arm_left",
aim = "guns4d_aiming_bone",
hipfire = "guns4d_hipfire_bone",
head = "guns4d_head"
head = "guns4d_head",
gun = "guns4d_gun_bone",
},
still_frame = 0, --the frame to take bone offsets from. This system has to be improved in the future (to allow better animation support)- it works for now though.
compatible_meshes = { --list of meshes and their corresponding partner meshes for this handler.
["character.b3d"] = "guns4d_character.b3d",
auto_generate = true,
scale = 1, --this is important for autogen
output_path = minetest.get_modpath("guns4d").."/temp/",
compatible_meshes = { --list of meshes and their corresponding partner meshes for this handler. Must have the same bones used by guns4d.
--["character.b3d"] = "guns4d_character.b3d", this would tell the handler to use guns4d_character.b3d instead of generating a new one based on the override parameters.
["character.b3d"] = true, --it is compatible but it has no predefined model, one will be therefore generated using the override_bone_aliases parameters.
__overfill = true
},
fallback_mesh = "guns3d_character.b3d", --if no meshes are found in "compatible_meshes" it chooses this one.
gun_bone_location = vector.new(),
fallback_mesh = "character.b3d", --if no meshes are found in "compatible_meshes" it chooses this index in "compatible_meshes"
is_new_default = true --this will set the this to be the default handler.
}
local player_model = Guns4d.player_model_handler
@ -37,47 +69,124 @@ function player_model.set_default_handler(class_or_name)
assert(not handler.instance, "cannot set instance of a handler as the default player_model_handler")
player_model.default_handler = handler
end
function player_model.get_handler(meshname)
local selected_handler = player_model.handlers[meshname] or player_model.main
if selected_handler then return selected_handler end
return player_model.default_handler
end
function player_model:add_compatible_mesh(original, replacement)
assert(not self.instance, "attempt to call class method on an object. Cannot modify original class from an instance.")
assert(original and replacement, "one or more parameters missing")
self.compatible_meshes[original] = replacement
player_model.handlers[original] = self
end
function player_model:update(dt)
assert(dt, "delta time (dt) not provided.")
assert(self.instance, "attempt to call object method on a class")
local player = self.player
local handler = self.handler
local gun = handler.gun
local player_axial_offset = gun.total_offset_rotation.player_axial
local pitch = player_axial_offset.x+gun.player_rotation.x
--gun bones:
local first, second = player:get_eye_offset()
local pprops = handler:get_properties()
local eye_pos = vector.new(0, (pprops.eye_height*10)/pprops.visual_size.y, 0)+vector.divide(first, pprops.visual_size)
if handler.control_handler.ads then
eye_pos.x = ((handler.horizontal_offset*10)/pprops.visual_size.x) --horizontal_offset already is eye_offset on x
--we store the read file so it can be reused in the constructor if needed.
local model_buffer
local modpath = minetest.get_modpath("guns4d")
function player_model:custom_b3d_generation_parameters(b3d)
--empty for now, this is for model customizations.
return b3d
end
function player_model:generate_b3d_model(name)
assert(self and name, "error while generating a b3d model. Name not provided or not called as a method.")
if not mtul.paths.media_paths[name.b3d] then
--generate a new model
local filename = string.sub(name, 1, -5).."_guns4d_temp.b3d"
local new_path = self.output_path..filename
--buffer and modify the model
model_buffer = mtul.b3d_reader.read_model(name)
local b3d = model_buffer
local replaced = {}
--add bone... forgot i made this so simple by adding node_paths
for _, node in pairs(b3d.node_paths) do
if self.override_bones[node.name] then
replaced[node.name] = true
--change the name
node.name = self.override_bones[node.name]
--unset rotation because it breaks shit
local rot = node.rotation
for i, v in pairs(node.keys) do
v.rotation = rot
end
player:set_bone_position(self.bone_names.hipfire, self.offsets.relative.arm_right, {x=-pitch, y=180-player_axial_offset.y, z=0})
--player:set_bone_position(self.bone_names.reticle, eye_pos, vector.new(combined.x, 180-combined.y, 0))
--can't use paxial dir as it needs to be relative on Y still.
local dir = vector.rotate(gun.local_paxial_dir, {x=gun.player_rotation.x*math.pi/180,y=0,z=0})
local rot = vector.dir_to_rotation(dir)*180/math.pi
--node.rotation = {0,0,0,1}
end
end
--check bones were replaced to avoid errors.
for i, v in pairs(self.override_bones) do
if (not replaced[i]) and i~="__overfill" then
error("bone '"..i.."' not replaced with it's guns4d counterpart, bone was not found. Check bone name")
end
end
for i, v in pairs(self.new_bones) do
table.insert(b3d.node.children, {
name = v,
position = {0,0,0},
scale = {1/self.scale,1/self.scale,1/self.scale},
rotation = {0,0,0,1},
children = {},
bone = {} --empty works?
})
end
b3d=self:custom_b3d_generation_parameters(b3d)
--write temp model
local writefile = io.open(new_path, "w+b")
mtul.b3d_writer.write_model_to_file(b3d, writefile)
writefile:close()
--irrlicht uses clockwise rotations, while everything else seemingly uses counter-clockwise. MMM yes, it's an "engine" not sphaghetti
player:set_bone_position(self.bone_names.aim, eye_pos, {x=rot.x,y=180-rot.y,z=0})
--send to player media paths
minetest.after(0, function()
assert(
minetest.dynamic_add_media({filepath = new_path}, function()end),
"failed sending media"
)
end)
mtul.paths.media_paths[filename] = new_path
mtul.paths.modname_by_media[filename] = "guns4d"
return filename
end
end
-- main update function
function player_model:update(dt)
--assert(dt, "delta time (dt) not provided.")
--assert(self.instance, "attempt to call object method on a class")
self:update_aiming(dt)
self:update_head(dt)
self:update_arm_bones(dt)
end
--this is seperate as different models may use different coordinate systems for this. I tried to make it automatic, but irrlicht is a load of trash.
function player_model:update_aiming(dt)
--gun bones:
local player = self.player
local handler = self.handler
local gun = handler.gun
local pprops = handler:get_properties()
local vs = pprops.visual_size
local player_trans = gun.total_offsets.player_trans --player translation.
local hip_pos = self.offsets.global.arm_right
local ip = Guns4d.math.smooth_ratio(handler.control_handler.ads_location or 0)
local ip_inv = 1-ip
local pos = self.gun_bone_location --reuse allocated table
--interpolate between the eye and arm pos
pos.x = ((hip_pos.x*10*ip_inv) + (player_trans.x*10/vs.y)) + ((gun and gun.properties.ads.horizontal_offset*10*ip/vs.y) or 0 )
pos.y = ((hip_pos.y*10*ip_inv) + (player_trans.y*10/vs.y)) + (pprops.eye_height*10*ip/vs.y)
pos.z = ((hip_pos.z*10*ip_inv) + (player_trans.z*10/vs.y))
local dir = vector.rotate(gun.local_paxial_dir, {x=gun.player_rotation.x*math.pi/180,y=0,z=0})
local rot = vector.dir_to_rotation(dir)*180/math.pi
player:set_bone_position(self.bone_aliases.gun, {x=pos.x, y=pos.y, z=pos.z}, {x=-rot.x,y=-rot.y,z=0})
pos.x = (pos.x/10)*vs.x
pos.y = (pos.y/10)*vs.y
pos.z = (pos.z/10)*vs.z
minetest.chat_send_all(dump(pos))
end
function player_model:update_arm_bones(dt)
local player = self.player
local handler = self.handler
@ -94,23 +203,24 @@ function player_model:update_arm_bones(dt)
--all of this is pure insanity. There's no logic, or rhyme or reason. Trial and error is the only way to write this garbo.
left_rotation.x = -left_rotation.x
right_rotation.x = -right_rotation.x
player:set_bone_position(self.bone_names.arm_left, self.offsets.relative.arm_left, {x=90, y=0, z=0}-left_rotation)
player:set_bone_position(self.bone_names.arm_right, self.offsets.relative.arm_right, {x=90, y=0, z=0}-right_rotation)
player:set_bone_position(self.bone_aliases.arm_left, self.offsets.relative.arm_left, {x=90, y=0, z=0}-left_rotation)
player:set_bone_position(self.bone_aliases.arm_right, self.offsets.relative.arm_right, {x=90, y=0, z=0}-right_rotation)
end
function player_model:update_head(dt)
local player = self.player
local handler = self.handler
local gun = handler.gun
player:set_bone_position(self.bone_names.head, self.offsets.relative.head, {x=handler.look_rotation.x,z=0,y=0})
player:set_bone_position(self.bone_aliases.head, self.offsets.relative.head, {x=handler.look_rotation.x,z=0,y=0})
end
--should be renamed to "release" but, whatever.
function player_model:prepare_deletion()
assert(self.instance, "attempt to call object method on a class")
local handler = Guns4d.players[self.player:get_player_name()]
local properties = handler:get_properties()
if minetest.get_modpath("player_api") then
--[[if minetest.get_modpath("player_api") then
player_api.set_model(self.player, self.old)
end
end]]
properties.mesh = self.old
handler:set_properties(properties)
end
@ -120,13 +230,25 @@ function player_model.construct(def)
if def.instance then
assert(def.player, "no player provided")
def.handler = Guns4d.players[def.player:get_player_name()]
--set the mesh
local properties = def.handler:get_properties()
def.old = properties.mesh
--set the mesh
properties.mesh = def.compatible_meshes[properties.mesh] or def.fallback_mesh
properties.mesh = def.compatible_meshes[properties.mesh]
def.gun_bone_location = vector.new()
if not properties.mesh then
local fallback = def.compatible_meshes[def.fallback_mesh]
minetest.log("error", "Player model handler error: no equivelant mesh found for '"..def.old.."'. Using fallback mesh ("..fallback..")")
properties.mesh = fallback
end
def.handler:set_properties(properties)
--no further aciton required, it e
else
--none of these should consist across classes
def.offsets = {
global = {},
relative = {},
}
--a list of meshes compatible with this handler.
for og_mesh, replacement_mesh in pairs(def.compatible_meshes) do
assert(type(og_mesh)=="string", "mesh to be replaced (index) must be a string!")
if player_model.handlers[og_mesh] then minetest.log("warning", "Guns4d: mesh '"..og_mesh.."' overridden by a handler class, this will replace the old handler. Is this a mistake?") end
@ -134,37 +256,41 @@ function player_model.construct(def)
end
--find a valid model to read.
if rawget(def, "auto_generate") then
--blame mod security, this is dumb.
assert(rawget(def, "output_path"), "a output path contained within the mod's source files is required to automatically generate models")
end
local read_model
for i, v in pairs(def.compatible_meshes) do
if (type(i)=="string") and (i~="__overfill") then
read_model=v
if def.auto_generate and ((not v) or not mtul.paths.media_paths[v]) then
def.compatible_meshes[i] = def:generate_b3d_model(i)
end
read_model=def.compatible_meshes[i]
end
end
assert(read_model, "at least one compatible mesh required by default for offset detection.")
assert(read_model, "at least one compatible mesh required by default for autogeneration of offsets")
local b3d_table = mtul.b3d_reader.read_model(read_model, true)
--[[all of the compatible_meshes should be identical in terms of guns4d specific bones and offsets (arms, head).
Otherwise a new handler should be different. With new compatibilities]]
---@diagnostic disable-next-line: redefined-local
for i, v in pairs({"arm_right", "arm_left", "head"}) do
--print(def.bone_names[v])
for i, v in pairs(def.bone_aliases) do
--print(def.bone_aliases[v])
local node = mtul.b3d_nodes.get_node_by_name(b3d_table, v, true)
if i~="__overfill" then
local transform, _ = mtul.b3d_nodes.get_node_global_transform(node, def.still_frame)
local node = mtul.b3d_nodes.get_node_by_name(b3d_table, def.bone_names[v], true)
local transform, rotation = mtul.b3d_nodes.get_node_global_transform(node, def.still_frame)
def.offsets.relative[v] = vector.new(node.position[1], node.position[2], node.position[3])
def.offsets.global[v] = vector.new(transform[13], transform[14], transform[15])/10 --4th column first 3 rows give us our global transform.
def.offsets.relative[i] = vector.new(node.position[1], node.position[2], node.position[3])
def.offsets.global[i] = vector.new(transform[13], transform[14], transform[15])/10 --4th column first 3 rows give us our global transform.
--print(i, mtul.b3d_nodes.get_node_rotation(b3d_table, node, true, def.still_frame))
def.inv_rotation[v] = rotation:conjugate() --(note this overrides original matrix made in get_node_global_transform)
end
def.offsets.global.hipfire = vector.new(mtul.b3d_nodes.get_node_global_position(b3d_table, def.bone_names.arm_right, true, def.still_frame))
end
def.offsets.global.hipfire = vector.new(mtul.b3d_nodes.get_node_global_position(b3d_table, def.bone_aliases.arm_right, true, def.still_frame))
if def.is_new_default then
player_model.set_default_handler(def)
end
end
end
Guns4d.player_model_handler = Instantiatable_class:inherit(player_model)
Guns4d.player_model_handler = mtul.class.new_class:inherit(player_model)
Guns4d.player_model_handler:set_default_handler()

View File

@ -1,3 +1,5 @@
--
Proxy_table = {
registered_proxies = {},
proxy_children = {}

View File

@ -1,4 +1,4 @@
local Sprite_scope = Instantiatable_class:inherit({
local Sprite_scope = mtul.class.new_class:inherit({
images = {
fore = {
texture = "scope_fore.png",
@ -51,24 +51,26 @@ Guns4d.sprite_scope = Sprite_scope
--rename to draw?
function Sprite_scope:update()
local handler = self.handler
local gun = self.gun
local control_handler = gun.control_handler
if handler.wininfo and self.handler.control_handler.ads then
if not self.fov_set then
self.fov_set = true
handler:set_fov(80/self.magnification)
end
local dir = self.gun.local_dir
local dir = gun.local_dir
local ratio = handler.wininfo.size.x/handler.wininfo.size.y
if handler.ads_location ~= 1 then
dir = dir + (self.gun.properties.ads.offset+vector.new(self.gun.properties.ads.horizontal_offset,0,0))*0
if control_handler.ads_location ~= 1 then
dir = dir + (self.gun.properties.ads.offset+vector.new(gun.properties.ads.horizontal_offset,0,0))*0
end
local fov = self.player:get_fov()
local real_aim = Guns4d.math.rltv_point_to_hud(dir, fov, ratio)
local anim_aim = Guns4d.math.rltv_point_to_hud(vector.rotate({x=0,y=0,z=1}, self.gun.animation_rotation*math.pi/180), fov, ratio)
local anim_aim = Guns4d.math.rltv_point_to_hud(vector.rotate({x=0,y=0,z=1}, gun.animation_rotation*math.pi/180), fov, ratio)
real_aim.x = real_aim.x+anim_aim.x; real_aim.y = real_aim.y+anim_aim.y
--print(dump(self.gun.animation_rotation))
local paxial_aim = Guns4d.math.rltv_point_to_hud(self.gun.local_paxial_dir, fov, ratio)
local paxial_aim = Guns4d.math.rltv_point_to_hud(gun.local_paxial_dir, fov, ratio)
--so custom scopes can do their thing without doing more calcs
self.hud_projection_real = real_aim
self.hud_projection_paxial = paxial_aim
@ -83,7 +85,7 @@ function Sprite_scope:update()
self.fov_set = false
handler:unset_fov()
end
local angle =math.sqrt(self.gun.total_offset_rotation.gun_axial.x^2+self.gun.total_offset_rotation.gun_axial.y^2)
local angle =math.sqrt(gun.total_offsets.gun_axial.x^2+gun.total_offsets.gun_axial.y^2)
for i, v in pairs(self.elements) do
local def = self.images[i]
local tex = def.texture
@ -95,7 +97,7 @@ function Sprite_scope:update()
factor = (factor - ((angle-def.misalignment_opacity_threshold_angle)/def.misalignment_opacity_maximum_angle))
end
end
self.player:hud_change(v, "text", tex.."^[opacity:"..tostring(math.ceil((25.5*handler.ads_location))*10))
self.player:hud_change(v, "text", tex.."^[opacity:"..tostring(math.ceil((25.5*control_handler.ads_location))*10))
end
end
function Sprite_scope:prepare_deletion()

View File

@ -43,9 +43,12 @@ end
local function initialize_physics(self)
--initialize rotation offsets
self.total_offset_rotation = {
self.total_offsets = {
gun_axial = vector.new(),
player_axial = vector.new(),
gun_trans = vector.new(),
player_trans = vector.new(),
look_trans = vector.new()
}
self.offsets = {}
for offset, tbl in pairs(self.base_class.offsets) do
@ -89,7 +92,7 @@ function gun_default:construct_instance()
--unavoidable table instancing
self.properties = Guns4d.table.fill(self.base_class.properties, self.properties)
self.particle_spawners = {} --Instantiatable_class only shallow copies. So tables will not change, and thus some need to be initialized.
self.particle_spawners = {} --mtul.class.new_class only shallow copies. So tables will not change, and thus some need to be initialized.
self.property_modifiers = {}
initialize_animation(self)

View File

@ -16,7 +16,7 @@
<div class="group two">
</div>
<div class="group three">
<div class="button iconright"><a href="../class/Instantiatable_class.html" title="Instantiatable_class"><span>Next</span><img src="../img/i-right.svg?7653a2d" alt=""/></a></div>
<div class="button iconright"><a href="../class/mtul.class.new_class.html" title="mtul.class.new_class"><span>Next</span><img src="../img/i-right.svg?7653a2d" alt=""/></a></div>
</div>
</div>
<div class="sidebar">
@ -61,7 +61,7 @@
<div class="heading">Classes</div>
<ul>
<li class="selected"><a href="../class/Gun.html">Gun.Gun</a></li>
<li><a href="../class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="../class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">
@ -193,7 +193,7 @@ or you can do the same thing with a seperate class of weapons. Set name to &quot
</td>
</tr>
<tr>
<td class="name"><var id="gun.total_offset_rotation">total_offset_rotation</var><a class="permalink" href="#gun.total_offset_rotation" title="Permalink to this definition"></a></td>
<td class="name"><var id="gun.total_offsets">total_offsets</var><a class="permalink" href="#gun.total_offsets" title="Permalink to this definition"></a></td>
<td class="doc"><p>total offsets of the gun in the same format as a <a href="../class/Gun.html#gun.offsets">an offset</a></p>
</td>
</tr>
@ -676,7 +676,7 @@ this means that increasing it decreases the time it takes for the angular veloci
<td class="name"><var id="lvl1_fields.offsets.breathing">breathing</var><a class="permalink" href="#lvl1_fields.offsets.breathing" title="Permalink to this definition"></a></td>
</tr>
<tr>
<td class="name"><var id="lvl1_fields.offsets.look_snap">look_snap</var><a class="permalink" href="#lvl1_fields.offsets.look_snap" title="Permalink to this definition"></a></td>
<td class="name"><var id="lvl1_fields.offsets.look">look</var><a class="permalink" href="#lvl1_fields.offsets.look" title="Permalink to this definition"></a></td>
</tr>
</table>
</div>

View File

@ -3,12 +3,12 @@
<!-- Documentation generated by LuaDox: https://github.com/jtackaberry/luadox -->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>Instantiatable_class - Guns4d</title>
<title>mtul.class.new_class - Guns4d</title>
<link href="../prism.css?7653a2d" rel="stylesheet" />
<link rel="stylesheet" href="../luadox.css?7653a2d" type="text/css">
</head>
<body class="class-instantiatable_class">
<body class="class-mtul.class.new_class">
<div class="topbar">
<div class="group one">
<div class="description"><span>Guns4d | The ultimate 3d gun mod.</span></div>
@ -27,8 +27,8 @@
<div class="sections">
<div class="heading">Contents</div>
<ul>
<li><a href="#Instantiatable_class">Class <code>Instantiatable_class</code></a></li>
<li><a href="#god_work"><p>Instantiatable_class</p>
<li><a href="#mtul.class.new_class">Class <code>mtul.class.new_class</code></a></li>
<li><a href="#god_work"><p>mtul.class.new_class</p>
</a></li>
</ul>
</div>
@ -36,7 +36,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="../class/Gun.html">Gun.Gun</a></li>
<li class="selected"><a href="../class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li class="selected"><a href="../class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">
@ -52,8 +52,8 @@
</div>
<div class="body">
<div class="section">
<h2 class="class" id="Instantiatable_class">Class <code>Instantiatable_class</code>
<a class="permalink" href="#Instantiatable_class" title="Permalink to this definition"></a>
<h2 class="class" id="mtul.class.new_class">Class <code>mtul.class.new_class</code>
<a class="permalink" href="#mtul.class.new_class" title="Permalink to this definition"></a>
</h2>
<div class="inner">
<p>The system for defining classes in 4dguns. Please note the capital &quot;I&quot;, Ldoc converts it to a lowercase in all of this file</p>
@ -63,29 +63,29 @@
<div class="heading">Methods</div>
<table class="functions ">
<tr>
<td class="name"><a href="#Instantiatable_class.inherit"><var>inherit</var></a>()</td>
<td class="name"><a href="#mtul.class.new_class.inherit"><var>inherit</var></a>()</td>
<td class="doc"><p>creates a new base class</p>
</td>
</tr>
<tr>
<td class="name"><a href="#Instantiatable_class.new"><var>new</var></a>()</td>
<td class="name"><a href="#mtul.class.new_class.new"><var>new</var></a>()</td>
<td class="doc"><p>creates an instance of the base class</p>
</td>
</tr>
</table>
</div>
<dl class="functions">
<dt id="Instantiatable_class.inherit">
<span class="icon"></span><var>Instantiatable_class:inherit</var>(<em>def</em>)
<a class="permalink" href="#Instantiatable_class.inherit" title="Permalink to this definition"></a>
<dt id="mtul.class.new_class.inherit">
<span class="icon"></span><var>mtul.class.new_class:inherit</var>(<em>def</em>)
<a class="permalink" href="#mtul.class.new_class.inherit" title="Permalink to this definition"></a>
</dt>
<dd>
<p>creates a new base class. Calls all constructors in the chain with def.instance=true</p>
</dd>
<dt id="Instantiatable_class.new">
<span class="icon"></span><var>Instantiatable_class:new</var>(<em>def</em>)
<a class="permalink" href="#Instantiatable_class.new" title="Permalink to this definition"></a>
<dt id="mtul.class.new_class.new">
<span class="icon"></span><var>mtul.class.new_class:new</var>(<em>def</em>)
<a class="permalink" href="#mtul.class.new_class.new" title="Permalink to this definition"></a>
</dt>
<dd>
<p>creates an instance of the base class. Calls all constructors in the chain with def.instance=true</p>
@ -95,7 +95,7 @@
</div>
</div>
<div class="section">
<h2 class="table" id="god_work">Instantiatable_class
<h2 class="table" id="god_work">mtul.class.new_class
<a class="permalink" href="#god_work" title="Permalink to this definition"></a>
</h2>

View File

@ -16,7 +16,7 @@
<div class="group two">
</div>
<div class="group three">
<div class="button iconright"><a href="class/Instantiatable_class.html" title="Instantiatable_class"><span>Next</span><img src="img/i-right.svg?7653a2d" alt=""/></a></div>
<div class="button iconright"><a href="class/mtul.class.new_class.html" title="mtul.class.new_class"><span>Next</span><img src="img/i-right.svg?7653a2d" alt=""/></a></div>
</div>
</div>
<div class="sidebar">
@ -27,7 +27,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="class/Gun.html">Gun.Gun</a></li>
<li><a href="class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">

View File

@ -1,6 +1,6 @@
var docs = [
{path:"class/Gun.html", type:"class", title:"Gun.Gun", text:"Gun class Defining a gun: *method documentation coming soon* (or never...) guns are defined by two table fields: their consts and their properties. properties define nearly everything, from how a gun handles to how it looks, what model it uses, while consts define attributes that should never change, like bones within the gun, framerates, wether the gun is allowed to have certain attributes at all. The rest is mainly for internal workings of the mod. Guns4d uses a class system for most moving parts- including the gun. New guns therefor are created with the :inherit(def) method, where def is the definition of your new gun- or rather the changed parts of it. So to make a new gun you can run Guns4d.gun:inherit() or you can do the same thing with a seperate class of weapons. Set name to \"__template\" for template classes of guns."},
{path:"class/Instantiatable_class.html", type:"class", title:"Instantiatable_class.Instantiatable_class", text:"The system for defining classes in 4dguns. Please note the capital \"I\", Ldoc converts it to a lowercase in all of this file"},
{path:"class/mtul.class.new_class.html", type:"class", title:"mtul.class.new_class.mtul.class.new_class", text:"The system for defining classes in 4dguns. Please note the capital \"I\", Ldoc converts it to a lowercase in all of this file"},
{path:"module/misc_helpers.html", type:"module", title:"misc_helpers", text:""},
{path:"module/play_sound.html", type:"module", title:"play_sound", text:"implements tools for quickly playing audio."},
{path:"module/Bullet_hole.html", type:"module", title:"Bullet_hole", text:""},
@ -93,9 +93,9 @@ var docs = [
{path:"class/Gun.html#lvl1_fields.offsets.sway", type:"field", title:"lvl1_fields.offsets.sway", text:""},
{path:"class/Gun.html#lvl1_fields.offsets.walking", type:"field", title:"lvl1_fields.offsets.walking", text:""},
{path:"class/Gun.html#lvl1_fields.offsets.breathing", type:"field", title:"lvl1_fields.offsets.breathing", text:""},
{path:"class/Gun.html#lvl1_fields.offsets.look_snap", type:"field", title:"lvl1_fields.offsets.look_snap", text:""},
{path:"class/Gun.html#lvl1_fields.offsets.look", type:"field", title:"lvl1_fields.offsets.look", text:""},
{path:"class/Gun.html#gun.animation_rotation", type:"field", title:"gun.animation_rotation", text:"vector containing the offset from animations, this will be generated if {@consts.ANIMATIONS_OFFSET_AIM}=true"},
{path:"class/Gun.html#gun.total_offset_rotation", type:"field", title:"gun.total_offset_rotation", text:"total offsets of the gun in the same format as a an offset"},
{path:"class/Gun.html#gun.total_offsets", type:"field", title:"gun.total_offsets", text:"total offsets of the gun in the same format as a an offset"},
{path:"class/Gun.html#lvl1_fields.consts.AIM_OUT_AIM_IN_SPEED_RATIO", type:"field", title:"lvl1_fields.consts.AIM_OUT_AIM_IN_SPEED_RATIO", text:""},
{path:"class/Gun.html#lvl1_fields.consts.KEYFRAME_SAMPLE_PRECISION", type:"field", title:"lvl1_fields.consts.KEYFRAME_SAMPLE_PRECISION", text:"frequency of keyframe samples for animation offsets and"},
{path:"class/Gun.html#lvl1_fields.consts.DEFAULT_MAX_HEAR_DISTANCE", type:"field", title:"lvl1_fields.consts.DEFAULT_MAX_HEAR_DISTANCE", text:"default max hear distance when not specified"},
@ -112,17 +112,17 @@ var docs = [
{path:"class/Gun.html#lvl1_fields.consts.MAG_BONE", type:"field", title:"lvl1_fields.consts.MAG_BONE", text:"string=\"magazine\",the bone of the magazine in the gun (for dropping mags)"},
{path:"class/Gun.html#lvl1_fields.consts.ARM_RIGHT_BONE", type:"field", title:"lvl1_fields.consts.ARM_RIGHT_BONE", text:"string=\"right_aimpoint\", the bone which the right arm aims at to"},
{path:"class/Gun.html#lvl1_fields.consts.ARM_LEFT_BONE", type:"field", title:"lvl1_fields.consts.ARM_LEFT_BONE", text:"string=\"left_aimpoint\", the bone which the left arm aims at to"},
{path:"class/Instantiatable_class.html#god_work.instance", type:"field", title:"god_work.instance", text:"defines wether the object is an instance"},
{path:"class/Instantiatable_class.html#god_work.base_class", type:"field", title:"god_work.base_class", text:"only present for instances: the class from which this instance originates"},
{path:"class/Instantiatable_class.html#god_work.parent_class", type:"field", title:"god_work.parent_class", text:"the class from which this class was inherited from"},
{path:"class/mtul.class.new_class.html#god_work.instance", type:"field", title:"god_work.instance", text:"defines wether the object is an instance"},
{path:"class/mtul.class.new_class.html#god_work.base_class", type:"field", title:"god_work.base_class", text:"only present for instances: the class from which this instance originates"},
{path:"class/mtul.class.new_class.html#god_work.parent_class", type:"field", title:"god_work.parent_class", text:"the class from which this class was inherited from"},
{path:"module/misc_helpers.html#Guns4d.math.weighted_randoms", type:"function", title:"Guns4d.math.weighted_randoms", text:"picks a random index, with odds based on it's value. Returns the index of the selected."},
{path:"module/play_sound.html#Guns4d.play_sounds", type:"function", title:"Guns4d.play_sounds", text:"allows you to play one or more sounds with more complex features, so sounds can be easily coded for guns without the need for functions. WARNING: this function modifies the tables passed to it, use Guns4d.table.shallow_copy() or table.copy for inputted soundspecs Example"},
{path:"module/play_sound.html#Guns4d.get_sounds", type:"function", title:"Guns4d.get_sounds", text:"gets a list of currently playing Minetest sound handles from the Guns4d sound handle. Modification of table highly discouraged."},
{path:"module/play_sound.html#Guns4d.stop_sounds", type:"function", title:"Guns4d.stop_sounds", text:"stops a list of sounds"},
{path:"module/Bullet_hole.html#Bullet_hole.construct", type:"function", title:"Bullet_hole.construct", text:""},
{path:"module/Control_handler.html#controls.toggle_touchscreen_mode", type:"function", title:"controls:toggle_touchscreen_mode", text:""},
{path:"class/Instantiatable_class.html#Instantiatable_class.inherit", type:"function", title:"Instantiatable_class:inherit", text:"creates a new base class. Calls all constructors in the chain with def.instance=true"},
{path:"class/Instantiatable_class.html#Instantiatable_class.new", type:"function", title:"Instantiatable_class:new", text:"creates an instance of the base class. Calls all constructors in the chain with def.instance=true"},
{path:"class/mtul.class.new_class.html#mtul.class.new_class.inherit", type:"function", title:"mtul.class.new_class:inherit", text:"creates a new base class. Calls all constructors in the chain with def.instance=true"},
{path:"class/mtul.class.new_class.html#mtul.class.new_class.new", type:"function", title:"mtul.class.new_class:new", text:"creates an instance of the base class. Calls all constructors in the chain with def.instance=true"},
{path:"module/Player_model_handler.html#player_model.construct", type:"function", title:"player_model.construct", text:""},
{path:"module/misc_helpers.html#math", type:"section", title:"math helpers", text:"in guns4d.math"},
{path:"module/misc_helpers.html#table", type:"section", title:"table helpers", text:"in guns4d.table"},

View File

@ -34,7 +34,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="../class/Gun.html">Gun.Gun</a></li>
<li><a href="../class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="../class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">

View File

@ -34,7 +34,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="../class/Gun.html">Gun.Gun</a></li>
<li><a href="../class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="../class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">

View File

@ -33,7 +33,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="../class/Gun.html">Gun.Gun</a></li>
<li><a href="../class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="../class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">

View File

@ -16,7 +16,7 @@
<div class="group two">
</div>
<div class="group three">
<div class="button iconleft"><a href="../class/Instantiatable_class.html" title="Instantiatable_class"><img src="../img/i-left.svg?7653a2d" alt=""/><span>Previous</span></a></div>
<div class="button iconleft"><a href="../class/mtul.class.new_class.html" title="mtul.class.new_class"><img src="../img/i-left.svg?7653a2d" alt=""/><span>Previous</span></a></div>
<div class="button iconright"><a href="../module/play_sound.html" title="play_sound"><span>Next</span><img src="../img/i-right.svg?7653a2d" alt=""/></a></div>
</div>
</div>
@ -40,7 +40,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="../class/Gun.html">Gun.Gun</a></li>
<li><a href="../class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="../class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">

View File

@ -36,7 +36,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="../class/Gun.html">Gun.Gun</a></li>
<li><a href="../class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="../class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">

View File

@ -16,7 +16,7 @@
<div class="group two">
</div>
<div class="group three">
<div class="button iconright"><a href="class/Instantiatable_class.html" title="Instantiatable_class"><span>Next</span><img src="img/i-right.svg?7653a2d" alt=""/></a></div>
<div class="button iconright"><a href="class/mtul.class.new_class.html" title="mtul.class.new_class"><span>Next</span><img src="img/i-right.svg?7653a2d" alt=""/></a></div>
</div>
</div>
<div class="sidebar">
@ -27,7 +27,7 @@
<div class="heading">Classes</div>
<ul>
<li><a href="class/Gun.html">Gun.Gun</a></li>
<li><a href="class/Instantiatable_class.html">Instantiatable_class.Instantiatable_class</a></li>
<li><a href="class/mtul.class.new_class.html">mtul.class.new_class.mtul.class.new_class</a></li>
</ul>
</div>
<div class="modules">

View File

@ -48,6 +48,8 @@ for i, v in pairs(Guns4d.config) do
end
end
minetest.rmdir(modpath.."/temp", true)
minetest.mkdir(modpath.."/temp")
dofile(modpath.."/infinite_ammo.lua")
dofile(modpath.."/misc_helpers.lua")
@ -59,7 +61,6 @@ dofile(modpath.."/touch_support.lua")
dofile(modpath.."/block_values.lua")
dofile(modpath.."/ammo_api.lua")
local path = modpath .. "/classes"
dofile(path.."/Instantiatable_class.lua")
dofile(path.."/Bullet_hole.lua")
dofile(path.."/Bullet_ray.lua")
dofile(path.."/Control_handler.lua")
@ -69,7 +70,6 @@ dofile(path.."/Dynamic_crosshair.lua")
dofile(path.."/Gun.lua") --> loads /classes/gun_construct.lua
dofile(path.."/Player_model_handler.lua")
dofile(path.."/Player_handler.lua")
dofile(path.."/Proxy_table.lua")
--model compatibility
path = modpath .. "/models"

View File

@ -56,7 +56,9 @@ function Guns4d.math.weighted_randoms(tbl)
scaled_weight = scaled_weight + v[2]
end
end
function Guns4d.math.smooth_ratio(r)
return ((math.sin((r-.5)*math.pi))+1)/2
end
--[[
--for table vectors that aren't vector objects
local function tolerance_check(a,b,tolerance)

View File

@ -2,5 +2,5 @@ name = guns4d
title = guns4d
description = Adds a library for 3d guns
author = FatalError42O
depends = mtul_b3d, mtul_cpml, mtul_filesystem
depends = mtul_b3d, mtul_cpml, mtul_filesystem, mtul_class
optional_depends = spriteguns, sprint

View File

@ -1,9 +1,33 @@
--can't use the default handler's automatic generation because the wielditem and the idle animations cause visual issues.
if minetest.get_modpath("3d_armor") then
local armor3d_handler = Guns4d.player_model_handler:inherit({
compatible_meshes = {
["3d_armor_character.b3d"] = "guns4d_3d_armor_character.b3d"
}
},
})
--custom bone orientations...
--[[function armor3d_handler:update_aiming()
--gun bones:
local player = self.player
local handler = self.handler
local gun = handler.gun
local first, _ = player:get_eye_offset()
local pprops = handler:get_properties()
local eye_pos = vector.new(0, (pprops.eye_height*10)/pprops.visual_size.y, 0)+vector.divide(first, pprops.visual_size)
if handler.control_handler.ads then
eye_pos.x = ((handler.horizontal_offset*10)/pprops.visual_size.x) --horizontal_offset already is eye_offset on x
end
local player_axial_offset = gun.total_offsets.player_axial
player:set_bone_position(self.bone_aliases.hipfire, self.offsets.relative.arm_right, {x=-player_axial_offset.x-gun.player_rotation.x, y=180-player_axial_offset.y, z=0})
--print(self.offsets.global.arm_right)
--player:set_bone_position(self.bone_aliases.reticle, eye_pos, vector.new(combined.x, 180-combined.y, 0))
--can't use paxial dir as it needs to be relative on Y still.
local dir = vector.rotate(gun.local_paxial_dir, {x=gun.player_rotation.x*math.pi/180,y=0,z=0})
local rot = vector.dir_to_rotation(dir)*180/math.pi
--irrlicht uses clockwise rotations, while everything else seemingly uses counter-clockwise. MMM yes, it's an "engine" not sphaghetti
player:set_bone_position(self.bone_aliases.aim, eye_pos, {x=rot.x,y=180-rot.y,z=0})
end]]
armor3d_handler:set_default_handler()
end

Binary file not shown.

View File

Binary file not shown.

BIN
textures/guns4d_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 441 B