Merge branch 'rootyjr_enderman'

This commit is contained in:
Wuzzy 2020-07-11 11:32:50 +02:00
commit 5125f6d739
9 changed files with 301 additions and 78 deletions

@ -1599,6 +1599,7 @@ local monster_attack = function(self)
if self.type ~= "monster" if self.type ~= "monster"
or not damage_enabled or not damage_enabled
or minetest.is_creative_enabled("") or minetest.is_creative_enabled("")
or self.passive
or self.state == "attack" or self.state == "attack"
or day_docile(self) then or day_docile(self) then
return return
@ -2579,6 +2580,14 @@ local falling = function(self, pos)
end end
end end
local teleport = function(self, target)
if self.do_teleport then
if self.do_teleport(self, target) == false then
return
end
end
end
-- deal damage and effects when mob punched -- deal damage and effects when mob punched
local mob_punch = function(self, hitter, tflp, tool_capabilities, dir) local mob_punch = function(self, hitter, tflp, tool_capabilities, dir)
@ -3391,6 +3400,8 @@ minetest.register_entity(name, {
_cmi_is_mob = true, _cmi_is_mob = true,
-- MCL2 extensions -- MCL2 extensions
teleport = teleport,
do_teleport = def.do_teleport,
spawn_class = def.spawn_class, spawn_class = def.spawn_class,
ignores_nametag = def.ignores_nametag or false, ignores_nametag = def.ignores_nametag or false,
rain_damage = def.rain_damage or 0, rain_damage = def.rain_damage or 0,

@ -3,13 +3,28 @@
--made for MC like Survival game --made for MC like Survival game
--License for code WTFPL and otherwise stated in readmes --License for code WTFPL and otherwise stated in readmes
-- ENDERMAN BEHAVIOUR: -- ENDERMAN BEHAVIOUR (OLD):
-- In this game, endermen attack the player on sight, like other monsters do. -- In this game, endermen attack the player on sight, like other monsters do.
-- However, they have a reduced viewing range to make them less dangerous. -- However, they have a reduced viewing range to make them less dangerous.
-- This differs from MC, in which endermen only become hostile when provoked, -- This differs from MC, in which endermen only become hostile when provoked,
-- and they are provoked by looking directly at them. -- and they are provoked by looking directly at them.
-- TODO: Implement MC behaviour. -- TODO: Implement MC behaviour.
-- Rootyjr
-----------------------------
-- implemented ability to detect when seen / break eye contact and aggressive response
-- implemented teleport to avoid arrows.
-- implemented teleport to avoid rain.
-- implemented teleport to chase.
-- added enderman particles.
-- drew mcl_portal_particle1.png
-- drew mcl_portal_particle2.png
-- drew mcl_portal_particle3.png
-- drew mcl_portal_particle4.png
-- drew mcl_portal_particle5.png
-- added rain damage.
-- fixed the grass_with_dirt issue.
local S = minetest.get_translator("mobs_mc") local S = minetest.get_translator("mobs_mc")
--################### --###################
@ -163,13 +178,13 @@ local select_enderman_animation = function(animation_type)
end end
end end
local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false
mobs:register_mob("mobs_mc:enderman", { mobs:register_mob("mobs_mc:enderman", {
-- TODO: Endermen should be classified as passive -- TODO: Endermen should be classified as passive
type = "monster", type = "monster",
spawn_class = "passive", spawn_class = "passive",
passive = false, passive = true,
pathfinding = 1, pathfinding = 1,
hp_min = 40, hp_min = 40,
hp_max = 40, hp_max = 40,
@ -197,7 +212,140 @@ mobs:register_mob("mobs_mc:enderman", {
}, },
animation = select_enderman_animation("normal"), animation = select_enderman_animation("normal"),
_taken_node = "", _taken_node = "",
-- TODO: Teleport enderman on damage, etc.
do_custom = function(self, dtime) do_custom = function(self, dtime)
-- PARTICLE BEHAVIOUR HERE.
local enderpos = self.object:get_pos()
local chanceOfParticle = math.random(0, 1)
if chanceOfParticle == 1 then
minetest.add_particle({
pos = {x=enderpos.x+math.random(-1,1)*math.random()/2,y=enderpos.y+math.random(0,3),z=enderpos.z+math.random(-1,1)*math.random()/2},
velocity = {x=math.random(-.25,.25), y=math.random(-.25,.25), z=math.random(-.25,.25)},
acceleration = {x=math.random(-.5,.5), y=math.random(-.5,.5), z=math.random(-.5,.5)},
expirationtime = math.random(),
size = math.random(),
collisiondetection = true,
vertical = false,
texture = "mcl_portals_particle"..math.random(1, 5)..".png",
})
end
-- RAIN DAMAGE / EVASIVE WARP BEHAVIOUR HERE.
if mcl_weather.state == "rain" or mcl_weather.state == "lightning" then
local damage = true
local enderpos = self.object:get_pos()
enderpos.y = enderpos.y+2.89
local height = {x=enderpos.x, y=enderpos.y+512,z=enderpos.z}
local ray = minetest.raycast(enderpos, height, true)
-- Check for blocks above enderman.
for pointed_thing in ray do
if pointed_thing.type == "node" then
local nn = minetest.get_node(minetest.get_pointed_thing_position(pointed_thing)).name
local def = minetest.registered_nodes[nn]
if (not def) or def.walkable then
-- There's a node in the way. Delete arrow without damage
damage = false
break
end
end
end
if damage == true then
self.state = ""
--rain hurts enderman
self.object:punch(self.object, 1.0, {
full_punch_interval=1.0,
damage_groups={fleshy=self._damage},
}, nil)
--randomly teleport hopefully under something.
self:teleport(nil)
end
end
-- AGRESSIVELY WARP/CHASE PLAYER BEHAVIOUR HERE.
if self.state == "attack" then
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil)
self.state = ""
else
if self.attack then
target = self.attack
pos = target:get_pos()
if pos ~= nil then
if vector.distance(self.object:get_pos(), target:get_pos()) > 10 then
self:teleport(target)
end
end
end
end
end
-- ARROW / DAYTIME PEOPLE AVOIDANCE BEHAVIOUR HERE.
-- Check for arrows and people nearby.
local enderpos = self.object:get_pos()
local objs = minetest.get_objects_inside_radius(enderpos, 4)
for n = 1, #objs do
obj = objs[n]
if obj then
if minetest.is_player(obj) then
-- Warp from players during day.
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil)
end
else
lua = obj:get_luaentity()
if lua then
if lua.name == "mcl_bows:arrow_entity" then
self:teleport(nil)
end
end
end
end
end
-- PROVOKED BEHAVIOUR HERE.
local enderpos = self.object:get_pos()
if self.provoked == "broke_contact" then
self.provoked = "false"
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil)
self.state = ""
else
if self.attack ~= nil then
self.state = 'attack'
end
end
end
-- Check to see if people are near by enough to look at us.
local objs = minetest.get_objects_inside_radius(enderpos, 64)
for n = 1, #objs do
obj = objs[n]
if obj then
if minetest.is_player(obj) then
-- Check if they are looking at us.
local player_pos = obj:get_pos()
local look_dir_not_normalized = obj:get_look_dir()
local look_dir = vector.normalize(look_dir_not_normalized)
local look_pos = vector.new({x = look_dir.x+player_pos.x, y = look_dir.y+player_pos.y + 1.5, z = look_dir.z+player_pos.z}) -- Arbitrary value (1.5) is head level according to player info mod.
-- Cast up to 64 to see if player is looking at enderman.
for n = 1,64,.25 do
local node = minetest.get_node(look_pos)
if node.name ~= "air" then
break
end
if look_pos.x-1<enderpos.x and look_pos.x+1>enderpos.x and look_pos.y-2.89<enderpos.y and look_pos.y-2>enderpos.y and look_pos.z-1<enderpos.z and look_pos.z+1>enderpos.z then
self.provoked = "staring"
self.attack = minetest.get_player_by_name(obj:get_player_name())
break
else
if self.provoked == "staring" then
self.provoked = "broke_contact"
end
end
look_pos.x = look_pos.x + (.25 * look_dir.x)
look_pos.y = look_pos.y + (.25 * look_dir.y)
look_pos.z = look_pos.z + (.25 * look_dir.z)
end
end
end
end
-- TAKE AND PLACE STUFF BEHAVIOUR BELOW.
if not mobs_griefing then if not mobs_griefing then
return return
end end
@ -218,44 +366,47 @@ mobs:register_mob("mobs_mc:enderman", {
local r = pr:next(1, #takable_nodes) local r = pr:next(1, #takable_nodes)
local take_pos = takable_nodes[r] local take_pos = takable_nodes[r]
local node = minetest.get_node(take_pos) local node = minetest.get_node(take_pos)
local dug = minetest.dig_node(take_pos) -- Don't destroy protected stuff.
if dug then if not minetest.is_protected(take_pos, "") then
if mobs_mc.enderman_replace_on_take[node.name] then local dug = minetest.dig_node(take_pos)
self._taken_node = mobs_mc.enderman_replace_on_take[node.name] if dug then
else if mobs_mc.enderman_replace_on_take[node.name] then
self._taken_node = node.name self._taken_node = mobs_mc.enderman_replace_on_take[node.name]
end else
local def = minetest.registered_nodes[self._taken_node] self._taken_node = node.name
-- Update animation and texture accordingly (adds visibly carried block) end
local block_type local def = minetest.registered_nodes[self._taken_node]
-- Cube-shaped -- Update animation and texture accordingly (adds visibly carried block)
if def.drawtype == "normal" or local block_type
def.drawtype == "nodebox" or -- Cube-shaped
def.drawtype == "liquid" or if def.drawtype == "normal" or
def.drawtype == "flowingliquid" or def.drawtype == "nodebox" or
def.drawtype == "glasslike" or def.drawtype == "liquid" or
def.drawtype == "glasslike_framed" or def.drawtype == "flowingliquid" or
def.drawtype == "glasslike_framed_optional" or def.drawtype == "glasslike" or
def.drawtype == "allfaces" or def.drawtype == "glasslike_framed" or
def.drawtype == "allfaces_optional" or def.drawtype == "glasslike_framed_optional" or
def.drawtype == nil then def.drawtype == "allfaces" or
block_type = "cube" def.drawtype == "allfaces_optional" or
elseif def.drawtype == "plantlike" then def.drawtype == nil then
-- Flowers and stuff block_type = "cube"
block_type = "plantlike45" elseif def.drawtype == "plantlike" then
elseif def.drawtype == "airlike" then -- Flowers and stuff
-- Just air block_type = "plantlike45"
block_type = nil elseif def.drawtype == "airlike" then
else -- Just air
-- Fallback for complex drawtypes block_type = nil
block_type = "unknown" else
end -- Fallback for complex drawtypes
self.base_texture = create_enderman_textures(block_type, self._taken_node) block_type = "unknown"
self.object:set_properties({ textures = self.base_texture }) end
self.animation = select_enderman_animation("block") self.base_texture = create_enderman_textures(block_type, self._taken_node)
mobs:set_animation(self, self.animation.current) self.object:set_properties({ textures = self.base_texture })
if def.sounds and def.sounds.dug then self.animation = select_enderman_animation("block")
minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16}, true) mobs:set_animation(self, self.animation.current)
if def.sounds and def.sounds.dug then
minetest.sound_play(def.sounds.dug, {pos = take_pos, max_hear_distance = 16}, true)
end
end end
end end
end end
@ -267,7 +418,8 @@ mobs:register_mob("mobs_mc:enderman", {
local yaw = self.object:get_yaw() local yaw = self.object:get_yaw()
-- Place node at looking direction -- Place node at looking direction
local place_pos = vector.subtract(pos, minetest.facedir_to_dir(minetest.dir_to_facedir(minetest.yaw_to_dir(yaw)))) local place_pos = vector.subtract(pos, minetest.facedir_to_dir(minetest.dir_to_facedir(minetest.yaw_to_dir(yaw))))
if minetest.get_node(place_pos).name == "air" then -- Also check to see if protected.
if minetest.get_node(place_pos).name == "air" and not minetest.is_protected(place_pos, "") then
-- ... but only if there's a free space -- ... but only if there's a free space
local success = minetest.place_node(place_pos, {name = self._taken_node}) local success = minetest.place_node(place_pos, {name = self._taken_node})
if success then if success then
@ -283,34 +435,70 @@ mobs:register_mob("mobs_mc:enderman", {
end end
end end
end, end,
-- TODO: Teleport enderman on damage, etc. do_teleport = function(self, target)
_do_teleport = function(self) if target ~= nil then
-- Attempt to randomly teleport enderman local target_pos = target:get_pos()
local pos = self.object:get_pos() -- Find all solid nodes below air in a 10×10×10 cuboid centered on the target
-- Find all solid nodes below air in a 65×65×65 cuboid centered on the enderman local nodes = minetest.find_nodes_in_area_under_air(vector.subtract(target_pos, 5), vector.add(target_pos, 5), {"group:solid", "group:cracky", "group:crumbly"})
local nodes = minetest.find_nodes_in_area_under_air(vector.subtract(pos, 32), vector.add(pos, 32), {"group:solid", "group:cracky", "group:crumbly"}) local telepos
local telepos if nodes ~= nil then
if #nodes > 0 then if #nodes > 0 then
-- Up to 64 attempts to teleport -- Up to 64 attempts to teleport
for n=1, math.min(64, #nodes) do for n=1, math.min(64, #nodes) do
local r = pr:next(1, #nodes) local r = pr:next(1, #nodes)
local nodepos = nodes[r] local nodepos = nodes[r]
local node_ok = true local node_ok = true
-- Selected node needs to have 3 nodes of free space above -- Selected node needs to have 3 nodes of free space above
for u=1, 3 do for u=1, 3 do
local node = minetest.get_node({x=nodepos.x, y=nodepos.y+u, z=nodepos.z}) local node = minetest.get_node({x=nodepos.x, y=nodepos.y+u, z=nodepos.z})
if minetest.registered_nodes[node.name].walkable then if minetest.registered_nodes[node.name].walkable then
node_ok = false node_ok = false
break break
end
end
if node_ok then
telepos = {x=nodepos.x, y=nodepos.y+1, z=nodepos.z}
end
end
if telepos then
self.object:set_pos(telepos)
end
end
end
else
-- Attempt to randomly teleport enderman
local pos = self.object:get_pos()
-- Up to 8 top-level attempts to teleport
for n=1, 8 do
local node_ok = false
-- We need to add (or subtract) different random numbers to each vector component, so it couldn't be done with a nice single vector.add() or .subtract():
local randomCube = vector.new( pos.x + 8*(pr:next(0,16)-8), pos.y + 8*(pr:next(0,16)-8), pos.z + 8*(pr:next(0,16)-8) )
local nodes = minetest.find_nodes_in_area_under_air(vector.subtract(randomCube, 4), vector.add(randomCube, 4), {"group:solid", "group:cracky", "group:crumbly"})
if nodes ~= nil then
if #nodes > 0 then
-- Up to 8 low-level (in total up to 8*8 = 64) attempts to teleport
for n=1, math.min(8, #nodes) do
local r = pr:next(1, #nodes)
local nodepos = nodes[r]
node_ok = true
for u=1, 3 do
local node = minetest.get_node({x=nodepos.x, y=nodepos.y+u, z=nodepos.z})
if minetest.registered_nodes[node.name].walkable then
node_ok = false
break
end
end
if node_ok then
self.object:set_pos({x=nodepos.x, y=nodepos.y+1, z=nodepos.z})
break
end
end
end end
end end
if node_ok then if node_ok then
telepos = {x=nodepos.x, y=nodepos.y+1, z=nodepos.z} break
end end
end end
if telepos then
self.object:set_pos(telepos)
end
end end
end, end,
on_die = function(self, pos) on_die = function(self, pos)
@ -319,10 +507,20 @@ mobs:register_mob("mobs_mc:enderman", {
minetest.add_item(pos, self._taken_node) minetest.add_item(pos, self._taken_node)
end end
end, end,
do_punch = function(self, hitter, tflp, tool_caps, dir)
-- damage from rain caused by itself so we don't want it to attack itself.
if hitter ~= self.object and hitter ~= nil then
if (minetest.get_timeofday() * 24000) > 5001 and (minetest.get_timeofday() * 24000) < 19000 then
self:teleport(nil)
else
self:teleport(hitter)
self.attack=hitter
self.state="attack"
end
end
end,
water_damage = 8, water_damage = 8,
-- TODO: Increase view range when it detects being seen view_range = 64,
-- Low view range to emulate that behaviour somehow
view_range = 4,
fear_height = 4, fear_height = 4,
attack_type = "dogfight", attack_type = "dogfight",
}) })

@ -177,11 +177,6 @@ mobs_mc.override.enderman_takable = {
"group:enderman_takable", "group:enderman_takable",
} }
mobs_mc.override.enderman_replace_on_take = { mobs_mc.override.enderman_replace_on_take = {
-- Turn covered dirt blocks to normal dirt.
-- This is a workaround because the dirt with grass texture fails when held by the enderman
-- (because of the node coloring).
-- FIXME: Remove these lines as soon we support rendering dirt with grass
["mcl_core:dirt_with_grass"] = "mcl_core:dirt",
} }
mobs_mc.override.misc = { mobs_mc.override.misc = {
totem_fail_nodes = { "mcl_core:void", "mcl_core:realm_barrier" }, totem_fail_nodes = { "mcl_core:void", "mcl_core:realm_barrier" },
@ -200,8 +195,18 @@ for i=1, 6 do
end end
table.insert(ctable, cbackground .. "^" .. last) table.insert(ctable, cbackground .. "^" .. last)
end end
mobs_mc.override.enderman_block_texture_overrides = { mobs_mc.override.enderman_block_texture_overrides = {
["mcl_core:cactus"] = ctable, ["mcl_core:cactus"] = ctable,
-- FIXME: replace colorize colors with colors from palette
["mcl_core:dirt_with_grass"] =
{
"mcl_core_grass_block_top.png^[colorize:green:90",
"default_dirt.png",
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)",
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)",
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)",
"default_dirt.png^(mcl_core_grass_block_side_overlay.png^[colorize:green:90)"}
} }
-- List of nodes on which mobs can spawn -- List of nodes on which mobs can spawn

@ -215,11 +215,20 @@ ARROW_ENTITY.on_step = function(self, dtime)
end end
end end
-- Punch target object -- Punch target object but avoid hurting enderman.
obj:punch(self.object, 1.0, { if lua then
full_punch_interval=1.0, if lua.name ~= "mobs_mc:enderman" then
damage_groups={fleshy=self._damage}, obj:punch(self.object, 1.0, {
}, nil) full_punch_interval=1.0,
damage_groups={fleshy=self._damage},
}, nil)
end
else
obj:punch(self.object, 1.0, {
full_punch_interval=1.0,
damage_groups={fleshy=self._damage},
}, nil)
end
if is_player then if is_player then
if self._shooter and self._shooter:is_player() then if self._shooter and self._shooter:is_player() then

Binary file not shown.

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 602 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 B