mirror of
https://git.minetest.land/VoxeLibre/VoxeLibre.git
synced 2024-11-17 08:31:08 +01:00
Create vl_projectile and refactor snowball and partially refactor ender pearl
This commit is contained in:
parent
4dc5d0939c
commit
f8d5ed2a7d
3 changed files with 153 additions and 174 deletions
|
@ -17,21 +17,18 @@ function mcl_throwing.register_throwable_object(name, entity, velocity)
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_throwing.throw(throw_item, pos, dir, velocity, thrower)
|
function mcl_throwing.throw(throw_item, pos, dir, velocity, thrower)
|
||||||
if velocity == nil then
|
velocity = velocity or velocities[throw_item] or 22
|
||||||
velocity = velocities[throw_item]
|
|
||||||
end
|
|
||||||
if velocity == nil then
|
|
||||||
velocity = 22
|
|
||||||
end
|
|
||||||
minetest.sound_play("mcl_throwing_throw", {pos=pos, gain=0.4, max_hear_distance=16}, true)
|
minetest.sound_play("mcl_throwing_throw", {pos=pos, gain=0.4, max_hear_distance=16}, true)
|
||||||
|
|
||||||
local itemstring = ItemStack(throw_item):get_name()
|
local itemstring = ItemStack(throw_item):get_name()
|
||||||
local obj = minetest.add_entity(pos, entity_mapping[itemstring])
|
local obj = vl_projectile.create(entity_mapping[itemstring], {
|
||||||
obj:set_velocity({x=dir.x*velocity, y=dir.y*velocity, z=dir.z*velocity})
|
pos = pos,
|
||||||
obj:set_acceleration({x=dir.x*-3, y=-GRAVITY, z=dir.z*-3})
|
owner = thrower,
|
||||||
if thrower then
|
dir = dir,
|
||||||
obj:get_luaentity()._thrower = thrower
|
velocity = velocity,
|
||||||
end
|
drag = 3,
|
||||||
|
})
|
||||||
|
obj:get_luaentity()._thrower = thrower
|
||||||
return obj
|
return obj
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -71,10 +68,11 @@ end
|
||||||
|
|
||||||
function mcl_throwing.on_activate(self, staticdata, dtime_s)
|
function mcl_throwing.on_activate(self, staticdata, dtime_s)
|
||||||
local data = minetest.deserialize(staticdata)
|
local data = minetest.deserialize(staticdata)
|
||||||
|
self._staticdata = data
|
||||||
if data then
|
if data then
|
||||||
self._lastpos = data._lastpos
|
self._lastpos = data._lastpos
|
||||||
self._thrower = data._thrower
|
self._thrower = data._thrower
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
dofile(modpath.."/register.lua")
|
dofile(modpath.."/register.lua")
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
name = mcl_throwing
|
name = mcl_throwing
|
||||||
depends = mcl_colors
|
depends = mcl_colors, vl_projectile
|
||||||
optional_depends = mcl_core, mcl_mobitems, doc, mcl_target
|
optional_depends = mcl_core, mcl_mobitems, doc, mcl_target
|
||||||
|
|
|
@ -6,7 +6,28 @@ local vector = vector
|
||||||
local mod_target = minetest.get_modpath("mcl_target")
|
local mod_target = minetest.get_modpath("mcl_target")
|
||||||
|
|
||||||
-- The snowball entity
|
-- The snowball entity
|
||||||
local snowball_ENTITY={
|
local function snowball_particles(pos, vel)
|
||||||
|
local vel = vector.normalize(vector.multiply(vel, -1))
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 20,
|
||||||
|
time = 0.001,
|
||||||
|
minpos = pos,
|
||||||
|
maxpos = pos,
|
||||||
|
minvel = vector.add({x=-2, y=3, z=-2}, vel),
|
||||||
|
maxvel = vector.add({x=2, y=5, z=2}, vel),
|
||||||
|
minacc = {x=0, y=-9.81, z=0},
|
||||||
|
maxacc = {x=0, y=-9.81, z=0},
|
||||||
|
minexptime = 1,
|
||||||
|
maxexptime = 3,
|
||||||
|
minsize = 0.7,
|
||||||
|
maxsize = 0.7,
|
||||||
|
collisiondetection = true,
|
||||||
|
collision_removal = true,
|
||||||
|
object_collision = false,
|
||||||
|
texture = "weather_pack_snow_snowflake"..math.random(1,2)..".png",
|
||||||
|
})
|
||||||
|
end
|
||||||
|
minetest.register_entity("mcl_throwing:snowball_entity", {
|
||||||
physical = false,
|
physical = false,
|
||||||
timer=0,
|
timer=0,
|
||||||
textures = {"mcl_throwing_snowball.png"},
|
textures = {"mcl_throwing_snowball.png"},
|
||||||
|
@ -17,9 +38,31 @@ local snowball_ENTITY={
|
||||||
get_staticdata = mcl_throwing.get_staticdata,
|
get_staticdata = mcl_throwing.get_staticdata,
|
||||||
on_activate = mcl_throwing.on_activate,
|
on_activate = mcl_throwing.on_activate,
|
||||||
_thrower = nil,
|
_thrower = nil,
|
||||||
|
_lastpos = nil,
|
||||||
|
|
||||||
_lastpos={},
|
_vl_projectile = {
|
||||||
}
|
behaviors = {
|
||||||
|
vl_projectile.collides_with_solids,
|
||||||
|
vl_projectile.collides_with_entities,
|
||||||
|
},
|
||||||
|
on_collide_with_solid = function(self, pos, node)
|
||||||
|
if mod_target and node.name == "mcl_target:target_off" then
|
||||||
|
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
||||||
|
end
|
||||||
|
|
||||||
|
snowball_particles(self._last_pos or pos, self.object:get_velocity())
|
||||||
|
end,
|
||||||
|
on_collide_with_entity = function(self, pos, entity)
|
||||||
|
snowball_particles(self._last_pos or pos, self.object:get_velocity())
|
||||||
|
end,
|
||||||
|
sounds = {
|
||||||
|
on_solid_collision = {"mcl_throwing_snowball_impact_hard", { max_hear_distance=16, gain=0.7 }, true},
|
||||||
|
on_entity_collision = {"mcl_throwing_snowball_impact_soft", { max_hear_distance=16, gain=0.7 }, true}
|
||||||
|
},
|
||||||
|
damage_groups = { snowball_vulnerable = 3 },
|
||||||
|
},
|
||||||
|
on_step = vl_projectile.update_projectile,
|
||||||
|
})
|
||||||
|
|
||||||
local egg_ENTITY={
|
local egg_ENTITY={
|
||||||
physical = false,
|
physical = false,
|
||||||
|
@ -37,7 +80,7 @@ local egg_ENTITY={
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Ender pearl entity
|
-- Ender pearl entity
|
||||||
local pearl_ENTITY={
|
minetest.register_entity("mcl_throwing:ender_pearl_entity",{
|
||||||
physical = false,
|
physical = false,
|
||||||
timer=0,
|
timer=0,
|
||||||
textures = {"mcl_throwing_ender_pearl.png"},
|
textures = {"mcl_throwing_ender_pearl.png"},
|
||||||
|
@ -50,7 +93,100 @@ local pearl_ENTITY={
|
||||||
|
|
||||||
_lastpos={},
|
_lastpos={},
|
||||||
_thrower = nil, -- Player ObjectRef of the player who threw the ender pearl
|
_thrower = nil, -- Player ObjectRef of the player who threw the ender pearl
|
||||||
}
|
_vl_projectile = {
|
||||||
|
behaviors = {
|
||||||
|
vl_projectile.collides_with_solids,
|
||||||
|
},
|
||||||
|
collides_with = {
|
||||||
|
"mcl_core:vine", "mcl_core:deadbush",
|
||||||
|
"group:flower", "group:sapling",
|
||||||
|
"group:plant", "group:mushroom",
|
||||||
|
},
|
||||||
|
on_collide_with_solid = function(self, pos, node)
|
||||||
|
if mod_target and node.name == "mcl_target:target_off" then
|
||||||
|
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
||||||
|
end
|
||||||
|
|
||||||
|
if node.name == "ignore" then
|
||||||
|
-- FIXME: This also means the player loses an ender pearl for throwing into unloaded areas
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make sure we have a reference to the player
|
||||||
|
local player = self._thrower and minetest.get_player_by_name(self._thrower)
|
||||||
|
if not player then return end
|
||||||
|
|
||||||
|
-- Teleport and hurt player
|
||||||
|
|
||||||
|
-- First determine good teleport position
|
||||||
|
local dir = {x=0, y=0, z=0}
|
||||||
|
|
||||||
|
local v = self.object:get_velocity()
|
||||||
|
if node_def and node_def.walkable then
|
||||||
|
local vc = table.copy(v) -- vector for calculating
|
||||||
|
-- Node is walkable, we have to find a place somewhere outside of that node
|
||||||
|
vc = vector.normalize(vc)
|
||||||
|
|
||||||
|
-- Zero-out the two axes with a lower absolute value than
|
||||||
|
-- the axis with the strongest force
|
||||||
|
local lv, ld
|
||||||
|
lv, ld = math.abs(vc.y), "y"
|
||||||
|
if math.abs(vc.x) > lv then
|
||||||
|
lv, ld = math.abs(vc.x), "x"
|
||||||
|
end
|
||||||
|
if math.abs(vc.z) > lv then
|
||||||
|
ld = "z" --math.abs(vc.z)
|
||||||
|
end
|
||||||
|
if ld ~= "x" then vc.x = 0 end
|
||||||
|
if ld ~= "y" then vc.y = 0 end
|
||||||
|
if ld ~= "z" then vc.z = 0 end
|
||||||
|
|
||||||
|
-- Final tweaks to the teleporting pos, based on direction
|
||||||
|
-- Impact from the side
|
||||||
|
dir.x = vc.x * -1
|
||||||
|
dir.z = vc.z * -1
|
||||||
|
|
||||||
|
-- Special case: top or bottom of node
|
||||||
|
if vc.y > 0 then
|
||||||
|
-- We need more space when impact is from below
|
||||||
|
dir.y = -2.3
|
||||||
|
elseif vc.y < 0 then
|
||||||
|
-- Standing on top
|
||||||
|
dir.y = 0.5
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- If node was not walkable, no modification to pos is made.
|
||||||
|
|
||||||
|
-- Final teleportation position
|
||||||
|
local telepos = vector.add(pos, dir)
|
||||||
|
local telenode = minetest.get_node(telepos)
|
||||||
|
|
||||||
|
--[[ It may be possible that telepos is walkable due to the algorithm.
|
||||||
|
Especially when the ender pearl is faster horizontally than vertical.
|
||||||
|
This applies final fixing, just to be sure we're not in a walkable node ]]
|
||||||
|
if not minetest.registered_nodes[telenode.name] or minetest.registered_nodes[telenode.name].walkable then
|
||||||
|
if v.y < 0 then
|
||||||
|
telepos.y = telepos.y + 0.5
|
||||||
|
else
|
||||||
|
telepos.y = telepos.y - 2.3
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local oldpos = player:get_pos()
|
||||||
|
-- Teleport and hurt player
|
||||||
|
player:set_pos(telepos)
|
||||||
|
player:set_hp(player:get_hp() - 5, { type = "fall", from = "mod" })
|
||||||
|
|
||||||
|
-- 5% chance to spawn endermite at the player's origin
|
||||||
|
local r = math.random(1,20)
|
||||||
|
if r == 1 then
|
||||||
|
minetest.add_entity(oldpos, "mobs_mc:endermite")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
},
|
||||||
|
|
||||||
|
on_step = vl_projectile.update_projectile,
|
||||||
|
})
|
||||||
|
|
||||||
local function check_object_hit(self, pos, dmg)
|
local function check_object_hit(self, pos, dmg)
|
||||||
for _,object in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
for _,object in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
|
||||||
|
@ -75,57 +211,6 @@ local function check_object_hit(self, pos, dmg)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
local function snowball_particles(pos, vel)
|
|
||||||
local vel = vector.normalize(vector.multiply(vel, -1))
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = 20,
|
|
||||||
time = 0.001,
|
|
||||||
minpos = pos,
|
|
||||||
maxpos = pos,
|
|
||||||
minvel = vector.add({x=-2, y=3, z=-2}, vel),
|
|
||||||
maxvel = vector.add({x=2, y=5, z=2}, vel),
|
|
||||||
minacc = {x=0, y=-9.81, z=0},
|
|
||||||
maxacc = {x=0, y=-9.81, z=0},
|
|
||||||
minexptime = 1,
|
|
||||||
maxexptime = 3,
|
|
||||||
minsize = 0.7,
|
|
||||||
maxsize = 0.7,
|
|
||||||
collisiondetection = true,
|
|
||||||
collision_removal = true,
|
|
||||||
object_collision = false,
|
|
||||||
texture = "weather_pack_snow_snowflake"..math.random(1,2)..".png",
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Snowball on_step()--> called when snowball is moving.
|
|
||||||
local function snowball_on_step(self, dtime)
|
|
||||||
self.timer = self.timer + dtime
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local vel = self.object:get_velocity()
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
local def = minetest.registered_nodes[node.name]
|
|
||||||
|
|
||||||
-- Destroy when hitting a solid node
|
|
||||||
if self._lastpos.x~=nil then
|
|
||||||
if (def and def.walkable) or not def then
|
|
||||||
minetest.sound_play("mcl_throwing_snowball_impact_hard", { pos = pos, max_hear_distance=16, gain=0.7 }, true)
|
|
||||||
snowball_particles(self._lastpos, vel)
|
|
||||||
self.object:remove()
|
|
||||||
if mod_target and node.name == "mcl_target:target_off" then
|
|
||||||
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if check_object_hit(self, pos, {snowball_vulnerable = 3}) then
|
|
||||||
minetest.sound_play("mcl_throwing_snowball_impact_soft", { pos = pos, max_hear_distance=16, gain=0.7 }, true)
|
|
||||||
snowball_particles(pos, vel)
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
self._lastpos = pos -- Set _lastpos-->Node will be added at last pos outside the node
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Movement function of egg
|
-- Movement function of egg
|
||||||
local function egg_on_step(self, dtime)
|
local function egg_on_step(self, dtime)
|
||||||
self.timer = self.timer + dtime
|
self.timer = self.timer + dtime
|
||||||
|
@ -168,113 +253,9 @@ local function egg_on_step(self, dtime)
|
||||||
self._lastpos = pos -- Set lastpos-->Node will be added at last pos outside the node
|
self._lastpos = pos -- Set lastpos-->Node will be added at last pos outside the node
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Movement function of ender pearl
|
|
||||||
local function pearl_on_step(self, dtime)
|
|
||||||
self.timer = self.timer + dtime
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
pos.y = math.floor(pos.y)
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
local nn = node.name
|
|
||||||
local def = minetest.registered_nodes[node.name]
|
|
||||||
|
|
||||||
-- Destroy when hitting a solid node
|
|
||||||
if self._lastpos.x~=nil then
|
|
||||||
local walkable = (def and def.walkable)
|
|
||||||
|
|
||||||
-- No teleport for hitting ignore for now. Otherwise the player could get stuck.
|
|
||||||
-- FIXME: This also means the player loses an ender pearl for throwing into unloaded areas
|
|
||||||
if node.name == "ignore" then
|
|
||||||
self.object:remove()
|
|
||||||
-- Activate when hitting a solid node or a plant
|
|
||||||
elseif walkable or nn == "mcl_core:vine" or nn == "mcl_core:deadbush" or minetest.get_item_group(nn, "flower") ~= 0 or minetest.get_item_group(nn, "sapling") ~= 0 or minetest.get_item_group(nn, "plant") ~= 0 or minetest.get_item_group(nn, "mushroom") ~= 0 or not def then
|
|
||||||
local player = self._thrower and minetest.get_player_by_name(self._thrower)
|
|
||||||
if player then
|
|
||||||
-- Teleport and hurt player
|
|
||||||
|
|
||||||
-- First determine good teleport position
|
|
||||||
local dir = {x=0, y=0, z=0}
|
|
||||||
|
|
||||||
local v = self.object:get_velocity()
|
|
||||||
if walkable then
|
|
||||||
local vc = table.copy(v) -- vector for calculating
|
|
||||||
-- Node is walkable, we have to find a place somewhere outside of that node
|
|
||||||
vc = vector.normalize(vc)
|
|
||||||
|
|
||||||
-- Zero-out the two axes with a lower absolute value than
|
|
||||||
-- the axis with the strongest force
|
|
||||||
local lv, ld
|
|
||||||
lv, ld = math.abs(vc.y), "y"
|
|
||||||
if math.abs(vc.x) > lv then
|
|
||||||
lv, ld = math.abs(vc.x), "x"
|
|
||||||
end
|
|
||||||
if math.abs(vc.z) > lv then
|
|
||||||
ld = "z" --math.abs(vc.z)
|
|
||||||
end
|
|
||||||
if ld ~= "x" then vc.x = 0 end
|
|
||||||
if ld ~= "y" then vc.y = 0 end
|
|
||||||
if ld ~= "z" then vc.z = 0 end
|
|
||||||
|
|
||||||
-- Final tweaks to the teleporting pos, based on direction
|
|
||||||
-- Impact from the side
|
|
||||||
dir.x = vc.x * -1
|
|
||||||
dir.z = vc.z * -1
|
|
||||||
|
|
||||||
-- Special case: top or bottom of node
|
|
||||||
if vc.y > 0 then
|
|
||||||
-- We need more space when impact is from below
|
|
||||||
dir.y = -2.3
|
|
||||||
elseif vc.y < 0 then
|
|
||||||
-- Standing on top
|
|
||||||
dir.y = 0.5
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-- If node was not walkable, no modification to pos is made.
|
|
||||||
|
|
||||||
-- Final teleportation position
|
|
||||||
local telepos = vector.add(pos, dir)
|
|
||||||
local telenode = minetest.get_node(telepos)
|
|
||||||
|
|
||||||
--[[ It may be possible that telepos is walkable due to the algorithm.
|
|
||||||
Especially when the ender pearl is faster horizontally than vertical.
|
|
||||||
This applies final fixing, just to be sure we're not in a walkable node ]]
|
|
||||||
if not minetest.registered_nodes[telenode.name] or minetest.registered_nodes[telenode.name].walkable then
|
|
||||||
if v.y < 0 then
|
|
||||||
telepos.y = telepos.y + 0.5
|
|
||||||
else
|
|
||||||
telepos.y = telepos.y - 2.3
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local oldpos = player:get_pos()
|
|
||||||
-- Teleport and hurt player
|
|
||||||
player:set_pos(telepos)
|
|
||||||
player:set_hp(player:get_hp() - 5, { type = "fall", from = "mod" })
|
|
||||||
|
|
||||||
-- 5% chance to spawn endermite at the player's origin
|
|
||||||
local r = math.random(1,20)
|
|
||||||
if r == 1 then
|
|
||||||
minetest.add_entity(oldpos, "mobs_mc:endermite")
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
self.object:remove()
|
|
||||||
if mod_target and node.name == "mcl_target:target_off" then
|
|
||||||
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
self._lastpos = pos -- Set lastpos-->Node will be added at last pos outside the node
|
|
||||||
end
|
|
||||||
|
|
||||||
snowball_ENTITY.on_step = snowball_on_step
|
|
||||||
egg_ENTITY.on_step = egg_on_step
|
egg_ENTITY.on_step = egg_on_step
|
||||||
pearl_ENTITY.on_step = pearl_on_step
|
|
||||||
|
|
||||||
minetest.register_entity("mcl_throwing:snowball_entity", snowball_ENTITY)
|
|
||||||
minetest.register_entity("mcl_throwing:egg_entity", egg_ENTITY)
|
minetest.register_entity("mcl_throwing:egg_entity", egg_ENTITY)
|
||||||
minetest.register_entity("mcl_throwing:ender_pearl_entity", pearl_ENTITY)
|
|
||||||
|
|
||||||
|
|
||||||
local how_to_throw = S("Use the punch key to throw.")
|
local how_to_throw = S("Use the punch key to throw.")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue