master
Juraj Vajda 2021-03-13 21:39:42 -05:00
parent 28a3bb0661
commit cddefb27c0
46 changed files with 1027 additions and 612 deletions

210
README.md
View File

@ -1,198 +1,12 @@
# RCBOWS
## function rcbows.register_arrow(name, def)
Example:
```
rcbows.register_arrow("farbows:e_arrow", {
damage = 5,
inventory_arrow = {
name = "farbows:inv_arrow",
description = S("Arrow"),
inventory_image = "farbows_arrow.png",
stack_max = 64, --optional, 99 by default
}
sounds = {
max_hear_distance = 10,
gain = 0.4,
},
})
```
## function rcbows.register_bow(name, def)
Example:
```
rcbows.register_bow("farbows:bow_wood", {
description = S("Wooden Bow"),
image = "farbows_bow_wood.png",
strength = 30,
uses = 150,
charge_time = 0.5,
recipe = {
{"", "group:wood", "farming:string"},
{"group:wood", "", "farming:string"},
{"", "group:wood", "farming:string"},
},
base_texture = "farbows_base_bow_wood.png",
overlay_empty = "farbows_overlay_empty.png",
overlay_charged = "farbows_overlay_charged.png",
arrows = "farbows:e_arrow",
sounds = {
max_hear_distance = 10,
gain = 0.4,
}
})
```
### Arrows
You can define "arrows" as a single arrow (string) or a table of arrows.
In this case the order matters. The first ones have preference over the last ones when charging the bow.
I.e:
```
arrows = {"farbows:e_arrow", ""farbows:ice_arrow""},
```
### Viewfinder
You can define a viewfinder for a bow. This produces a zoom effect.
```
viewfinder = {
zoom = 15, --level of zoom; by default 15.
texture = "" --optional
}
```
- When the bow charged, toogle the viewfinder with the secondary use (right-click).
- You can define an optional texture to being showed. If you define texture as empty (""), you get the default rcbows viewfinder texture.
## Audio
1. If you define ``sounds={}``, you get the default sounds.
For no sound at all do not declare 'sounds'.
Also you can set the sound parameters 'max_hear_distance' and 'gain'.
In example:
```
sounds = {
max_hear_distance = 10,
gain = 0.4,
}
```
2. You also can define your own soundfiles.
You can set "soundfile_draw_bow" and/or "soundfile_fire_arrow" for bows, and "soundfile_hit_arrow" for arrows.
In example for a Bow:
```
sounds = {
soundfile_draw_bow = "my_draw_bow"
soundfile_fire_arrow = "my_fire_arrow"
max_hear_distance = 5,
--set the gain by default (0.5)
}
```
In example for a Arrow:
```
sounds = {
soundfile_hit_arrow = "my_hit_arrow"
max_hear_distance = 5,
--set the gain by default (0.5)
}
```
## Drop
By default the arrow drops the inventory_arrow when reachs a solid node.
If you want to define another item to drop, define it with 'drop':
```
rcbows.register_arrow("farbows:e_arrow", {
damage = 5,
inventory_arrow = {
name = "farbows:inv_arrow",
description = S("Arrow"),
inventory_image = "farbows_arrow.png",
}
drop = "farbows_drop_arrow"
})
```
If you want not any drop at all, add:
```
drop = "",
```
## Arrow Effects
You can define some arrow effects
### replace_node
Replace the hit node for this one.
### trail_particle
Particle texture to create an arrow trail.
It can be a string with "texture" only, or a table for animated textures: {texture = "texture", animation = "animation"}.
### explosion
It requires "tnt" or "explosion" mods as an optional dependency.
It is a table in where to define:
- mod = "tnt" or "explosions",
- radius
- damage = It is "damage_radius" for the "tnt" mod or "strength" for "explosions"
In example:
```
rcbows.register_arrow("farbows:fire_arrow", {
damage = 7,
inventory_arrow = {
name = "farbows:inv_fire_arrow",
description = S("Fire Arrow"),
inventory_image = "farbows_arrow_fire.png",
},
drop = "farbows:inv_arrow",
effects = {
replace_node = "fire:basic_flame",
trail_particle = "farbows_particle_fire.png",
explosion = {
mod = "tnt",
radius= 10,
damage = 1,
}
}
})
```
### water
An effect that extinguishes the flames.
It requires "fire" mod as an optional dependency.
It is a table in where to define:
- flame_node = The name of the flame node to extinguish
- radius
- particles = A water particles effect [optional]
```
rcbows.register_arrow("farbows:water_arrow", {
projectile_texture = "farbows_water_arrow",
damage = 2,
inventory_arrow = {
name = "farbows:inv_water_arrow",
description = S("Water Arrow"),
inventory_image = "farbows_arrow_water.png",
},
drop = "bucket:bucket_empty",
effects = {
trail_particle = "default_water.png",
water = {
radius = 5,
flame_node = "fire:basic_flame",
particles = true,
},
}
})
```
# x_bows
## TODO
- critical hit one shot kill will not remove the arrow
- change poison damage texture overlay
- add poison particles to target
- add player to poison damage
- charged bow should not hit and shoot at the same time when pointing on node/object
- caused by non charged bow after switching itemstack form charged to not charged bow
- add vertical knockback to poison punch?
- arrow too large when attached to entitites with visual_scale

