partial implementation of Ammo_handlers, very crude documentation of ammunition related properties. Changed system for handling magazines.

This commit is contained in:
FatalErr42O 2023-08-12 21:20:34 -07:00
parent 21ab76ce95
commit b059c175b8
8 changed files with 221 additions and 99 deletions

View File

@ -1,56 +1,102 @@
Ammo_handler = Instantiatable_class:inherit({
name = "Gun_ammo_handler",
name = "Ammo_handler",
construct = function(def)
assert(def.gun)
def.itemstack = def.gun.itemstack
def.handler = def.gun.handler
def.inventory = def.handler.inventory
local meta = def.gun.meta
if gun.properties.magazine then
local mag_meta = meta:get_string("guns4d_loaded_mag")
if mag_meta == "" then
meta:set_string("guns4d_loaded_mag", gun.properties.magazine.comes_with or "empty")
meta:set_string("guns4d_loaded_bullets", minetest.serialize({}))
else
def.mag = mag_meta
def.bullets = minetest.deserialize(meta:get_string("guns4d_loaded_bullets"))
end
else
local bullet_meta = meta:get_string("guns4d_loaded_bullets")
if bullet_meta == "" then
meta:set_string("guns4d_loaded_bullets", minetest.serialize({}))
else
def.ammo.bullets = minetest.deserailize(bullet_meta)
if def.instance then
assert(def.gun, "no gun")
def.itemstack = def.gun.itemstack
def.handler = def.gun.handler
def.inventory = def.handler.inventory
local meta = def.gun.meta
local gun = def.gun
def.ammo = {}
if gun.properties.magazine then
if meta:get_string("guns4d_loaded_bullets") == "" then
meta:set_string("guns4d_loaded_mag", gun.properties.magazine.comes_with or "empty")
meta:set_string("guns4d_next_bullet", "empty")
meta:set_int("guns4d_total_bullets", 0)
meta:set_string("guns4d_loaded_bullets", minetest.serialize({}))
def.ammo.loaded_mag = "empty"
def.ammo.next_bullet = "empty"
def.ammo.total_bullets = 0
def.ammo.bullets = {}
else
def.ammo.loaded_mag = meta:get_string("guns4d_loaded_mag")
def.ammo.bullets = minetest.deserialize(meta:get_string("guns4d_loaded_bullets"))
def.ammo.total_bullets = meta:get_int("guns4d_total_bullets")
def.ammo.next_bullet = meta:get_string("guns4d_next_bullet")
end
end
end
end
})
function Gun_ammo:load_mag()
local inv = self.inventory
for _, ammunition in pairs(self.gun.accepted_mags) do
for i = 1, inv:get_size("main") do
--spend the round, return false if impossible.
function Ammo_handler:spend_round()
local bullet_spent = self.ammo.next_bullet
local meta = self.gun.meta
--subtract the bullet
if self.ammo.total_bullets > 0 then
self.ammo.bullets[bullet_spent] = self.ammo.bullets[bullet_spent]-1
if self.ammo.bullets[bullet_spent] == 0 then self.ammo.bullets[bullet_spent] = nil end
self.ammo.total_bullets = self.ammo.total_bullets - 1
meta:set_string("guns4d_loaded_bullets", minetest.serialize(self.ammo.bullets))
--set the new current bullet
if next(self.ammo.bullets) then
self.ammo.next_bullet = math.weighted_randoms(self.ammo.bullets)
meta:set_string("guns4d_next_bullet", self.ammo.next_bullet)
else
self.ammo.next_bullet = "empty"
meta:set_string("guns4d_next_bullet", "empty")
end
else
return false
end
end
function Ammo_handler:load_mag()
local inv = self.inventory
local magstack_index
local highest_ammo = 0
local gun = self.gun
local gun_accepts = gun.accepted_ammo
for i, v in pairs(inv:get_list("main")) do
if gun_accepts[v:get_name()] then
local meta = v:get_meta()
if meta:get_int("guns4d_total_bullets") > highest_ammo then
magstack_index = i
end
end
end
if magstack then
ammo_table = minetest.deserialize(magstack:get_meta():get_string("ammo"))
inv:set_stack("main", index, "")
state = next_state
state_changed = true
if magstack_index then
local magstack = inv:get_stack("main", magstack_index)
local magstack_meta = magstack:get_meta()
--get the ammo stuff
local meta = self.gun.meta
self.ammo.loaded_mag = magstack:get_name()
self.ammo.bullets = minetest.deserialize(magstack_meta:get_string("guns4d_loaded_bullets"))
self.ammo.total_bullets = magstack_meta:get_int("guns4d_total_bullets")
self.ammo.next_bullet = magstack_meta:get_string("guns4d_next_bullet")
--
meta:set_string("guns4d_loaded_mag", self.ammo.loaded_mag)
meta:set_string("guns4d_loaded_bullets", magstack_meta:get_string("guns4d_loaded_bullets"))
meta:set_int("guns4d_total_bullets", self.ammo.total_bullets)
meta:set_string("guns4d_next_bullet", self.ammo.next_bullet)
inv:set_stack("main", magstack_index, "")
return
end
end
function Gun_ammo:unload_mag()
function Ammo_handler:unload_mag()
end
function Gun_ammo:load_magless()
function Ammo_handler:load_magless()
end
function Gun_ammo:unload_magless()
function Ammo_handler:unload_magless()
end
function Gun_ammo:load_fractional()
function Ammo_handler:load_fractional()
end
function Gun_ammo:unload_fractional()
function Ammo_handler:unload_fractional()
end
function Gun_ammo:unload_chamber()
function Ammo_handler:unload_chamber()
end

