Crafter/mods/bow/init.lua
2020-06-26 01:18:07 -04:00

446 lines
16 KiB
Lua

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"},
},
})