511
arrow.lua Normal file
View File

@ -0,0 +1,511 @@
-- Gets total armor level from 3d armor
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
local function limit(x, min, max)
return math.min(math.max(x, min), max)
end
-- Gets `ObjectRef` collision box
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
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',
visual_size = {x = 0.2, y = 0.2, z = 0.3},
collisionbox = {0, 0, 0, 0, 0, 0},
selectionbox = {0, 0, 0, 0, 0, 0},
physical = false,
textures = {'air'},
hp_max = 0.5
},
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,
ref = 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
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,
on_death = function(self, killer)
minetest.item_drop(ItemStack(self.arrow), nil, vector.round(self._old_pos))
end,
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')
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 = self._attached_to.ref
self._nodechecktimer = 0.5
if node.name == 'air' then
local pos = self._attached_to.pos
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._attached_to.ref = 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
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
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
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
self._attached_to.ref = pointed_thing.ref
local children = pointed_thing.ref:get_children()
-- remove last arrow when too many already attached
if #children >= 5 then
children[1]:remove()
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
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._attached_to.ref = node_def
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 k, 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,
})

726
init.lua
View File

@ -1,449 +1,357 @@
rcbows = {}
local mod_start_time = minetest.get_us_time()
local bow_charged_timer = 0
local S = minetest.get_translator(minetest.get_current_modname())
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'),
registered_arrows = {},
registered_bows = {},
player_bow_sneak = {}
}
--CONSTANTS
local DEFAULT_MAX_HEAR_DISTANCE = 10
local DEFAULT_GAIN = 0.5
function rcbows.spawn_arrow(user, strength, itemstack)
local pos = user:get_pos()
pos.y = pos.y + 1.5 -- camera offset
local dir = user:get_look_dir()
local yaw = user:get_look_horizontal()
local meta = itemstack:get_meta()
local arrow = meta:get_string("rcbows:charged_arrow")
local obj = nil
if pos and arrow then
obj = minetest.add_entity(pos, arrow)
end
if not obj then
return
end
local lua_ent = obj:get_luaentity()
lua_ent.shooter_name = user:get_player_name()
obj:set_yaw(yaw - 0.5 * math.pi)
local velocity = vector.multiply(dir, strength)
obj:set_velocity(velocity)
return true
function x_bows.is_creative(name)
return x_bows.creative or minetest.check_player_privs(name, {creative = true})
end
function rcbows.launch_arrow(user, name, def, itemstack)
if not rcbows.spawn_arrow(user, def.strength, itemstack) then --throw arrow (spawn arrow entity)
return -- something failed
end
if def.sounds then
local user_pos = user:get_pos()
if not def.sounds.soundfile_fire_arrow then
def.sounds.soundfile_fire_arrow = "rcbows_fire_arrow"
end
rcbows.make_sound("pos", user_pos, def.sounds.soundfile_fire_arrow, DEFAULT_GAIN, DEFAULT_MAX_HEAR_DISTANCE)
end
itemstack:set_name(name)
itemstack:set_wear(itemstack:get_wear() + 0x10000 / def.uses)
if def.viewfinder then --reset the viewfinder-zoom
if not(user:get_fov() == 0) then
user:set_fov(0)
remove_viewfinder(user, itemstack:get_meta():get_int( "rcbows:viewfinder_id"))
end
end
return itemstack
end
local function show_viewfinder(player, texture)
local hud_id = player:hud_add({
hud_elem_type = "image",
text = texture,
position = {x=0, y=0},
scale = {x=-100, y=-100},
alignment = {x=1, y=1},
offset = {x=0, y=0}
})
return hud_id
end
function remove_viewfinder(player, hud_id)
if hud_id then
player:hud_remove(hud_id)
end
end
function rcbows.register_bow(name, def)
assert(type(def.description) == "string")
assert(type(def.image) == "string")
assert(type(def.strength) == "number")
assert(def.uses > 0)
local function reload_bow(itemstack, user, pointed_thing)
local inv = user:get_inventory()
local arrow, inventory_arrows, inventory_arrow, inv_arrow_name
local inv_list = inv:get_list("main")
if type(def.arrows) == 'table' then --more than one arrow?
for i = 1, #def.arrows do
arrow = def.arrows[i]
inv_arrow_name = minetest.registered_entities[arrow].inventory_arrow_name
if inv:contains_item("main", inv_arrow_name) then
if not inventory_arrows then
inventory_arrows = {}
end
inventory_arrows[#inventory_arrows+1] = {arrow = arrow, inv_arrow_name = inv_arrow_name}
end
end
else
arrow = def.arrows
inventory_arrow = minetest.registered_entities[def.arrows].inventory_arrow_name
end
if not inventory_arrow and not inventory_arrows then
return
end
if inventory_arrows then --more than one arrow?
for i = 1, #inv_list do
if inventory_arrow then
break
end
for j = 1, #inventory_arrows do
local inv_arrow = inventory_arrows[j]
if inv_list[i]:get_name() == inv_arrow.inv_arrow_name then
arrow = inv_arrow.arrow
inventory_arrow = inv_arrow.inv_arrow_name
break
end
end
end
if not inventory_arrow then
return
end
end
if not inv:remove_item("main", inventory_arrow):is_empty() then
minetest.after(def.charge_time or 0, function(v_user, v_name)
local wielded_item = v_user:get_wielded_item()
local wielded_item_name = wielded_item:get_name()
if wielded_item_name == v_name then
local meta = wielded_item:get_meta()
meta:set_string("rcbows:charged_arrow", arrow) --save the arrow in the meta
wielded_item:set_name(v_name .. "_charged")
v_user:set_wielded_item(wielded_item)
end
end, user, name)
if def.sounds then
local user_pos = user:get_pos()
if not def.sounds.soundfile_draw_bow then
def.sounds.soundfile_draw_bow = "rcbows_draw_bow"
end
rcbows.make_sound("pos", user_pos, def.sounds.soundfile_draw_bow, DEFAULT_GAIN, DEFAULT_MAX_HEAR_DISTANCE)
end
return itemstack
end
function x_bows.register_bow(name, def)
if name == nil or name == '' then
return false
end
minetest.register_tool(name, {
description = def.description .. " ".. S("(place to reload)"),
inventory_image = def.image .. "^" .. def.overlay_empty,
def.name = 'x_bows:' .. name
def.name_charged = 'x_bows:' .. name .. '_charged'
def.description = def.description or name
def.uses = def.uses or 150
on_use = function() end,
on_place = reload_bow,
on_secondary_use = reload_bow,
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_use = function(itemstack, user, pointed_thing)
-- end,
on_place = x_bows.load,
on_secondary_use = x_bows.load,
groups = {bow = 1, flammable = 1},
-- range = 0
})
-- 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},
})
-- recipes
if def.recipe then
minetest.register_craft({
output = name,
output = def.name,
recipe = def.recipe
})
end
local charged_name = name .. "_charged"
minetest.register_tool(charged_name, {
description = def.description .. " " .. S("(use to fire)"),
inventory_image = def.base_texture .. "^" ..def.overlay_charged,
groups = {not_in_creative_inventory=1},
on_use = function(itemstack, user, pointed_thing)
return rcbows.launch_arrow(user, name, def, itemstack)
end,
on_secondary_use = function(itemstack, user, pointed_thing) --viewfinder
if not def.viewfinder then
return
end
if user:get_fov() == 0 then
user:set_fov(def.viewfinder.zoom or 15)
if def.viewfinder.texture then
local viewfinder_texture = def.viewfinder.texture
if viewfinder_texture == "" then
viewfinder_texture = "rcbows_viewfinder.png"
end
itemstack:get_meta():set_int("rcbows:viewfinder_id", show_viewfinder(user, viewfinder_texture))
end
else
user:set_fov(0)
if def.viewfinder.texture then
remove_viewfinder(user, itemstack:get_meta():get_int( "rcbows:viewfinder_id"))
end
end
return itemstack
end
})
end
function rcbows.register_arrow(name, def)
minetest.register_entity(name, {
hp_max = 4, -- possible to catch the arrow (pro skills)
physical = false, -- use Raycast
collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
visual = "wielditem",
textures = {def.inventory_arrow.name},
visual_size = {x = 0.2, y = 0.15},
old_pos = nil,
velocity = nil,
liquidflag = nil,
shooter_name = "",
waiting_for_removal = false,
inventory_arrow_name = def.inventory_arrow.name,
groups = {arrow = 1},
function x_bows.register_arrow(name, def)
if name == nil or name == '' then
return false
end
on_activate = function(self)
self.object:set_acceleration({x = 0, y = -9.81, z = 0})
end,
def.name = 'x_bows:' .. name
def.description = def.description or name
on_step = function(self, dtime)
if self.waiting_for_removal then
self.object:remove()
return
end
local pos = self.object:get_pos()
self.old_pos = self.old_pos or pos
local velocity = self.object:get_velocity()
if def.sounds and not(def.sounds.soundfile_hit_arrow) then
def.sounds.soundfile_hit_arrow = "rcbows_hit_arrow"
end
local cast = minetest.raycast(self.old_pos, pos, true, true)
local thing = cast:next()
while thing do
if thing.type == "object" and thing.ref ~= self.object then
if not thing.ref:is_player() or thing.ref:get_player_name() ~= self.shooter_name then
thing.ref:punch(self.object, 1.0, {
full_punch_interval = 0.5,
damage_groups = {fleshy = def.damage or 1}
})
self.waiting_for_removal = true
self.object:remove()
if def.sounds then
local thing_pos = thing.ref:get_pos()
if thing_pos then
rcbows.make_sound("pos", thing_pos, def.sounds.soundfile_hit_arrow, DEFAULT_GAIN, DEFAULT_MAX_HEAR_DISTANCE)
end
end
x_bows.registered_arrows[def.name] = def
-- no effects or not owner, nothing to do.
-- some effects should also happen if hitting an other object. like tnt, water etc.
if not def.effects or minetest.is_protected(pos, self.shooter_name) then
return
end
rcbows.boom_effect(def, pos) -- BOOM
rcbows.water_effect(def, pos) -- water - extinguish fires
return
end
elseif thing.type == "node" then
local node_name = minetest.get_node(thing.under).name
local drawtype = minetest.registered_nodes[node_name]["drawtype"]
if drawtype == 'liquid' then
if not self.liquidflag then
self.velocity = velocity
self.liquidflag = true
local liquidviscosity = minetest.registered_nodes[node_name]["liquid_viscosity"]
local drag = 1/(liquidviscosity*6)
self.object:set_velocity(vector.multiply(velocity, drag))
self.object:set_acceleration({x = 0, y = -1.0, z = 0})
rcbows.splash(self.old_pos, "rcbows_bubble.png")
end
elseif self.liquidflag then
self.liquidflag = 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 minetest.registered_items[node_name].walkable then
if not(def.drop) then
minetest.item_drop(ItemStack(def.inventory_arrow), nil, vector.round(self.old_pos))
else
if not(def.drop == "") then
minetest.item_drop(ItemStack(def.drop), nil, vector.round(self.old_pos))
end
end
self.waiting_for_removal = true
self.object:remove()
if def.sounds then
if pos then
rcbows.make_sound("pos", pos, def.sounds.soundfile_hit_arrow, DEFAULT_GAIN, DEFAULT_MAX_HEAR_DISTANCE)
end
end
-- no effects or not owner, nothing to do.
if not def.effects or minetest.is_protected(pos, self.shooter_name) then
return
end
--replace node
if def.effects.replace_node then
minetest.set_node(pos, {name = def.effects.replace_node})
end
rcbows.boom_effect(def, pos) -- BOOM
rcbows.water_effect(def, pos) -- water - extinguish fires
return
end
end
thing = cast:next()
end
if def.effects and def.effects.trail_particle then
rcbows.trail(self.old_pos, pos, def.effects.trail_particle)
end
self.old_pos = pos
end,
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'),
inventory_image = def.inventory_image,
groups = {arrow = 1, flammable = 1}
})
minetest.register_craftitem(def.inventory_arrow.name, {
description = def.inventory_arrow.description,
inventory_image = def.inventory_arrow.inventory_image,
stack_max = def.stack_max or 99,
})
end
--SOUND SYSTEM
function rcbows.make_sound(dest_type, dest, soundfile, gain, max_hear_distance)
if dest_type == "object" then
minetest.sound_play(soundfile, {object = dest, gain = gain or DEFAULT_GAIN, max_hear_distance = max_hear_distance or DEFAULT_MAX_HEAR_DISTANCE,})
elseif dest_type == "player" then
local player_name = dest:get_player_name()
minetest.sound_play(soundfile, {to_player = player_name, gain = gain or DEFAULT_GAIN, max_hear_distance = max_hear_distance or DEFAULT_MAX_HEAR_DISTANCE,})
elseif dest_type == "pos" then
minetest.sound_play(soundfile, {pos = dest, gain = gain or DEFAULT_GAIN, max_hear_distance = max_hear_distance or DEFAULT_MAX_HEAR_DISTANCE,})
-- recipes
if def.craft then
minetest.register_craft({
output = def.name ..' ' .. (def.craft_count or 4),
recipe = def.craft
})
end
end
--ARROW EFFECTS
function x_bows.load(itemstack, user, pointed_thing)
local time_load = minetest.get_us_time()
local inv = user:get_inventory()
local inv_list = inv:get_list('main')
local bow_name = itemstack:get_name()
local bow_def = x_bows.registered_bows[bow_name .. '_charged']
local itemstack_arrows = {}
function rcbows.boom_effect(def, pos)
if def.effects.explosion and def.effects.explosion.mod then
local mod_name = def.effects.explosion.mod
if minetest.get_modpath(mod_name) ~= nil then
if mod_name == "tnt" then
tnt.boom(pos, {radius = def.effects.explosion.radius, damage_radius = def.effects.explosion.damage})
elseif mod_name == "explosions" then
explosions.explode(pos, {radius = def.effects.explosion.radius, strength = def.effects.explosion.damage})
end
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
node_def.on_rightclick(pointed_thing.under, node, user, itemstack, pointed_thing)
return
end
end
end
function rcbows.water_effect(def, pos)
if def.effects.water then
if def.effects.water.particles then
rcbows.splash(pos, "rcbows_water.png")
for k, st in ipairs(inv_list) do
if not st:is_empty() and x_bows.registered_arrows[st:get_name()] then
table.insert(itemstack_arrows, st)
end
local radius = def.effects.water.radius or 5
local flames = minetest.find_nodes_in_area({x=pos.x -radius, y=pos.y -radius, z=pos.z -radius}, {x=pos.x+radius, y=pos.y+radius, z=pos.z+radius}, {def.effects.water.flame_node})
if flames and #flames > 0 then
for i=1, #flames do
minetest.set_node(flames[i], {name="air"})
if def.effects.water.particles then
rcbows.splash(flames[i], "rcbows_water.png")
end
-- take 1st found arrow in the list
local itemstack_arrow = itemstack_arrows[1]
if itemstack_arrow and bow_def then
local _tool_capabilities = x_bows.registered_arrows[itemstack_arrow:get_name()].tool_capabilities
minetest.after(0, function(v_user, v_bow_name, v_time_load)
local wielded_item = v_user:get_wielded_item()
local wielded_item_name = wielded_item:get_name()
if wielded_item_name == v_bow_name then
local meta = wielded_item:get_meta()
meta:set_string('arrow', itemstack_arrow:get_name())
meta:set_string('time_load', tostring(v_time_load))
wielded_item:set_name(v_bow_name .. '_charged')
v_user:set_wielded_item(wielded_item)
if not x_bows.is_creative(user:get_player_name()) then
inv:remove_item('main', itemstack_arrow:get_name())
end
end
end
end, user, bow_name, time_load)
-- sound plays when charge time reaches full punch interval time
-- @TODO: find a way to prevent this from playing when not fully charged
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 = user:get_player_name(),
gain = 0.6
})
end
end, user, bow_name)
minetest.sound_play('x_bows_bow_load', {
to_player = user:get_player_name(),
gain = 0.6
})
return itemstack
end
end
--PARTICLES EFFECTS
function x_bows.shoot(itemstack, user, pointed_thing)
local time_shoot = minetest.get_us_time();
local meta = itemstack:get_meta()
local meta_arrow = meta:get_string('arrow')
local time_load = tonumber(meta:get_string('time_load'))
local tflp = (time_shoot - time_load) / 1000000
function rcbows.trail(old_pos, pos, trail_particle)
local texture, animation
if type(trail_particle) == 'table' then
texture = trail_particle.texture
animation = trail_particle.animation
else
texture = trail_particle
animation = ""
if not x_bows.registered_arrows[meta_arrow] then
return itemstack
end
minetest.add_particlespawner({
texture = texture,
amount = 20,
time = 0.2,
minpos = old_pos,
maxpos = pos,
--minvel = {x=1, y=0, z=1},
--maxvel = {x=1, y=0, z=1},
--minacc = {x=1, y=0, z=1},
--maxacc = {x=1, y=0, z=1},
minexptime = 0.2,
maxexptime = 0.5,
minsize = 0.5,
maxsize = 1.5,
collisiondetection = false,
vertical = false,
glow = 14,
animation = animation,
})
end
function rcbows.splash(old_pos, splash_particle)
minetest.add_particlespawner({
amount = 5,
time = 1,
minpos = old_pos,
maxpos = old_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 = 1,
maxsize = 1,
collisiondetection = false,
vertical = false,
texture = splash_particle,
playername = "singleplayer"
})
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[meta_arrow].tool_capabilities
rcbows.register_bow("rcbows:bow_wood", {
description = S("Wooden Bow"),
image = "rcbows_pulling_0.png",
strength = 30,
uses = 150,
charge_time = 0.5,
recipe = {
{"", "group:wood", "farming:string"},
{"group:wood", "", "farming:string"},
{"", "group:wood", "farming:string"},
},
base_texture = "rcbows_pulling_0.png",
overlay_empty = "rcbows_standby.png",
overlay_charged = "rcbows_pulling_2.png",
arrows = "rcbows:arrow",
sounds = {
max_hear_distance = 10,
gain = 0.4,
local staticdata = {
arrow = meta_arrow,
user_name = user:get_player_name(),
is_critical_hit = false,
_tool_capabilities = _tool_capabilities,
_tflp = tflp,
}
})
rcbows.register_arrow("rcbows:arrow", {
damage = 5,
inventory_arrow = {
name = "rcbows:inv_arrow",
description = S("Arrow"),
inventory_image = "rcbows_arrow.png"
},
sounds = {
max_hear_distance = 10,
gain = 0.4,
},
})
-- 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
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: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 lua_ent = obj:get_luaentity()
local strength_multiplier = tflp
if strength_multiplier > _tool_capabilities.full_punch_interval then
strength_multiplier = 1
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
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 == '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()
if not item then
return
end
if not x_bows.player_bow_sneak[name] then
x_bows.player_bow_sneak[name] = {}
end
if item == 'x_bows:bow_wood_charged' and not x_bows.player_bow_sneak[name].sneak then
if minetest.get_modpath('playerphysics') then
playerphysics.add_physics_factor(player, 'speed', 'x_bows:bow_wood_charged', 0.25)
end
x_bows.player_bow_sneak[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 minetest.get_modpath('playerphysics') then
playerphysics.remove_physics_factor(player, 'speed', 'x_bows:bow_wood_charged')
end
x_bows.player_bow_sneak[name].sneak = false
player:set_fov(1, true, 0.4)
end
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')
local mod_end_time = (minetest.get_us_time() - mod_start_time) / 1000000
print('[Mod] x_bows loaded.. ['.. mod_end_time ..'s]')

130
items.lua Normal file
View File

@ -0,0 +1,130 @@
x_bows.register_bow('bow_wood', {
description = 'Wooden Bow',
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'},
}
})
x_bows.register_arrow('arrow_wood', {
description = 'Arrow Wood',
inventory_image = 'x_bows_arrow_wood.png',
craft = {
{'', 'default:flint', ''},
{'', 'group:stick', ''},
{'', 'group:wool', ''}
},
tool_capabilities = {
full_punch_interval = 1,
max_drop_level = 0,
damage_groups = {fleshy=2}
}
})
x_bows.register_arrow('arrow_stone', {
description = 'Arrow Stone',
inventory_image = 'x_bows_arrow_stone.png',
craft = {
{'', 'default:flint', ''},
{'', 'group:stone', ''},
{'', 'group:wool', ''}
},
tool_capabilities = {
full_punch_interval = 1.2,
max_drop_level = 0,
damage_groups = {fleshy=4}
}
})
x_bows.register_arrow('arrow_bronze', {
description = 'Arrow Bronze',
inventory_image = 'x_bows_arrow_bronze.png',
craft = {
{'', 'default:flint', ''},
{'', 'default:bronze_ingot', ''},
{'', 'group:wool', ''}
},
tool_capabilities = {
full_punch_interval = 0.8,
max_drop_level = 1,
damage_groups = {fleshy=6}
}
})
x_bows.register_arrow('arrow_steel', {
description = 'Arrow Steel',
inventory_image = 'x_bows_arrow_steel.png',
craft = {
{'', 'default:flint', ''},
{'', 'default:steel_ingot', ''},
{'', 'group:wool', ''}
},
tool_capabilities = {
full_punch_interval = 0.7,
max_drop_level = 1,
damage_groups = {fleshy=6}
}
})
x_bows.register_arrow('arrow_mese', {
description = 'Arrow Mese',
inventory_image = 'x_bows_arrow_mese.png',
craft = {
{'', 'default:flint', ''},
{'', 'default:mese_crystal', ''},
{'', 'group:wool', ''}
},
tool_capabilities = {
full_punch_interval = 0.7,
max_drop_level = 1,
damage_groups = {fleshy=7}
}
})
x_bows.register_arrow('arrow_diamond', {
description = 'Arrow Diamond',
inventory_image = 'x_bows_arrow_diamond.png',
craft = {
{'', 'default:flint', ''},
{'', 'default:diamond', ''},
{'', 'group:wool', ''}
},
tool_capabilities = {
full_punch_interval = 0.7,
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 = {
{'', '', ''},
{'', 'farming_addons:poisonouspotato', ''},
{'', 'x_bows:arrow_wood', ''}
},
tool_capabilities = {
full_punch_interval = 0.7,
max_drop_level = 1,
damage_groups = {fleshy=8}
},
craft_count = 1
})
minetest.register_craft({
type = 'fuel',
recipe = 'x_bows:bow_wood',
burntime = 3,
})
minetest.register_craft({
type = 'fuel',
recipe = 'x_bows:arrow_wood',
burntime = 1,
})

View File

@ -1,3 +0,0 @@
# textdomain: rcbows
(place to reload)=(dcha. ratón para recargar)
(use to fire)=(izda. ratón para usar)

View File

@ -1,3 +1,4 @@
name = rcbows
name = x_bows
description = Adds bows to Minetest.
depends =
optional_depends = tnt, explosions
optional_depends = farming, 3d_armor, hbhunger, mesecons, playerphysics

54
nodes.lua Normal file
View File

@ -0,0 +1,54 @@
minetest.register_node('x_bows:arrow_node', {
drawtype = 'nodebox',
node_box = {
type = 'fixed',
fixed = {
{-0.1875, 0, -0.5, 0.1875, 0, 0.5},
{0, -0.1875, -0.5, 0, 0.1875, 0.5},
{-0.5, -0.5, -0.5, 0.5, 0.5, -0.5},
}
},
-- Textures of node; +Y, -Y, +X, -X, +Z, -Z
-- Textures of node; top, bottom, right, left, front, back
tiles = {
'x_bows_arrow_tile_point_top.png',
'x_bows_arrow_tile_point_bottom.png',
'x_bows_arrow_tile_point_right.png',
'x_bows_arrow_tile_point_left.png',
'x_bows_arrow_tile_tail.png',
'x_bows_arrow_tile_tail.png'
},
groups = {not_in_creative_inventory=1},
sunlight_propagates = true,
paramtype = 'light',
collision_box = {0, 0, 0, 0, 0, 0},
selection_box = {0, 0, 0, 0, 0, 0}
})
minetest.register_node('x_bows:target', {
description = 'Straw',
tiles = {'x_bows_target.png'},
is_ground_content = false,
groups = {snappy=3, flammable=4, fall_damage_add_percent=-30},
sounds = default.node_sound_leaves_defaults(),
mesecons = {receptor = {state = 'off'}},
on_timer = function (pos, elapsed)
mesecon.receptor_off(pos)
return false
end,
})
minetest.register_craft({
type = 'fuel',
recipe = 'x_bows:target',
burntime = 3,
})
minetest.register_craft({
output = 'x_bows:target',
recipe = {
{'', 'default:mese_crystal', ''},
{'default:mese_crystal', 'farming:straw', 'default:mese_crystal'},
{'', 'default:mese_crystal', ''},
}
})

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 341 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

BIN
textures/x_bows_bubble.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 146 B

BIN
textures/x_bows_target.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 B