completely reworked sound
This commit is contained in:
parent
8212981d2d
commit
c2da4ad0cb
@ -64,7 +64,7 @@ function Guns4d.ammo.register_magazine(def)
|
|||||||
def.accepted_bullets_set = {} --this table is a "lookup" table, I didn't go to college so I have no idea
|
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
|
for i, v in pairs(def.accepted_bullets) do
|
||||||
--TODO: make an actual error/minetest.log
|
--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
|
if not Guns4d.ammo.registered_bullets[v] then print("guns4D: WARNING! bullet "..v.." not registered! is this a mistake?") end --TODO replace with minetest.log
|
||||||
def.accepted_bullets_set[v] = true
|
def.accepted_bullets_set[v] = true
|
||||||
end
|
end
|
||||||
Guns4d.ammo.registered_magazines[def.itemstring] = def
|
Guns4d.ammo.registered_magazines[def.itemstring] = def
|
||||||
|
@ -182,14 +182,36 @@ function Ammo_handler:load_magazine()
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function Ammo_handler:inventory_has_ammo()
|
function Ammo_handler:load_single_cartridge()
|
||||||
|
local inv = self.inventory
|
||||||
|
local gun = self.gun
|
||||||
|
local ammo = self.ammo
|
||||||
|
local bullet
|
||||||
|
if self.ammo.total_bullets >= gun.properties.ammo.capacity then return false end
|
||||||
|
for i, v in pairs(inv:get_list("main")) do
|
||||||
|
if gun.accepted_bullets[v:get_name()] then
|
||||||
|
self:update_meta()
|
||||||
|
bullet = v:get_name()
|
||||||
|
v:take_item(1)
|
||||||
|
inv:set_stack("main", i, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if bullet then
|
||||||
|
ammo.loaded_bullets[bullet] = (ammo.loaded_bullets[bullet] or 0)+1
|
||||||
|
ammo.total_bullets = ammo.total_bullets+1
|
||||||
|
self:update_meta()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
function Ammo_handler:inventory_has_ammo(only_cartridges)
|
||||||
local inv = self.inventory
|
local inv = self.inventory
|
||||||
local gun = self.gun
|
local gun = self.gun
|
||||||
for i, v in pairs(inv:get_list("main")) do
|
for i, v in pairs(inv:get_list("main")) do
|
||||||
if gun.accepted_magazines[v:get_name()] and (tally_ammo_from_meta(v:get_meta())>0) then
|
if (not only_cartridges) and gun.accepted_magazines[v:get_name()] and (tally_ammo_from_meta(v:get_meta())>0) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if (not gun.properties.ammo.magazine_only) and gun.accepted_bullets[v:get_name()] then
|
if ((only_cartridges or not gun.properties.ammo.magazine_only) and gun.accepted_bullets[v:get_name()]) then
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,62 +1,153 @@
|
|||||||
local player_positions = {}
|
local player_positions = {}
|
||||||
minetest.register_globalstep(function(dt)
|
minetest.register_globalstep(function(dt)
|
||||||
|
for i, v in pairs(player_positions) do
|
||||||
end)
|
player_positions[i] = nil
|
||||||
|
|
||||||
Bullet_hole = Instantiatable_class:inherit({
|
|
||||||
unrendered_exptime = 20,
|
|
||||||
unrendered_texture = 'bullet_hole.png',
|
|
||||||
expiration_time = 60,
|
|
||||||
heat_effect = false,
|
|
||||||
render_distance = 50,
|
|
||||||
deletion_distance = 80,
|
|
||||||
timer = 0,
|
|
||||||
construct = function(def)
|
|
||||||
assert(def.pos)
|
|
||||||
end
|
end
|
||||||
|
for i, player_handler in pairs(Guns4d.players) do
|
||||||
|
table.insert(player_positions, player_handler.player:get_pos())
|
||||||
|
end
|
||||||
|
local count = 0
|
||||||
|
for i = #Guns4d.bullet_hole.instances+1, 1, -1 do --start at the last so the bullet_holes added sooner are expired.
|
||||||
|
local obj = Guns4d.bullet_hole.instances[i]
|
||||||
|
if obj then --obj can be a false value.
|
||||||
|
if (count > Guns4d.config.maximum_bullet_holes) and (obj.exp_time > 2.5) then
|
||||||
|
obj.exp_time = 2.5
|
||||||
|
end
|
||||||
|
count = count + 1
|
||||||
|
local closest_dist
|
||||||
|
for _, pos in pairs(player_positions) do
|
||||||
|
local dist = vector.distance(obj.pos, pos)
|
||||||
|
if (not closest_dist) or (dist < closest_dist) then
|
||||||
|
closest_dist = dist
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if closest_dist > obj.deletion_distance then
|
||||||
|
obj:delete()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if (closest_dist > obj.render_distance) and obj.rendered then
|
||||||
|
obj:unrender()
|
||||||
|
elseif (closest_dist < obj.render_distance*.85) and not obj.rendered then
|
||||||
|
obj:render()
|
||||||
|
end
|
||||||
|
obj:update(dt)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
Guns4d.bullet_hole = Instantiatable_class:inherit({
|
||||||
|
texture = 'bullet_hole.png',
|
||||||
|
exp_time = 30, --how much time a rendered bullet hole takes to expire
|
||||||
|
unrendered_exptime = 10, --how much time an unrendered bullet hole takes to expire
|
||||||
|
deletion_distance = 100,
|
||||||
|
--heat_effect = false,
|
||||||
|
instances = {},
|
||||||
|
size = .15,
|
||||||
|
render_distance = 25,
|
||||||
|
particle_spawner_id = nil,
|
||||||
|
hole_entity = "guns4d:bullet_hole",
|
||||||
|
rendered = true,
|
||||||
})
|
})
|
||||||
|
local Bullet_hole = Guns4d.bullet_hole
|
||||||
|
---@diagnostic disable-next-line: duplicate-set-field
|
||||||
|
function Bullet_hole.construct(def)
|
||||||
|
if def.instance then
|
||||||
|
assert(def.pos)
|
||||||
|
assert(def.rotation)
|
||||||
|
--[[for i, v in pairs(def.pos) do
|
||||||
|
if math.abs(v-Guns4d.math.round(v)) > (.5-(def.size/2)) then
|
||||||
|
def.pos[i] = Guns4d.math.round(v)+((math.abs(v-Guns4d.math.round(v))/(v-Guns4d.math.round(v)))*(.5-(def.size/2)))
|
||||||
|
end
|
||||||
|
end]]
|
||||||
|
Bullet_hole.instances[(#Bullet_hole.instances)+1]=def
|
||||||
|
def.id = #Bullet_hole.instances
|
||||||
|
def.unrendered_expire_speed = def.exp_time/def.unrendered_exptime
|
||||||
|
def:render()
|
||||||
|
end
|
||||||
|
end
|
||||||
function Bullet_hole:render()
|
function Bullet_hole:render()
|
||||||
if self.old_timer then
|
assert(self.instance)
|
||||||
--acount for the time lost.
|
self.rendered = true
|
||||||
self.timer = self.old_timer-(self.unrendered_exptime-self.timer)
|
|
||||||
|
local normal = vector.rotate(vector.new(0,0,1), self.rotation)
|
||||||
|
local ent = minetest.add_entity(self.pos+(normal*(.001+math.random()/1000)), self.hole_entity)
|
||||||
|
ent:set_rotation(vector.dir_to_rotation(normal))
|
||||||
|
ent:set_properties({visual_size={x=self.size, y=self.size, z=0}})
|
||||||
|
self.entity = ent
|
||||||
|
local lua_ent = ent:get_luaentity()
|
||||||
|
lua_ent.lua_instance = self
|
||||||
|
|
||||||
|
if self.particle_spawner_id then
|
||||||
|
minetest.delete_particlespawner(self.particle_spawner_id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function Bullet_hole:unrender()
|
function Bullet_hole:unrender()
|
||||||
self.old_timer = self.timer
|
assert(self.instance)
|
||||||
self.timer = self.unrendered_exptime
|
self.rendered = false
|
||||||
minetest.add_particlespawner({
|
|
||||||
pos = self.pos,
|
local normal = vector.rotate(vector.new(0,0,1), self.rotation)
|
||||||
amount = 1,
|
local time_left = self.exp_time/self.unrendered_expire_speed
|
||||||
time=0,
|
local number_of_particles = 2
|
||||||
exptime = self.unrendered_exptime,
|
minetest.add_particle({
|
||||||
|
size = self.size*10,
|
||||||
|
texture = self.texture,
|
||||||
|
expiration_time = 2*time_left/math.ceil(number_of_particles*time_left),
|
||||||
|
pos = self.pos+(normal*.05)
|
||||||
|
})
|
||||||
|
self.particle_spawner_id = minetest.add_particlespawner({
|
||||||
|
pos = self.pos+(normal*.05),
|
||||||
|
amount = math.ceil(number_of_particles*time_left)*5, --multiply so it doesn't flash in and out of existence...
|
||||||
|
time=time_left,
|
||||||
|
exptime = time_left/math.ceil(number_of_particles*time_left),
|
||||||
texture = {
|
texture = {
|
||||||
name = 'bullet_hole.png',
|
name = self.texture,
|
||||||
alpha_tween = {1,0}
|
scale = self.size*10
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if self.entity:get_pos() then
|
if self.entity:get_pos() then
|
||||||
self.entity:remove()
|
self.entity:remove()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function Bullet_hole:update()
|
function Bullet_hole:delete()
|
||||||
|
assert(self.instance)
|
||||||
|
Bullet_hole.instances[self.id] = false
|
||||||
|
if self.entity:get_pos() then
|
||||||
|
self.entity:remove()
|
||||||
|
end
|
||||||
|
if self.particle_spawner_id then
|
||||||
|
minetest.delete_particlespawner(self.particle_spawner_id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
function Bullet_hole:update_ent()
|
function Bullet_hole:update(dt)
|
||||||
|
assert(self.instance)
|
||||||
|
if self.rendered then
|
||||||
|
self.exp_time = self.exp_time-dt
|
||||||
|
else
|
||||||
|
self.exp_time = self.exp_time-(dt*self.unrendered_expire_speed)
|
||||||
|
end
|
||||||
|
if self.exp_time <= 0 then
|
||||||
|
self:delete()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
minetest.register_entity("guns4d:bullet_hole", {
|
minetest.register_entity("guns4d:bullet_hole", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
visual = "cube",
|
visual = "cube",
|
||||||
visual_size = {x=.15, y=.15, z=0},
|
visual_size = {x=.1, y=.1, z=0},
|
||||||
pointable = false,
|
pointable = false,
|
||||||
static_save = false,
|
static_save = false,
|
||||||
use_texture_alpha = true,
|
use_texture_alpha = true,
|
||||||
textures = {"blank.png", "blank.png", "blank.png", "blank.png", "bullet_hole.png", "blank.png"}
|
textures = {"blank.png", "blank.png", "blank.png", "blank.png", "bullet_hole.png", "blank.png"}
|
||||||
},
|
},
|
||||||
on_step = function(self, dtime)
|
on_step = function(self, dt)
|
||||||
if TICK % 50 then
|
if TICK % 50 then
|
||||||
local class_inst = self.class_Inst
|
local lua_instance = self.lua_instance
|
||||||
if class_inst.timer < 30 then
|
if lua_instance.exp_time <= 0 then
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if lua_instance.exp_time < 2.5 then
|
||||||
local properties = self.object:get_properties()
|
local properties = self.object:get_properties()
|
||||||
properties.textures[5] = 'bullet_hole.png^[opacity:'..(math.floor((12.75*tostring(self.timer/30)))*20)
|
properties.textures[5] = lua_instance.texture..'^[opacity:'..(math.floor((12.75*tostring(lua_instance.exp_time/2.5)))*20)
|
||||||
self.object:set_properties(properties)
|
self.object:set_properties(properties)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -3,8 +3,7 @@ local ray = {
|
|||||||
state = "free",
|
state = "free",
|
||||||
--pos = pos,
|
--pos = pos,
|
||||||
last_node = "",
|
last_node = "",
|
||||||
hole_entity = "guns4d:bullet_hole",
|
bullet_hole_class = Guns4d.bullet_hole,
|
||||||
normal = vector.new(),
|
|
||||||
--last_dir
|
--last_dir
|
||||||
--exit_direction = dir,
|
--exit_direction = dir,
|
||||||
--range_left = def.bullet.range,
|
--range_left = def.bullet.range,
|
||||||
@ -45,6 +44,10 @@ local ray = {
|
|||||||
pass_sound_mixing_factor = Guns4d.config.default_pass_sound_mixing_factor, --determines the ratio to use based on energy
|
pass_sound_mixing_factor = Guns4d.config.default_pass_sound_mixing_factor, --determines the ratio to use based on energy
|
||||||
damage = 0,
|
damage = 0,
|
||||||
energy = 0,
|
energy = 0,
|
||||||
|
spread_deviation = 1, --deviation of the standard distribution represented. The lower the closer to the center of the spread a pellet is more likely to be.
|
||||||
|
spread = 0, --defaults to 1 if pellets present but spread not defined.
|
||||||
|
pellets = 1,
|
||||||
|
wall_penetration = true, --turns off by default if pellets are greater then one.
|
||||||
ITERATION_DISTANCE = Guns4d.config.default_penetration_iteration_distance,
|
ITERATION_DISTANCE = Guns4d.config.default_penetration_iteration_distance,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,7 +66,7 @@ function ray:find_transverse_edge()
|
|||||||
return pointed.intersection_point, pointed.intersection_normal
|
return pointed.intersection_point, pointed.intersection_normal
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function ray:_cast()
|
function ray:cast()
|
||||||
assert(self.instance, "attempt to call obj method on a class")
|
assert(self.instance, "attempt to call obj method on a class")
|
||||||
local next_state = self.state --next state of course the state of the next ray.
|
local next_state = self.state --next state of course the state of the next ray.
|
||||||
|
|
||||||
@ -84,7 +87,7 @@ function ray:_cast()
|
|||||||
end_pos = self.pos+(self.dir*self.range)
|
end_pos = self.pos+(self.dir*self.range)
|
||||||
end
|
end
|
||||||
--do the main raycast. We don't account for mmRHA dropoff here.
|
--do the main raycast. We don't account for mmRHA dropoff here.
|
||||||
local continue = true --indicates wether to :_iterate wether the Bullet_ray has ended
|
local continue = true --indicates wether to :iterate wether the Bullet_ray has ended
|
||||||
local cast = minetest.raycast(self.pos, end_pos, true, true)
|
local cast = minetest.raycast(self.pos, end_pos, true, true)
|
||||||
local edge_length
|
local edge_length
|
||||||
if edge then
|
if edge then
|
||||||
@ -142,9 +145,9 @@ function ray:_cast()
|
|||||||
return pointed_node, pointed_object, next_state, end_pos, end_normal, continue
|
return pointed_node, pointed_object, next_state, end_pos, end_normal, continue
|
||||||
end
|
end
|
||||||
--the main function.
|
--the main function.
|
||||||
function ray:_iterate(initialized)
|
function ray:iterate()
|
||||||
assert(self.instance, "attempt to call obj method on a class")
|
assert(self.instance, "attempt to call obj method on a class")
|
||||||
local pointed_node, pointed_object, next_state, end_pos, end_normal, continue = self:_cast()
|
local pointed_node, pointed_object, next_state, end_pos, end_normal, continue = self:cast()
|
||||||
|
|
||||||
local distance = vector.distance(self.pos, end_pos)
|
local distance = vector.distance(self.pos, end_pos)
|
||||||
if self.state == "free" then
|
if self.state == "free" then
|
||||||
@ -163,7 +166,7 @@ function ray:_iterate(initialized)
|
|||||||
--calc penetration loss from traveling through the block
|
--calc penetration loss from traveling through the block
|
||||||
local penetration_loss = distance*Guns4d.node_properties[self.last_node_name].mmRHA
|
local penetration_loss = distance*Guns4d.node_properties[self.last_node_name].mmRHA
|
||||||
--calculate our energy loss based on the percentage of energy our penetration represents.
|
--calculate our energy loss based on the percentage of energy our penetration represents.
|
||||||
self.energy = self.energy-((self.init_energy*self.energy_sharp_ratio)*(penetration_loss/self.sharp_penetration))
|
self.energy = self.energy-((self.init_def.energy*self.energy_sharp_ratio)*(penetration_loss/self.sharp_penetration))
|
||||||
end
|
end
|
||||||
--set values for next iteration.
|
--set values for next iteration.
|
||||||
self.range = self.range-distance
|
self.range = self.range-distance
|
||||||
@ -191,7 +194,7 @@ function ray:_iterate(initialized)
|
|||||||
normal = end_normal, --end normal may be nil, as it's only for hit effects.
|
normal = end_normal, --end normal may be nil, as it's only for hit effects.
|
||||||
})
|
})
|
||||||
if continue and self.range > 0 and self.energy > 0 then
|
if continue and self.range > 0 and self.energy > 0 then
|
||||||
self:_iterate(true)
|
self:iterate()
|
||||||
end
|
end
|
||||||
--[[if not initialized then
|
--[[if not initialized then
|
||||||
for i, v in pairs(self.history) do
|
for i, v in pairs(self.history) do
|
||||||
@ -220,7 +223,7 @@ function ray:hit_entity(object)
|
|||||||
|
|
||||||
local resistance = object:get_armor_groups() -- support for different body parts is needed here, that's for... a later date, though.
|
local resistance = object:get_armor_groups() -- support for different body parts is needed here, that's for... a later date, though.
|
||||||
--calculate the amount of penetration we've lost based on how much of the energy is converted to penetration (energy_sharp_ratio)
|
--calculate the amount of penetration we've lost based on how much of the energy is converted to penetration (energy_sharp_ratio)
|
||||||
local dropoff_ratio = (1-(self.energy/self.init_energy))
|
local dropoff_ratio = (1-(self.energy/self.init_def.energy))
|
||||||
local bullet_sharp_pen = self.sharp_penetration-(self.sharp_penetration*dropoff_ratio*self.energy_sharp_ratio)
|
local bullet_sharp_pen = self.sharp_penetration-(self.sharp_penetration*dropoff_ratio*self.energy_sharp_ratio)
|
||||||
local effective_sharp_pen = Guns4d.math.clamp(bullet_sharp_pen - (resistance.guns4d_mmRHA or 0), 0, math.huge)
|
local effective_sharp_pen = Guns4d.math.clamp(bullet_sharp_pen - (resistance.guns4d_mmRHA or 0), 0, math.huge)
|
||||||
local converted_Pa = (bullet_sharp_pen-effective_sharp_pen) * self.sharp_to_blunt_conversion_factor
|
local converted_Pa = (bullet_sharp_pen-effective_sharp_pen) * self.sharp_to_blunt_conversion_factor
|
||||||
@ -265,14 +268,14 @@ function ray:apply_damage(object, sharp_pen, blunt_pen)
|
|||||||
end
|
end
|
||||||
function ray:bullet_hole(pos, normal)
|
function ray:bullet_hole(pos, normal)
|
||||||
assert(self.instance, "attempt to call obj method on a class")
|
assert(self.instance, "attempt to call obj method on a class")
|
||||||
local nearby_players = false
|
--[[local nearby_players = false
|
||||||
for pname, player in pairs(minetest.get_connected_players()) do
|
for pname, player in pairs(minetest.get_connected_players()) do
|
||||||
if vector.distance(player:get_pos(), pos) < 50 then
|
if vector.distance(player:get_pos(), pos) < 50 then
|
||||||
nearby_players = true; break
|
nearby_players = true; break
|
||||||
end
|
end
|
||||||
end
|
end]]
|
||||||
--if it's close enough to any players, then add it
|
--if it's close enough to any players, then add it
|
||||||
if nearby_players then
|
--[[if nearby_players then
|
||||||
--this entity will keep track of itself.
|
--this entity will keep track of itself.
|
||||||
local ent = minetest.add_entity(pos+(normal*(.0001+math.random()/1000)), self.hole_entity)
|
local ent = minetest.add_entity(pos+(normal*(.0001+math.random()/1000)), self.hole_entity)
|
||||||
ent:set_rotation(vector.dir_to_rotation(normal))
|
ent:set_rotation(vector.dir_to_rotation(normal))
|
||||||
@ -280,18 +283,23 @@ function ray:bullet_hole(pos, normal)
|
|||||||
lua_ent.block_pos = pos
|
lua_ent.block_pos = pos
|
||||||
else
|
else
|
||||||
Guns4d.effects.spawn_bullet_hole_particle(pos, self.hole_scale, '(bullet_hole_1.png^(bullet_hole_2.png^[opacity:129))')
|
Guns4d.effects.spawn_bullet_hole_particle(pos, self.hole_scale, '(bullet_hole_1.png^(bullet_hole_2.png^[opacity:129))')
|
||||||
end
|
end]]
|
||||||
|
local bullet_hole = self.bullet_hole_class:new({
|
||||||
|
pos = vector.new(pos),
|
||||||
|
rotation = vector.dir_to_rotation(normal)
|
||||||
|
})
|
||||||
|
-- ent:set_rotation(vector.dir_to_rotation(normal))
|
||||||
end
|
end
|
||||||
function ray:play_bullet_pass_sounds()
|
function ray:play_bullet_pass_sounds()
|
||||||
--iteration done, damage applied, find players to apply bullet whizz to
|
--iteration done, damage applied, find players to apply bullet whizz to
|
||||||
local start_pos = self.init_pos
|
local start_pos = self.init_def.pos
|
||||||
local played_for = {}
|
local played_for = {}
|
||||||
for i = #self.history, 1, -1 do
|
for i = #self.history, 1, -1 do
|
||||||
local v = self.history[i]
|
local v = self.history[i]
|
||||||
for _, player in pairs(minetest.get_connected_players()) do
|
for _, player in pairs(minetest.get_connected_players()) do
|
||||||
if (player~=self.player) and not played_for[player] then
|
if (player~=self.player) and not played_for[player] then
|
||||||
local pos = player:get_pos()+vector.new(0,player:get_properties().eye_height,0)
|
local pos = player:get_pos()+vector.new(0,player:get_properties().eye_height,0)
|
||||||
local nearest = Guns4d.nearest_point_on_line(start_pos, v.pos, pos)
|
local nearest = Guns4d.math.nearest_point_on_line(start_pos, v.pos, pos)
|
||||||
if vector.distance(nearest, pos) < self.pass_sound_max_distance then
|
if vector.distance(nearest, pos) < self.pass_sound_max_distance then
|
||||||
played_for[player] = true
|
played_for[player] = true
|
||||||
if self.pass_sounds[1] then
|
if self.pass_sounds[1] then
|
||||||
@ -301,7 +309,7 @@ function ray:play_bullet_pass_sounds()
|
|||||||
else
|
else
|
||||||
--interpolate to find the energy of the shot to determine supersonic or not.
|
--interpolate to find the energy of the shot to determine supersonic or not.
|
||||||
local v1
|
local v1
|
||||||
if #self.history > i then v1 = v[i+1].energy else v1 = self.init_energy end
|
if #self.history > i then v1 = v[i+1].energy else v1 = self.init_def.energy end
|
||||||
local v2 = v.energy
|
local v2 = v.energy
|
||||||
|
|
||||||
local ip_r = vector.distance(start_pos, nearest)/vector.distance(start_pos, pos)
|
local ip_r = vector.distance(start_pos, nearest)/vector.distance(start_pos, pos)
|
||||||
@ -348,6 +356,32 @@ function ray:play_bullet_pass_sounds()
|
|||||||
start_pos = v.pos
|
start_pos = v.pos
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
function ray:simple_cast(pos, dir)
|
||||||
|
local cast = minetest.raycast(pos, pos+(dir*self.range), true, true)
|
||||||
|
local pointed
|
||||||
|
for hit in cast do
|
||||||
|
if hit.type == "node" and Guns4d.node_properties[minetest.get_node(hit.under).name].behavior ~= "ignore" then
|
||||||
|
self:bullet_hole(hit.intersection_point, hit.intersection_normal)
|
||||||
|
pointed = hit
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if pointed then
|
||||||
|
table.insert(self.history, {
|
||||||
|
pos = pointed.intersection_pos,
|
||||||
|
energy = self.energy, --TODO, ENERGY CALCS
|
||||||
|
state = "free",
|
||||||
|
last_node = (pointed.under and minetest.get_node(pointed.under).name),
|
||||||
|
normal = pointed.intersection_normal, --end normal may be nil, as it's only for hit effects.
|
||||||
|
})
|
||||||
|
else
|
||||||
|
table.insert(self.history, {
|
||||||
|
pos = pos+(dir*self.range),
|
||||||
|
energy = self.energy, --TODO, ENERGY CALCS
|
||||||
|
state = "free",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
function ray.construct(def)
|
function ray.construct(def)
|
||||||
if def.instance then
|
if def.instance then
|
||||||
--these asserts aren't necessary, probably drags down performance a tiny bit.
|
--these asserts aren't necessary, probably drags down performance a tiny bit.
|
||||||
@ -379,15 +413,40 @@ function ray.construct(def)
|
|||||||
end
|
end
|
||||||
def.energy_sharp_ratio = (def.energy-def.blunt_penetration)/def.energy
|
def.energy_sharp_ratio = (def.energy-def.blunt_penetration)/def.energy
|
||||||
|
|
||||||
def.init_energy = def.energy
|
def.init_def = {
|
||||||
|
energy = def.energy,
|
||||||
|
pos = def.pos,
|
||||||
|
dir = def.dir or def.gun:get_dir(),
|
||||||
|
range = def.range
|
||||||
|
}
|
||||||
|
if def.pellets > 1 then
|
||||||
|
if rawget(def, "wall_penetration") == nil then
|
||||||
|
def.wall_penetration = false
|
||||||
|
end
|
||||||
|
if rawget(def, "spread") == nil then
|
||||||
|
def.spread = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
--blunt pen is in the same units (1 Joule/Area^3 = 1 Pa), so we use it to make the ratio by subtraction.
|
--blunt pen is in the same units (1 Joule/Area^3 = 1 Pa), so we use it to make the ratio by subtraction.
|
||||||
|
local init_def = def.init_def
|
||||||
def.dir = vector.new(def.dir)
|
for i=1,def.pellets do
|
||||||
def.pos = vector.new(def.pos)
|
local x, y = Guns4d.math.angular_normal_distribution(def.spread_deviation)
|
||||||
|
--x, y = (math.random()-.5)*2, (math.random()-.5)*2
|
||||||
|
local dir = def.gun:get_dir(false, x*def.spread, y*def.spread)
|
||||||
|
if def.wall_penetration then
|
||||||
|
def.energy = init_def.energy
|
||||||
|
def.dir = dir or vector.new(init_def.dir)
|
||||||
|
def.pos = vector.new(init_def.pos)
|
||||||
|
def.range = init_def.range
|
||||||
def.history = {}
|
def.history = {}
|
||||||
def.init_pos = vector.new(def.pos) --has to be cloned before iteration
|
def:iterate()
|
||||||
def:_iterate()
|
|
||||||
def:play_bullet_pass_sounds()
|
def:play_bullet_pass_sounds()
|
||||||
|
else
|
||||||
|
def.history = {} --we still have to use this for the pass sounds
|
||||||
|
def:simple_cast(init_def.pos, dir or init_def.dir)
|
||||||
|
def:play_bullet_pass_sounds()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Guns4d.bullet_ray = Instantiatable_class:inherit(ray)
|
Guns4d.bullet_ray = Instantiatable_class:inherit(ray)
|
@ -12,6 +12,9 @@ Guns4d.control_handler = {
|
|||||||
loop = false,
|
loop = false,
|
||||||
func=function(active, interrupted, data, busy_controls)
|
func=function(active, interrupted, data, busy_controls)
|
||||||
}
|
}
|
||||||
|
on_use = function()
|
||||||
|
on_secondary_use = function()
|
||||||
|
on_drop = function() return a bool to indicate wether to drop the item or not.
|
||||||
}
|
}
|
||||||
]]
|
]]
|
||||||
ads = false,
|
ads = false,
|
||||||
@ -114,6 +117,12 @@ function controls:on_use(itemstack, pointed_thing)
|
|||||||
actions.on_use(itemstack, self.handler, pointed_thing)
|
actions.on_use(itemstack, self.handler, pointed_thing)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
function controls:on_drop(itemstack, pointed_thing, pos)
|
||||||
|
local actions = self:get_actions()
|
||||||
|
if actions.on_drop then
|
||||||
|
return actions.on_use(itemstack, self.handler, pos)
|
||||||
|
end
|
||||||
|
end
|
||||||
function controls:on_secondary_use(itemstack, pointed_thing)
|
function controls:on_secondary_use(itemstack, pointed_thing)
|
||||||
assert(self.instance, "attempt to call object method on a class")
|
assert(self.instance, "attempt to call object method on a class")
|
||||||
local actions = self:get_actions()
|
local actions = self:get_actions()
|
||||||
|
@ -30,7 +30,7 @@ end
|
|||||||
local function render_length(rotation, fov)
|
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})
|
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})
|
vector.rotate(dir,{x=0,y=rotation.y*math.pi/180,z=0})
|
||||||
local out = Guns4d.rltv_point_to_hud(dir, fov, 1)
|
local out = Guns4d.math.rltv_point_to_hud(dir, fov, 1)
|
||||||
return math.sqrt(out.x^2+out.y^2)
|
return math.sqrt(out.x^2+out.y^2)
|
||||||
end
|
end
|
||||||
function Dynamic_crosshair:update(dt)
|
function Dynamic_crosshair:update(dt)
|
||||||
@ -85,7 +85,7 @@ function Dynamic_crosshair:update(dt)
|
|||||||
|
|
||||||
|
|
||||||
--now figure out what frame will be our correct spread
|
--now figure out what frame will be our correct spread
|
||||||
local offset = Guns4d.rltv_point_to_hud(dir, fov, 1) --pretend it's a 1:1 ratio so we can do things correctly.
|
local offset = Guns4d.math.rltv_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 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 img_perc = (self.scale*2*handler.wininfo.real_hud_scaling*self.width)/handler.wininfo.size.x --the percentage that the hud element takes up
|
||||||
|
127
classes/Gun.lua
127
classes/Gun.lua
@ -125,7 +125,9 @@ local gun_default = {
|
|||||||
charging = { --how the gun "cocks"
|
charging = { --how the gun "cocks"
|
||||||
require_draw_on_swap = true,
|
require_draw_on_swap = true,
|
||||||
bolt_charge_mode = "none", --"none"-chamber is always full, "catch"-when fired to dry bolt will not need to be charged after reload, "no_catch" bolt will always need to be charged after reload.
|
bolt_charge_mode = "none", --"none"-chamber is always full, "catch"-when fired to dry bolt will not need to be charged after reload, "no_catch" bolt will always need to be charged after reload.
|
||||||
default_draw_time = 1,
|
draw_time = 1,
|
||||||
|
draw_animation = "draw",
|
||||||
|
draw_sound = "draw"
|
||||||
--sound = soundspec
|
--sound = soundspec
|
||||||
},
|
},
|
||||||
reload = { --used by defualt controls. Still provides usefulness elsewhere.
|
reload = { --used by defualt controls. Still provides usefulness elsewhere.
|
||||||
@ -135,12 +137,14 @@ local gun_default = {
|
|||||||
},
|
},
|
||||||
ammo = { --used by ammo_handler
|
ammo = { --used by ammo_handler
|
||||||
magazine_only = false,
|
magazine_only = false,
|
||||||
|
--capacity = 0, --this is only needed if magazine_only = false
|
||||||
accepted_bullets = {},
|
accepted_bullets = {},
|
||||||
accepted_magazines = {},
|
accepted_magazines = {},
|
||||||
initial_mag = "empty"
|
initial_mag = "empty"
|
||||||
},
|
},
|
||||||
visuals = {
|
visuals = {
|
||||||
--mesh
|
--textures = {},
|
||||||
|
--mesh="string.b3d",
|
||||||
backface_culling = true,
|
backface_culling = true,
|
||||||
root = "gun",
|
root = "gun",
|
||||||
magazine = "magazine",
|
magazine = "magazine",
|
||||||
@ -252,8 +256,6 @@ local gun_default = {
|
|||||||
consts = {
|
consts = {
|
||||||
HIP_PLAYER_GUN_ROT_RATIO = .75,
|
HIP_PLAYER_GUN_ROT_RATIO = .75,
|
||||||
AIM_OUT_AIM_IN_SPEED_RATIO = 2.5,
|
AIM_OUT_AIM_IN_SPEED_RATIO = 2.5,
|
||||||
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.
|
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)]]
|
This will fuck shit up if you change it after gun construction/inheritence (interpolation between precalculated vectors will not work right)]]
|
||||||
WAG_CYCLE_SPEED = 1.6,
|
WAG_CYCLE_SPEED = 1.6,
|
||||||
@ -266,7 +268,10 @@ local gun_default = {
|
|||||||
HAS_WAG = true,
|
HAS_WAG = true,
|
||||||
HAS_GUN_AXIAL_OFFSETS = true,
|
HAS_GUN_AXIAL_OFFSETS = true,
|
||||||
ANIMATIONS_OFFSET_AIM = false,
|
ANIMATIONS_OFFSET_AIM = false,
|
||||||
LOOP_IDLE_ANIM = false
|
LOOP_IDLE_ANIM = false,
|
||||||
|
THIRD_PERSON_GAIN_MULTIPLIER = Guns4d.config.third_person_gain_multiplier,
|
||||||
|
ITEM_COLLISIONBOX = ((not Guns4d.config.realistic_items) and {-.1,-.1,-.1, .1,.1,.1}) or {-.1,-.05,-.1, .1,.15,.1},
|
||||||
|
ITEM_SELECTIONBOX = {-.2,-.2,-.2, .1,.2,.2},
|
||||||
},
|
},
|
||||||
--[[animation_data = { --where animations data is stored.
|
--[[animation_data = { --where animations data is stored.
|
||||||
anim_runtime = 0,
|
anim_runtime = 0,
|
||||||
@ -295,17 +300,15 @@ end
|
|||||||
function gun_default:draw()
|
function gun_default:draw()
|
||||||
assert(self.instance, "attempt to call object method on a class")
|
assert(self.instance, "attempt to call object method on a class")
|
||||||
local props = self.properties
|
local props = self.properties
|
||||||
if props.visuals.animations.draw then
|
if props.visuals.animations[props.charging.draw_animation] then
|
||||||
self:set_animation(props.visuals.animations.draw, props.charging.default_draw_time)
|
self:set_animation(props.visuals.animations[props.charging.draw_animation], props.charging.draw_time)
|
||||||
end
|
end
|
||||||
if props.sounds.draw then
|
if props.sounds[props.charging.draw_sound] then
|
||||||
local sounds = Guns4d.table.deep_copy(props.sounds.draw)
|
local sounds = Guns4d.table.deep_copy(props.sounds[props.charging.draw_sound])
|
||||||
sounds.player = self.player
|
self:play_sounds(sounds)
|
||||||
sounds.pos = self.pos
|
|
||||||
Guns4d.play_sounds(sounds)
|
|
||||||
end
|
end
|
||||||
self.ammo_handler:chamber_round()
|
self.ammo_handler:chamber_round()
|
||||||
self.rechamber_time = props.charging.default_draw_time
|
self.rechamber_time = props.charging.draw_time
|
||||||
end
|
end
|
||||||
--update gun, the main function.
|
--update gun, the main function.
|
||||||
function gun_default:update(dt)
|
function gun_default:update(dt)
|
||||||
@ -442,7 +445,7 @@ function gun_default:attempt_fire()
|
|||||||
player = self.player,
|
player = self.player,
|
||||||
--we don't want it to be doing fuckshit and letting players shoot through walls.
|
--we don't want it to be doing fuckshit and letting players shoot through walls.
|
||||||
pos = pos-((self.handler.control_handler.ads and dir*self.properties.ads.offset.z) or dir*self.properties.hip.offset.z),
|
pos = pos-((self.handler.control_handler.ads and dir*self.properties.ads.offset.z) or dir*self.properties.hip.offset.z),
|
||||||
dir = dir,
|
--dir = dir, this is now collected directly by calling get_dir so pellets and spread can be handled by the bullet_ray instance.
|
||||||
gun = self
|
gun = self
|
||||||
})
|
})
|
||||||
Guns4d.bullet_ray:new(bullet_def)
|
Guns4d.bullet_ray:new(bullet_def)
|
||||||
@ -455,7 +458,7 @@ function gun_default:attempt_fire()
|
|||||||
--print(dump(self.properties.sounds.fire))
|
--print(dump(self.properties.sounds.fire))
|
||||||
local fire_sound = Guns4d.table.deep_copy(self.properties.sounds.fire) --important that we copy because play_sounds modifies it.
|
local fire_sound = Guns4d.table.deep_copy(self.properties.sounds.fire) --important that we copy because play_sounds modifies it.
|
||||||
fire_sound.pos = self.pos
|
fire_sound.pos = self.pos
|
||||||
Guns4d.play_sounds(fire_sound)
|
self:play_sounds(fire_sound)
|
||||||
|
|
||||||
self.rechamber_time = 60/self.properties.firerateRPM
|
self.rechamber_time = 60/self.properties.firerateRPM
|
||||||
return true
|
return true
|
||||||
@ -516,37 +519,72 @@ function gun_default:get_player_axial_dir(rltv)
|
|||||||
end, hud)]]
|
end, hud)]]
|
||||||
return dir
|
return dir
|
||||||
end
|
end
|
||||||
function gun_default:get_dir(rltv)
|
--this needs to be optimized because it may be called frequently...
|
||||||
|
function gun_default:get_dir(rltv, offset_x, offset_y)
|
||||||
assert(self.instance, "attempt to call object method on a class")
|
assert(self.instance, "attempt to call object method on a class")
|
||||||
local rotation = self.total_offset_rotation
|
local rotation = self.total_offset_rotation
|
||||||
local handler = self.handler
|
local handler = self.handler
|
||||||
--rotate x and then y.
|
--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}))
|
--old code. I used a site (symbolab.com) to precalculate the rotation matrices to save on performance since spread has to run this.
|
||||||
dir = Vec.rotate(dir, {y=((rotation.gun_axial.y+rotation.player_axial.y)*math.pi/180), x=0, z=0})
|
--local dir = Vec.rotate({x=0, y=0, z=1}, {y=0, x=((rotation.gun_axial.x+rotation.player_axial.x)*math.pi/180), z=0})
|
||||||
if not rltv then
|
--dir = Vec.rotate(dir, {y=((rotation.gun_axial.y+rotation.player_axial.y)*math.pi/180), x=0, z=0})
|
||||||
|
local p = -(rotation.gun_axial.x+rotation.player_axial.x+(offset_x or 0))*math.pi/180
|
||||||
|
local y = -(rotation.gun_axial.y+rotation.player_axial.y+(offset_y or 0))*math.pi/180
|
||||||
|
local Cy = math.cos(y)
|
||||||
|
local Sy = math.sin(y)
|
||||||
|
local Cp = math.cos(p)
|
||||||
|
local Sp = math.sin(p)
|
||||||
|
local dir = {
|
||||||
|
x=Sy*Cy,
|
||||||
|
y=-Sp,
|
||||||
|
z=Cy*Cp
|
||||||
|
}
|
||||||
|
if not rltv then --look rotation is that actual rotation of the player's camera, player_rotation is the gun's current rotation (because vertical rotation will differ for smoothness.)
|
||||||
if (self.properties.sprite_scope and handler.control_handler.ads) or (self.properties.crosshair and not handler.control_handler.ads) then
|
if (self.properties.sprite_scope and handler.control_handler.ads) or (self.properties.crosshair and not handler.control_handler.ads) then
|
||||||
--we need the head rotation in either of these cases, as that's what they're showing.
|
--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})
|
--dir = Vec.rotate(dir, {x=-handler.look_rotation.x*math.pi/180,y=-handler.look_rotation.y*math.pi/180,z=0})
|
||||||
|
--[[
|
||||||
|
Cy = math.cos(y)
|
||||||
|
Sy = math.sin(y)
|
||||||
|
Cp = math.cos(p)
|
||||||
|
Sp = math.sin(p)
|
||||||
|
dir.x = (Cy*dir.x)+(Sy*Sp*dir.y)+(Sy*Cp*dir.z)
|
||||||
|
dir.y = (dir.y*Cp)-(dir.z*Sp)
|
||||||
|
dir.z = -(dir.x*Sy)+(dir.y*Sp*Cy)+(dir.z*Cy*Cp)]]
|
||||||
|
p = handler.look_rotation.x*math.pi/180
|
||||||
|
y = handler.look_rotation.y*math.pi/180
|
||||||
else
|
else
|
||||||
dir = Vec.rotate(dir, {x=self.player_rotation.x*math.pi/180,y=self.player_rotation.y*math.pi/180,z=0})
|
p = -self.player_rotation.x*math.pi/180
|
||||||
|
y = -self.player_rotation.y*math.pi/180
|
||||||
|
--dir = Vec.rotate(dir, {x=self.player_rotation.x*math.pi/180,y=self.player_rotation.y*math.pi/180,z=0})
|
||||||
end
|
end
|
||||||
|
Cy = math.cos(y)
|
||||||
|
Sy = math.sin(y)
|
||||||
|
Cp = math.cos(p)
|
||||||
|
Sp = math.sin(p)
|
||||||
|
dir = vector.new(
|
||||||
|
(Cy*dir.x)+(Sy*Sp*dir.y)+(Sy*Cp*dir.z),
|
||||||
|
(dir.y*Cp)-(dir.z*Sp),
|
||||||
|
(-dir.x*Sy)+(dir.y*Sp*Cy)+(dir.z*Cy*Cp)
|
||||||
|
)
|
||||||
|
else
|
||||||
|
dir = vector.new(dir)
|
||||||
end
|
end
|
||||||
|
return dir
|
||||||
--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})
|
end
|
||||||
--[[local hud = player:hud_add({
|
--some old debug code for get_dir
|
||||||
|
--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",
|
hud_elem_type = "image_waypoint",
|
||||||
text = "muzzle_flash2.png",
|
text = "muzzle_flash2.png",
|
||||||
world_pos = hud_pos,
|
world_pos = hud_pos,
|
||||||
scale = {x=10, y=10},
|
scale = {x=10, y=10},
|
||||||
alignment = {x=0,y=0},
|
alignment = {x=0,y=0},
|
||||||
offset = {x=0,y=0},
|
offset = {x=0,y=0},
|
||||||
})
|
})
|
||||||
minetest.after(0, function(hud)
|
minetest.after(0, function(hud)
|
||||||
player:hud_remove(hud)
|
player:hud_remove(hud)
|
||||||
end, hud)]]
|
end, hud)]]
|
||||||
return dir
|
|
||||||
end
|
|
||||||
|
|
||||||
--broken! doesn't properly reflect values.
|
--broken! doesn't properly reflect values.
|
||||||
function gun_default:get_pos(added_pos, relative, debug)
|
function gun_default:get_pos(added_pos, relative, debug)
|
||||||
assert(self.instance, "attempt to call object method on a class")
|
assert(self.instance, "attempt to call object method on a class")
|
||||||
@ -742,7 +780,36 @@ function gun_default:clear_animation()
|
|||||||
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)
|
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
|
||||||
end
|
end
|
||||||
|
local function adjust_gain(tbl, v)
|
||||||
|
v = tbl.third_person_gain_multiplier or v
|
||||||
|
for i = 1, #tbl do
|
||||||
|
adjust_gain(tbl[i], v)
|
||||||
|
end
|
||||||
|
if tbl.gain and (tbl.split_audio_by_perspective~=false) then
|
||||||
|
if type(tbl.gain) == "number" then
|
||||||
|
tbl.gain = tbl.gain*v
|
||||||
|
else
|
||||||
|
tbl.gain.min = tbl.gain.min*v
|
||||||
|
tbl.gain.max = tbl.gain.max*v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
function gun_default:play_sounds(sound)
|
||||||
|
local thpson_sound = Guns4d.table.deep_copy(sound)
|
||||||
|
local fsprsn_sound = Guns4d.table.deep_copy(sound)
|
||||||
|
|
||||||
|
thpson_sound.pos = self.pos
|
||||||
|
thpson_sound.player = self.player
|
||||||
|
thpson_sound.exclude_player = self.player
|
||||||
|
adjust_gain(thpson_sound, self.consts.THIRD_PERSON_GAIN_MULTIPLIER)
|
||||||
|
|
||||||
|
fsprsn_sound.player = self.player
|
||||||
|
fsprsn_sound.to_player = "from_player"
|
||||||
|
|
||||||
|
return Guns4d.play_sounds(thpson_sound), Guns4d.play_sounds(fsprsn_sound)
|
||||||
|
end
|
||||||
function gun_default:update_breathing(dt)
|
function gun_default:update_breathing(dt)
|
||||||
|
assert(self.instance)
|
||||||
local breathing_info = {pause=1.4, rate=4.2}
|
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-
|
--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.
|
--"length" (aka rate-pause), thus it will pi/length or pi/(rate-pause) will represent out slope of our control.
|
||||||
|
@ -63,12 +63,12 @@ function Sprite_scope:update()
|
|||||||
dir = dir + (self.gun.properties.ads.offset+vector.new(self.gun.properties.ads.horizontal_offset,0,0))*0
|
dir = dir + (self.gun.properties.ads.offset+vector.new(self.gun.properties.ads.horizontal_offset,0,0))*0
|
||||||
end
|
end
|
||||||
local fov = self.player:get_fov()
|
local fov = self.player:get_fov()
|
||||||
local real_aim = Guns4d.rltv_point_to_hud(dir, fov, ratio)
|
local real_aim = Guns4d.math.rltv_point_to_hud(dir, fov, ratio)
|
||||||
local anim_aim = Guns4d.rltv_point_to_hud(vector.rotate({x=0,y=0,z=1}, self.gun.animation_rotation*math.pi/180), fov, ratio)
|
local anim_aim = Guns4d.math.rltv_point_to_hud(vector.rotate({x=0,y=0,z=1}, self.gun.animation_rotation*math.pi/180), fov, ratio)
|
||||||
real_aim.x = real_aim.x+anim_aim.x; real_aim.y = real_aim.y+anim_aim.y
|
real_aim.x = real_aim.x+anim_aim.x; real_aim.y = real_aim.y+anim_aim.y
|
||||||
|
|
||||||
--print(dump(self.gun.animation_rotation))
|
--print(dump(self.gun.animation_rotation))
|
||||||
local paxial_aim = Guns4d.rltv_point_to_hud(self.gun.local_paxial_dir, fov, ratio)
|
local paxial_aim = Guns4d.math.rltv_point_to_hud(self.gun.local_paxial_dir, fov, ratio)
|
||||||
--so custom scopes can do their thing without doing more calcs
|
--so custom scopes can do their thing without doing more calcs
|
||||||
self.hud_projection_real = real_aim
|
self.hud_projection_real = real_aim
|
||||||
self.hud_projection_paxial = paxial_aim
|
self.hud_projection_paxial = paxial_aim
|
||||||
|
@ -198,46 +198,60 @@ local function initialize_b3d_animation_data(self, props)
|
|||||||
end]]
|
end]]
|
||||||
--print()
|
--print()
|
||||||
end
|
end
|
||||||
local function complete_item(self, props)
|
local function reregister_item(self, props)
|
||||||
assert(self.itemstring, "no itemstring provided. Cannot create a gun without an associated itemstring.")
|
assert(self.itemstring, "no itemstring provided. Cannot create a gun without an associated itemstring.")
|
||||||
local item_def = minetest.registered_items[self.itemstring]
|
local item_def = minetest.registered_items[self.itemstring]
|
||||||
assert(rawget(self, "name"), "no name provided in new class")
|
assert(rawget(self, "name"), "no name provided in new class")
|
||||||
assert(rawget(self, "itemstring"), "no itemstring provided in new class")
|
assert(rawget(self, "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(props.ammo.capacity or props.ammo.magazine_only, "gun does not accept magazines, but has no set capcity! Please define ammo.capacity")
|
||||||
assert(item_def, self.itemstring.." : item is not registered.")
|
assert(item_def, self.itemstring.." : item is not registered.")
|
||||||
|
|
||||||
--override methods so control handler can do it's job
|
--override methods so control handler can do it's job
|
||||||
local old_on_use = item_def.on_use
|
local old_on_use = item_def.on_use
|
||||||
local old_on_s_use = item_def.on_secondary_use
|
local old_on_s_use = item_def.on_secondary_use
|
||||||
|
local old_on_drop = item_def.on_drop
|
||||||
self.properties.inventory_image = item_def.inventory_image
|
self.properties.inventory_image = item_def.inventory_image
|
||||||
--override the item to hook in controls. (on_drop needed)
|
--override the item to hook in controls. (on_drop needed)
|
||||||
minetest.override_item(self.itemstring, {
|
minetest.override_item(self.itemstring, {
|
||||||
on_use = function(itemstack, user, pointed_thing)
|
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()].control_handler:on_use(itemstack, pointed_thing)
|
Guns4d.players[user:get_player_name()].control_handler:on_use(itemstack, pointed_thing)
|
||||||
|
if old_on_use then
|
||||||
|
return old_on_use(itemstack, user, pointed_thing)
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
on_secondary_use = function(itemstack, user, pointed_thing)
|
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()].control_handler:on_secondary_use(itemstack, pointed_thing)
|
Guns4d.players[user:get_player_name()].control_handler:on_secondary_use(itemstack, pointed_thing)
|
||||||
|
if old_on_s_use then
|
||||||
|
return old_on_s_use(itemstack, user, pointed_thing)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_drop = function(itemstack, user, pos)
|
||||||
|
local cancel_drop = Guns4d.players[user:get_player_name()].control_handler:on_drop(itemstack)
|
||||||
|
if (not cancel_drop) and old_on_drop then
|
||||||
|
return old_on_drop(itemstack, user, pos)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
Guns4d.register_item(self.itemstring, {
|
||||||
|
collisionbox = self.consts.ITEM_COLLISIONBOX,
|
||||||
|
selectionbox = self.consts.ITEM_SELECTIONBOX,
|
||||||
|
mesh = self.properties.visuals.mesh,
|
||||||
|
textures = self.properties.visuals.textures,
|
||||||
|
animation = self.properties.visuals.animations.loaded
|
||||||
|
})
|
||||||
end
|
end
|
||||||
local function create_visual_entity(self,props)
|
local function create_visual_entity(def, props)
|
||||||
minetest.register_entity(self.name.."_visual", {
|
minetest.register_entity(def.name.."_visual", {
|
||||||
initial_properties = {
|
initial_properties = {
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = props.visuals.mesh,
|
mesh = props.visuals.mesh,
|
||||||
textures = props.textures,
|
textures = props.visuals.textures,
|
||||||
glow = 0,
|
glow = 0,
|
||||||
pointable = false,
|
pointable = false,
|
||||||
static_save = false,
|
static_save = false,
|
||||||
backface_culling = props.visuals.backface_culling
|
backface_culling = props.visuals.backface_culling
|
||||||
},
|
},
|
||||||
on_step = function(self, dtime)
|
on_step = --[[def.entity_function or]] function(self)
|
||||||
local obj = self.object
|
local obj = self.object
|
||||||
if not self.parent_player then obj:remove() return end
|
if not self.parent_player then obj:remove() return end
|
||||||
local player = self.parent_player
|
local player = self.parent_player
|
||||||
@ -258,11 +272,11 @@ local function create_visual_entity(self,props)
|
|||||||
end
|
end
|
||||||
if handler.control_handler.ads then
|
if handler.control_handler.ads then
|
||||||
local normal_pos = (props.ads.offset)*10
|
local normal_pos = (props.ads.offset)*10
|
||||||
obj:set_attach(player, lua_object.consts.AIMING_BONE, normal_pos, -axial_rot, visibility)
|
obj:set_attach(player, handler.player_model_handler.bone_names.aim, normal_pos, -axial_rot, visibility)
|
||||||
else
|
else
|
||||||
local normal_pos = vector.new(props.hip.offset)*10
|
local normal_pos = vector.new(props.hip.offset)*10
|
||||||
-- vector.multiply({x=normal_pos.x, y=normal_pos.z, z=-normal_pos.y}, 10)
|
-- vector.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)
|
obj:set_attach(player, handler.player_model_handler.bone_names.hipfire, normal_pos, -axial_rot, visibility)
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
})
|
})
|
||||||
@ -282,7 +296,7 @@ function gun_default:construct_base_class()
|
|||||||
|
|
||||||
-- if it's not a template, then create an item, override some props
|
-- if it's not a template, then create an item, override some props
|
||||||
if self.name ~= "__template" then
|
if self.name ~= "__template" then
|
||||||
complete_item(self, props)
|
reregister_item(self, props)
|
||||||
end
|
end
|
||||||
--create sets. This may need to be put in instances of modifications can change accepted ammos
|
--create sets. This may need to be put in instances of modifications can change accepted ammos
|
||||||
self.accepted_bullets = {}
|
self.accepted_bullets = {}
|
||||||
|
@ -67,7 +67,7 @@ end
|
|||||||
local reload_actions = {}
|
local reload_actions = {}
|
||||||
function Guns4d.default_controls.register_reloading_state_type(name, def)
|
function Guns4d.default_controls.register_reloading_state_type(name, def)
|
||||||
assert(type(def)=="table", "improper definition type")
|
assert(type(def)=="table", "improper definition type")
|
||||||
assert(type(def.on_completion)=="function", "action has no completion function")
|
assert(type(def.on_completion)=="function", "action has no completion function") --return a bool (or nil) indicating wether to progress. Nil returns the function (breaking out of the reload cycle.)
|
||||||
assert(type(def.validation_check)=="function") --return bool indicating wether it is valid. If nil it is assumed to be valid
|
assert(type(def.validation_check)=="function") --return bool indicating wether it is valid. If nil it is assumed to be valid
|
||||||
reload_actions[name] = def
|
reload_actions[name] = def
|
||||||
end
|
end
|
||||||
@ -92,21 +92,21 @@ reg_mstate("unload_mag", {
|
|||||||
|
|
||||||
reg_mstate("store", {
|
reg_mstate("store", {
|
||||||
on_completion = function(gun, ammo_handler, next_state)
|
on_completion = function(gun, ammo_handler, next_state)
|
||||||
local pause = false
|
--[[local pause = false
|
||||||
--needs to happen before so we don't detect the ammo we just unloaded
|
--needs to happen before so we don't detect the ammo we just unloaded
|
||||||
if not ammo_handler:inventory_has_ammo() then
|
if not ammo_handler:inventory_has_ammo() then
|
||||||
pause=true
|
pause=true
|
||||||
end
|
end]]
|
||||||
if gun.properties.ammo.magazine_only and (ammo_handler.ammo.loaded_mag ~= "empty") then
|
if gun.properties.ammo.magazine_only and (ammo_handler.ammo.loaded_mag ~= "empty") then
|
||||||
ammo_handler:unload_magazine()
|
ammo_handler:unload_magazine()
|
||||||
else
|
else
|
||||||
ammo_handler:unload_all()
|
ammo_handler:unload_all()
|
||||||
end
|
end
|
||||||
--if there's no ammo make hold so you don't reload the same ammo you just unloaded.
|
--if there's no ammo make hold so you don't reload the same ammo you just unloaded.
|
||||||
if pause then
|
--[[if pause then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
return true
|
return true]]
|
||||||
end,
|
end,
|
||||||
validation_check = function(gun, ammo_handler, next_state)
|
validation_check = function(gun, ammo_handler, next_state)
|
||||||
if gun.properties.ammo.magazine_only and (ammo_handler.ammo.loaded_mag == "empty") then
|
if gun.properties.ammo.magazine_only and (ammo_handler.ammo.loaded_mag == "empty") then
|
||||||
@ -150,7 +150,19 @@ reg_mstate("load", {
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
|
reg_mstate("load_cartridge", {
|
||||||
|
on_completion = function(gun, ammo_handler, next_state)
|
||||||
|
return not ammo_handler:load_single_cartridge() --it returns wether the cartidge could be loaded
|
||||||
|
end,
|
||||||
|
validation_check = function(gun, ammo_handler, next_state)
|
||||||
|
if (ammo_handler.ammo.total_bullets<gun.properties.ammo.capacity) and ammo_handler:inventory_has_ammo(true) then
|
||||||
|
minetest.chat_send_all(dump((ammo_handler:inventory_has_ammo(true))))
|
||||||
|
return true
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
})
|
||||||
reg_mstate("charge", {
|
reg_mstate("charge", {
|
||||||
on_completion = function(gun, ammo_handler)
|
on_completion = function(gun, ammo_handler)
|
||||||
ammo_handler:chamber_round()
|
ammo_handler:chamber_round()
|
||||||
@ -159,9 +171,10 @@ reg_mstate("charge", {
|
|||||||
validation_check = function(gun, ammo_handler, next_state)
|
validation_check = function(gun, ammo_handler, next_state)
|
||||||
if (ammo_handler.ammo.next_bullet ~= "empty") or (ammo_handler.ammo.total_bullets == 0) then
|
if (ammo_handler.ammo.next_bullet ~= "empty") or (ammo_handler.ammo.total_bullets == 0) then
|
||||||
return false
|
return false
|
||||||
end
|
else
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
end
|
||||||
})
|
})
|
||||||
Guns4d.default_controls.reload = {
|
Guns4d.default_controls.reload = {
|
||||||
conditions = {"zoom"},
|
conditions = {"zoom"},
|
||||||
@ -259,11 +272,10 @@ Guns4d.default_controls.reload = {
|
|||||||
if type(next_state.sounds) == "table" then
|
if type(next_state.sounds) == "table" then
|
||||||
sounds = Guns4d.table.deep_copy(props.reload[next_state_index].sounds)
|
sounds = Guns4d.table.deep_copy(props.reload[next_state_index].sounds)
|
||||||
elseif type(next_state.sounds) == "string" then
|
elseif type(next_state.sounds) == "string" then
|
||||||
sounds = assert(props.sounds[next_state.sounds])
|
sounds = Guns4d.table.deep_copy(assert(props.sounds[next_state.sounds], "no sound by the name of "..next_state.sounds))
|
||||||
end
|
end
|
||||||
sounds.pos = gun.pos
|
sounds.pos = gun.pos
|
||||||
sounds.max_hear_distance = sounds.max_hear_distance or gun.consts.DEFAULT_MAX_HEAR_DISTANCE
|
data.played_sounds = {gun:play_sounds(sounds)}
|
||||||
data.played_sounds = Guns4d.play_sounds(sounds)
|
|
||||||
end
|
end
|
||||||
--print(dump(next_state_index))
|
--print(dump(next_state_index))
|
||||||
--end
|
--end
|
||||||
|
10
init.lua
10
init.lua
@ -11,7 +11,7 @@ Guns4d.config = {
|
|||||||
show_gun_inv_ammo_count = true,
|
show_gun_inv_ammo_count = true,
|
||||||
control_hybrid_toggle_threshold = .3,
|
control_hybrid_toggle_threshold = .3,
|
||||||
control_held_toggle_threshold = 0,
|
control_held_toggle_threshold = 0,
|
||||||
empty_symbol = "0e",
|
empty_symbol = "E",
|
||||||
default_damage_group = "fleshy",
|
default_damage_group = "fleshy",
|
||||||
infinite_ammo_priv = "guns4d_infinite_ammo",
|
infinite_ammo_priv = "guns4d_infinite_ammo",
|
||||||
interpret_initial_wear_as_ammo = false,
|
interpret_initial_wear_as_ammo = false,
|
||||||
@ -23,15 +23,19 @@ Guns4d.config = {
|
|||||||
headshot_damage_factor = 1.75,
|
headshot_damage_factor = 1.75,
|
||||||
enable_touchscreen_command_name = "guns4d_enable_touchmode",
|
enable_touchscreen_command_name = "guns4d_enable_touchmode",
|
||||||
minimum_supersonic_energy_assumption = 900, --used to determine the energy of a "supersonic" bullet for bullet whizzing sound effects
|
minimum_supersonic_energy_assumption = 900, --used to determine the energy of a "supersonic" bullet for bullet whizzing sound effects
|
||||||
|
default_audio_attenuation_rate = .8, --changes the dropoff rate of sound. Acts as a multiplier for the distance used to calculate inverse square law. Most guns (from the gun packs) set their own, so this is mainly for reloads.
|
||||||
mix_supersonic_and_subsonic_sounds = true,
|
mix_supersonic_and_subsonic_sounds = true,
|
||||||
default_pass_sound_mixing_factor = 10,
|
default_pass_sound_mixing_factor = 10,
|
||||||
|
third_person_gain_multiplier = 1/3,
|
||||||
default_penetration_iteration_distance = .25,
|
default_penetration_iteration_distance = .25,
|
||||||
|
maximum_bullet_holes = 20,
|
||||||
|
--enable_assert = false,
|
||||||
|
realistic_items = true
|
||||||
--`["official_content.replace_ads_with_bloom"] = false,
|
--`["official_content.replace_ads_with_bloom"] = false,
|
||||||
--`["official_content.uses_magazines"] = true
|
--`["official_content.uses_magazines"] = true
|
||||||
}
|
}
|
||||||
local path = minetest.get_modpath("guns4d")
|
local path = minetest.get_modpath("guns4d")
|
||||||
|
|
||||||
print("file read?")
|
|
||||||
local conf = Settings(path.."/guns4d_settings.conf"):to_table() or {}
|
local conf = Settings(path.."/guns4d_settings.conf"):to_table() or {}
|
||||||
local mt_conf = minetest.settings:to_table() --allow use of MT config for servers that regularly update 4dguns through it's development
|
local mt_conf = minetest.settings:to_table() --allow use of MT config for servers that regularly update 4dguns through it's development
|
||||||
for i, v in pairs(Guns4d.config) do
|
for i, v in pairs(Guns4d.config) do
|
||||||
@ -44,6 +48,7 @@ for i, v in pairs(Guns4d.config) do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
dofile(path.."/infinite_ammo.lua")
|
dofile(path.."/infinite_ammo.lua")
|
||||||
dofile(path.."/misc_helpers.lua")
|
dofile(path.."/misc_helpers.lua")
|
||||||
dofile(path.."/item_entities.lua")
|
dofile(path.."/item_entities.lua")
|
||||||
@ -55,6 +60,7 @@ dofile(path.."/block_values.lua")
|
|||||||
dofile(path.."/ammo_api.lua")
|
dofile(path.."/ammo_api.lua")
|
||||||
path = path .. "/classes"
|
path = path .. "/classes"
|
||||||
dofile(path.."/Instantiatable_class.lua")
|
dofile(path.."/Instantiatable_class.lua")
|
||||||
|
dofile(path.."/Bullet_hole.lua")
|
||||||
dofile(path.."/Bullet_ray.lua")
|
dofile(path.."/Bullet_ray.lua")
|
||||||
dofile(path.."/Control_handler.lua")
|
dofile(path.."/Control_handler.lua")
|
||||||
dofile(path.."/Ammo_handler.lua")
|
dofile(path.."/Ammo_handler.lua")
|
||||||
|
@ -33,7 +33,11 @@ local defaults = {
|
|||||||
--light_source = 0,
|
--light_source = 0,
|
||||||
collisionbox_size = 2,
|
collisionbox_size = 2,
|
||||||
visual_size = 1,
|
visual_size = 1,
|
||||||
offset = {x=0,y=0,z=0}
|
realistic = Guns4d.config.realistic_items,
|
||||||
|
backface_culling = false,
|
||||||
|
--animation = {x=0,y=0, speed=15, loop=true, blend=nil},
|
||||||
|
selectionbox = {-.2,-.2,-.2, .2,.2,.2},
|
||||||
|
collisionbox = (Guns4d.config.realistic_items and {-.2,-.05,-.2, .2,.15,.2}) or {-.2,-.2,-.2, .2,.2,.2}
|
||||||
}
|
}
|
||||||
--- replaces the item entity of the provided item with a 3d entity based on the definition
|
--- replaces the item entity of the provided item with a 3d entity based on the definition
|
||||||
-- @param itemstring
|
-- @param itemstring
|
||||||
@ -43,10 +47,6 @@ function Guns4d.register_item(itemstring, def)
|
|||||||
assert(minetest.registered_items[itemstring], "item: `"..tostring(itemstring).."` not registered by minetest")
|
assert(minetest.registered_items[itemstring], "item: `"..tostring(itemstring).."` not registered by minetest")
|
||||||
assert(type(def)=="table", "definition is not a table")
|
assert(type(def)=="table", "definition is not a table")
|
||||||
def = Guns4d.table.fill(defaults, def)
|
def = Guns4d.table.fill(defaults, def)
|
||||||
if not def.selectionbox then
|
|
||||||
def.selectionbox = vector.new(def.collisionbox_size, def.collisionbox_size, def.collisionbox_size)
|
|
||||||
end
|
|
||||||
def.offset = vector.new(def.offset)
|
|
||||||
Guns4d.registered_items[itemstring] = def
|
Guns4d.registered_items[itemstring] = def
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -63,60 +63,80 @@ def.set_item = function(self, item)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local item_def = Guns4d.registered_items[stack:get_name()]
|
local item_def = Guns4d.registered_items[stack:get_name()]
|
||||||
local cbox
|
--[[local a = item_def.collisionbox_size
|
||||||
local sbox
|
local o = item_def.collisionbox_offset
|
||||||
local a = item_def.collisionbox_size
|
|
||||||
local b = item_def.selectionbox
|
local b = item_def.selectionbox
|
||||||
if item_def.realistic == true then
|
if item_def.realistic == true then
|
||||||
cbox = {-a/20, 0, -a/20, a/20, (a*2)/20, a/20} --we want the collision_box to sit above it.
|
cbox = {(-a-o.x)/20, 0-(o.y/20), (-a-o.z)/20, (a-o.x)/20, (a-o.y)/10, (a-o.z)/20} --we want the collision_box to sit above it.
|
||||||
sbox = {-b.x/20, 0, -b.z/20, b.x/20, b.y/10, b.z/20, rotate=true}
|
sbox = {(-b.x-o.x)/20, (-b.y/20), (-b.z-o.z)/20, (b.x-o.x)/20, (b.y/20), (b.z-o.z)/20, rotate=true}
|
||||||
else
|
else
|
||||||
cbox = {-a/20, -a/20, -a/20, a/20, a/20, a/20}
|
cbox = {(-a-o.x)/20, (-a-o.y)/20, (-a-o.z)/20, (a-o.x)/20, (a-o.y)/20, (a-o.z)/20}
|
||||||
sbox = {-b.x/20, -b.y/20, -b.z/20, b.x/20, b.y/20, b.z/20}
|
sbox = {(-b.x-o.x)/20, (-b.y-o.y)/20, (-b.z-o.z)/20, (b.x-o.x)/20, (b.y-o.y)/20, (b.z-o.z)/20}
|
||||||
end
|
end]]
|
||||||
|
local cbox = item_def.collisionbox
|
||||||
|
local sbox = item_def.selectionbox
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
is_visible = true,
|
is_visible = true,
|
||||||
visual = "mesh",
|
visual = "mesh",
|
||||||
mesh = item_def.mesh,
|
mesh = item_def.mesh,
|
||||||
textures = item_def.textures,
|
textures = item_def.textures,
|
||||||
collisionbox = cbox,
|
collisionbox = cbox,
|
||||||
selectionbox = sbox,
|
selectionbox = {sbox[1], sbox[2], sbox[3], sbox[4], sbox[5], sbox[6], rotate=true},
|
||||||
glow = item_def and item_def.light_source and math.floor(def.light_source/2+0.5),
|
glow = item_def and item_def.light_source and math.floor(def.light_source/2+0.5),
|
||||||
|
backface_culling = item_def.backface_culling,
|
||||||
visual_size = {x=item_def.visual_size,y=item_def.visual_size,z=item_def.visual_size},
|
visual_size = {x=item_def.visual_size,y=item_def.visual_size,z=item_def.visual_size},
|
||||||
automatic_rotate = (not item_def.realistic) and math.pi * 0.5 * 0.2 / a,
|
automatic_rotate = ((not item_def.realistic) and math.pi * 0.5 * 0.2 / 5) or nil,
|
||||||
infotext = stack:get_description(),
|
infotext = stack:get_description(),
|
||||||
})
|
})
|
||||||
self._collisionbox = cbox
|
--self._collisionbox = cbox
|
||||||
end
|
end
|
||||||
local old = def.on_step
|
local old = def.on_step
|
||||||
|
def._respawn = function(self)
|
||||||
|
minetest.add_item(self.object:get_pos(), self.itemstring)
|
||||||
|
end
|
||||||
def.on_step = function(self, dt, mr, ...)
|
def.on_step = function(self, dt, mr, ...)
|
||||||
old(self, dt, mr, ...)
|
old(self, dt, mr, ...)
|
||||||
--icky nesting.
|
--icky nesting.
|
||||||
|
local item_def
|
||||||
|
if not self._guns4d_animation_set then
|
||||||
|
item_def = Guns4d.registered_items[ItemStack(self.itemstring):get_name()]
|
||||||
|
if item_def then
|
||||||
|
local anim = item_def.animation
|
||||||
|
if anim then
|
||||||
|
self.object:set_animation({x=anim.x, y=anim.y}, anim.speed, anim.blend, anim.loop)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
self:_respawn()
|
||||||
|
end
|
||||||
|
end
|
||||||
if mr and mr.touching_ground then
|
if mr and mr.touching_ground then
|
||||||
local item_def = Guns4d.registered_items[ItemStack(self.itemstring):get_name()]
|
item_def = item_def or Guns4d.registered_items[ItemStack(self.itemstring):get_name()]
|
||||||
if item_def and not self._rotated then
|
if item_def and not self._4dguns_rotated then
|
||||||
if item_def.realistic then
|
if item_def.realistic then
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
automatic_rotate = (not item_def.realistic) and math.pi * 0.5 * 0.2 / item_def.visual_size,
|
automatic_rotate = nil
|
||||||
})
|
})
|
||||||
local rot = self.object:get_rotation()
|
local rot = self.object:get_rotation()
|
||||||
self.object:set_rotation({y=rot.y, x=rot.x+(math.pi/2), z=0})
|
self.object:set_rotation({y=rot.y, x=rot.x, z=math.pi*.5})
|
||||||
self._rotated = true
|
self._4dguns_rotated = true
|
||||||
else
|
else
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
automatic_rotate = (not item_def.realistic) and math.pi * 0.5 * 0.2 / item_def.visual_size,
|
automatic_rotate = math.pi * 0.5 * 0.2 / item_def.visual_size,
|
||||||
})
|
})
|
||||||
local rot = self.object:get_rotation()
|
local rot = self.object:get_rotation()
|
||||||
self.object:set_rotation({y=rot.y, x=0, z=0})
|
self.object:set_rotation({y=rot.y, x=0, z=0})
|
||||||
self._rotated = true
|
self._4dguns_rotated = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if not item_def then
|
||||||
|
self:_respawn()
|
||||||
|
end
|
||||||
else
|
else
|
||||||
if self._rotated then
|
if self._4dguns_rotated then
|
||||||
self.object:set_properties({
|
self.object:set_properties({
|
||||||
automatic_rotate = 0,
|
automatic_rotate = 0,
|
||||||
})
|
})
|
||||||
self._rotated = false
|
self._4dguns_rotated = false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -266,7 +266,7 @@ end
|
|||||||
--for the following function only:
|
--for the following function only:
|
||||||
--for license see the link on the next line (direct permission was granted).
|
--for license see the link on the next line (direct permission was granted).
|
||||||
--https://github.com/3dreamengine/3DreamEngine
|
--https://github.com/3dreamengine/3DreamEngine
|
||||||
function Guns4d.rltv_point_to_hud(pos, fov, aspect)
|
function Guns4d.math.rltv_point_to_hud(pos, fov, aspect)
|
||||||
local n = .1 --near
|
local n = .1 --near
|
||||||
local f = 1000 --far
|
local f = 1000 --far
|
||||||
local scale = math.tan(fov * math.pi / 360)
|
local scale = math.tan(fov * math.pi / 360)
|
||||||
@ -287,7 +287,7 @@ end
|
|||||||
|
|
||||||
--Code: Elkien3 (CC BY-SA 3.0)
|
--Code: Elkien3 (CC BY-SA 3.0)
|
||||||
--https://github.com/Elkien3/spriteguns/blob/1c632fe12c35c840d6c0b8307c76d4dfa44d1bd7/init.lua#L76
|
--https://github.com/Elkien3/spriteguns/blob/1c632fe12c35c840d6c0b8307c76d4dfa44d1bd7/init.lua#L76
|
||||||
function Guns4d.nearest_point_on_line(lineStart, lineEnd, pnt)
|
function Guns4d.math.nearest_point_on_line(lineStart, lineEnd, pnt)
|
||||||
local line = vector.subtract(lineEnd, lineStart)
|
local line = vector.subtract(lineEnd, lineStart)
|
||||||
local len = vector.length(line)
|
local len = vector.length(line)
|
||||||
line = vector.normalize(line)
|
line = vector.normalize(line)
|
||||||
@ -297,3 +297,33 @@ function Guns4d.nearest_point_on_line(lineStart, lineEnd, pnt)
|
|||||||
d = Guns4d.math.clamp(d, 0, len);
|
d = Guns4d.math.clamp(d, 0, len);
|
||||||
return vector.add(lineStart, vector.multiply(line, d))
|
return vector.add(lineStart, vector.multiply(line, d))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Guns4d.math.rand_box_muller(deviation)
|
||||||
|
local tau = math.pi*2
|
||||||
|
--our first value cant be 0
|
||||||
|
math.randomseed(math.random())
|
||||||
|
local r1 = 0
|
||||||
|
while r1 == 0 do r1=math.random() end
|
||||||
|
local r2=math.random()
|
||||||
|
print(r1, r2)
|
||||||
|
|
||||||
|
local a = deviation * math.sqrt(-2.0*math.log(r1))
|
||||||
|
return a * math.cos(tau * r1), a * math.sin(tau * r2);
|
||||||
|
end
|
||||||
|
local e = 2.7182818284590452353602874713527 --I don't know how to find it otherwise...
|
||||||
|
--deviation just changes the distribution, range is the maximum spread
|
||||||
|
function Guns4d.math.angular_normal_distribution(deviation)
|
||||||
|
local x=math.random()
|
||||||
|
--print(x)
|
||||||
|
--positive only normal distribution
|
||||||
|
local a = (1/(deviation*math.sqrt(2*math.pi)))
|
||||||
|
local exp = (-.5*(x/deviation)^2)
|
||||||
|
local exp_x_1 = (-.5*(1/deviation)^2) --exp value where x=1
|
||||||
|
local y=( (a*e^exp) - (a*e^exp_x_1) )/( a - (a*e^exp_x_1) ) --subtraction is to bring the value of x=1 to 0 on the curve and the division is to keep it normalized to an output of one
|
||||||
|
print(y)
|
||||||
|
local theta = math.random()*math.pi*2
|
||||||
|
return y*math.cos(theta), y*math.sin(theta)
|
||||||
|
end
|
||||||
|
function Guns4d.math.round(n)
|
||||||
|
return (n-math.floor(n)<.5 and math.floor(n)) or math.ceil(n)
|
||||||
|
end
|
2
mod.conf
2
mod.conf
@ -3,4 +3,4 @@ title = guns4d
|
|||||||
description = Adds a library for 3d guns
|
description = Adds a library for 3d guns
|
||||||
author = FatalError42O
|
author = FatalError42O
|
||||||
depends = mtul_b3d, mtul_cpml, mtul_filesystem
|
depends = mtul_b3d, mtul_cpml, mtul_filesystem
|
||||||
optional_depends = spriteguns
|
optional_depends = spriteguns, sprint
|
@ -35,7 +35,9 @@ local sqrt = math.sqrt
|
|||||||
-- @field to_player 4dguns changes `to_player` so it only plays positionless audio (as it is only intended for first person audio). If set to string "from_player" and player present
|
-- @field to_player 4dguns changes `to_player` so it only plays positionless audio (as it is only intended for first person audio). If set to string "from_player" and player present
|
||||||
-- @field player this is so to_player being set to "from_player". It's to be set to the player which fired the weapon.
|
-- @field player this is so to_player being set to "from_player". It's to be set to the player which fired the weapon.
|
||||||
-- @field delay delay the playing of the sound
|
-- @field delay delay the playing of the sound
|
||||||
-- @field has_speed_of_sound = true
|
-- @field attenuation_rate float the rate of dropoff for a sound. I figure this is a bit more intuitive then jacking the gain up super high for every sound... Set the default in config.
|
||||||
|
-- @field split_audio_by_perspective true [GUN CLASS SPECIFIC] tells the gun wether to split into third and first person (positionless) audio and adjust gain.
|
||||||
|
-- @field third_person_gain_multiplier float [GUN CLASS SPECIFIC] replaces the constant/config value "third_person_gain_multiplier/THIRD_PERSON_GAIN_MULTIPLIER".
|
||||||
-- @table guns4d_soundspec
|
-- @table guns4d_soundspec
|
||||||
|
|
||||||
local function handle_min_max(tbl)
|
local function handle_min_max(tbl)
|
||||||
@ -84,12 +86,11 @@ function Guns4d.play_sounds(soundspecs_list)
|
|||||||
end
|
end
|
||||||
local handle = #sound_handles+1 --determine the sound handle before playing
|
local handle = #sound_handles+1 --determine the sound handle before playing
|
||||||
sound_handles[handle] = {}
|
sound_handles[handle] = {}
|
||||||
local handle_object = sound_handles[handle]
|
--local handle_object = sound_handles[handle]
|
||||||
for arg, soundspec in pairs(soundspecs_list) do
|
for arg, soundspec in pairs(soundspecs_list) do
|
||||||
if soundspec.to_player == "from_player" then soundspec.to_player = soundspec.player:get_player_name() end --setter of sound may not have access to this info, so add a method to use it.
|
if soundspec.to_player == "from_player" then soundspec.to_player = soundspec.player:get_player_name() end --setter of sound may not have access to this info, so add a method to use it.
|
||||||
assert(not (soundspec.to_player and soundspec.min_distance), "in argument '"..tostring(arg).."' `min_distance` and `to_player` are incompatible parameters.")
|
assert(not (soundspec.to_player and soundspec.min_distance), "in argument '"..tostring(arg).."' `min_distance` and `to_player` are incompatible parameters.")
|
||||||
local sound = soundspec.sound
|
local sound = soundspec.sound
|
||||||
local outval
|
|
||||||
for i, v in pairs(soundspec) do
|
for i, v in pairs(soundspec) do
|
||||||
if type(v) == "table" and v.min then
|
if type(v) == "table" and v.min then
|
||||||
soundspec[i]=handle_min_max(v)
|
soundspec[i]=handle_min_max(v)
|
||||||
@ -102,29 +103,34 @@ function Guns4d.play_sounds(soundspecs_list)
|
|||||||
if not mtul.paths.media_paths[(sound or "[NIL]")..".ogg"] then
|
if not mtul.paths.media_paths[(sound or "[NIL]")..".ogg"] then
|
||||||
minetest.log("error", "no sound by the name `"..mtul.paths.media_paths[(sound or "[NIL]")..".ogg"].."`")
|
minetest.log("error", "no sound by the name `"..mtul.paths.media_paths[(sound or "[NIL]")..".ogg"].."`")
|
||||||
end
|
end
|
||||||
|
local exclude_player_ref = soundspec.exclude_player
|
||||||
|
if type(soundspec.exclude_player)=="string" then
|
||||||
|
exclude_player_ref = minetest.get_player_by_name(soundspec.exclude_player)
|
||||||
|
elseif soundspec.exclude_player then
|
||||||
|
exclude_player_ref = soundspec.exclude_player
|
||||||
|
soundspec.exclude_player = exclude_player_ref:get_player_name()
|
||||||
|
end
|
||||||
--print(dump(soundspecs_list), i)
|
--print(dump(soundspecs_list), i)
|
||||||
if soundspec.to_player then soundspec.pos = nil end
|
if soundspec.to_player then soundspec.pos = nil end
|
||||||
if soundspec.min_hear_distance then
|
|
||||||
local exclude_player_ref
|
|
||||||
if soundspec.exclude_player then
|
|
||||||
exclude_player_ref = minetest.get_player_by_name(soundspec.exclude_player)
|
|
||||||
end
|
|
||||||
--play sound for all players outside min hear distance
|
--play sound for all players outside min hear distance
|
||||||
for _, player in pairs(minetest.get_connected_players()) do
|
local original_gain = soundspec.gain or 1
|
||||||
|
local attenuation_rate = soundspec.attenuation_rate or Guns4d.config.default_audio_attenuation_rate
|
||||||
|
local player_list = ((not soundspec.to_player) and minetest.get_connected_players()) or {minetest.get_player_by_name(soundspec.to_player)}
|
||||||
|
for _, player in pairs(player_list) do
|
||||||
soundspec.sound = nil
|
soundspec.sound = nil
|
||||||
local pos = player:get_pos()
|
local pos = player:get_pos()
|
||||||
local dist = sqrt( sqrt((pos.x-soundspec.pos.x)^2+(pos.y-soundspec.pos.y)^2)^2 + (pos.z-soundspec.pos.z)^2)
|
local dist = 0
|
||||||
if (dist > soundspec.min_hear_distance) and (player~=exclude_player_ref) then
|
if soundspec.pos then
|
||||||
|
dist = sqrt( sqrt((pos.x-(soundspec.pos.x))^2+(pos.y-soundspec.pos.y)^2)^2 + (pos.z-soundspec.pos.z)^2)
|
||||||
|
end
|
||||||
|
if ((not soundspec.max_hear_distance) or (dist <= soundspec.max_hear_distance)) and ((not soundspec.min_hear_distance) or (dist > soundspec.min_hear_distance)) and (player~=exclude_player_ref) then
|
||||||
|
print(player:get_player_name(), dist, sound, (dist-(soundspec.min_hear_distance or 0))*attenuation_rate, soundspec.min_hear_distance)
|
||||||
soundspec.exclude_player = nil --not needed anyway because we can just not play it for this player.
|
soundspec.exclude_player = nil --not needed anyway because we can just not play it for this player.
|
||||||
soundspec.to_player = player:get_player_name()
|
soundspec.to_player = player:get_player_name()
|
||||||
|
soundspec.gain = original_gain/(Guns4d.math.clamp((dist-(soundspec.min_hear_distance or 0))*attenuation_rate, 1, math.huge)^2) --so i found out the hard way that it doesn't fucking reduce volume by distance if there's a to_player. Kind of pisses me off.
|
||||||
play_sound(sound, soundspec, handle, arg)
|
play_sound(sound, soundspec, handle, arg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
|
||||||
--print(dump(soundspec))
|
|
||||||
soundspec.sound = nil
|
|
||||||
play_sound(sound, soundspec, handle, arg)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return handle
|
return handle
|
||||||
end
|
end
|
||||||
|
@ -75,7 +75,7 @@ function Guns4d.effects.muzzle_flash(self)
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
function Guns4d.effects.spawn_bullet_hole_particle(pos, size, texture)
|
--[[function Guns4d.effects.spawn_bullet_hole_particle(pos, size, texture)
|
||||||
--modern syntax isn't accepted by add particle to my knowledge, or it's not documented.
|
--modern syntax isn't accepted by add particle to my knowledge, or it's not documented.
|
||||||
--so I have to use a particle spawner
|
--so I have to use a particle spawner
|
||||||
minetest.add_particlespawner({
|
minetest.add_particlespawner({
|
||||||
@ -155,4 +155,4 @@ minetest.register_entity("guns4d:bullet_hole", {
|
|||||||
self.object:set_properties(properties)
|
self.object:set_properties(properties)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
})
|
})]]
|
Loading…
x
Reference in New Issue
Block a user