Introduce classes and reusable OOP
This commit is contained in:
commit
d04601a316
|
@ -3,7 +3,10 @@ allow_defined_top = true
|
|||
max_line_length = false
|
||||
|
||||
globals = {
|
||||
'x_bows'
|
||||
'XBows',
|
||||
'XBowsQuiver',
|
||||
'XBowsEntityDefBase',
|
||||
'XBowsEntityDefCustom'
|
||||
}
|
||||
|
||||
read_globals = {
|
||||
|
|
|
@ -23,7 +23,6 @@ Video: https://youtu.be/pItpltmUoa8
|
|||
* arrows adjusts pitch when flying
|
||||
* arrows can be picked up again after stuck in solid nodes
|
||||
* registers only one entity reused for all arrows
|
||||
* (experimental) poison arrow - dealing damage for 5s but will not kill the target
|
||||
* target block reduces fall damage by -30
|
||||
* quiver for more arrow storage (can hold only arrows)
|
||||
* quiver perks when in inventory (faster arrows, more arrow damage...)
|
||||
|
@ -74,7 +73,6 @@ There are few indications on how to know when the bow shot arrow from quiver:
|
|||
- default (recipes)
|
||||
- farming (bow and target recipes)
|
||||
- 3d_armor (calculates damage including the armor)
|
||||
- hbhunger (changes hudbar when poisoned)
|
||||
- mesecons (target can be used to trigger mesecon signal)
|
||||
- playerphysics (force sneak when holding charged bow)
|
||||
- player_monoids (force sneak when holding charged bow)
|
||||
|
@ -94,7 +92,6 @@ GNU Lesser General Public License v2.1 or later (see included LICENSE file)
|
|||
- x_bows_bow_wood_charged.png
|
||||
- x_bows_arrow_wood.png
|
||||
- x_bows_arrow_particle.png
|
||||
- x_bows_arrow_tipped_particle.png
|
||||
- x_bows_bubble.png
|
||||
- x_bows_target.png
|
||||
|
||||
|
@ -105,7 +102,6 @@ Modified by SaKeL:
|
|||
- x_bows_arrow_steel.png
|
||||
- x_bows_arrow_mese.png
|
||||
- x_bows_arrow_diamond.png
|
||||
- x_bows_arrow_diamond_poison.png
|
||||
|
||||
**CC-BY-SA-3.0, by paramat**
|
||||
|
||||
|
|
598
arrow.lua
598
arrow.lua
|
@ -1,597 +1 @@
|
|||
---Gets total armor level from 3d armor
|
||||
---@param player ObjectRef
|
||||
---@return integer
|
||||
local function get_3d_armor_armor(player)
|
||||
local armor_total = 0
|
||||
|
||||
if not player:is_player() or not minetest.get_modpath('3d_armor') or not armor.def[player:get_player_name()] then
|
||||
return armor_total
|
||||
end
|
||||
|
||||
armor_total = armor.def[player:get_player_name()].level
|
||||
|
||||
return armor_total
|
||||
end
|
||||
|
||||
---Limits number `x` between `min` and `max` values
|
||||
---@param x integer
|
||||
---@param min integer
|
||||
---@param max integer
|
||||
---@return integer
|
||||
local function limit(x, min, max)
|
||||
return math.min(math.max(x, min), max)
|
||||
end
|
||||
|
||||
---Gets collision box
|
||||
---@param obj ObjectRef
|
||||
---@return number[]
|
||||
local function get_obj_box(obj)
|
||||
local box
|
||||
|
||||
if obj:is_player() then
|
||||
box = obj:get_properties().collisionbox or {-0.5, 0.0, -0.5, 0.5, 1.0, 0.5}
|
||||
else
|
||||
box = obj:get_luaentity().collisionbox or {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}
|
||||
end
|
||||
|
||||
return box
|
||||
end
|
||||
|
||||
---Poison Arrow Effects
|
||||
---@param tick integer|number
|
||||
---@param time integer|number
|
||||
---@param time_left integer|number
|
||||
---@param arrow_obj ObjectRef
|
||||
---@param target_obj ObjectRef
|
||||
---@param old_damage_texture_modifier string
|
||||
---@param punch_def table
|
||||
function x_bows.poison_effect(tick, time, time_left, arrow_obj, target_obj, old_damage_texture_modifier, punch_def)
|
||||
if not arrow_obj or target_obj:get_hp() <= 0 then
|
||||
return
|
||||
end
|
||||
|
||||
target_obj:set_properties({damage_texture_modifier = '^[colorize:#00FF0050'})
|
||||
|
||||
time_left = time_left + tick
|
||||
|
||||
if time_left <= time then
|
||||
minetest.after(
|
||||
tick,
|
||||
x_bows.poison_effect,
|
||||
tick,
|
||||
time,
|
||||
time_left,
|
||||
arrow_obj,
|
||||
target_obj,
|
||||
old_damage_texture_modifier,
|
||||
punch_def
|
||||
)
|
||||
elseif target_obj:is_player() then
|
||||
if x_bows.hbhunger then
|
||||
-- Reset HUD bar color
|
||||
hb.change_hudbar(target_obj, 'health', nil, nil, 'hudbars_icon_health.png', nil, 'hudbars_bar_health.png')
|
||||
end
|
||||
|
||||
if old_damage_texture_modifier then
|
||||
target_obj:set_properties({damage_texture_modifier = old_damage_texture_modifier})
|
||||
end
|
||||
|
||||
-- return
|
||||
else
|
||||
-- local lua_ent = target_obj:get_luaentity()
|
||||
|
||||
-- if not lua_ent then
|
||||
-- return
|
||||
-- end
|
||||
|
||||
-- lua_ent[arrow_obj.arrow .. '_active'] = false
|
||||
|
||||
|
||||
if old_damage_texture_modifier then
|
||||
target_obj:set_properties({damage_texture_modifier = old_damage_texture_modifier})
|
||||
end
|
||||
-- return
|
||||
end
|
||||
|
||||
local _damage = punch_def.tool_capabilities.damage_groups.fleshy
|
||||
if target_obj:get_hp() - _damage > 0 then
|
||||
target_obj:punch(
|
||||
punch_def.puncher,
|
||||
punch_def.time_from_last_punch,
|
||||
punch_def.tool_capabilities
|
||||
)
|
||||
|
||||
local target_obj_pos = target_obj:get_pos()
|
||||
|
||||
if target_obj_pos then
|
||||
x_bows.particle_effect(target_obj_pos, 'arrow_tipped')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Main Arrow Entity
|
||||
minetest.register_entity('x_bows:arrow_entity', {
|
||||
initial_properties = {
|
||||
visual = 'wielditem',
|
||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||
selectionbox = {0, 0, 0, 0, 0, 0},
|
||||
physical = false,
|
||||
textures = {'air'},
|
||||
hp_max = 1
|
||||
},
|
||||
|
||||
---@param self table
|
||||
---@param staticdata string
|
||||
on_activate = function(self, staticdata)
|
||||
if not self or not staticdata or staticdata == '' then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
local _staticdata = minetest.deserialize(staticdata)
|
||||
|
||||
-- set/reset - do not inherit from previous entity table
|
||||
self._velocity = {x = 0, y = 0, z = 0}
|
||||
self._old_pos = nil
|
||||
self._attached = false
|
||||
self._attached_to = {
|
||||
type = '',
|
||||
pos = nil
|
||||
}
|
||||
self._has_particles = false
|
||||
self._lifetimer = 60
|
||||
self._nodechecktimer = 0.5
|
||||
self._is_drowning = false
|
||||
self._in_liquid = false
|
||||
self._poison_arrow = false
|
||||
self._shot_from_pos = self.object:get_pos()
|
||||
self.arrow = _staticdata.arrow
|
||||
self.user = minetest.get_player_by_name(_staticdata.user_name)
|
||||
self._tflp = _staticdata._tflp
|
||||
self._tool_capabilities = _staticdata._tool_capabilities
|
||||
self._is_critical_hit = _staticdata.is_critical_hit
|
||||
self._faster_arrows_multiplier = _staticdata.faster_arrows_multiplier
|
||||
self._add_damage = _staticdata.add_damage
|
||||
|
||||
if self.arrow == 'x_bows:arrow_diamond_tipped_poison' then
|
||||
self._poison_arrow = true
|
||||
end
|
||||
|
||||
self.object:set_properties({
|
||||
textures = {'x_bows:arrow_node'},
|
||||
infotext = self.arrow
|
||||
})
|
||||
end,
|
||||
|
||||
---@param self table
|
||||
---@param killer ObjectRef|nil
|
||||
on_death = function(self, killer)
|
||||
if not self._old_pos then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
minetest.item_drop(ItemStack(self.arrow), nil, vector.round(self._old_pos))
|
||||
end,
|
||||
|
||||
---@param self table
|
||||
---@param dtime integer|number
|
||||
on_step = function(self, dtime)
|
||||
local pos = self.object:get_pos()
|
||||
self._old_pos = self._old_pos or pos
|
||||
local ray = minetest.raycast(self._old_pos, pos, true, true)
|
||||
local pointed_thing = ray:next()
|
||||
|
||||
self._lifetimer = self._lifetimer - dtime
|
||||
self._nodechecktimer = self._nodechecktimer - dtime
|
||||
|
||||
-- adjust pitch when flying
|
||||
if not self._attached then
|
||||
local velocity = self.object:get_velocity()
|
||||
local v_rotation = self.object:get_rotation()
|
||||
local pitch = math.atan2(velocity.y, math.sqrt(velocity.x^2 + velocity.z^2))
|
||||
|
||||
self.object:set_rotation({
|
||||
x = pitch,
|
||||
y = v_rotation.y,
|
||||
z = v_rotation.z
|
||||
})
|
||||
end
|
||||
|
||||
-- remove attached arrows after lifetime
|
||||
if self._lifetimer <= 0 then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
-- add particles only when not attached
|
||||
if not self._attached and not self._in_liquid then
|
||||
self._has_particles = true
|
||||
|
||||
if self._tflp >= self._tool_capabilities.full_punch_interval then
|
||||
if self._is_critical_hit then
|
||||
x_bows.particle_effect(self._old_pos, 'arrow_crit')
|
||||
elseif self._faster_arrows_multiplier then
|
||||
x_bows.particle_effect(self._old_pos, 'arrow_fast')
|
||||
else
|
||||
x_bows.particle_effect(self._old_pos, 'arrow')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- remove attached arrows after object dies
|
||||
if not self.object:get_attach() and self._attached_to.type == 'object' then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
-- arrow falls down when not attached to node any more
|
||||
if self._attached_to.type == 'node' and self._attached and self._nodechecktimer <= 0 then
|
||||
local node = minetest.get_node(self._attached_to.pos)
|
||||
self._nodechecktimer = 0.5
|
||||
|
||||
if not node then
|
||||
return
|
||||
end
|
||||
|
||||
if node.name == 'air' then
|
||||
self.object:set_velocity({x = 0, y = -3, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = -3, z = 0})
|
||||
-- reset values
|
||||
self._attached = false
|
||||
self._attached_to.type = ''
|
||||
self._attached_to.pos = nil
|
||||
self.object:set_properties({collisionbox = {0, 0, 0, 0, 0, 0}})
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
while pointed_thing do
|
||||
local ip_pos = pointed_thing.intersection_point
|
||||
local in_pos = pointed_thing.intersection_normal
|
||||
self.pointed_thing = pointed_thing
|
||||
|
||||
if pointed_thing.type == 'object'
|
||||
and pointed_thing.ref ~= self.object
|
||||
and pointed_thing.ref:get_hp() > 0
|
||||
and (
|
||||
(pointed_thing.ref:is_player() and pointed_thing.ref:get_player_name() ~= self.user:get_player_name())
|
||||
or (
|
||||
pointed_thing.ref:get_luaentity()
|
||||
and pointed_thing.ref:get_luaentity().physical
|
||||
and pointed_thing.ref:get_luaentity().name ~= '__builtin:item'
|
||||
)
|
||||
)
|
||||
and self.object:get_attach() == nil
|
||||
then
|
||||
if pointed_thing.ref:is_player() then
|
||||
minetest.sound_play('x_bows_arrow_successful_hit', {
|
||||
to_player = self.user:get_player_name(),
|
||||
gain = 0.3
|
||||
})
|
||||
else
|
||||
minetest.sound_play('x_bows_arrow_hit', {
|
||||
to_player = self.user:get_player_name(),
|
||||
gain = 0.6
|
||||
})
|
||||
end
|
||||
|
||||
-- store these here before punching in case pointed_thing.ref dies
|
||||
local collisionbox = get_obj_box(pointed_thing.ref)
|
||||
local xmin = collisionbox[1] * 100
|
||||
local ymin = collisionbox[2] * 100
|
||||
local zmin = collisionbox[3] * 100
|
||||
local xmax = collisionbox[4] * 100
|
||||
local ymax = collisionbox[5] * 100
|
||||
local zmax = collisionbox[6] * 100
|
||||
|
||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||
|
||||
-- calculate damage
|
||||
local target_armor_groups = pointed_thing.ref:get_armor_groups()
|
||||
local _damage = 0
|
||||
|
||||
if self._add_damage then
|
||||
_damage = _damage + self._add_damage
|
||||
end
|
||||
|
||||
for group, base_damage in pairs(self._tool_capabilities.damage_groups) do
|
||||
_damage = _damage
|
||||
+ base_damage
|
||||
* limit(self._tflp / self._tool_capabilities.full_punch_interval, 0.0, 1.0)
|
||||
* ((target_armor_groups[group] or 0) + get_3d_armor_armor(pointed_thing.ref)) / 100.0
|
||||
end
|
||||
|
||||
-- crits
|
||||
if self._is_critical_hit then
|
||||
_damage = _damage * 2
|
||||
end
|
||||
|
||||
-- knockback
|
||||
local dir = vector.normalize(vector.subtract(self._shot_from_pos, ip_pos))
|
||||
local distance = vector.distance(self._shot_from_pos, ip_pos)
|
||||
local knockback = minetest.calculate_knockback(
|
||||
pointed_thing.ref,
|
||||
self.object,
|
||||
self._tflp,
|
||||
{
|
||||
full_punch_interval = self._tool_capabilities.full_punch_interval,
|
||||
damage_groups = {fleshy = _damage},
|
||||
},
|
||||
dir,
|
||||
distance,
|
||||
_damage
|
||||
)
|
||||
|
||||
pointed_thing.ref:add_velocity({
|
||||
x = dir.x * knockback * -1,
|
||||
y = 7,
|
||||
z = dir.z * knockback * -1
|
||||
})
|
||||
|
||||
pointed_thing.ref:punch(
|
||||
self.object,
|
||||
self._tflp,
|
||||
{
|
||||
full_punch_interval = self._tool_capabilities.full_punch_interval,
|
||||
damage_groups = {fleshy = _damage, knockback = knockback}
|
||||
},
|
||||
{
|
||||
x = dir.x * -1,
|
||||
y = 7,
|
||||
z = dir.z * -1
|
||||
}
|
||||
)
|
||||
|
||||
-- already dead (entity)
|
||||
if not pointed_thing.ref:get_luaentity() and not pointed_thing.ref:is_player() then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
-- already dead (player)
|
||||
if pointed_thing.ref:get_hp() <= 0 then
|
||||
if x_bows.hbhunger then
|
||||
-- Reset HUD bar color
|
||||
hb.change_hudbar(pointed_thing.ref, 'health', nil, nil, 'hudbars_icon_health.png', nil, 'hudbars_bar_health.png')
|
||||
end
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
-- attach arrow prepare
|
||||
local rotation = {x = 0, y = 0, z = 0}
|
||||
local position = {x = 0, y = 0, z = 0}
|
||||
|
||||
if in_pos.x == 1 then
|
||||
-- x = 0
|
||||
-- y = -90
|
||||
-- z = 0
|
||||
rotation.x = math.random(-10, 10)
|
||||
rotation.y = math.random(-100, -80)
|
||||
rotation.z = math.random(-10, 10)
|
||||
|
||||
position.x = xmax / 10
|
||||
position.y = math.random(ymin, ymax) / 10
|
||||
position.z = math.random(zmin, zmax) / 10
|
||||
elseif in_pos.x == -1 then
|
||||
-- x = 0
|
||||
-- y = 90
|
||||
-- z = 0
|
||||
rotation.x = math.random(-10, 10)
|
||||
rotation.y = math.random(80, 100)
|
||||
rotation.z = math.random(-10, 10)
|
||||
|
||||
position.x = xmin / 10
|
||||
position.y = math.random(ymin, ymax) / 10
|
||||
position.z = math.random(zmin, zmax) / 10
|
||||
elseif in_pos.y == 1 then
|
||||
-- x = -90
|
||||
-- y = 0
|
||||
-- z = -180
|
||||
rotation.x = math.random(-100, -80)
|
||||
rotation.y = math.random(-10, 10)
|
||||
rotation.z = math.random(-190, -170)
|
||||
|
||||
position.x = math.random(xmin, xmax) / 10
|
||||
position.y = ymax / 10
|
||||
position.z = math.random(zmin, zmax) / 10
|
||||
elseif in_pos.y == -1 then
|
||||
-- x = 90
|
||||
-- y = 0
|
||||
-- z = 180
|
||||
rotation.x = math.random(80, 100)
|
||||
rotation.y = math.random(-10, 10)
|
||||
rotation.z = math.random(170, 190)
|
||||
|
||||
position.x = math.random(xmin, xmax) / 10
|
||||
position.y = ymin / 10
|
||||
position.z = math.random(zmin, zmax) / 10
|
||||
elseif in_pos.z == 1 then
|
||||
-- x = 180
|
||||
-- y = 0
|
||||
-- z = 180
|
||||
rotation.x = math.random(170, 190)
|
||||
rotation.y = math.random(-10, 10)
|
||||
rotation.z = math.random(170, 190)
|
||||
|
||||
position.x = math.random(xmin, xmax) / 10
|
||||
position.y = math.random(ymin, ymax) / 10
|
||||
position.z = zmax / 10
|
||||
elseif in_pos.z == -1 then
|
||||
-- x = -180
|
||||
-- y = 180
|
||||
-- z = -180
|
||||
rotation.x = math.random(-190, -170)
|
||||
rotation.y = math.random(170, 190)
|
||||
rotation.z = math.random(-190, -170)
|
||||
|
||||
position.x = math.random(xmin, xmax) / 10
|
||||
position.y = math.random(ymin, ymax) / 10
|
||||
position.z = zmin / 10
|
||||
end
|
||||
|
||||
-- poison arrow
|
||||
if self._poison_arrow then
|
||||
local old_damage_texture_modifier = pointed_thing.ref:get_properties().damage_texture_modifier
|
||||
local punch_def = {}
|
||||
punch_def.puncher = self.object
|
||||
punch_def.time_from_last_punch = self._tflp
|
||||
punch_def.tool_capabilities = {
|
||||
full_punch_interval = self._tool_capabilities.full_punch_interval,
|
||||
damage_groups = {fleshy = _damage, knockback = knockback}
|
||||
}
|
||||
|
||||
if pointed_thing.ref:is_player() then
|
||||
-- @TODO missing `active` posion arrow check for player (see lua_ent below)
|
||||
if x_bows.hbhunger then
|
||||
-- Set poison bar
|
||||
hb.change_hudbar(
|
||||
pointed_thing.ref,
|
||||
'health',
|
||||
nil,
|
||||
nil,
|
||||
'hbhunger_icon_health_poison.png',
|
||||
nil,
|
||||
'hbhunger_bar_health_poison.png'
|
||||
)
|
||||
end
|
||||
|
||||
x_bows.poison_effect(1, 5, 0, self, pointed_thing.ref, old_damage_texture_modifier, punch_def)
|
||||
else
|
||||
-- local lua_ent = pointed_thing.ref:get_luaentity()
|
||||
-- if not lua_ent[self.arrow .. '_active'] or lua_ent[self.arrow .. '_active'] == 'false' then
|
||||
-- lua_ent[self.arrow .. '_active'] = true
|
||||
x_bows.poison_effect(1, 5, 0, self, pointed_thing.ref, old_damage_texture_modifier, punch_def)
|
||||
-- end
|
||||
end
|
||||
end
|
||||
|
||||
if not x_bows.settings.x_bows_attach_arrows_to_entities and not pointed_thing.ref:is_player() then
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
-- attach arrow
|
||||
self.object:set_attach(
|
||||
pointed_thing.ref,
|
||||
'',
|
||||
position,
|
||||
rotation,
|
||||
true
|
||||
)
|
||||
self._attached = true
|
||||
self._attached_to.type = pointed_thing.type
|
||||
self._attached_to.pos = position
|
||||
|
||||
-- remove last arrow when too many already attached
|
||||
local children = {}
|
||||
|
||||
for _, object in ipairs(pointed_thing.ref:get_children()) do
|
||||
if object:get_luaentity() and object:get_luaentity().name == 'x_bows:arrow_entity' then
|
||||
table.insert(children, object)
|
||||
end
|
||||
end
|
||||
|
||||
if #children >= 5 then
|
||||
children[1]:remove()
|
||||
end
|
||||
|
||||
return
|
||||
|
||||
elseif pointed_thing.type == 'node' and not self._attached then
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
local node_def = minetest.registered_nodes[node.name]
|
||||
|
||||
if not node_def then
|
||||
return
|
||||
end
|
||||
|
||||
self._velocity = self.object:get_velocity()
|
||||
|
||||
if node_def.drawtype == 'liquid' and not self._is_drowning then
|
||||
self._is_drowning = true
|
||||
self._in_liquid = true
|
||||
local drag = 1 / (node_def.liquid_viscosity * 6)
|
||||
self.object:set_velocity(vector.multiply(self._velocity, drag))
|
||||
self.object:set_acceleration({x = 0, y = -1.0, z = 0})
|
||||
|
||||
x_bows.particle_effect(self._old_pos, 'bubble')
|
||||
elseif self._is_drowning then
|
||||
self._is_drowning = false
|
||||
|
||||
if self._velocity then
|
||||
self.object:set_velocity(self._velocity)
|
||||
end
|
||||
|
||||
self.object:set_acceleration({x = 0, y = -9.81, z = 0})
|
||||
end
|
||||
|
||||
if x_bows.mesecons and node.name == 'x_bows:target' then
|
||||
local distance = vector.distance(pointed_thing.under, ip_pos)
|
||||
distance = math.floor(distance * 100) / 100
|
||||
|
||||
-- only close to the center of the target will trigger signal
|
||||
if distance < 0.54 then
|
||||
mesecon.receptor_on(pointed_thing.under)
|
||||
minetest.get_node_timer(pointed_thing.under):start(2)
|
||||
end
|
||||
end
|
||||
|
||||
if node_def.walkable then
|
||||
self.object:set_velocity({x=0, y=0, z=0})
|
||||
self.object:set_acceleration({x=0, y=0, z=0})
|
||||
self.object:set_pos(ip_pos)
|
||||
self.object:set_rotation(self.object:get_rotation())
|
||||
self._attached = true
|
||||
self._attached_to.type = pointed_thing.type
|
||||
self._attached_to.pos = pointed_thing.under
|
||||
self.object:set_properties({collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2}})
|
||||
|
||||
-- remove last arrow when too many already attached
|
||||
local children = {}
|
||||
|
||||
for _, object in ipairs(minetest.get_objects_inside_radius(pointed_thing.under, 1)) do
|
||||
if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == 'x_bows:arrow_entity' then
|
||||
table.insert(children, object)
|
||||
end
|
||||
end
|
||||
|
||||
if #children >= 5 then
|
||||
children[#children]:remove()
|
||||
end
|
||||
|
||||
minetest.sound_play('x_bows_arrow_hit', {
|
||||
pos = pointed_thing.under,
|
||||
gain = 0.6,
|
||||
max_hear_distance = 16
|
||||
})
|
||||
|
||||
return
|
||||
end
|
||||
end
|
||||
pointed_thing = ray:next()
|
||||
end
|
||||
|
||||
self._old_pos = pos
|
||||
end,
|
||||
|
||||
---@param self table
|
||||
---@param puncher ObjectRef|nil
|
||||
---@param time_from_last_punch number|integer|nil
|
||||
---@param tool_capabilities ToolCapabilitiesDef|nil
|
||||
---@param dir Vector
|
||||
---@param damage number|integer
|
||||
on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage)
|
||||
local wood_sound_def = default.node_sound_wood_defaults()
|
||||
|
||||
minetest.sound_play(wood_sound_def.dig.name, {
|
||||
pos = self.object:get_pos(),
|
||||
gain = wood_sound_def.dig.gain
|
||||
})
|
||||
|
||||
return false
|
||||
end,
|
||||
})
|
||||
XBows:register_entity('arrow_entity', {})
|
||||
|
|
626
init.lua
626
init.lua
|
@ -8,636 +8,70 @@ default = default--[[@as MtgDefault]]
|
|||
|
||||
math.randomseed(tonumber(tostring(os.time()):reverse():sub(1, 9))--[[@as number]])
|
||||
|
||||
local path = minetest.get_modpath('x_bows')
|
||||
local mod_start_time = minetest.get_us_time()
|
||||
local bow_charged_timer = 0
|
||||
|
||||
---x_bows main class
|
||||
---@class XBows
|
||||
x_bows = {
|
||||
pvp = minetest.settings:get_bool('enable_pvp') or false,
|
||||
creative = minetest.settings:get_bool('creative_mode') or false,
|
||||
mesecons = minetest.get_modpath('mesecons'),
|
||||
hbhunger = minetest.get_modpath('hbhunger'),
|
||||
playerphysics = minetest.get_modpath('playerphysics'),
|
||||
player_monoids = minetest.get_modpath('player_monoids'),
|
||||
registered_arrows = {},
|
||||
registered_bows = {},
|
||||
registered_quivers = {},
|
||||
player_bow_sneak = {},
|
||||
settings = {
|
||||
x_bows_attach_arrows_to_entities = minetest.settings:get_bool('x_bows_attach_arrows_to_entities', false)
|
||||
},
|
||||
quiver = {
|
||||
hud_item_ids = {},
|
||||
after_job = {}
|
||||
},
|
||||
charge_sound_after_job = {}
|
||||
}
|
||||
dofile(path .. '/api.lua')
|
||||
dofile(path .. '/particle_effects.lua')
|
||||
dofile(path .. '/nodes.lua')
|
||||
dofile(path .. '/arrow.lua')
|
||||
dofile(path .. '/items.lua')
|
||||
dofile(path .. '/quiver.lua')
|
||||
|
||||
---Shorthand for checking creative priv
|
||||
---@param name string
|
||||
---@return boolean
|
||||
function x_bows.is_creative(name)
|
||||
return x_bows.creative or minetest.check_player_privs(name, {creative = true})
|
||||
end
|
||||
|
||||
---Reset charged bow to uncharged bow, this will return the arrow item to the inventory also
|
||||
---@param player ObjectRef Player Ref
|
||||
---@param includeWielded? boolean Will include reset for wielded bow also. default: `false`
|
||||
---@return nil
|
||||
local function reset_charged_bow(player, includeWielded)
|
||||
local _includeWielded = includeWielded or false
|
||||
local inv = player:get_inventory()
|
||||
|
||||
if inv and inv:contains_item('main', 'x_bows:bow_wood_charged') then
|
||||
local inv_list = inv:get_list('main')
|
||||
|
||||
for i, st in ipairs(inv_list) do
|
||||
local reset = _includeWielded or player:get_wield_index() ~= i
|
||||
|
||||
if not st:is_empty() and x_bows.registered_bows[st:get_name()] and reset then
|
||||
local item_meta = st:get_meta()
|
||||
local arrow_itemstack = ItemStack(minetest.deserialize(item_meta:get_string('arrow_itemstack_string')))
|
||||
|
||||
-- return arrow
|
||||
if arrow_itemstack and not x_bows.is_creative(player:get_player_name()) then
|
||||
if inv:room_for_item('main', {name=arrow_itemstack:get_name()}) then
|
||||
inv:add_item('main', arrow_itemstack:get_name())
|
||||
else
|
||||
minetest.item_drop(ItemStack({name=arrow_itemstack:get_name(), count=1}), player, player:get_pos())
|
||||
end
|
||||
end
|
||||
|
||||
-- reset bow to uncharged bow
|
||||
inv:set_stack('main', i, ItemStack({
|
||||
name=x_bows.registered_bows[st:get_name()].name,
|
||||
count=st:get_count(),
|
||||
wear=st:get_wear()
|
||||
}))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
---backwards compatibility
|
||||
minetest.register_alias('x_bows:arrow_diamond_tipped_poison', 'x_bows:arrow_diamond')
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
reset_charged_bow(player, true)
|
||||
x_bows.quiver.close_quiver(player)
|
||||
XBows:reset_charged_bow(player, true)
|
||||
XBowsQuiver:close_quiver(player)
|
||||
end)
|
||||
|
||||
---Register bows
|
||||
---@param name string
|
||||
---@param def table
|
||||
---@return boolean|nil
|
||||
function x_bows.register_bow(name, def)
|
||||
if name == nil or name == '' then
|
||||
return false
|
||||
end
|
||||
|
||||
def.name = 'x_bows:' .. name
|
||||
def.name_charged = 'x_bows:' .. name .. '_charged'
|
||||
def.description = def.description or name
|
||||
def.uses = def.uses or 150
|
||||
|
||||
x_bows.registered_bows[def.name_charged] = def
|
||||
|
||||
-- not charged bow
|
||||
minetest.register_tool(def.name, {
|
||||
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Critical Arrow Chance: '
|
||||
.. (1 / def.crit_chance) * 100 .. '%'),
|
||||
inventory_image = def.inventory_image or 'x_bows_bow_wood.png',
|
||||
on_place = x_bows.load,
|
||||
on_secondary_use = x_bows.load,
|
||||
groups = {bow = 1, flammable = 1}
|
||||
})
|
||||
|
||||
-- charged bow
|
||||
minetest.register_tool(def.name_charged, {
|
||||
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Critical Arrow Chance: '
|
||||
.. (1 / def.crit_chance) * 100 .. '%'),
|
||||
inventory_image = def.inventory_image_charged or 'x_bows_bow_wood_charged.png',
|
||||
on_use = x_bows.shoot,
|
||||
groups = {bow = 1, flammable = 1, not_in_creative_inventory = 1},
|
||||
on_drop = function(itemstack, dropper, pos)
|
||||
local item_meta = itemstack:get_meta()
|
||||
local arrow_itemstack = ItemStack(minetest.deserialize(item_meta:get_string('arrow_itemstack_string')))
|
||||
|
||||
-- return arrow
|
||||
if arrow_itemstack and not x_bows.is_creative(dropper:get_player_name()) then
|
||||
minetest.item_drop(ItemStack({name=arrow_itemstack:get_name(), count=1}), dropper, {x=pos.x + 0.5, y=pos.y + 0.5, z=pos.z + 0.5})
|
||||
end
|
||||
|
||||
itemstack:set_name(def.name)
|
||||
-- returns leftover itemstack
|
||||
return minetest.item_drop(itemstack, dropper, pos)
|
||||
end
|
||||
})
|
||||
|
||||
-- recipes
|
||||
if def.recipe then
|
||||
minetest.register_craft({
|
||||
output = def.name,
|
||||
recipe = def.recipe
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---Register arrows
|
||||
---@param name string
|
||||
---@param def table
|
||||
---@return boolean|nil
|
||||
function x_bows.register_arrow(name, def)
|
||||
if name == nil or name == '' then
|
||||
return false
|
||||
end
|
||||
|
||||
def.name = 'x_bows:' .. name
|
||||
def.description = def.description or name
|
||||
|
||||
x_bows.registered_arrows[def.name] = def
|
||||
|
||||
minetest.register_craftitem('x_bows:' .. name, {
|
||||
description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Damage: '
|
||||
.. def.tool_capabilities.damage_groups.fleshy) .. '\n' .. minetest.colorize('#00BFFF', 'Charge Time: '
|
||||
.. def.tool_capabilities.full_punch_interval .. 's'),
|
||||
short_description = def.description,
|
||||
inventory_image = def.inventory_image,
|
||||
groups = {arrow = 1, flammable = 1}
|
||||
})
|
||||
|
||||
-- recipes
|
||||
if def.craft then
|
||||
minetest.register_craft({
|
||||
output = def.name ..' ' .. (def.craft_count or 4),
|
||||
recipe = def.craft
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---Register quivers
|
||||
---@param name string
|
||||
---@param def table
|
||||
---@return boolean|nil
|
||||
function x_bows.register_quiver(name, def)
|
||||
if name == nil or name == '' then
|
||||
return false
|
||||
end
|
||||
|
||||
def.name = 'x_bows:' .. name
|
||||
def.name_open = 'x_bows:' .. name .. '_open'
|
||||
def.description = def.description or name
|
||||
def.uses = def.uses or 150
|
||||
|
||||
x_bows.registered_quivers[def.name] = def
|
||||
|
||||
---closed quiver
|
||||
minetest.register_tool(def.name, {
|
||||
description = def.description
|
||||
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
||||
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
||||
short_description = def.short_description
|
||||
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
||||
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
||||
inventory_image = def.inventory_image or 'x_bows_quiver.png',
|
||||
wield_image = def.wield_image or 'x_bows_quiver.png',
|
||||
groups = {quiver = 1, flammable = 1},
|
||||
on_secondary_use = function(itemstack, user, pointed_thing)
|
||||
return x_bows.open_quiver(itemstack, user)
|
||||
end,
|
||||
---@param itemstack ItemStack
|
||||
---@param placer ObjectRef
|
||||
---@param pointed_thing PointedThingDef
|
||||
---@return ItemStack|nil
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.under then
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
local node_def = minetest.registered_nodes[node.name]
|
||||
|
||||
if node_def and node_def.on_rightclick then
|
||||
return node_def.on_rightclick(pointed_thing.under, node, placer, itemstack, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
return x_bows.open_quiver(itemstack, placer)
|
||||
end
|
||||
})
|
||||
|
||||
---open quiver
|
||||
minetest.register_tool(def.name_open, {
|
||||
description = def.description
|
||||
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
||||
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
||||
short_description = def.short_description
|
||||
.. '\n' .. minetest.colorize('#00FF00', 'Faster Arrows: ' .. (1 / def.faster_arrows) * 100 .. '%')
|
||||
.. '\n' .. minetest.colorize('#FF8080', 'Arrow Damage: +' .. def.add_damage),
|
||||
inventory_image = def.inventory_image_open or 'x_bows_quiver_open.png',
|
||||
wield_image = def.wield_image_open or 'x_bows_quiver_open.png',
|
||||
groups = {quiver = 1, flammable = 1, not_in_creative_inventory = 1},
|
||||
---@param itemstack ItemStack
|
||||
---@param dropper ObjectRef|nil
|
||||
---@param pos Vector
|
||||
---@return ItemStack
|
||||
on_drop = function (itemstack, dropper, pos)
|
||||
local replace_item = x_bows.quiver.get_replacement_item(itemstack, 'x_bows:quiver')
|
||||
return minetest.item_drop(replace_item, dropper, pos)
|
||||
end
|
||||
})
|
||||
|
||||
-- recipes
|
||||
if def.recipe then
|
||||
minetest.register_craft({
|
||||
output = def.name,
|
||||
recipe = def.recipe
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
---Loads bow
|
||||
---@param itemstack ItemStack
|
||||
---@param user ObjectRef
|
||||
---@param pointed_thing PointedThingDef
|
||||
---@return ItemStack
|
||||
function x_bows.load(itemstack, user, pointed_thing)
|
||||
local player_name = user:get_player_name()
|
||||
local inv = user:get_inventory()--[[@as InvRef]]
|
||||
local inv_list = inv:get_list('main')
|
||||
local bow_name = itemstack:get_name()
|
||||
local bow_def = x_bows.registered_bows[bow_name .. '_charged']
|
||||
---@alias ItemStackArrows {["stack"]: ItemStack, ["idx"]: number|integer}[]
|
||||
---@type ItemStackArrows
|
||||
local itemstack_arrows = {}
|
||||
|
||||
---trigger right click event if pointed item has one
|
||||
if pointed_thing.under then
|
||||
local node = minetest.get_node(pointed_thing.under)
|
||||
local node_def = minetest.registered_nodes[node.name]
|
||||
|
||||
if node_def and node_def.on_rightclick then
|
||||
return node_def.on_rightclick(pointed_thing.under, node, user, itemstack, pointed_thing)
|
||||
end
|
||||
end
|
||||
|
||||
---find itemstack arrow in quiver
|
||||
local quiver_result = x_bows.quiver.get_itemstack_arrow_from_quiver(user)
|
||||
local itemstack_arrow = quiver_result.found_arrow_stack
|
||||
|
||||
if itemstack_arrow then
|
||||
local itemstack_arrow_meta = itemstack_arrow:get_meta()
|
||||
|
||||
itemstack_arrow_meta:set_int('is_arrow_from_quiver', 1)
|
||||
itemstack_arrow_meta:set_string('quiver_name', quiver_result.quiver_name)
|
||||
itemstack_arrow_meta:set_string('quiver_id', quiver_result.quiver_id)
|
||||
else
|
||||
x_bows.quiver.remove_hud(user)
|
||||
|
||||
---find itemstack arrow in players inventory
|
||||
for i, st in ipairs(inv_list) do
|
||||
if not st:is_empty() and x_bows.registered_arrows[st:get_name()] then
|
||||
table.insert(itemstack_arrows, {stack = st, idx = i})
|
||||
end
|
||||
end
|
||||
|
||||
-- take 1st found arrow in the list
|
||||
itemstack_arrow = #itemstack_arrows > 0 and itemstack_arrows[1].stack or nil
|
||||
end
|
||||
|
||||
if itemstack_arrow and bow_def then
|
||||
local _tool_capabilities = x_bows.registered_arrows[itemstack_arrow:get_name()].tool_capabilities
|
||||
|
||||
---@param v_user ObjectRef
|
||||
---@param v_bow_name string
|
||||
---@param v_itemstack_arrow ItemStack
|
||||
---@param v_inv InvRef
|
||||
---@param v_itemstack_arrows ItemStackArrows
|
||||
minetest.after(0, function(v_user, v_bow_name, v_itemstack_arrow, v_inv, v_itemstack_arrows)
|
||||
local wielded_item = v_user:get_wielded_item()
|
||||
|
||||
if wielded_item:get_name() == v_bow_name then
|
||||
local wielded_item_meta = wielded_item:get_meta()
|
||||
local v_itemstack_arrow_meta = v_itemstack_arrow:get_meta()
|
||||
|
||||
wielded_item_meta:set_string('arrow_itemstack_string', minetest.serialize(v_itemstack_arrow:to_table()))
|
||||
wielded_item_meta:set_string('time_load', tostring(minetest.get_us_time()))
|
||||
|
||||
wielded_item:set_name(v_bow_name .. '_charged')
|
||||
v_user:set_wielded_item(wielded_item)
|
||||
|
||||
if not x_bows.is_creative(v_user:get_player_name()) and v_itemstack_arrow_meta:get_int('is_arrow_from_quiver') ~= 1 then
|
||||
v_itemstack_arrow:take_item()
|
||||
v_inv:set_stack('main', v_itemstack_arrows[1].idx, v_itemstack_arrow)
|
||||
end
|
||||
end
|
||||
end, user, bow_name, itemstack_arrow, inv, itemstack_arrows)
|
||||
|
||||
---stop previous charged sound after job
|
||||
if x_bows.charge_sound_after_job[player_name] then
|
||||
for _, v in pairs(x_bows.charge_sound_after_job[player_name]) do
|
||||
v:cancel()
|
||||
end
|
||||
|
||||
x_bows.charge_sound_after_job[player_name] = {}
|
||||
else
|
||||
x_bows.charge_sound_after_job[player_name] = {}
|
||||
end
|
||||
|
||||
---sound plays when charge time reaches full punch interval time
|
||||
table.insert(x_bows.charge_sound_after_job[player_name], minetest.after(_tool_capabilities.full_punch_interval, function(v_user, v_bow_name)
|
||||
local wielded_item = v_user:get_wielded_item()
|
||||
local wielded_item_name = wielded_item:get_name()
|
||||
|
||||
if wielded_item_name == v_bow_name .. '_charged' then
|
||||
minetest.sound_play('x_bows_bow_loaded', {
|
||||
to_player = v_user:get_player_name(),
|
||||
gain = 0.6
|
||||
})
|
||||
end
|
||||
end, user, bow_name))
|
||||
|
||||
minetest.sound_play('x_bows_bow_load', {
|
||||
to_player = player_name,
|
||||
gain = 0.6
|
||||
})
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
---Shoots the bow
|
||||
---@param itemstack ItemStack
|
||||
---@param user ObjectRef
|
||||
---@param pointed_thing? PointedThingDef
|
||||
---@return ItemStack
|
||||
function x_bows.shoot(itemstack, user, pointed_thing)
|
||||
local time_shoot = minetest.get_us_time();
|
||||
local meta = itemstack:get_meta()
|
||||
local time_load = tonumber(meta:get_string('time_load'))
|
||||
local tflp = (time_shoot - time_load) / 1000000
|
||||
---@type ItemStack
|
||||
local arrow_itemstack = ItemStack(minetest.deserialize(meta:get_string('arrow_itemstack_string')))
|
||||
local arrow_itemstack_meta = arrow_itemstack:get_meta()
|
||||
local arrow_name = arrow_itemstack:get_name()
|
||||
local is_arrow_from_quiver = arrow_itemstack_meta:get_int('is_arrow_from_quiver')
|
||||
local quiver_name = arrow_itemstack_meta:get_string('quiver_name')
|
||||
local quiver_id = arrow_itemstack_meta:get_string('quiver_id')
|
||||
local detached_inv = x_bows.quiver.get_or_create_detached_inv(
|
||||
quiver_id,
|
||||
user:get_player_name()
|
||||
)
|
||||
|
||||
if is_arrow_from_quiver == 1 then
|
||||
x_bows.quiver.udate_or_create_hud(user, detached_inv:get_list('main'))
|
||||
else
|
||||
x_bows.quiver.remove_hud(user)
|
||||
end
|
||||
|
||||
if not x_bows.registered_arrows[arrow_name] then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local bow_name_charged = itemstack:get_name()
|
||||
local bow_name = x_bows.registered_bows[bow_name_charged].name
|
||||
local uses = x_bows.registered_bows[bow_name_charged].uses
|
||||
local crit_chance = x_bows.registered_bows[bow_name_charged].crit_chance
|
||||
local _tool_capabilities = x_bows.registered_arrows[arrow_name].tool_capabilities
|
||||
local quiver_xbows_def = x_bows.registered_quivers[quiver_name]
|
||||
|
||||
local staticdata = {
|
||||
arrow = arrow_name,
|
||||
user_name = user:get_player_name(),
|
||||
is_critical_hit = false,
|
||||
_tool_capabilities = _tool_capabilities,
|
||||
_tflp = tflp,
|
||||
}
|
||||
|
||||
---crits, only on full punch interval
|
||||
if crit_chance and crit_chance > 1 and tflp >= _tool_capabilities.full_punch_interval then
|
||||
if math.random(1, crit_chance) == 1 then
|
||||
staticdata.is_critical_hit = true
|
||||
end
|
||||
end
|
||||
|
||||
---speed multiply
|
||||
if quiver_xbows_def and quiver_xbows_def.faster_arrows and quiver_xbows_def.faster_arrows > 1 then
|
||||
staticdata.faster_arrows_multiplier = quiver_xbows_def.faster_arrows
|
||||
end
|
||||
|
||||
---add damage
|
||||
if quiver_xbows_def and quiver_xbows_def.add_damage and quiver_xbows_def.add_damage > 1 then
|
||||
staticdata.add_damage = quiver_xbows_def.add_damage
|
||||
end
|
||||
|
||||
---sound
|
||||
local sound_name = 'x_bows_bow_shoot'
|
||||
if staticdata.is_critical_hit then
|
||||
sound_name = 'x_bows_bow_shoot_crit'
|
||||
end
|
||||
|
||||
meta:set_string('arrow_itemstack_string', '')
|
||||
itemstack:set_name(bow_name)
|
||||
|
||||
local pos = user:get_pos()
|
||||
local dir = user:get_look_dir()
|
||||
local obj = minetest.add_entity(
|
||||
{
|
||||
x = pos.x,
|
||||
y = pos.y + 1.5,
|
||||
z = pos.z
|
||||
},
|
||||
'x_bows:arrow_entity',
|
||||
minetest.serialize(staticdata)
|
||||
)
|
||||
|
||||
if not obj then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local strength_multiplier = tflp
|
||||
|
||||
if strength_multiplier > _tool_capabilities.full_punch_interval then
|
||||
strength_multiplier = 1
|
||||
|
||||
---faster arrow, only on full punch interval
|
||||
if staticdata.faster_arrows_multiplier then
|
||||
strength_multiplier = strength_multiplier + (strength_multiplier / staticdata.faster_arrows_multiplier)
|
||||
end
|
||||
end
|
||||
|
||||
local strength = 30 * strength_multiplier
|
||||
|
||||
obj:set_velocity(vector.multiply(dir, strength))
|
||||
obj:set_acceleration({x = dir.x * -3, y = -10, z = dir.z * -3})
|
||||
obj:set_yaw(minetest.dir_to_yaw(dir))
|
||||
|
||||
if not x_bows.is_creative(user:get_player_name()) then
|
||||
itemstack:add_wear(65535 / uses)
|
||||
end
|
||||
|
||||
minetest.sound_play(sound_name, {
|
||||
gain = 0.3,
|
||||
pos = user:get_pos(),
|
||||
max_hear_distance = 10
|
||||
})
|
||||
|
||||
return itemstack
|
||||
end
|
||||
|
||||
---Arrow particle
|
||||
---@param pos Vector
|
||||
---@param type 'arrow' | 'arrow_crit' | 'bubble' | 'arrow_tipped'
|
||||
---@return number|nil
|
||||
function x_bows.particle_effect(pos, type)
|
||||
if type == 'arrow' then
|
||||
return minetest.add_particlespawner({
|
||||
amount = 1,
|
||||
time = 0.1,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 2,
|
||||
maxsize = 2,
|
||||
texture = 'x_bows_arrow_particle.png',
|
||||
animation = {
|
||||
type = 'vertical_frames',
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 1,
|
||||
},
|
||||
glow = 1
|
||||
})
|
||||
elseif type == 'arrow_crit' then
|
||||
return minetest.add_particlespawner({
|
||||
amount = 3,
|
||||
time = 0.1,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minexptime = 0.5,
|
||||
maxexptime = 0.5,
|
||||
minsize = 2,
|
||||
maxsize = 2,
|
||||
texture = 'x_bows_arrow_particle.png^[colorize:#B22222:127',
|
||||
animation = {
|
||||
type = 'vertical_frames',
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 1,
|
||||
},
|
||||
glow = 1
|
||||
})
|
||||
elseif type == 'arrow_fast' then
|
||||
return minetest.add_particlespawner({
|
||||
amount = 3,
|
||||
time = 0.1,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minexptime = 0.5,
|
||||
maxexptime = 0.5,
|
||||
minsize = 2,
|
||||
maxsize = 2,
|
||||
texture = 'x_bows_arrow_particle.png^[colorize:#0000FF:64',
|
||||
animation = {
|
||||
type = 'vertical_frames',
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 1,
|
||||
},
|
||||
glow = 1
|
||||
})
|
||||
elseif type == 'bubble' then
|
||||
return minetest.add_particlespawner({
|
||||
amount = 1,
|
||||
time = 1,
|
||||
minpos = pos,
|
||||
maxpos = pos,
|
||||
minvel = {x=1, y=1, z=0},
|
||||
maxvel = {x=1, y=1, z=0},
|
||||
minacc = {x=1, y=1, z=1},
|
||||
maxacc = {x=1, y=1, z=1},
|
||||
minexptime = 0.2,
|
||||
maxexptime = 0.5,
|
||||
minsize = 0.5,
|
||||
maxsize = 1,
|
||||
texture = 'x_bows_bubble.png'
|
||||
})
|
||||
elseif type == 'arrow_tipped' then
|
||||
return minetest.add_particlespawner({
|
||||
amount = 5,
|
||||
time = 1,
|
||||
minpos = vector.subtract(pos, 0.5),
|
||||
maxpos = vector.add(pos, 0.5),
|
||||
minexptime = 0.4,
|
||||
maxexptime = 0.8,
|
||||
minvel = {x=-0.4, y=0.4, z=-0.4},
|
||||
maxvel = {x=0.4, y=0.6, z=0.4},
|
||||
minacc = {x=0.2, y=0.4, z=0.2},
|
||||
maxacc = {x=0.4, y=0.6, z=0.4},
|
||||
minsize = 4,
|
||||
maxsize = 6,
|
||||
texture = 'x_bows_arrow_tipped_particle.png^[colorize:#008000:127',
|
||||
animation = {
|
||||
type = 'vertical_frames',
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 1,
|
||||
},
|
||||
glow = 1
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
-- sneak, fov adjustments when bow is charged
|
||||
minetest.register_globalstep(function(dtime)
|
||||
bow_charged_timer = bow_charged_timer + dtime
|
||||
|
||||
if bow_charged_timer > 0.5 then
|
||||
for _, player in ipairs(minetest.get_connected_players()) do
|
||||
local name = player:get_player_name()
|
||||
local stack = player:get_wielded_item()
|
||||
local item = stack:get_name()
|
||||
local player_name = player:get_player_name()
|
||||
local wielded_stack = player:get_wielded_item()
|
||||
local wielded_stack_name = wielded_stack:get_name()
|
||||
|
||||
if not item then
|
||||
if not wielded_stack_name then
|
||||
return
|
||||
end
|
||||
|
||||
if not x_bows.player_bow_sneak[name] then
|
||||
x_bows.player_bow_sneak[name] = {}
|
||||
if not XBows.player_bow_sneak[player_name] then
|
||||
XBows.player_bow_sneak[player_name] = {}
|
||||
end
|
||||
|
||||
if item == 'x_bows:bow_wood_charged' and not x_bows.player_bow_sneak[name].sneak then
|
||||
if x_bows.playerphysics then
|
||||
playerphysics.add_physics_factor(player, 'speed', 'x_bows:bow_wood_charged', 0.25)
|
||||
elseif x_bows.player_monoids then
|
||||
player_monoids.speed:add_change(player, 0.25, 'x_bows:bow_wood_charged')
|
||||
if minetest.get_item_group(wielded_stack_name, 'bow_charged') ~= 0 and not XBows.player_bow_sneak[player_name].sneak then
|
||||
if XBows.playerphysics then
|
||||
playerphysics.add_physics_factor(player, 'speed', 'x_bows:bow_charged_speed', 0.25)
|
||||
elseif XBows.player_monoids then
|
||||
player_monoids.speed:add_change(player, 0.25, 'x_bows:bow_charged_speed')
|
||||
end
|
||||
|
||||
x_bows.player_bow_sneak[name].sneak = true
|
||||
XBows.player_bow_sneak[player_name].sneak = true
|
||||
player:set_fov(0.9, true, 0.4)
|
||||
elseif item ~= 'x_bows:bow_wood_charged' and x_bows.player_bow_sneak[name].sneak then
|
||||
if x_bows.playerphysics then
|
||||
playerphysics.remove_physics_factor(player, 'speed', 'x_bows:bow_wood_charged')
|
||||
elseif x_bows.player_monoids then
|
||||
player_monoids.speed:del_change(player, 'x_bows:bow_wood_charged')
|
||||
elseif minetest.get_item_group(wielded_stack_name, 'bow_charged') == 0 and XBows.player_bow_sneak[player_name].sneak then
|
||||
if XBows.playerphysics then
|
||||
playerphysics.remove_physics_factor(player, 'speed', 'x_bows:bow_charged_speed')
|
||||
elseif XBows.player_monoids then
|
||||
player_monoids.speed:del_change(player, 'x_bows:bow_charged_speed')
|
||||
end
|
||||
|
||||
x_bows.player_bow_sneak[name].sneak = false
|
||||
XBows.player_bow_sneak[player_name].sneak = false
|
||||
player:set_fov(0, true, 0.4)
|
||||
end
|
||||
|
||||
reset_charged_bow(player)
|
||||
XBows:reset_charged_bow(player)
|
||||
end
|
||||
|
||||
bow_charged_timer = 0
|
||||
end
|
||||
end)
|
||||
|
||||
local path = minetest.get_modpath('x_bows')
|
||||
|
||||
dofile(path .. '/nodes.lua')
|
||||
dofile(path .. '/arrow.lua')
|
||||
dofile(path .. '/items.lua')
|
||||
dofile(path .. '/quiver.lua')
|
||||
|
||||
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000
|
||||
|
||||
print('[Mod] x_bows loaded.. ['.. mod_end_time ..'s]')
|
||||
|
|
100
items.lua
100
items.lua
|
@ -1,20 +1,31 @@
|
|||
x_bows.register_bow('bow_wood', {
|
||||
XBows:register_bow('bow_wood', {
|
||||
description = 'Wooden Bow',
|
||||
short_description = 'Wooden Bow',
|
||||
custom = {
|
||||
uses = 385,
|
||||
-- `crit_chance` 10% chance, 5 is 20% chance
|
||||
-- (1 / crit_chance) * 100 = % chance
|
||||
crit_chance = 10,
|
||||
recipe = {
|
||||
{'', 'default:stick', 'farming:string'},
|
||||
{'default:stick', '', 'farming:string'},
|
||||
{'', 'default:stick', 'farming:string'},
|
||||
{'', 'default:stick', 'farming:string'}
|
||||
},
|
||||
fuel_burntime = 3,
|
||||
allowed_ammunition = {
|
||||
'x_bows:arrow_wood',
|
||||
'x_bows:arrow_stone',
|
||||
'x_bows:arrow_bronze',
|
||||
'x_bows:arrow_steel',
|
||||
'x_bows:arrow_mese',
|
||||
'x_bows:arrow_diamond'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
x_bows.register_arrow('arrow_wood', {
|
||||
XBows:register_arrow('arrow_wood', {
|
||||
description = 'Arrow Wood',
|
||||
inventory_image = 'x_bows_arrow_wood.png',
|
||||
craft = {
|
||||
custom = {
|
||||
recipe = {
|
||||
{'default:flint'},
|
||||
{'group:stick'},
|
||||
{'group:wool'}
|
||||
|
@ -23,13 +34,16 @@ x_bows.register_arrow('arrow_wood', {
|
|||
full_punch_interval = 1,
|
||||
max_drop_level = 0,
|
||||
damage_groups = {fleshy=2}
|
||||
},
|
||||
fuel_burntime = 1
|
||||
}
|
||||
})
|
||||
|
||||
x_bows.register_arrow('arrow_stone', {
|
||||
XBows:register_arrow('arrow_stone', {
|
||||
description = 'Arrow Stone',
|
||||
inventory_image = 'x_bows_arrow_stone.png',
|
||||
craft = {
|
||||
custom = {
|
||||
recipe = {
|
||||
{'default:flint'},
|
||||
{'group:stone'},
|
||||
{'group:wool'}
|
||||
|
@ -39,12 +53,14 @@ x_bows.register_arrow('arrow_stone', {
|
|||
max_drop_level = 0,
|
||||
damage_groups = {fleshy=4}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
x_bows.register_arrow('arrow_bronze', {
|
||||
XBows:register_arrow('arrow_bronze', {
|
||||
description = 'Arrow Bronze',
|
||||
inventory_image = 'x_bows_arrow_bronze.png',
|
||||
craft = {
|
||||
custom = {
|
||||
recipe = {
|
||||
{'default:flint'},
|
||||
{'default:bronze_ingot'},
|
||||
{'group:wool'}
|
||||
|
@ -54,12 +70,14 @@ x_bows.register_arrow('arrow_bronze', {
|
|||
max_drop_level = 1,
|
||||
damage_groups = {fleshy=6}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
x_bows.register_arrow('arrow_steel', {
|
||||
XBows:register_arrow('arrow_steel', {
|
||||
description = 'Arrow Steel',
|
||||
inventory_image = 'x_bows_arrow_steel.png',
|
||||
craft = {
|
||||
custom = {
|
||||
recipe = {
|
||||
{'default:flint'},
|
||||
{'default:steel_ingot'},
|
||||
{'group:wool'}
|
||||
|
@ -69,12 +87,14 @@ x_bows.register_arrow('arrow_steel', {
|
|||
max_drop_level = 1,
|
||||
damage_groups = {fleshy=6}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
x_bows.register_arrow('arrow_mese', {
|
||||
XBows:register_arrow('arrow_mese', {
|
||||
description = 'Arrow Mese',
|
||||
inventory_image = 'x_bows_arrow_mese.png',
|
||||
craft = {
|
||||
custom = {
|
||||
recipe = {
|
||||
{'default:flint'},
|
||||
{'default:mese_crystal'},
|
||||
{'group:wool'}
|
||||
|
@ -84,12 +104,14 @@ x_bows.register_arrow('arrow_mese', {
|
|||
max_drop_level = 1,
|
||||
damage_groups = {fleshy=7}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
x_bows.register_arrow('arrow_diamond', {
|
||||
XBows:register_arrow('arrow_diamond', {
|
||||
description = 'Arrow Diamond',
|
||||
inventory_image = 'x_bows_arrow_diamond.png',
|
||||
craft = {
|
||||
custom = {
|
||||
recipe = {
|
||||
{'default:flint'},
|
||||
{'default:diamond'},
|
||||
{'group:wool'}
|
||||
|
@ -99,25 +121,13 @@ x_bows.register_arrow('arrow_diamond', {
|
|||
max_drop_level = 1,
|
||||
damage_groups = {fleshy=8}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
x_bows.register_arrow('arrow_diamond_tipped_poison', {
|
||||
description = 'Arrow Diamond Tipped Poison (0:05)',
|
||||
inventory_image = 'x_bows_arrow_diamond_poison.png',
|
||||
craft = {
|
||||
{'', '', ''},
|
||||
{'', 'default:marram_grass_1', ''},
|
||||
{'', 'x_bows:arrow_diamond', ''}
|
||||
},
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 0.7,
|
||||
max_drop_level = 1,
|
||||
damage_groups = {fleshy=8}
|
||||
},
|
||||
craft_count = 1
|
||||
})
|
||||
|
||||
x_bows.register_quiver('quiver', {
|
||||
XBows:register_quiver('quiver', {
|
||||
description = 'Quiver \n\n Empty\n',
|
||||
short_description = 'Quiver',
|
||||
custom = {
|
||||
description = 'Quiver \n\n Empty\n',
|
||||
short_description = 'Quiver',
|
||||
recipe = {
|
||||
|
@ -125,25 +135,9 @@ x_bows.register_quiver('quiver', {
|
|||
{'group:arrow', 'wool:brown', 'group:arrow'},
|
||||
{'group:arrow', 'group:arrow', 'group:arrow'}
|
||||
},
|
||||
craft_count = 1,
|
||||
recipe_count = 1,
|
||||
faster_arrows = 5,
|
||||
add_damage = 2
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = 'fuel',
|
||||
recipe = 'x_bows:bow_wood',
|
||||
burntime = 3,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = 'fuel',
|
||||
recipe = 'x_bows:arrow_wood',
|
||||
burntime = 1,
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = 'fuel',
|
||||
recipe = 'x_bows:quiver',
|
||||
burntime = 3,
|
||||
add_damage = 2,
|
||||
fuel_burntime = 3
|
||||
}
|
||||
})
|
||||
|
|
2
mod.conf
2
mod.conf
|
@ -1,6 +1,6 @@
|
|||
name = x_bows
|
||||
description = Adds bow and arrows to Minetest.
|
||||
depends =
|
||||
optional_depends = default, farming, 3d_armor, hbhunger, mesecons, playerphysics, player_monoids, wool
|
||||
optional_depends = default, farming, 3d_armor, mesecons, playerphysics, player_monoids, wool
|
||||
supported_games = minetest_game
|
||||
min_minetest_version = 5.4
|
||||
|
|
14
nodes.lua
14
nodes.lua
|
@ -10,22 +10,28 @@ minetest.register_node('x_bows:arrow_node', {
|
|||
})
|
||||
|
||||
minetest.register_node('x_bows:target', {
|
||||
description = 'Straw',
|
||||
description = 'Target',
|
||||
tiles = {'x_bows_target.png'},
|
||||
is_ground_content = false,
|
||||
groups = {snappy=3, flammable=4, fall_damage_add_percent=-30},
|
||||
groups = {snappy=3, flammable=4, fall_damage_add_percent = -30},
|
||||
sounds = default.node_sound_leaves_defaults(),
|
||||
mesecons = {receptor = {state = 'off'}},
|
||||
---@param pos Vector
|
||||
---@param elapsed number
|
||||
---@return boolean
|
||||
on_timer = function (pos, elapsed)
|
||||
if XBows.mesecons then
|
||||
mesecon.receptor_off(pos)
|
||||
end
|
||||
|
||||
return false
|
||||
end,
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = 'fuel',
|
||||
recipe = 'x_bows:target',
|
||||
burntime = 3,
|
||||
burntime = 3
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
XBows:register_particle_effect('arrow', {
|
||||
amount = 1,
|
||||
time = 0.1,
|
||||
minexptime = 1,
|
||||
maxexptime = 1,
|
||||
minsize = 2,
|
||||
maxsize = 2,
|
||||
texture = 'x_bows_arrow_particle.png',
|
||||
animation = {
|
||||
type = 'vertical_frames',
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 1,
|
||||
},
|
||||
glow = 1
|
||||
})
|
||||
|
||||
XBows:register_particle_effect('arrow_crit', {
|
||||
amount = 3,
|
||||
time = 0.1,
|
||||
minexptime = 0.5,
|
||||
maxexptime = 0.5,
|
||||
minsize = 2,
|
||||
maxsize = 2,
|
||||
texture = 'x_bows_arrow_particle.png^[colorize:#B22222:127',
|
||||
animation = {
|
||||
type = 'vertical_frames',
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 1,
|
||||
},
|
||||
glow = 1
|
||||
})
|
||||
|
||||
XBows:register_particle_effect('arrow_fast', {
|
||||
amount = 3,
|
||||
time = 0.1,
|
||||
minexptime = 0.5,
|
||||
maxexptime = 0.5,
|
||||
minsize = 2,
|
||||
maxsize = 2,
|
||||
texture = 'x_bows_arrow_particle.png^[colorize:#0000FF:64',
|
||||
animation = {
|
||||
type = 'vertical_frames',
|
||||
aspect_w = 8,
|
||||
aspect_h = 8,
|
||||
length = 1,
|
||||
},
|
||||
glow = 1
|
||||
})
|
||||
|
||||
XBows:register_particle_effect('bubble', {
|
||||
amount = 1,
|
||||
time = 1,
|
||||
minvel = {x=1, y=1, z=0},
|
||||
maxvel = {x=1, y=1, z=0},
|
||||
minacc = {x=1, y=1, z=1},
|
||||
maxacc = {x=1, y=1, z=1},
|
||||
minexptime = 0.2,
|
||||
maxexptime = 0.5,
|
||||
minsize = 0.5,
|
||||
maxsize = 1,
|
||||
texture = 'x_bows_bubble.png'
|
||||
})
|
512
quiver.lua
512
quiver.lua
|
@ -1,515 +1,5 @@
|
|||
---Close one or all open quivers in players inventory
|
||||
---@param player ObjectRef
|
||||
---@param quiver_id? string If `nil` then all open quivers will be closed
|
||||
---@returns nil
|
||||
function x_bows.quiver.close_quiver(player, quiver_id)
|
||||
local player_inv = player:get_inventory()
|
||||
|
||||
---find matching quiver item in players inventory with the open formspec name
|
||||
if player_inv and player_inv:contains_item('main', 'x_bows:quiver_open') then
|
||||
local inv_list = player_inv:get_list('main')
|
||||
|
||||
for i, st in ipairs(inv_list) do
|
||||
local st_meta = st:get_meta()
|
||||
|
||||
if not st:is_empty() and st:get_name() == 'x_bows:quiver_open' then
|
||||
if quiver_id and st_meta:get_string('quiver_id') == quiver_id then
|
||||
local replace_item = x_bows.quiver.get_replacement_item(st, 'x_bows:quiver')
|
||||
player_inv:set_stack('main', i, replace_item)
|
||||
break
|
||||
else
|
||||
local replace_item = x_bows.quiver.get_replacement_item(st, 'x_bows:quiver')
|
||||
player_inv:set_stack('main', i, replace_item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---Swap item in player inventory indicating open quiver. Preserve all ItemStack definition and meta.
|
||||
---@param from_stack ItemStack transfer data from this item
|
||||
---@param to_item_name string transfer data to this item
|
||||
---@return ItemStack ItemStack replacement item
|
||||
function x_bows.quiver.get_replacement_item(from_stack, to_item_name)
|
||||
---@type ItemStack
|
||||
local replace_item = ItemStack({
|
||||
name = to_item_name,
|
||||
count = from_stack:get_count(),
|
||||
wear = from_stack:get_wear()
|
||||
})
|
||||
local replace_item_meta = replace_item:get_meta()
|
||||
local from_stack_meta = from_stack:get_meta()
|
||||
|
||||
replace_item_meta:set_string('quiver_items', from_stack_meta:get_string('quiver_items'))
|
||||
replace_item_meta:set_string('quiver_id', from_stack_meta:get_string('quiver_id'))
|
||||
replace_item_meta:set_string('description', from_stack_meta:get_string('description'))
|
||||
|
||||
return replace_item
|
||||
end
|
||||
|
||||
---Gets arrow from quiver
|
||||
---@param player ObjectRef
|
||||
---@return {["found_arrow_stack"]: ItemStack|nil, ["quiver_id"]: string|nil, ["quiver_name"]: string|nil}
|
||||
function x_bows.quiver.get_itemstack_arrow_from_quiver(player)
|
||||
local player_inv = player:get_inventory()
|
||||
---@type ItemStack|nil
|
||||
local found_arrow_stack
|
||||
local prev_detached_inv_list = {}
|
||||
local quiver_id
|
||||
local quiver_name
|
||||
|
||||
---find matching quiver item in players inventory with the open formspec name
|
||||
if player_inv and player_inv:contains_item('main', 'x_bows:quiver') then
|
||||
local inv_list = player_inv:get_list('main')
|
||||
|
||||
for i, st in ipairs(inv_list) do
|
||||
if not st:is_empty() and st:get_name() == 'x_bows:quiver' then
|
||||
local st_meta = st:get_meta()
|
||||
local player_name = player:get_player_name()
|
||||
quiver_id = st_meta:get_string('quiver_id')
|
||||
|
||||
local detached_inv = x_bows.quiver.get_or_create_detached_inv(
|
||||
quiver_id,
|
||||
player_name,
|
||||
st_meta:get_string('quiver_items')
|
||||
)
|
||||
|
||||
if not detached_inv:is_empty('main') then
|
||||
local detached_inv_list = detached_inv:get_list('main')
|
||||
|
||||
---find arrows inside quiver inventory
|
||||
for j, qst in ipairs(detached_inv_list) do
|
||||
---save copy of inv list before we take the item
|
||||
table.insert(prev_detached_inv_list, detached_inv:get_stack('main', j))
|
||||
|
||||
if not qst:is_empty() and not found_arrow_stack then
|
||||
quiver_name = st:get_name()
|
||||
found_arrow_stack = qst:take_item()
|
||||
|
||||
if not x_bows.is_creative(player_name) then
|
||||
detached_inv:set_list('main', detached_inv_list)
|
||||
x_bows.quiver.save(detached_inv, player, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if found_arrow_stack then
|
||||
---show HUD - quiver inventory
|
||||
x_bows.quiver.udate_or_create_hud(player, prev_detached_inv_list)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
found_arrow_stack = found_arrow_stack,
|
||||
quiver_id = quiver_id,
|
||||
quiver_name = quiver_name
|
||||
}
|
||||
end
|
||||
|
||||
---Remove all added HUDs
|
||||
---@param player ObjectRef
|
||||
function x_bows.quiver.remove_hud(player)
|
||||
local player_name = player:get_player_name()
|
||||
|
||||
if x_bows.quiver.hud_item_ids[player_name] then
|
||||
for _, v in pairs(x_bows.quiver.hud_item_ids[player_name]) do
|
||||
if type(v) == 'table' then
|
||||
for _, v2 in pairs(v) do
|
||||
player:hud_remove(v2)
|
||||
end
|
||||
else
|
||||
player:hud_remove(v)
|
||||
end
|
||||
end
|
||||
|
||||
x_bows.quiver.hud_item_ids[player_name] = {
|
||||
arrow_inv_img = {},
|
||||
stack_count = {}
|
||||
}
|
||||
else
|
||||
x_bows.quiver.hud_item_ids[player_name] = {
|
||||
arrow_inv_img = {},
|
||||
stack_count = {}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
---Update or create HUD
|
||||
---@todo implement hud_change?
|
||||
---@param player ObjectRef
|
||||
---@param inv_list ItemStack[]
|
||||
---@return nil
|
||||
function x_bows.quiver.udate_or_create_hud(player, inv_list)
|
||||
local player_name = player:get_player_name()
|
||||
local selected_bg_added = false
|
||||
|
||||
if x_bows.quiver.after_job[player_name] then
|
||||
for _, v in pairs(x_bows.quiver.after_job[player_name]) do
|
||||
v:cancel()
|
||||
end
|
||||
|
||||
x_bows.quiver.after_job[player_name] = {}
|
||||
else
|
||||
x_bows.quiver.after_job[player_name] = {}
|
||||
end
|
||||
|
||||
x_bows.quiver.remove_hud(player)
|
||||
|
||||
---title image
|
||||
x_bows.quiver.hud_item_ids[player_name].title_image = player:hud_add({
|
||||
hud_elem_type = 'image',
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -120, y = -140},
|
||||
text = 'x_bows_quiver.png',
|
||||
scale = {x = 4, y = 4},
|
||||
alignment = 0,
|
||||
})
|
||||
|
||||
---title copy
|
||||
local quiver_def = minetest.registered_items['x_bows:quiver']
|
||||
x_bows.quiver.hud_item_ids[player_name].title_copy = player:hud_add({
|
||||
hud_elem_type = 'text',
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -120, y = -75},
|
||||
text = quiver_def.short_description,
|
||||
alignment = 0,
|
||||
scale = {x = 100, y = 30},
|
||||
number = 0xFFFFFF,
|
||||
})
|
||||
|
||||
---hotbar bg
|
||||
x_bows.quiver.hud_item_ids[player_name].hotbar_bg = player:hud_add({
|
||||
hud_elem_type = 'image',
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -238, y = 0},
|
||||
text = 'x_bows_quiver_hotbar.png',
|
||||
scale = {x = 1, y = 1},
|
||||
alignment = {x = 1, y = 0 },
|
||||
})
|
||||
|
||||
for j, qst in ipairs(inv_list) do
|
||||
if not qst:is_empty() then
|
||||
local found_arrow_stack_def = minetest.registered_items[qst:get_name()]
|
||||
|
||||
if not selected_bg_added then
|
||||
selected_bg_added = true
|
||||
|
||||
---ui selected bg
|
||||
x_bows.quiver.hud_item_ids[player_name].hotbar_selected = player:hud_add({
|
||||
hud_elem_type = 'image',
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -308 + (j * 74), y = 2},
|
||||
text = 'x_bows_quiver_hotbar_selected.png',
|
||||
scale = {x = 1, y = 1},
|
||||
alignment = {x = 1, y = 0 },
|
||||
})
|
||||
end
|
||||
|
||||
if found_arrow_stack_def then
|
||||
---arrow inventory image
|
||||
table.insert(x_bows.quiver.hud_item_ids[player_name].arrow_inv_img, player:hud_add({
|
||||
hud_elem_type = 'image',
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -300 + (j * 74), y = 0},
|
||||
text = found_arrow_stack_def.inventory_image,
|
||||
scale = {x = 4, y = 4},
|
||||
alignment = {x = 1, y = 0 },
|
||||
}))
|
||||
|
||||
---stack count
|
||||
table.insert(x_bows.quiver.hud_item_ids[player_name].stack_count, player:hud_add({
|
||||
hud_elem_type = 'text',
|
||||
position = {x = 1, y = 0.5},
|
||||
offset = {x = -244 + (j * 74), y = 23},
|
||||
text = qst:get_count(),
|
||||
alignment = -1,
|
||||
scale = {x = 50, y = 10},
|
||||
number = 0xFFFFFF,
|
||||
}))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---@param v_player ObjectRef
|
||||
table.insert(x_bows.quiver.after_job[player_name], minetest.after(10, function(v_player)
|
||||
x_bows.quiver.remove_hud(v_player)
|
||||
end, player))
|
||||
end
|
||||
|
||||
---Get existing detached inventory or create new one
|
||||
---@param quiver_id string
|
||||
---@param player_name string
|
||||
---@param quiver_items? string
|
||||
---@return InvRef
|
||||
function x_bows.quiver.get_or_create_detached_inv(quiver_id, player_name, quiver_items)
|
||||
local detached_inv
|
||||
|
||||
if quiver_id ~= '' then
|
||||
detached_inv = minetest.get_inventory({type='detached', name=quiver_id})
|
||||
end
|
||||
|
||||
if not detached_inv then
|
||||
detached_inv = minetest.create_detached_inventory(quiver_id, {
|
||||
---@param inv InvRef detached inventory
|
||||
---@param from_list string
|
||||
---@param from_index number
|
||||
---@param to_list string
|
||||
---@param to_index number
|
||||
---@param count number
|
||||
---@param player ObjectRef
|
||||
allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
if x_bows.quiver.quiver_can_allow(inv, player) then
|
||||
return count
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
---@param inv InvRef detached inventory
|
||||
---@param listname string listname of the inventory, e.g. `'main'`
|
||||
---@param index number
|
||||
---@param stack ItemStack
|
||||
---@param player ObjectRef
|
||||
allow_put = function(inv, listname, index, stack, player)
|
||||
if minetest.get_item_group(stack:get_name(), 'arrow') ~= 0 and x_bows.quiver.quiver_can_allow(inv, player) then
|
||||
return stack:get_count()
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
---@param inv InvRef detached inventory
|
||||
---@param listname string listname of the inventory, e.g. `'main'`
|
||||
---@param index number
|
||||
---@param stack ItemStack
|
||||
---@param player ObjectRef
|
||||
allow_take = function(inv, listname, index, stack, player)
|
||||
if minetest.get_item_group(stack:get_name(), 'arrow') ~= 0 and x_bows.quiver.quiver_can_allow(inv, player) then
|
||||
return stack:get_count()
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end,
|
||||
---@param inv InvRef detached inventory
|
||||
---@param from_list string
|
||||
---@param from_index number
|
||||
---@param to_list string
|
||||
---@param to_index number
|
||||
---@param count number
|
||||
---@param player ObjectRef
|
||||
on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
|
||||
x_bows.quiver.save(inv, player)
|
||||
end,
|
||||
---@param inv InvRef detached inventory
|
||||
---@param listname string listname of the inventory, e.g. `'main'`
|
||||
---@param index number index where was item put
|
||||
---@param stack ItemStack stack of item what was put
|
||||
---@param player ObjectRef
|
||||
on_put = function(inv, listname, index, stack, player)
|
||||
x_bows.quiver.save(inv, player)
|
||||
end,
|
||||
---@param inv InvRef detached inventory
|
||||
---@param listname string listname of the inventory, e.g. `'main'`
|
||||
---@param index number
|
||||
---@param stack ItemStack
|
||||
---@param player ObjectRef
|
||||
on_take = function(inv, listname, index, stack, player)
|
||||
x_bows.quiver.save(inv, player)
|
||||
end,
|
||||
}, player_name)
|
||||
|
||||
detached_inv:set_size('main', 3 * 1)
|
||||
end
|
||||
|
||||
---populate items in inventory
|
||||
if quiver_items and quiver_items ~= '' then
|
||||
x_bows.quiver.set_string_to_inv(detached_inv, quiver_items)
|
||||
end
|
||||
|
||||
return detached_inv
|
||||
end
|
||||
|
||||
---create UUID
|
||||
---@return string
|
||||
local function uuid()
|
||||
local template ='xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
|
||||
|
||||
---@diagnostic disable-next-line: redundant-return-value
|
||||
return string.gsub(template, '[xy]', function (c)
|
||||
local v = (c == 'x') and math.random(0, 0xf) or math.random(8, 0xb)
|
||||
return string.format('%x', v)
|
||||
end)
|
||||
end
|
||||
|
||||
---create formspec
|
||||
---@param name string name of the form
|
||||
---@return string
|
||||
local function get_formspec(name)
|
||||
local width = 3
|
||||
local height = 1
|
||||
local list_w = 8
|
||||
local list_pos_x = (list_w - width) / 2
|
||||
|
||||
local formspec =
|
||||
'size['..list_w..',6]' ..
|
||||
'list[detached:'..name..';main;'..list_pos_x..',0.3;'..width..',1;]'..
|
||||
'list[current_player;main;0,'..(height + 0.85)..';'..list_w..',1;]'..
|
||||
'list[current_player;main;0,'..(height + 2.08)..';'..list_w..',3;8]'..
|
||||
'listring[detached:'..name..';main]'..
|
||||
'listring[current_player;main]'..
|
||||
default.get_hotbar_bg(0, height + 0.85)
|
||||
|
||||
--update formspec
|
||||
local inv = minetest.get_inventory({type='detached', name=name})
|
||||
local invlist = inv:get_list(name)
|
||||
|
||||
---inventory slots overlay
|
||||
local px, py = list_pos_x, 0.3
|
||||
|
||||
for i = 1, 3 do
|
||||
if not invlist or invlist[i]:is_empty() then
|
||||
formspec = formspec ..
|
||||
'image[' .. px .. ',' .. py .. ';1,1;x_bows_arrow_slot.png]'
|
||||
end
|
||||
|
||||
px = px + 1
|
||||
end
|
||||
|
||||
return formspec
|
||||
end
|
||||
|
||||
---convert inventory of itemstacks to serialized string
|
||||
---@param inv InvRef
|
||||
---@return {['inv_string']: string, ['content_description']: string}
|
||||
local function get_string_from_inv(inv)
|
||||
local inv_list = inv:get_list('main')
|
||||
local t = {}
|
||||
local content_description = ''
|
||||
|
||||
for i, st in ipairs(inv_list) do
|
||||
if not st:is_empty() then
|
||||
table.insert(t, st:to_table())
|
||||
content_description = content_description .. '\n' ..st:get_short_description()..' '..st:get_count()
|
||||
else
|
||||
table.insert(t, {is_empty = true})
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
inv_string = minetest.serialize(t),
|
||||
content_description = content_description == '' and '\nEmpty' or content_description
|
||||
}
|
||||
end
|
||||
|
||||
---set items from serialized string to inventory
|
||||
---@param inv InvRef inventory to add items to
|
||||
---@param str string previously stringified inventory of itemstacks
|
||||
function x_bows.quiver.set_string_to_inv(inv, str)
|
||||
local t = minetest.deserialize(str)
|
||||
|
||||
for i, item in ipairs(t) do
|
||||
if not item.is_empty then
|
||||
inv:set_stack('main', i, ItemStack(item))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---save quiver inventory to itemstack meta
|
||||
---@param inv InvRef
|
||||
---@param player ObjectRef
|
||||
---@param quiver_is_closed? boolean default `false`
|
||||
function x_bows.quiver.save(inv, player, quiver_is_closed)
|
||||
local player_inv = player:get_inventory()
|
||||
local inv_loc = inv:get_location()
|
||||
local quiver_item_name = quiver_is_closed and 'x_bows:quiver' or 'x_bows:quiver_open'
|
||||
|
||||
---find matching quiver item in players inventory with the open formspec name
|
||||
if player_inv and player_inv:contains_item('main', quiver_item_name) then
|
||||
local inv_list = player_inv:get_list('main')
|
||||
|
||||
for i, st in ipairs(inv_list) do
|
||||
local st_meta = st:get_meta()
|
||||
|
||||
if not st:is_empty() and st:get_name() == quiver_item_name and st_meta:get_string('quiver_id') == inv_loc.name then
|
||||
---save inventory items in quiver item meta
|
||||
local string_from_inventory_result = get_string_from_inv(inv)
|
||||
|
||||
st_meta:set_string('quiver_items', string_from_inventory_result.inv_string)
|
||||
|
||||
---update description
|
||||
local new_description = st:get_short_description()..'\n'..string_from_inventory_result.content_description..'\n'
|
||||
|
||||
st_meta:set_string('description', new_description)
|
||||
player_inv:set_stack('main', i, st)
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
---check if we are allowing actions in the correct quiver inventory
|
||||
---@param inv InvRef
|
||||
---@param player ObjectRef
|
||||
---@return boolean
|
||||
function x_bows.quiver.quiver_can_allow(inv, player)
|
||||
local player_inv = player:get_inventory()
|
||||
local inv_loc = inv:get_location()
|
||||
|
||||
---find matching quiver item in players inventory with the open formspec name
|
||||
if player_inv and player_inv:contains_item('main', 'x_bows:quiver_open') then
|
||||
local inv_list = player_inv:get_list('main')
|
||||
|
||||
for i, st in ipairs(inv_list) do
|
||||
local st_meta = st:get_meta()
|
||||
|
||||
if not st:is_empty() and st:get_name() == 'x_bows:quiver_open' and st_meta:get_string('quiver_id') == inv_loc.name then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
---Open quiver
|
||||
---@param itemstack ItemStack
|
||||
---@param user ObjectRef
|
||||
---@return ItemStack
|
||||
function x_bows.open_quiver(itemstack, user)
|
||||
local itemstack_meta = itemstack:get_meta()
|
||||
local pname = user:get_player_name()
|
||||
local quiver_id = itemstack_meta:get_string('quiver_id')
|
||||
|
||||
---create inventory id and save it
|
||||
if quiver_id == '' then
|
||||
quiver_id = itemstack:get_name()..'_'..uuid()
|
||||
itemstack_meta:set_string('quiver_id', quiver_id)
|
||||
end
|
||||
|
||||
local quiver_items = itemstack_meta:get_string('quiver_items')
|
||||
|
||||
x_bows.quiver.get_or_create_detached_inv(quiver_id, pname, quiver_items)
|
||||
|
||||
---show open variation of quiver
|
||||
local replace_item = x_bows.quiver.get_replacement_item(itemstack, 'x_bows:quiver_open')
|
||||
|
||||
itemstack:replace(replace_item)
|
||||
|
||||
minetest.sound_play('x_bows_quiver', {
|
||||
to_player = user:get_player_name(),
|
||||
gain = 0.1
|
||||
})
|
||||
|
||||
minetest.show_formspec(pname, quiver_id, get_formspec(quiver_id))
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if player and fields.quit then
|
||||
x_bows.quiver.close_quiver(player, formname)
|
||||
XBowsQuiver:close_quiver(player, formname)
|
||||
end
|
||||
end)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 200 B |
Binary file not shown.
Before Width: | Height: | Size: 194 B |
|
@ -3,13 +3,13 @@
|
|||
---Entity definition
|
||||
---@class EntityDef
|
||||
---@field initial_properties ObjectProperties A table of object properties. The properties in this table are applied to the object once when it is spawned. `dtime_s` is the time passed since the object was unloaded, which can be used for updating the entity state.
|
||||
---@field on_activate fun(self: table, staticdata: string, dtime_s: integer|number) Function receive a "luaentity" table as `self`. Called when the object is instantiated.
|
||||
---@field on_activate fun(self: table, staticdata: string, dtime_s: integer|number): nil Function receive a "luaentity" table as `self`. Called when the object is instantiated.
|
||||
---@field on_deactivate fun(self: table, removal: boolean): nil Function receive a "luaentity" table as `self`. Called when the object is about to get removed or unloaded. `removal`: boolean indicating whether the object is about to get removed. Calling `object:remove()` on an active object will call this with `removal=true`. The mapblock the entity resides in being unloaded will call this with `removal=false`. Note that this won't be called if the object hasn't been activated in the first place. In particular, `minetest.clear_objects({mode = "full"})` won't call this, whereas `minetest.clear_objects({mode = "quick"})` might call this.
|
||||
---@field on_step fun(self: table, dtime: integer|number, moveresult?: table) Function receive a "luaentity" table as `self`. Called on every server tick, after movement and collision processing. `dtime`: elapsed time since last call. `moveresult`: table with collision info (only available if physical=true).
|
||||
---@field on_step fun(self: table, dtime: integer|number, moveresult?: table): nil Function receive a "luaentity" table as `self`. Called on every server tick, after movement and collision processing. `dtime`: elapsed time since last call. `moveresult`: table with collision info (only available if physical=true).
|
||||
---@field on_punch fun(self: table, puncher: ObjectRef|nil, time_from_last_punch: number|integer|nil, tool_capabilities: ToolCapabilitiesDef|nil, dir: Vector, damage: number|integer): boolean|nil Function receive a "luaentity" table as `self`. Called when somebody punches the object. Note that you probably want to handle most punches using the automatic armor group system. Can return `true` to prevent the default damage mechanism.
|
||||
---@field on_death fun(self: table, killer: ObjectRef|nil): nil Function receive a "luaentity" table as `self`. Called when the object dies.
|
||||
---@field on_rightclick fun(self: table, clicker: ObjectRef): nil Function receive a "luaentity" table as `self`. Called when `clicker` pressed the 'place/use' key while pointing to the object (not neccessarily an actual rightclick). `clicker`: an `ObjectRef` (may or may not be a player)
|
||||
---@field on_attach_child fun(self: table, child: ObjectRef): nil Function receive a "luaentity" table as `self`. `child`: an `ObjectRef` of the child that attaches
|
||||
---@field on_detach_child fun(self: table, child: ObjectRef): nil Function receive a "luaentity" table as `self`. `child`: an `ObjectRef` of the child that detaches
|
||||
---@field on_detach fun(self: table, parent: ObjectRef|nil) Function receive a "luaentity" table as `self`. `parent`: an `ObjectRef` (can be `nil`) from where it got detached. This happens before the parent object is removed from the world.
|
||||
---@field on_detach fun(self: table, parent: ObjectRef|nil): nil Function receive a "luaentity" table as `self`. `parent`: an `ObjectRef` (can be `nil`) from where it got detached. This happens before the parent object is removed from the world.
|
||||
---@field get_staticdata fun(self: table) Function receive a "luaentity" table as `self`. Should return a string that will be passed to `on_activate` when the object is instantiated the next time.
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
---@field sound ItemSoundDef
|
||||
---@field on_place fun(itemstack: ItemStack, placer: ObjectRef|nil, pointed_thing: PointedThingDef): ItemStack|nil When the 'place' key was pressed with the item in hand and a node was pointed at. Shall place item and return the leftover itemstack or nil to not modify the inventory. The placer may be any ObjectRef or nil. default: minetest.item_place
|
||||
---@field on_secondary_use fun(itemstack: ItemStack, user: ObjectRef|nil, pointed_thing: PointedThingDef): ItemStack|nil Same as on_place but called when not pointing at a node. Function must return either nil if inventory shall not be modified, or an itemstack to replace the original itemstack. The user may be any ObjectRef or nil. default: nil
|
||||
---@field on_drop fun(itemstack: ItemStack, dropper: ObjectRef|nil, pos: Vector): ItemStack Shall drop item and return the leftover itemstack. The dropper may be any ObjectRef or nil. default: minetest.item_drop
|
||||
---@field on_drop fun(itemstack: ItemStack, dropper: ObjectRef|nil, pos: Vector): ItemStack|nil Shall drop item and return the leftover itemstack. The dropper may be any ObjectRef or nil. default: minetest.item_drop
|
||||
---@field on_pickup fun(itemstack: ItemStack, picker: ObjectRef|nil, pointed_thing?: PointedThingDef, time_from_last_punch?: number|integer, rest?: any): ItemStack|nil Called when a dropped item is punched by a player. Shall pick-up the item and return the leftover itemstack or nil to not modify the dropped item. `rest` are other parameters from `luaentity:on_punch`. default: `minetest.item_pickup`
|
||||
---@field on_use fun(itemstack: ItemStack, user: ObjectRef|nil, pointed_thing: PointedThingDef): ItemStack|nil default: nil. When user pressed the 'punch/mine' key with the item in hand. Function must return either nil if inventory shall not be modified, or an itemstack to replace the original itemstack. e.g. itemstack:take_item(); return itemstack. Otherwise, the function is free to do what it wants. The user may be any ObjectRef or nil. The default functions handle regular use cases.
|
||||
---@field after_use fun(itemstack: ItemStack, user: ObjectRef|nil, node: NodeDef, digparams: DigParamsDef): ItemStack|nil default: nil. If defined, should return an itemstack and will be called instead of wearing out the item (if tool). If returns nil, does nothing.
|
||||
|
|
|
@ -67,6 +67,9 @@
|
|||
---@field show_formspec fun(playername: string, formname: string, formspec: string): nil `playername`: name of player to show formspec, `formname`: name passed to `on_player_receive_fields` callbacks. It should follow the `"modname:<whatever>"` naming convention. `formspec`: formspec to display
|
||||
---@field register_on_player_receive_fields fun(func: fun(player: ObjectRef, formname: string, fields: table)): nil Called when the server received input from `player` in a formspec with the given `formname`. Specifically, this is called on any of the following events: a button was pressed, Enter was pressed while the focus was on a text field, a checkbox was toggled, something was selected in a dropdown list, a different tab was selected, selection was changed in a textlist or table, an entry was double-clicked in a textlist or table, a scrollbar was moved, or the form was actively closed by the player.
|
||||
---@field get_inventory fun(location: {['"type"']: 'player'|'node'|'detached', ['"name"']: string|nil, ['"pos"']: Vector|nil}): InvRef
|
||||
---@field dir_to_wallmounted fun(dir: Vector): number Convert a vector to a wallmounted value, used for `paramtype2="wallmounted"`
|
||||
---@field item_place_node fun(itemstack: ItemStack, placer: ObjectRef, pointed_thing: PointedThingDef, param2?: , prevent_after_place?: boolean): Vector|nil Place item as a node, `param2` overrides `facedir` and wallmounted `param2`, `prevent_after_place`: if set to `true`, `after_place_node` is not called or the newly placed node to prevent a callback and placement loop. returns `itemstack, position`, `position`: the location the node was placed to. `nil` if nothing was placed.
|
||||
---@field unregister_item fun(name: string): nil Unregisters the item from the engine, and deletes the entry with key `name` from `minetest.registered_items` and from the associated item table according to its nature: `minetest.registered_nodes`, etc.
|
||||
|
||||
---Minetest settings
|
||||
---@class MinetestSettings
|
||||
|
|
|
@ -8,3 +8,4 @@
|
|||
---@field steps number How many steps the plant has to grow, until it can be harvested
|
||||
---@field minlight number Minimum light to grow
|
||||
---@field maxlight number Maximum light to grow
|
||||
---@field on_timer fun(pos: Vector, elapsed: number): boolean default: nil, called by NodeTimers, see minetest.get_node_timer and NodeTimerRef. elapsed is the total time passed since the timer was started. return true to run the timer for another cycle with the same timeout value.
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
---@field hud_remove fun(self: ObjectRef, id: number|integer): nil remove the HUD element of the specified id
|
||||
---@field hud_change fun(self: ObjectRef, id: number|integer, stat: string, value: any): nil change a value of a previously added HUD element. `stat` supports the same keys as in the hud definition table except for `"hud_elem_type"`.
|
||||
---@field set_wielded_item fun(self: ObjectRef, item: ItemStack): boolean replaces the wielded item, returns `true` if successful.
|
||||
---@field move_to fun(self: ObjectRef, pos: Vector, continuous?: boolean): nil Does an interpolated move for Lua entities for visually smooth transitions. If `continuous` is true, the Lua entity will not be moved to the current position before starting the interpolated move. For players this does the same as `set_pos`,`continuous` is ignored.
|
||||
---@field set_hp fun(self: ObjectRef, hp: number, reason: table): nil set number of health points See reason in register_on_player_hpchange Is limited to the range of 0 ... 65535 (2^16 - 1) For players: HP are also limited by `hp_max` specified in object properties
|
||||
|
||||
---Moving things in the game are generally these.
|
||||
---This is basically a reference to a C++ `ServerActiveObject`.
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
---https://github.com/sumneko/lua-language-server/wiki
|
||||
|
||||
---@alias tablelib tablelib|TableAbstract
|
||||
|
||||
---Table helpers
|
||||
---@class TableAbstract
|
||||
---@field copy fun(table: table): table returns a deep copy of `table`
|
|
@ -0,0 +1,139 @@
|
|||
---Base class XBows
|
||||
---@class XBows
|
||||
---@field pvp boolean
|
||||
---@field creative boolean
|
||||
---@field mesecons string|nil
|
||||
---@field playerphysics string|nil
|
||||
---@field player_monoids string|nil
|
||||
---@field registered_bows table<string, ItemDef|BowItemDefCustom>
|
||||
---@field registered_arrows table<string, ItemDef|ArrowItemDefCustom>
|
||||
---@field registered_quivers table<string, ItemDef|QuiverItemDefCustom>
|
||||
---@field registered_particle_spawners table<string, ParticlespawnerDef|ParticlespawnerDefCustom>
|
||||
---@field player_bow_sneak table<string, table<string, boolean>>
|
||||
---@field settings table
|
||||
---@field quiver table Quiver class
|
||||
---@field charge_sound_after_job table<string, JobTable>
|
||||
---@field is_allowed_ammunition fun(self: XBows, weapon_name: string, ammo_name: string): boolean Check if ammunition is allowed to charge this weapon
|
||||
---@field is_creative fun(self: XBows, name: string): boolean Check if creative is enabled or if player has creative priv
|
||||
---@field get_particle_effect_for_arrow fun(self: XBows, name: string, pos: Vector): number|boolean Get particle effect from registered spawners table
|
||||
---@field register_entity fun(self: EntityDef|XBows, name: string, def: XBowsEntityDef): nil Register new projectile entity
|
||||
---@field update_bow_allowed_ammunition fun(self: XBows, name: string, def: string[]): nil Updates `allowed_ammunition` definition on already registered item, so MODs can add new ammunitions to this list.
|
||||
---@field reset_charged_bow fun(self: XBows, player: ObjectRef, includeWielded?: boolean): nil Reset charged bow to uncharged bow, this will return the arrow item to the inventory also
|
||||
---@field register_bow fun(self: XBows, name: string, def: ItemDef | BowItemDefCustom, mod_override?: boolean): boolean|nil Register new bow/gun.
|
||||
---@field register_arrow fun(self: XBows, name: string, def: ItemDef | ArrowItemDefCustom): boolean|nil Register new arrow/projectile.
|
||||
---@field register_quiver fun(self: XBows, name: string, def: ItemDef | ArrowItemDefCustom): boolean|nil Register new quiver.
|
||||
---@field load fun(self: XBows, itemstack: ItemStack, user: ObjectRef, pointed_thing: PointedThingDef): ItemStack Load bow
|
||||
---@field shoot fun(self: XBows, itemstack: ItemStack, user: ObjectRef, pointed_thing?: PointedThingDef): ItemStack Shoot bow
|
||||
---@field register_particle_effect fun(self: XBows, name: string, def: ParticlespawnerDef|ParticlespawnerDefCustom): nil Add new particle to XBow registration
|
||||
---@field open_quiver fun(self: XBowsQuiver, itemstack: ItemStack, user: ObjectRef): ItemStack Open quiver
|
||||
|
||||
|
||||
---XBowsQuiver class extended from XBows
|
||||
---@alias XBowsQuiver XBowsQuiverBase|XBows
|
||||
---@class XBowsQuiverBase
|
||||
---@field hud_item_ids table
|
||||
---@field after_job table<string, JobTable>
|
||||
---@field udate_or_create_hud fun(self: XBowsQuiver, player: ObjectRef, inv_list: ItemStack[], idx?: number): nil Update or create quiver HUD
|
||||
---@field get_or_create_detached_inv fun(self: XBowsQuiver, quiver_id: string, player_name: string, quiver_items?: string): InvRef|unknown Get existing detached inventory or create new one
|
||||
---@field save fun(self: XBowsQuiver, inv: InvRef, player: ObjectRef, quiver_is_closed?: boolean): nil Save quiver inventory to itemstack meta
|
||||
---@field close_quiver fun(self: XBowsQuiver, player: ObjectRef, quiver_id?: string): nil Close one or all open quivers in players inventory
|
||||
---@field get_replacement_item fun(self: XBowsQuiver, from_stack: ItemStack, to_item_name: string): ItemStack Swap item in player inventory indicating open quiver. Preserve all ItemStack definition and meta.
|
||||
---@field get_itemstack_arrow_from_quiver fun(self: XBowsQuiver, player: ObjectRef, to_item_name: string): {["found_arrow_stack"]: ItemStack|nil, ["quiver_id"]: string|nil, ["quiver_name"]: string|nil, ["found_arrow_stack_idx"]: number} Gets arrow from quiver
|
||||
---@field remove_hud fun(self: XBowsQuiver, player: ObjectRef): nil Remove all added HUDs
|
||||
---@field get_formspec fun(self: XBowsQuiver, name: string): string Create formspec
|
||||
---@field get_string_from_inv fun(self: XBowsQuiver, inv: InvRef): {['inv_string']: string, ['content_description']: string} Convert inventory of itemstacks to serialized string
|
||||
---@field set_string_to_inv fun(self: XBowsQuiver, inv: InvRef, str: string): nil Set items from serialized string to inventory
|
||||
---@field quiver_can_allow fun(self: XBowsQuiver, inv: InvRef, player: ObjectRef): boolean Check if we are allowing actions in the correct quiver inventory
|
||||
|
||||
|
||||
---Custom field in ParticlespawnerDef
|
||||
---@class ParticlespawnerDefCustom
|
||||
---@field custom ParticlespawnerDefCustomAttr
|
||||
|
||||
---Custom field attributes in ParticlespawnerDef
|
||||
---@class ParticlespawnerDefCustomAttr
|
||||
---@field minpos Vector
|
||||
---@field maxpos Vector
|
||||
|
||||
---Custom field in ItemDef
|
||||
---@class BowItemDefCustom
|
||||
---@field custom BowItemDefCustomAttr
|
||||
|
||||
---Custom field attributes in ItemDef
|
||||
---@class BowItemDefCustomAttr
|
||||
---@field crit_chance number `crit_chance` 10% chance, 5 is 20% chance, (1 / crit_chance) * 100 = % chance
|
||||
---@field inventory_image_charged string
|
||||
---@field recipe table
|
||||
---@field fuel_burntime number
|
||||
---@field name_charged string
|
||||
---@field name string
|
||||
---@field mod_name string
|
||||
---@field uses number
|
||||
---@field strength number How strong is the bow. Defines how far the arrow will fly.
|
||||
---@field strength_min number|nil How strong is the bow. Defines how far the arrow will fly.
|
||||
---@field strength_max number|nil How strong is the bow. Defines how far the arrow will fly.
|
||||
---@field allowed_ammunition string[]|nil
|
||||
---@field wield_image_charged string|nil
|
||||
---@field acc_x_min number|nil
|
||||
---@field acc_y_min number|nil
|
||||
---@field acc_z_min number|nil
|
||||
---@field acc_x_max number|nil
|
||||
---@field acc_y_max number|nil
|
||||
---@field acc_z_max number|nil
|
||||
---@field sound_load string
|
||||
---@field sound_hit string
|
||||
---@field sound_shoot string
|
||||
---@field sound_shoot_crit string
|
||||
---@field gravity number
|
||||
|
||||
---Custom field in ItemDef
|
||||
---@class ArrowItemDefCustom
|
||||
---@field custom ArrowItemDefCustomAttr
|
||||
|
||||
---Custom field attributes in ItemDef
|
||||
---@class ArrowItemDefCustomAttr
|
||||
---@field tool_capabilities ToolCapabilitiesDef
|
||||
---@field craft_count number
|
||||
---@field recipe table
|
||||
---@field fuel_burntime number
|
||||
---@field name string
|
||||
---@field mod_name string
|
||||
---@field particle_effect string|nil
|
||||
---@field particle_effect_crit string|nil
|
||||
---@field particle_effect_fast string|nil
|
||||
---@field projectile_textures table|nil
|
||||
---@field projectile_visual_size table
|
||||
---@field projectile_entity string
|
||||
---@field on_hit_node fun(self: table, pointed_thing_ref: table)
|
||||
---@field on_hit_entity fun(self: table, pointed_thing_ref: table)
|
||||
---@field on_hit_player fun(self: table, pointed_thing_ref: table)
|
||||
---@field on_after_activate fun(self: table)
|
||||
|
||||
|
||||
---Custom field in ItemDef
|
||||
---@class QuiverItemDefCustom
|
||||
---@field custom QuiverItemDefCustomAttr
|
||||
|
||||
---Custom field attributes in ItemDef
|
||||
---@class QuiverItemDefCustomAttr
|
||||
---@field recipe table
|
||||
---@field recipe_count number
|
||||
---@field faster_arrows number
|
||||
---@field add_damage number
|
||||
---@field fuel_burntime number
|
||||
---@field inventory_image_open string
|
||||
---@field wield_image_open string
|
||||
---@field name string
|
||||
|
||||
|
||||
---Custom field in EntityDef
|
||||
---@alias XBowsEntityDef EntityDef|EntityDefCustom|XBows
|
||||
---@class EntityDefCustom
|
||||
---@field on_death fun(self: XBowsEntityDef, selfObj: table, killer: ObjectRef|nil): nil Function receive a "luaentity" table as `self`. Called when the object dies.
|
||||
---@field on_punch fun(self: XBowsEntityDef, selfObj: table, puncher: ObjectRef|nil, time_from_last_punch: number|integer|nil, tool_capabilities: ToolCapabilitiesDef|nil, dir: Vector, damage: number|integer): boolean|nil Function receive a "luaentity" table as `self`. Called when somebody punches the object. Note that you probably want to handle most punches using the automatic armor group system. Can return `true` to prevent the default damage mechanism.
|
||||
---@field _custom EntityDefCustomAttr
|
||||
|
||||
---@alias EntityDefCustomAttr EntityDefCustomAttrDef|EntityDef
|
||||
---@class EntityDefCustomAttrDef
|
||||
---@field name string
|
||||
---@field mod_name string
|
Ŝarĝante…
Reference in New Issue