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 4dc5d0939c
commit f8d5ed2a7d
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 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,
velocity = velocity,
drag = 3,
})
obj:get_luaentity()._thrower = thrower obj:get_luaentity()._thrower = thrower
end
return obj return obj
end end
@ -71,6 +68,7 @@ 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

View file

@ -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

View file

@ -6,75 +6,6 @@ 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={
physical = false,
timer=0,
textures = {"mcl_throwing_snowball.png"},
visual_size = {x=0.5, y=0.5},
collisionbox = {0,0,0,0,0,0},
pointable = false,
get_staticdata = mcl_throwing.get_staticdata,
on_activate = mcl_throwing.on_activate,
_thrower = nil,
_lastpos={},
}
local egg_ENTITY={
physical = false,
timer=0,
textures = {"mcl_throwing_egg.png"},
visual_size = {x=0.45, y=0.45},
collisionbox = {0,0,0,0,0,0},
pointable = false,
get_staticdata = mcl_throwing.get_staticdata,
on_activate = mcl_throwing.on_activate,
_thrower = nil,
_lastpos={},
}
-- Ender pearl entity
local pearl_ENTITY={
physical = false,
timer=0,
textures = {"mcl_throwing_ender_pearl.png"},
visual_size = {x=0.9, y=0.9},
collisionbox = {0,0,0,0,0,0},
pointable = false,
get_staticdata = mcl_throwing.get_staticdata,
on_activate = mcl_throwing.on_activate,
_lastpos={},
_thrower = nil, -- Player ObjectRef of the player who threw the ender pearl
}
local function check_object_hit(self, pos, dmg)
for _,object in pairs(minetest.get_objects_inside_radius(pos, 1.5)) do
local entity = object:get_luaentity()
if entity
and entity.name ~= self.object:get_luaentity().name then
if object:is_player() and self._thrower ~= object:get_player_name() then
self.object:remove()
return true
elseif (entity.is_mob == true or entity._hittable_by_projectile) and (self._thrower ~= object) then
object:punch(self.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = dmg,
}, nil)
return true
end
end
end
return false
end
local function snowball_particles(pos, vel) local function snowball_particles(pos, vel)
local vel = vector.normalize(vector.multiply(vel, -1)) local vel = vector.normalize(vector.multiply(vel, -1))
minetest.add_particlespawner({ minetest.add_particlespawner({
@ -96,106 +27,102 @@ local function snowball_particles(pos, vel)
texture = "weather_pack_snow_snowflake"..math.random(1,2)..".png", texture = "weather_pack_snow_snowflake"..math.random(1,2)..".png",
}) })
end end
minetest.register_entity("mcl_throwing:snowball_entity", {
physical = false,
timer=0,
textures = {"mcl_throwing_snowball.png"},
visual_size = {x=0.5, y=0.5},
collisionbox = {0,0,0,0,0,0},
pointable = false,
-- Snowball on_step()--> called when snowball is moving. get_staticdata = mcl_throwing.get_staticdata,
local function snowball_on_step(self, dtime) on_activate = mcl_throwing.on_activate,
self.timer = self.timer + dtime _thrower = nil,
local pos = self.object:get_pos() _lastpos = nil,
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 _vl_projectile = {
if self._lastpos.x~=nil then behaviors = {
if (def and def.walkable) or not def then vl_projectile.collides_with_solids,
minetest.sound_play("mcl_throwing_snowball_impact_hard", { pos = pos, max_hear_distance=16, gain=0.7 }, true) vl_projectile.collides_with_entities,
snowball_particles(self._lastpos, vel) },
self.object:remove() on_collide_with_solid = function(self, pos, node)
if mod_target and node.name == "mcl_target:target_off" then if mod_target and node.name == "mcl_target:target_off" then
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
end 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 snowball_particles(self._last_pos or pos, self.object:get_velocity())
local function egg_on_step(self, dtime) end,
self.timer = self.timer + dtime on_collide_with_entity = function(self, pos, entity)
local pos = self.object:get_pos() snowball_particles(self._last_pos or pos, self.object:get_velocity())
local node = minetest.get_node(pos) end,
local def = minetest.registered_nodes[node.name] 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,
})
-- Destroy when hitting a solid node or entity, with chance to spawn chicks local egg_ENTITY={
if (def and def.walkable) or not def or check_object_hit(self, pos, 0) then physical = false,
-- If egg has just been thrown, use current position timer=0,
if not self._lastpos.x then textures = {"mcl_throwing_egg.png"},
self._lastpos = pos visual_size = {x=0.45, y=0.45},
end collisionbox = {0,0,0,0,0,0},
-- 1/8 chance to spawn a chick pointable = false,
-- FIXME: Chicks have a quite good chance to spawn in walls
if math.random(1,8) == 1 then
mcl_mobs.spawn_child(self._lastpos, "mobs_mc:chicken")
-- BONUS ROUND: 1/32 chance to spawn 3 additional chicks get_staticdata = mcl_throwing.get_staticdata,
if math.random(1,32) == 1 then on_activate = mcl_throwing.on_activate,
local offsets = { _thrower = nil,
{ x=0.7, y=0, z=0 },
{ x=-0.7, y=0, z=-0.7 }, _lastpos={},
{ x=-0.7, y=0, z=0.7 }, }
}
for o=1, 3 do -- Ender pearl entity
local pos = vector.add(self._lastpos, offsets[o]) minetest.register_entity("mcl_throwing:ender_pearl_entity",{
mcl_mobs.spawn_child(pos, "mobs_mc:chicken") physical = false,
end timer=0,
end textures = {"mcl_throwing_ender_pearl.png"},
end visual_size = {x=0.9, y=0.9},
minetest.sound_play("mcl_throwing_egg_impact", { pos = self.object:get_pos(), max_hear_distance=10, gain=0.5 }, true) collisionbox = {0,0,0,0,0,0},
self.object:remove() pointable = false,
get_staticdata = mcl_throwing.get_staticdata,
on_activate = mcl_throwing.on_activate,
_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 if mod_target and node.name == "mcl_target:target_off" then
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
end end
return
end
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 if node.name == "ignore" then
self.object:remove() -- FIXME: This also means the player loses an ender pearl for throwing into unloaded areas
-- Activate when hitting a solid node or a plant return
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 end
-- Make sure we have a reference to the player
local player = self._thrower and minetest.get_player_by_name(self._thrower) local player = self._thrower and minetest.get_player_by_name(self._thrower)
if player then if not player then return end
-- Teleport and hurt player -- Teleport and hurt player
-- First determine good teleport position -- First determine good teleport position
local dir = {x=0, y=0, z=0} local dir = {x=0, y=0, z=0}
local v = self.object:get_velocity() local v = self.object:get_velocity()
if walkable then if node_def and node_def.walkable then
local vc = table.copy(v) -- vector for calculating local vc = table.copy(v) -- vector for calculating
-- Node is walkable, we have to find a place somewhere outside of that node -- Node is walkable, we have to find a place somewhere outside of that node
vc = vector.normalize(vc) vc = vector.normalize(vc)
@ -255,26 +182,80 @@ local function pearl_on_step(self, dtime)
if r == 1 then if r == 1 then
minetest.add_entity(oldpos, "mobs_mc:endermite") minetest.add_entity(oldpos, "mobs_mc:endermite")
end end
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
local entity = object:get_luaentity()
if entity
and entity.name ~= self.object:get_luaentity().name then
if object:is_player() and self._thrower ~= object:get_player_name() then
self.object:remove()
return true
elseif (entity.is_mob == true or entity._hittable_by_projectile) and (self._thrower ~= object) then
object:punch(self.object, 1.0, {
full_punch_interval = 1.0,
damage_groups = dmg,
}, nil)
return true
end
end
end
return false
end
-- Movement function of egg
local function egg_on_step(self, dtime)
self.timer = self.timer + dtime
local pos = self.object:get_pos()
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
-- Destroy when hitting a solid node or entity, with chance to spawn chicks
if (def and def.walkable) or not def or check_object_hit(self, pos, 0) then
-- If egg has just been thrown, use current position
if not self._lastpos.x then
self._lastpos = pos
end
-- 1/8 chance to spawn a chick
-- FIXME: Chicks have a quite good chance to spawn in walls
if math.random(1,8) == 1 then
mcl_mobs.spawn_child(self._lastpos, "mobs_mc:chicken")
-- BONUS ROUND: 1/32 chance to spawn 3 additional chicks
if math.random(1,32) == 1 then
local offsets = {
{ x=0.7, y=0, z=0 },
{ x=-0.7, y=0, z=-0.7 },
{ x=-0.7, y=0, z=0.7 },
}
for o=1, 3 do
local pos = vector.add(self._lastpos, offsets[o])
mcl_mobs.spawn_child(pos, "mobs_mc:chicken")
end
end
end
minetest.sound_play("mcl_throwing_egg_impact", { pos = self.object:get_pos(), max_hear_distance=10, gain=0.5 }, true)
self.object:remove() self.object:remove()
if mod_target and node.name == "mcl_target:target_off" then if mod_target and node.name == "mcl_target:target_off" then
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
end end
return return
end end
end
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
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.")