partial implementation of Ammo_handlers, very crude documentation of ammunition related properties. Changed system for handling magazines.
This commit is contained in:
parent
21ab76ce95
commit
b059c175b8
@ -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
|
@ -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
|
||||
|
@ -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"
|
||||
}
|
@ -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
|
26
gun_api.lua
26
gun_api.lua
@ -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
|
||||
},
|
||||
|
1
init.lua
1
init.lua
@ -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")
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user