Introduce classes and reusable OOP

This commit is contained in:
Juraj Vajda 2022-10-21 13:37:35 -04:00
parent bf833c4575
commit eee721d992
12 changed files with 89 additions and 191 deletions

View File

@ -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**

193
api.lua
View File

@ -24,7 +24,6 @@ XBows = {
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_bows = {},
@ -58,6 +57,25 @@ function XBows.is_creative(self, name)
return self.creative or minetest.check_player_privs(name, {creative = true})
end
function XBows.update_bow_allowed_ammunition(self, name, allowed_ammunition)
local _name = 'x_bows:'..name
local def = self.registered_bows[_name]
if not def then
return
end
local def_copy = table.copy(def)
minetest.unregister_item(_name)
for _, v in ipairs(allowed_ammunition) do
table.insert(def_copy.custom.allowed_ammunition, v)
end
self:register_bow(name, def_copy, 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`
@ -103,8 +121,9 @@ end
---Register bows
---@param name string
---@param def ItemDef | BowItemDefCustom
---@param override? boolean MOD everride
---@return boolean|nil
function XBows.register_bow(self, name, def)
function XBows.register_bow(self, name, def, override)
if name == nil or name == '' then
return false
end
@ -112,7 +131,8 @@ function XBows.register_bow(self, name, def)
local mod_name = def.custom.mod_name or 'x_bows'
def.custom.name = mod_name .. ':' .. name
def.custom.name_charged = mod_name .. ':' .. name .. '_charged'
def.description = def.description or name
def.short_description = def.short_description
def.description = override and def.short_description or (def.description or name)
def.custom.uses = def.custom.uses or 150
def.groups = mergeTables({bow = 1, flammable = 1}, def.groups or {})
def.custom.groups_charged = mergeTables({bow_charged = 1, flammable = 1, not_in_creative_inventory = 1}, def.groups or {})
@ -122,6 +142,7 @@ function XBows.register_bow(self, name, def)
def.custom.sound_hit = def.custom.sound_hit or 'x_bows_arrow_hit'
def.custom.sound_shoot = def.custom.sound_shoot or 'x_bows_bow_shoot'
def.custom.sound_shoot_crit = def.custom.sound_shoot_crit or 'x_bows_bow_shoot_crit'
def.custom.gravity = def.custom.gravity or -10
if def.custom.crit_chance then
def.description = def.description .. '\n' .. minetest.colorize('#00FF00', 'Critical Arrow Chance: '
@ -145,7 +166,7 @@ function XBows.register_bow(self, name, def)
self.registered_bows[def.custom.name_charged] = def
---not charged bow
minetest.register_tool(def.custom.name, {
minetest.register_tool(override and ':' .. def.custom.name or def.custom.name, {
description = def.description,
inventory_image = def.inventory_image or 'x_bows_bow_wood.png',
wield_image = def.wield_image or def.inventory_image,
@ -171,7 +192,7 @@ function XBows.register_bow(self, name, def)
})
---charged bow
minetest.register_tool(def.custom.name_charged, {
minetest.register_tool(override and ':' .. def.custom.name_charged or def.custom.name_charged, {
description = def.description,
inventory_image = def.custom.inventory_image_charged or 'x_bows_bow_wood_charged.png',
wield_image = def.custom.wield_image_charged or def.custom.inventory_image_charged,
@ -249,6 +270,9 @@ function XBows.register_arrow(self, name, def)
def.custom.projectile_visual_size = def.custom.projectile_visual_size or {x = 1, y = 1, z = 1}
def.custom.projectile_entity = def.custom.projectile_entity or 'x_bows:arrow_entity'
def.custom.on_hit_node = def.custom.on_hit_node or nil
def.custom.on_hit_entity = def.custom.on_hit_entity or nil
def.custom.on_hit_player = def.custom.on_hit_player or nil
def.custom.on_after_activate = def.custom.on_after_activate or nil
self.registered_arrows[def.custom.name] = def
@ -546,6 +570,7 @@ function XBows.shoot(self, itemstack, user, pointed_thing)
local acc_x_max = x_bows_registered_bow_charged_def.custom.acc_x_max
local acc_y_max = x_bows_registered_bow_charged_def.custom.acc_y_max
local acc_z_max = x_bows_registered_bow_charged_def.custom.acc_z_max
local gravity = x_bows_registered_bow_charged_def.custom.gravity
---Arrow
local projectile_entity = x_bows_registered_arrow_def.custom.projectile_entity
---Quiver
@ -623,7 +648,7 @@ function XBows.shoot(self, itemstack, user, pointed_thing)
---acceleration
local acc_x = dir.x
local acc_y = -10
local acc_y = gravity
local acc_z = dir.z
if acc_x_min and acc_x_max then
@ -778,10 +803,10 @@ function XBowsEntityDef.on_activate(self, selfObj, staticdata)
selfObj._nodechecktimer = 0.5
selfObj._is_drowning = false
selfObj._in_liquid = false
selfObj._poison_arrow = false
selfObj._shot_from_pos = selfObj.object:get_pos()
selfObj._arrow_name = _staticdata._arrow_name
selfObj._bow_name = _staticdata._bow_name
selfObj._user_name = _staticdata.user_name
selfObj.user = minetest.get_player_by_name(_staticdata.user_name)
selfObj._tflp = _staticdata._tflp
selfObj._tool_capabilities = _staticdata._tool_capabilities
@ -798,16 +823,20 @@ function XBowsEntityDef.on_activate(self, selfObj, staticdata)
selfObj._projectile_textures = x_bows_registered_arrow_def.custom.projectile_textures
selfObj._projectile_visual_size = x_bows_registered_arrow_def.custom.projectile_visual_size
selfObj._sound_hit = x_bows_registered_bow_def.custom.sound_hit
if selfObj._arrow_name == 'x_bows:arrow_diamond_tipped_poison' then
selfObj._poison_arrow = true
end
selfObj._caused_damage = 0
selfObj._caused_knockback = 0
selfObj.object:set_properties({
textures = selfObj._projectile_textures,
infotext = selfObj._arrow_name,
visual_size = selfObj._projectile_visual_size
})
local on_after_activate_callback = x_bows_registered_arrow_def.custom.on_after_activate
if on_after_activate_callback then
on_after_activate_callback(selfObj)
end
end
function XBowsEntityDef.on_death(self, selfObj, killer)
@ -907,6 +936,7 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
)
)
and selfObj.object:get_attach() == nil
and not selfObj._attached
then
if pointed_thing.ref:is_player() then
minetest.sound_play('x_bows_arrow_successful_hit', {
@ -988,6 +1018,9 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
}
)
selfObj._caused_damage = _damage
selfObj._caused_knockback = knockback
-- already dead (entity)
if not pointed_thing.ref:get_luaentity() and not pointed_thing.ref:is_player() then
selfObj.object:remove()
@ -996,10 +1029,6 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
-- already dead (player)
if pointed_thing.ref:get_hp() <= 0 then
if XBows.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
selfObj.object:remove()
return
end
@ -1076,42 +1105,6 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
position.z = zmin / 10
end
-- poison arrow
if selfObj._poison_arrow then
local old_damage_texture_modifier = pointed_thing.ref:get_properties().damage_texture_modifier
local punch_def = {}
punch_def.puncher = selfObj.object
punch_def.time_from_last_punch = selfObj._tflp
punch_def.tool_capabilities = {
full_punch_interval = selfObj._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 XBows.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
XBows:poison_effect(1, 5, 0, selfObj, pointed_thing.ref, old_damage_texture_modifier, punch_def)
else
-- local lua_ent = pointed_thing.ref:get_luaentity()
-- if not lua_ent[selfObj.arrow .. '_active'] or lua_ent[selfObj.arrow .. '_active'] == 'false' then
-- lua_ent[selfObj.arrow .. '_active'] = true
XBows:poison_effect(1, 5, 0, selfObj, pointed_thing.ref, old_damage_texture_modifier, punch_def)
-- end
end
end
if not XBows.settings.x_bows_attach_arrows_to_entities and not pointed_thing.ref:is_player() then
selfObj.object:remove()
return
@ -1131,9 +1124,10 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
-- remove last arrow when too many already attached
local children = {}
local projectile_entity = self.registered_arrows[selfObj._arrow_name].custom.projectile_entity
for _, object in ipairs(pointed_thing.ref:get_children()) do
if object:get_luaentity() and object:get_luaentity().name == 'x_bows:arrow_entity' then
if object:get_luaentity() and object:get_luaentity().name == projectile_entity then
table.insert(children, object)
end
end
@ -1142,6 +1136,20 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
children[1]:remove()
end
if pointed_thing.ref:is_player() then
local on_hit_player_callback = self.registered_arrows[selfObj._arrow_name].custom.on_hit_player
if on_hit_player_callback then
on_hit_player_callback(selfObj, pointed_thing)
end
else
local on_hit_entity_callback = self.registered_arrows[selfObj._arrow_name].custom.on_hit_entity
if on_hit_entity_callback then
on_hit_entity_callback(selfObj, pointed_thing)
end
end
return
elseif pointed_thing.type == 'node' and not selfObj._attached then
@ -1195,9 +1203,10 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
-- remove last arrow when too many already attached
local children = {}
local projectile_entity = self.registered_arrows[selfObj._arrow_name].custom.projectile_entity
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
if not object:is_player() and object:get_luaentity() and object:get_luaentity().name == projectile_entity then
table.insert(children, object)
end
end
@ -1209,7 +1218,7 @@ function XBowsEntityDef.on_step(self, selfObj, dtime)
local on_hit_node_callback = self.registered_arrows[selfObj._arrow_name].custom.on_hit_node
if on_hit_node_callback then
on_hit_node_callback(selfObj)
on_hit_node_callback(selfObj, pointed_thing)
end
minetest.sound_play(selfObj._sound_hit, {
@ -1287,82 +1296,6 @@ function XBows.register_entity(self, name, def)
})
end
----
--- ARROW API
----
---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 XBows.poison_effect(self, 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,
self.poison_effect,
tick,
time,
time_left,
arrow_obj,
target_obj,
old_damage_texture_modifier,
punch_def
)
elseif target_obj:is_player() then
if self.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
self:get_particle_effect_for_arrow('arrow_tipped', target_obj_pos)
end
end
end
----
--- QUIVER API
----

View File

@ -19,6 +19,9 @@ dofile(path .. '/arrow.lua')
dofile(path .. '/items.lua')
dofile(path .. '/quiver.lua')
---backwards compatibility
minetest.register_alias('x_bows:arrow_diamond_tipped_poison', 'x_bows:arrow_diamond')
minetest.register_on_joinplayer(function(player)
XBows:reset_charged_bow(player, true)
XBowsQuiver:close_quiver(player)

View File

@ -1,5 +1,6 @@
XBows:register_bow('bow_wood', {
description = 'Wooden Bow',
short_description = 'Wooden Bow',
custom = {
uses = 385,
crit_chance = 10,
@ -15,8 +16,7 @@ XBows:register_bow('bow_wood', {
'x_bows:arrow_bronze',
'x_bows:arrow_steel',
'x_bows:arrow_mese',
'x_bows:arrow_diamond',
'x_bows:arrow_diamond_tipped_poison'
'x_bows:arrow_diamond'
}
}
})
@ -124,28 +124,12 @@ XBows:register_arrow('arrow_diamond', {
}
})
XBows:register_arrow('arrow_diamond_tipped_poison', {
description = 'Arrow Diamond Tipped Poison (0:05)',
inventory_image = 'x_bows_arrow_diamond_poison.png',
custom = {
recipe = {
{'', '', ''},
{'', 'default:marram_grass_1', ''},
{'', 'x_bows:arrow_diamond', ''}
},
tool_capabilities = {
full_punch_interval = 0.7,
max_drop_level = 1,
damage_groups = {fleshy=8}
},
recipe_count = 1
}
})
XBows:register_quiver('quiver', {
description = 'Quiver \n\n Empty\n',
short_description = 'Quiver',
custom = {
description = 'Quiver \n\n Empty\n',
short_description = 'Quiver',
recipe = {
{'group:arrow', 'group:arrow', 'group:arrow'},
{'group:arrow', 'wool:brown', 'group:arrow'},
@ -154,15 +138,6 @@ XBows:register_quiver('quiver', {
recipe_count = 1,
faster_arrows = 5,
add_damage = 2,
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:arrow_diamond_tipped_poison'
}
fuel_burntime = 3
}
})

View File

@ -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

View File

@ -62,28 +62,3 @@ XBows:register_particle_effect('bubble', {
maxsize = 1,
texture = 'x_bows_bubble.png'
})
XBows:register_particle_effect('arrow_tipped', {
amount = 5,
time = 1,
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,
custom = {
minpos = {x = -0.5, y = -0.5, z = -0.5},
maxpos = {x = 0.5, y = 0.5, z = 0.5}
}
})

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 194 B

View File

@ -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

View File

@ -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`.

7
types/table.type.lua Normal file
View File

@ -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`

View File

@ -3,7 +3,6 @@
---@field pvp boolean
---@field creative boolean
---@field mesecons string|nil
---@field hbhunger string|nil
---@field playerphysics string|nil
---@field player_monoids string|nil
---@field registered_bows table<string, ItemDef|BowItemDefCustom>
@ -69,6 +68,7 @@
---@field sound_hit string
---@field sound_shoot string
---@field sound_shoot_crit string
---@field gravity number
---Custom field in ItemDef
---@class ArrowItemDefCustom
@ -88,6 +88,10 @@
---@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