diff --git a/classes/Gun.lua b/classes/Gun.lua index 0467fb6..61b0cb3 100644 --- a/classes/Gun.lua +++ b/classes/Gun.lua @@ -1,9 +1,10 @@ local Vec = vector -local gun = { +local gun_default = { --itemstack = Itemstack --gun_entity = ObjRef name = "__template__", registered = {}, + property_modifiers = {}, properties = { hip = { offset = Vec.new(), @@ -55,12 +56,11 @@ local gun = { flash_offset = Vec.new(), aim_time = 1, firerateRPM = 10000, - controls = {} + controls = {}, + accepted_mags = {} }, offsets = { - pos = Vec.new(), player_rotation = Vec.new(), - dir = Vec.new(), --I'll need all three of them, do some precalculation. total_offset_rotation = { gun_axial = Vec.new(), @@ -93,9 +93,6 @@ local gun = { player_axial = Vec.new(), }, }, - particle_spawners = { - --muzzle_smoke - }, --magic number BEGONE consts = { HIP_PLAYER_GUN_ROT_RATIO = .75, @@ -106,7 +103,9 @@ local gun = { HAS_BREATHING = true, HAS_SWAY = true, HAS_WAG = true, + INFINITE_AMMO_IN_CREATIVE = true, }, + particle_spawners = {}, walking_tick = 0, time_since_last_fire = 0, time_since_creation = 0, @@ -114,7 +113,9 @@ local gun = { muzzle_flash = Guns4d.muzzle_flash } -function gun:fire() +function gun_default:spend_round() +end +function gun_default:fire() assert(self.instance, "attempt to call object method on a class") if self.rechamber_time <= 0 then local dir = self.dir @@ -138,7 +139,7 @@ function gun:fire() end end -function gun:recoil() +function gun_default:recoil() assert(self.instance, "attempt to call object method on a class") for axis, recoil in pairs(self.velocities.recoil) do for _, i in pairs({"x","y"}) do @@ -148,7 +149,7 @@ function gun:recoil() self.time_since_last_fire = 0 end -function gun:get_dir(rltv) +function gun_default:get_dir(rltv) assert(self.instance, "attempt to call object method on a class") local player = self.player local player_rotation @@ -161,24 +162,11 @@ function gun:get_dir(rltv) 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) +function gun_default:get_pos(added_pos) assert(self.instance, "attempt to call object method on a class") added_pos = Vec.new(added_pos) local player = self.player @@ -217,7 +205,7 @@ function gun:get_pos(added_pos) return bone_pos+gun_offset+handler:get_pos(), bone_pos, gun_offset end -function gun:add_entity() +function gun_default: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") local obj = self.entity:get_luaentity() @@ -225,7 +213,7 @@ function gun:add_entity() obj:on_step() end -function gun:has_entity() +function gun_default: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 @@ -233,7 +221,7 @@ function gun:has_entity() end --update the gun, da meat and da potatoes -function gun:update(dt) +function gun_default:update(dt) assert(self.instance, "attempt to call object method on a class") if not self:has_entity() then self:add_entity() end self.pos = self:get_pos() @@ -269,11 +257,6 @@ function gun:update(dt) --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 @@ -282,7 +265,8 @@ function gun:update(dt) 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 end -function gun:update_wag(dt) + +function gun_default:update_wag(dt) local handler = self.handler if handler.walking then self.walking_tick = self.walking_tick + (dt*Vec.length(self.player:get_velocity())) @@ -314,7 +298,8 @@ function gun:update_wag(dt) end end end -function gun:update_recoil(dt) + +function gun_default:update_recoil(dt) for axis, _ in pairs(self.offsets.recoil) do for _, i in pairs({"x","y"}) do local recoil = self.offsets.recoil[axis][i] @@ -351,7 +336,8 @@ function gun:update_recoil(dt) end end end -function gun:update_breathing(dt) + +function gun_default:update_breathing(dt) local breathing_info = {pause=1.4, rate=4.2} --we want X to be between 0 and 4.2. Since math.pi is a positive crest, we want X to be above it before it reaches our- --"length" (aka rate-pause), thus it will pi/length or pi/(rate-pause) will represent out slope of our control. @@ -364,7 +350,8 @@ function gun:update_breathing(dt) self.offsets.breathing.player_axial = scale*(math.sin(x)) end end -function gun:update_sway(dt) + +function gun_default:update_sway(dt) for axis, sway in pairs(self.offsets.sway) do local sway_vel = self.velocities.sway[axis] local ran @@ -384,18 +371,25 @@ function gun:update_sway(dt) self.velocities.sway[axis] = sway_vel end end -function gun:prepare_deletion() + +function gun_default:prepare_deletion() assert(self.instance, "attempt to call object method on a class") if self:has_entity() then self.entity:remove() end if self.sprite_scope then self.sprite_scope:prepare_deletion() end end ---construction for the gun class -gun.construct = function(def) +--construction for the base gun class +gun_default.construct = function(def) if def.instance then - --remember to give gun an id - assert(def.itemstack, "no itemstack provided for initialized object") - assert(def.player, "no player provided") + --make some quick checks. + assert(def.handler, "no player handler object provided") + + --initialize some variables + def.player = def.handler.player local meta = def.itemstack:get_meta() + def.meta = meta + local out = {} + + --create ID so we can track switches between weapons if meta:get_string("guns4d_id") == "" then local id = tostring(Unique_id.generate()) meta:set_string("guns4d_id", id) @@ -404,30 +398,55 @@ gun.construct = function(def) else def.id = meta:get_string("guns4d_id") end - --make sure there's nothing missing, aka copy over all of the properties. - def.properties = table.fill(gun.properties, def.properties) - --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) + + --unavoidable table instancing + def.properties = table.fill(def.base_class.properties, def.properties) + def.particle_spawners = {} --Instantiatable_class only shallow copies. So tables will not change, and thus some need to be initialized. + def.property_modifiers = {} + + --initialize all offsets + --def.offsets = table.deep_copy(def.base_class.offsets) + def.offsets = {} + for i, tbl in pairs(def.base_class.offsets) do + if (tbl.gun_axial and tbl.player_axial) then + local ty = type(tbl.gun_axial) + if (ty=="table") and tbl.gun_axial.x and tbl.gun_axial.y and tbl.gun_axial.z then + def.offsets[i] = {} + def.offsets[i].gun_axial = Vec.new() + def.offsets[i].player_axial = Vec.new() + else + def.offsets[i] = {} + def.offsets[i] = table.deep_copy(def.offsets[i]) + end + elseif tbl.x and tbl.y and tbl.z then + def.offsets[i] = Vec.new() end end - def.velocities = table.deep_copy(gun.velocities) - for i, tbl in pairs(def.velocities) do + + + --def.velocities = table.deep_copy(def.base_class.velocities) + def.velocities = {} + for i, tbl in pairs(def.base_class.velocities) do + def.velocities[i] = {} if tbl.gun_axial and tbl.player_axial then - tbl.gun_axial = Vec.new(tbl.gun_axial) - tbl.player_axial = Vec.new(tbl.player_axial) + def.velocities[i].gun_axial = Vec.new() + def.velocities[i].player_axial = Vec.new() end end + --properties have been assigned, create necessary objects + if def.properties.sprite_scope then + if not def.sprite_scope then + def.sprite_scope = def.properties.sprite_scope:new({ + gun = def + }) + end + end + elseif def.name ~= "__template__" then 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 @@ -445,8 +464,9 @@ gun.construct = function(def) Guns4d.players[user:get_player_name()].handler.control_handler:on_secondary_use(itemstack, pointed_thing) end }) + def.properties = table.fill(def.parent_class.properties, def.properties or {}) + def.consts = table.fill(def.parent_class.consts, def.consts or {}) - --(this tableref is ephermeral after constructor is called, see instantiatable_class) Guns4d.gun.registered[def.name] = def minetest.register_entity(def.name.."_visual", { initial_properties = { @@ -480,14 +500,14 @@ gun.construct = function(def) 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) + obj:set_attach(player, lua_object.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, visibility) + obj:set_attach(player, lua_object.consts.HIPFIRE_BONE, normal_pos, -axial_rot, visibility) end end }) end end -Guns4d.gun = Instantiatable_class:inherit(gun) \ No newline at end of file +Guns4d.gun = Instantiatable_class:inherit(gun_default) \ No newline at end of file diff --git a/classes/Gun_ammo.lua b/classes/Gun_ammo.lua new file mode 100644 index 0000000..daa9e1d --- /dev/null +++ b/classes/Gun_ammo.lua @@ -0,0 +1,56 @@ +Gun_ammo = Instantiatable_class:inherit({ + name = "Gun_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) + 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 + + 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 + end +end +function Gun_ammo:unload_mag() +end +function Gun_ammo:load_magless() +end +function Gun_ammo:unload_magless() +end +function Gun_ammo:load_fractional() +end +function Gun_ammo:unload_fractional() +end +function Gun_ammo:unload_chamber() +end \ No newline at end of file diff --git a/classes/Instantiatable_class.lua b/classes/Instantiatable_class.lua index 65cb4b5..f31e8cc 100644 --- a/classes/Instantiatable_class.lua +++ b/classes/Instantiatable_class.lua @@ -4,7 +4,8 @@ Instantiatable_class = { --not that construction change is NOT called for inheriting an object. function Instantiatable_class:inherit(def) --construction chain for inheritance - if not def then def = {} else def = table.shallow_copy(def) end + --if not def then def = {} else def = table.shallow_copy(def) end + def.parent_class = self def.instance = false def._construct_low = def.construct --this effectively creates a construction chain by overwriting .construct @@ -17,13 +18,15 @@ function Instantiatable_class:inherit(def) self.construct(parameters) end end + --print("CONSTRUCTED") def.construct(def) --iterate through table properties setmetatable(def, {__index = self}) return def end function Instantiatable_class:new(def) - if not def then def = {} else def = table.shallow_copy(def) end + --if not def then def = {} else def = table.shallow_copy(def) end + def.base_class = self def.instance = true function def:inherit(def) assert(false, "cannot inherit instantiated object") diff --git a/classes/Modifier.lua b/classes/Modifier.lua new file mode 100644 index 0000000..a31f0a1 --- /dev/null +++ b/classes/Modifier.lua @@ -0,0 +1,49 @@ + +function table.resolve_string(object, address) + local indexes = string.split(address) + local current + for i, v in pairs(indexes) do + current = current[i] + end + return current +end +local function split_into_adresses(object, path, out) + out = out or {} + path = path or "" + for index, val in pairs(object) do + local this_path = path.."."..index + if type(val) == "table" then + + end + + end + return out +end +Modifier = Instantiatable_class:inherit({ + overwrites = {}, + construct = function(def) + if def.instance then + assert(type(def.apply)=="function", "no application function found for modifier") + assert(def.name, "name is required for modifiers") + assert(def.properties, "cannot modify a nonexisent properties table") + local old_apply = def.apply + def.is_active = false + def.immutable_props = Proxy_table:get_or_create() + function def.apply(properties) + assert(not def.is_active, "attempt to double apply modifier '"..def.name.."'") + def.is_active = true + local proxy = Proxy_table:get_or_create(properties) --the proxy prevents unintended modification of the original table. + local add_table, override_table = old_apply(proxy) + if add_table then + + end + if override_table then + + end + end + function def.stop() + end + end + end, + +}) \ No newline at end of file diff --git a/classes/Player_handler.lua b/classes/Player_handler.lua index 759cc97..5e7a5fc 100644 --- a/classes/Player_handler.lua +++ b/classes/Player_handler.lua @@ -20,7 +20,7 @@ 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:is_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 @@ -28,22 +28,21 @@ function player_handler:update(dt) --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 - + --initialize important player data + self.itemstack = self.wielded_item + self.inventory = player:get_inventory() ----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. - + self.gun = held_gun:new({itemstack=self.wielded_item, handler=self}) --this will set itemstack meta, and create the gun based off of meta and other data. ----model handler---- if self.model_handler then --if model_handler present, then delete self.model_handler:prepare_deletion() self.model_handler = nil 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 @@ -51,7 +50,6 @@ function player_handler:update(dt) 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 @@ -163,7 +161,7 @@ function player_handler:set_properties(properties) self.player:set_properties(properties) self.properties = table.fill(self.properties, properties) end -function player_handler:is_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 diff --git a/classes/Proxy_table.lua b/classes/Proxy_table.lua new file mode 100644 index 0000000..82fbf75 --- /dev/null +++ b/classes/Proxy_table.lua @@ -0,0 +1,58 @@ +Proxy_table = { + registered_proxies = {}, + proxy_children = {} +} +--this creates proxy tables in a structure of tables +--this is great if you want to prevent the change of a table +--but still want it to be viewable, such as with constants +function Proxy_table:new(og_table, parent) + local new = {} + self.registered_proxies[og_table] = new + if parent then + self.proxy_children[parent][og_table] = true + else + self.proxy_children[og_table] = {} + parent = og_table + end + --set the proxy's metatable + setmetatable(new, { + __index = function(t, key) + if type(og_table[key]) == "table" then + return Proxy_table:get_or_create(og_table[key], parent) + else + return og_table[key] + end + end, + __newindex = function(table, key) + assert(false, "attempt to edit immutable table, cannot edit a proxy") + end, + }) + --[[overwrite og_table meta to destroy the proxy aswell (but I realized it wont be GCed unless it's removed altogether, so this is pointless) + local mtable = getmetatable(og_table) + local old_gc = mtable.__gc + mtable.__gc = function(t) + self.registered_proxies[t] = nil + self.proxy_children[t] = nil + old_gc(t) + end + setmetatable(og_table, mtable)]] + --premake proxy tables + for i, v in pairs(og_table) do + if type(v) == "table" then + Proxy_table:get_or_create(v, parent) + end + end + return new +end +function Proxy_table:get_or_create(og_table, parent) + return self.registered_proxies[og_table] or Proxy_table:new(og_table, parent) +end +function Proxy_table:destroy_proxy(parent) + self.registered_proxies[parent] = nil + if self.proxy_children[parent] then + for i, v in pairs(self.proxy_children[parent]) do + Proxy_table:destroy_proxy(i) + end + end + self.proxy_children[parent] = nil +end \ No newline at end of file diff --git a/classes/Sprite_scope.lua b/classes/Sprite_scope.lua index df922cd..b881559 100644 --- a/classes/Sprite_scope.lua +++ b/classes/Sprite_scope.lua @@ -2,7 +2,7 @@ Sprite_scope = Instantiatable_class:inherit({ images = { fore = { texture = "scope_fore.png", - scale = {x=10,y=10}, + scale = {x=11,y=11}, movement_multiplier = 1, }, back = { diff --git a/docs/future control scheme ideas.txt b/docs/future control scheme ideas.txt new file mode 100644 index 0000000..b2f6c61 --- /dev/null +++ b/docs/future control scheme ideas.txt @@ -0,0 +1,18 @@ + +z = reload +tap shift+z = switch fire mode + +while aiming + q-e = aux switch leaning side/aiming side (default is always right) + +while hip + e = aux + hold shift+z = open gun menu + q = drop the weapon + +gun menu + turn safety on (can be turned off with shift+z) + unload weapon + set preferred ammo type(s) + set preferred ammo type weighting(?) (allow for control over when there's not enough ammo in a mag to prefer it) + add modifications diff --git a/register_magazine.lua b/docs/gun properties.txt similarity index 100% rename from register_magazine.lua rename to docs/gun properties.txt diff --git a/docs/modifier class.txt b/docs/modifier class.txt new file mode 100644 index 0000000..8ae421c --- /dev/null +++ b/docs/modifier class.txt @@ -0,0 +1,14 @@ +The modifier class is a class that's used to specify +specific changes to a gun's properties. + +to make a modifier, do something like this +Modifier:new({ + apply = function(properties) + --absolutely DO NOT EVER change properties here + --this function is only so you can calculate changes + --based on the properties of the gun. + return { + + } + end +}) \ No newline at end of file diff --git a/docs/required_features.txt b/docs/required_features.txt index 9970af4..e701997 100644 --- a/docs/required_features.txt +++ b/docs/required_features.txt @@ -1,4 +1,6 @@ + + VFX, SFX: Bullet hit node FX (steal mostly from 3dguns) Bullet fly-by SFX @@ -20,6 +22,8 @@ gun features "3d" optics attachments (last before beta) fix shooting through walls be pressing against them + correct player look-down + gun leaning bullets bullet class system diff --git a/gun_api.lua b/gun_api.lua index fe311a9..07023e5 100644 --- a/gun_api.lua +++ b/gun_api.lua @@ -53,11 +53,9 @@ local default_def = { 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 diff --git a/init.lua b/init.lua index 3d68a62..ce23537 100644 --- a/init.lua +++ b/init.lua @@ -15,6 +15,7 @@ dofile(path.."/Sprite_scope.lua") dofile(path.."/Gun.lua") dofile(path.."/Player_model_handler.lua") dofile(path.."/Player_handler.lua") +dofile(path.."/Proxy_table.lua") --load after path = minetest.get_modpath("guns4d") diff --git a/misc_helpers.lua b/misc_helpers.lua index d3bf03c..e0a81fe 100644 --- a/misc_helpers.lua +++ b/misc_helpers.lua @@ -58,6 +58,56 @@ function table.deep_copy(tbl, copy_metatable, indexed_tables) end return new_table end + + +function table.contains(tbl, value) + for i, v in pairs(tbl) do + if v == value then + return i + end + end + return false +end +local function parse_index(i) + if type(i) == "string" then + return "[\""..i.."\"]" + else + return "["..tostring(i).."]" + end +end +--dump() sucks. +function table.tostring(tbl, shallow, tables, depth) + --create a list of tables that have been tostringed in this chain + if not table then return "nil" end + if not tables then tables = {this_table = tbl} end + if not depth then depth = 0 end + depth = depth + 1 + local str = "{" + local initial_string = "\n" + for i = 1, depth do + initial_string = initial_string .. " " + end + for i, v in pairs(tbl) do + local val_type = type(v) + if val_type == "string" then + str = str..initial_string..parse_index(i).." = \""..v.."\"," + elseif val_type == "table" and (not shallow) then + local contains = table.contains(tables, v) + --to avoid infinite loops, make sure that the table has not been tostringed yet + if not contains then + tables[i] = v + str = str..initial_string..parse_index(i).." = "..table.tostring(v, shallow, tables, depth).."," + else + str = str..initial_string..parse_index(i).." = "..tostring(v).." ("..contains..")," + end + else + str = str..initial_string..parse_index(i).." = "..tostring(v).."," + end + end + return str..string.sub(initial_string, 1, -5).."}" +end + + --replace elements in tbl with elements in replacement, but preserve the rest function table.fill(tbl, replacement, preserve_reference, indexed_tables) if not indexed_tables then indexed_tables = {} end --store tables to prevent circular referencing diff --git a/register_ammo.lua b/register_ammo.lua new file mode 100644 index 0000000..c23c36e --- /dev/null +++ b/register_ammo.lua @@ -0,0 +1,46 @@ + +Default_bullet = { + 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 +} +Guns4d.ammo = { + registered_bullets = { + + }, + registered_magazines = { + + } +} +function Guns4d.ammo.register_bullet(def) + assert(def.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 +function Guns4d.ammo.register_magazine(def) + assert(def.accepted_bullets, "missing property def.accepted_bullets. Need specified bullets to allow for loading") + for i, v in pairs(def.accepted_bullets) do + if not Guns4d.ammo.registered_bullets[v] then print("WARNING! bullet "..v.." not registered! is this a mistake?") end + end + --register craft prediction + minetest.register_craft_predict(function(itemstack, player, old_craft_grid, craft_inv) + if craft_inv:contains_item("craft", def.itemstring) and itemstack:get_name()=="" then + --potentially give predicted ammo gauge here + return def.itemstring + end + end) + minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv) + if craft_inv:contains_item("craft", def.itemstring) and craft_inv:contains_item("craftpreview", def.itemstring) then + end + end) + --register the actual recipe to add ammo to a mag +end