From f46b99772b8fb20b16bd5adcf62737ad496ee408 Mon Sep 17 00:00:00 2001 From: teknomunk Date: Wed, 22 May 2024 19:42:36 +0000 Subject: [PATCH] Create vl_projectile and refactor snowball and partially refactor ender pearl --- mods/ITEMS/mcl_throwing/init.lua | 24 +-- mods/ITEMS/mcl_throwing/mod.conf | 2 +- mods/ITEMS/mcl_throwing/register.lua | 301 +++++++++++++-------------- 3 files changed, 153 insertions(+), 174 deletions(-) diff --git a/mods/ITEMS/mcl_throwing/init.lua b/mods/ITEMS/mcl_throwing/init.lua index c468946dd..599f757f0 100644 --- a/mods/ITEMS/mcl_throwing/init.lua +++ b/mods/ITEMS/mcl_throwing/init.lua @@ -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,10 +68,11 @@ 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 end end -dofile(modpath.."/register.lua") \ No newline at end of file +dofile(modpath.."/register.lua") diff --git a/mods/ITEMS/mcl_throwing/mod.conf b/mods/ITEMS/mcl_throwing/mod.conf index 18248e76f..a33420cc1 100644 --- a/mods/ITEMS/mcl_throwing/mod.conf +++ b/mods/ITEMS/mcl_throwing/mod.conf @@ -1,3 +1,3 @@ name = mcl_throwing -depends = mcl_colors +depends = mcl_colors, vl_projectile optional_depends = mcl_core, mcl_mobitems, doc, mcl_target diff --git a/mods/ITEMS/mcl_throwing/register.lua b/mods/ITEMS/mcl_throwing/register.lua index 43dedd33b..e7e8724be 100644 --- a/mods/ITEMS/mcl_throwing/register.lua +++ b/mods/ITEMS/mcl_throwing/register.lua @@ -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={x=pos.x, y=pos.y, z=pos.z} -- 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 @@ -176,113 +261,9 @@ local function egg_on_step(self, dtime) self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- 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={x=pos.x, y=pos.y, z=pos.z} -- 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.")