diff --git a/mods/ITEMS/REDSTONE/mesecons_button/init.lua b/mods/ITEMS/REDSTONE/mesecons_button/init.lua index 20d329bf7..3e34670e0 100644 --- a/mods/ITEMS/REDSTONE/mesecons_button/init.lua +++ b/mods/ITEMS/REDSTONE/mesecons_button/init.lua @@ -17,6 +17,20 @@ local boxes_on = { wall_top = { -4/16, 7/16, -2/16, 4/16, 8/16, 2/16 }, } +-- Push the button +mesecon.push_button = function(pos, node) + -- No-op if button is already pushed + if mesecon.is_receptor_on(node) then + return + end + local def = minetest.registered_nodes[node.name] + minetest.set_node(pos, {name="mesecons_button:button_"..def._mcl_button_basename.."_on", param2=node.param2}) + mesecon.receptor_on(pos, button_get_output_rules(node)) + minetest.sound_play("mesecons_button_push", {pos=pos}) + local timer = minetest.get_node_timer(pos) + timer:start(def._mcl_button_timer) +end + local on_button_place = function(itemstack, placer, pointed_thing) if pointed_thing.type ~= "node" then -- no interaction possible with entities @@ -66,7 +80,7 @@ end local buttonuse = "Rightclick the button to push it." -mesecon.register_button = function(basename, description, texture, recipeitem, sounds, plusgroups, button_timer, longdesc) +mesecon.register_button = function(basename, description, texture, recipeitem, sounds, plusgroups, button_timer, push_by_arrow, longdesc) local groups_off = table.copy(plusgroups) groups_off.attached_node=1 groups_off.dig_by_water=1 @@ -78,6 +92,11 @@ mesecon.register_button = function(basename, description, texture, recipeitem, s groups_on.not_in_creative_inventory=1 groups_on.button=2 -- button (on) + if push_by_arrow then + groups_off.button_push_by_arrow = 1 + groups_on.button_push_by_arrow = 1 + end + minetest.register_node("mesecons_button:button_"..basename.."_off", { drawtype = "nodebox", tiles = {texture}, @@ -98,17 +117,16 @@ mesecon.register_button = function(basename, description, texture, recipeitem, s on_place = on_button_place, node_placement_prediction = "", on_rightclick = function (pos, node) - minetest.set_node(pos, {name="mesecons_button:button_"..basename.."_on", param2=node.param2}) - mesecon.receptor_on(pos, button_get_output_rules(node)) - minetest.sound_play("mesecons_button_push", {pos=pos}) - local timer = minetest.get_node_timer(pos) - timer:start(button_timer) + mesecon.push_button(pos, node) end, sounds = sounds, mesecons = {receptor = { state = mesecon.state.off, rules = button_get_output_rules, }}, + _mcl_button_basename = basename, + _mcl_button_timer = button_timer, + _mcl_blast_resistance = 2.5, _mcl_hardness = 0.5, }) @@ -134,14 +152,33 @@ mesecon.register_button = function(basename, description, texture, recipeitem, s state = mesecon.state.on, rules = button_get_output_rules }}, + _mcl_button_basename = basename, + _mcl_button_timer = button_timer, on_timer = function(pos, elapsed) local node = minetest.get_node(pos) if node.name=="mesecons_button:button_"..basename.."_on" then --has not been dug + -- Is button pushable by arrow? + if push_by_arrow then + -- If there's an arrow stuck in the button, keep it pressed and check + -- it again later. + local objs = minetest.get_objects_inside_radius(pos, 1) + for o=1, #objs do + local entity = objs[o]:get_luaentity() + if entity and entity.name == "mcl_bows:arrow_entity" then + local timer = minetest.get_node_timer(pos) + timer:start(button_timer) + return + end + end + end + + -- Normal operation: Un-press the button minetest.set_node(pos, {name="mesecons_button:button_"..basename.."_off",param2=node.param2}) minetest.sound_play("mesecons_button_pop", {pos=pos}) mesecon.receptor_off(pos, button_get_output_rules(node)) end end, + _mcl_blast_resistance = 2.5, _mcl_hardness = 0.5, }) @@ -160,6 +197,7 @@ mesecon.register_button( mcl_sounds.node_sound_stone_defaults(), {material_stone=1,handy=1,pickaxey=1}, 1, + false, "A stone button is a redstone component made out of stone which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for 1 second. It can only be placed on solid opaque full cubes (like cobblestone).") local woods = { @@ -180,7 +218,8 @@ for w=1, #woods do mcl_sounds.node_sound_wood_defaults(), {material_wood=1,handy=1,axey=1}, 1.5, - "A wooden button is a redstone component made out of wood which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for 1.5 seconds. It can only be placed on solid opaque full cubes (like cobblestone).") + true, + "A wooden button is a redstone component made out of wood which can be pushed to provide redstone power. When pushed, it powers adjacent redstone components for 1.5 seconds. It can only be placed on solid opaque full cubes (like cobblestone). Wooden buttons may also be pushed by arrows.") minetest.register_craft({ type = "fuel", diff --git a/mods/ITEMS/mcl_bows/arrow.lua b/mods/ITEMS/mcl_bows/arrow.lua index 0eda86895..a7e06ae80 100644 --- a/mods/ITEMS/mcl_bows/arrow.lua +++ b/mods/ITEMS/mcl_bows/arrow.lua @@ -1,3 +1,6 @@ +-- Time in seconds after which a stuck arrow is deleted +local ARROW_TIMEOUT = 60 + local mod_mcl_hunger = minetest.get_modpath("mcl_hunger") local mod_awards = minetest.get_modpath("awards") and minetest.get_modpath("mcl_achievements") @@ -43,25 +46,60 @@ minetest.register_node("mcl_bows:arrow_box", { groups = {not_in_creative_inventory=1}, }) -local THROWING_ARROW_ENTITY={ - physical = false, +-- FIXME: Arrow velocity is a bit strange. If the arrow flies VERY long, the acceleration can cause the velocity to become negative +-- and the arrow flies backwards. +local ARROW_ENTITY={ + physical = true, visual = "wielditem", visual_size = {x=0.4, y=0.4}, textures = {"mcl_bows:arrow_box"}, - collisionbox = {0,0,0,0,0,0}, + collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1}, + collide_with_objects = false, _lastpos={}, _startpos=nil, _damage=1, -- Damage on impact + _stuck=false, -- Whether arrow is stuck + _stucktimer=nil,-- Amount of time (in seconds) the arrow has been stuck so far _shooter=nil, -- ObjectRef of player or mob who shot it } -THROWING_ARROW_ENTITY.on_step = function(self, dtime) +ARROW_ENTITY.on_step = function(self, dtime) local pos = self.object:getpos() - local node = minetest.get_node(pos) + local dpos = table.copy(pos) -- digital pos + dpos = vector.round(dpos) + local node = minetest.get_node(dpos) + if self._stuck then + self._stucktimer = self._stucktimer + dtime + if self._stucktimer > ARROW_TIMEOUT then + self.object:remove() + return + end + local objects = minetest.get_objects_inside_radius(pos, 2) + for _,obj in ipairs(objects) do + if obj:is_player() then + if not minetest.settings:get_bool("creative_mode") then + -- Pickup arrow if player is nearby + if obj:get_inventory():room_for_item("main", "mcl_bows:arrow") then + obj:get_inventory():add_item("main", "mcl_bows:arrow") + minetest.sound_play("item_drop_pickup", { + pos = pos, + max_hear_distance = 16, + gain = 1.0, + }) + self.object:remove() + return + end + else + self.object:remove() + return + end + end + end + -- Check for object collision. Done every tick (hopefully this is not too stressing) - do + else local objs = minetest.get_objects_inside_radius(pos, 2) local closest_object local closest_distance @@ -123,18 +161,27 @@ THROWING_ARROW_ENTITY.on_step = function(self, dtime) end end self.object:remove() + return end end end -- Check for node collision - if self._lastpos.x~=nil then + -- FIXME: Also collides with ignore + if self._lastpos.x~=nil and not self._stuck then local def = minetest.registered_nodes[node.name] - if (def and def.walkable) or not def then - if not minetest.settings:get_bool("creative_mode") then - minetest.add_item(self._lastpos, 'mcl_bows:arrow') + local vel = self.object:get_velocity() + -- Arrow has stopped + if (math.abs(vel.x) < 0.0001) or (math.abs(vel.z) < 0.0001) or (math.abs(vel.y) < 0.00001) then + -- Arrow is stuck and no longer moves + self._stuck = true + self._stucktimer = 0 + self.object:set_velocity({x=0, y=0, z=0}) + self.object:set_acceleration({x=0, y=0, z=0}) + -- Push the button + if minetest.get_modpath("mesecons_button") and minetest.get_item_group(node.name, "button") > 0 and minetest.get_item_group(node.name, "button_push_by_arrow") == 1 then + mesecon.push_button(dpos, node) end - self.object:remove() elseif (def and def.liquidtype ~= "none") then -- Slow down arrow in liquids local v = def.liquid_viscosity @@ -142,7 +189,6 @@ THROWING_ARROW_ENTITY.on_step = function(self, dtime) v = 0 end local vpenalty = math.max(0.1, 0.98 - 0.1 * v) - local vel = self.object:get_velocity() if math.abs(vel.x) > 0.001 then vel.x = vel.x * vpenalty end @@ -157,7 +203,38 @@ THROWING_ARROW_ENTITY.on_step = function(self, dtime) self._lastpos={x=pos.x, y=pos.y, z=pos.z} end -minetest.register_entity("mcl_bows:arrow_entity", THROWING_ARROW_ENTITY) +ARROW_ENTITY.get_staticdata = function(self) + local out = { + lastpos = self._lastpos, + startpos = self._startpos, + damage = self._damage, + stuck = self._stuck, + stucktimer = self._stucktimer, + } + if self._shooter and self._shooter:is_player() then + out.shootername = self._shooter:get_player_name() + end + return minetest.serialize(out) +end + +ARROW_ENTITY.on_activate = function(self, staticdata, dtime_s) + local data = minetest.deserialize(staticdata) + if data then + self._lastpos = data.lastpos + self._startpos = data.startpos + self._damage = data.damage + self._stuck = data.stuck + self._stucktimer = data.stucktimer + if data.shootername then + local shooter = minetest.get_player_by_name(data.shootername) + if shooter and shooter:is_player() then + self._shooter = shooter + end + end + end +end + +minetest.register_entity("mcl_bows:arrow_entity", ARROW_ENTITY) if minetest.get_modpath("mcl_core") and minetest.get_modpath("mcl_mobitems") then minetest.register_craft({ diff --git a/mods/ITEMS/mcl_bows/depends.txt b/mods/ITEMS/mcl_bows/depends.txt index 98f2eaa73..eb7f075a1 100644 --- a/mods/ITEMS/mcl_bows/depends.txt +++ b/mods/ITEMS/mcl_bows/depends.txt @@ -6,3 +6,4 @@ mcl_core? mcl_mobitems? mcl_playerphysics? doc? +mesecons_button?