698 lines
29 KiB
Lua

local Vec = vector
local gun_default = {
--itemstack = Itemstack
--gun_entity = ObjRef
name = "__guns4d:default__",
itemstring = "",
registered = {},
property_modifiers = {},
properties = {
hip = { --used by gun entity (attached offset)
offset = Vec.new(),
},
ads = { --used by player_handler, animation handler (eye bone offset from horizontal_offset), gun entity (attached offset)
offset = Vec.new(),
horizontal_offset = 0,
aim_time = 1,
},
recoil = { --used by update_recoil()
velocity_correction_factor = { --velocity correction factor is currently very broken.
gun_axial = 1,
player_axial = 1,
},
target_correction_factor = { --angular correction rate per second: time_since_fire*target_correction_factor
gun_axial = 1,
player_axial = 1,
},
angular_velocity_max = { --max velocity, so your gun doesnt "spin me right round baby round round"
gun_axial = 1,
player_axial = 1,
},
angular_velocity = {
gun_axial = {x=0, y=0},
player_axial = {x=0, y=0},
},
angular_velocity_bias = {
gun_axial = {x=1, y=0},
player_axial = {x=1, y=0},
},
target_correction_max_rate = { --the cap for time_since_fire*target_correction_factor
gun_axial = 1,
player_axial = 1,
},
},
sway = { --used by update_sway()
max_angle = {
gun_axial = 0,
player_axial = 0,
},
angular_velocity = {
gun_axial = 0,
player_axial = 0,
},
},
walking_offset = { --used by update_walking() (or something)
gun_axial = {x=1, y=-1},
player_axial = {x=1,y=1},
},
controls = { --used by control_handler
__overfill=true, --if present, this table will not be filled in.
aim = Guns4d.default_controls.aim,
--fire = Guns4d.default_controls.fire,
reload = Guns4d.default_controls.reload,
on_use = Guns4d.default_controls.on_use
},
reload = { --used by defualt controls. Still provides usefulness elsewhere.
__overfill=true, --if present, this table will not be filled in.
{type="unload", time=1, anim="unload", interupt="to_ground", hold = true},
{type="load", time=1, anim="load"}
},
ammo = { --used by ammo_handler
magazine_only = false,
accepted_bullets = {},
accepted_magazines = {}
},
animations = { --used by animations handler for idle, and default controls
empty = {x=0,y=0},
loaded = {x=1,y=1},
},
--used by ammo_handler
flash_offset = Vec.new(), --used by fire() (for fsx and ray start pos) [RENAME NEEDED]
firerateRPM = 600, --used by update() and by extent fire() + default controls
ammo_handler = Ammo_handler
},
offsets = {
player_rotation = Vec.new(),
--I'll need all three of them, do some precalculation.
total_offset_rotation = {
gun_axial = Vec.new(),
player_axial = Vec.new(),
},
recoil = {
gun_axial = Vec.new(),
player_axial = Vec.new(),
},
sway = {
gun_axial = Vec.new(),
player_axial = Vec.new(),
},
walking = {
gun_axial = Vec.new(),
player_axial = Vec.new(),
},
breathing = {
gun_axial = 1,
player_axial = 1,
}
},
velocities = {
recoil = {
gun_axial = Vec.new(),
player_axial = Vec.new(),
},
sway = {
gun_axial = Vec.new(),
player_axial = Vec.new(),
},
},
--magic number BEGONE
consts = {
HIP_PLAYER_GUN_ROT_RATIO = .75,
AIM_OUT_AIM_IN_SPEED_RATIO = 2.5,
HIPFIRE_BONE = "guns3d_hipfire_bone",
AIMING_BONE = "guns3d_aiming_bone",
HAS_RECOIL = true,
HAS_BREATHING = true,
HAS_SWAY = true,
HAS_WAG = true,
INFINITE_AMMO_IN_CREATIVE = true,
DEFAULT_FPS = 20,
LOOP_IDLE_ANIM = false
},
animation_data = { --where animations data is stored.
anim_runtime = 0,
length = 0,
fps = 0,
animations_frames = {0,0},
current_frame = 0,
},
particle_spawners = {},
walking_tick = 0,
time_since_last_fire = 0,
time_since_creation = 0,
rechamber_time = 0,
muzzle_flash = Guns4d.muzzle_flash
}
function gun_default:attempt_fire()
assert(self.instance, "attempt to call object method on a class")
if self.rechamber_time <= 0 then
local spent_bullet = self.ammo_handler:spend_round()
if spent_bullet then
local dir = self.dir
local pos = self:get_pos()
--[[print(dump(Guns4d.ammo.registered_bullets))
print(self.ammo_handler.next_bullet)
print(Guns4d.ammo.registered_bullets[self.ammo_handler.next_bullet])]]
local bullet_def = table.fill(Guns4d.ammo.registered_bullets[spent_bullet], {
player = self.player,
pos = pos,
dir = dir,
gun = self
})
Guns4d.bullet_ray:new(bullet_def)
self:recoil()
self:muzzle_flash()
self.rechamber_time = 60/self.properties.firerateRPM
end
end
end
function gun_default:recoil()
assert(self.instance, "attempt to call object method on a class")
for axis, recoil in pairs(self.velocities.recoil) do
for _, i in pairs({"x","y"}) do
recoil[i] = recoil[i] + (self.properties.recoil.angular_velocity[axis][i]*math.rand_sign((self.properties.recoil.angular_velocity_bias[axis][i]/2)+.5))
end
end
self.time_since_last_fire = 0
end
--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.offsets.total_offset_rotation
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
end
function gun_default:get_player_axial_dir(rltv)
assert(self.instance, "attempt to call object method on a class")
local player = self.player
local rotation = self.offsets.total_offset_rotation
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
dir = Vec.rotate(dir, {x=self.offsets.player_rotation.x*(math.pi/180),y=0,z=0})
end
--[[local hud_pos = Vec.rotate(dir, {x=0,y=self.offsets.player_rotation.y*math.pi/180,z=0})+player:get_pos()+{x=0,y=player:get_properties().eye_height,z=0}+vector.rotate(player:get_eye_offset()/10, {x=0,y=self.offsets.player_rotation.y*math.pi/180,z=0})
local hud = player:hud_add({
hud_elem_type = "image_waypoint",
text = "muzzle_flash2.png",
world_pos = hud_pos,
scale = {x=10, y=10},
alignment = {x=0,y=0},
offset = {x=0,y=0},
})
minetest.after(0, function(hud)
player:hud_remove(hud)
end, hud)]]
return dir
end
function gun_default:get_dir(rltv)
assert(self.instance, "attempt to call object method on a class")
local rotation = self.offsets.total_offset_rotation
local dir = Vec.new(Vec.rotate({x=0, y=0, z=1}, {y=0, x=((rotation.gun_axial.x+rotation.player_axial.x)*math.pi/180), z=0}))
dir = Vec.rotate(dir, {y=((rotation.gun_axial.y+rotation.player_axial.y)*math.pi/180), x=0, z=0})
--for it to be relative the the camera, rotation by player look occours post.
if not rltv then
dir = Vec.rotate(dir, {x=self.offsets.player_rotation.x*math.pi/180,y=self.offsets.player_rotation.y*math.pi/180,z=0})
end
--local hud_pos = dir+player:get_pos()+{x=0,y=player:get_properties().eye_height,z=0}+vector.rotate(player:get_eye_offset()/10, {x=0,y=player_rotation.y*math.pi/180,z=0})
--[[local hud = player:hud_add({
hud_elem_type = "image_waypoint",
text = "muzzle_flash2.png",
world_pos = hud_pos,
scale = {x=10, y=10},
alignment = {x=0,y=0},
offset = {x=0,y=0},
})
minetest.after(0, function(hud)
player:hud_remove(hud)
end, hud)]]
return dir
end
function gun_default:get_pos(added_pos)
assert(self.instance, "attempt to call object method on a class")
added_pos = Vec.new(added_pos)
local player = self.player
local handler = self.handler
local bone_location = Vec.new(handler.model_handler.offsets.arm.right)/10
local gun_offset = Vec.new(self.properties.hip.offset)
local player_rotation = Vec.new(self.offsets.player_rotation.x, self.offsets.player_rotation.y, 0)
if handler.control_bools.ads then
gun_offset = self.properties.ads.offset
bone_location = Vec.new(0, handler:get_properties().eye_height, 0)+player:get_eye_offset()/10
else
--minetest is really wacky.
bone_location.x = -bone_location.x
player_rotation.x = self.offsets.player_rotation.x*self.consts.HIP_PLAYER_GUN_ROT_RATIO
end
gun_offset = gun_offset+added_pos
--dir needs to be rotated twice seperately to avoid weirdness
local rotation = self.offsets.total_offset_rotation
local bone_pos = Vec.rotate(bone_location, {x=0, y=player_rotation.y*math.pi/180, z=0})
local gun_offset = Vec.rotate(Vec.rotate(gun_offset, {x=(rotation.player_axial.x+player_rotation.x)*math.pi/180,y=0,z=0}), {x=0,y=(rotation.player_axial.y+player_rotation.y)*math.pi/180,z=0})
--[[local hud_pos = bone_pos+gun_offset+handler:get_pos()
if not false then
local hud = player:hud_add({
hud_elem_type = "image_waypoint",
text = "muzzle_flash2.png",
world_pos = hud_pos,
scale = {x=10, y=10},
alignment = {x=0,y=0},
offset = {x=0,y=0},
})
minetest.after(0, function(hud)
player:hud_remove(hud)
end, hud)
end]]
--world pos, position of bone, offset of gun from bone (with added_pos)
return bone_pos+gun_offset+handler:get_pos(), bone_pos, gun_offset
end
function gun_default:add_entity()
assert(self.instance, "attempt to call object method on a class")
self.entity = minetest.add_entity(self.player:get_pos(), self.name.."_visual")
local obj = self.entity:get_luaentity()
obj.parent_player = self.player
obj:on_step()
end
function gun_default:has_entity()
assert(self.instance, "attempt to call object method on a class")
if not self.entity then return false end
if not self.entity:get_pos() then return false end
return true
end
--update the gun, da meat and da potatoes
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() end
self.pos = self:get_pos()
local handler = self.handler
local look_rotation = {x=handler.look_rotation.x,y=handler.look_rotation.y}
local total_rot = self.offsets.total_offset_rotation
local player_rot = self.offsets.player_rotation
local constant = 6
--player look rotation. I'm going to keep it real, I don't remember what this equation does.
if not self.sprite_scope then
local next_vert_aim = ((player_rot.x+look_rotation.x)/(1+constant*dt))-look_rotation.x
if math.abs(look_rotation.x-next_vert_aim) > .005 then
player_rot.x = next_vert_aim
else
player_rot.x = -look_rotation.x
end
else
player_rot.x = -look_rotation.x
end
--timers
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
--update some vectors
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
if self.consts.HAS_WAG then self:update_wag(dt) end
self.dir = self:get_dir()
self.local_dir = self:get_dir(true)
self.paxial_dir = self:get_player_axial_dir()
self.local_paxial_dir = self:get_player_axial_dir(true)
--sprite scope
if self.properties.sprite_scope then
self.sprite_scope:update()
end
player_rot.y = -handler.look_rotation.y
local offsets = self.offsets
total_rot.player_axial = offsets.recoil.player_axial + offsets.walking.player_axial + offsets.sway.player_axial + {x=offsets.breathing.player_axial,y=0,z=0}
total_rot.gun_axial = offsets.recoil.gun_axial + offsets.walking.gun_axial + offsets.sway.gun_axial
end
function gun_default:update_wag(dt)
local handler = self.handler
if handler.walking then
self.walking_tick = self.walking_tick + (dt*Vec.length(self.player:get_velocity()))
else
self.walking_tick = 0
end
local walking_offset = self.offsets.walking
for _, i in pairs({"x","y"}) do
for axis, _ in pairs(walking_offset) do
if handler.walking then
local time = self.walking_tick
local multiplier = 1
if i == "x" then
multiplier = 2
end
walking_offset[axis][i] = math.sin((time/1.6)*math.pi*multiplier)*self.properties.walking_offset[axis][i]
else
local old_value = walking_offset[axis][i]
if (math.abs(walking_offset[axis][i]) > .5 and axis=="player_axial") or (math.abs(walking_offset[axis][i]) > .6 and axis=="gun_axial") then
local multiplier = (walking_offset[axis][i]/math.abs(walking_offset[axis][i]))
walking_offset[axis][i] = walking_offset[axis][i]-(dt*2*multiplier)
elseif axis == "gun_axial" then
walking_offset[axis][i] = 0
end
if math.abs(walking_offset[axis][i]) > math.abs(old_value) then
walking_offset[axis][i] = 0
end
end
end
end
end
function gun_default:update_recoil(dt)
for axis, _ in pairs(self.offsets.recoil) do
for _, i in pairs({"x","y"}) do
local recoil = self.offsets.recoil[axis][i]
local recoil_vel = 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
if math.abs(recoil_vel) > 0.01 then
--look, I know this doesn't really make sense, but this is the best I can do atm. I've looked for better and mroe intuitive methods, I cannot find them.
--8-(8*(1-(8/100))
--recoil_vel = recoil_vel-((recoil_vel-(recoil_vel/(1+self.properties.recoil.velocity_correction_factor[axis])))*dt*10)
recoil_vel = recoil_vel * (recoil_vel/(recoil_vel/(self.properties.recoil.velocity_correction_factor[axis]*2))*dt)
else
recoil_vel = 0
end
if math.abs(recoil_vel)>math.abs(old_recoil_vel) then
recoil_vel = 0
end
--ax^2+bx+c
--recoil_velocity_correction_rate
--recoil_correction_rate
local old_recoil = recoil
if math.abs(recoil) > 0.001 then
local correction_multiplier = self.time_since_last_fire*self.properties.recoil.target_correction_factor[axis]
local correction_value = recoil*correction_multiplier
correction_value = math.clamp(math.abs(correction_value), 0, self.properties.recoil.target_correction_max_rate[axis])
recoil=recoil-(correction_value*dt*(math.abs(recoil)/recoil))
--prevent overcorrection
if math.abs(recoil) > math.abs(old_recoil) then
recoil = 0
end
end
self.velocities.recoil[axis][i] = recoil_vel
self.offsets.recoil[axis][i] = recoil
end
end
end
local animation_data = {
anim_runtime = 0,
length = 0,
fps = 0,
animations_frames = {0,0},
current_frame = 0,
}
function gun_default:animation_update(dt)
local ent = self.entity
local data = self.animation_data
local anim_range, frame_speed, frame_blend, frame_loop = ent:get_animation()
if (not data.animation_frames) or (anim_range.x ~= data.x) or (anim_range.y ~= data.y) then
data.runtime = 0
data.animations_frames = false
elseif data.animation_frames then
data.runtime = data.runtime + dt
data.current_frame = math.clamp(data.runtime*data.fps, data.animation_frames.x, data.animation_frames.y)
end
end
function gun_default:set_animation(frames, length, fps, loop)
loop = loop or false --why the fuck default is loop? I DONT FUCKIN KNOW
assert(type(frames)=="table" and frames.x and frames.y, "frames invalid or nil in set_animation()!")
assert(length or fps, "need either length or FPS for animation")
assert(not (length and fps), "cannot play animation with both specified length and specified fps. Only one parameter can be used.")
local num_frames = math.abs(frames.x-frames.y)
local data = self.animation_data
if length then
fps = num_frames/length
elseif fps then
length = num_frames/fps
else
fps = self.consts.DEFAULT_FPS
length = num_frames/self.consts.DEFAULT_FPS
end
data.runtime = 0
data.length = length
self.entity:set_animation(frames, fps, 0, loop)
end
function gun_default:clear_animation()
local loaded = false
for i, v in pairs(self.ammo_handler) do
print(i,v )
end
if self.properties.ammo.magazine_only then
if self.ammo_handler.ammo.loaded_mag ~= "empty" then
loaded = true
end
elseif self.ammo_handler.ammo.total_bullets > 0 then
loaded = true
end
if loaded then
self.entity:set_animation({x=self.properties.animations.loaded.x, y=self.properties.animations.loaded.y}, 0, 0, self.consts.LOOP_IDLE_ANIM)
else
self.entity:set_animation({x=self.properties.animations.empty.x, y=self.properties.animations.empty.y}, 0, 0, self.consts.LOOP_IDLE_ANIM)
end
local data = self.animation_data
data.runtime = 0
data.length = false
data.animations_frames = false
end
function gun_default:update_breathing(dt)
local breathing_info = {pause=1.4, rate=4.2}
--we want X to be between 0 and 4.2. Since math.pi is a positive crest, we want X to be above it before it reaches our-
--"length" (aka rate-pause), thus it will pi/length or pi/(rate-pause) will represent out slope of our control.
local x = (self.time_since_creation%breathing_info.rate)*math.pi/(breathing_info.rate-breathing_info.pause)
local scale = 1
--now if it's above math.pi we know it's in the pause half of the cycle. For smoothness, we cut the sine off early and decay the value linearly.
if x > math.pi*(8/9) then
self.offsets.breathing.player_axial=self.offsets.breathing.player_axial-(self.offsets.breathing.player_axial*2*dt)
else
self.offsets.breathing.player_axial = scale*(math.sin(x))
end
end
function gun_default:update_sway(dt)
for axis, sway in pairs(self.offsets.sway) do
local sway_vel = self.velocities.sway[axis]
local ran
ran = Vec.apply(Vec.new(), function(i,v)
if i ~= "x" then
return (math.random()-.5)*2
end
end)
ran.z = 0
sway_vel = Vec.normalize(sway_vel+(ran*dt))*self.properties.sway.angular_velocity[axis]
sway=sway+(sway_vel*dt)
if Vec.length(sway) > self.properties.sway.max_angle[axis] then
sway=Vec.normalize(sway)*self.properties.sway.max_angle[axis]
sway_vel = Vec.new()
end
self.offsets.sway[axis] = sway
self.velocities.sway[axis] = sway_vel
end
end
function gun_default:prepare_deletion()
assert(self.instance, "attempt to call object method on a class")
if self:has_entity() then self.entity:remove() end
if self.sprite_scope then self.sprite_scope:prepare_deletion() end
end
--construction for the base gun class
local valid_ctrls = {
up=true,
down=true,
left=true,
right=true,
jump=true,
aux1=true,
sneak=true,
dig=true,
place=true,
LMB=true,
RMB=true,
zoom=true,
}
gun_default.construct = function(def)
if def.instance then
--make some quick checks.
assert(def.handler, "no player handler object provided")
--initialize some variables
def.player = def.handler.player
local meta = def.itemstack:get_meta()
def.meta = meta
local out = {}
--create ID so we can track switches between weapons
if meta:get_string("guns4d_id") == "" then
local id = tostring(Unique_id.generate())
meta:set_string("guns4d_id", id)
def.player:set_wielded_item(def.itemstack)
def.id = id
else
def.id = meta:get_string("guns4d_id")
end
--unavoidable table instancing
def.properties = table.fill(def.base_class.properties, def.properties)
def.particle_spawners = {} --Instantiatable_class only shallow copies. So tables will not change, and thus some need to be initialized.
def.property_modifiers = {}
--initialize all offsets
--def.offsets = table.deep_copy(def.base_class.offsets)
def.offsets = {}
for i, tbl in pairs(def.base_class.offsets) do
if (tbl.gun_axial and tbl.player_axial) then
local ty = type(tbl.gun_axial)
if (ty=="table") and tbl.gun_axial.x and tbl.gun_axial.y and tbl.gun_axial.z then
def.offsets[i] = {}
def.offsets[i].gun_axial = Vec.new()
def.offsets[i].player_axial = Vec.new()
else
def.offsets[i] = {}
def.offsets[i] = table.deep_copy(def.offsets[i])
end
elseif tbl.x and tbl.y and tbl.z then
def.offsets[i] = Vec.new()
end
end
--def.velocities = table.deep_copy(def.base_class.velocities)
def.velocities = {}
for i, tbl in pairs(def.base_class.velocities) do
def.velocities[i] = {}
if tbl.gun_axial and tbl.player_axial then
def.velocities[i].gun_axial = Vec.new()
def.velocities[i].player_axial = Vec.new()
end
end
--properties have been assigned, create necessary objects
if def.properties.sprite_scope then
if not def.sprite_scope then
def.sprite_scope = def.properties.sprite_scope:new({
gun = def
})
end
end
if def.properties.entity_scope then
if not def.entity_scope then
end
end
def.ammo_handler = def.properties.ammo_handler:new({
gun = def
})
elseif def.name ~= "__guns4d:default__" then
local props = def.properties
--validate controls, done before properties are filled to avoid duplication.
if props.controls then
for i, control in pairs(props.controls) do
if not (i=="on_use") and not (i=="on_secondary_use") then
assert(control.conditions, "no conditions provided for control")
for _, condition in pairs(control.conditions) do
if not valid_ctrls[condition] then
assert(false, "invalid key: '"..condition.."'")
end
end
end
end
end
--fill in the properties.
def.properties = table.fill(def.parent_class.properties, props or {})
print(table.tostring(def.properties))
def.consts = table.fill(def.parent_class.consts, def.consts or {})
props = def.properties --have to reinitialize this as the reference is replaced.
if def.name ~= "__template" then
assert(rawget(def, "name"), "no name provided in new class")
assert(rawget(def, "itemstring"), "no itemstring provided in new class")
assert(not((props.ammo.capacity) and (not props.ammo.magazine_only)), "gun does not accept magazines, but has no set capcity! Please define ammo.capacity")
assert(minetest.registered_items[def.itemstring], def.itemstring.." : item is not registered, check dependencies.")
--override methods so control handler can do it's job
local old_on_use = minetest.registered_items[def.itemstring].on_use
local old_on_s_use = minetest.registered_items[def.itemstring].on_secondary_use
--override the item to hook in controls. (on_drop needed)
minetest.override_item(def.itemstring, {
on_use = function(itemstack, user, pointed_thing)
if old_on_use then
old_on_use(itemstack, user, pointed_thing)
end
Guns4d.players[user:get_player_name()].handler.control_handler:on_use(itemstack, pointed_thing)
end,
on_secondary_use = function(itemstack, user, pointed_thing)
if old_on_s_use then
old_on_s_use(itemstack, user, pointed_thing)
end
Guns4d.players[user:get_player_name()].handler.control_handler:on_secondary_use(itemstack, pointed_thing)
end
})
end
def.accepted_bullets = {}
for _, v in pairs(def.properties.ammo.accepted_bullets) do
def.accepted_bullets[v] = true
end
def.accepted_magazines = {}
for _, v in pairs(def.properties.ammo.accepted_magazines) do
def.accepted_magazines[v] = true
end
--add gun def to the registered table
Guns4d.gun.registered[def.name] = def
--register the visual entity
minetest.register_entity(def.name.."_visual", {
initial_properties = {
visual = "mesh",
mesh = props.mesh,
textures = props.textures,
glow = 0,
pointable = false,
static_save = false,
},
on_step = function(self, dtime)
local name = string.gsub(self.name, "_visual", "")
local obj = self.object
if not self.parent_player then obj:remove() return end
local player = self.parent_player
local handler = Guns4d.players[player:get_player_name()].handler
local lua_object = handler.gun
if not lua_object then obj:remove() return end
--this is changing the point of rotation if not aiming, this is to make it look less shit.
local axial_modifier = Vec.new()
if not handler.control_bools.ads then
local pitch = lua_object.offsets.total_offset_rotation.player_axial.x+lua_object.offsets.player_rotation.x
axial_modifier = Vec.new(pitch*(1-lua_object.consts.HIP_PLAYER_GUN_ROT_RATIO),0,0)
end
local axial_rot = lua_object.offsets.total_offset_rotation.gun_axial+axial_modifier
--attach to the correct bone, and rotate
local visibility = true
if lua_object.sprite_scope and lua_object.sprite_scope.hide_gun and (not (handler.ads_location == 0)) then
visibility = false
end
if handler.control_bools.ads then
local normal_pos = (props.ads.offset)*10
obj:set_attach(player, lua_object.consts.AIMING_BONE, normal_pos, -axial_rot, visibility)
else
local normal_pos = Vec.new(props.hip.offset)*10
-- Vec.multiply({x=normal_pos.x, y=normal_pos.z, z=-normal_pos.y}, 10)
obj:set_attach(player, lua_object.consts.HIPFIRE_BONE, normal_pos, -axial_rot, visibility)
end
end
})
end
end
Guns4d.gun = Instantiatable_class:inherit(gun_default)