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 b136cbf9bb
commit 58df082b68
3 changed files with 153 additions and 174 deletions

View File

@ -17,21 +17,18 @@ function mcl_throwing.register_throwable_object(name, entity, velocity)
end
function mcl_throwing.throw(throw_item, pos, dir, velocity, thrower)
if velocity == nil then
velocity = velocities[throw_item]
end
if velocity == nil then
velocity = 22
end
velocity = velocity or velocities[throw_item] or 22
minetest.sound_play("mcl_throwing_throw", {pos=pos, gain=0.4, max_hear_distance=16}, true)
local itemstring = ItemStack(throw_item):get_name()
local obj = minetest.add_entity(pos, entity_mapping[itemstring])
obj:set_velocity({x=dir.x*velocity, y=dir.y*velocity, z=dir.z*velocity})
obj:set_acceleration({x=dir.x*-3, y=-GRAVITY, z=dir.z*-3})
if thrower then
obj:get_luaentity()._thrower = thrower
end
local obj = vl_projectile.create(entity_mapping[itemstring], {
pos = pos,
owner = thrower,
dir = dir,
velocity = velocity,
drag = 3,
})
obj:get_luaentity()._thrower = thrower
return obj
end
@ -71,6 +68,7 @@ end
function mcl_throwing.on_activate(self, staticdata, dtime_s)
local data = minetest.deserialize(staticdata)
self._staticdata = data
if data then
self._lastpos = data._lastpos
self._thrower = data._thrower

View File

@ -1,3 +1,3 @@
name = mcl_throwing
depends = mcl_colors
depends = mcl_colors, vl_projectile
optional_depends = mcl_core, mcl_mobitems, doc, mcl_target

View File

@ -6,7 +6,28 @@ local vector = vector
local mod_target = minetest.get_modpath("mcl_target")
-- 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,
timer=0,
textures = {"mcl_throwing_snowball.png"},
@ -17,9 +38,31 @@ local snowball_ENTITY={
get_staticdata = mcl_throwing.get_staticdata,
on_activate = mcl_throwing.on_activate,
_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={
physical = false,
@ -37,7 +80,7 @@ local egg_ENTITY={
}
-- Ender pearl entity
local pearl_ENTITY={
minetest.register_entity("mcl_throwing:ender_pearl_entity",{
physical = false,
timer=0,
textures = {"mcl_throwing_ender_pearl.png"},
@ -50,7 +93,100 @@ local pearl_ENTITY={
_lastpos={},
_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)
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
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
local function egg_on_step(self, 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
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
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:ender_pearl_entity", pearl_ENTITY)
local how_to_throw = S("Use the punch key to throw.")