account for position in animation, player model support for reflector sights, reflector sight class, versioning system
This commit is contained in:
parent
459683814e
commit
4cc3c030a5
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -5,7 +5,8 @@
|
||||
"dump",
|
||||
"player_api",
|
||||
"ItemStack",
|
||||
"leef"
|
||||
"leef",
|
||||
"core"
|
||||
],
|
||||
"Lua.diagnostics.disable": [
|
||||
"undefined-field"
|
||||
|
@ -1,94 +0,0 @@
|
||||
--will have to merge with ammo_handler eventually for coherency.
|
||||
local attachment_handler = leef.class.new_class:inherit({})
|
||||
Guns4d.attachment_handler = attachment_handler
|
||||
function attachment_handler:construct()
|
||||
assert(self.gun, "no gun object provided")
|
||||
local meta = self.gun.meta
|
||||
|
||||
if self.instance then
|
||||
self.modifier = {}
|
||||
self.gun.property_modifiers = self.modifier
|
||||
self.handler = self.gun.handler
|
||||
if meta:get_string("guns4d_attachments") == "" then
|
||||
self.attachments = {}
|
||||
for i, v in pairs(self.gun.properties.inventory.attachment_slots) do
|
||||
self.attachments[i] = {}
|
||||
if type(v.default)=="string" then
|
||||
self:add_attachment(v.default)
|
||||
end
|
||||
end
|
||||
meta:set_string("guns4d_attachments", minetest.serialize(self.attachments))
|
||||
else
|
||||
self.attachments = minetest.deserialize(meta:get_string("guns4d_attachments"))
|
||||
--self:update_meta()
|
||||
end
|
||||
end
|
||||
end
|
||||
Guns4d.registered_attachments = {}
|
||||
function attachment_handler.register_attachment(def)
|
||||
assert(def.itemstring, "itemstring field required")
|
||||
--assert(def.modifier)
|
||||
Guns4d.registered_attachments[def.itemstring] = def
|
||||
end
|
||||
function attachment_handler:rebuild_modifiers()
|
||||
--rebuild the modifier
|
||||
local new_mods = self.modifier
|
||||
local index = 1
|
||||
--replace indices with modifiers
|
||||
for _, v in pairs(self.attachments) do
|
||||
for name, _ in pairs(v) do
|
||||
if Guns4d.registered_attachments[name].modifier then
|
||||
new_mods[index]=Guns4d.registered_attachments[name].modifier
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
--remove any remaining modifiers
|
||||
if index < #new_mods then
|
||||
for i=index, #new_mods do
|
||||
new_mods[i]=nil
|
||||
end
|
||||
end
|
||||
self.gun.property_modifiers["attachment_handler"] = self.modifier
|
||||
end
|
||||
--returns bool indicating success.
|
||||
function attachment_handler:add_attachment(itemstack, slot)
|
||||
assert(self.instance)
|
||||
itemstack = ItemStack(itemstack)
|
||||
local stackname = itemstack:get_name()
|
||||
if self:can_add(itemstack, slot) then
|
||||
self.attachments[slot][stackname] = itemstack
|
||||
self:rebuild_modifiers()
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
function attachment_handler:can_add(itemstack, slot)
|
||||
assert(self.instance)
|
||||
local name = itemstack:get_name()
|
||||
local props = self.gun.properties
|
||||
--print(slot, dump(self.attachments))
|
||||
if Guns4d.registered_attachments[name] and (not self.attachments[slot][name]) and (props.inventory.attachment_slots[slot].allowed) then
|
||||
--check if it's allowed, group check required
|
||||
for i, v in pairs(props.inventory.attachment_slots[slot].allowed) do
|
||||
--print(v, name)
|
||||
if v==name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
--returns bool indicating success.
|
||||
function attachment_handler:remove_attachment(itemstack, slot)
|
||||
assert(self.instance)
|
||||
local stackname = itemstack:get_name()
|
||||
if (self.attachments[slot][stackname]) then
|
||||
self.attachments[slot][stackname] = nil
|
||||
self:rebuild_modifiers()
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
@ -8,6 +8,9 @@ local ray = {
|
||||
--exit_direction = dir,
|
||||
--range_left = def.bullet.range,
|
||||
--energy = def.bullet.penetration_RHA
|
||||
--raw_sharp_damage = 0,
|
||||
--raw_blunt_damage = 0,
|
||||
--sharp_penetration = 0,
|
||||
sharp_to_blunt_conversion_factor = .5, -- 1mmRHA is converted to 1mPA of blunt force
|
||||
blunt_damage_groups = {}, --minetest.deserialize(Guns4d.config.default_blunt_groups), --these are multiplied by blunt_damage
|
||||
sharp_damage_groups = {}, --minetest.deserialize(Guns4d.config.default_sharp_groups),
|
||||
|
@ -13,7 +13,7 @@ local mat4 = leef.math.mat4
|
||||
*
|
||||
]]
|
||||
|
||||
local function initialize_data(self)
|
||||
local function initialize_tracking_meta(self)
|
||||
--create ID so we can track switches between weapons, also get some other data.
|
||||
local meta = self.itemstack:get_meta()
|
||||
self.meta = meta
|
||||
@ -30,15 +30,18 @@ local function initialize_data(self)
|
||||
end
|
||||
end
|
||||
local function initialize_ammo(self)
|
||||
self.ammo_handler = self.properties.ammo_handler:new({ --initialize ammo handler from gun and gun metadata.
|
||||
--initialize the ammo handler
|
||||
self.ammo_handler = self.properties.subclasses.ammo_handler:new({ --initialize ammo handler from gun and gun metadata.
|
||||
gun = self
|
||||
})
|
||||
local ammo = self.ammo_handler.ammo
|
||||
self.subclass_instances.ammo_handler = self.ammo_handler
|
||||
--draw the gun if properties specify it
|
||||
if self.properties.require_draw_on_swap then
|
||||
ammo.next_bullet = "empty"
|
||||
self.ammo_handler.ammo.next_bullet = "empty"
|
||||
end
|
||||
minetest.after(0, function() if ammo.total_bullets > 0 then self:draw() end end)
|
||||
self:update_image_and_text_meta() --has to be called manually in post as ammo_handler would not exist yet.
|
||||
minetest.after(0, function() if self.ammo_handler.ammo.total_bullets > 0 then self:draw() end end) --call this as soon as the gun is loaded in
|
||||
--update metadata
|
||||
self:update_image_and_text_meta()
|
||||
self.player:set_wielded_item(self.itemstack)
|
||||
end
|
||||
|
||||
@ -71,51 +74,59 @@ local function initialize_physics(self)
|
||||
end
|
||||
end
|
||||
|
||||
local function initialize_animation(self)
|
||||
local function initialize_animation_tracking_data(self)
|
||||
self.animation_data = { --where animations data is stored.
|
||||
anim_runtime = 0,
|
||||
runtime = 0,
|
||||
length = 0,
|
||||
fps = 0,
|
||||
frames = {0,0},
|
||||
frames = {x=0,y=0},
|
||||
current_frame = 0,
|
||||
}
|
||||
self.player_rotation = vector.new(self.properties.initial_vertical_rotation,0,0)
|
||||
self.animation_rotation = vector.new()
|
||||
self.animation_translation = vector.new()
|
||||
end
|
||||
|
||||
function gun_default:construct_instance()
|
||||
assert(self.handler, "no player handler object provided")
|
||||
|
||||
--initialize important data.
|
||||
self.player = self.handler.player
|
||||
initialize_data(self)
|
||||
initialize_ammo(self)
|
||||
|
||||
--unavoidable table instancing
|
||||
self.properties = Guns4d.table.fill(self.base_class.properties, self.properties)
|
||||
--instantiate some tables for runtime data
|
||||
self.property_modifiers = {}
|
||||
self.subclass_instances = {}
|
||||
self.particle_spawners = {}
|
||||
self.property_modifiers = {}
|
||||
|
||||
initialize_animation(self)
|
||||
initialize_physics(self)
|
||||
|
||||
if self.properties.inventory.attachment_slots then
|
||||
self.attachment_handler = self.properties.attachment_handler:new({
|
||||
--initialize important stuff
|
||||
self.player = self.handler.player
|
||||
self:add_entity()
|
||||
initialize_tracking_meta(self)
|
||||
|
||||
--initialize properties now that any attachments or ammo modifiers have been applied
|
||||
self._properties_unsafe = Guns4d.table.deep_copy(self.base_class.properties) --we need this copy because proxy tables dont prevent garbage collection
|
||||
self.properties = leef.class.proxy_table.new(self._properties_unsafe)
|
||||
|
||||
--initialize built in subclasses
|
||||
if self.properties.inventory and self.properties.inventory.part_slots then
|
||||
self.subclass_instances.part_handler = self.properties.subclasses.part_handler:new({
|
||||
gun = self
|
||||
})
|
||||
end
|
||||
if self.properties.sprite_scope then
|
||||
self.sprite_scope = self.properties.sprite_scope:new({
|
||||
gun = self
|
||||
})
|
||||
end
|
||||
if self.properties.crosshair then
|
||||
self.crosshair = self.properties.crosshair:new({
|
||||
gun = self
|
||||
})
|
||||
|
||||
--initialize special subclasses
|
||||
initialize_ammo(self)
|
||||
initialize_animation_tracking_data(self)
|
||||
|
||||
--initialize any remaining subclasses
|
||||
for i, class in pairs(self.properties.subclasses) do
|
||||
if (not self.subclass_instances[i]) and (i~="part_handler") then
|
||||
self.subclass_instances[i] = class:new({
|
||||
gun = self
|
||||
})
|
||||
end
|
||||
end
|
||||
self.part_handler = self.subclass_instances.part_handler
|
||||
|
||||
if self.custom_construct then self:custom_construct() end
|
||||
self:regenerate_properties()
|
||||
end
|
||||
|
||||
--[[
|
||||
@ -146,7 +157,7 @@ local function validate_controls(props)
|
||||
--validate controls, done before properties are filled to avoid duplication.
|
||||
if props.control_actions then
|
||||
for i, control in pairs(props.control_actions) do
|
||||
if (i~="on_use") and (i~="on_secondary_use") and (i~="__overfill") then
|
||||
if (i~="on_use") and (i~="on_secondary_use") and (i~="__replace_old_table") then
|
||||
assert(control.conditions, "no conditions provided for control")
|
||||
for _, condition in pairs(control.conditions) do
|
||||
if not valid_ctrls[condition] then
|
||||
@ -162,7 +173,8 @@ local function initialize_b3d_animation_data(self, props)
|
||||
self.b3d_model.global_frames = {
|
||||
arm_right = {}, --the aim position of the right arm
|
||||
arm_left = {}, --the aim position of the left arm
|
||||
rotation = {} --rotation of the gun (this is assumed as gun_axial, but that's probably fucked for holo sight alignments)
|
||||
root_rotation = {},
|
||||
root_translation = {}
|
||||
}
|
||||
--print(table.tostring(self.b3d_model))
|
||||
--precalculate keyframe "samples" for intepolation.
|
||||
@ -186,11 +198,16 @@ local function initialize_b3d_animation_data(self, props)
|
||||
|
||||
--we compose it by the inverse because we need to get the global offset in rotation for the animation rotation offset. I really need to comment more often
|
||||
--print(leef.b3d_nodes.get_node_rotation(nil, main, nil, -1))
|
||||
local newvec = leef.b3d_nodes.get_node_rotation(nil, main, nil, target_frame)*leef.b3d_nodes.get_node_rotation(nil, main, nil, props.visuals.animations.loaded.x):inverse()
|
||||
--delta rotation
|
||||
local this_transform, this_rotation = leef.b3d_nodes.get_node_global_transform(main, target_frame)
|
||||
local rest_transform, rest_rotation = leef.b3d_nodes.get_node_global_transform(main, props.visuals.animations.loaded.x)
|
||||
local quat = this_rotation*rest_rotation:inverse()
|
||||
local vec3 = vector.new(this_transform[13], this_transform[14], this_transform[15])-vector.new(rest_transform[13], rest_transform[14], rest_transform[15]) --extract translation
|
||||
--used to use euler
|
||||
table.insert(self.b3d_model.global_frames.rotation, newvec)
|
||||
table.insert(self.b3d_model.global_frames.root_rotation, quat)
|
||||
table.insert(self.b3d_model.global_frames.root_translation, vec3)
|
||||
end
|
||||
local t, r = leef.b3d_nodes.get_node_global_transform(main, props.visuals.animations.loaded.x,1)
|
||||
local t, _ = leef.b3d_nodes.get_node_global_transform(main, props.visuals.animations.loaded.x,1)
|
||||
self.b3d_model.root_orientation_rest = mat4.new(t)
|
||||
self.b3d_model.root_orientation_rest_inverse = mat4.invert(mat4.new(), t)
|
||||
|
||||
@ -277,15 +294,38 @@ local function reregister_item(self, props)
|
||||
animation = self.properties.visuals.animations.loaded
|
||||
})
|
||||
end
|
||||
--accept a chain of indices where the value from old_index overrides new_index
|
||||
local function warn_deprecation(gun, field, new_field)
|
||||
minetest.log("warning", "Guns4d: `"..gun.."` deprecated use of field `"..field.."` use `"..new_field.."` instead.")
|
||||
end
|
||||
local function patch_old_gun(self, minor_version)
|
||||
local props = self.properties
|
||||
--minor version 2 changes...
|
||||
if minor_version==2 then
|
||||
if props.firemode_inventory_overlays then
|
||||
warn_deprecation(self.name, "firemode_inventory_overlays", "inventory.firemode_inventory_overlays")
|
||||
for i, _ in pairs(props.firemode_inventory_overlays) do
|
||||
props.inventory.firemode_inventory_overlays[i] = props.firemode_inventory_overlays[i]
|
||||
end
|
||||
end
|
||||
for _, i in pairs {"ammo_handler", "part_handler", "crosshair", "sprite_scope"} do
|
||||
if props[i] then
|
||||
warn_deprecation(self.name, i, "subclasses."..i)
|
||||
props.subclasses[i] = props[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--========================== MAIN CLASS CONSTRUCTOR ===============================
|
||||
|
||||
function gun_default:construct_base_class()
|
||||
local props = self.properties
|
||||
|
||||
--copy the properties
|
||||
self.properties = Guns4d.table.fill(self.parent_class.properties, props or {})
|
||||
self.consts = Guns4d.table.fill(self.parent_class.consts, self.consts or {})
|
||||
props = self.properties
|
||||
self._properties_unsafe = Guns4d.table.fill(self.parent_class.properties, self.properties or {})
|
||||
self.properties = self._properties_unsafe
|
||||
self._consts_unsafe = Guns4d.table.fill(self.parent_class.consts, self.consts or {})
|
||||
self.consts = self._consts_unsafe
|
||||
|
||||
local props = self.properties
|
||||
validate_controls(props)
|
||||
assert((self.properties.recoil.velocity_correction_factor.gun_axial>=1) and (self.properties.recoil.velocity_correction_factor.player_axial>=1), "velocity correction must not be less than one.")
|
||||
|
||||
@ -304,7 +344,18 @@ function gun_default:construct_base_class()
|
||||
for _, v in pairs(self.properties.ammo.accepted_magazines) do
|
||||
self.accepted_magazines[v] = true
|
||||
end
|
||||
self.properties = leef.class.proxy_table:new(self.properties)
|
||||
|
||||
--versioning and backwards compatibility stuff
|
||||
assert(self.consts.VERSION[1]==Guns4d.version[1], "Guns4d gun `"..self.name.." has major version mismatch")
|
||||
if self.consts.VERSION[1] ~= Guns4d.version[1] then
|
||||
minetest.log("error", "Guns4d gun `"..self.name.."` minor version mismatch")
|
||||
end
|
||||
if self.consts.VERSION[2] < 3 then
|
||||
minetest.log("error", "Guns4d: `"..self.name.."` had minor version before `1.3.0` indicating that this gun likely has no versioning. Attempting patches for `1.2.0`...")
|
||||
patch_old_gun(self, 2)
|
||||
end
|
||||
|
||||
self.properties = leef.class.proxy_table.new(self.properties)
|
||||
self.consts = leef.class.proxy_table.new(self.consts)
|
||||
Guns4d.gun._registered[self.name] = self --add gun self to the registered table
|
||||
end
|
@ -15,9 +15,7 @@ end
|
||||
-- @tparam float 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(); self:clear_animation() end
|
||||
local handler = self.handler
|
||||
|
||||
if not self:has_entity() then self:add_entity() self:clear_animation() end
|
||||
--player look rotation. I'm going to keep it real, I don't remember what this math does. Player handler just stores the player's rotation from MT in degrees, which is for some reason inverted
|
||||
|
||||
--it's set up like this so that if the gun is fired on auto and the RPM is very fast (faster then globalstep) we know how many rounds to let off.
|
||||
@ -31,27 +29,102 @@ function gun_default:update(dt)
|
||||
if self.burst_queue > 0 then self:update_burstfire() end
|
||||
--update some vectors
|
||||
self:update_look_offsets(dt)
|
||||
--I should make this into a list
|
||||
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:update_animation(dt)
|
||||
self.dir = self:get_dir()
|
||||
self.dir = self:get_dir(nil,nil,nil,self.consts.ANIMATIONS_OFFSET_AIM)
|
||||
self.pos = self:get_pos()+self.handler:get_pos()
|
||||
|
||||
--update subclasses
|
||||
self:update_entity()
|
||||
if self.properties.sprite_scope then
|
||||
self.sprite_scope:update()
|
||||
end
|
||||
if self.properties.crosshair then
|
||||
self.crosshair:update()
|
||||
--this should really be a list of subclasses so its more easily expansible
|
||||
for i, instance in pairs(self.subclass_instances) do
|
||||
if instance.update then instance:update(dt) end
|
||||
if not self.properties.subclasses[i] then
|
||||
instance:prepare_deletion()
|
||||
self.subclass_instances[i] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--finalize transforms
|
||||
self:update_transforms()
|
||||
end
|
||||
|
||||
function gun_default:regenerate_properties()
|
||||
self._properties_unsafe = Guns4d.table.deep_copy(self.base_class.properties)
|
||||
self.properties = self._properties_unsafe
|
||||
for i, func in pairs(self.property_modifiers) do
|
||||
func(self)
|
||||
end
|
||||
self.properties = leef.class.proxy_table.new(self.properties)
|
||||
self:update_visuals()
|
||||
end
|
||||
|
||||
--- not typically called every step, updates the gun object's visuals
|
||||
function gun_default:update_visuals()
|
||||
local props = self.properties
|
||||
self.entity:set_properties({
|
||||
mesh = props.visuals.mesh,
|
||||
textures = table.copy(props.visuals.textures),
|
||||
backface_culling = props.visuals.backface_culling,
|
||||
visual_size = {x=10*props.visuals.scale,y=10*props.visuals.scale,z=10*props.visuals.scale}
|
||||
})
|
||||
for i, ent in pairs(self.attached_objects) do
|
||||
if not self.properties.visuals.attached_objects[i] then
|
||||
ent:remove()
|
||||
end
|
||||
end
|
||||
for i, attached in pairs(self.properties.visuals.attached_objects) do
|
||||
if attached.mesh then
|
||||
assert(type(attached)=="table", self.name..": `attached.objects` expects a list of tables, incorrect type given.")
|
||||
local obj
|
||||
if (not self.attached_objects[i]) or (not self.attached_objects[i]:is_valid()) then
|
||||
obj = minetest.add_entity(self.handler:get_pos(), "guns4d:gun_entity")
|
||||
self.attached_objects[i] = obj
|
||||
else
|
||||
obj = self.attached_objects[i]
|
||||
end
|
||||
obj:set_properties({
|
||||
mesh = attached.mesh,
|
||||
textures = table.copy(attached.textures or self.properties.visuals.textures),
|
||||
backface_culling = attached.backface_culling,
|
||||
visual_size = {x=attached.scale or 1, y=attached.scale or 1, z=attached.scale or 1}
|
||||
})
|
||||
local offset
|
||||
if attached.offset then
|
||||
offset = attached.offset
|
||||
offset = mat4.mul_vec4({}, self.b3d_model.root_orientation_rest_inverse, {offset.x, offset.y, offset.z, 0})
|
||||
offset = {x=offset[1], y=offset[2], z=offset[3]}
|
||||
end
|
||||
local rotation
|
||||
if attached.rotation then
|
||||
rotation = attached.rotation
|
||||
local rotm4 = mat4.set_rot_luanti_entity(mat4.identity(), rotation.x*math.pi/180, rotation.y*math.pi/180, rotation.z*math.pi/180)
|
||||
rotm4 = self.b3d_model.root_orientation_rest_inverse*rotm4
|
||||
rotation = {rotm4:get_rot_luanti_entity()}
|
||||
rotation = {x=rotation[1]*180/math.pi, y=rotation[2]*180/math.pi, z=rotation[3]*180/math.pi}
|
||||
else
|
||||
rotation = {(self.b3d_model.root_orientation_rest_inverse):get_rot_luanti_entity()}
|
||||
rotation = {x=rotation[1]*180/math.pi, y=rotation[2]*180/math.pi, z=rotation[3]*180/math.pi}
|
||||
end
|
||||
obj:set_attach(
|
||||
self.entity,
|
||||
self.consts.ROOT_BONE,
|
||||
offset,
|
||||
rotation
|
||||
--true
|
||||
)
|
||||
else
|
||||
minetest.log("error", "Guns4d: attached object had no mesh")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- updates self.total_offsets which stores offsets for bones
|
||||
function gun_default:update_transforms()
|
||||
local total_offset = self.total_offsets
|
||||
@ -76,6 +149,7 @@ end
|
||||
--- Update and fire the queued weapon burst
|
||||
function gun_default:update_burstfire()
|
||||
if self.rechamber_time <= 0 then
|
||||
local iter = 1
|
||||
while true do
|
||||
local success = self:attempt_fire()
|
||||
if success then
|
||||
@ -86,6 +160,7 @@ function gun_default:update_burstfire()
|
||||
end
|
||||
break
|
||||
end
|
||||
iter = iter + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -132,8 +207,8 @@ function gun_default:update_image_and_text_meta(meta)
|
||||
for i, v in pairs(self.properties.firemodes) do
|
||||
firemodes = firemodes+1
|
||||
end
|
||||
if firemodes > 1 and self.properties.firemode_inventory_overlays[self.properties.firemodes[self.current_firemode]] then
|
||||
image = image.."^"..self.properties.firemode_inventory_overlays[self.properties.firemodes[self.current_firemode]]
|
||||
if firemodes > 1 and self.properties.inventory.firemode_inventory_overlays[self.properties.firemodes[self.current_firemode]] then
|
||||
image = image.."^"..self.properties.inventory.firemode_inventory_overlays[self.properties.firemodes[self.current_firemode]]
|
||||
end
|
||||
if self.handler.infinite_ammo then
|
||||
image = image.."^"..self.properties.infinite_inventory_overlay
|
||||
@ -193,6 +268,12 @@ function gun_default:attempt_fire()
|
||||
self:play_sounds(fire_sound)
|
||||
|
||||
self.rechamber_time = self.rechamber_time + (60/props.firerateRPM)
|
||||
|
||||
--acount for animation rotation in same update firing
|
||||
if (self.rechamber_time<(60/props.firerateRPM)) and props.firemodes[self.current_firemode]~="single" then
|
||||
self.animation_data.runtime = self.animation_data.runtime + (60/props.firerateRPM)
|
||||
self:update_animation_transforms()
|
||||
end
|
||||
return true
|
||||
end
|
||||
end
|
||||
@ -231,6 +312,77 @@ function gun_default:recoil()
|
||||
end
|
||||
self.time_since_last_fire = 0
|
||||
end
|
||||
function gun_default:open_inventory_menu()
|
||||
local props = self.properties
|
||||
local player = self.player
|
||||
local pname = player:get_player_name()
|
||||
local inv = minetest.get_inventory({type="player", name=pname})
|
||||
local window = minetest.get_player_window_information(pname)
|
||||
local listname = Guns4d.config.inventory_listname
|
||||
local form_dimensions = {x=20,y=15}
|
||||
|
||||
local inv_height=4+((4-1)*.125)
|
||||
local hotbar_length = player:hud_get_hotbar_itemcount()
|
||||
local form = "\
|
||||
formspec_version[7]\
|
||||
size[".. form_dimensions.x ..",".. form_dimensions.y .."]"
|
||||
|
||||
local hotbar_height = math.ceil(hotbar_length/8)
|
||||
form = form.."\
|
||||
scroll_container[.25,"..(form_dimensions.y)-inv_height-1.25 ..";10,5;player_inventory;vertical;.05]\
|
||||
list[current_player;"..listname..";0,0;"..hotbar_length..","..hotbar_height..";]\
|
||||
list[current_player;"..listname..";0,1.5;8,3;"..hotbar_length.."]\
|
||||
scroll_container_end[]\
|
||||
"
|
||||
if math.ceil(inv:get_size("main")/8) > 4 then
|
||||
local h = math.ceil(inv:get_size("main")/8)
|
||||
form=form.."\
|
||||
scrollbaroptions[max="..h+((h-1)*.125).."]\
|
||||
scrollbar[10.25,"..(form_dimensions.y)-inv_height-1.25 ..";.5,5;vertical;player_inventory;0]\
|
||||
"
|
||||
end
|
||||
--display gun preview
|
||||
local len = math.abs(self.model_bounding_box[3]-self.model_bounding_box[6])/props.visuals.scale
|
||||
local hei = math.abs(self.model_bounding_box[2]-self.model_bounding_box[5])/props.visuals.scale
|
||||
local offsets = {x=(-self.model_bounding_box[6]/props.visuals.scale)-(len/2), y=(self.model_bounding_box[5]/props.visuals.scale)+(hei/2)}
|
||||
|
||||
local meter_scale = 15
|
||||
local image_scale = meter_scale*(props.inventory.render_size or 1)
|
||||
local gun_gui_offset = {x=0,y=-2.5}
|
||||
form = form.."container["..((form_dimensions.x-image_scale)/2)+gun_gui_offset.x.. ","..((form_dimensions.y-image_scale)/2)+gun_gui_offset.y.."]"
|
||||
if props.inventory.render_image then
|
||||
form = form.."image["
|
||||
..(offsets.x*meter_scale) ..","
|
||||
..(offsets.y*meter_scale) ..";"
|
||||
..image_scale..","
|
||||
..image_scale..";"
|
||||
..props.inventory.render_image.."]"
|
||||
end
|
||||
if self.part_handler then
|
||||
--local attachment_inv = self.part_handler.virtual_inventory
|
||||
if props.inventory.part_slots and self.part_handler then
|
||||
for i, attachment in pairs(props.inventory.part_slots) do
|
||||
form = form.."label["..(image_scale/2)+(attachment.formspec_offset.x or 0)-.75 ..","..(image_scale/2)+(-attachment.formspec_offset.y or 0)-.2 ..";"..(attachment.description or i).."]"
|
||||
--list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]
|
||||
local width = attachment.slots or 1
|
||||
width = width+((width-1)*.125)
|
||||
form = form.."list[detached:guns4d_attachment_inv_"..pname..";"..i..";"..(image_scale/2)+(attachment.formspec_offset.x or 0)-(width/2)..","..(image_scale/2)+(-attachment.formspec_offset.y or 0)..";3,5;]"
|
||||
end
|
||||
end
|
||||
end
|
||||
form = form.."container_end[]"
|
||||
minetest.show_formspec(self.handler.player:get_player_name(), "guns4d:inventory", form)
|
||||
end
|
||||
core.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname=="guns4d:inventory" and fields.quit then
|
||||
local gun = Guns4d.players[player:get_player_name()].gun
|
||||
gun:regenerate_properties()
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- update the offsets of the player's look created by the gun
|
||||
function gun_default:update_look_offsets(dt)
|
||||
@ -275,21 +427,21 @@ function gun_default:update_look_offsets(dt)
|
||||
end
|
||||
--============================================== positional info =====================================
|
||||
--all of this dir shit needs to be optimized HARD
|
||||
function gun_default:get_gun_axial_dir()
|
||||
--[[function gun_default:get_gun_axial_dir()
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local rotation = self.total_offsets
|
||||
local dir = vector.new(vector.rotate({x=0, y=0, z=1}, {y=0, x=rotation.gun_axial.x*math.pi/180, z=0}))
|
||||
dir = vector.rotate(dir, {y=rotation.gun_axial.y*math.pi/180, x=0, z=0})
|
||||
return dir
|
||||
end
|
||||
function gun_default:get_player_axial_dir(rltv)
|
||||
end]]
|
||||
--[[function gun_default:get_player_axial_dir(rltv)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local handler = self.handler
|
||||
local rotation = self.total_offsets
|
||||
local dir = vector.new(vector.rotate({x=0, y=0, z=1}, {y=0, x=((rotation.player_axial.x)*math.pi/180), z=0}))
|
||||
dir = vector.rotate(dir, {y=((rotation.player_axial.y)*math.pi/180), x=0, z=0})
|
||||
if not rltv then
|
||||
if (self.properties.sprite_scope and handler.control_handler.ads) or (self.properties.crosshair and not handler.control_handler.ads) then
|
||||
if (self.properties.subclasses.sprite_scope and handler.control_handler.ads) or (self.properties.subclasses.crosshair and not handler.control_handler.ads) then
|
||||
--we need the head rotation in either of these cases, as that's what they're showing.
|
||||
dir = vector.rotate(dir, {x=handler.look_rotation.x*math.pi/180,y=-handler.look_rotation.y*math.pi/180,z=0})
|
||||
else
|
||||
@ -297,44 +449,8 @@ function gun_default:get_player_axial_dir(rltv)
|
||||
end
|
||||
end
|
||||
return dir
|
||||
end
|
||||
--This should be replaced with
|
||||
--[[function gun_default:get_dir(rltv, offset_x, offset_y)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
local rotation = self.total_offsets
|
||||
local handler = self.handler
|
||||
--rotate x and then y.
|
||||
--used symbolab.com to precalculate the rotation matrices to save on performance since spread for pellets has to run this.
|
||||
local p = -(rotation.gun_axial.x+rotation.player_axial.x+(offset_x or 0))*math.pi/180
|
||||
local y = -(rotation.gun_axial.y+rotation.player_axial.y+(offset_y or 0))*math.pi/180
|
||||
local Cy = math.cos(y)
|
||||
local Sy = math.sin(y)
|
||||
local Cp = math.cos(p)
|
||||
local Sp = math.sin(p)
|
||||
local dir = {
|
||||
x=Sy*Cy,
|
||||
y=-Sp,
|
||||
z=Cy*Cp
|
||||
}
|
||||
if not rltv then
|
||||
p = -self.player_rotation.x*math.pi/180
|
||||
y = -self.player_rotation.y*math.pi/180
|
||||
Cy = math.cos(y)
|
||||
Sy = math.sin(y)
|
||||
Cp = math.cos(p)
|
||||
Sp = math.sin(p)
|
||||
dir = vector.new(
|
||||
(Cy*dir.x)+(Sy*Sp*dir.y)+(Sy*Cp*dir.z),
|
||||
(dir.y*Cp)-(dir.z*Sp),
|
||||
(-dir.x*Sy)+(dir.y*Sp*Cy)+(dir.z*Cy*Cp)
|
||||
)
|
||||
else
|
||||
dir = vector.new(dir)
|
||||
end
|
||||
return dir
|
||||
end]]
|
||||
--probably doesnt get much more optimized then this.
|
||||
local bone_location = vector.new()
|
||||
|
||||
local tmv3_rot = vector.new()
|
||||
local tmv4_in = {0,0,0,1}
|
||||
local tmv4_pivot_inv = {0,0,0,0}
|
||||
@ -346,50 +462,58 @@ local out = vector.new() --reserve the memory, we still want to create new vecto
|
||||
--gets the gun's position relative to the player. Relative indicates wether it's relative to the player's horizontal look
|
||||
--offset is relative to the's rotation
|
||||
|
||||
--- get the global position of the gun. This is customized to rely on the assumption that there are 3-4 main rotations and 2-3 translations. If the behavior of the bones are changed this method may not work
|
||||
--- get the global position of the gun. This is customized to rely on the assumption that there are 3-4 main rotations and 2-3 translations. If the behavior of the bones are changed this method may not work.
|
||||
-- the point of this is to allow the user to find the gun's object origin as well as calculate where a given point should be offset given the parameters.
|
||||
-- @tparam vec3 offset_pos
|
||||
-- @tparam bool relative_y wether the y axis is relative to the player's look
|
||||
-- @tparam bool relative_x wether the x axis is relative to the player's look
|
||||
-- @tparam bool with_animation wether rotational and translational offsets from the animation are applied
|
||||
-- @treturn vec3 position of gun (in global or local orientation) relative to the player's position
|
||||
function gun_default:get_pos(offset, relative_y, relative_x, with_animation)
|
||||
assert(self.instance, "attempt to call object method on a class")
|
||||
--local player = self.player
|
||||
local handler = self.handler
|
||||
local raw_bone_location = handler.player_model_handler.gun_bone_location
|
||||
local vs = handler:get_properties().visual_size
|
||||
bone_location.x, bone_location.y, bone_location.z = raw_bone_location.x, raw_bone_location.y, raw_bone_location.z
|
||||
local gun_translation = self.gun_translation --needs a refactor
|
||||
local root_transform = self.b3d_model.root_orientation_rest
|
||||
offset = offset or empty_vec
|
||||
--dir needs to be rotated twice seperately to avoid weirdness
|
||||
local gun_scale = self.properties.visuals.scale
|
||||
--player look. If its relative on a given axis we eliminate it by setting it to 0.
|
||||
local px = (relative_x and 0) or nil
|
||||
local py = (relative_y and 0) or nil
|
||||
local ax = ((not with_animation) and 0) or nil
|
||||
local ay = ((not with_animation) and 0) or nil
|
||||
local az = ((not with_animation) and 0) or nil
|
||||
--offset is relative to the gun, which is rotated about the origin of the root bone, so we need to make the vector relative to the root's vector, rotate, and then bring it back to global space.
|
||||
offset = offset or empty_vec
|
||||
|
||||
local gun_translation = self.gun_translation --needs a refactor
|
||||
local root_transform = self.b3d_model.root_orientation_rest
|
||||
--dir needs to be rotated twice seperately to avoid weirdness
|
||||
local gun_scale = self.properties.visuals.scale
|
||||
--generate rotation values based on our output
|
||||
|
||||
ttransform=self:get_rotation_transform(ttransform,nil,nil,nil,nil,nil,px,py,ax,ay,az)
|
||||
|
||||
--change the pivot of `offset` to the root bone by making our vector relative to it (basically setting it to origin)
|
||||
tmv4_in[1], tmv4_in[2], tmv4_in[3] = offset.x-root_transform[13]*gun_scale, offset.y-root_transform[14]*gun_scale, offset.z-root_transform[15]*gun_scale
|
||||
tmv4_offset = ttransform.mul_vec4(tmv4_offset, ttransform, tmv4_in)
|
||||
tmv4_offset = ttransform.mul_vec4(tmv4_offset, ttransform, tmv4_in) --rotate by our rotation transform
|
||||
--to bring it back to global space we need to find what we offset it by in `ttransform`'s local space, so we apply the transform to it
|
||||
tmv4_in[1], tmv4_in[2], tmv4_in[3] = root_transform[13]*gun_scale, root_transform[14]*gun_scale, root_transform[15]*gun_scale
|
||||
tmv4_pivot_inv = ttransform.mul_vec4(tmv4_pivot_inv, ttransform, tmv4_in) --bring it back to global space by adding what we subtracted earlier (which has now been transformed.)
|
||||
tmv4_pivot_inv = ttransform.mul_vec4(tmv4_pivot_inv, ttransform, tmv4_in)
|
||||
|
||||
--quickly add together tmv4_offset+tmv4_pivot_inv to get the global position of the offset relative to the entity
|
||||
tmv4_offset[1],tmv4_offset[2],tmv4_offset[3] = tmv4_offset[1]+tmv4_pivot_inv[1], tmv4_offset[2]+tmv4_pivot_inv[2], tmv4_offset[3]+tmv4_pivot_inv[3]
|
||||
|
||||
--the translation of the gun is translation of the root bone's coordinate system, so the gun_axial rotation would not affect it, therefore we set local rotations to 0.
|
||||
--get the position of the gun entity in global space relative to the bone which it is attached to.
|
||||
ttransform=self:get_rotation_transform(ttransform, 0,0,0,nil,nil,px,py,ax,ay,az)
|
||||
tmv4_in[1], tmv4_in[2], tmv4_in[3] = gun_translation.x, gun_translation.y, gun_translation.z
|
||||
tmv4_gun = ttransform.mul_vec4(tmv4_gun, ttransform, tmv4_in)
|
||||
|
||||
--the bone location is determined by the rotation of the player alone. This currently only supports player look but will probably support the actual rotation in the future
|
||||
--get the position of the bone globally
|
||||
local bone_location = self.handler.player_model_handler.gun_bone_location
|
||||
if relative_y then
|
||||
out = vector.new(bone_location)
|
||||
else
|
||||
tmv3_rot.y = -handler.look_rotation.y*math.pi/180
|
||||
tmv3_rot.y = -self.handler.look_rotation.y*math.pi/180
|
||||
out = vector.rotate(bone_location, tmv3_rot)
|
||||
end
|
||||
out.x, out.y, out.z = out.x+tmv4_gun[1]+tmv4_offset[1], out.y+tmv4_gun[2]+tmv4_offset[2], out.z+tmv4_gun[3]+tmv4_offset[3]
|
||||
--add our global translations together
|
||||
--bonepos + gunentity + gunoffset + animation offset
|
||||
local anim = (with_animation and self.animation_translation) or empty_vec
|
||||
out.x, out.y, out.z = out.x+anim.x+tmv4_gun[1]+tmv4_offset[1], out.y+anim.y+tmv4_gun[2]+tmv4_offset[2], out.z+anim.z+tmv4_gun[3]+tmv4_offset[3]
|
||||
return out
|
||||
end
|
||||
|
||||
@ -423,12 +547,20 @@ function gun_default:get_rotation_transform(out, lx,ly,lz,gx,gy,px,py,ax,ay,az)
|
||||
end
|
||||
local forward = {0,0,1,0}
|
||||
local tmv4_out = {0,0,0,0}
|
||||
function gun_default:get_dir(rltv, offx, offy)
|
||||
-- get the direction for firing
|
||||
function gun_default:get_dir(rltv, offx, offy, suppress_anim)
|
||||
local rotations = self.total_offsets
|
||||
local anim_x = (suppress_anim and 0) or nil
|
||||
local anim_y = (suppress_anim and 0) or nil
|
||||
local anim_z = (suppress_anim and 0) or nil
|
||||
if rltv then
|
||||
ttransform = self:get_rotation_transform(ttransform, (-rotations.gun_axial.x-(offx or 0) )*trad, (-rotations.gun_axial.y-(offy or 0))*trad, nil, nil, nil, 0, 0, nil,nil,nil)
|
||||
ttransform = self:get_rotation_transform(ttransform, (-rotations.gun_axial.x-(offx or 0) )*trad, (-rotations.gun_axial.y-(offy or 0))*trad, nil, nil, nil, 0, 0, anim_x,anim_y,anim_z)
|
||||
else
|
||||
ttransform = self:get_rotation_transform(ttransform, (-rotations.gun_axial.x-(offx or 0))*trad, (-rotations.gun_axial.y-(offy or 0))*trad, nil, nil, nil, nil, nil, nil,nil,nil)
|
||||
local player_aim
|
||||
if (self.properties.subclasses.sprite_scope and self.handler.control_handler.ads) or (self.properties.subclasses.crosshair and not self.handler.control_handler.ads) then
|
||||
player_aim=self.player:get_look_vertical()
|
||||
end
|
||||
ttransform = self:get_rotation_transform(ttransform, (-rotations.gun_axial.x-(offx or 0))*trad, (-rotations.gun_axial.y-(offy or 0))*trad, nil, nil, nil, player_aim, nil, anim_x,anim_y,anim_z)
|
||||
end
|
||||
local tmv4 = ttransform.mul_vec4(tmv4_out, ttransform, forward)
|
||||
local pos = vector.new(tmv4[1], tmv4[2], tmv4[3])
|
||||
@ -443,15 +575,8 @@ 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(), "guns4d:gun_entity")
|
||||
local props = self.properties
|
||||
self.entity:set_properties({
|
||||
mesh = props.visuals.mesh,
|
||||
textures = props.visuals.textures,
|
||||
backface_culling = props.visuals.backface_culling,
|
||||
visual_size = {x=10*props.visuals.scale,y=10*props.visuals.scale,z=10*props.visuals.scale}
|
||||
})
|
||||
Guns4d.gun_by_ObjRef[self.entity] = self
|
||||
--obj:on_step()
|
||||
--self:update_entity()
|
||||
self:update_visuals()
|
||||
end
|
||||
local tmp_mat4_rot = mat4.identity()
|
||||
local ip_time = Guns4d.config.gun_axial_interpolation_time
|
||||
@ -461,12 +586,11 @@ local ip_time2 = Guns4d.config.translation_interpolation_time
|
||||
function gun_default:update_entity()
|
||||
local obj = self.entity
|
||||
local player = self.player
|
||||
local axial_rot = self.total_offsets.gun_axial
|
||||
local handler = self.handler
|
||||
local props = self.properties
|
||||
--attach to the correct bone, and rotate
|
||||
local visibility = true
|
||||
if self.sprite_scope and self.sprite_scope.hide_gun and (not (self.control_handler.ads_location == 0)) then
|
||||
if self.subclass_instances.sprite_scope and self.subclass_instances.sprite_scope.hide_gun and (not (self.control_handler.ads_location == 0)) then
|
||||
visibility = false
|
||||
end
|
||||
--Irrlicht uses counterclockwise but we use clockwise.
|
||||
@ -601,21 +725,19 @@ function gun_default:update_recoil(dt)
|
||||
self.offsets.recoil[axis][i] = abs*sign
|
||||
end
|
||||
end
|
||||
--print(self.velocities.recoil.player_axial.x, self.velocities.recoil.player_axial.y)
|
||||
end
|
||||
|
||||
--- updates the gun's animation data
|
||||
-- @tparam dt
|
||||
function gun_default:update_animation(dt)
|
||||
local ent = self.entity
|
||||
--local ent = self.entity
|
||||
local data = self.animation_data
|
||||
data.runtime = data.runtime + dt
|
||||
data.current_frame = Guns4d.math.clamp(data.current_frame+(dt*data.fps), data.frames.x, data.frames.y)
|
||||
if data.loop and (data.current_frame > data.frames.y) then
|
||||
data.current_frame = data.frames.x
|
||||
end
|
||||
--track rotations and applies to aim.
|
||||
if self.consts.ANIMATIONS_OFFSET_AIM then self:update_animation_rotation() end
|
||||
self:update_animation_transforms()
|
||||
end
|
||||
--IMPORTANT!!! this does not directly modify the animation_data table anymore, it's all hooked through ObjRef:set_animation() (init.lua) so if animation is set elsewhere it doesnt break.
|
||||
--this may be deprecated in the future- as it is no longer really needed now that I hook ObjRef functions.
|
||||
@ -729,40 +851,40 @@ end
|
||||
|
||||
|
||||
--should merge these functions eventually...
|
||||
function gun_default:update_animation_rotation()
|
||||
function gun_default:update_animation_transforms()
|
||||
local current_frame = self.animation_data.current_frame+self.consts.KEYFRAME_SAMPLE_PRECISION
|
||||
local frame1 = math.floor(current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION)
|
||||
local frame2 = math.floor(current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION)+1
|
||||
current_frame = current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION
|
||||
local rotations = self.b3d_model.global_frames.rotation
|
||||
local out
|
||||
if rotations then
|
||||
if rotations[frame1] then
|
||||
if (not rotations[frame2]) or (current_frame==frame1) then
|
||||
out = vector.new(rotations[frame1]:get_euler_irrlicht_bone())*180/math.pi
|
||||
--print("rawsent")
|
||||
else --to stop nan
|
||||
local ip_ratio = (current_frame-frame1)/(frame2-frame1)
|
||||
local vec1 = rotations[frame1]
|
||||
local vec2 = rotations[frame2]
|
||||
out = vector.new(vec1:slerp(vec2, ip_ratio):get_euler_irrlicht_bone())*180/math.pi
|
||||
end
|
||||
else
|
||||
out = vector.copy(rotations[1])
|
||||
end
|
||||
--print(frame1, frame2, current_frame, dump(out))
|
||||
local rotations = self.b3d_model.global_frames.root_rotation
|
||||
local positions = self.b3d_model.global_frames.root_translation
|
||||
local euler_rot
|
||||
local trans
|
||||
if not rotations[frame1] then --note that we are inverting the rotations, this is because b3d turns the wrong way or something? It might be an issue with LEEF idk.
|
||||
euler_rot = vector.new(rotations[1]:get_euler_irrlicht_bone())*-1
|
||||
else
|
||||
out = vector.new()
|
||||
local ip_ratio = (frame2 and (current_frame-frame1)/(frame2-frame1)) or 1
|
||||
local vec1 = rotations[frame1]
|
||||
local vec2 = rotations[frame2] or rotations[frame1]
|
||||
euler_rot = vector.new(vec1:slerp(vec2, ip_ratio):get_euler_irrlicht_bone())*-180/math.pi
|
||||
end
|
||||
--we use a different rotation system
|
||||
self.animation_rotation = -out
|
||||
|
||||
if not positions[frame1] then --note that we are inverting the rotations, this is because b3d turns the wrong way or something? It might be an issue with LEEF idk.
|
||||
trans = positions[1]*-1
|
||||
else
|
||||
local ip_ratio = (frame2 and (current_frame-frame1)/(frame2-frame1)) or 1
|
||||
local vec1 = positions[frame1]
|
||||
local vec2 = positions[frame2] or positions[frame1]
|
||||
trans = (vec1*(1-ip_ratio))+(vec2*ip_ratio)
|
||||
end
|
||||
self.animation_rotation = euler_rot
|
||||
self.animation_translation = trans
|
||||
end
|
||||
|
||||
--relative to the gun's entity. Returns left, right vectors.
|
||||
local out = {arm_left=vector.new(), arm_right=vector.new()}
|
||||
function gun_default:get_arm_aim_pos()
|
||||
local current_frame = self.animation_data.current_frame
|
||||
print(current_frame)
|
||||
local frame1 = math.floor(current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION)
|
||||
local frame2 = math.floor(current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION)+1
|
||||
current_frame = current_frame/self.consts.KEYFRAME_SAMPLE_PRECISION
|
||||
@ -776,7 +898,6 @@ function gun_default:get_arm_aim_pos()
|
||||
local ip_ratio = (current_frame-frame1)/(frame2-frame1)
|
||||
local vec1 = self.b3d_model.global_frames[i][frame1]
|
||||
local vec2 = self.b3d_model.global_frames[i][frame2]
|
||||
--print(current_frame, frame1, frame2, ip_ratio)
|
||||
out[i] = vec1+((vec1-vec2)*ip_ratio)
|
||||
end
|
||||
else
|
||||
@ -790,10 +911,13 @@ function gun_default:get_arm_aim_pos()
|
||||
--return vector.copy(self.b3d_model.global_frames.arm_left[1]), vector.copy(self.b3d_model.global_frames.arm_right[1])
|
||||
end
|
||||
|
||||
--- ready the gun to be deleted
|
||||
function gun_default:prepare_deletion()
|
||||
self.released = true
|
||||
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
|
||||
if self.crosshair then self.crosshair:prepare_deletion() end
|
||||
|
||||
for i, instance in pairs(self.subclass_instances) do
|
||||
if instance.prepare_deletion then instance:prepare_deletion() end
|
||||
end
|
||||
end
|
||||
|
@ -21,8 +21,10 @@ local Vec = vector
|
||||
-- where def is the definition of your new gun- or rather the changed parts of it. So to make a new gun you can run Guns4d.gun:inherit()
|
||||
-- or you can do the same thing with a seperate class of weapons. Set name to "__template" for template classes of guns.
|
||||
--
|
||||
-- *Please note:* there are likey undocumented fields that are used in internal functions. If you find one, please make an issue on Github.
|
||||
-- for properties: for tables where you wish to delete the parent class's fields altogether (since inheritence prevents this) you can set the field "__replace_old_table=true"
|
||||
-- additionally
|
||||
--
|
||||
-- *Please note:* there are likey undocumented fields that are used in internal functions. If you find one, please make an issue on Github.
|
||||
--
|
||||
-- @class gun
|
||||
-- @field properties @{lvl1_fields.properties|properties} which define the vast majority of gun attributes and may change accross instances
|
||||
@ -66,8 +68,12 @@ local gun_default = {
|
||||
muzzle_flash = Guns4d.effects.muzzle_flash,
|
||||
--- `vec3` translation of the gun relative to the "gun" bone or the player axial rotation.
|
||||
gun_translation = vector.new(),
|
||||
--- `table` indexed list of modifiers not set by the gun but to be applied to the gun. After changing, gun:update_modifiers() must be called to update it. Also may contain lists of modifiers.
|
||||
--- `table` indexed list of functions which are called when the gun's properties need to be built. This is used for things like attachments, etc.
|
||||
property_modifiers = nil,
|
||||
--- `table` a list of ObjRefs that are attached to the gun as a result of attached_objects
|
||||
attached_objects = {},
|
||||
--- `table` list of subclass instances (i.e. Sprite_scope)
|
||||
subclass_instances = {},
|
||||
|
||||
--- properties
|
||||
--
|
||||
@ -85,14 +91,6 @@ local gun_default = {
|
||||
-- @field visuals `table` @{gun.properties.visuals|defines visual attributes of the gun}
|
||||
-- @compact
|
||||
properties = {
|
||||
--- `Ammo_handler` the class (based on) ammo_handler to create an instance of and use. Default is `Guns4d.ammo_handler`
|
||||
ammo_handler = Guns4d.ammo_handler,
|
||||
--- `Attachment_handler` attachment_handler class to use. Default is `Guns4d.attachment_handler`
|
||||
attachment_handler = Guns4d.attachment_handler,
|
||||
--- `Sprite_scope` sprite scope class to use
|
||||
sprite_scope = nil,
|
||||
--- `Dynamic_crosshair` crosshair class to use
|
||||
crosshair = nil,
|
||||
--- starting vertical rotation of the gun
|
||||
initial_vertical_rotation = -60,
|
||||
--- `float`=.5 max angular deviation (vertical) from breathing
|
||||
@ -123,7 +121,6 @@ local gun_default = {
|
||||
burst = 3,
|
||||
--- `table` containing a list of actions for PC users passed to @{Control_handler}
|
||||
pc_control_actions = { --used by control_handler
|
||||
__overfill=true, --this table will not be filled in.
|
||||
aim = Guns4d.default_controls.aim,
|
||||
auto = Guns4d.default_controls.auto,
|
||||
reload = Guns4d.default_controls.reload,
|
||||
@ -133,7 +130,6 @@ local gun_default = {
|
||||
},
|
||||
--- `table` containing a list of actions for touch screen users passed to @{Control_handler}
|
||||
touch_control_actions = {
|
||||
__overfill=true,
|
||||
aim = Guns4d.default_touch_controls.aim,
|
||||
auto = Guns4d.default_touch_controls.auto,
|
||||
reload = Guns4d.default_touch_controls.reload,
|
||||
@ -142,7 +138,7 @@ local gun_default = {
|
||||
jump_cancel_ads = Guns4d.default_touch_controls.jump_cancel_ads
|
||||
},
|
||||
inventory = {
|
||||
--[[attachment_slots = {
|
||||
--[[part_slots = {
|
||||
underbarrel = {
|
||||
formspec_inventory_location = {x=0, y=1}
|
||||
slots = 2,
|
||||
@ -155,7 +151,32 @@ local gun_default = {
|
||||
},]]
|
||||
render_size = 2, --length (in meters) which is visible accross the z/forward axis at y/up=0, x=0. For orthographic this will be the scale of the orthographic camera. Default 2
|
||||
render_image = "m4_ortho.png", --expects an image of the right side of the gun, where the gun is facing the right. Default "m4_ortho.png"
|
||||
--rendered_from_model = true --if true the rendering is automatically moved to the center of the screen
|
||||
--- table of firemodes and their overlays in the player's inventory when the gun is on that firemode
|
||||
firemode_inventory_overlays = { --#4
|
||||
--singlefire default: "inventory_overlay_single.png"
|
||||
single = "inventory_overlay_single.png",
|
||||
--automatic default: "inventory_overlay_auto.png"
|
||||
auto = "inventory_overlay_auto.png",
|
||||
--burstfire default: "inventory_overlay_burst.png"
|
||||
burst = "inventory_overlay_burst.png",
|
||||
--safe default: "inventory_overlay_safe.png" (unimplemented firemode)
|
||||
safe = "inventory_overlay_safe.png"
|
||||
},
|
||||
},
|
||||
--- `table` a list of subclasses to create on construct and update. Note special fields `ammo_handler` and `part_handler`.
|
||||
--
|
||||
-- @table gun.properties.subclsses
|
||||
-- @see lvl1_fields.properties|properties
|
||||
-- @compact
|
||||
subclasses = {
|
||||
--- `Ammo_handler` the class (based on) ammo_handler to create an instance of and use. Default is `Guns4d.ammo_handler`
|
||||
ammo_handler = Guns4d.ammo_handler,
|
||||
--- `part_handler` Part_handler class to use. Default is `Guns4d.part_handler`
|
||||
part_handler = Guns4d.part_handler,
|
||||
--- `Sprite_scope` sprite scope class to use. Nil by default, inherit Sprite_scope for class (**documentation needed, reccomended contact for help if working with it**)
|
||||
sprite_scope = nil,
|
||||
--- `Dynamic_crosshair` crosshair class to use. Nil by default, set to `Guns4d.Dynamic_crosshair` for a generic circular expanding reticle.
|
||||
crosshair = nil,
|
||||
},
|
||||
--- properties.ads
|
||||
--
|
||||
@ -197,22 +218,6 @@ local gun_default = {
|
||||
firemodes = { --#3
|
||||
"single", --not limited to semi-automatic.
|
||||
},
|
||||
--- properties.firemode_inventory_overlays
|
||||
--
|
||||
-- Defines the overlay on the gun item for each firemode. These are selected automatically by firemode string. Defaults are as follows:
|
||||
-- @table gun.properties.firemode_inventory_overlays
|
||||
-- @see lvl1_fields.properties|properties
|
||||
-- @compact
|
||||
firemode_inventory_overlays = { --#4
|
||||
--- singlefire default: "inventory_overlay_single.png"
|
||||
single = "inventory_overlay_single.png",
|
||||
--- automatic default: "inventory_overlay_auto.png"
|
||||
auto = "inventory_overlay_auto.png",
|
||||
--- burstfire default: "inventory_overlay_burst.png"
|
||||
burst = "inventory_overlay_burst.png",
|
||||
--- safe default: "inventory_overlay_safe.png" (unimplemented firemode)
|
||||
safe = "inventory_overlay_safe.png"
|
||||
},
|
||||
--- properties.recoil
|
||||
--
|
||||
-- **IMPORTANT**: expects fields to be tables containing a "gun_axial" and "player_axial" field.
|
||||
@ -357,6 +362,18 @@ local gun_default = {
|
||||
textures = {},
|
||||
--- scale multiplier. Default 1
|
||||
scale = 1,
|
||||
--- objects that are attached to the gun. This is especially useful for attachments
|
||||
--
|
||||
-- @example
|
||||
-- my_object = {
|
||||
-- textures = {"blank.png"},
|
||||
-- visual_size = {x=1,y=1,z=1},
|
||||
-- offset = {x=0,y=0,z=0},
|
||||
-- bone = "main",
|
||||
-- backface_culling = false,
|
||||
-- glow = 0
|
||||
-- }
|
||||
attached_objects = {},
|
||||
--- toggles backface culling. Default true
|
||||
backface_culling = true,
|
||||
--- a table of animations. Indexes define the name of the animation to be refrenced by other functions of the gun.
|
||||
@ -381,7 +398,7 @@ local gun_default = {
|
||||
sounds = { --this does not contain reload sound effects.
|
||||
fire = {
|
||||
{
|
||||
__overfill=true,
|
||||
__replace_old_table=true,
|
||||
sound = "ar_firing",
|
||||
max_hear_distance = 40, --far min_hear_distance is also this.
|
||||
pitch = {
|
||||
@ -394,7 +411,7 @@ local gun_default = {
|
||||
}
|
||||
},
|
||||
{
|
||||
__overfill=true,
|
||||
__replace_old_table=true,
|
||||
sound = "ar_firing_far",
|
||||
min_hear_distance = 40,
|
||||
max_hear_distance = 600,
|
||||
@ -410,8 +427,10 @@ local gun_default = {
|
||||
},
|
||||
},
|
||||
},
|
||||
--- `vector` containing the offset from animations, this will be generated if {@consts.ANIMATIONS_OFFSET_AIM}=true
|
||||
--- `vector` containing the rotation offset from the current frame, this will be factored into the gun's direction if {@consts.ANIMATIONS_OFFSET_AIM}=true
|
||||
animation_rotation = vector.new(),
|
||||
--- `vector` containing the translational/positional offset from the current frame
|
||||
animation_translation = vector.new(),
|
||||
--- all offsets from @{offsets|gun.offset} of a type added together gun in the same format as a @{offsets|an offset} (that is, five vectors, `gun_axial`, `player_axial`, etc.). Note that if
|
||||
-- offsets are changed after update, this will not be updated automatically until the next update. update_rotations() must be called to do so.
|
||||
total_offsets = {
|
||||
@ -507,7 +526,7 @@ local gun_default = {
|
||||
--- `bool` wether the gun rotates on it's own axis instead of the player's view (i.e. ironsight misalignments)
|
||||
HAS_GUN_AXIAL_OFFSETS = true,
|
||||
--- wether animations create an offset
|
||||
ANIMATIONS_OFFSET_AIM = false,
|
||||
ANIMATIONS_OFFSET_AIM = true,
|
||||
--- whether the idle animation changes or not
|
||||
LOOP_IDLE_ANIM = false,
|
||||
--- general gain multiplier for third persons when hearing sounds
|
||||
@ -520,8 +539,14 @@ local gun_default = {
|
||||
ARM_RIGHT_BONE = "right_aimpoint",
|
||||
--- `string`="left_aimpoint", the bone which the left arm aims at to
|
||||
ARM_LEFT_BONE = "left_aimpoint",
|
||||
--- `table` version of 4dguns this gun is made for. If left empty it will be assumed it is before 1.3.
|
||||
VERSION = {1, 2, 0}
|
||||
},
|
||||
}
|
||||
gun_default._properties_unsafe = gun_default.properties
|
||||
gun_default.properties = leef.class.proxy_table.new(gun_default.properties)
|
||||
gun_default._consts_unsafe = gun_default.consts
|
||||
gun_default.consts = leef.class.proxy_table.new(gun_default.consts)
|
||||
|
||||
minetest.register_entity("guns4d:gun_entity", {
|
||||
initial_properties = {
|
||||
|
@ -1,49 +0,0 @@
|
||||
|
||||
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 = leef.class.new_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,
|
||||
|
||||
})
|
200
classes/Part_handler.lua
Normal file
200
classes/Part_handler.lua
Normal file
@ -0,0 +1,200 @@
|
||||
|
||||
local Part_handler = leef.class.new_class:inherit({})
|
||||
Guns4d.part_handler = Part_handler
|
||||
function Part_handler:construct()
|
||||
assert(self.gun, "no gun object provided")
|
||||
if self.instance then
|
||||
local gun = self.gun
|
||||
local meta = gun.meta
|
||||
self.player = gun.player
|
||||
|
||||
--just a function to warn that there is a cheater...
|
||||
local warn_cheater = function(p)
|
||||
core.log("warning", "player: `"..p:get_player_name().."` attempted to access another player's (`"..self.player:get_player_name().."`) gun attachment inventory. This is not possible without cheating!")
|
||||
end
|
||||
|
||||
--currently there is no support for multiple attachments of the same type in a given slot
|
||||
self.invstring = "guns4d_attachment_inv_"..gun.player:get_player_name()
|
||||
core.remove_detached_inventory(self.invstring)
|
||||
local inv = core.create_detached_inventory(self.invstring, {
|
||||
--allow_move = allow_move,
|
||||
allow_put = function(_, listname, index, stack, player)
|
||||
if player == self.player then
|
||||
local props = gun.properties
|
||||
if props.inventory.part_slots[listname] and self:can_add(stack, listname) then
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
else
|
||||
warn_cheater(player)
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
on_put = function(_, listname, index, stack, _)
|
||||
self:add_attachment(stack, listname, index)
|
||||
end,
|
||||
|
||||
allow_take = function(inv, listname, index, stack, player)
|
||||
if (player == self.player) then
|
||||
if self.parts[listname][index] then
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
else
|
||||
warn_cheater(player)
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
on_take = function(_, listname, index, _, _)
|
||||
self:remove_attachment(index, listname)
|
||||
end,
|
||||
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
if player == self.player then
|
||||
if self.parts[from_list][from_index] and self:can_add(inv:get_stack(from_list, from_index), to_list) then --can be removed
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
end
|
||||
else
|
||||
warn_cheater(player)
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
self:remove_attachment(from_index, from_list)
|
||||
self:add_attachment(inv:get_stack(to_list, to_index), to_list, to_index)
|
||||
end
|
||||
--allow_take = allow_take
|
||||
})
|
||||
self.virtual_inventory = inv
|
||||
self.handler = self.gun.handler
|
||||
gun.property_modifiers["part_handler"] = function(props)
|
||||
for _, slot in pairs(self.parts) do
|
||||
for _, stack in pairs(slot) do
|
||||
local mod_def = Guns4d.registered_attachments[stack:get_name()]
|
||||
if mod_def and mod_def.mod then
|
||||
mod_def.mod(props)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
--initialize attachments
|
||||
if meta:get_string("guns4d_attachments") == "" then
|
||||
self.parts = {}
|
||||
for i, partdef in pairs(self.gun.properties.inventory.part_slots) do
|
||||
--set the size of the virtual inventory slot
|
||||
inv:set_size(i, partdef.slots or 1)
|
||||
self.parts[i] = {}
|
||||
if type(partdef.default)=="string" then
|
||||
self:add_attachment(partdef.default)
|
||||
end
|
||||
end
|
||||
meta:set_string("guns4d_attachments", core.serialize(self.parts))
|
||||
else
|
||||
self.parts = core.deserialize(meta:get_string("guns4d_attachments"))
|
||||
for slotname, slot in pairs(self.parts) do
|
||||
--set the size of the virtual inventory slot
|
||||
inv:set_size(slotname, self.gun.properties.inventory.part_slots[slotname].slots or 1)
|
||||
for i, stack in pairs(slot) do
|
||||
slot[i] = ItemStack(stack)
|
||||
if type(i) == "number" then
|
||||
inv:set_stack(slotname, i, slot[i])
|
||||
else
|
||||
slot[i] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self.gun:regenerate_properties()
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
--basically. Done like this to allow for quick lookups of stacks
|
||||
attachments = {
|
||||
part_slot = { --part slot as defined in properties.inventory.part_slots
|
||||
["item_name"] = ItemStack("item_name . . .")
|
||||
}
|
||||
}
|
||||
]]
|
||||
|
||||
|
||||
Guns4d.registered_attachments = {}
|
||||
function Part_handler.register_attachment(def)
|
||||
assert(def.itemstring, "itemstring field required")
|
||||
--assert(def.modifier)
|
||||
Guns4d.registered_attachments[def.itemstring] = def
|
||||
end
|
||||
|
||||
function Part_handler:update_parts()
|
||||
local meta = self.gun.meta
|
||||
local new_meta = table.copy(self.parts)
|
||||
for _, slot in pairs(new_meta) do
|
||||
for stackname, stack in pairs(slot) do
|
||||
slot[stackname] = stack:to_string()
|
||||
end
|
||||
end
|
||||
--print(dump(new_meta))
|
||||
meta:set_string("guns4d_attachments", core.serialize(new_meta))
|
||||
self.handler.player:set_wielded_item(self.gun.itemstack)
|
||||
end
|
||||
|
||||
--returns bool indicating success. Attempts to add the attachment.
|
||||
function Part_handler:add_attachment(itemstack, slotname, index)
|
||||
assert(self.instance)
|
||||
itemstack = ItemStack(itemstack)
|
||||
if self:can_add(itemstack, slotname) then
|
||||
self.parts[slotname][index]=itemstack
|
||||
self:update_parts()
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
--check if it has a part
|
||||
function Part_handler:has_part(slotname, itemname)
|
||||
for i, v in pairs(self.parts[slotname]) do
|
||||
if v and (v:get_name()==itemname) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
--check if it can be added. WARNING: after a change is made, the gun's regenerate_properties must be called
|
||||
function Part_handler:can_add(itemstack, slotname)
|
||||
assert(self.instance)
|
||||
local itemname = itemstack:get_name()
|
||||
local props = self.gun.properties
|
||||
--if props.inventory.part_slots[slotname][index] then return false end
|
||||
--print(slot, dump(self.parts))
|
||||
if
|
||||
(not self:has_part(slotname, itemname)) and (props.inventory.part_slots[slotname].allowed)
|
||||
then
|
||||
--check if it's allowed, group check required
|
||||
for i, v in pairs(props.inventory.part_slots[slotname].allowed) do
|
||||
--print(v, name)
|
||||
if v==itemname then
|
||||
return true
|
||||
end
|
||||
end
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
--returns bool indicating success.
|
||||
function Part_handler:remove_attachment(index, slot)
|
||||
assert(self.instance)
|
||||
if self.parts[slot][index] then
|
||||
self.parts[slot][index] = nil
|
||||
self:update_parts()
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function Part_handler:prepare_deletion()
|
||||
core.remove_detached_inventory(self.invstring)
|
||||
end
|
@ -22,28 +22,27 @@ local physics_system = leef.class.new_class:inherit({
|
||||
--calculate delta-velocity of a given forcefield
|
||||
--@tparam int index of the forcefield
|
||||
--@treturn deltaV of
|
||||
local rpos = vector.new()
|
||||
--not going to optimize this because AHHHHHHHHHH its a lot of vector math
|
||||
function physics_system:update(dt)
|
||||
for i, v in pairs() do
|
||||
|
||||
end
|
||||
end
|
||||
function physics_system:calculate_dv(dt, i)
|
||||
local pos =
|
||||
|
||||
|
||||
|
||||
local field = self.forcefields[i]
|
||||
local borderR = field.border_radius
|
||||
local deadzoneR = field.deadzone_radius
|
||||
local midlineR = field.midline_radius
|
||||
local fpos = field.pos
|
||||
local field_pos = field.pos
|
||||
local pos = field.target_pos
|
||||
|
||||
--rpos.x, rpos.y, rpos.z = pos.x-fpos.x,pos.y-fpos.y,pos.z-fpos.x
|
||||
rpos = pos-fpos --relative pos
|
||||
local midline_intersect = rpos:normalize()*midlineR --dir*r is the intersect with midline
|
||||
local d=(midline_intersect-pos):length() --distance from midline
|
||||
local e= field.elastic_constant
|
||||
local f = e*d^(math.abs(e)/e) --force
|
||||
|
||||
--local a = f/self.object_weight --acceleration
|
||||
return a*dt --change in velocity
|
||||
end
|
||||
local dir = (pos-field_pos):normalize() --direction of pos from field
|
||||
local midline_intersect = dir*midlineR --dir*r is the intersect with midline
|
||||
local dist = (midline_intersect-pos):length()-deadzoneR --distance from midline's deadzone
|
||||
if dist < 0 then return vector.new() end
|
||||
-- if dist > 0 then we pull it to the radius
|
||||
local e = field.elastic_constant
|
||||
local ft = e*dist^(math.abs(e)/e) * vector.dot(dir, pos) --force applied to translation
|
||||
local a = ft/self.object_weight --acceleration
|
||||
return dir*(a*dt)
|
||||
end
|
@ -39,12 +39,15 @@ Guns4d.player_model_handler = {
|
||||
},
|
||||
new_bones = { --currently only supports empty bones. Sets at identity rotation, position 0, and parentless
|
||||
"guns4d_gun_bone",
|
||||
"guns4d_reflector_bone"
|
||||
},
|
||||
bone_aliases = { --names of bones used by the model handler and other parts of guns4d.
|
||||
arm_right = "guns4d_arm_right", --this is a needed alias for hipfire position
|
||||
arm_left = "guns4d_arm_left",
|
||||
head = "guns4d_head",
|
||||
|
||||
gun = "guns4d_gun_bone", --another needed alias
|
||||
reflector = "guns4d_reflector_bone"
|
||||
},
|
||||
still_frame = 0, --the frame to take bone offsets from. This system has to be improved in the future (to allow better animation support)- it works for now though.
|
||||
auto_generate = true,
|
||||
@ -80,74 +83,6 @@ end
|
||||
end]]
|
||||
|
||||
--we store the read file so it can be reused in the constructor if needed.
|
||||
local model_buffer
|
||||
local modpath = minetest.get_modpath("guns4d")
|
||||
function player_model:custom_b3d_generation_parameters(b3d)
|
||||
--empty for now, this is for model customizations.
|
||||
return b3d
|
||||
end
|
||||
function player_model:replace_b3d_bone(b3d)
|
||||
end
|
||||
--generates a new guns4d model bases off of the `new_bones` and `bone_overrides` parameters if one does not already exist.
|
||||
function player_model:generate_b3d_model(name)
|
||||
assert(self and name, "error while generating a b3d model. Name not provided or not called as a method.")
|
||||
--generate a new model
|
||||
local filename = string.sub(name, 1, -5).."_guns4d_temp.b3d"
|
||||
local new_path = self.output_path..filename
|
||||
|
||||
--buffer and modify the model
|
||||
model_buffer = leef.b3d_reader.read_model(name)
|
||||
local b3d = model_buffer
|
||||
local replaced = {}
|
||||
--add bone... forgot i made this so simple by adding node_paths
|
||||
for _, node in pairs(b3d.node_paths) do
|
||||
if self.override_bones[node.name] then
|
||||
replaced[node.name] = true
|
||||
--change the name
|
||||
node.name = self.override_bones[node.name]
|
||||
--unset rotation because it breaks shit
|
||||
local rot = node.rotation
|
||||
for i, v in pairs(node.keys) do
|
||||
v.rotation = rot
|
||||
end
|
||||
--node.rotation = {0,0,0,1}
|
||||
end
|
||||
end
|
||||
--check bones were replaced to avoid errors.
|
||||
for i, v in pairs(self.override_bones) do
|
||||
if (not replaced[i]) and i~="__overfill" then
|
||||
error("bone '"..i.."' not replaced with it's guns4d counterpart, bone was not found. Check bone name")
|
||||
end
|
||||
end
|
||||
for i, v in pairs(self.new_bones) do
|
||||
table.insert(b3d.node.children, {
|
||||
name = v,
|
||||
position = {0,0,0},
|
||||
scale = {1/self.scale,1/self.scale,1/self.scale},
|
||||
rotation = {0,0,0,1},
|
||||
children = {},
|
||||
bone = {} --empty works?
|
||||
})
|
||||
end
|
||||
--call custom generation parameters...
|
||||
b3d=self:custom_b3d_generation_parameters(b3d)
|
||||
--write temp model
|
||||
local writefile = io.open(new_path, "w+b")
|
||||
leef.b3d_writer.write_model_to_file(b3d, writefile)
|
||||
writefile:close()
|
||||
|
||||
--send to player media paths
|
||||
minetest.after(0, function()
|
||||
assert(
|
||||
minetest.dynamic_add_media({filepath = new_path}, function()end),
|
||||
"failed sending media"
|
||||
)
|
||||
end)
|
||||
leef.paths.media_paths[filename] = new_path
|
||||
leef.paths.modname_by_media[filename] = "guns4d"
|
||||
return filename
|
||||
|
||||
end
|
||||
|
||||
-- main update function
|
||||
function player_model:update(dt)
|
||||
@ -176,12 +111,29 @@ function player_model:update_aiming(dt)
|
||||
local ip = Guns4d.math.smooth_ratio(handler.control_handler.ads_location or 0)
|
||||
local ip_inv = 1-ip
|
||||
local pos = self.gun_bone_location --reuse allocated table
|
||||
--hip pos is already relative to local scale
|
||||
local xr,yr,zr = gun:get_rotation_transform(ttransform, nil, nil, nil, nil, nil, nil, 0, nil,nil,nil):get_rot_irrlicht_bone()
|
||||
pos.x = ( (player_trans.x*10) + ((gun and gun.properties.ads.horizontal_offset*10) or 0 ))/vs.x
|
||||
pos.y = ( (player_trans.y*10) + (pprops.eye_height*10) )/vs.y
|
||||
pos.z = ( (player_trans.z*10) )/vs.z
|
||||
player:set_bone_override(self.bone_aliases.reflector,
|
||||
{
|
||||
position = {
|
||||
vec={x=pos.x, y=pos.y, z=pos.z},
|
||||
interpolation=ip_time2,
|
||||
absolute = true
|
||||
},
|
||||
rotation = {
|
||||
vec={x=xr,y=yr,z=zr},
|
||||
interpolation=ip_time,
|
||||
absolute = true
|
||||
}
|
||||
})
|
||||
--hip pos is already relative to local scale
|
||||
pos.x = (hip_pos.x*10*ip_inv)+( (player_trans.x*10) + ((gun and gun.properties.ads.horizontal_offset*10*ip) or 0 ))/vs.x
|
||||
pos.y = (hip_pos.y*10*ip_inv)+( (player_trans.y*10) + (pprops.eye_height*10*ip) )/vs.y
|
||||
pos.z = (hip_pos.z*10*ip_inv)+( (player_trans.z*10) )/vs.z
|
||||
|
||||
local xr,yr,zr = gun:get_rotation_transform(ttransform, 0,0,0, nil,nil, nil,0, 0,0,0):get_rot_irrlicht_bone()
|
||||
xr,yr,zr = gun:get_rotation_transform(ttransform, 0,0,0, nil,nil, nil,0, 0,0,0):get_rot_irrlicht_bone()
|
||||
player:set_bone_override(self.bone_aliases.gun,
|
||||
{
|
||||
position = {
|
||||
@ -270,6 +222,76 @@ function player_model:prepare_deletion()
|
||||
handler:set_properties(properties)
|
||||
end
|
||||
--todo: add value for "still_frame" (the frame to take samples from in case 0, 0 is not still.)
|
||||
|
||||
|
||||
local model_buffer
|
||||
local modpath = minetest.get_modpath("guns4d")
|
||||
function player_model:custom_b3d_generation_parameters(b3d)
|
||||
--empty for now, this is for model customizations.
|
||||
return b3d
|
||||
end
|
||||
|
||||
--generates a new guns4d model bases off of the `new_bones` and `bone_overrides` parameters if one does not already exist.
|
||||
function player_model:generate_b3d_model(name)
|
||||
assert(self and name, "error while generating a b3d model. Name not provided or not called as a method.")
|
||||
--generate a new model
|
||||
local filename = string.sub(name, 1, -5).."_guns4d_temp.b3d"
|
||||
local new_path = self.output_path..filename
|
||||
|
||||
--buffer and modify the model
|
||||
model_buffer = leef.b3d_reader.read_model(name)
|
||||
local b3d = model_buffer
|
||||
local replaced = {}
|
||||
--add bone... forgot i made this so simple by adding node_paths
|
||||
for _, node in pairs(b3d.node_paths) do
|
||||
if self.override_bones[node.name] then
|
||||
replaced[node.name] = true
|
||||
--change the name
|
||||
node.name = self.override_bones[node.name]
|
||||
--unset rotation because it breaks shit
|
||||
local rot = node.rotation
|
||||
for i, v in pairs(node.keys) do
|
||||
v.rotation = rot
|
||||
end
|
||||
--node.rotation = {0,0,0,1}
|
||||
end
|
||||
end
|
||||
--check bones were replaced to avoid errors.
|
||||
for i, v in pairs(self.override_bones) do
|
||||
if (not replaced[i]) and i~="__replace_old_table" then
|
||||
error("bone '"..i.."' not replaced with it's guns4d counterpart, bone was not found. Check bone name")
|
||||
end
|
||||
end
|
||||
|
||||
for i, v in pairs(self.new_bones) do
|
||||
table.insert(b3d.node.children, {
|
||||
name = v,
|
||||
position = {0,0,0},
|
||||
scale = {1/self.scale,1/self.scale,1/self.scale},
|
||||
rotation = {0,0,0,1},
|
||||
children = {},
|
||||
bone = {} --empty works?
|
||||
})
|
||||
end
|
||||
--call custom generation parameters...
|
||||
b3d=self:custom_b3d_generation_parameters(b3d)
|
||||
--write temp model
|
||||
local writefile = io.open(new_path, "w+b")
|
||||
leef.b3d_writer.write_model_to_file(b3d, writefile)
|
||||
writefile:close()
|
||||
|
||||
--send to player media paths
|
||||
minetest.after(0, function()
|
||||
assert(
|
||||
minetest.dynamic_add_media({filepath = new_path}, function()end),
|
||||
"failed sending media"
|
||||
)
|
||||
end)
|
||||
leef.paths.media_paths[filename] = new_path
|
||||
leef.paths.modname_by_media[filename] = "guns4d"
|
||||
return filename
|
||||
end
|
||||
|
||||
---@diagnostic disable-next-line: duplicate-set-field
|
||||
function player_model.construct(def)
|
||||
if def.instance then
|
||||
@ -320,7 +342,6 @@ function player_model.construct(def)
|
||||
--[[all of the compatible_meshes should be identical in terms of guns4d specific bones and offsets (arms, head).
|
||||
Otherwise a new handler should be different. With new compatibilities]]
|
||||
for i, v in pairs(def.bone_aliases) do
|
||||
print(def.bone_aliases[i])
|
||||
local node = leef.b3d_nodes.get_node_by_name(b3d_table, v, true)
|
||||
assert(node, "player model handler: no node found by the name of \""..v.."\" check that it is the correct value, or that it has been correctly overriden to use that name.")
|
||||
local transform, _ = leef.b3d_nodes.get_node_global_transform(node, def.still_frame)
|
||||
|
77
classes/Reflector_sight.lua
Normal file
77
classes/Reflector_sight.lua
Normal file
@ -0,0 +1,77 @@
|
||||
local Reflector_sight = {
|
||||
texture = "holographic_reflection.png",
|
||||
scale = 1,
|
||||
offset = 1,
|
||||
deviation_tolerance = {
|
||||
min = .01,
|
||||
max = .05,
|
||||
depth = .1
|
||||
},
|
||||
}
|
||||
local blank = "blank.png"
|
||||
core.register_entity("guns4d:reflector_sight", {
|
||||
initial_properties = {
|
||||
textures = {
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
Reflector_sight.texture,
|
||||
},
|
||||
glow = 14,
|
||||
visual = "cube",
|
||||
visual_size = {x=.1, y=.1, z=.1},
|
||||
physical = false,
|
||||
shaded = false
|
||||
}
|
||||
})
|
||||
Reflector_sight.on_construct = function(self)
|
||||
self:initialize_entity()
|
||||
end
|
||||
local m4 = leef.math.mat4.new()
|
||||
function Reflector_sight:initialize_entity()
|
||||
local obj = minetest.add_entity(self.gun.player:get_pos(), "guns4d:reflector_sight")
|
||||
obj:set_properties({
|
||||
textures = {
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
self.texture
|
||||
},
|
||||
visual_size = {x=self.scale/10, y=self.scale/10, z=self.scale/10},
|
||||
use_texture_alpha = true
|
||||
})
|
||||
self.entity = obj
|
||||
obj:set_attach(self.gun.player, self.gun.handler.player_model_handler.bone_aliases.reflector, nil, nil, true)
|
||||
end
|
||||
function Reflector_sight:update(dt)
|
||||
if self.entity then
|
||||
self.entity:set_attach(self.gun.player, self.gun.handler.player_model_handler.bone_aliases.reflector, {x=0,y=0,z=self.offset*10}, nil, true)
|
||||
|
||||
local v1 = leef.math.mat4.mul_vec4({}, self.gun:get_rotation_transform(m4, nil, nil, nil, nil, nil, nil, nil, nil,nil,nil), {0,0,self.offset,0})
|
||||
local v2 = leef.math.mat4.mul_vec4({}, self.gun:get_rotation_transform(m4, 0, 0, 0, nil, nil, nil, nil, 0,0,0), {0,0,self.offset,0})
|
||||
--[[local dist = vector.distance({x=v1[1], y=v1[2], z=v1[3]}, {x=v2[1], y=v2[2], z=v2[3]})
|
||||
minetest.chat_send_all(dist)
|
||||
self.entity:set_properties({
|
||||
textures = {
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
blank,
|
||||
self.texture .. "^[opacity:"..255-math.ceil(255*((dist-self.deviation_tolerance.min)/self.deviation_tolerance.max))
|
||||
},
|
||||
})]]
|
||||
else
|
||||
self:initialize_entity()
|
||||
end
|
||||
end
|
||||
function Reflector_sight:prepare_deletion()
|
||||
if self.entity then
|
||||
self.entity:remove()
|
||||
end
|
||||
end
|
||||
Guns4d.Reflector_sight = leef.class.new_class:inherit(Reflector_sight)
|
@ -69,6 +69,8 @@ function Sprite_scope:update()
|
||||
local image = self.images[i]
|
||||
local projection_pos=image.projection_pos
|
||||
local relative_pos
|
||||
vec4_dir = mat4.mul_vec4(vec4_dir, gun:get_rotation_transform(transform,nil,nil,nil, nil,nil, 0,0), vec4_forward)
|
||||
|
||||
if projection_pos then
|
||||
vec3_in.x = projection_pos.x/10
|
||||
vec3_in.y = projection_pos.y/10
|
||||
@ -78,21 +80,19 @@ function Sprite_scope:update()
|
||||
relative_pos.x = relative_pos.x - (player_trans.x + (gun and gun.properties.ads.horizontal_offset or 0))
|
||||
relative_pos.y = relative_pos.y - hip_trans.y - (player_trans.y + pprops.eye_height)
|
||||
relative_pos.z = relative_pos.z - (player_trans.z)
|
||||
gun:get_rotation_transform(transform,nil,nil,nil, nil,nil, 0,0)
|
||||
else
|
||||
local r = gun.total_offsets.gun_axial
|
||||
local a = gun.animation_rotation
|
||||
vec4_dir = mat4.mul_vec4(vec4_dir, gun:get_rotation_transform(transform,nil,nil,nil, nil,nil, 0,0), vec4_forward)
|
||||
relative_pos = vec3_in
|
||||
relative_pos.x = vec4_dir[1]
|
||||
relative_pos.y = vec4_dir[2]
|
||||
relative_pos.z = vec4_dir[3]
|
||||
|
||||
--relative_pos = gun:get_dir(true)
|
||||
end
|
||||
|
||||
local hud_pos = Guns4d.math.rltv_point_to_hud(relative_pos, 80/self.magnification, ratio)
|
||||
--print(i, hud_pos.x, hud_pos.y)
|
||||
self.player:hud_change(v, "position", {x=hud_pos.x+.5, y=hud_pos.y+.5})
|
||||
local z = relative_pos.z
|
||||
self.player:hud_change(v, "scale", {x=(image.scale.x/z)*1-vec4_dir[2], y=(image.scale.y/z)*1-vec4_dir[1]})
|
||||
end
|
||||
elseif self.fov_set then
|
||||
self.fov_set = false
|
||||
|
@ -300,7 +300,8 @@ Guns4d.default_controls.reload = {
|
||||
if type(next_state.sounds) == "table" then
|
||||
sounds = Guns4d.table.deep_copy(props.reload[next_state_index].sounds)
|
||||
elseif type(next_state.sounds) == "string" then
|
||||
sounds = Guns4d.table.deep_copy(assert(props.sounds[next_state.sounds], "no sound by the name of "..next_state.sounds))
|
||||
assert(props.sounds[next_state.sounds], "no sound by the name of "..next_state.sounds)
|
||||
sounds = Guns4d.table.deep_copy(props.sounds[next_state.sounds])
|
||||
end
|
||||
sounds.pos = gun.pos
|
||||
data.played_sounds = {gun:play_sounds(sounds)}
|
||||
|
@ -6,7 +6,7 @@
|
||||
<title>gun - Guns4d</title>
|
||||
<link href="../prism.css?7653a2d" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="../luadox.css?7653a2d" type="text/css">
|
||||
|
||||
|
||||
</head>
|
||||
<body class="class-gun">
|
||||
<div class="topbar">
|
||||
@ -297,8 +297,8 @@ offsets are changed after update, this will not be updated automatically until t
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="name"><var id="lvl1_fields.properties.attachment_handler">attachment_handler</var><a class="permalink" href="#lvl1_fields.properties.attachment_handler" title="Permalink to this definition">¶</a></td>
|
||||
<td class="doc"><p><code>Attachment_handler</code> attachment_handler class to use. Default is <code>Guns4d.attachment_handler</code></p>
|
||||
<td class="name"><var id="lvl1_fields.properties.part_handler">Part_handler</var><a class="permalink" href="#lvl1_fields.properties.part_handler" title="Permalink to this definition">¶</a></td>
|
||||
<td class="doc"><p><code>Part_handler</code> Part_handler class to use. Default is <code>Guns4d.part_handler</code></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -48,7 +48,7 @@ var docs = [
|
||||
{path:"class/gun.html#lvl1_fields.properties.ammo", type:"field", title:"lvl1_fields.properties.ammo", text:"table defines what ammo the gun uses"},
|
||||
{path:"class/gun.html#lvl1_fields.properties.visuals", type:"field", title:"lvl1_fields.properties.visuals", text:"table defines visual attributes of the gun"},
|
||||
{path:"class/gun.html#lvl1_fields.properties.ammo_handler", type:"field", title:"lvl1_fields.properties.ammo_handler", text:"Ammo_handler the class (based on) ammo_handler to create an instance of and use. Default is Guns4d.ammo_handler"},
|
||||
{path:"class/gun.html#lvl1_fields.properties.attachment_handler", type:"field", title:"lvl1_fields.properties.attachment_handler", text:"Attachment_handler attachment_handler class to use. Default is Guns4d.attachment_handler"},
|
||||
{path:"class/gun.html#lvl1_fields.properties.part_handler", type:"field", title:"lvl1_fields.properties.part_handler", text:"Part_handler Part_handler class to use. Default is Guns4d.part_handler"},
|
||||
{path:"class/gun.html#lvl1_fields.properties.sprite_scope", type:"field", title:"lvl1_fields.properties.sprite_scope", text:"Sprite_scope sprite scope class to use"},
|
||||
{path:"class/gun.html#lvl1_fields.properties.crosshair", type:"field", title:"lvl1_fields.properties.crosshair", text:"Dynamic_crosshair crosshair class to use"},
|
||||
{path:"class/gun.html#lvl1_fields.properties.initial_vertical_rotation", type:"field", title:"lvl1_fields.properties.initial_vertical_rotation", text:"starting vertical rotation of the gun"},
|
||||
|
30
init.lua
30
init.lua
@ -2,7 +2,8 @@ local Vec = vector
|
||||
Guns4d = {
|
||||
players = {},
|
||||
handler_by_ObjRef = {},
|
||||
gun_by_ObjRef = {} --used for getting the gun object by the ObjRef of the gun
|
||||
gun_by_ObjRef = {}, --used for getting the gun object by the ObjRef of the gun
|
||||
version = {1, 3, 0}
|
||||
}
|
||||
--default config values, config will be added soon:tm:
|
||||
Guns4d.config = {
|
||||
@ -70,8 +71,9 @@ dofile(path.."/Bullet_hole.lua")
|
||||
dofile(path.."/Bullet_ray.lua")
|
||||
dofile(path.."/Control_handler.lua")
|
||||
dofile(path.."/Ammo_handler.lua")
|
||||
dofile(path.."/Attachment_handler.lua")
|
||||
dofile(path.."/Part_handler.lua")
|
||||
dofile(path.."/Sprite_scope.lua")
|
||||
dofile(path.."/Reflector_sight.lua")
|
||||
dofile(path.."/Dynamic_crosshair.lua")
|
||||
dofile(path.."/Gun.lua") --> loads /classes/gun_construct.lua
|
||||
dofile(path.."/Player_model_handler.lua")
|
||||
@ -143,6 +145,12 @@ minetest.register_chatcommand("ammoinf", {
|
||||
})
|
||||
|
||||
--player handling
|
||||
setmetatable(Guns4d.handler_by_ObjRef, {
|
||||
__mode = "kv"
|
||||
})
|
||||
setmetatable(Guns4d.gun_by_ObjRef, {
|
||||
__mode = "kv"
|
||||
})
|
||||
local player_handler = Guns4d.player_handler
|
||||
local objref_mtable
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
@ -199,21 +207,9 @@ minetest.register_on_joinplayer(function(player)
|
||||
local data = gun.animation_data
|
||||
data.runtime = 0
|
||||
data.fps = frame_speed or 15
|
||||
data.loop = frame_loop
|
||||
--[[if frame_loop == nil then --still have no idea what nutjob made the default true >:(
|
||||
frame_loop = false
|
||||
end
|
||||
--so... minetest is stupid, and so it won't let me set something to the same animation twice (utterly fucking brilliant).
|
||||
--This means I literally need to flip flop between +1 frames
|
||||
--minetest.chat_send_all(dump(frame_range))
|
||||
if (data.frames.x == frame_range.x and data.frames.y == frame_range.y) and not (frame_range.x==frame_range.y) then
|
||||
--oh yeah, and it only accepts whole frames... because of course.
|
||||
frame_range.x = frame_range.x
|
||||
--minetest.chat_send_all("+1")
|
||||
end]]
|
||||
--frame_blend = 25
|
||||
--minetest.chat_send_all(dump(frame_range))
|
||||
data.frames = frame_range
|
||||
--dont use the frame_range table because it's parent could get GCed if it's a metatable (proxy table issue.)
|
||||
data.frames.x = frame_range.x
|
||||
data.frames.y = frame_range.y
|
||||
data.current_frame = data.frames.x
|
||||
end
|
||||
return old_set_animation(self, frame_range, frame_speed, frame_blend, frame_loop, ...)
|
||||
|
@ -85,107 +85,16 @@ minetest.register_chatcommand("guns4d_guide", {
|
||||
Guns4d.show_guide(player,1)
|
||||
end
|
||||
})
|
||||
|
||||
local function lstdmn(h,w)
|
||||
return {x=w+((w-1)*.125), y=h+((h-1)*.125)}
|
||||
end
|
||||
--[[local allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
end]]
|
||||
local allow_put = function(inv, listname, index, stack, player, gun)
|
||||
local props = gun.properties
|
||||
local atthan = gun.attachment_handler
|
||||
if props.inventory.attachment_slots[listname] and atthan:can_add(stack, listname) then
|
||||
return 1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
--[[local allow_take = function(inv, listname, index, stack, player, gun)
|
||||
end]]
|
||||
local on_put = function(inv, listname, index, stack, player, gun)
|
||||
gun.attachment_handler:add_attachment(stack, listname)
|
||||
end
|
||||
local on_take = function(inv, listname, index, stack, player, gun)
|
||||
gun.attachment_handler:remove_attachment(stack, listname)
|
||||
end
|
||||
function Guns4d.show_gun_menu(gun)
|
||||
local props = gun.properties
|
||||
local player = gun.player
|
||||
local pname = player:get_player_name()
|
||||
local inv = minetest.get_inventory({type="player", name=pname})
|
||||
local window = minetest.get_player_window_information(pname)
|
||||
local listname = Guns4d.config.inventory_listname
|
||||
local form_dimensions = {x=20,y=15}
|
||||
|
||||
local inv_height=4+((4-1)*.125)
|
||||
local hotbar_length = player:hud_get_hotbar_itemcount()
|
||||
local form = "\
|
||||
formspec_version[7]\
|
||||
size[".. form_dimensions.x ..",".. form_dimensions.y .."]"
|
||||
|
||||
local hotbar_height = math.ceil(hotbar_length/8)
|
||||
form = form.."\
|
||||
scroll_container[.25,"..(form_dimensions.y)-inv_height-1.25 ..";10,5;player_inventory;vertical;.05]\
|
||||
list[current_player;"..listname..";0,0;"..hotbar_length..","..hotbar_height..";]\
|
||||
list[current_player;"..listname..";0,1.5;8,3;"..hotbar_length.."]\
|
||||
scroll_container_end[]\
|
||||
"
|
||||
if math.ceil(inv:get_size("main")/8) > 4 then
|
||||
local h = math.ceil(inv:get_size("main")/8)
|
||||
form=form.."\
|
||||
scrollbaroptions[max="..h+((h-1)*.125).."]\
|
||||
scrollbar[10.25,"..(form_dimensions.y)-inv_height-1.25 ..";.5,5;vertical;player_inventory;0]\
|
||||
"
|
||||
end
|
||||
--display gun preview
|
||||
local len = math.abs(gun.model_bounding_box[3]-gun.model_bounding_box[6])/props.visuals.scale
|
||||
local hei = math.abs(gun.model_bounding_box[2]-gun.model_bounding_box[5])/props.visuals.scale
|
||||
local offsets = {x=(-gun.model_bounding_box[6]/props.visuals.scale)-(len/2), y=(gun.model_bounding_box[5]/props.visuals.scale)+(hei/2)}
|
||||
|
||||
local meter_scale = 15
|
||||
local image_scale = meter_scale*(props.inventory.render_size or 1)
|
||||
local gun_gui_offset = {x=0,y=-2.5}
|
||||
form = form.."container["..((form_dimensions.x-image_scale)/2)+gun_gui_offset.x.. ","..((form_dimensions.y-image_scale)/2)+gun_gui_offset.y.."]"
|
||||
if props.inventory.render_image then
|
||||
form = form.."image["
|
||||
..(offsets.x*meter_scale) ..","
|
||||
..(offsets.y*meter_scale) ..";"
|
||||
..image_scale..","
|
||||
..image_scale..";"
|
||||
..props.inventory.render_image.."]"
|
||||
end
|
||||
local attachment_inv = minetest.create_detached_inventory("guns4d_inv_"..pname, {
|
||||
--allow_move = allow_move,
|
||||
allow_put = function(inv, putlistname, index, stack, player)
|
||||
return allow_put(inv, putlistname, index, stack, player, gun)
|
||||
end,
|
||||
on_put = function(inv, putlistname, index, stack, player)
|
||||
return on_put(inv, putlistname, index, stack, player, gun)
|
||||
end,
|
||||
on_take = function(inv, putlistname, index, stack, player)
|
||||
return on_take(inv, putlistname, index, stack, player, gun)
|
||||
end
|
||||
--allow_take = allow_take
|
||||
})
|
||||
if props.inventory.attachment_slots then
|
||||
for i, attachment in pairs(props.inventory.attachment_slots) do
|
||||
attachment_inv:set_size(i, attachment.slots or 1)
|
||||
form = form.."label["..(image_scale/2)+(attachment.formspec_offset.x or 0)-.75 ..","..(image_scale/2)+(-attachment.formspec_offset.y or 0)-.2 ..";"..(attachment.description or i).."]"
|
||||
--list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]
|
||||
local width = attachment.slots or 1
|
||||
width = width+((width-1)*.125)
|
||||
form = form.."list[detached:guns4d_inv_"..pname..";"..i..";"..(image_scale/2)+(attachment.formspec_offset.x or 0)-(width/2)..","..(image_scale/2)+(-attachment.formspec_offset.y or 0)..";3,5;]"
|
||||
end
|
||||
end
|
||||
form = form.."container_end[]"
|
||||
minetest.show_formspec(gun.handler.player:get_player_name(), "guns4d:inventory", form)
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("guns4d_inv", {
|
||||
description = "Show the gun menu.",
|
||||
func = function(pname, arg)
|
||||
local gun = Guns4d.players[pname].gun
|
||||
if gun then
|
||||
Guns4d.show_gun_menu(gun)
|
||||
gun:open_inventory_menu()
|
||||
else
|
||||
minetest.chat_send_player(pname, "cannot show the inventory menu for a gun which is not help")
|
||||
end
|
||||
|
@ -295,40 +295,57 @@ end
|
||||
return "{"..str..string.sub(initial_string, 1, -5).."}"
|
||||
end]]
|
||||
|
||||
--replace fields (and fill sub-tables) in `tbl` with elements in `replacement`. Recursively iterates all sub-tables. use property __overfill=true for subtables that don't want to be overfilled.
|
||||
function Guns4d.table.fill(tbl, replacement, preserve_reference, indexed_tables)
|
||||
--these need to be documented flags:
|
||||
--__no_copy --the replacing (table) will not be copied, and the field containing it will be a reference to the orginal table found in replacement
|
||||
--__replace_old_table = true --the replacing table declares that the old table should be replaced with the new one
|
||||
--__replace_only = true --the original table declares that it should be replaced with the new one
|
||||
--[field] = "__redact_field" --the field will be nil even if it existed in the old table
|
||||
|
||||
--replace fields (and fill sub-tables) in `tbl` with elements in `replacement`. Recursively iterates all sub-tables. use property __replace_old_table=true for subtables that don't want to be overfilled.
|
||||
function Guns4d.table.fill(to_fill, replacement, overwrite_original, 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 = Guns4d.table.deep_copy(tbl)
|
||||
local fill_table = to_fill
|
||||
if not overwrite_original then
|
||||
fill_table = Guns4d.table.deep_copy(to_fill)
|
||||
end
|
||||
for i, v in pairs(replacement) do
|
||||
if new_table[i] then
|
||||
if fill_table[i] then --check if it actually exists in the table we're filling
|
||||
|
||||
local replacement_type = type(v)
|
||||
if replacement_type == "table" then
|
||||
if type(new_table[i]) == "table" then
|
||||
if not indexed_tables[v] then
|
||||
if not new_table[i].__overfill then
|
||||
indexed_tables[v] = true
|
||||
new_table[i] = Guns4d.table.fill(tbl[i], replacement[i], false, indexed_tables)
|
||||
else --if overfill is present, we don't want to preserve the old table.
|
||||
new_table[i] = Guns4d.table.deep_copy(replacement[i])
|
||||
end
|
||||
|
||||
if (type(fill_table[i]) == "table") then
|
||||
--only need to check if it's been indexed if we're iterating downward into it where it could loop.
|
||||
if (not indexed_tables[v]) and (not replacement[i].__replace_old_table) and (not fill_table[i].__replace_only) then
|
||||
indexed_tables[v] = true
|
||||
--get the original table and not the copy otherwise
|
||||
fill_table[i] = Guns4d.table.fill(fill_table[i], replacement[i], true, indexed_tables)
|
||||
elseif not replacement[i].__no_copy then
|
||||
fill_table[i] = Guns4d.table.deep_copy(replacement[i])
|
||||
end
|
||||
elseif not replacement[i].__no_copy then
|
||||
new_table[i] = Guns4d.table.deep_copy(replacement[i])
|
||||
|
||||
elseif not replacement[i].__no_copy then --if it's allowed to be copyed, since there's no table to fill in, we just copy it.
|
||||
fill_table[i] = Guns4d.table.deep_copy(replacement[i])
|
||||
else
|
||||
new_table[i] = replacement[i]
|
||||
fill_table[i] = replacement[i]
|
||||
end
|
||||
new_table[i].__overfill = nil
|
||||
else
|
||||
new_table[i] = replacement[i]
|
||||
|
||||
--cant modify the original table
|
||||
if not replacement[i].__no_copy then
|
||||
fill_table[i].__no_copy = nil
|
||||
fill_table[i].__replace_old_table = nil
|
||||
fill_table[i].__replace_only = nil
|
||||
end
|
||||
|
||||
elseif replacement[i] ~= "__redact_field" then
|
||||
fill_table[i] = replacement[i]
|
||||
end
|
||||
|
||||
else
|
||||
new_table[i] = replacement[i]
|
||||
fill_table[i] = replacement[i]
|
||||
end
|
||||
end
|
||||
return new_table
|
||||
return fill_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)
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
temp/character_guns4d_temp.b3d
Normal file
BIN
temp/character_guns4d_temp.b3d
Normal file
Binary file not shown.
BIN
textures/holographic_reflection.png
Normal file
BIN
textures/holographic_reflection.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 153 B |
Loading…
x
Reference in New Issue
Block a user