View File

@ -53,11 +53,41 @@ local gun_default = {
gun_axial = {x=.2, y=-.2},
player_axial = {x=1,y=1},
},
controls = {
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
},
fire = {
conditions = {"LMB"},
loop = true,
timer = 0,
func = function(active, interrupted, data, busy_list, handler)
if not handler.control_handler.busy_list.on_use then
handler.gun:attempt_fire()
end
end
},
on_use = function(itemstack, handler, pointed_thing)
handler.gun:attempt_fire()
handler.control_handler.busy_list.on_use = true
end
},
magazine = {
magazine_only = false,
accepted_magazines = {}
},
accepted_bullets = {},
flash_offset = Vec.new(),
aim_time = 1,
firerateRPM = 10000,
controls = {},
accepted_mags = {}
ammo_handler = Ammo_handler
},
offsets = {
player_rotation = Vec.new(),
@ -115,9 +145,9 @@ local gun_default = {
function gun_default:spend_round()
end
function gun_default:fire()
function gun_default:attempt_fire()
assert(self.instance, "attempt to call object method on a class")
if self.rechamber_time <= 0 then
if self.rechamber_time <= 0 and self.ammo_handler:spend_round() then
local dir = self.dir
local pos = self:get_pos()
Guns4d.bullet_ray:new({
@ -421,7 +451,14 @@ gun_default.construct = function(def)
def.offsets[i] = Vec.new()
end
end
def.accepted_bullets = {}
for i, v in pairs(def.properties.accepted_bullets) do
def.accepted_bullets[i] = true
end
def.accepted_magazines = {}
for i, v in pairs(def.properties.magazine.accepted_magazines) do
def.accepted_bullets[i] = true
end
--def.velocities = table.deep_copy(def.base_class.velocities)
def.velocities = {}
@ -440,7 +477,14 @@ gun_default.construct = function(def)
})
end
end
if def.properties.entity_scope then
if not def.sprite_scope then
end
end
def.ammo_handler = def.properties.ammo_handler:new({
gun = def
})
elseif def.name ~= "__template__" then
local props = def.properties
assert(def.name, "no name provided")
@ -467,6 +511,7 @@ gun_default.construct = function(def)
def.consts = table.fill(def.parent_class.consts, def.consts or {})
Guns4d.gun.registered[def.name] = def
--register the visual entity
minetest.register_entity(def.name.."_visual", {
initial_properties = {
visual = "mesh",
@ -497,7 +542,6 @@ gun_default.construct = function(def)
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, lua_object.consts.AIMING_BONE, normal_pos, -axial_rot, visibility)
else

View File

@ -0,0 +1,43 @@
{
--defines the base recoil this gun experiences
recoil = {
--big ass table here
}
----------------------------------AMMUNITION--------------------------------------
(praise the lord and pass the ammunition can't afford to be a politician)
--note: internally clips are the same as a magazine (sort of)
--Ammo_handler derived class to handle ammunition, can be changed for modularity- not reccomended.
ammo_handler = Ammo_handler
--a table containing info about the magazines this gun may use.
magazine = {
--define if the gun only uses a magazine
magazine_only = false,
--the name of the magazine that the gun comes loaded with (optional)
comes_with = "modname:itemstring",
--a list of magazines that this gun takes. These must be registered via Guns4d.ammo.register_magazine()
--note that if the magazine contains unaccepted bullets (properties.accepted_bullets), the magazine won't be accepted.
accepted_mags = {
"modname:itemstring",
"modname:itemstring"
}
}
--list of bullets this gun accepts. These must be registered via Guns4d.ammo.register_bullet()
accepted_bullets = {
"modname:itemstring",
"modname:itemstring"
}

View File

@ -40,4 +40,5 @@ auxillary (beta+/never)
bullet tracers
inverse kinematics
stamina
a better system for reloading magazine (something like a inventory that fractionally reloads magazines so it takes time.)
a better system for reloading magazine (something like a inventory that fractionally reloads magazines so it takes time.)
create a non-player gun API

View File

@ -34,32 +34,6 @@ local default_def = {
},
},
firerateRPM = 600,
controls = {
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
},
fire = {
conditions = {"LMB"},
loop = true,
timer = 0,
func = function(active, interrupted, data, busy_list, handler)
if not handler.control_handler.busy_list.on_use then
handler.gun:fire()
end
end
},
on_use = function(itemstack, handler, pointed_thing)
handler.gun:fire()
handler.control_handler.busy_list.on_use = true
end
},
consts = {
HIP_PLAYER_GUN_ROT_RATIO = .6
},

View File

@ -12,6 +12,7 @@ path = path .. "/classes"
dofile(path.."/Instantiatable_class.lua")
dofile(path.."/Bullet_ray.lua")
dofile(path.."/Control_handler.lua")
dofile(path.."/Ammo_handler.lua")
dofile(path.."/Sprite_scope.lua")
dofile(path.."/Gun.lua")
dofile(path.."/Player_model_handler.lua")

View File

@ -6,6 +6,7 @@ function math.clamp(val, lower, upper)
if lower > upper then lower, upper = upper, lower end
return math.max(lower, math.min(upper, val))
end
--I need to store this so there arent duplicates lol
function Unique_id.generate()
local genned_ids = Unique_id.generated
local id = string.sub(tostring(math.random()), 3)
@ -15,12 +16,36 @@ function Unique_id.generate()
genned_ids[id] = true
return id
end
function math.weighted_randoms(tbl)
local total_weight = 0
local new_tbl = {}
for i, v in pairs(tbl) do
total_weight=total_weight+v
table.insert(new_tbl, {i, v})
end
local ran = math.random()*total_weight
--[[the point of the new table is so we can have them
sorted in order of weight, so we can check if the random
fufills the lower values first.]]
table.sort(new_tbl, function(a, b) return a[2] > b[2] end)
local scaled_weight = 0 --[[so this is added to the weight so it's chances are proportional
to it's actual weight as opposed to being wether the lower values are picked- if you have
weight 19 and 20, 20 would have a 1/20th chance of being picked if we didn't do this]]
for i, v in pairs(tbl) do
if (v[2]+scaled_weight) > ran then
return v[1]
end
scaled_weight = scaled_weight + v[2]
end
end
function math.rand_sign(b)
b = b or .5
local int = 1
if math.random() > b then int=-1 end
return int
end
--weighted randoms
--for table vectors that aren't vector objects
---@diagnostic disable-next-line: lowercase-global
function tolerance_check(a,b,tolerance)
@ -133,28 +158,6 @@ function table.fill(tbl, replacement, preserve_reference, indexed_tables)
end
return new_table
end
--fill "holes" in the tables.
function table.fill_in(tbl, replacement, preserve_reference, indexed_tables)
if not indexed_tables then indexed_tables = {} end --store tables to prevent circular referencing
local new_table = tbl
if not preserve_reference then
new_table = table.deep_copy(tbl)
end
for i, v in pairs(replacement) do
if new_table[i]==nil then
if type(v)=="table" then
new_table[i] = table.deep_copy(v)
else
new_table[i] = v
end
else
if (type(new_table[i]) == "table") and (type(v) == "table") then
table.fill_in(new_table[i], v, true, indexed_tables)
end
end
end
return new_table
end
--for class based OOP, ensure values containing a table in btbl are tables in a_tbl- instantiate, but do not fill.
function table.instantiate_struct(tbl, btbl, indexed_tables)
if not indexed_tables then indexed_tables = {} end --store tables to prevent circular referencing
@ -178,6 +181,8 @@ function table.shallow_copy(t)
return new_table
end
function weighted_randoms()
end
--for the following code and functions only:
--for license see the link on the next line.

View File

@ -11,6 +11,8 @@ Default_mag = {
craft_reload = true
}
Guns4d.ammo = {
default_empty_loaded_bullets = {
},
registered_bullets = {
},
@ -20,7 +22,7 @@ Guns4d.ammo = {
}
local max_wear = 65535
function Guns4d.ammo.register_bullet(def)
assert(def.itemstring)
assert(def.itemstring, "no itemstring")
assert(minetest.registered_items[def.itemstring], "no item '"..def.itemstring.."' found. Must be a registered item (check dependencies?)")
Guns4d.ammo.registered_bullets[def.itemstring] = table.fill(Default_bullet, def)
end
@ -32,16 +34,21 @@ function Guns4d.ammo.initialize_mag_data(itemstack, meta)
end
return itemstack
end
function Guns4d.ammo.update_mag_wear(def, itemstack, meta)
function Guns4d.ammo.update_mag(def, itemstack, meta)
meta = meta or itemstack:get_meta()
local bullets = minetest.deserialize(meta:get_string("guns4d_loaded_bullets"))
local count = 0
local current_bullet = "empty"
for i, v in pairs(bullets) do
current_bullet = i
count = count + v
end
itemstack:set_wear(max_wear-(max_wear*count/def.capacity))
meta:set_int("guns4d_total_bullets", count)
meta:set_string("guns4d_next_bullet", current_bullet)
return itemstack
end
function Guns4d.ammo.register_magazine(def)
def = table.fill(Default_mag, def)
assert(def.accepted_bullets, "missing property def.accepted_bullets. Need specified bullets to allow for loading")
@ -60,7 +67,7 @@ function Guns4d.ammo.register_magazine(def)
old_on_use(itemstack, user, pointed_thing)
end
local meta = itemstack:get_meta()
local ammo = minetest.deserialize(meta:get_string("guns4d_loaded_bullets"))
local ammo = meta:get_int("guns4d_total_bullets")
if ammo then
minetest.chat_send_player(user:get_player_name(), "rounds in magazine:")
for i, v in pairs(ammo) do
@ -149,8 +156,9 @@ function Guns4d.ammo.register_magazine(def)
end
mag_stack:set_count(mag_stack:get_count()-1)
craft_inv:set_stack("craft", mag_stack_index, mag_stack)
new_stack:get_meta():set_string("guns4d_loaded_bullets", minetest.serialize(new_ammo_table))
new_stack = Guns4d.ammo.update_mag_wear(def, new_stack)
local meta = new_stack:get_meta()
meta:set_string("guns4d_loaded_bullets", minetest.serialize(new_ammo_table))
new_stack = Guns4d.ammo.update_mag(def, new_stack, meta)
--print(new_stack:get_string())
return new_stack
end