diff --git a/README.md b/README.md index b3b9098..e4af8bd 100644 --- a/README.md +++ b/README.md @@ -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 \ No newline at end of file diff --git a/arrow.lua b/arrow.lua new file mode 100644 index 0000000..3e8df3f --- /dev/null +++ b/arrow.lua @@ -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, +}) \ No newline at end of file diff --git a/init.lua b/init.lua index 6226477..cda8962 100644 --- a/init.lua +++ b/init.lua @@ -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, - }, -}) \ No newline at end of file + -- 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]') diff --git a/items.lua b/items.lua new file mode 100644 index 0000000..68c0b7d --- /dev/null +++ b/items.lua @@ -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, +}) diff --git a/locale/rcbows.es.tr b/locale/rcbows.es.tr deleted file mode 100644 index 202eb20..0000000 --- a/locale/rcbows.es.tr +++ /dev/null @@ -1,3 +0,0 @@ -# textdomain: rcbows -(place to reload)=(dcha. ratón para recargar) -(use to fire)=(izda. ratón para usar) diff --git a/mod.conf b/mod.conf index deb8356..321ca0a 100644 --- a/mod.conf +++ b/mod.conf @@ -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 diff --git a/nodes.lua b/nodes.lua new file mode 100644 index 0000000..9c8200c --- /dev/null +++ b/nodes.lua @@ -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', ''}, + } +}) diff --git a/sounds/x_bows_arrow_hit.1.ogg b/sounds/x_bows_arrow_hit.1.ogg new file mode 100644 index 0000000..3b0d6a2 Binary files /dev/null and b/sounds/x_bows_arrow_hit.1.ogg differ diff --git a/sounds/x_bows_arrow_hit.2.ogg b/sounds/x_bows_arrow_hit.2.ogg new file mode 100644 index 0000000..0ece9e9 Binary files /dev/null and b/sounds/x_bows_arrow_hit.2.ogg differ diff --git a/sounds/x_bows_arrow_hit.3.ogg b/sounds/x_bows_arrow_hit.3.ogg new file mode 100644 index 0000000..3ff8234 Binary files /dev/null and b/sounds/x_bows_arrow_hit.3.ogg differ diff --git a/sounds/x_bows_arrow_hit.4.ogg b/sounds/x_bows_arrow_hit.4.ogg new file mode 100644 index 0000000..82aa26f Binary files /dev/null and b/sounds/x_bows_arrow_hit.4.ogg differ diff --git a/sounds/x_bows_arrow_successful_hit.ogg b/sounds/x_bows_arrow_successful_hit.ogg new file mode 100644 index 0000000..a38b2cc Binary files /dev/null and b/sounds/x_bows_arrow_successful_hit.ogg differ diff --git a/sounds/x_bows_bow_load.1.ogg b/sounds/x_bows_bow_load.1.ogg new file mode 100644 index 0000000..3ca3ef3 Binary files /dev/null and b/sounds/x_bows_bow_load.1.ogg differ diff --git a/sounds/x_bows_bow_load.2.ogg b/sounds/x_bows_bow_load.2.ogg new file mode 100644 index 0000000..9761393 Binary files /dev/null and b/sounds/x_bows_bow_load.2.ogg differ diff --git a/sounds/x_bows_bow_load.3.ogg b/sounds/x_bows_bow_load.3.ogg new file mode 100644 index 0000000..510ad79 Binary files /dev/null and b/sounds/x_bows_bow_load.3.ogg differ diff --git a/sounds/x_bows_bow_loaded.ogg b/sounds/x_bows_bow_loaded.ogg new file mode 100644 index 0000000..56a8319 Binary files /dev/null and b/sounds/x_bows_bow_loaded.ogg differ diff --git a/sounds/x_bows_bow_shoot.1.ogg b/sounds/x_bows_bow_shoot.1.ogg new file mode 100644 index 0000000..8d1a195 Binary files /dev/null and b/sounds/x_bows_bow_shoot.1.ogg differ diff --git a/sounds/x_bows_bow_shoot_crit.ogg b/sounds/x_bows_bow_shoot_crit.ogg new file mode 100644 index 0000000..f1e8633 Binary files /dev/null and b/sounds/x_bows_bow_shoot_crit.ogg differ diff --git a/textures/rcbows_arrow.png b/textures/rcbows_arrow.png deleted file mode 100644 index 9d765a7..0000000 Binary files a/textures/rcbows_arrow.png and /dev/null differ diff --git a/textures/rcbows_bubble.png b/textures/rcbows_bubble.png deleted file mode 100755 index b153ab1..0000000 Binary files a/textures/rcbows_bubble.png and /dev/null differ diff --git a/textures/rcbows_pulling_0.png b/textures/rcbows_pulling_0.png deleted file mode 100644 index f65e706..0000000 Binary files a/textures/rcbows_pulling_0.png and /dev/null differ diff --git a/textures/rcbows_pulling_1.png b/textures/rcbows_pulling_1.png deleted file mode 100644 index 01a3dd8..0000000 Binary files a/textures/rcbows_pulling_1.png and /dev/null differ diff --git a/textures/rcbows_pulling_2.png b/textures/rcbows_pulling_2.png deleted file mode 100644 index db24421..0000000 Binary files a/textures/rcbows_pulling_2.png and /dev/null differ diff --git a/textures/rcbows_standby.png b/textures/rcbows_standby.png deleted file mode 100644 index e372294..0000000 Binary files a/textures/rcbows_standby.png and /dev/null differ diff --git a/textures/rcbows_viewfinder.png b/textures/rcbows_viewfinder.png deleted file mode 100644 index b694cd9..0000000 Binary files a/textures/rcbows_viewfinder.png and /dev/null differ diff --git a/textures/rcbows_water.png b/textures/rcbows_water.png deleted file mode 100755 index cd8cbc6..0000000 Binary files a/textures/rcbows_water.png and /dev/null differ diff --git a/textures/x_bows_arrow_bronze.png b/textures/x_bows_arrow_bronze.png new file mode 100644 index 0000000..5262042 Binary files /dev/null and b/textures/x_bows_arrow_bronze.png differ diff --git a/textures/x_bows_arrow_crit.png b/textures/x_bows_arrow_crit.png new file mode 100644 index 0000000..a5afae1 Binary files /dev/null and b/textures/x_bows_arrow_crit.png differ diff --git a/textures/x_bows_arrow_diamond.png b/textures/x_bows_arrow_diamond.png new file mode 100644 index 0000000..d3872e9 Binary files /dev/null and b/textures/x_bows_arrow_diamond.png differ diff --git a/textures/x_bows_arrow_diamond_poison.png b/textures/x_bows_arrow_diamond_poison.png new file mode 100644 index 0000000..53f35c9 Binary files /dev/null and b/textures/x_bows_arrow_diamond_poison.png differ diff --git a/textures/x_bows_arrow_mese.png b/textures/x_bows_arrow_mese.png new file mode 100644 index 0000000..edc382b Binary files /dev/null and b/textures/x_bows_arrow_mese.png differ diff --git a/textures/x_bows_arrow_particle.png b/textures/x_bows_arrow_particle.png new file mode 100644 index 0000000..9dc3258 Binary files /dev/null and b/textures/x_bows_arrow_particle.png differ diff --git a/textures/x_bows_arrow_steel.png b/textures/x_bows_arrow_steel.png new file mode 100644 index 0000000..ac15296 Binary files /dev/null and b/textures/x_bows_arrow_steel.png differ diff --git a/textures/x_bows_arrow_stone.png b/textures/x_bows_arrow_stone.png new file mode 100644 index 0000000..fa794c0 Binary files /dev/null and b/textures/x_bows_arrow_stone.png differ diff --git a/textures/x_bows_arrow_tile_point_bottom.png b/textures/x_bows_arrow_tile_point_bottom.png new file mode 100644 index 0000000..6cf6f9c Binary files /dev/null and b/textures/x_bows_arrow_tile_point_bottom.png differ diff --git a/textures/x_bows_arrow_tile_point_left.png b/textures/x_bows_arrow_tile_point_left.png new file mode 100644 index 0000000..f407440 Binary files /dev/null and b/textures/x_bows_arrow_tile_point_left.png differ diff --git a/textures/x_bows_arrow_tile_point_right.png b/textures/x_bows_arrow_tile_point_right.png new file mode 100644 index 0000000..28ef4c6 Binary files /dev/null and b/textures/x_bows_arrow_tile_point_right.png differ diff --git a/textures/x_bows_arrow_tile_point_top.png b/textures/x_bows_arrow_tile_point_top.png new file mode 100644 index 0000000..67a0bc6 Binary files /dev/null and b/textures/x_bows_arrow_tile_point_top.png differ diff --git a/textures/x_bows_arrow_tile_tail.png b/textures/x_bows_arrow_tile_tail.png new file mode 100644 index 0000000..6790099 Binary files /dev/null and b/textures/x_bows_arrow_tile_tail.png differ diff --git a/textures/x_bows_arrow_tipped_particle.png b/textures/x_bows_arrow_tipped_particle.png new file mode 100644 index 0000000..1a17ed6 Binary files /dev/null and b/textures/x_bows_arrow_tipped_particle.png differ diff --git a/textures/x_bows_arrow_wood.png b/textures/x_bows_arrow_wood.png new file mode 100644 index 0000000..47c472c Binary files /dev/null and b/textures/x_bows_arrow_wood.png differ diff --git a/textures/x_bows_bow_wood.png b/textures/x_bows_bow_wood.png new file mode 100644 index 0000000..e895ef1 Binary files /dev/null and b/textures/x_bows_bow_wood.png differ diff --git a/textures/x_bows_bow_wood_charged.png b/textures/x_bows_bow_wood_charged.png new file mode 100644 index 0000000..205220f Binary files /dev/null and b/textures/x_bows_bow_wood_charged.png differ diff --git a/textures/x_bows_bubble.png b/textures/x_bows_bubble.png new file mode 100644 index 0000000..e937bd9 Binary files /dev/null and b/textures/x_bows_bubble.png differ diff --git a/textures/x_bows_poisoned_heart.png b/textures/x_bows_poisoned_heart.png new file mode 100644 index 0000000..abb47cf Binary files /dev/null and b/textures/x_bows_poisoned_heart.png differ diff --git a/textures/x_bows_target.png b/textures/x_bows_target.png new file mode 100644 index 0000000..7799aec Binary files /dev/null and b/textures/x_bows_target.png differ