Move throwing physics to builtin

master
stujones11 2019-06-21 21:43:20 +01:00 committed by MoNTE48
parent 5b424fd67b
commit 9a65213458
7 changed files with 218 additions and 203 deletions

View File

@ -435,6 +435,81 @@ function core.item_secondary_use(itemstack, placer)
return itemstack
end
local function item_throw_step(entity, dtime)
entity.throw_timer = entity.throw_timer + dtime
if entity.throw_timer > 20 then
entity.object:remove()
return
end
if not entity.thrower then
return
end
local pos = entity.object:get_pos()
if not core.is_valid_pos(pos) then
entity.object:remove()
return
end
local hit_object = nil
local node = minetest.get_node({x=pos.x, y=pos.y - 1, z=pos.z})
local objs = minetest.get_objects_inside_radius(pos, 1)
for _, obj in pairs(objs) do
if obj:is_player() then
local name = obj:get_player_name()
if name ~= entity.thrower then
hit_object = obj
end
elseif obj:get_luaentity() ~= nil and
obj:get_luaentity().name ~= entity.name then
hit_object = obj
end
end
if hit_object or (node.name ~= "air" and node.name ~= "ignore") then
local player = core.get_player_by_name(entity.thrower)
entity.on_impact(player, pos, entity.throw_direction, hit_object)
entity.object:remove()
end
end
function core.item_throw(name, thrower, speed, accel, on_impact)
if not thrower or not thrower:is_player() then
return
end
local pos = thrower:get_pos()
if not core.is_valid_pos(pos) then
return
end
pos.y = pos.y + 1.5
local obj = nil
local properties = {is_visible=true}
if core.registered_entities[name] then
obj = core.add_entity(pos, name)
elseif core.registered_items[name] then
obj = core.add_entity(pos, "__builtin:throwing_item")
properties.textures = {name}
else
return
end
if obj then
local ent = obj:get_luaentity()
if ent then
local s = speed or 19 -- default speed
local a = accel or -3 -- default acceleration
local dir = thrower:get_look_dir()
ent.thrower = thrower:get_player_name()
ent.throw_timer = 0
ent.throw_direction = dir
ent.on_step = item_throw_step
ent.on_impact = on_impact and on_impact or function() end
obj:set_properties(properties)
obj:set_velocity({x=dir.x * s, y=dir.y * s, z=dir.z * s})
obj:set_acceleration({x=dir.x * a, y=-9.81, z=dir.z * a})
return obj
else
obj:remove()
end
end
end
function core.item_drop(itemstack, dropper, pos)
local dropper_is_player = dropper and dropper:is_player()
local p = table.copy(pos)

View File

@ -17,6 +17,22 @@ end
local time_to_live = tonumber(core.settings:get("item_entity_ttl")) or 900
local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
core.register_entity(":__builtin:throwing_item", {
physical = true,
visual = "wielditem",
collisionbox = {0,0,0, 0,0,0},
textures = {""},
visual_size = {x=0.4, y=0.4},
is_visible = false,
on_activate = function(self, staticdata)
if staticdata == "expired" then
self.object:remove()
end
end,
get_staticdata = function()
return "expired"
end,
})
core.register_entity(":__builtin:item", {
initial_properties = {

View File

@ -2565,6 +2565,18 @@ and `minetest.auth_reload` call the authetification handler.
* Returns `ObjectRef`, or `nil` if failed
* `minetest.add_item(pos, item)`: Spawn item
* Returns `ObjectRef`, or `nil` if failed
* `minetest.item_throw(name, thrower, speed, accel, func(thrower, pos, dir, obj)`
* Returns an `ObjectRef` or `nil` if failed
* `name`: entity name or itemstring
* `thrower`: player `ObjectRef`
* `speed`: optional, default = 19
* `accel`: optional, default = -3
* `func`: optional, function called when the item has landed
* `thrower`: player `ObjectRef`
* `pos`: landing position
* `dir`: direction of travel
* `obj`: hit entity or player `ObjectRef`, may be nil
Note: the entity will be automatically removed upon landing
* `minetest.get_player_by_name(name)`: Get an `ObjectRef` to a player
* `minetest.get_objects_inside_radius(pos, radius)`
* `radius`: using an euclidean metric
@ -2725,6 +2737,9 @@ and `minetest.auth_reload` call the authetification handler.
unattached `group:attached_node` node to fall.
* spread these updates to neighbours and can cause a cascade
of nodes to fall.
* `minetest.is_valid_pos(pos)`
* Returns `true` if the position lies within the range -32000 to 32000
### Inventory
`minetest.get_inventory(location)`: returns an `InvRef`

View File

@ -516,33 +516,44 @@ minetest.register_abm({
--
-- Shoot snowball
snow_shoot_snowball = function (item, player, pointed_thing)
local pos = player:get_pos()
local obj = minetest.add_entity({x=pos.x, y=pos.y + 1.5, z=pos.z},
"default:snowball_entity")
if obj then
local ent = obj:get_luaentity()
if ent then
ent.thrower = player:get_player_name()
local dir = player:get_look_dir()
obj:setvelocity({x = dir.x * 19, y = dir.y * 19, z = dir.z * 19})
obj:set_acceleration({x = dir.x * -3, y = -9.81, z = dir.z * -3})
item:take_item()
else
obj:remove()
local function snowball_impact(thrower, pos)
local node = minetest.get_node(pos)
if node.name == "air" then
local pos_under = vector.subtract(pos, {x=0, y=1, z=0})
node = minetest.get_node(pos_under)
if node.name then
local def = minetest.registered_items[node.name] or {}
if def.buildable_to == true then
minetest.add_node(pos_under, {name="default:snow"})
elseif def.walkable == true then
minetest.add_node(pos, {name="default:snow"})
end
elseif node.name then
local def = minetest.registered_items[node.name]
if def and def.buildable_to == true then
minetest.add_node(pos, {name="default:snow"})
end
end
end
return item
end
-- Snowball entity
local snowball_entity = {
physical = false,
function default.snow_shoot_snowball(itemstack, player, pointed_thing)
itemstack:take_item()
minetest.item_throw("default:snowball_entity", player, 19, -3,
snowball_impact)
minetest.sound_play("throwing_sound", {
pos = pos,
gain = 1.0,
max_hear_distance = 5,
})
return itemstack
end
minetest.register_entity("default:snowball_entity", {
physical = true,
textures = {"default_snowball.png",},
visual = "sprite",
thrower = nil,
timer = 0,
lastpos = {},
collisionbox = {0,0,0, 0,0,0},
on_activate = function(self, staticdata)
if staticdata == "expired" then
@ -552,68 +563,4 @@ local snowball_entity = {
get_staticdata = function()
return "expired"
end,
}
-- Called when snowball is moving.
snowball_entity.on_step = function(self, dtime)
self.timer = self.timer + dtime
if self.timer > 20 then
self.object:remove()
return
end
if not self.thrower then
return
end
local drop_pos = nil
local pos = self.object:getpos()
local node = minetest.get_node(pos)
local objs = minetest.get_objects_inside_radius({x=pos.x, y=pos.y, z=pos.z}, 1)
for _, obj in pairs(objs) do
if obj:is_player() then
local name = obj:get_player_name()
if name ~= self.thrower then
drop_pos = obj:getpos()
end
elseif obj:get_luaentity() ~= nil and
obj:get_luaentity().name ~= "default:snowball_entity" then
drop_pos = obj:getpos()
end
end
if node.name ~= "air" and node.name ~= "ignore" then
for i = 1, 3 do
local p = {x=pos.x, y=pos.y + i, z=pos.z}
local n = minetest.get_node(p)
if n.name == "air" then
drop_pos = vector.new(p)
break
end
end
if not drop_pos then
self.object:remove()
return
end
end
if drop_pos then
node = minetest.get_node(drop_pos)
if node.name == "air" then
local pos_under = vector.subtract(drop_pos, {x=0, y=1, z=0})
node = minetest.get_node(pos_under)
if node.name then
local def = minetest.registered_items[node.name] or {}
if def.buildable_to == true then
minetest.add_node(pos_under, {name="default:snow"})
elseif def.walkable == true then
minetest.add_node(drop_pos, {name="default:snow"})
end
end
elseif node.name then
local def = minetest.registered_items[node.name]
if def and def.buildable_to == true then
minetest.add_node(drop_pos, {name="default:snow"})
end
end
self.object:remove()
end
end
minetest.register_entity("default:snowball_entity", snowball_entity)
})

View File

@ -240,7 +240,7 @@ minetest.register_node("default:snow", {
},
groups = {crumbly = 3, falling_node = 1, snowy = 1, puts_out_fire = 1, misc = 1, speed = -30},
sounds = default.node_sound_snow_defaults(),
on_use = snow_shoot_snowball,
on_use = default.snow_shoot_snowball,
on_construct = function(pos)
pos.y = pos.y - 1
if minetest.get_node(pos).name == "default:dirt_with_grass" then

View File

@ -1,88 +0,0 @@
minetest.register_craftitem("throwing:arrow", {
description = "Arrow",
inventory_image = "throwing_arrow_inv.png",
})
minetest.register_node("throwing:arrow_box", {
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
-- Shaft
{-6.5/17, -1.5/17, -1.5/17, 6.5/17, 1.5/17, 1.5/17},
--Spitze
{-4.5/17, 2.5/17, 2.5/17, -3.5/17, -2.5/17, -2.5/17},
{-8.5/17, 0.5/17, 0.5/17, -6.5/17, -0.5/17, -0.5/17},
--Federn
{6.5/17, 1.5/17, 1.5/17, 7.5/17, 2.5/17, 2.5/17},
{7.5/17, -2.5/17, 2.5/17, 6.5/17, -1.5/17, 1.5/17},
{7.5/17, 2.5/17, -2.5/17, 6.5/17, 1.5/17, -1.5/17},
{6.5/17, -1.5/17, -1.5/17, 7.5/17, -2.5/17, -2.5/17},
{7.5/17, 2.5/17, 2.5/17, 8.5/17, 3.5/17, 3.5/17},
{8.5/17, -3.5/17, 3.5/17, 7.5/17, -2.5/17, 2.5/17},
{8.5/17, 3.5/17, -3.5/17, 7.5/17, 2.5/17, -2.5/17},
{7.5/17, -2.5/17, -2.5/17, 8.5/17, -3.5/17, -3.5/17},
}
},
tiles = {"throwing_arrow.png", "throwing_arrow.png", "throwing_arrow_back.png", "throwing_arrow_front.png", "throwing_arrow_2.png", "throwing_arrow.png"},
groups = {not_in_creative_inventory=1},
})
local THROWING_ARROW_ENTITY={
physical = false,
timer=0,
visual = "wielditem",
visual_size = {x=0.3, y=0.3},
textures = {"throwing:arrow_box"},
lastpos={},
collisionbox = {0,0,0,0,0,0},
}
THROWING_ARROW_ENTITY.on_step = function(self, dtime)
self.timer=self.timer+dtime
local pos = self.object:getpos()
local node = minetest.get_node(pos)
if self.timer>0.2 then
local objs = minetest.get_objects_inside_radius({x=pos.x,y=pos.y,z=pos.z}, 2)
for k, obj in pairs(objs) do
if obj:get_luaentity() ~= nil then
if obj:get_luaentity().name ~= "throwing:arrow_entity" and obj:get_luaentity().name ~= "__builtin:item" then
local damage = 5
obj:punch(self.object, 1.0, {
full_punch_interval=1.0,
damage_groups={fleshy=damage},
}, nil)
self.object:remove()
end
else
local damage = 5
obj:punch(self.object, 1.0, {
full_punch_interval=1.0,
damage_groups={fleshy=damage},
}, nil)
self.object:remove()
end
end
end
if self.lastpos.x~=nil then
if node.name ~= "air" then
minetest.add_item(self.lastpos, 'throwing:arrow')
self.object:remove()
end
end
self.lastpos={x=pos.x, y=pos.y, z=pos.z}
end
minetest.register_entity("throwing:arrow_entity", THROWING_ARROW_ENTITY)
minetest.register_craft({
output = 'throwing:arrow 4',
recipe = {
{'fire:flint_and_steel'},
{'default:stick'},
{'default:paper'}
}
})

View File

@ -1,42 +1,42 @@
arrows = {
local arrows = {
{"throwing:arrow", "throwing:arrow_entity"},
}
local creative = minetest.settings:get_bool("creative_mode")
local wear
local function valid_pos(pos)
if pos then
for _, v in pairs({"x", "y", "z"}) do
if not pos[v] or pos[v] < -32000 or pos[v] > 32000 then
return
end
end
return true
local function arrow_impact(thrower, pos, dir, hit_object)
if hit_object then
local punch_damage = {
full_punch_interval = 1.0,
damage_groups = {fleshy=5},
}
hit_object:punch(thrower, 1.0, punch_damage, dir)
end
minetest.add_item(pos, "throwing:arrow")
end
local throwing_shoot_arrow = function(itemstack, player)
for _,arrow in ipairs(arrows) do
if player:get_inventory():get_stack("main", player:get_wield_index()+1):get_name() == arrow[1] then
if player:get_inventory():get_stack("main",
player:get_wield_index() + 1):get_name() == arrow[1] then
if not creative or not minetest.is_singleplayer()then
player:get_inventory():remove_item("main", arrow[1])
end
local playerpos = player:get_pos()
if not valid_pos(playerpos) then
if not minetest.is_valid_pos(playerpos) then
return
end
local obj = minetest.add_entity({x=playerpos.x,y=playerpos.y+1.5,z=playerpos.z}, arrow[2])
local dir = player:get_look_dir()
obj:setvelocity({x=dir.x*19, y=dir.y*19, z=dir.z*19})
obj:set_acceleration({x=dir.x*-3, y=-10, z=dir.z*-3})
obj:setyaw(player:get_look_yaw()+math.pi)
minetest.sound_play("throwing_sound", {pos=playerpos})
if obj:get_luaentity().player == "" then
obj:get_luaentity().player = player
local obj = minetest.item_throw("throwing:arrow_box", player,
19, -3, arrow_impact)
if obj then
local ent = obj:get_luaentity()
if ent then
minetest.sound_play("throwing_sound", {pos=playerpos})
obj:set_yaw(player:get_look_yaw() + math.pi)
return true
else
obj:remove()
end
end
obj:get_luaentity().node = player:get_inventory():get_stack("main", 1):get_name()
return true
end
end
return false
@ -56,7 +56,7 @@ minetest.register_tool("throwing:bow_arrow", {
inventory_image = "throwing_bow_arrow.png",
groups = {not_in_creative_inventory=1},
on_place = function(itemstack, user, pointed_thing)
wear = itemstack:get_wear()
local wear = itemstack:get_wear()
itemstack:replace("throwing:bow")
itemstack:add_wear(wear)
if throwing_shoot_arrow(itemstack, user, pointed_thing) then
@ -67,7 +67,7 @@ minetest.register_tool("throwing:bow_arrow", {
return itemstack
end,
on_use = function(itemstack, user, pointed_thing)
wear = itemstack:get_wear()
local wear = itemstack:get_wear()
itemstack:replace("throwing:bow")
itemstack:add_wear(wear)
if throwing_shoot_arrow(itemstack, user, pointed_thing) then
@ -75,10 +75,39 @@ minetest.register_tool("throwing:bow_arrow", {
itemstack:add_wear(65535/385)
end
end
return itemstack
return itemstack
end,
})
minetest.register_node("throwing:arrow_box", {
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = {
-- Shaft
{-6.5/17, -1.5/17, -1.5/17, 6.5/17, 1.5/17, 1.5/17},
--Spitze
{-4.5/17, 2.5/17, 2.5/17, -3.5/17, -2.5/17, -2.5/17},
{-8.5/17, 0.5/17, 0.5/17, -6.5/17, -0.5/17, -0.5/17},
--Federn
{6.5/17, 1.5/17, 1.5/17, 7.5/17, 2.5/17, 2.5/17},
{7.5/17, -2.5/17, 2.5/17, 6.5/17, -1.5/17, 1.5/17},
{7.5/17, 2.5/17, -2.5/17, 6.5/17, 1.5/17, -1.5/17},
{6.5/17, -1.5/17, -1.5/17, 7.5/17, -2.5/17, -2.5/17},
{7.5/17, 2.5/17, 2.5/17, 8.5/17, 3.5/17, 3.5/17},
{8.5/17, -3.5/17, 3.5/17, 7.5/17, -2.5/17, 2.5/17},
{8.5/17, 3.5/17, -3.5/17, 7.5/17, 2.5/17, -2.5/17},
{7.5/17, -2.5/17, -2.5/17, 8.5/17, -3.5/17, -3.5/17},
}
},
tiles = {
"throwing_arrow.png", "throwing_arrow.png", "throwing_arrow_back.png",
"throwing_arrow_front.png", "throwing_arrow_2.png", "throwing_arrow.png"
},
groups = {not_in_creative_inventory=1},
})
minetest.register_craft({
output = 'throwing:bow',
recipe = {
@ -88,8 +117,29 @@ minetest.register_craft({
}
})
minetest.register_craftitem("throwing:arrow", {
description = "Arrow",
inventory_image = "throwing_arrow_inv.png",
})
minetest.register_craft({
output = 'throwing:arrow 4',
recipe = {
{'fire:flint_and_steel'},
{'default:stick'},
{'default:paper'}
}
})
-- Legacy support
minetest.register_entity("throwing:arrow_entity", {
is_visible = false,
on_activate = function(self)
self.object:remove()
end
})
minetest.register_alias("throwing:bow_0", "throwing:bow_arrow")
minetest.register_alias("throwing:bow_1", "throwing:bow_arrow")
minetest.register_alias("throwing:bow_2", "throwing:bow_arrow")
dofile(minetest.get_modpath("throwing").."/arrow.lua")