Create vl_projectile and refactor snowball and partially refactor ender pearl

This commit is contained in:
teknomunk 2024-05-22 19:42:36 +00:00
parent 972185907f
commit 0528d20759
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.")