been awhile since I pushed, not sure what I added
This commit is contained in:
parent
6ec442ee31
commit
e80a4a4242
@ -43,10 +43,13 @@ function Ammo_handler:spend_round()
|
||||
local meta = self.gun.meta
|
||||
--subtract the bullet
|
||||
if self.ammo.total_bullets > 0 then
|
||||
self.ammo.loaded_bullets[bullet_spent] = self.ammo.loaded_bullets[bullet_spent]-1
|
||||
if self.ammo.loaded_bullets[bullet_spent] == 0 then self.ammo.loaded_bullets[bullet_spent] = nil end
|
||||
self.ammo.total_bullets = self.ammo.total_bullets - 1
|
||||
--set the new current bullet
|
||||
--only actually subtract the round if INFINITE_AMMO_IN_CREATIVE isnt true or they arent creative.
|
||||
if not (self.gun.consts.INFINITE_AMMO_IN_CREATIVE and minetest.check_player_privs(self.gun.player, "creative")) then
|
||||
self.ammo.loaded_bullets[bullet_spent] = self.ammo.loaded_bullets[bullet_spent]-1
|
||||
if self.ammo.loaded_bullets[bullet_spent] == 0 then self.ammo.loaded_bullets[bullet_spent] = nil end
|
||||
self.ammo.total_bullets = self.ammo.total_bullets - 1
|
||||
end
|
||||
--set the new current bullet
|
||||
if next(self.ammo.loaded_bullets) then
|
||||
self.ammo.next_bullet = math.weighted_randoms(self.ammo.loaded_bullets)
|
||||
meta:set_string("guns4d_next_bullet", self.ammo.next_bullet)
|
||||
@ -66,7 +69,7 @@ function Ammo_handler:load_magazine()
|
||||
local highest_ammo = -1
|
||||
local gun = self.gun
|
||||
local gun_accepts = gun.accepted_magazines
|
||||
if self.ammo.loaded_mag ~= "empty" then
|
||||
if self.ammo.loaded_mag ~= "empty" or self.ammo.total_bullets > 0 then
|
||||
--it's undefined, make assumptions.
|
||||
self:unload_all()
|
||||
end
|
||||
|
@ -209,8 +209,8 @@ function ray:apply_damage(object, sharp_pen, blunt_pen)
|
||||
local sharp_dmg = self.raw_sharp_damage*sharp_ratio
|
||||
|
||||
local hp = (object:get_hp()-blunt_dmg)-sharp_dmg
|
||||
print(blunt_dmg, sharp_dmg, blunt_ratio, sharp_ratio)
|
||||
print(self.blunt_penetration, self.sharp_penetration)
|
||||
--print(blunt_dmg, sharp_dmg, blunt_ratio, sharp_ratio)
|
||||
--print(self.blunt_penetration, self.sharp_penetration)
|
||||
if hp < 0 then hp = 0 end
|
||||
object:set_hp(hp, {type="set_hp", from="guns4d"})
|
||||
|
||||
|
105
classes/Dynamic_crosshair.lua
Normal file
105
classes/Dynamic_crosshair.lua
Normal file
@ -0,0 +1,105 @@
|
||||
local Dynamic_crosshair = Instantiatable_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",
|
||||
scale = 3,
|
||||
normalize_walking = true,
|
||||
normalize_breathing = true,
|
||||
normalize_sway = true,
|
||||
old_walking_vec = vector.new(),
|
||||
construct = function(def)
|
||||
if def.instance then
|
||||
assert(def.gun, "no gun instance provided")
|
||||
def.player = def.gun.player
|
||||
def.handler = def.gun.handler
|
||||
def.width = def.frames/def.increments
|
||||
def.hud = def.player:hud_add{
|
||||
hud_elem_type = "image",
|
||||
position = {x=.5,y=.5},
|
||||
scale = {x=def.scale,y=def.scale},
|
||||
text = def.image.."^[verticalframe:"..def.frames..":0",
|
||||
}
|
||||
end
|
||||
end
|
||||
})
|
||||
Guns4d.dynamic_crosshair = Dynamic_crosshair
|
||||
local function absolute_vector(v)
|
||||
return {x=math.abs(v.x), y=math.abs(v.y), z=math.abs(v.z)}
|
||||
end
|
||||
--really wish there was a better way to do this.
|
||||
local function render_length(rotation, fov)
|
||||
local dir = vector.rotate({x=0,y=0,z=1}, {x=rotation.x*math.pi/180,y=0,z=0})
|
||||
vector.rotate(dir,{x=0,y=rotation.y*math.pi/180,z=0})
|
||||
local out = Point_to_hud(dir, fov, 1)
|
||||
return math.sqrt(out.x^2+out.y^2)
|
||||
end
|
||||
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_bools.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) then
|
||||
print(offset)
|
||||
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)
|
||||
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)
|
||||
if gun.consts.HAS_BREATHING and self.normalize_breathing then
|
||||
temp_vector = temp_vector + {x=gun.properties.breathing_scale, y=0, z=0}
|
||||
end
|
||||
--stop wag from looking wierd so the offset only expands and doesnt do a weird pulsing thing. Inefficient, hopefully not a terrible deal, taking the advice not to prematurely optimize.
|
||||
local walking_vec = gun.offsets.walking.gun_axial + gun.offsets.walking.player_axial
|
||||
if gun.consts.HAS_WAG and self.normalize_walking then
|
||||
if handler.walking then --"velocity" is used to track velocity of the player for after movement effects. When they are no longer needed it is expunged, also indicating to the function its over
|
||||
--only accept higher values for animation
|
||||
if render_length(walking_vec, fov) > render_length(self.old_walking_vec, fov) then
|
||||
self.old_walking_vec = vector.copy(walking_vec)
|
||||
temp_vector = temp_vector + absolute_vector(walking_vec)
|
||||
else
|
||||
temp_vector = temp_vector + absolute_vector(self.old_walking_vec)
|
||||
end
|
||||
else
|
||||
--only accept lower values for animation
|
||||
if render_length(walking_vec, fov) < render_length(self.old_walking_vec, fov) then
|
||||
self.old_walking_vec = walking_vec
|
||||
temp_vector = temp_vector + absolute_vector(walking_vec)
|
||||
else
|
||||
temp_vector = temp_vector + absolute_vector(self.old_walking_vec)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--create a new dir using our parameters.
|
||||
local dir = vector.rotate({x=0,y=0,z=1}, {x=temp_vector.x*math.pi/180, y=0, z=0})
|
||||
dir = vector.rotate(dir, {x=0, y=temp_vector.y*math.pi/180, z=0})
|
||||
|
||||
|
||||
--now figure out what frame will be our correct spread
|
||||
local offset = Point_to_hud(dir, fov, 1) --pretend it's a 1:1 ratio so we can do things correctly.
|
||||
local length = math.sqrt(offset.x^2+offset.y^2) --get the max length.
|
||||
|
||||
local img_perc = (self.scale*2*handler.wininfo.real_hud_scaling*self.width)/handler.wininfo.size.x --the percentage that the hud element takes up
|
||||
local frame = length/img_perc --the percentage of the size the length takes up.
|
||||
frame = math.floor(self.frames*frame)
|
||||
frame = math.clamp(frame, 0, self.frames-1)
|
||||
--"^[vertical_frame:"..self.frames..":"..frame
|
||||
self.player:hud_change(self.hud, "text", self.image.."^[verticalframe:"..self.frames..":"..frame)
|
||||
else
|
||||
self.player:hud_change(self.hud, "text", "blank.png")
|
||||
end
|
||||
end
|
||||
|
||||
function Dynamic_crosshair:prepare_deletion()
|
||||
self.player:hud_remove(self.hud)
|
||||
end
|
397
classes/Gun.lua
397
classes/Gun.lua
@ -9,6 +9,8 @@ local gun_default = {
|
||||
properties = {
|
||||
hip = { --used by gun entity (attached offset)
|
||||
offset = Vec.new(),
|
||||
sway_vel_mul = 5, --these are multipliers for various attributes. Does support fractional vals (which can be useful if you want to make hipfire more random with spread.)
|
||||
sway_angle_mul = 1,
|
||||
},
|
||||
ads = { --used by player_handler, animation handler (eye bone offset from horizontal_offset), gun entity (attached offset)
|
||||
offset = Vec.new(),
|
||||
@ -28,18 +30,22 @@ local gun_default = {
|
||||
gun_axial = 1,
|
||||
player_axial = 1,
|
||||
},
|
||||
angular_velocity = {
|
||||
angular_velocity = { --the velocity added per shot. This is the real "recoil" part of the recoil
|
||||
gun_axial = {x=0, y=0},
|
||||
player_axial = {x=0, y=0},
|
||||
},
|
||||
angular_velocity_bias = {
|
||||
bias = { --dictates the distribution bias for the direction angular_velocity is in. I.e. if you want recoil to always go up you set x to 1, more often to the right? y to -.5
|
||||
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
|
||||
target_correction_max_rate = { --the cap for target_correction_fire (i.e. this is the maximum amount it will ever correct per second.)
|
||||
gun_axial = 1,
|
||||
player_axial = 1,
|
||||
},
|
||||
hipfire_multiplier = { --the mutliplier for recoil (angular_velocity) at hipfire (can be fractional)
|
||||
gun_axial = 1,
|
||||
player_axial = 1
|
||||
},
|
||||
},
|
||||
sway = { --used by update_sway()
|
||||
max_angle = {
|
||||
@ -50,15 +56,24 @@ local gun_default = {
|
||||
gun_axial = 0,
|
||||
player_axial = 0,
|
||||
},
|
||||
hipfire_angle_multiplier = { --the mutliplier for sway max_angle at hipfire (can be fractional)
|
||||
gun_axial = 2,
|
||||
player_axial = 2
|
||||
},
|
||||
hipfire_velocity_multiplier = { --same as above but for velocity.
|
||||
gun_axial = 2,
|
||||
player_axial = 2
|
||||
}
|
||||
},
|
||||
walking_offset = { --used by update_walking() (or something)
|
||||
gun_axial = {x=1, y=-1},
|
||||
player_axial = {x=1,y=1},
|
||||
},
|
||||
breathing_scale = .5, --the max angluler offset caused by breathing.
|
||||
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,
|
||||
fire = Guns4d.default_controls.fire,
|
||||
reload = Guns4d.default_controls.reload,
|
||||
on_use = Guns4d.default_controls.on_use
|
||||
},
|
||||
@ -72,9 +87,14 @@ local gun_default = {
|
||||
accepted_bullets = {},
|
||||
accepted_magazines = {}
|
||||
},
|
||||
animations = { --used by animations handler for idle, and default controls
|
||||
empty = {x=0,y=0},
|
||||
loaded = {x=1,y=1},
|
||||
visuals = {
|
||||
--mesh
|
||||
arm_right = "right_aimpoint",
|
||||
arm_left = "left_aimpoint",
|
||||
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]
|
||||
@ -82,12 +102,6 @@ local gun_default = {
|
||||
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(),
|
||||
@ -99,12 +113,22 @@ local gun_default = {
|
||||
walking = {
|
||||
gun_axial = Vec.new(),
|
||||
player_axial = Vec.new(),
|
||||
tick = 0,
|
||||
--velocity
|
||||
},
|
||||
breathing = {
|
||||
gun_axial = 1,
|
||||
player_axial = 1,
|
||||
gun_axial = Vec.new(), --gun axial unimplemented...
|
||||
player_axial = Vec.new(),
|
||||
}
|
||||
},
|
||||
spread = {
|
||||
|
||||
},
|
||||
total_offset_rotation = { --can't be in offsets, as they're added automatically.
|
||||
gun_axial = Vec.new(),
|
||||
player_axial = Vec.new(),
|
||||
},
|
||||
player_rotation = Vec.new(),
|
||||
velocities = {
|
||||
recoil = {
|
||||
gun_axial = Vec.new(),
|
||||
@ -119,22 +143,31 @@ local gun_default = {
|
||||
consts = {
|
||||
HIP_PLAYER_GUN_ROT_RATIO = .75,
|
||||
AIM_OUT_AIM_IN_SPEED_RATIO = 2.5,
|
||||
HIPFIRE_BONE = "guns3d_hipfire_bone",
|
||||
HIPFIRE_BONE = "guns3d_hipfire_bone", --these shouldn't be here at all, these need to be model determinant.
|
||||
AIMING_BONE = "guns3d_aiming_bone",
|
||||
KEYFRAME_SAMPLE_PRECISION = 1, --[[what frequency to take precalcualted keyframe samples. The lower this is the higher the memory allocation it will need- though minimal.
|
||||
This will fuck shit up if you change it after gun construction/inheritence (interpolation between precalculated vectors will not work right)]]
|
||||
HAS_RECOIL = true,
|
||||
HAS_BREATHING = true,
|
||||
HAS_SWAY = true,
|
||||
HAS_WAG = true,
|
||||
WAG_CYCLE_SPEED = 1.6,
|
||||
WAG_DECAY = 1, --divisions per second
|
||||
HAS_GUN_AXIAL_OFFSETS = true,
|
||||
INFINITE_AMMO_IN_CREATIVE = true,
|
||||
DEFAULT_FPS = 20,
|
||||
DEFAULT_FPS = 60,
|
||||
LOOP_IDLE_ANIM = false
|
||||
},
|
||||
animation_data = { --where animations data is stored.
|
||||
anim_runtime = 0,
|
||||
length = 0,
|
||||
fps = 0,
|
||||
animations_frames = {0,0},
|
||||
frames = {0,0},
|
||||
current_frame = 0,
|
||||
--[[animations = {
|
||||
|
||||
}
|
||||
]]
|
||||
},
|
||||
particle_spawners = {},
|
||||
walking_tick = 0,
|
||||
@ -143,25 +176,24 @@ local gun_default = {
|
||||
rechamber_time = 0,
|
||||
muzzle_flash = Guns4d.effects.muzzle_flash
|
||||
}
|
||||
|
||||
function gun_default.multiplier_coefficient(multiplier, ratio)
|
||||
return 1+((multiplier*ratio)-ratio)
|
||||
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(); self:clear_animation() end
|
||||
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
|
||||
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.
|
||||
local total_rot = self.total_offset_rotation
|
||||
local player_rot = self.player_rotation
|
||||
local constant = 5 --make this a config setting
|
||||
|
||||
--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
|
||||
--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
|
||||
player_rot.y = -handler.look_rotation.y
|
||||
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
|
||||
@ -179,37 +211,44 @@ function gun_default:update(dt)
|
||||
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
|
||||
|
||||
--dynamic crosshair needs to be updated BEFORE wag
|
||||
if self.properties.sprite_scope then
|
||||
self.sprite_scope:update()
|
||||
end
|
||||
if self.properties.crosshair then
|
||||
self.crosshair:update()
|
||||
end
|
||||
|
||||
self:update_animation(dt)
|
||||
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)
|
||||
self.pos = self:get_pos()
|
||||
self.pos = self:get_pos()+self.handler:get_pos()
|
||||
|
||||
local bone, quat = self.model_handler:get_bone_global("Magazine")
|
||||
minetest.chat_send_all(dump(bone))
|
||||
--minetest.chat_send_all(dump(quat))
|
||||
self:get_pos(bone/10, 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
|
||||
--local player_axial = offsets.recoil.player_axial + offsets.walking.player_axial + offsets.sway.player_axial + offsets.breathing.player_axial
|
||||
--local gun_axial = offsets.recoil.gun_axial + offsets.walking.gun_axial + offsets.sway.gun_axial
|
||||
--apply the 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]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
if spent_bullet and spent_bullet ~= "empty" then
|
||||
local dir = self.dir
|
||||
local pos = self.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,
|
||||
@ -217,6 +256,10 @@ function gun_default:attempt_fire()
|
||||
gun = self
|
||||
})
|
||||
Guns4d.bullet_ray:new(bullet_def)
|
||||
if self.properties.visuals.animations.fire then
|
||||
minetest.chat_send_all(dump(self.properties.visuals.animations.fire))
|
||||
self:set_animation(self.properties.visuals.animations.fire, nil, false)
|
||||
end
|
||||
self:recoil()
|
||||
self:muzzle_flash()
|
||||
self.rechamber_time = 60/self.properties.firerateRPM
|
||||
@ -226,9 +269,12 @@ end
|
||||
|
||||
function gun_default:recoil()
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local rprops = self.properties.recoil
|
||||
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))
|
||||
recoil[i] = recoil[i] + (rprops.angular_velocity[axis][i]
|
||||
*math.rand_sign((rprops.bias[axis][i]/2)+.5))
|
||||
*self.multiplier_coefficient(rprops.hipfire_multiplier[axis], 1-self.handler.ads_location)
|
||||
end
|
||||
end
|
||||
self.time_since_last_fire = 0
|
||||
@ -236,21 +282,26 @@ 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 rotation = self.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 handler = self.handler
|
||||
local rotation = self.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=self.offsets.player_rotation.y*math.pi/180,z=0})
|
||||
if (self.properties.sprite_scope and handler.control_bools.ads) or (self.properties.crosshair and not handler.control_bools.ads) then
|
||||
--we need the head rotation in either of these cases, as that's what they're showing.
|
||||
dir = Vec.rotate(dir, {x=-handler.look_rotation.x*math.pi/180,y=-handler.look_rotation.y*math.pi/180,z=0})
|
||||
else
|
||||
dir = Vec.rotate(dir, {x=self.player_rotation.x*math.pi/180,y=self.player_rotation.y*math.pi/180,z=0})
|
||||
end
|
||||
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_pos = Vec.rotate(dir, {x=0,y=self.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.player_rotation.y*math.pi/180,z=0})
|
||||
local hud = player:hud_add({
|
||||
hud_elem_type = "image_waypoint",
|
||||
text = "muzzle_flash2.png",
|
||||
@ -266,13 +317,20 @@ function gun_default:get_player_axial_dir(rltv)
|
||||
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 rotation = self.total_offset_rotation
|
||||
local handler = self.handler
|
||||
--rotate x and then y.
|
||||
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})
|
||||
if (self.properties.sprite_scope and handler.control_bools.ads) or (self.properties.crosshair and not handler.control_bools.ads) then
|
||||
--we need the head rotation in either of these cases, as that's what they're showing.
|
||||
dir = Vec.rotate(dir, {x=-handler.look_rotation.x*math.pi/180,y=-handler.look_rotation.y*math.pi/180,z=0})
|
||||
else
|
||||
dir = Vec.rotate(dir, {x=self.player_rotation.x*math.pi/180,y=self.player_rotation.y*math.pi/180,z=0})
|
||||
end
|
||||
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",
|
||||
@ -289,7 +347,7 @@ function gun_default:get_dir(rltv)
|
||||
end
|
||||
|
||||
--broken! doesn't properly reflect values.
|
||||
function gun_default:get_pos(added_pos, debug)
|
||||
function gun_default:get_pos(added_pos, relative, debug)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local player = self.player
|
||||
local handler = self.handler
|
||||
@ -297,14 +355,14 @@ function gun_default:get_pos(added_pos, debug)
|
||||
local gun_offset
|
||||
if handler.control_bools.ads then
|
||||
gun_offset = self.properties.ads.offset
|
||||
bone_location, _ = player:get_eye_offset() or vector.zero(), nil
|
||||
bone_location = player:get_eye_offset() or vector.zero()
|
||||
bone_location.y = bone_location.y + handler:get_properties().eye_height
|
||||
bone_location.x = handler.horizontal_offset
|
||||
else
|
||||
--minetest is really wacky.
|
||||
gun_offset = self.properties.hip.offset
|
||||
bone_location = handler.model_handler.offsets.arm.right
|
||||
bone_location.x = -bone_location.x / 10
|
||||
bone_location = vector.new(handler.player_model_handler.offsets.global.hipfire)
|
||||
bone_location.x = bone_location.x / 10
|
||||
bone_location.z = bone_location.z / 10
|
||||
bone_location.y = bone_location.y / 10
|
||||
end
|
||||
@ -312,11 +370,20 @@ function gun_default:get_pos(added_pos, debug)
|
||||
gun_offset = gun_offset+added_pos
|
||||
end
|
||||
--dir needs to be rotated twice seperately to avoid weirdness
|
||||
local rotation = self.offsets.total_offset_rotation
|
||||
local 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))
|
||||
local hud_pos = pos+handler:get_pos()
|
||||
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))
|
||||
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
|
||||
end
|
||||
if debug then
|
||||
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({
|
||||
hud_elem_type = "image_waypoint",
|
||||
text = "muzzle_flash2.png",
|
||||
@ -350,27 +417,43 @@ function gun_default:has_entity()
|
||||
end
|
||||
function gun_default:update_wag(dt)
|
||||
local handler = self.handler
|
||||
local wag = self.offsets.walking
|
||||
local velocity = wag.velocity
|
||||
local old_tick
|
||||
if handler.walking then
|
||||
self.walking_tick = self.walking_tick + (dt*Vec.length(self.player:get_velocity()))
|
||||
else
|
||||
self.walking_tick = 0
|
||||
velocity = self.player:get_velocity()
|
||||
wag.velocity = velocity
|
||||
end
|
||||
old_tick = old_tick or wag.tick
|
||||
if velocity then
|
||||
if handler.walking then
|
||||
wag.tick = wag.tick + (dt*Vec.length(velocity))
|
||||
else
|
||||
wag.tick = wag.tick + (dt*4)
|
||||
end
|
||||
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
|
||||
if velocity and (not handler.walking) and (math.ceil(old_tick/self.consts.WAG_CYCLE_SPEED)+.5 < (math.ceil(wag.tick/self.consts.WAG_CYCLE_SPEED))+.5) and (wag.tick > old_tick) then
|
||||
wag.velocity = nil
|
||||
return
|
||||
end
|
||||
for _, i in ipairs({"x","y"}) do
|
||||
for _, axis in ipairs({"player_axial", "gun_axial"}) do
|
||||
if velocity then
|
||||
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]
|
||||
--if the result is negative we know that it's flipped, and thus can be ended.
|
||||
local inp = (wag.tick/self.consts.WAG_CYCLE_SPEED)*math.pi*multiplier
|
||||
--this is a mess, I think that 1.6 is the frequency of human steps or something
|
||||
walking_offset[axis][i] = math.sin(inp)*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
|
||||
if math.abs(walking_offset[axis][i]) > .005 then
|
||||
local multiplier = 1/self.consts.WAG_DECAY
|
||||
walking_offset[axis][i] = walking_offset[axis][i]-(walking_offset[axis][i]*multiplier*dt)
|
||||
else
|
||||
walking_offset[axis][i] = 0
|
||||
end
|
||||
if math.abs(walking_offset[axis][i]) > math.abs(old_value) then
|
||||
@ -417,42 +500,27 @@ function gun_default:update_recoil(dt)
|
||||
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)
|
||||
function gun_default:update_animation(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)
|
||||
data.runtime = data.runtime + dt
|
||||
data.current_frame = math.clamp(data.current_frame+(dt*data.fps), data.frames.x, data.frames.y)
|
||||
if data.loop and (data.current_frame > data.frames.y) then
|
||||
data.current_frame = data.frames.x
|
||||
end
|
||||
end
|
||||
--IMPORTANT!!! this does not directly modify the animation_data table, it's all hooked through ObjRef:set_animation() (init.lua) so if animation is set elsewhere it doesnt break.
|
||||
--this may be deprecated in the future- as it is no longer really needed now that I hook ObjRef functions.
|
||||
function gun_default:set_animation(frames, length, fps, loop)
|
||||
loop = loop or false --why the fuck default is loop? I DONT FUCKIN KNOW
|
||||
loop = loop or false --why the fuck default is true? I DONT FUCKIN KNOW (this undoes this)
|
||||
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
|
||||
elseif not fps then
|
||||
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()
|
||||
@ -464,31 +532,34 @@ function gun_default:clear_animation()
|
||||
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
|
||||
minetest.chat_send_all(tostring(loaded))
|
||||
minetest.chat_send_all(self.ammo_handler.ammo.loaded_mag)
|
||||
local data = self.animation_data
|
||||
data.runtime = 0
|
||||
data.length = false
|
||||
data.animations_frames = false
|
||||
if loaded then
|
||||
self.entity:set_animation({x=self.properties.visuals.animations.loaded.x, y=self.properties.visuals.animations.loaded.y}, 0, 0, self.consts.LOOP_IDLE_ANIM)
|
||||
else
|
||||
minetest.chat_send_all(self.properties.visuals.animations.empty.x)
|
||||
self.entity:set_animation({x=self.properties.visuals.animations.empty.x, y=self.properties.visuals.animations.empty.y}, 0, 0, self.consts.LOOP_IDLE_ANIM)
|
||||
end
|
||||
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.
|
||||
local scale = self.properties.breathing_scale
|
||||
--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 non-linearly.
|
||||
--not sure why 8/9 is a constant here... I assume it's if it's 8/9 of the way through the cycle. Not going to worry about it.
|
||||
if x > math.pi*(8/9) then
|
||||
self.offsets.breathing.player_axial=self.offsets.breathing.player_axial-(self.offsets.breathing.player_axial*2*dt)
|
||||
self.offsets.breathing.player_axial.x=self.offsets.breathing.player_axial.x-(self.offsets.breathing.player_axial.x*2*dt)
|
||||
else
|
||||
self.offsets.breathing.player_axial = scale*(math.sin(x))
|
||||
self.offsets.breathing.player_axial.x = scale*(math.sin(x))
|
||||
end
|
||||
end
|
||||
|
||||
function gun_default:update_sway(dt)
|
||||
assert(self.instance, "attempt to call object method from a base class")
|
||||
local sprops = self.properties.sway
|
||||
for axis, sway in pairs(self.offsets.sway) do
|
||||
local sway_vel = self.velocities.sway[axis]
|
||||
local ran
|
||||
@ -498,10 +569,12 @@ function gun_default:update_sway(dt)
|
||||
end
|
||||
end)
|
||||
ran.z = 0
|
||||
sway_vel = Vec.normalize(sway_vel+(ran*dt))*self.properties.sway.angular_velocity[axis]
|
||||
local vel_mul = self.multiplier_coefficient(sprops.hipfire_velocity_multiplier[axis], 1-self.handler.ads_location)
|
||||
sway_vel = Vec.normalize(sway_vel+(ran*dt))*sprops.angular_velocity[axis]*vel_mul
|
||||
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]
|
||||
local len_mul = self.multiplier_coefficient(sprops.hipfire_angle_multiplier[axis], 1-self.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()
|
||||
end
|
||||
self.offsets.sway[axis] = sway
|
||||
@ -509,13 +582,39 @@ function gun_default:update_sway(dt)
|
||||
end
|
||||
end
|
||||
|
||||
--this needs to save results eventually. Shouldn't be particularly important though. <-not sure what the fuck I meant by that
|
||||
--relative to the gun's entity. Returns left, right vectors.
|
||||
local out = {arm_left=vector.new(), arm_right=vector.new()}
|
||||
function gun_default:get_arm_aim_pos()
|
||||
local current_frame = self.animation_data.current_frame
|
||||
local frame1 = math.floor(current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION)
|
||||
local frame2 = math.floor(current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION)+1
|
||||
|
||||
for i, v in pairs(out) do
|
||||
if self.b3d_model.global_frames[i][frame1] then
|
||||
if (not self.b3d_model.global_frames[i][frame2]) or (current_frame==frame1) then
|
||||
out[i] = vector.copy(self.b3d_model.global_frames[i][frame1])
|
||||
else --to stop nan
|
||||
local ip_ratio = (current_frame-frame1)/self.consts.KEYFRAME_SAMPLE_PRECISION
|
||||
local vec1 = self.b3d_model.global_frames[i][frame1]
|
||||
local vec2 = self.b3d_model.global_frames[i][frame2]
|
||||
out[i] = vec1+((vec1-vec2)*ip_ratio)
|
||||
end
|
||||
else
|
||||
out[i]=vector.copy(self.b3d_model.global_frames[i][1])
|
||||
end
|
||||
end
|
||||
return out.arm_left, out.arm_right
|
||||
--return vector.copy(self.b3d_model.global_frames.arm_left[1]), vector.copy(self.b3d_model.global_frames.arm_right[1])
|
||||
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
|
||||
if self.crosshair then self.crosshair:prepare_deletion() end
|
||||
end
|
||||
--construction for the base gun class
|
||||
local valid_ctrls = {
|
||||
local valid_ctrls = { --for validation of controls.
|
||||
up=true,
|
||||
down=true,
|
||||
left=true,
|
||||
@ -560,19 +659,14 @@ gun_default.construct = function(def)
|
||||
--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()
|
||||
for offset, tbl in pairs(def.base_class.offsets) do
|
||||
def.offsets[offset] = {}
|
||||
for i, v in pairs(tbl) do
|
||||
if type(v) == "table" and v.x then
|
||||
def.offsets[offset][i] = vector.new()
|
||||
else
|
||||
def.offsets[i] = {}
|
||||
def.offsets[i] = table.deep_copy(def.offsets[i])
|
||||
def.offsets[offset][i] = v
|
||||
end
|
||||
elseif tbl.x and tbl.y and tbl.z then
|
||||
def.offsets[i] = Vec.new()
|
||||
end
|
||||
end
|
||||
|
||||
@ -580,18 +674,19 @@ gun_default.construct = function(def)
|
||||
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
|
||||
def.velocities[i].gun_axial = Vec.new()
|
||||
def.velocities[i].player_axial = Vec.new()
|
||||
end
|
||||
--properties have been assigned, create necessary objects
|
||||
--properties have been assigned, create necessary objects TODO: completely change this system for defining them.
|
||||
if def.properties.sprite_scope then
|
||||
if not def.sprite_scope then
|
||||
def.sprite_scope = def.properties.sprite_scope:new({
|
||||
gun = def
|
||||
})
|
||||
end
|
||||
def.sprite_scope = def.properties.sprite_scope:new({
|
||||
gun = def
|
||||
})
|
||||
end
|
||||
if def.properties.crosshair then
|
||||
def.crosshair = def.properties.crosshair:new({
|
||||
gun = def
|
||||
})
|
||||
end
|
||||
if def.properties.entity_scope then
|
||||
if not def.entity_scope then
|
||||
@ -624,8 +719,20 @@ gun_default.construct = function(def)
|
||||
props = def.properties --have to reinitialize this as the reference is replaced.
|
||||
|
||||
--print(table.tostring(props))
|
||||
def.model_handler = Guns4d.Model_bone_handler:new({modelpath = props.mesh})
|
||||
|
||||
def.b3d_model = mtul.b3d_reader.read_model(props.visuals.mesh, true)
|
||||
def.b3d_model.global_frames = {
|
||||
arm_right = {}, --the aim position of the right arm
|
||||
arm_left = {}, --the aim position of the left arm
|
||||
rotation = {} --rotation of the gun (this is assumed as gun_axial, but that's probably fucked for holo sight alignments)
|
||||
}
|
||||
--print(table.tostring(def.b3d_model))
|
||||
--precalculate keyframe "samples" for intepolation.
|
||||
--mildly infuriating that lua just stops short by one (so I have to add one extra) I *think* I get why though.
|
||||
for target_frame = 0, def.b3d_model.node.animation.frames+1, def.consts.KEYFRAME_SAMPLE_PRECISION do
|
||||
table.insert(def.b3d_model.global_frames.arm_right, vector.new(mtul.b3d_nodes.get_node_global_position(def.b3d_model, props.visuals.arm_right, true, target_frame))/10)
|
||||
table.insert(def.b3d_model.global_frames.arm_left, vector.new(mtul.b3d_nodes.get_node_global_position(def.b3d_model, props.visuals.arm_left, true, target_frame))/10)
|
||||
--def.b3d_model.rotation =
|
||||
end
|
||||
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")
|
||||
@ -667,14 +774,13 @@ gun_default.construct = function(def)
|
||||
minetest.register_entity(def.name.."_visual", {
|
||||
initial_properties = {
|
||||
visual = "mesh",
|
||||
mesh = props.mesh,
|
||||
mesh = props.visuals.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
|
||||
@ -684,10 +790,10 @@ gun_default.construct = function(def)
|
||||
--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
|
||||
local pitch = lua_object.total_offset_rotation.player_axial.x+lua_object.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
|
||||
local axial_rot = lua_object.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
|
||||
@ -703,7 +809,6 @@ gun_default.construct = function(def)
|
||||
end
|
||||
end,
|
||||
})
|
||||
--I don't know why lua's syntax makes me make it anon, but uh, fuck you.
|
||||
end
|
||||
end
|
||||
Guns4d.gun = Instantiatable_class:inherit(gun_default)
|
@ -1,117 +0,0 @@
|
||||
local path_seperator = "/@/"
|
||||
|
||||
Guns4d.Model_bone_handler = Instantiatable_class:inherit({
|
||||
construct = function(def)
|
||||
if def.instance then
|
||||
assert(def.modelpath, "no path provided")
|
||||
if mtul.media_paths[def.modelpath] then def.modelpath = mtul.media_paths[def.modelpath] end
|
||||
local stream = io.open(def.modelpath, "rb")
|
||||
def.b3d_table = mtul.b3d.read(stream)
|
||||
stream:close()
|
||||
stream = minetest.request_insecure_environment().io.open(minetest.get_modpath("guns4d").."/test.gltf", "wb")
|
||||
modlib.b3d.write_gltf(def.b3d_table, stream)
|
||||
stream:close()
|
||||
|
||||
def.paths = {}
|
||||
def:process_and_reformat()
|
||||
end
|
||||
end
|
||||
})
|
||||
local model_bones = Guns4d.Model_bone_handler
|
||||
--this creates a list of bone paths, and changes the index from an int to names.
|
||||
local function retrieve_hierarchy(node, out)
|
||||
if not out then out = {node} end
|
||||
if node.parent then
|
||||
table.insert(out, 1, node.parent)
|
||||
retrieve_hierarchy(node.parent, out)
|
||||
end
|
||||
return out
|
||||
end
|
||||
function model_bones:solve_global_transform(node)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local global_transform
|
||||
local hierarcy = retrieve_hierarchy(node)
|
||||
print("start")
|
||||
for i, v in pairs(hierarcy) do
|
||||
print(i, v.name)
|
||||
end
|
||||
print("end")
|
||||
for i, v in pairs(hierarcy) do
|
||||
local pos_vec = v.position
|
||||
local rot_vec = v.rotation
|
||||
local scl_vec = v.scale
|
||||
if v.keys[2] then
|
||||
pos_vec = v.keys[2].position
|
||||
rot_vec = v.keys[2].rotation
|
||||
scl_vec = v.keys[2].scale
|
||||
end
|
||||
--rot_vec = {rot_vec[2], rot_vec[3], rot_vec[4], rot_vec[1]}
|
||||
pos_vec = {-pos_vec[1], pos_vec[2], pos_vec[3]}
|
||||
local pos = modlib.matrix4.translation(pos_vec)
|
||||
rot_vec = {-rot_vec[1], rot_vec[2], rot_vec[3], rot_vec[4]}
|
||||
local rot = modlib.matrix4.rotation(modlib.quaternion.normalize(rot_vec))
|
||||
local scl = modlib.matrix4.scale(scl_vec)
|
||||
local local_transform = scl:compose(rot):compose(pos)
|
||||
|
||||
if global_transform then
|
||||
global_transform=global_transform:multiply(local_transform)
|
||||
else
|
||||
global_transform=local_transform
|
||||
end
|
||||
end
|
||||
local pos
|
||||
if node.keys[2] then
|
||||
pos = node.position
|
||||
else
|
||||
pos = node.keys[2].position
|
||||
end
|
||||
--pos = global_transform:apply({pos[1], pos[2], pos[3], 1})
|
||||
--print(dump(global_transform))
|
||||
--return vector.new(pos[1], pos[2], pos[3])
|
||||
return vector.new(global_transform[1][4], global_transform[2][4], global_transform[3][4])
|
||||
end
|
||||
function model_bones:get_bone_global(bone_name)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
for i, v in pairs(self.paths) do
|
||||
local s, e = string.find(i, bone_name, #i-#bone_name)
|
||||
--this needs to be fixed.
|
||||
if s then
|
||||
local v1, v2 = self:solve_global_transform(v)
|
||||
return v1, v2
|
||||
end
|
||||
end
|
||||
end
|
||||
function model_bones:process_and_reformat(node, path)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local first = false
|
||||
if not node then
|
||||
first = true
|
||||
node = self.b3d_table.node
|
||||
end
|
||||
path = path or ""
|
||||
node.mesh = nil --we wont be needing this
|
||||
for i, v in pairs(node.children) do
|
||||
if type(i) == "number" then
|
||||
local newpath
|
||||
if path ~= "" then
|
||||
newpath = path.." @ "..v.name
|
||||
else
|
||||
newpath = v.name
|
||||
end
|
||||
self.paths[newpath] = v
|
||||
v.mesh = nil
|
||||
v.parent = node
|
||||
node.children[v.name] = v
|
||||
node.children[i] = nil
|
||||
self:process_and_reformat(v, newpath)
|
||||
end
|
||||
end
|
||||
if first then
|
||||
for i, v in pairs(self.paths) do
|
||||
print(i)
|
||||
print(table.tostring(v.rotation))
|
||||
print(table.tostring(v.position))
|
||||
print(table.tostring(v.scale))
|
||||
end
|
||||
end
|
||||
end
|
@ -8,7 +8,7 @@ local player_handler = {
|
||||
--wielded_item = ItemStack
|
||||
--gun = Gun (class)
|
||||
--wield_index = Int
|
||||
--model_handler = player_model_handler
|
||||
--player_model_handler = player_model_handler
|
||||
look_rotation = {x=0, y=0},
|
||||
look_offset = Vec.new(),
|
||||
ads_location = 0, --interpolation scalar for gun aiming location
|
||||
@ -16,7 +16,6 @@ local player_handler = {
|
||||
fov = 80,
|
||||
horizontal_offset = 0
|
||||
}
|
||||
local model_handler = Guns4d.player_model_handler
|
||||
function player_handler:update(dt)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local player = self.player
|
||||
@ -39,11 +38,11 @@ function player_handler:update(dt)
|
||||
end
|
||||
self.gun = held_gun:new({itemstack=self.wielded_item, handler=self}) --this will set itemstack meta, and create the gun based off of meta and other data.
|
||||
----model handler----
|
||||
if self.model_handler then --if model_handler present, then delete
|
||||
self.model_handler:prepare_deletion()
|
||||
self.model_handler = nil
|
||||
if self.player_model_handler then --if player_model_handler present, then delete
|
||||
self.player_model_handler:prepare_deletion()
|
||||
self.player_model_handler = nil
|
||||
end
|
||||
self.model_handler = model_handler.get_handler(self:get_properties().mesh):new({player=self.player})
|
||||
self.player_model_handler = Guns4d.player_model_handler.get_handler(self:get_properties().mesh):new({player=self.player})
|
||||
----control handler----
|
||||
self.control_handler = Guns4d.control_handler:new({player=player, controls=self.gun.properties.controls})
|
||||
|
||||
@ -65,7 +64,7 @@ function player_handler:update(dt)
|
||||
--update handlers
|
||||
self.gun:update(dt) --gun should be updated first so self.dir is available.
|
||||
self.control_handler:update(dt)
|
||||
self.model_handler:update(dt)
|
||||
self.player_model_handler:update(dt)
|
||||
|
||||
--this has to be checked after control handler
|
||||
if TICK % 4 == 0 then
|
||||
@ -79,8 +78,8 @@ function player_handler:update(dt)
|
||||
self.gun = nil
|
||||
self:reset_controls_table() --return controls to default
|
||||
--delete model handler object (this resets the player model)
|
||||
self.model_handler:prepare_deletion()
|
||||
self.model_handler = nil
|
||||
self.player_model_handler:prepare_deletion()
|
||||
self.player_model_handler = nil
|
||||
player:hud_set_flags({wielditem = true, crosshair = true}) --reenable hud elements
|
||||
end
|
||||
|
||||
|
@ -7,51 +7,107 @@ local Vec = vector
|
||||
arm_left_global = vector.new(3.15, 11.55, 0),
|
||||
}]]
|
||||
Guns4d.player_model_handler = {
|
||||
handlers = {}, --not for children, this stores a global list of handlers by meshname.
|
||||
offsets = {
|
||||
arm = {
|
||||
right = Vec.new(-3.15, 11.55, 0),
|
||||
rltv_right = Vec.new(-3.15, 5.5, 0),
|
||||
left = Vec.new(3.15, 11.55, 0),
|
||||
rltv_left = Vec.new(3.15, 5.5, 0)
|
||||
global = {
|
||||
--right arm (for hipfire bone)
|
||||
},
|
||||
relative = { --none of these are specifically needed... perhaps delegate this to the
|
||||
--left arm
|
||||
--right arm
|
||||
--head
|
||||
},
|
||||
head = Vec.new(0,6.3,0)
|
||||
},
|
||||
handlers = {},
|
||||
mesh = "guns3d_character.b3d"
|
||||
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 = {
|
||||
arm_right = "guns3d_arm_right",
|
||||
arm_left = "guns3d_arm_left",
|
||||
aim = "guns3d_aiming_bone",
|
||||
hipfire = "guns3d_hipfire_bone",
|
||||
head = "guns3d_head"
|
||||
},
|
||||
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"] = "guns3d_character.b3d"
|
||||
},
|
||||
fallback_mesh = "guns3d_character.b3d", --if no meshes are found in "compatible_meshes" it chooses this one.
|
||||
is_new_default = true --this will set the this to be the default handler.
|
||||
}
|
||||
local player_model = Guns4d.player_model_handler
|
||||
function player_model:set_default_handler()
|
||||
assert(not self.instance, "cannot set default handler to an instance of a handler")
|
||||
player_model.default_handler = self
|
||||
function player_model.set_default_handler(class_or_name)
|
||||
assert(class_or_name, "class or mesh name (string) needed. Example: 'character.b3d' sets the default handler to whatever handler is used for character.b3d.")
|
||||
local handler = assert(((type(class_or_name) == "table") and class_or_name) or player_model.get_handler(class_or_name), "no handler by the name '"..tostring(class_or_name).."' found.")
|
||||
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]
|
||||
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:update()
|
||||
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 = Guns4d.players[player:get_player_name()].handler
|
||||
local handler = self.handler
|
||||
local gun = handler.gun
|
||||
local player_axial_offset = gun.offsets.total_offset_rotation.player_axial
|
||||
local pitch = player_axial_offset.x+gun.offsets.player_rotation.x
|
||||
local combined = player_axial_offset+gun.offsets.total_offset_rotation.gun_axial+Vec.new(gun.offsets.player_rotation.x,0,0)
|
||||
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 eye_pos = vector.new(0, handler:get_properties().eye_height*10, 0)+first
|
||||
if handler.control_bools.ads then
|
||||
eye_pos.x = handler.horizontal_offset*10
|
||||
end
|
||||
player:set_bone_position("guns3d_hipfire_bone", self.offsets.arm.rltv_right, vector.new(-(pitch*gun.consts.HIP_PLAYER_GUN_ROT_RATIO), 180-player_axial_offset.y, 0))
|
||||
player:set_bone_position("guns3d_reticle_bone", eye_pos, vector.new(combined.x, 180-combined.y, 0))
|
||||
player:set_bone_position("guns3d_head", self.offsets.head, {x=pitch,z=0,y=0})
|
||||
|
||||
player:set_bone_position(self.bone_names.hipfire, self.offsets.relative.arm_right, {x=-(pitch*gun.consts.HIP_PLAYER_GUN_ROT_RATIO), 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.offsets.player_rotation.x*math.pi/180,y=0,z=0})
|
||||
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("guns3d_aiming_bone", eye_pos, {x=rot.x,y=-rot.y+180,z=0})
|
||||
|
||||
--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})
|
||||
|
||||
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_arm_bones(dt)
|
||||
local player = self.player
|
||||
local handler = self.handler
|
||||
local gun = handler.gun
|
||||
|
||||
local left_bone, right_bone = self.offsets.global.arm_left, self.offsets.global.arm_right
|
||||
local left_trgt, right_trgt = gun:get_arm_aim_pos() --this gives us our offsets relative to the gun.
|
||||
--get the real position of the gun's bones relative to the player (2nd param true)
|
||||
left_trgt = gun:get_pos(left_trgt, true)
|
||||
right_trgt = gun:get_pos(right_trgt, true)
|
||||
local left_rotation = vector.dir_to_rotation(vector.direction(left_bone, left_trgt))*180/math.pi
|
||||
local right_rotation = vector.dir_to_rotation(vector.direction(right_bone, right_trgt))*180/math.pi
|
||||
--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)
|
||||
end
|
||||
function player_model:update_head(dt)
|
||||
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
|
||||
player:set_bone_position(self.bone_names.head, self.offsets.relative.head, {x=pitch,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()].handler
|
||||
@ -62,23 +118,47 @@ function player_model:prepare_deletion()
|
||||
properties.mesh = self.old
|
||||
handler:set_properties(properties)
|
||||
end
|
||||
--todo: add value for "still_frame" (the frame to take samples from in case 0, 0 is not still.)
|
||||
---@diagnostic disable-next-line: duplicate-set-field
|
||||
function player_model.construct(def)
|
||||
if def.instance then
|
||||
assert(def.mesh, "model has no mesh")
|
||||
assert(def.player, "no player provided")
|
||||
local handler = Guns4d.players[def.player:get_player_name()].handler
|
||||
local properties = handler:get_properties()
|
||||
def.handler = Guns4d.players[def.player:get_player_name()].handler
|
||||
local properties = def.handler:get_properties()
|
||||
def.old = properties.mesh
|
||||
--set the model
|
||||
if minetest.get_modpath("player_api") then
|
||||
player_api.set_model(def.player, def.mesh)
|
||||
end
|
||||
properties.mesh = def.mesh
|
||||
handler:set_properties(properties)
|
||||
--set the mesh
|
||||
|
||||
properties.mesh = def.compatible_meshes[properties.mesh] or def.fallback_mesh
|
||||
def.handler:set_properties(properties)
|
||||
else
|
||||
if def.replace then
|
||||
player_model.handlers[def.replace] = def
|
||||
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
|
||||
player_model.handlers[replacement_mesh] = def
|
||||
end
|
||||
if def.is_new_default then
|
||||
player_model.set_default_handler(def)
|
||||
end
|
||||
local i, v = next(def.compatible_meshes)
|
||||
local b3d_table = mtul.b3d_reader.read_model(v, 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])
|
||||
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.
|
||||
--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))
|
||||
|
||||
if def.is_new_default then
|
||||
player_model.set_default_handler(def)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,4 +1,4 @@
|
||||
Sprite_scope = Instantiatable_class:inherit({
|
||||
local Sprite_scope = Instantiatable_class:inherit({
|
||||
images = {
|
||||
fore = {
|
||||
texture = "blank.png",
|
||||
@ -52,6 +52,8 @@ Sprite_scope = Instantiatable_class:inherit({
|
||||
end
|
||||
end
|
||||
})
|
||||
Guns4d.sprite_scope = Sprite_scope
|
||||
--rename to draw?
|
||||
function Sprite_scope:update()
|
||||
local handler = self.handler
|
||||
if handler.wininfo and self.handler.control_bools.ads then
|
||||
@ -69,7 +71,7 @@ function Sprite_scope:update()
|
||||
self.player:hud_change(self.elements.reticle, "position", {x=(v1.x*self.images.reticle.movement_multiplier)+.5, y=(v1.y*self.images.reticle.movement_multiplier)+.5})
|
||||
--update textures
|
||||
end
|
||||
local angle =math.sqrt(self.gun.offsets.total_offset_rotation.gun_axial.x^2+self.gun.offsets.total_offset_rotation.gun_axial.y^2)
|
||||
local angle =math.sqrt(self.gun.total_offset_rotation.gun_axial.x^2+self.gun.total_offset_rotation.gun_axial.y^2)
|
||||
for i, v in pairs(self.elements) do
|
||||
local def = self.images[i]
|
||||
local tex = def.texture
|
||||
|
@ -151,9 +151,11 @@ Guns4d.default_controls.reload = {
|
||||
data.held = false
|
||||
local anim = next_state.anim
|
||||
if type(next_state.anim) == "string" then
|
||||
anim = props.animations[next_state.anim]
|
||||
anim = props.visuals.animations[next_state.anim]
|
||||
end
|
||||
if anim then
|
||||
gun:set_animation(anim, next_state.time)
|
||||
end
|
||||
gun:set_animation(anim, next_state.time)
|
||||
end
|
||||
elseif interrupted then
|
||||
local this_state = props.reload[data.state]
|
||||
|
42
init.lua
42
init.lua
@ -11,18 +11,16 @@ dofile(path.."/block_values.lua")
|
||||
dofile(path.."/register_ammo.lua")
|
||||
path = path .. "/classes"
|
||||
dofile(path.."/Instantiatable_class.lua")
|
||||
dofile(path.."/Model_reader.lua")
|
||||
dofile(path.."/Bullet_ray.lua")
|
||||
dofile(path.."/Control_handler.lua")
|
||||
dofile(path.."/Ammo_handler.lua")
|
||||
dofile(path.."/Sprite_scope.lua")
|
||||
dofile(path.."/Dynamic_crosshair.lua")
|
||||
dofile(path.."/Gun.lua")
|
||||
dofile(path.."/Player_model_handler.lua")
|
||||
dofile(path.."/Player_handler.lua")
|
||||
dofile(path.."/Proxy_table.lua")
|
||||
|
||||
Guns4d.Model_bone_handler:new({modelpath="model_reader_test.b3d"})
|
||||
|
||||
--load after
|
||||
path = minetest.get_modpath("guns4d")
|
||||
|
||||
@ -34,10 +32,10 @@ minetest.register_on_joinplayer(function(player)
|
||||
handler = player_handler:new({player=player})
|
||||
}
|
||||
player:set_fov(80)
|
||||
|
||||
--ObjRef overrides will be integrated into MTUL (eventually TM)
|
||||
if not objref_mtable then
|
||||
objref_mtable = getmetatable(player)
|
||||
--putting this here is hacky as fuck.
|
||||
|
||||
local old_get_pos = objref_mtable.get_pos
|
||||
function objref_mtable.get_pos(self)
|
||||
local gun = Guns4d.gun_by_ObjRef[self]
|
||||
@ -48,6 +46,40 @@ minetest.register_on_joinplayer(function(player)
|
||||
return v
|
||||
end
|
||||
end
|
||||
|
||||
local old_set_animation = objref_mtable.set_animation
|
||||
--put vargs there for maintainability.
|
||||
function objref_mtable.set_animation(self, frame_range, frame_speed, frame_blend, frame_loop, ...)
|
||||
local gun = Guns4d.gun_by_ObjRef[self]
|
||||
if gun then
|
||||
local data = gun.animation_data
|
||||
data.runtime = 0
|
||||
data.fps = frame_speed or 15
|
||||
data.loop = frame_loop --still have no idea what nutjob made the default true >:(
|
||||
if frame_loop == nil then
|
||||
frame_loop = true
|
||||
end
|
||||
--so... minetest is stupid, and so it won't let me set something to the same animation twice (utterly fucking brilliant).
|
||||
--This means I literally need to flip flop between +1 frames
|
||||
frame_range = (frame_range and table.copy(frame_range)) or {x=1,y=1}
|
||||
if data.frames.x == frame_range.x and data.frames.y == frame_range.y then
|
||||
frame_range.y = frame_range.y + 1 --oh yeah, and it only accepts whole frames... because of course.
|
||||
end
|
||||
data.frames = frame_range
|
||||
data.current_frame = data.frames.x
|
||||
end
|
||||
return old_set_animation(self, frame_range, frame_speed, frame_blend, frame_loop, ...)
|
||||
end
|
||||
|
||||
local old_set_frame_speed = objref_mtable.set_animation_frame_speed
|
||||
function objref_mtable.set_animation_frame_speed(self, frame_speed, ...)
|
||||
local gun = Guns4d.gun_by_ObjRef[self]
|
||||
if gun then
|
||||
gun.animation_data.fps = frame_speed or 15
|
||||
end
|
||||
old_set_frame_speed(self, frame_speed, ...)
|
||||
end
|
||||
|
||||
local old_remove = objref_mtable.remove
|
||||
function objref_mtable.remove(self)
|
||||
local gun = Guns4d.gun_by_ObjRef[self]
|
||||
|
@ -120,7 +120,7 @@ local function parse_index(i)
|
||||
end
|
||||
end
|
||||
--dump() sucks.
|
||||
function table.tostring(tbl, shallow, tables, depth)
|
||||
function table.tostring(tbl, shallow, long_lists, tables, depth)
|
||||
--create a list of tables that have been tostringed in this chain
|
||||
if not table then return "nil" end
|
||||
if not tables then tables = {this_table = tbl} end
|
||||
@ -131,7 +131,12 @@ function table.tostring(tbl, shallow, tables, depth)
|
||||
for i = 1, depth do
|
||||
initial_string = initial_string .. " "
|
||||
end
|
||||
if depth > 20 then
|
||||
return "(TABLE): depth limited reached"
|
||||
end
|
||||
local iterations = 0
|
||||
for i, v in pairs(tbl) do
|
||||
iterations = iterations + 1
|
||||
local val_type = type(v)
|
||||
if val_type == "string" then
|
||||
str = str..initial_string..parse_index(i).." = \""..v.."\","
|
||||
@ -140,17 +145,63 @@ function table.tostring(tbl, shallow, tables, depth)
|
||||
--to avoid infinite loops, make sure that the table has not been tostringed yet
|
||||
if not contains then
|
||||
tables[i] = v
|
||||
str = str..initial_string..parse_index(i).." = "..table.tostring(v, shallow, tables, depth)..","
|
||||
str = str..initial_string..parse_index(i).." = "..table.tostring(v, shallow, long_lists, tables, depth)..","
|
||||
else
|
||||
str = str..initial_string..parse_index(i).." = "..tostring(v).." ("..contains.."),"
|
||||
str = str..initial_string..parse_index(i).." = "..tostring(v).." (index: '"..tostring(contains).."'),"
|
||||
end
|
||||
else
|
||||
str = str..initial_string..parse_index(i).." = "..tostring(v)..","
|
||||
end
|
||||
end
|
||||
if iterations > 100 and not long_lists then
|
||||
return "(TABLE): too long, 100+ indices"
|
||||
end
|
||||
return str..string.sub(initial_string, 1, -5).."}"
|
||||
end
|
||||
|
||||
function table.tostring_structure_only(tbl, shallow, tables, depth)
|
||||
--create a list of tables that have been tostringed in this chain
|
||||
if not table then return "nil" end
|
||||
if not tables then tables = {this_table = tbl} end
|
||||
if not depth then depth = 0 end
|
||||
depth = depth + 1
|
||||
local str = ""
|
||||
local initial_string = "\n"
|
||||
for i = 1, depth do
|
||||
initial_string = initial_string .. " "
|
||||
end
|
||||
if depth > 20 then
|
||||
return "(TABLE): depth limited reached (20 nested tables)"
|
||||
end
|
||||
local iterations = 0
|
||||
if tbl.name then
|
||||
str = str..initial_string.."[\"name\"] = \""..tbl.name.."\","
|
||||
end
|
||||
if tbl.type then
|
||||
str = str..initial_string.."[\"type\"] = \""..tbl.type.."\","
|
||||
end
|
||||
for i, v in pairs(tbl) do
|
||||
iterations = iterations + 1
|
||||
local val_type = type(v)
|
||||
if val_type == "table" then
|
||||
local contains = table.contains(tables, v)
|
||||
--to avoid infinite loops, make sure that the table has not been tostringed yet
|
||||
if not contains then
|
||||
tables[parse_index(i).." ["..tostring(v).."]"] = v
|
||||
str = str..initial_string..parse_index(i).."("..tostring(v)..") = "..table.tostring_structure_only(v, shallow, tables, depth)..","
|
||||
elseif type(v) == "table" then
|
||||
str = str..initial_string..parse_index(i).." = "..tostring(v)
|
||||
else
|
||||
str = str..initial_string..parse_index(i).." = "..tostring(v).." ("..tostring(v).."),"
|
||||
end
|
||||
end
|
||||
end
|
||||
if iterations == 0 then
|
||||
return "{}"
|
||||
elseif iterations > 100 then
|
||||
return "table too long"
|
||||
end
|
||||
return "{"..str..string.sub(initial_string, 1, -5).."}"
|
||||
end
|
||||
|
||||
--replace fields (and fill sub-tables) in `tbl` with elements in `replacement`. Recursively iterates all sub-tables. use property __overfill=true for subtables that don't want to be overfilled.
|
||||
function table.fill(tbl, replacement, preserve_reference, indexed_tables)
|
||||
@ -210,9 +261,6 @@ function table.shallow_copy(t)
|
||||
return new_table
|
||||
end
|
||||
|
||||
function weighted_randoms()
|
||||
end
|
||||
|
||||
--for the following code and functions only:
|
||||
--for license see the link on the next line.
|
||||
--https://github.com/3dreamengine/3DreamEngine
|
||||
@ -234,5 +282,5 @@ function Point_to_hud(pos, fov, aspect)
|
||||
local x = (pos.x/pos.z)*a1
|
||||
local y = (pos.y/pos.z)*a6
|
||||
local z = (pos.z/pos.z)*a11
|
||||
return vector.new(x / 2, -y / 2, z)
|
||||
return {x=x / 2,y=-y / 2,}
|
||||
end
|
Binary file not shown.
@ -59,6 +59,7 @@ function Guns4d.ammo.register_magazine(def)
|
||||
assert(def.itemstring, "missing item name")
|
||||
def.accepted_bullets_set = {} --this table is a "lookup" table, I didn't go to college so I have no idea
|
||||
for i, v in pairs(def.accepted_bullets) do
|
||||
--TODO: make an actual error/minetest.log
|
||||
if not Guns4d.ammo.registered_bullets[v] then print("guns4D: WARNING! bullet "..v.." not registered! is this a mistake?") end
|
||||
def.accepted_bullets_set[v] = true
|
||||
end
|
||||
|
BIN
textures/dynamic_crosshair_circular.png
Normal file
BIN
textures/dynamic_crosshair_circular.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.5 KiB |
@ -10,8 +10,8 @@ function Guns4d.effects.muzzle_flash(self)
|
||||
end
|
||||
local dir, offset_pos = self.dir, self:get_pos(self.properties.flash_offset)
|
||||
offset_pos=offset_pos+self.player:get_pos()
|
||||
local min = vector.rotate(vector.new(-1, -1, -.15), {x=0,y=self.offsets.player_rotation.y,z=0})
|
||||
local max = vector.rotate(vector.new(1, 1, .15), {x=0,y=self.offsets.player_rotation.y,z=0})
|
||||
local min = vector.rotate(vector.new(-1, -1, -.15), {x=0,y=self.player_rotation.y,z=0})
|
||||
local max = vector.rotate(vector.new(1, 1, .15), {x=0,y=self.player_rotation.y,z=0})
|
||||
minetest.add_particlespawner({
|
||||
exptime = .18,
|
||||
time = .1,
|
||||
|
Loading…
x
Reference in New Issue
Block a user