finished Sprite_scopes (mostly)

This commit is contained in:
FatalErr42O 2023-08-08 13:43:36 -07:00
parent b3e91825a5
commit 00ea0ff13e
17 changed files with 787 additions and 177 deletions

15
classes/Bullet.lua Normal file
View File

@ -0,0 +1,15 @@
Bullet = Instantiatable_class:inherit({
registered = {},
range = 100,
force_mmRHA = 1,
dropoff_mmRHA = 0,
damage = 0,
itemstring = "",
construct = function(def)
assert(not def.instance, "attempt to create instance of a template")
assert(rawget(def, "itemstring"), "no string provided to new bullet template")
assert(minetest.registered_items[def.itemstring], "bullet item is not registered. Check dependencies?")
end
})

View File

@ -11,8 +11,6 @@ local ray = {
ITERATION_DISTANCE = .3,
damage = 0
}
function ray:validate_location()
end
function ray:record_state()
table.insert(self.history, {
@ -115,10 +113,6 @@ function ray:cast()
--set "last" values.
return pointed, next_penetration_val, next_state, end_pos, continue
end
function ray:apply_damage(obj)
local damage = math.floor((self.damage*(self.force_mmRHA/self.init_force_mmRHA))+1)
obj:punch(self.player, nil, {damage_groups = {fleshy = damage, penetration_mmRHA=self.force_mmRHA}}, self.dir)
end
function ray:iterate(initialized)
assert(self.instance, "attempt to call obj method on a class")
local pointed, penetration, next_state, end_pos, continue = self:cast()
@ -134,7 +128,7 @@ function ray:iterate(initialized)
if pointed.type == "node" then
self.last_node_name = minetest.get_node(pointed.under).name
elseif pointed.type == "object" then
ray:apply_damage(pointed.ref)
ray:hit_entity(pointed.ref)
end
end
table.insert(self.history, {
@ -149,7 +143,7 @@ function ray:iterate(initialized)
end
if not initialized then
for i, v in pairs(self.history) do
--[[local hud = self.player:hud_add({
local hud = self.player:hud_add({
hud_elem_type = "waypoint",
text = "mmRHA:"..tostring(math.floor(v.force_mmRHA or 0)).." ",
number = 255255255,
@ -161,7 +155,7 @@ function ray:iterate(initialized)
})
minetest.after(40, function(hud)
self.player:hud_remove(hud)
end, hud)]]
end, hud)
end
end
end
@ -174,6 +168,8 @@ function ray.construct(def)
assert(def.range, "no range")
assert(def.force_mmRHA, "no force")
assert(def.dropoff_mmRHA, "no force dropoff")
--assert(def.on_node_hit, "no node hit behavior")
assert(def.hit_entity, "no entity hit behavior")
def.init_force_mmRHA = def.force_mmRHA
def.dir = vector.new(def.dir)
def.pos = vector.new(def.pos)

View File

@ -21,44 +21,63 @@ local controls = Guns4d.control_handler
--[[-modify controls (future implementation if needed)
function controls.modify()
end]]
--this function always ends up a mess. I rewrote it here 2 times,
--and in 3dguns I rewrote it at least 3 times. It's always just...
--impossible to understand. So if you see ALOT of comments, that's why.
function controls:update(dt)
assert(self.instance, "attempt to call object method on a class")
self.player_pressed = self.player:get_player_control()
local pressed = self.player_pressed
local call_queue = {} --so I need to have a "call" queue so I can tell the functions the names of other active controls (busy_list)
local busy_list = {} --list of controls that have their conditions met
local busy_list = self.busy_list --list of controls that have their conditions met. Has to be reset at END of update, so on_use and on_secondary_use can be marked
for i, control in pairs(self.controls) do
local def = control
local data = control.data
local conditions_met = true
for _, key in pairs(control.conditions) do
if not pressed[key] then conditions_met = false break end
end
if not conditions_met then
busy_list[i] = true
data.held = false
--detect interrupts
if data.timer ~= def.timer then
table.insert(call_queue, {control=def, active=false, interrupt=true, data=data})
data.timer = def.timer
if not (i=="on_use") and not (i=="on_secondary_use") then
local def = control
local data = control.data
local conditions_met = true
--check no conditions are false
for _, key in pairs(control.conditions) do
if not pressed[key] then conditions_met = false break end
end
else
data.timer = data.timer - dt
--when time is over, if it wasnt held (or loop is active) then reset and call the function.
if data.timer <= 0 and ((not data.held) or def.loop) then
data.held = true
table.insert(call_queue, {control=def, active=true, interrupt=false, data=data})
elseif def.call_before_timer then
table.insert(call_queue, {control=def, active=false, interrupt=false, data=data})
if conditions_met then
data.timer = data.timer - dt
--when time is over, if it wasnt held (or loop is active) then reset and call the function.
--held indicates wether the function was called (as active) before last step.
if data.timer <= 0 and ((not data.held) or def.loop) then
data.held = true
table.insert(call_queue, {control=def, active=true, interrupt=false, data=data})
elseif def.call_before_timer then --this is useful for functionst that need to play animations for their progress.
table.insert(call_queue, {control=def, active=false, interrupt=false, data=data})
end
else
busy_list[i] = true
data.held = false
--detect interrupts, check if the timer was in progress
if data.timer ~= def.timer then
table.insert(call_queue, {control=def, active=false, interrupt=true, data=data})
data.timer = def.timer
end
end
end
end
local count = 0
for i, v in pairs(busy_list) do
count = count + 1
end
if count == 0 then busy_list = nil end --so funcs can quickly deduce if they can call
--busy list is so we can tell if a function should be allowed or not
if #busy_list == 0 then busy_list = nil end
for i, tbl in pairs(call_queue) do
tbl.control.func(tbl.active, tbl.interrupt, tbl.data, busy_list, Guns4d.players[self.player:get_player_name()].handler)
tbl.control.func(tbl.active, tbl.interrupt, tbl.data, busy_list, self.handler)
end
self.busy_list = {}
end
function controls:on_use(itemstack, pointed_thing)
assert(self.instance, "attempt to call object method on a class")
if self.controls.on_use then
self.controls.on_use(itemstack, self.handler, pointed_thing)
end
end
function controls:on_secondary_use(itemstack, pointed_thing)
assert(self.instance, "attempt to call object method on a class")
if self.controls.on_secondary_use then
self.controls.on_secondary_use(itemstack, self.handler, pointed_thing)
end
end
---@diagnostic disable-next-line: duplicate-set-field
@ -67,12 +86,16 @@ function controls.construct(def)
assert(def.controls, "no controls provided")
assert(def.player, "no player provided")
def.controls = table.deep_copy(def.controls)
def.busy_list = {}
def.handler = Guns4d.players[def.player:get_player_name()].handler
for i, control in pairs(def.controls) do
control.timer = control.timer or 0
control.data = {
timer = control.timer,
held = false
}
if not (i=="on_use") and not (i=="on_secondary_use") then
control.timer = control.timer or 0
control.data = {
timer = control.timer,
held = false
}
end
end
table.sort(def.controls, function(a,b)
return #a.conditions > #b.conditions

View File

@ -22,8 +22,8 @@ local gun = {
player_axial = 1,
},
angular_velocity_max = {
gun_axial = 2,
player_axial = 2,
gun_axial = 1,
player_axial = 1,
},
angular_velocity = {
gun_axial = {x=0, y=0},
@ -34,8 +34,8 @@ local gun = {
player_axial = {x=1, y=0},
},
target_correction_max_rate = { --the cap for time_since_fire*target_correction_factor
gun_axial = 10000,
player_axial = 10000,
gun_axial = 1,
player_axial = 1,
},
},
sway = {
@ -57,7 +57,7 @@ local gun = {
firerateRPM = 10000,
controls = {}
},
transforms = {
offsets = {
pos = Vec.new(),
player_rotation = Vec.new(),
dir = Vec.new(),
@ -99,6 +99,7 @@ local gun = {
--magic number BEGONE
consts = {
HIP_PLAYER_GUN_ROT_RATIO = .75,
AIM_OUT_AIM_IN_SPEED_RATIO = 2.5,
HIPFIRE_BONE = "guns3d_hipfire_bone",
AIMING_BONE = "guns3d_aiming_bone",
HAS_RECOIL = true,
@ -112,10 +113,11 @@ local gun = {
rechamber_time = 0,
muzzle_flash = Guns4d.muzzle_flash
}
function gun:fire()
assert(self.instance, "attempt to call object method on a class")
if self.rechamber_time <= 0 then
local dir = self:get_dir()
local dir = self.dir
local pos = self:get_pos()
Guns4d.bullet_ray:new({
player = self.player,
@ -124,13 +126,18 @@ function gun:fire()
range = 100,
gun = self,
force_mmRHA = 1,
dropoff_mmRHA = 0
dropoff_mmRHA = 0,
hit_entity = function(pointed)
local damage = math.floor((self.damage*(self.force_mmRHA/self.init_force_mmRHA))+1)
pointed.ref:punch(self.player, nil, {damage_groups = {fleshy = damage, penetration_mmRHA=self.force_mmRHA}}, self.dir)
end
})
self:recoil()
self:muzzle_flash()
self.rechamber_time = 60/self.properties.firerateRPM
end
end
function gun:recoil()
assert(self.instance, "attempt to call object method on a class")
for axis, recoil in pairs(self.velocities.recoil) do
@ -140,34 +147,37 @@ function gun:recoil()
end
self.time_since_last_fire = 0
end
function gun:get_dir(gun_relative)
assert(self.instance, "attempt to call object method on a class")
local player = self.player
local player_rotation
if gun_relative then
player_rotation = Vec.new(gun_relative)
else
player_rotation = Vec.new(self.transforms.player_rotation.x, self.transforms.player_rotation.y, 0)
end
local rotation = self.transforms.total_offset_rotation
local dir = Vec.new(Vec.rotate({x=0, y=0, z=1}, {y=0, x=((rotation.gun_axial.x+rotation.player_axial.x+player_rotation.x)*math.pi/180), z=0}))
dir = Vec.rotate(dir, {y=((rotation.gun_axial.y+rotation.player_axial.y+player_rotation.y)*math.pi/180), x=0, z=0})
--[[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})
if not false then
local hud = player:hud_add({
hud_elem_type = "image_waypoint",
text = "muzzle_flash2.png",
world_pos = hud_pos,
scale = {x=10, y=10},
alignment = {x=0,y=0},
offset = {x=0,y=0},
})
minetest.after(0, function(hud)
player:hud_remove(hud)
end, hud)
end]]
function gun:get_dir(rltv)
assert(self.instance, "attempt to call object method on a class")
local player = self.player
local player_rotation
if rltv then
player_rotation = Vec.new()
else
player_rotation = Vec.new(self.offsets.player_rotation.x, self.offsets.player_rotation.y, 0)
end
local rotation = self.offsets.total_offset_rotation
local dir = Vec.new(Vec.rotate({x=0, y=0, z=1}, {y=0, x=((rotation.gun_axial.x+rotation.player_axial.x+player_rotation.x)*math.pi/180), z=0}))
dir = Vec.rotate(dir, {y=((rotation.gun_axial.y+rotation.player_axial.y+player_rotation.y)*math.pi/180), x=0, z=0})
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})
if not false then
local hud = player:hud_add({
hud_elem_type = "image_waypoint",
text = "muzzle_flash2.png",
world_pos = hud_pos,
scale = {x=10, y=10},
alignment = {x=0,y=0},
offset = {x=0,y=0},
})
minetest.after(0, function(hud)
player:hud_remove(hud)
end, hud)
end
return dir
end
function gun:get_pos(added_pos)
assert(self.instance, "attempt to call object method on a class")
added_pos = Vec.new(added_pos)
@ -175,18 +185,18 @@ function gun:get_pos(added_pos)
local handler = self.handler
local bone_location = Vec.new(handler.model_handler.offsets.arm.right)/10
local gun_offset = Vec.new(self.properties.hip.offset)
local player_rotation = Vec.new(self.transforms.player_rotation.x, self.transforms.player_rotation.y, 0)
if handler.controls.ads then
local player_rotation = Vec.new(self.offsets.player_rotation.x, self.offsets.player_rotation.y, 0)
if handler.control_bools.ads then
gun_offset = self.properties.ads.offset
bone_location = Vec.new(0, handler:get_properties().eye_height, 0)+player:get_eye_offset()/10
else
--minetest is really wacky.
bone_location.x = -bone_location.x
player_rotation.x = self.transforms.player_rotation.x*self.consts.HIP_PLAYER_GUN_ROT_RATIO
player_rotation.x = self.offsets.player_rotation.x*self.consts.HIP_PLAYER_GUN_ROT_RATIO
end
gun_offset = gun_offset+added_pos
--dir needs to be rotated twice seperately to avoid weirdness
local rotation = self.transforms.total_offset_rotation
local rotation = self.offsets.total_offset_rotation
local bone_pos = Vec.rotate(bone_location, {x=0, y=player_rotation.y*math.pi/180, z=0})
local gun_offset = Vec.rotate(Vec.rotate(gun_offset, {x=(rotation.player_axial.x+player_rotation.x)*math.pi/180,y=0,z=0}), {x=0,y=(rotation.player_axial.y+player_rotation.y)*math.pi/180,z=0})
--[[local hud_pos = bone_pos+gun_offset+handler:get_pos()
@ -206,6 +216,7 @@ function gun:get_pos(added_pos)
--world pos, position of bone, offset of gun from bone (with added_pos)
return bone_pos+gun_offset+handler:get_pos(), bone_pos, gun_offset
end
function gun:add_entity()
assert(self.instance, "attempt to call object method on a class")
self.entity = minetest.add_entity(self.player:get_pos(), self.name.."_visual")
@ -213,21 +224,23 @@ function gun:add_entity()
obj.parent_player = self.player
obj:on_step()
end
function gun:has_entity()
assert(self.instance, "attempt to call object method on a class")
if not self.entity then return false end
if not self.entity:get_pos() then return false end
return true
end
--update the gun, da meat and da potatoes
function gun:update(dt)
assert(self.instance, "attempt to call object method on a class")
if not self:has_entity() then self:add_entity() end
self.dir = self:get_dir()
self.pos = self:get_pos()
local handler = self.handler
local look_rotation = {x=handler.look_rotation.x,y=handler.look_rotation.y}
local total_rot = self.transforms.total_offset_rotation
local player_rot = self.transforms.player_rotation
local total_rot = self.offsets.total_offset_rotation
local player_rot = self.offsets.player_rotation
local constant = 1.4
--player look rotation
@ -237,6 +250,7 @@ function gun:update(dt)
else
player_rot.x = look_rotation.x
end
--timers
if self.rechamber_time > 0 then
self.rechamber_time = self.rechamber_time - dt
else
@ -244,53 +258,29 @@ function gun:update(dt)
end
self.time_since_creation = self.time_since_creation + dt
self.time_since_last_fire = self.time_since_last_fire + dt
--update some vectors
if self.consts.HAS_SWAY then self:update_sway(dt) end
if self.consts.HAS_RECOIL then self:update_recoil(dt) end
if self.consts.HAS_BREATHING then self:update_breathing(dt) end
if self.consts.HAS_WAG then self:update_wag(dt) end
self.dir = self:get_dir()
self.local_dir = self:get_dir(true)
--sprite scope
if self.properties.sprite_scope then
if not self.sprite_scope then
self.sprite_scope = self.properties.sprite_scope:new({
gun = self
})
end
self.sprite_scope:update()
end
player_rot.y = -handler.look_rotation.y
local offsets = self.transforms
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} + {x=0,y=0,z=0}
total_rot.gun_axial = offsets.recoil.gun_axial + offsets.walking.gun_axial + offsets.sway.gun_axial
local dir = vector.gun
if self.handler.controls.ads then
if not self.useless_hud then
self.useless_hud = {}
self.useless_hud.reticle = self.player:hud_add{
hud_elem_type = "image",
position = {x=.5,y=.5},
scale = {x=1.5,y=1.5},
text = "gun_mrkr.png",
}
self.useless_hud.fore = self.player:hud_add{
hud_elem_type = "image",
position = {x=.5,y=.5},
scale = {x=10,y=10},
text = "scope_fore.png",
}
self.useless_hud.back = self.player:hud_add{
hud_elem_type = "image",
position = {x=.5,y=.5},
scale = {x=10,y=10},
text = "scope_back.png",
}
end
local wininfo = minetest.get_player_window_information(self.player:get_player_name())
if wininfo then
local dir = self:get_dir({x=0,y=0,z=0})
--local dir2 = self:get_dir({x=0,y=0,z=0})
local ratio = wininfo.size.x/wininfo.size.y
local v = Point_to_hud(dir, 80, ratio)
self.player:hud_change(self.useless_hud.reticle, "position", {x=v.x, y=v.y})
self.player:hud_change(self.useless_hud.fore, "position", {x=((v.x-.5)/1.1)+.5, y=((v.x-.5)/1.1)+.5})
self.player:hud_change(self.useless_hud.back, "position", {x=((2*total_rot.player_axial.y/(80*2))+.5), y=(((2*total_rot.player_axial.x)/(80*2))+.5)})
end
elseif self.useless_hud then
for i, v in pairs(self.useless_hud) do
self.player:hud_remove(v)
end
self.useless_hud = nil
end
end
function gun:update_wag(dt)
local handler = self.handler
@ -299,7 +289,7 @@ function gun:update_wag(dt)
else
self.walking_tick = 0
end
local walking_offset = self.transforms.walking
local walking_offset = self.offsets.walking
for _, i in pairs({"x","y"}) do
for axis, _ in pairs(walking_offset) do
if handler.walking then
@ -325,9 +315,9 @@ function gun:update_wag(dt)
end
end
function gun:update_recoil(dt)
for axis, _ in pairs(self.transforms.recoil) do
for axis, _ in pairs(self.offsets.recoil) do
for _, i in pairs({"x","y"}) do
local recoil = self.transforms.recoil[axis][i]
local recoil = self.offsets.recoil[axis][i]
local recoil_vel = math.clamp(self.velocities.recoil[axis][i],-self.properties.recoil.angular_velocity_max[axis],self.properties.recoil.angular_velocity_max[axis])
local old_recoil_vel = recoil_vel
recoil = recoil + recoil_vel
@ -357,7 +347,7 @@ function gun:update_recoil(dt)
end
end
self.velocities.recoil[axis][i] = recoil_vel
self.transforms.recoil[axis][i] = recoil
self.offsets.recoil[axis][i] = recoil
end
end
end
@ -369,13 +359,13 @@ function gun:update_breathing(dt)
local scale = 1
--now if it's above math.pi we know it's in the pause half of the cycle. For smoothness, we cut the sine off early and decay the value linearly.
if x > math.pi*(8/9) then
self.transforms.breathing.player_axial=self.transforms.breathing.player_axial-(self.transforms.breathing.player_axial*2*dt)
self.offsets.breathing.player_axial=self.offsets.breathing.player_axial-(self.offsets.breathing.player_axial*2*dt)
else
self.transforms.breathing.player_axial = scale*(math.sin(x))
self.offsets.breathing.player_axial = scale*(math.sin(x))
end
end
function gun:update_sway(dt)
for axis, sway in pairs(self.transforms.sway) do
for axis, sway in pairs(self.offsets.sway) do
local sway_vel = self.velocities.sway[axis]
local ran
ran = Vec.apply(Vec.new(), function(i,v)
@ -390,14 +380,16 @@ function gun:update_sway(dt)
sway=Vec.normalize(sway)*self.properties.sway.max_angle[axis]
sway_vel = Vec.new()
end
self.transforms.sway[axis] = sway
self.offsets.sway[axis] = sway
self.velocities.sway[axis] = sway_vel
end
end
function gun:prepare_deletion()
assert(self.instance, "attempt to call object method on a class")
if self:has_entity() then self.entity:remove() end
if self.sprite_scope then self.sprite_scope:prepare_deletion() end
end
--construction for the gun class
gun.construct = function(def)
if def.instance then
--remember to give gun an id
@ -412,11 +404,12 @@ gun.construct = function(def)
else
def.id = meta:get_string("guns4d_id")
end
--make sure there's nothing missing
--make sure there's nothing missing, aka copy over all of the properties.
def.properties = table.fill(gun.properties, def.properties)
--Vecize
def.transforms = table.deep_copy(gun.transforms)
for i, tbl in pairs(def.transforms) do
--so, we copy the offsets table so we have all of the offsets
--then we create new vectors for gun_axial and player_axial.
def.offsets = table.deep_copy(gun.offsets)
for i, tbl in pairs(def.offsets) do
if tbl.gun_axial and tbl.player_axial and (not i=="breathing") then
tbl.gun_axial = Vec.new(tbl.gun_axial)
tbl.player_axial = Vec.new(tbl.player_axial)
@ -433,6 +426,26 @@ gun.construct = function(def)
local props = def.properties
assert(def.name, "no name provided")
assert(def.itemstring, "no itemstring provided")
assert(minetest.registered_items[def.itemstring], "item is not registered, check dependencies.")
--override methods so control handler can do it's job
local old_on_use = minetest.registered_items[def.itemstring].on_use
local old_on_s_use = minetest.registered_items[def.itemstring].on_secondary_use
minetest.override_item(def.itemstring, {
on_use = function(itemstack, user, pointed_thing)
if old_on_use then
old_on_use(itemstack, user, pointed_thing)
end
Guns4d.players[user:get_player_name()].handler.control_handler:on_use(itemstack, pointed_thing)
end,
on_secondary_use = function(itemstack, user, pointed_thing)
if old_on_s_use then
old_on_s_use(itemstack, user, pointed_thing)
end
Guns4d.players[user:get_player_name()].handler.control_handler:on_secondary_use(itemstack, pointed_thing)
end
})
--(this tableref is ephermeral after constructor is called, see instantiatable_class)
Guns4d.gun.registered[def.name] = def
minetest.register_entity(def.name.."_visual", {
@ -454,19 +467,24 @@ gun.construct = function(def)
if not lua_object then obj:remove() return end
--this is changing the point of rotation if not aiming, this is to make it look less shit.
local axial_modifier = Vec.new()
if not handler.controls.ads then
local pitch = lua_object.transforms.total_offset_rotation.player_axial.x+lua_object.transforms.player_rotation.x
if not handler.control_bools.ads then
local pitch = lua_object.offsets.total_offset_rotation.player_axial.x+lua_object.offsets.player_rotation.x
axial_modifier = Vec.new(pitch*(1-lua_object.consts.HIP_PLAYER_GUN_ROT_RATIO),0,0)
end
local axial_rot = lua_object.transforms.total_offset_rotation.gun_axial+axial_modifier
local axial_rot = lua_object.offsets.total_offset_rotation.gun_axial+axial_modifier
--attach to the correct bone, and rotate
if handler.controls.ads == false then
local visibility = true
if lua_object.sprite_scope and lua_object.sprite_scope.hide_gun and (not (handler.ads_location == 0)) then
visibility = false
end
if handler.control_bools.ads then
local normal_pos = (props.ads.offset+Vec.new(props.ads.horizontal_offset,0,0))*10
obj:set_attach(player, gun.consts.AIMING_BONE, normal_pos, -axial_rot, visibility)
else
local normal_pos = Vec.new(props.hip.offset)*10
-- Vec.multiply({x=normal_pos.x, y=normal_pos.z, z=-normal_pos.y}, 10)
obj:set_attach(player, gun.consts.HIPFIRE_BONE, normal_pos, -axial_rot, true)
else
local normal_pos = (props.ads.offset+Vec.new(props.ads.horizontal_offset,0,0))*10
obj:set_attach(player, gun.consts.AIMING_BONE, normal_pos, -axial_rot, true)
obj:set_attach(player, gun.consts.HIPFIRE_BONE, normal_pos, -axial_rot, visibility)
end
end
})

View File

@ -11,7 +11,7 @@ local player_handler = {
--model_handler = player_model_handler
look_rotation = {x=0, y=0},
look_offset = Vec.new(),
ads_location = 0,
ads_location = 0, --interpolation scalar for gun aiming location
controls = {},
horizontal_offset = 0
}
@ -20,37 +20,54 @@ function player_handler:update(dt)
assert(self.instance, "attempt to call object method on a class")
local player = self.player
self.wielded_item = self.player:get_wielded_item()
local held_gun = self:holding_gun() --get the gun class that is associated with the held gun
local held_gun = self:is_holding_Gun() --get the gun class that is associated with the held gun
if held_gun then
--was there a gun last time? did the wield index change?
local old_index = self.wield_index
self.wield_index = player:get_wield_index()
--if gun has changed or was not held, then reset.
--initialize all handlers and objects
if (not self.gun) or (self.gun.id ~= self.wielded_item:get_meta():get_string("guns4d_id")) then
--initialize all handlers
----gun handler----
----gun (handler w/physical manifestation)----
if self.gun then --delete gun object if present
self.gun:prepare_deletion()
self.gun = nil
end
self.gun = held_gun:new({itemstack=self.wielded_item, player=self.player, 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
end
self.model_handler = 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})
--reinitialize some handler data and set set_hud_flags
self.horizontal_offset = self.gun.properties.ads.horizontal_offset
player:hud_set_flags({wielditem = false, crosshair = false})
end
--update some properties.
self.look_rotation.x, self.look_rotation.y = player:get_look_vertical()*180/math.pi, -player:get_look_horizontal()*180/math.pi
if TICK % 10 == 0 then
self.wininfo = minetest.get_player_window_information(self.player:get_player_name())
end
--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.gun:update(dt)
--this has to be checked after control handler
if TICK % 4 == 0 then
self.touching_ground = self:get_is_on_ground()
self.walking = self:get_is_walking()
end
elseif self.gun then
self.control_handler = nil
--delete gun object
@ -62,23 +79,22 @@ function player_handler:update(dt)
self.model_handler = nil
player:hud_set_flags({wielditem = true, crosshair = true}) --reenable hud elements
end
--eye offsets
if self.controls.ads and (self.ads_location<1) then
--eye offsets and ads_location
if self.control_bools.ads and (self.ads_location<1) then
--if aiming, then increase ADS location
self.ads_location = math.clamp(self.ads_location + (dt/self.gun.properties.aim_time), 0, 1)
elseif (not self.controls.ads) and self.ads_location>0 then
local divisor = .4
elseif (not self.control_bools.ads) and self.ads_location>0 then
local divisor = .2
if self.gun then
divisor = self.gun.properties.aim_time
divisor = self.gun.properties.aim_time/self.gun.consts.AIM_OUT_AIM_IN_SPEED_RATIO
end
self.ads_location = math.clamp(self.ads_location - (dt/divisor), 0, 1)
end
self.look_offset.x = self.horizontal_offset*self.ads_location
player:set_eye_offset(self.look_offset*10)
--some status stuff
if TICK % 2 == 0 then
self.touching_ground = self:get_is_on_ground()
end
self.walking = self:get_is_walking()
--stored properties and pos must be reset as they could be outdated.
self.properties = nil
self.pos = nil
@ -111,7 +127,10 @@ function player_handler:get_is_walking()
else
controls = self.control_handler.player_pressed
end
if (vector.length(vector.new(velocity.x, 0, velocity.z)) > .1) and (controls.up or controls.down or controls.left or controls.right) and self.touching_ground then
if (vector.length(vector.new(velocity.x, 0, velocity.z)) > .1)
and (controls.up or controls.down or controls.left or controls.right)
and self.touching_ground
then
walking = true
end
return walking
@ -119,7 +138,7 @@ end
--resets the controls bools table for the player_handler
function player_handler:reset_controls_table()
assert(self.instance, "attempt to call object method on a class")
self.controls = table.deep_copy(default_active_controls)
self.control_bools = table.deep_copy(default_active_controls)
end
--doubt I'll ever use this... but just in case I don't want to forget.
function player_handler:get_pos()
@ -144,7 +163,7 @@ function player_handler:set_properties(properties)
self.player:set_properties(properties)
self.properties = table.fill(self.properties, properties)
end
function player_handler:holding_gun()
function player_handler:is_holding_Gun()
assert(self.instance, "attempt to call object method on a class")
if self.wielded_item then
for name, obj in pairs(Guns4d.gun.registered) do
@ -189,7 +208,7 @@ function player_handler.construct(def)
end
end
def.look_rotation = table.deep_copy(player_handler.look_rotation)
def.controls = table.deep_copy(default_active_controls)
def.control_bools = table.deep_copy(default_active_controls)
end
end
Guns4d.player_handler = Instantiatable_class:inherit(player_handler)

View File

@ -34,9 +34,9 @@ function player_model:update()
local player = self.player
local handler = Guns4d.players[player:get_player_name()].handler
local gun = handler.gun
local player_axial_offset = gun.transforms.total_offset_rotation.player_axial
local pitch = player_axial_offset.x+gun.transforms.player_rotation.x
local combined = player_axial_offset+gun.transforms.total_offset_rotation.gun_axial+Vec.new(gun.transforms.player_rotation.x,0,0)
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 eye_pos = vector.new(0, handler:get_properties().eye_height*10, 0)
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_aiming_bone", eye_pos, vector.new(pitch, 180-player_axial_offset.y, 0))

88
classes/Sprite_scope.lua Normal file
View File

@ -0,0 +1,88 @@
Sprite_scope = Instantiatable_class:inherit({
images = {
fore = {
texture = "scope_fore.png",
scale = {x=10,y=10},
movement_multiplier = 1,
},
back = {
texture = "scope_back.png",
scale = {x=10,y=10},
movement_multiplier = -1,
},
reticle = {
texture = "gun_mrkr.png",
scale = {x=1,y=1},
movement_multiplier = 1,
misalignment_opacity_threshold_angle = 3,
misalignment_opacity_maximum_angle = 8,
},
--mask = "blank.png",
},
hide_gun = true,
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.elements = {}
local new_images = table.deep_copy(def.images)
if def.images then
def.images = table.fill(new_images, def.images)
end
def.elements.reticle = def.player:hud_add{
hud_elem_type = "image",
position = {x=.5,y=.5},
scale = def.images.reticle.scale,
text = "blank.png",
}
def.elements.fore = def.player:hud_add{
hud_elem_type = "image",
position = {x=.5,y=.5},
scale = def.images.fore.scale,
text = "blank.png",
}
def.elements.back = def.player:hud_add{
hud_elem_type = "image",
position = {x=.5,y=.5},
scale = def.images.back.scale,
text = "blank.png",
}
end
end
})
function Sprite_scope:update()
local handler = self.handler
if handler.wininfo and self.handler.control_bools.ads then
local dir = self.gun.local_dir
local ratio = handler.wininfo.size.x/handler.wininfo.size.y
local added_pos
if handler.ads_location ~= 1 then
dir = dir + (self.gun.properties.ads.offset+vector.new(self.gun.properties.ads.horizontal_offset,0,0))*0
end
local v = Point_to_hud(dir, 80, ratio)
self.player:hud_change(self.elements.reticle, "position", {x=(v.x*self.images.reticle.movement_multiplier)+.5, y=(v.y*self.images.reticle.movement_multiplier)+.5})
self.player:hud_change(self.elements.fore, "position", {x=(v.x*self.images.fore.movement_multiplier)+.5, y=(v.y*self.images.fore.movement_multiplier)+.5})
self.player:hud_change(self.elements.back, "position", {x=(v.x*self.images.back.movement_multiplier)+.5, y=(v.y*self.images.back.movement_multiplier)+.5})
--update textures
end
for i, v in pairs(self.elements) do
local def = self.images[i]
local tex = def.texture
--"smoother is better" it's not. Apparently, this creates a new image each time. It is, however, cached. So i'd rather have
--25 possible images, instead of 255.
local factor = 1
if def.misalignment_opacity_threshold_angle then
local angle = math.sqrt(self.gun.offsets.total_offset_rotation.gun_axial.x^2+self.gun.offsets.total_offset_rotation.gun_axial.y^2)
if def.misalignment_opacity_threshold_angle < angle then
factor = (factor - ((angle-def.misalignment_opacity_threshold_angle)/def.misalignment_opacity_maximum_angle))
end
end
self.player:hud_change(v, "text", tex.."^[opacity:"..tostring(math.ceil((25.5*handler.ads_location*factor))*10))
end
end
function Sprite_scope:prepare_deletion()
for i, v in pairs(self.elements) do
self.player:hud_remove(v)
end
end

1
docs/known bugs.txt Normal file
View File

@ -0,0 +1 @@
when spamming on_use, sometimes RMB in get_player_control will become stuck

View File

@ -0,0 +1,38 @@
VFX, SFX:
Bullet hit node FX (steal mostly from 3dguns)
Bullet fly-by SFX
HUD system (most can once again be pulled from 3dguns)
reload progress
ammunition left
ammo type
gun animations (take 3dguns as an example, then completely rewrite)
+player model & gun model reading
gun sounds
gun features
firemodes
gun ammo:
magazine relaoding
fractional reloading
magless flat reload
!!fix gimbal lock (ironsights broken)
"3d" optics
attachments (last before beta)
fix shooting through walls be pressing against them
bullets
bullet class system
add blunt force properties
on_hitnode function callback
auxillary (beta+/never)
player hitboxes
server to client lag prediction
possible user CSM for lag prediction
bullet drop (maybe bullet wind?)
bullet tracers
inverse kinematics
stamina

View File

@ -0,0 +1,310 @@
<mxfile host="app.diagrams.net" modified="2023-08-08T02:26:17.905Z" agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36" etag="44jzb9uK1u4jJbBgrsUi" version="21.6.6" type="device">
<diagram name="Page-1" id="hjH4469px_Ex89IZIbiB">
<mxGraphModel dx="1049" dy="561" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="XBcHE4cyeVAlC1C74syX-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="W6xmUVEbs_vsLZUt_kYx-8" target="W6xmUVEbs_vsLZUt_kYx-12">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="520" y="680" />
<mxPoint x="405" y="680" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-12" value="Instantiate" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="XBcHE4cyeVAlC1C74syX-11">
<mxGeometry x="0.4996" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-102" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fillColor=#d5e8d4;strokeColor=#82b366;entryX=0.25;entryY=0;entryDx=0;entryDy=0;exitX=0.75;exitY=1;exitDx=0;exitDy=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-8" target="W6xmUVEbs_vsLZUt_kYx-73" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="676" y="630" as="sourcePoint" />
<mxPoint x="714" y="775" as="targetPoint" />
<Array as="points">
<mxPoint x="550" y="720" />
<mxPoint x="700" y="720" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-5" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="740" y="580" as="targetPoint" />
<mxPoint x="830" y="545" as="sourcePoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-15" value="Creates and updates" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="W6xmUVEbs_vsLZUt_kYx-10">
<mxGeometry x="-0.261" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-5" target="W6xmUVEbs_vsLZUt_kYx-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-37" value="Creates &amp;amp; updates" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="W6xmUVEbs_vsLZUt_kYx-11" vertex="1" connectable="0">
<mxGeometry x="-0.3348" y="1" relative="1" as="geometry">
<mxPoint x="-17" y="-1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-15" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-5" target="W6xmUVEbs_vsLZUt_kYx-14" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="820" y="510" as="sourcePoint" />
<mxPoint x="950" y="580" as="targetPoint" />
<Array as="points">
<mxPoint x="990" y="510" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-61" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;fillColor=#d80073;strokeColor=#A50040;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-5" target="W6xmUVEbs_vsLZUt_kYx-14" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="960" y="530" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-64" value="Pass gun controls def" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="W6xmUVEbs_vsLZUt_kYx-61" vertex="1" connectable="0">
<mxGeometry x="-0.3137" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-5" value="Player handler" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="680" y="480" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-8" target="W6xmUVEbs_vsLZUt_kYx-9" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="600" y="560" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-32" value="Bone info" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="W6xmUVEbs_vsLZUt_kYx-28" vertex="1" connectable="0">
<mxGeometry x="0.1556" y="1" relative="1" as="geometry">
<mxPoint x="-12" y="1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-72" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.582;entryY=-0.003;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryPerimeter=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-8" target="W6xmUVEbs_vsLZUt_kYx-71" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="520" y="680" />
<mxPoint x="590" y="680" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-77" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-8" target="W6xmUVEbs_vsLZUt_kYx-71" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="550" y="670" />
<mxPoint x="550" y="670" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-78" value="Bone info &amp;amp;&lt;br&gt;gun direction" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="W6xmUVEbs_vsLZUt_kYx-77" vertex="1" connectable="0">
<mxGeometry x="0.1298" y="-2" relative="1" as="geometry">
<mxPoint y="-5" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-75" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-8" target="W6xmUVEbs_vsLZUt_kYx-73" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="520" y="680" />
<mxPoint x="730" y="680" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-76" value="Creates &amp;amp; updates" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="W6xmUVEbs_vsLZUt_kYx-75" vertex="1" connectable="0">
<mxGeometry x="-0.8968" y="-1" relative="1" as="geometry">
<mxPoint x="1" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-95" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;entryX=0.875;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-8" target="W6xmUVEbs_vsLZUt_kYx-88" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-8" value="Gun" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="460" y="580" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-9" value="model handler" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="680" y="580" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1" source="W6xmUVEbs_vsLZUt_kYx-12" target="W6xmUVEbs_vsLZUt_kYx-19">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-12" value="Bullet ray(s)" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="345" y="790" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.75;entryDx=0;entryDy=0;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-14" target="W6xmUVEbs_vsLZUt_kYx-8" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="990" y="660" />
<mxPoint x="615" y="660" />
<mxPoint x="615" y="625" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-7" value="call :fire() or other methods" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="wAXQPkzS34ZOuWmKrVVe-5" vertex="1" connectable="0">
<mxGeometry x="-0.0751" relative="1" as="geometry">
<mxPoint x="-20" as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-14" value="Control handler" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="930" y="580" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-17" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="1" target="W6xmUVEbs_vsLZUt_kYx-5" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="740" y="330" as="sourcePoint" />
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-35" value="Creates" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="W6xmUVEbs_vsLZUt_kYx-17" vertex="1" connectable="0">
<mxGeometry x="-0.1753" y="2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-16" value="on joinplayer" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="680" y="270" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-19" value="Target or node" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" parent="1" vertex="1">
<mxGeometry x="260" y="790" width="30" height="60" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-38" value="Creates &amp;amp; updates" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="1" vertex="1" connectable="0">
<mxGeometry x="869.9966666666667" y="510" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-58" target="W6xmUVEbs_vsLZUt_kYx-121" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-58" value="Gun class" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" parent="1" vertex="1">
<mxGeometry x="867.5" y="245" width="80" height="100" as="geometry" />
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-16" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.385;entryY=0.052;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="1" source="W6xmUVEbs_vsLZUt_kYx-67" target="W6xmUVEbs_vsLZUt_kYx-83">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-67" value="attachments" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
<mxGeometry x="1045" y="230" width="80" height="100" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-71" value="Sprite scope" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="520" y="790" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-73" value="Physical scope" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
<mxGeometry x="670" y="790" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-26" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-83" target="W6xmUVEbs_vsLZUt_kYx-121" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-88" target="W6xmUVEbs_vsLZUt_kYx-8" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-10" value="Predict client&lt;br&gt;view or request&lt;br&gt;directly" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="wAXQPkzS34ZOuWmKrVVe-35">
<mxGeometry x="-0.1935" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-88" value="External server side modifications" style="ellipse;shape=cloud;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
<mxGeometry x="210" y="565" width="135" height="90" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-109" value="&lt;h1&gt;&lt;i&gt;&lt;u&gt;guns4d structure specification diagram&lt;/u&gt;&lt;/i&gt;&lt;/h1&gt;" style="text;html=1;strokeColor=#FFFFFF;fillColor=#fff2cc;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;align=center;" parent="1" vertex="1">
<mxGeometry x="40" y="30" width="510" height="40" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-114" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#FFFFFF;" parent="1" vertex="1">
<mxGeometry x="40" y="90" width="120" height="250" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-115" value="process" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;" parent="1" vertex="1">
<mxGeometry x="50" y="110" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-118" value="class&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" parent="1" vertex="1">
<mxGeometry x="50" y="165" width="100" height="45" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-120" value="Instance" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="50" y="230" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-121" value="&lt;font style=&quot;font-size: 12px;&quot;&gt;&lt;span style=&quot;background-color: rgb(255, 255, 255);&quot;&gt;create correct gun by finding correct class and modifying properties by item metadata&lt;/span&gt;&lt;/font&gt;" style="rounded=0;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="835" y="375" width="145" height="80" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-127" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" source="W6xmUVEbs_vsLZUt_kYx-5" target="W6xmUVEbs_vsLZUt_kYx-121" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="775" y="460" as="sourcePoint" />
<mxPoint x="825" y="410" as="targetPoint" />
<Array as="points">
<mxPoint x="770" y="455" />
<mxPoint x="770" y="415" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-130" value="inactive" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=#C3ABD0;fillColor=#e1d5e7;" parent="1" vertex="1">
<mxGeometry x="50" y="290" width="100" height="30" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.991;entryY=0.611;entryDx=0;entryDy=0;entryPerimeter=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1226.08" y="609.92" as="sourcePoint" />
<mxPoint x="1050.0000000000005" y="609.5799999999998" as="targetPoint" />
<Array as="points">
<mxPoint x="1226.08" y="609.92" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-17" value="Player controls" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="wAXQPkzS34ZOuWmKrVVe-21">
<mxGeometry x="0.0076" y="-1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1260" y="449.9999999999999" as="targetPoint" />
<mxPoint x="1260" y="530" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-131" value="Gun holder" style="shape=umlActor;verticalLabelPosition=bottom;verticalAlign=top;html=1;outlineConnect=0;" parent="1" vertex="1">
<mxGeometry x="1235" y="540" width="50" height="100" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-8" value="" style="shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;rotation=-180;position2=1;base=45;strokeColor=none;fillColor=#fff2cc;" parent="1" vertex="1">
<mxGeometry x="800" y="670" width="120" height="120" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-11" value="&lt;font style=&quot;font-size: 8px;&quot;&gt;Note that this is the behavior from the API layer (via properties.controls), but in the base class there&#39;s no interaction between these two components by default&lt;/font&gt;" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="1" vertex="1">
<mxGeometry x="800" y="710" width="115" height="70" as="geometry" />
</mxCell>
<mxCell id="W6xmUVEbs_vsLZUt_kYx-83" value="held item string and metadata" style="ellipse;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" vertex="1">
<mxGeometry x="1045" y="380" width="105" height="70" as="geometry" />
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-41" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" parent="1" target="W6xmUVEbs_vsLZUt_kYx-83" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1200" y="414.9999999999999" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-43" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.75;entryY=0;entryDx=0;entryDy=0;exitX=0.181;exitY=0.967;exitDx=0;exitDy=0;exitPerimeter=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="1" source="wAXQPkzS34ZOuWmKrVVe-42" target="W6xmUVEbs_vsLZUt_kYx-14" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1225" y="448" />
<mxPoint x="1225" y="510" />
<mxPoint x="1020" y="510" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-45" value="on_use (etc) &lt;br&gt;callbacks" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="wAXQPkzS34ZOuWmKrVVe-43" vertex="1" connectable="0">
<mxGeometry x="-0.0218" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="wAXQPkzS34ZOuWmKrVVe-42" value="Item / Itemstack" style="rounded=1;whiteSpace=wrap;html=1;" parent="1" vertex="1">
<mxGeometry x="1200" y="390" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="497.5" y="470" as="sourcePoint" />
<mxPoint x="497" y="580" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-9" value="Bullet definition" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="XBcHE4cyeVAlC1C74syX-8">
<mxGeometry x="-0.6286" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-7" value="held item string and metadata" style="ellipse;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="1">
<mxGeometry x="445" y="400" width="105" height="70" as="geometry" />
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="1" source="XBcHE4cyeVAlC1C74syX-18" target="XBcHE4cyeVAlC1C74syX-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="XBcHE4cyeVAlC1C74syX-18" value="Bullet class" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="1">
<mxGeometry x="457.5" y="230" width="80" height="100" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>

92
docs/using controls.txt Normal file
View File

@ -0,0 +1,92 @@
This document contains (hopefully) all you need to know about
the control_handler, and any other relevant input systems
The control system is designed to be simple but versatile,
----------------------------------------------------------------------------
basic information
----------------------------------------------------------------------------
IMPORTANT: `on_use` and `on_secondary_use` are RESERVED indexes. They
have different behaviors and function parameters. (see special actions)
control action example:
------------------------------------------------------------------------
(The default aim taken from gun_api.lua)
aim = {
conditions = {"RMB"},
loop = false,
timer = 0,
func = function(active, interrupted, data, busy_list, handler)
if active then
handler.control_bools.ads = not handler.control_bools.ads
end
end
},
------------------------------------------------------------------------
here's the breakdown:
`conditions = {"RMB"}` is the list of conditions (see get_player_control
in minetest's lua_api.txt) that need to be fufulled for the control to
be activated
`timer = 0` tells the handler that if condtions are met for 0 seconds
that it is active.
`loop = false` indicates the it only will call once if the key(s) are
held for the the specified amount of time `timer = 0`. In short, this
tells the handler to run the function one time instead of calling it
in (as the name implies) a loop.
additional properties:
`call_before_timer = false` if true, then the handler calls the function
while the timer is in progress, this is very useful if you want something
like a reload animation, or maybe a bow with a cocking animation, etc.
use the function parameters (as follows) for better information.
----------------------------------------------------------------------------
standard function parameters
----------------------------------------------------------------------------
function parameters for normal actions (see special actions)
`active` indicates wether conditions or timers were met/finished, the
function can be called for reasons other then conditions being finished.
use this to check if you actually want to do an action (such as firing)
or finalizing reloads.
`interrupted` indicates if conditions were no longer met while the timer
was in progress. This cannot not be called with active as true.
`data` is actually the Control_handler's table for the control action,
it has `timer` which is the time left on the action and `held` which
indicates if the keyboard and mouse conditions are met.
`busy_list` is a list of other active actions, use this if you want to
only act if
`handler` is the Player_handler, this is guns4d's *everything* handler.
to get the gun use `handler.gun`, to get the player use `handler.player`
----------------------------------------------------------------------------
special action
----------------------------------------------------------------------------
The indexes `on_use` and `on_secondary_use` are reserved for calls from
the default on_use and on_secondary_use of tools. By default, the on_use
and on_secondary_use of the itemstring provided is overwritten to contain
this call.
set these as functions (with the following parameters) for on_use and
on_secondary_use. It's not set in busy list by defaults, so if you
wish (or otherwise need to) add a `self.busy_list.on_use = true` to it.
note that the busy list is cleared each update by default.
function parameters:
`itemstack`, `handler`, `pointed_thing`
see lua_api.txt for details on `itemstack` and `pointed_thing` and
see "standard function parameters" for handler info.

View File

@ -29,8 +29,8 @@ local default_def = {
player_axial = 0,
},
angular_velocity = {
gun_axial = {x=.2, y=.25},
player_axial = {x=.25, y=.4},
gun_axial = {x=.1, y=.1},
player_axial = {x=.1, y=.1},
},
},
firerateRPM = 600,
@ -41,7 +41,7 @@ local default_def = {
timer = 0,
func = function(active, interrupted, data, busy_list, handler)
if active then
handler.controls.ads = not handler.controls.ads
handler.control_bools.ads = not handler.control_bools.ads
end
end
},
@ -50,9 +50,17 @@ local default_def = {
loop = true,
timer = 0,
func = function(active, interrupted, data, busy_list, handler)
handler.gun:fire()
if not handler.control_handler.busy_list.on_use then
handler.gun:fire()
end
print(handler.control_handler.busy_list.on_use)
end
}
},
on_use = function(itemstack, handler, pointed_thing)
print("use")
handler.gun:fire()
handler.control_handler.busy_list.on_use = true
end
},
consts = {
HIP_PLAYER_GUN_ROT_RATIO = .6
@ -85,10 +93,12 @@ function Guns4d.register_gun_default(def)
--validate controls
if new_def.properties.controls then
for i, control in pairs(new_def.properties.controls) do
assert(control.conditions, "no conditions provided for control")
for _, condition in pairs(control.conditions) do
if not valid_ctrls[condition] then
assert(false, "invalid key: '"..condition.."'")
if not (i=="on_use") and not (i=="on_secondary_use") then
assert(control.conditions, "no conditions provided for control")
for _, condition in pairs(control.conditions) do
if not valid_ctrls[condition] then
assert(false, "invalid key: '"..condition.."'")
end
end
end
end

View File

@ -11,6 +11,7 @@ path = path .. "/classes"
dofile(path.."/Instantiatable_class.lua")
dofile(path.."/Bullet_ray.lua")
dofile(path.."/Control_handler.lua")
dofile(path.."/Sprite_scope.lua")
dofile(path.."/Gun.lua")
dofile(path.."/Player_model_handler.lua")
dofile(path.."/Player_handler.lua")
@ -18,11 +19,7 @@ dofile(path.."/Player_handler.lua")
--load after
path = minetest.get_modpath("guns4d")
local player_handler = Guns4d.player_handler
local gun = Guns4d.gun
minetest.register_on_joinplayer(function(player)
local pname = player:get_player_name()
@ -31,11 +28,14 @@ minetest.register_on_joinplayer(function(player)
}
player:set_fov(80)
end)
minetest.register_on_leaveplayer(function(player)
local pname = player:get_player_name()
Guns4d.players[pname].handler:prepare_deletion()
Guns4d.players[pname] = nil
end)
--ticks are rarely used, but still ideal for rare checks with minimal overhead.
TICK = 0
minetest.register_globalstep(function(dt)
TICK = TICK + 1

View File

@ -150,5 +150,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)+.5, (-y / 2)+.5, z)
return vector.new(x / 2, -y / 2, z)
end

0
register_magazine.lua Normal file
View File

View File

@ -4,7 +4,7 @@ function Guns4d.muzzle_flash(self)
if self.particle_spawners.muzzle_smoke and self.particle_spawners.muzzle_smoke ~= -1 then
minetest.delete_particlespawner(self.particle_spawners.muzzle_smoke, self.player:get_player_name())
end
local dir, offset_pos = self:get_dir(), self:get_pos(self.properties.flash_offset)
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(-2, -2, -.3), vector.dir_to_rotation(dir))
local max = vector.rotate(vector.new(2, 2, .3), vector.dir_to_rotation(dir))