local minetest,math,vector,ipairs,string,type = minetest,math,vector,ipairs,string,type -- minetest library local get_connected_players = minetest.get_connected_players local get_player_by_name = minetest.get_player_by_name local get_objects_inside_radius = minetest.get_objects_inside_radius local create_raycast = minetest.raycast local dir_to_yaw = minetest.dir_to_yaw local deserialize = minetest.deserialize local serialize = minetest.serialize -- string library local s_sub = string.sub local s_len = string.len -- math library local pi = math.pi local random = math.random local floor = math.floor -- vector library local new_vec = vector.new local floor_vec = vector.floor local vec_distance = vector.distance local normalize_vec = vector.normalize local add_vec = vector.add local sub_vec = vector.subtract local multiply_vec = vector.multiply local divide_vec = vector.divide local vec_direction = vector.direction --[[ ██████╗ ██╗ █████╗ ██╗ ██╗███████╗██████╗ ██████╗ █████╗ ██████╗ ████████╗ ██╔══██╗██║ ██╔══██╗╚██╗ ██╔╝██╔════╝██╔══██╗ ██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝ ██████╔╝██║ ███████║ ╚████╔╝ █████╗ ██████╔╝ ██████╔╝███████║██████╔╝ ██║ ██╔═══╝ ██║ ██╔══██║ ╚██╔╝ ██╔══╝ ██╔══██╗ ██╔═══╝ ██╔══██║██╔══██╗ ██║ ██║ ███████╗██║ ██║ ██║ ███████╗██║ ██║ ██║ ██║ ██║██║ ██║ ██║ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ]]-- -- data pool local pool = {} --this is a very complicated function which makes the bow work local temp_pool local player local new_index local rightclick local inv local dir local vel local pos local object local function arrow_check(name,dtime) temp_pool = pool[name] player = get_player_by_name(name) rightclick = player:get_player_control().RMB new_index = player:get_wield_index() -- if player changes selected item if new_index ~= temp_pool.index then inv:set_stack("main", temp_pool.index, ItemStack("bow:bow_empty")) pool[name] = nil return end -- if player lets go of rightclick if temp_pool.step ~= 5 and not rightclick then inv:set_stack("main", temp_pool.index, ItemStack("bow:bow_empty")) pool[name] = nil return end -- if player isn't holding a bow if minetest.get_item_group(player:get_wielded_item():get_name(), "bow") == 0 then pool[name] = nil return end inv = player:get_inventory() -- if player doesn't have any arrows if not inv:contains_item("main", ItemStack("bow:arrow")) then inv:set_stack("main", temp_pool.index, ItemStack("bow:bow_empty")) pool[name] = nil return end -- count steps using dtime if temp_pool.step < 5 then temp_pool.float = temp_pool.float + dtime if temp_pool.float > 0.05 then temp_pool.float = 0 temp_pool.step = temp_pool.step + 1 player:set_wielded_item(ItemStack("bow:bow_"..temp_pool.step)) end end if temp_pool.step == 5 and not rightclick then dir = player:get_look_dir() vel = multiply_vec(dir,50) pos = player:get_pos() pos.y = pos.y + 1.5 object = minetest.add_entity(add_vec(pos,divide_vec(dir,10)),"bow:arrow") object:set_velocity(vel) object:get_luaentity().owner = name object:get_luaentity().oldpos = pos minetest.sound_play("bow", {object=player, gain = 1.0, max_hear_distance = 60,pitch = random(80,100)/100}) inv:remove_item("main", ItemStack("bow:arrow")) inv:set_stack("main", temp_pool.index, ItemStack("bow:bow_empty")) pool[name] = nil end --add hand fatigue timer --gradually increase fatigue until cap is reached end minetest.register_globalstep(function(dtime) for name in pairs(pool) do arrow_check(name,dtime) end end) --[[ █████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗ ██╔══██╗██╔══██╗██╔══██╗██╔═══██╗██║ ██║ ██╔════╝██║ ██║████╗ ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝ ███████║██████╔╝██████╔╝██║ ██║██║ █╗ ██║ █████╗ ██║ ██║██╔██╗ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗ ██╔══██║██╔══██╗██╔══██╗██║ ██║██║███╗██║ ██╔══╝ ██║ ██║██║╚██╗██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║ ██║ ██║██║ ██║██║ ██║╚██████╔╝╚███╔███╔╝ ██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ ]]-- local pos local vel local owner local pos2 local player_velocity local direction local distance local multiplier local velocity local collision local ray local dir local y local x local function arrow_step(self, dtime,moveresult) self.timer = self.timer + dtime pos = self.object:get_pos() vel = self.object:get_velocity() if self.collecting == true then owner = get_player_by_name(self.owner) for _,object in ipairs(get_objects_inside_radius(pos, self.radius)) do if owner then self.object:set_acceleration(new_vec(0,0,0)) --get the variables pos2 = owner:get_pos() player_velocity = owner:get_player_velocity() pos2.y = pos2.y + self.collection_height direction = normalize_vec(sub_vec(pos2,pos)) distance = vec_distance(pos2,pos) --remove if too far away if distance > self.radius then distance = 0 end multiplier = (self.radius*5) - distance velocity = multiply_vec(direction,multiplier) velocity = add_vec(player_velocity,velocity) self.object:set_velocity(velocity) if distance < 0.2 then self.object:remove() end return else print(self.owner.." does not exist") self.object:remove() end end else for _,object in ipairs(get_objects_inside_radius(pos, 2)) do if self.stuck == false and ((object:is_player() and object:get_player_name() ~= self.owner and object:get_hp() > 0) or (object:get_luaentity() and object:get_luaentity().mobname)) then object:punch(self.object, 2, { full_punch_interval=1.5, damage_groups = {damage=3}, }) self.object:remove() return elseif self.timer > 3 and (object:is_player() and object:get_player_name() == self.owner) then self.collecting = true local inv = object:get_inventory() if inv and inv:room_for_item("main", ItemStack("bow:arrow")) then inv:add_item("main",ItemStack("bow:arrow")) minetest.sound_play("pickup", { to_player = object:get_player_name(), gain = 0.4, pitch = random(60,100)/100 }) else self.object:remove() minetest.throw_item(pos,"bow:arrow") end end end if moveresult and moveresult.collides and moveresult.collisions and moveresult.collisions[1] and moveresult.collisions[1].new_velocity and self.stuck == false then collision = moveresult.collisions[1] if collision.new_velocity.x == 0 and collision.old_velocity.x ~= 0 then self.check_dir = vec_direction(new_vec(pos.x,0,0),new_vec(collision.node_pos.x,0,0)) elseif collision.new_velocity.y == 0 and collision.old_velocity.y ~= 0 then self.check_dir = vec_direction(new_vec(0,pos.y,0),new_vec(0,collision.node_pos.y,0)) elseif collision.new_velocity.z == 0 and collision.old_velocity.z ~= 0 then self.check_dir = vec_direction(new_vec(0,0,pos.z),new_vec(0,0,collision.node_pos.z)) end if collision.new_pos then --print(dump(collision.new_pos)) self.object:set_pos(collision.new_pos) end --print(dump(collision.new_pos)) minetest.sound_play("arrow_hit",{object=self.object,gain=1,pitch=random(80,100)/100,max_hear_distance=64}) self.stuck = true self.object:set_velocity(new_vec(0,0,0)) self.object:set_acceleration(new_vec(0,0,0)) elseif self.stuck == true and self.check_dir then pos2 = add_vec(pos,multiply_vec(self.check_dir,0.2)) ray = create_raycast(pos, pos2, false, false) if not ray:next() then self.stuck = false self.object:set_acceleration(new_vec(0,-9.81,0)) end end if not self.stuck and pos and self.oldpos then self.spin = self.spin + (dtime*10) if self.spin > pi then self.spin = -pi end dir = normalize_vec(sub_vec(pos,self.oldpos)) y = dir_to_yaw(dir) x = (dir_to_yaw(new_vec(vec_distance(new_vec(pos.x,0,pos.z),new_vec(self.oldpos.x,0,self.oldpos.z)),0,pos.y-self.oldpos.y))+(pi/2)) self.object:set_rotation(new_vec(x,y,self.spin)) end if self.stuck == false then self.oldpos = pos self.oldvel = vel end end end --[[ █████╗ ██████╗ ██████╗ ██████╗ ██╗ ██╗ ███████╗███╗ ██╗████████╗██╗████████╗██╗ ██╗ ██╔══██╗██╔══██╗██╔══██╗██╔═══██╗██║ ██║ ██╔════╝████╗ ██║╚══██╔══╝██║╚══██╔══╝╚██╗ ██╔╝ ███████║██████╔╝██████╔╝██║ ██║██║ █╗ ██║ █████╗ ██╔██╗ ██║ ██║ ██║ ██║ ╚████╔╝ ██╔══██║██╔══██╗██╔══██╗██║ ██║██║███╗██║ ██╔══╝ ██║╚██╗██║ ██║ ██║ ██║ ╚██╔╝ ██║ ██║██║ ██║██║ ██║╚██████╔╝╚███╔███╔╝ ███████╗██║ ╚████║ ██║ ██║ ██║ ██║ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ]]-- local arrow = {} arrow.initial_properties = { physical = true, collide_with_objects = false, collisionbox = {-0.05, -0.05, -0.05, 0.05, 0.05, 0.05}, visual = "mesh", visual_size = {x = 1 , y = 1}, mesh = "basic_bow_arrow.b3d", textures = { "basic_bow_arrow_uv.png" }, pointable = false, --automatic_face_movement_dir = 0.0, --automatic_face_movement_max_rotation_per_sec = 600, } arrow.on_activate = function(self, staticdata, dtime_s) --self.object:set_animation({x=0,y=180}, 15, 0, true) local vel = nil if s_sub(staticdata, 1, s_len("return")) == "return" then local data = deserialize(staticdata) if data and type(data) == "table" then self.spin = data.spin self.owner = data.owner self.stuck = data.stuck self.timer = data.timer self.collecting = data.collecting self.check_dir = data.check_dir vel = data.vel end end if not self.stuck then self.object:set_acceleration(new_vec(0,-9.81,0)) if vel then self.object:set_velocity(vel) end end end arrow.get_staticdata = function(self) return serialize({ spin = self.spin, owner = self.owner, stuck = self.stuck, timer = self.timer, collecting = self.collecting, check_dir = self.check_dir, vel = self.object:get_velocity() }) end arrow.spin = 0 arrow.owner = "" arrow.stuck = false arrow.timer = 0 arrow.collecting = false arrow.collection_height = 0.5 arrow.radius = 2 arrow.on_step = function(self, dtime,moveresult) arrow_step(self, dtime,moveresult) end minetest.register_entity("bow:arrow", arrow) --[[ ██╗████████╗███████╗███╗ ███╗███████╗ ██║╚══██╔══╝██╔════╝████╗ ████║██╔════╝ ██║ ██║ █████╗ ██╔████╔██║███████╗ ██║ ██║ ██╔══╝ ██║╚██╔╝██║╚════██║ ██║ ██║ ███████╗██║ ╚═╝ ██║███████║ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝ ]]-- local inv local function initialize_pullback(player) inv = player:get_inventory() if inv:contains_item("main", ItemStack("bow:arrow")) then name = player:get_player_name() pool[name] = {} pool[name].index = player:get_wield_index() pool[name].float = 0 pool[name].step = 0 minetest.sound_play("bow_pull_back", {object=player, gain = 1.0, max_hear_distance = 60,pitch = random(70,110)/100}) end end minetest.register_craftitem("bow:bow_empty", { description = "Bow", inventory_image = "bow.png", stack_max = 1, groups = {bow=1}, range = 0, on_secondary_use = function(itemstack, user, pointed_thing) initialize_pullback(user) end, on_place = function(itemstack, placer, pointed_thing) initialize_pullback(placer) end, }) for i = 1,5 do minetest.register_craftitem("bow:bow_"..i, { description = "Bow", inventory_image = "bow_"..i..".png", stack_max = 1, groups = {bow=1,bow_loaded=i}, range = 0, on_drop = function(itemstack, dropper, pos) itemstack = ItemStack("bow:bow_empty") minetest.item_drop(itemstack, dropper, pos) return(itemstack) end, }) end minetest.register_craftitem("bow:arrow", { description = "Arrow", inventory_image = "arrow_item.png", }) --[[ ██████╗██████╗ █████╗ ███████╗████████╗██╗███╗ ██╗ ██████╗ ██╔════╝██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██║████╗ ██║██╔════╝ ██║ ██████╔╝███████║█████╗ ██║ ██║██╔██╗ ██║██║ ███╗ ██║ ██╔══██╗██╔══██║██╔══╝ ██║ ██║██║╚██╗██║██║ ██║ ╚██████╗██║ ██║██║ ██║██║ ██║ ██║██║ ╚████║╚██████╔╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ]]-- minetest.register_craft({ output = "bow:bow_empty", recipe = { {"" , "main:stick", "mob:string"}, {"main:stick" , "" , "mob:string"}, {"" , "main:stick", "mob:string"}, }, }) minetest.register_craft({ output = "bow:arrow 16", recipe = { {"main:iron", "" , "" }, {"" , "main:stick", "" }, {"" , "" , "mob:feather"}, }, })