diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 5872ce742..d37bb8785 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -612,7 +612,7 @@ function mcl_util.deal_damage(target, damage, mcl_reason) end return end - end + elseif not target:is_player() then return end local is_immortal = target:get_armor_groups().immortal or 0 if is_immortal>0 then diff --git a/mods/ENTITIES/mcl_mobs/combat.lua b/mods/ENTITIES/mcl_mobs/combat.lua index 4b3393e36..7775c1b8e 100644 --- a/mods/ENTITIES/mcl_mobs/combat.lua +++ b/mods/ENTITIES/mcl_mobs/combat.lua @@ -21,6 +21,8 @@ local function atan(x) end end +mcl_mobs.effect_functions = {} + -- check if daytime and also if mob is docile during daylight hours function mob_class:day_docile() @@ -382,7 +384,8 @@ function mob_class:monster_attack() -- find specific mob to attack, failing that attack player/npc/animal if specific_attack(self.specific_attack, name) and (type == "player" or ( type == "npc" and self.attack_npcs ) - or (type == "animal" and self.attack_animals == true)) then + or (type == "animal" and self.attack_animals == true) + or (self.extra_hostile and not self.attack_exception(player))) then p = player:get_pos() sp = s @@ -1103,6 +1106,11 @@ function mob_class:do_states_attack (dtime) full_punch_interval = 1.0, damage_groups = {fleshy = self.damage} }, nil) + if self.dealt_effect then + mcl_mobs.effect_functions[self.dealt_effect.name]( + self.attack, self.dealt_effect.factor, self.dealt_effect.dur + ) + end end else self.custom_attack(self, p) @@ -1209,6 +1217,9 @@ function mob_class:do_states_attack (dtime) end end end + + elseif self.attack_type == "custom" and self.attack_state then + self.attack_state(self, dtime) else end diff --git a/mods/ENTITIES/mcl_mobs/init.lua b/mods/ENTITIES/mcl_mobs/init.lua index 62100c627..cb9bf040a 100644 --- a/mods/ENTITIES/mcl_mobs/init.lua +++ b/mods/ENTITIES/mcl_mobs/init.lua @@ -297,6 +297,7 @@ function mcl_mobs.register_mob(name, def) return false, true, {} end, do_punch = def.do_punch, + deal_damage = def.deal_damage, on_breed = def.on_breed, on_grown = def.on_grown, on_pick_up = def.on_pick_up, @@ -311,8 +312,15 @@ function mcl_mobs.register_mob(name, def) return self:mob_activate(staticdata, def, dtime) end, + attack_state = def.attack_state, harmed_by_heal = def.harmed_by_heal, - on_lightning_strike = def.on_lightning_strike + is_boss = def.is_boss, + dealt_effect = def.dealt_effect, + on_lightning_strike = def.on_lightning_strike, + extra_hostile = def.extra_hostile, + attack_exception = def.attack_exception or function(p) return false end, + + _spawner = def._spawner, } minetest.register_entity(name, setmetatable(final_def,mcl_mobs.mob_class_meta)) @@ -343,9 +351,10 @@ function mcl_mobs.register_arrow(name, def) collisionbox = {0, 0, 0, 0, 0, 0}, -- remove box around arrows timer = 0, switch = 0, + _lifetime = def._lifetime or 150, owner_id = def.owner_id, rotate = def.rotate, - on_punch = function(self) + on_punch = def.on_punch or function(self) local vel = self.object:get_velocity() self.object:set_velocity({x=vel.x * -1, y=vel.y * -1, z=vel.z * -1}) end, @@ -362,7 +371,7 @@ function mcl_mobs.register_arrow(name, def) local pos = self.object:get_pos() if self.switch == 0 - or self.timer > 150 + or self.timer > self._lifetime or not within_limits(pos, 0) then mcl_burning.extinguish(self.object) self.object:remove(); diff --git a/mods/ENTITIES/mcl_mobs/physics.lua b/mods/ENTITIES/mcl_mobs/physics.lua index 341e28984..7ffa62ffe 100644 --- a/mods/ENTITIES/mcl_mobs/physics.lua +++ b/mods/ENTITIES/mcl_mobs/physics.lua @@ -681,6 +681,9 @@ function mob_class:do_env_damage() -- don't fall when on ignore, just stand still if self.standing_in == "ignore" then self.object:set_velocity({x = 0, y = 0, z = 0}) + -- wither rose effect + elseif self.standing_in == "mcl_flowers:wither_rose" then + mcl_potions.withering_func(self.object, 1, 2) end local nodef = minetest.registered_nodes[self.standing_in] diff --git a/mods/ENTITIES/mcl_wither_spawning/init.lua b/mods/ENTITIES/mcl_wither_spawning/init.lua index 74cd1e6e2..2a7a9c595 100644 --- a/mods/ENTITIES/mcl_wither_spawning/init.lua +++ b/mods/ENTITIES/mcl_wither_spawning/init.lua @@ -2,6 +2,9 @@ local dim = {"x", "z"} local modpath = minetest.get_modpath(minetest.get_current_modname()) +local anti_troll = minetest.settings:get_bool("wither_anti_troll_measures", false) +local peaceful = minetest.settings:get_bool("only_peaceful_mobs", false) + local function load_schem(filename) local file = io.open(modpath .. "/schems/" .. filename, "r") local data = minetest.deserialize(file:read()) @@ -9,6 +12,14 @@ local function load_schem(filename) return data end +local wboss_overworld = 0 +local wboss_nether = 0 +local wboss_end = 0 + +local LIM_OVERWORLD = tonumber(minetest.settings:get("wither_cap_overworld")) or 3 +local LIM_NETHER = tonumber(minetest.settings:get("wither_cap_nether")) or 10 +local LIM_END = tonumber(minetest.settings:get("wither_cap_end")) or 5 + local wither_spawn_schems = {} for _, d in pairs(dim) do @@ -16,8 +27,13 @@ for _, d in pairs(dim) do end local function check_schem(pos, schem) + local cn_name for _, n in pairs(schem) do - if minetest.get_node(vector.add(pos, n)).name ~= n.name then + cn_name = minetest.get_node(vector.add(pos, n)).name + if string.find(cn_name, "mcl_heads:wither_skeleton") then + cn_name = "mcl_heads:wither_skeleton" + end + if cn_name ~= n.name then return false end end @@ -30,14 +46,32 @@ local function remove_schem(pos, schem) end end -local function wither_spawn(pos) +local function check_limit(pos) + local dim = mcl_worlds.pos_to_dimension(pos) + if dim == "overworld" and wboss_overworld >= LIM_OVERWORLD then return false + elseif dim == "end" and wboss_end >= LIM_END then return false + elseif wboss_nether >= LIM_NETHER then return false + else return true end +end + +local function wither_spawn(pos, player) + if peaceful then return end for _, d in pairs(dim) do for i = 0, 2 do local p = vector.add(pos, {x = 0, y = -2, z = 0, [d] = -i}) local schem = wither_spawn_schems[d] - if check_schem(p, schem) then + if check_schem(p, schem) and (not anti_troll or check_limit(pos)) then remove_schem(p, schem) - minetest.add_entity(vector.add(p, {x = 0, y = 1, z = 0, [d] = 1}), "mobs_mc:wither") + local wither = minetest.add_entity(vector.add(p, {x = 0, y = 1, z = 0, [d] = 1}), "mobs_mc:wither") + if not wither then return end + local wither_ent = wither:get_luaentity() + wither_ent._spawner = player:get_player_name() + local dim = mcl_worlds.pos_to_dimension(pos) + if dim == "overworld" then + wboss_overworld = wboss_overworld + 1 + elseif dim == "end" then + wboss_end = wboss_end + 1 + else wboss_nether = wboss_nether + 1 end local objects = minetest.get_objects_inside_radius(pos, 20) for _, players in ipairs(objects) do if players:is_player() then @@ -54,7 +88,19 @@ local old_on_place = wither_head.on_place function wither_head.on_place(itemstack, placer, pointed) local n = minetest.get_node(vector.offset(pointed.above,0,-1,0)) if n and n.name == "mcl_nether:soul_sand" then - minetest.after(0, wither_spawn, pointed.above) + minetest.after(0, wither_spawn, pointed.above, placer) end return old_on_place(itemstack, placer, pointed) end + +if anti_troll then + -- pull wither counts per dimension + minetest.register_globalstep(function(dtime) + wboss_overworld = mobs_mc.wither_count_overworld + wboss_nether = mobs_mc.wither_count_nether + wboss_end = mobs_mc.wither_count_end + mobs_mc.wither_count_overworld = 0 + mobs_mc.wither_count_nether = 0 + mobs_mc.wither_count_end = 0 + end) +end diff --git a/mods/ENTITIES/mobs_mc/ender_dragon.lua b/mods/ENTITIES/mobs_mc/ender_dragon.lua index fb92f51c4..d916596dd 100644 --- a/mods/ENTITIES/mobs_mc/ender_dragon.lua +++ b/mods/ENTITIES/mobs_mc/ender_dragon.lua @@ -127,7 +127,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", { minetest.set_node(vector.add(self._portal_pos, vector.new(0, 5, 0)), {name = "mcl_end:dragon_egg"}) end end - + -- Free The End Advancement for _,players in pairs(minetest.get_objects_inside_radius(pos,64)) do if players:is_player() then @@ -136,6 +136,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", { end end, fire_resistant = true, + is_boss = true, }) diff --git a/mods/ENTITIES/mobs_mc/mod.conf b/mods/ENTITIES/mobs_mc/mod.conf index 5b94879b2..ec7446505 100644 --- a/mods/ENTITIES/mobs_mc/mod.conf +++ b/mods/ENTITIES/mobs_mc/mod.conf @@ -2,4 +2,4 @@ name = mobs_mc author = maikerumine description = Adds Minecraft-like monsters and animals. depends = mcl_init, mcl_particles, mcl_mobs, mcl_wip, mcl_core, mcl_util -optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, doc_items +optional_depends = default, mcl_tnt, mcl_bows, mcl_throwing, mcl_fishing, bones, mesecons_materials, doc_items, mcl_worlds diff --git a/mods/ENTITIES/mobs_mc/skeleton_wither.lua b/mods/ENTITIES/mobs_mc/skeleton_wither.lua index 314260581..865fa13f0 100644 --- a/mods/ENTITIES/mobs_mc/skeleton_wither.lua +++ b/mods/ENTITIES/mobs_mc/skeleton_wither.lua @@ -96,6 +96,11 @@ mcl_mobs.register_mob("mobs_mc:witherskeleton", { fear_height = 4, harmed_by_heal = true, fire_resistant = true, + dealt_effect = { + name = "withering", + factor = 1, + dur = 10, + }, }) --spawn diff --git a/mods/ENTITIES/mobs_mc/spider.lua b/mods/ENTITIES/mobs_mc/spider.lua index 65d3a2d21..38138a71c 100644 --- a/mods/ENTITIES/mobs_mc/spider.lua +++ b/mods/ENTITIES/mobs_mc/spider.lua @@ -114,9 +114,7 @@ mcl_mobs.register_mob("mobs_mc:spider", spider) local cave_spider = table.copy(spider) cave_spider.description = S("Cave Spider") cave_spider.textures = { {"mobs_mc_cave_spider.png^(mobs_mc_spider_eyes.png^[makealpha:0,0,0)"} } --- TODO: Poison damage --- TODO: Revert damage to 2 -cave_spider.damage = 3 -- damage increased to undo non-existing poison +cave_spider.damage = 2 cave_spider.hp_min = 1 cave_spider.hp_max = 12 cave_spider.collisionbox = {-0.35, -0.01, -0.35, 0.35, 0.46, 0.35} @@ -138,6 +136,11 @@ cave_spider.walk_velocity = 1.3 cave_spider.run_velocity = 3.2 cave_spider.sounds = table.copy(spider.sounds) cave_spider.sounds.base_pitch = 1.25 +cave_spider.dealt_effect = { + name = "poison", + factor = 2.5, + dur = 7, +} mcl_mobs.register_mob("mobs_mc:cave_spider", cave_spider) diff --git a/mods/ENTITIES/mobs_mc/witch.lua b/mods/ENTITIES/mobs_mc/witch.lua index b00a68116..c9cc3c8ee 100644 --- a/mods/ENTITIES/mobs_mc/witch.lua +++ b/mods/ENTITIES/mobs_mc/witch.lua @@ -70,6 +70,11 @@ mcl_mobs.register_mob("mobs_mc:witch", { }, view_range = 16, fear_height = 4, + deal_damage = function(self, damage, mcl_reason) + local factor = 1 + if mcl_reason.type == "magic" then factor = 0.15 end + self.health = self.health - factor*damage + end, }) -- potion projectile (EXPERIMENTAL) diff --git a/mods/ENTITIES/mobs_mc/wither.lua b/mods/ENTITIES/mobs_mc/wither.lua index 6b47d601a..cda3f0153 100644 --- a/mods/ENTITIES/mobs_mc/wither.lua +++ b/mods/ENTITIES/mobs_mc/wither.lua @@ -1,14 +1,72 @@ --MCmobs v0.4 --maikerumine +--updated by Herowl --made for MC like Survival game --License for code WTFPL and otherwise stated in readmes local S = minetest.get_translator("mobs_mc") +local mobs_griefing = minetest.settings:get_bool("mobs_griefing", true) +local follow_spawner = minetest.settings:get_bool("wither_follow_spawner", false) +local w_strafes = minetest.settings:get_bool("wither_strafes", true) +local anti_troll = minetest.settings:get_bool("wither_anti_troll_measures", false) + +local WITHER_INIT_BOOM = 7 +local WITHER_MELEE_COOLDOWN = 3 + +local function atan(x) + if not x or x ~= x then + return 0 + else + return math.atan(x) + end +end --################### --################### WITHER --################### +local function wither_unstuck(self) + local pos = self.object:get_pos() + if mobs_griefing then -- destroy blocks very nearby (basically, colliding with) + local col = self.collisionbox + local pos1 = vector.offset(pos, col[1], col[2], col[3]) + local pos2 = vector.offset(pos, col[4], col[5], col[6]) + for z = pos1.z, pos2.z do for y = pos1.y, pos2.y do for x = pos1.x, pos2.x do + local npos = vector.new(x,y,z) + local name = minetest.get_node(npos).name + if name ~= "air" then + local ndef = minetest.registered_nodes[name] + if ndef and ndef._mcl_hardness and ndef._mcl_hardness >= 0 then + local drops = minetest.get_node_drops(name, "") + if minetest.dig_node(npos) then + for _, item in ipairs(drops) do + if type(item) ~= "string" then + item = item:get_name() .. item:get_count() + end + minetest.add_item(npos, item) + end + end + end + end + end end end + end + mcl_mobs.mob_class.safe_boom(self, pos, 2) +end + +local function get_dim_relative_y(pos) + if (pos.y >= mcl_vars.mg_realm_barrier_overworld_end_max) then + return pos.y + elseif (pos.y <= mcl_vars.mg_nether_max + 200) then + return (pos.y - mcl_vars.mg_nether_min - 20) + else + return (pos.y - mcl_vars.mg_end_min - 50) + end +end + +mobs_mc.wither_count_overworld = 0 +mobs_mc.wither_count_nether = 0 +mobs_mc.wither_count_end = 0 + mcl_mobs.register_mob("mobs_mc:wither", { description = S("Wither"), type = "monster", @@ -26,11 +84,11 @@ mcl_mobs.register_mob("mobs_mc:wither", { {"mobs_mc_wither.png"}, }, visual_size = {x=4, y=4}, - makes_footstep_sound = true, - view_range = 16, + view_range = 50, fear_height = 4, walk_velocity = 2, run_velocity = 4, + strafes = w_strafes, sounds = { shoot_attack = "mobs_mc_ender_dragon_shoot", attack = "mobs_mc_ender_dragon_attack", @@ -41,9 +99,8 @@ mcl_mobs.register_mob("mobs_mc:wither", { jump_height = 10, fly = true, makes_footstep_sound = false, - dogshoot_switch = 1, - dogshoot_count_max = 1, - attack_animals = true, + dogshoot_switch = 1, -- unused + dogshoot_count_max = 1, -- unused can_despawn = false, drops = { {name = "mcl_mobitems:nether_star", @@ -53,13 +110,13 @@ mcl_mobs.register_mob("mobs_mc:wither", { }, lava_damage = 0, fire_damage = 0, - attack_type = "dogshoot", + attack_type = "custom", explosion_strength = 8, dogshoot_stop = true, arrow = "mobs_mc:wither_skull", reach = 5, - shoot_interval = 0.5, - shoot_offset = -1, + shoot_interval = 1, + shoot_offset = -0.5, animation = { walk_speed = 12, run_speed = 12, stand_speed = 12, stand_start = 0, stand_end = 20, @@ -67,57 +124,377 @@ mcl_mobs.register_mob("mobs_mc:wither", { run_start = 0, run_end = 20, }, harmed_by_heal = true, - do_custom = function(self) + is_boss = true, + extra_hostile = true, + attack_exception = function(p) + local ent = p:get_luaentity() + if p:is_player() then return false end + if not ent or not ent.is_mob or ent.harmed_by_heal or string.find(ent.name, "ghast") then return true + else return false end + end, + + do_custom = function(self, dtime) + if self._spawning then + -- "loading" bar while spawning + if not self._spw_max then self._spw_max = self._spawning end + self._spawning = self._spawning - dtime + local bardef = { + color = "dark_purple", + text = "Wither spawning", + percentage = math.floor((self._spw_max - self._spawning) / self._spw_max * 100), + } + + local pos = self.object:get_pos() + for _, player in pairs(minetest.get_connected_players()) do + local d = vector.distance(pos, player:get_pos()) + if d <= 80 then + mcl_bossbars.add_bar(player, bardef, true, d) + end + end + + -- turn around and flash while spawning + self.object:set_yaw(self._spawning*10) + local factor = math.floor((math.sin(self._spawning*10)+1.5) * 85) + local str = minetest.colorspec_to_colorstring({r=factor, g=factor, b=factor}) + self.object:set_texture_mod("^[brighten^[multiply:"..str) + + -- when fully spawned, explode + if self._spawning <= 0 then + if mobs_griefing and not minetest.is_protected(pos, "") then + mcl_explosions.explode(pos, WITHER_INIT_BOOM, { drop_chance = 1.0 }, self.object) + else + mcl_mobs.mob_class.safe_boom(self, pos, WITHER_INIT_BOOM) + end + self.object:set_texture_mod("") + self._spawning = nil + self._spw_max = nil + else + return false + end + end + + -- passive regeneration + self._custom_timer = self._custom_timer + dtime + if self._custom_timer > 1 then + self.health = math.min(self.health + 1, self.hp_max) + self._custom_timer = self._custom_timer - 1 + end + + -- anti-troll measures + if anti_troll then + if self._spawner then + local spawner = minetest.get_player_by_name(self._spawner) + if follow_spawner and spawner then + self._death_timer = 0 + local pos = self.object:get_pos() + local spw = spawner:get_pos() + local dist = vector.distance(pos, spw) + if dist > 60 then -- teleport to the player who spawned the wither + local R = 10 + pos.x = spw.x + math.random(-R, R) + pos.y = spw.y + math.random(-R, R) + pos.z = spw.z + math.random(-R, R) + self.object:set_pos(pos) + end + else -- despawn automatically after set time + -- HP changes impact timer: taking damage sets it back + self._death_timer = self._death_timer + self.health - self._health_old + if self.health == self._health_old then self._death_timer = self._death_timer + dtime end + if self._death_timer > 100 then + self.object:remove() + return false + end + self._health_old = self.health + end + end + -- count withers per dimension + local dim = mcl_worlds.pos_to_dimension(self.object:get_pos()) + if dim == "overworld" then mobs_mc.wither_count_overworld = mobs_mc.wither_count_overworld + 1 + elseif dim == "nether" then mobs_mc.wither_count_nether = mobs_mc.wither_count_nether + 1 + elseif dim == "end" then mobs_mc.wither_count_end = mobs_mc.wither_count_end + 1 end + end + + -- update things dependent on HP + local rand_factor if self.health < (self.hp_max / 2) then self.base_texture = "mobs_mc_wither_half_health.png" self.fly = false - self.object:set_properties({textures={self.base_texture}}) - self.armor = {undead = 80, fleshy = 80} + self._arrow_resistant = true + rand_factor = 3 + else + self.base_texture = "mobs_mc_wither.png" + self.fly = true + self._arrow_resistant = false + rand_factor = 10 end + if not self.attack then + local y = get_dim_relative_y(self.object:get_pos()) + if y > 0 then + self.fly = false + else + self.fly = true + local vel = self.object:get_velocity() + self.object:set_velocity(vector.new(vel.x, self.walk_velocity, vel.z)) + end + end + self.object:set_properties({textures={self.base_texture}}) mcl_bossbars.update_boss(self.object, "Wither", "dark_purple") + if math.random(1, rand_factor) < 2 then + self.arrow = "mobs_mc:wither_skull_strong" + else + self.arrow = "mobs_mc:wither_skull" + end end, + + attack_state = function(self, dtime) + local s = self.object:get_pos() + local p = self.attack:get_pos() or s + + p.y = p.y - .5 + s.y = s.y + .5 + + local dist = vector.distance(p, s) + local vec = { + x = p.x - s.x, + y = p.y - s.y, + z = p.z - s.z + } + + local yaw = (atan(vec.z / vec.x) +math.pi/ 2) - self.rotate + if p.x > s.x then yaw = yaw +math.pi end + yaw = self:set_yaw( yaw, 0, dtime) + + local stay_away_from_player = vector.zero() + + --strafe back and fourth + + --stay away from player so as to shoot them + if dist < self.avoid_distance and self.shooter_avoid_enemy then + self:set_animation( "shoot") + stay_away_from_player=vector.multiply(vector.direction(p, s), 0.33) + end + + if self.fly then + local vel = self.object:get_velocity() + local diff = s.y - p.y + local FLY_FACTOR = self.walk_velocity + if diff < 10 then + self.object:set_velocity({x=vel.x, y= FLY_FACTOR, z=vel.z}) + elseif diff > 15 then + self.object:set_velocity({x=vel.x, y=-FLY_FACTOR, z=vel.z}) + end + for i=1, 15 do + if minetest.get_node(vector.offset(s, 0, -i, 0)).name ~= "air" then + self.object:set_velocity({x=vel.x, y= FLY_FACTOR, z=vel.z}) + break + elseif minetest.get_node(vector.offset(s, 0, i, 0)).name ~= "air" then + self.object:set_velocity({x=vel.x, y=-FLY_FACTOR/i, z=vel.z}) + break + end + end + end + + if self.strafes then + if not self.strafe_direction then + self.strafe_direction = 1.57 + end + if math.random(40) == 1 then + self.strafe_direction = self.strafe_direction*-1 + end + + local dir = vector.rotate_around_axis(vector.direction(s, p), vector.new(0,1,0), self.strafe_direction) + local dir2 = vector.multiply(dir, 0.3 * self.walk_velocity) + + if dir2 and stay_away_from_player then + self.acc = vector.add(dir2, stay_away_from_player) + end + else + self:set_velocity(0) + end + + if dist > 30 then self.acc = vector.add(self.acc, vector.direction(s, p)*0.01) end + + local side_cor = vector.new(0.7*math.cos(yaw), 0, 0.7*math.sin(yaw)) + local m = self.object:get_pos() -- position of the middle head + local sr = self.object:get_pos() + side_cor -- position of side right head + local sl = self.object:get_pos() - side_cor -- position of side left head + -- height corrections + m.y = m.y + self.collisionbox[5] + sr.y = sr.y + self.collisionbox[5] - 0.3 + sl.y = sl.y + self.collisionbox[5] - 0.3 + local rand_pos = math.random(1,3) + if rand_pos == 1 then m = sr + elseif rand_pos == 2 then m = sl end + + -- melee attack + if not self._melee_timer then + self._melee_timer = 0 + end + if self._melee_timer < WITHER_MELEE_COOLDOWN then + self._melee_timer = self._melee_timer + dtime + else + self._melee_timer = 0 + local pos = table.copy(s) + pos.y = pos.y + 2 + local objs = minetest.get_objects_inside_radius(pos, self.reach) + local obj_pos, dist + local hit_some = false + for n = 1, #objs do + objs[n]:punch(objs[n], 1.0, { + full_punch_interval = 1.0, + damage_groups = {fleshy = 4}, + }, pos) + local ent = objs[n]:get_luaentity() + if objs[n]:is_player() or (ent and ent ~= self and (not ent._shooter or ent._shooter ~= self)) then + mcl_util.deal_damage(objs[n], 8, {type = "magic"}) + hit_some = true + end + mcl_mobs.effect_functions["withering"](objs[n], 0.5, 10) + end + if hit_some then + mcl_mobs.effect(pos, 32, "mcl_particles_soul_fire_flame.png", 5, 10, self.reach, 1, 0) + end + end + + if dist < self.reach then + self.shoot_interval = 3 + else + self.shoot_interval = 1 + end + + if self.shoot_interval + and self.timer > self.shoot_interval + and not minetest.raycast(vector.add(m, vector.new(0,self.shoot_offset,0)), vector.add(self.attack:get_pos(), vector.new(0,1.5,0)), false, false):next() + and math.random(1, 100) <= 60 then + + self.timer = 0 + self:set_animation( "shoot") + + -- play shoot attack sound + self:mob_sound("shoot_attack") + + -- Shoot arrow + if minetest.registered_entities[self.arrow] then + + local arrow, ent + local v = 1 + if not self.shoot_arrow then + self.firing = true + minetest.after(1, function() + self.firing = false + end) + arrow = minetest.add_entity(m, self.arrow) + ent = arrow:get_luaentity() + if ent.velocity then + v = ent.velocity + end + ent.switch = 1 + ent.owner_id = tostring(self.object) -- add unique owner id to arrow + + -- important for mcl_shields + ent._shooter = self.object + ent._saved_shooter_pos = self.object:get_pos() + end + + local amount = (vec.x * vec.x + vec.y * vec.y + vec.z * vec.z) ^ 0.5 + -- offset makes shoot aim accurate + vec.y = vec.y + self.shoot_offset + vec.x = vec.x * (v / amount) + vec.y = vec.y * (v / amount) + vec.z = vec.z * (v / amount) + if self.shoot_arrow then + vec = vector.normalize(vec) + self:shoot_arrow(m, vec) + else + arrow:set_velocity(vec) + end + end + end + end, + + do_punch = function(self, hitter, tflp, tool_capabilities, dir) + if self._spawning or hitter == self.object then return false end + local ent = hitter:get_luaentity() + if ent and self._arrow_resistant and (string.find(ent.name, "arrow") or string.find(ent.name, "rocket")) then return false end + wither_unstuck(self) + return true + end, + deal_damage = function(self, damage, mcl_reason) + if self._spawning then return end + if self._arrow_resistant and mcl_reason.type == "magic" then return end + wither_unstuck(self) + self.health = self.health - damage + end, + on_spawn = function(self) minetest.sound_play("mobs_mc_wither_spawn", {object=self.object, gain=1.0, max_hear_distance=64}) + self._custom_timer = 0.0 + self._death_timer = 0.0 + self._health_old = self.hp_max + self._spawning = 10 + return true end, }) -local mobs_griefing = minetest.settings:get_bool("mobs_griefing") ~= false local wither_rose_soil = { "group:grass_block", "mcl_core:dirt", "mcl_core:coarse_dirt", "mcl_nether:netherrack", "group:soul_block", "mcl_mud:mud", "mcl_moss:moss" } +local function spawn_wither_rose(obj) + local n = minetest.find_node_near(obj:get_pos(),2,wither_rose_soil) + if n then + local p = vector.offset(n,0,1,0) + if minetest.get_node(p).name == "air" then + if not ( mobs_griefing and minetest.place_node(p,{name="mcl_flowers:wither_rose"}) ) then + minetest.add_item(p,"mcl_flowers:wither_rose") + end + end + end +end mcl_mobs.register_arrow("mobs_mc:wither_skull", { - visual = "sprite", - visual_size = {x = 0.75, y = 0.75}, - -- TODO: 3D projectile, replace tetxture - textures = {"mobs_mc_TEMP_wither_projectile.png"}, - velocity = 6, + visual = "cube", + visual_size = {x = 0.3, y = 0.3}, + textures = { + "mobs_mc_wither_projectile.png^[verticalframe:6:0", -- top + "mobs_mc_wither_projectile.png^[verticalframe:6:1", -- bottom + "mobs_mc_wither_projectile.png^[verticalframe:6:2", -- left + "mobs_mc_wither_projectile.png^[verticalframe:6:3", -- right + "mobs_mc_wither_projectile.png^[verticalframe:6:4", -- back + "mobs_mc_wither_projectile.png^[verticalframe:6:5", -- front + }, + velocity = 7, + rotate = 90, + _lifetime = 350, + on_punch = function(self) end, -- direct hit hit_player = function(self, player) + local pos = vector.new(self.object:get_pos()) + mcl_mobs.effect_functions["withering"](player, 0.5, 10) player:punch(self.object, 1.0, { full_punch_interval = 0.5, damage_groups = {fleshy = 8}, }, nil) - mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1) + mcl_mobs.mob_class.boom(self, pos, 1) + if player:get_hp() <= 0 then + local shooter = self._shooter:get_luaentity() + if shooter then shooter.health = shooter.health + 5 end + spawn_wither_rose(player) + end end, hit_mob = function(self, mob) + local pos = vector.new(self.object:get_pos()) + mcl_mobs.effect_functions["withering"](mob, 0.5, 10) mob:punch(self.object, 1.0, { full_punch_interval = 0.5, damage_groups = {fleshy = 8}, }, nil) - mcl_mobs.mob_class.boom(self,self.object:get_pos(), 1) + mcl_mobs.mob_class.boom(self, pos, 1) local l = mob:get_luaentity() if l and l.health - 8 <= 0 then - local n = minetest.find_node_near(mob:get_pos(),2,wither_rose_soil) - if n then - local p = vector.offset(n,0,1,0) - if minetest.get_node(p).name == "air" then - if not ( mobs_griefing and minetest.place_node(p,{name="mcl_flowers:wither_rose"}) ) then - minetest.add_item(p,"mcl_flowers:wither_rose") - end - end - end + local shooter = self._shooter:get_luaentity() + if shooter then shooter.health = shooter.health + 5 end + spawn_wither_rose(mob) end end, @@ -126,10 +503,75 @@ mcl_mobs.register_arrow("mobs_mc:wither_skull", { mcl_mobs.mob_class.boom(self,pos, 1) end }) --- TODO: Add blue wither skull +mcl_mobs.register_arrow("mobs_mc:wither_skull_strong", { + visual = "cube", + visual_size = {x = 0.35, y = 0.35}, + textures = { + "mobs_mc_wither_projectile_strong.png^[verticalframe:6:0", -- top + "mobs_mc_wither_projectile_strong.png^[verticalframe:6:1", -- bottom + "mobs_mc_wither_projectile_strong.png^[verticalframe:6:2", -- left + "mobs_mc_wither_projectile_strong.png^[verticalframe:6:3", -- right + "mobs_mc_wither_projectile_strong.png^[verticalframe:6:4", -- back + "mobs_mc_wither_projectile_strong.png^[verticalframe:6:5", -- front + }, + velocity = 4, + rotate = 90, + _lifetime = 500, + on_punch = function(self) end, + + -- direct hit + hit_player = function(self, player) + local pos = vector.new(self.object:get_pos()) + mcl_mobs.effect_functions["withering"](player, 0.5, 10) + player:punch(self.object, 1.0, { + full_punch_interval = 0.5, + damage_groups = {fleshy = 12}, + }, nil) + if mobs_griefing and not minetest.is_protected(pos, "") then + mcl_explosions.explode(pos, 1, { drop_chance = 1.0, max_blast_resistance = 0, }, self.object) + else + mcl_mobs.mob_class.safe_boom(self, pos, 1) --need to call it this way bc self is the "arrow" object here + end + if player:get_hp() <= 0 then + local shooter = self._shooter:get_luaentity() + if shooter then shooter.health = shooter.health + 5 end + spawn_wither_rose(player) + end + end, + + hit_mob = function(self, mob) + local pos = vector.new(self.object:get_pos()) + mcl_mobs.effect_functions["withering"](mob, 0.5, 10) + mob:punch(self.object, 1.0, { + full_punch_interval = 0.5, + damage_groups = {fleshy = 12}, + }, nil) + if mobs_griefing and not minetest.is_protected(pos, "") then + mcl_explosions.explode(pos, 1, { drop_chance = 1.0, max_blast_resistance = 0, }, self.object) + else + mcl_mobs.mob_class.safe_boom(self, pos, 1) --need to call it this way bc self is the "arrow" object here + end + local l = mob:get_luaentity() + if l and l.health - 8 <= 0 then + local shooter = self._shooter:get_luaentity() + if shooter then shooter.health = shooter.health + 5 end + spawn_wither_rose(mob) + end + end, + + -- node hit, explode + hit_node = function(self, pos, node) + if mobs_griefing and not minetest.is_protected(pos, "") then + mcl_explosions.explode(pos, 1, { drop_chance = 1.0, max_blast_resistance = 0, }, self.object) + else + mcl_mobs.mob_class.safe_boom(self, pos, 1) --need to call it this way bc self is the "arrow" object here + end + end +}) --Spawn egg mcl_mobs.register_egg("mobs_mc:wither", S("Wither"), "#4f4f4f", "#4f4f4f", 0, true) mcl_wip.register_wip_item("mobs_mc:wither") mcl_mobs:non_spawn_specific("mobs_mc:wither","overworld",0,minetest.LIGHT_MAX+1) + diff --git a/mods/ITEMS/mcl_potions/commands.lua b/mods/ITEMS/mcl_potions/commands.lua index 76ac71e72..10bf86311 100644 --- a/mods/ITEMS/mcl_potions/commands.lua +++ b/mods/ITEMS/mcl_potions/commands.lua @@ -20,6 +20,7 @@ get_chat_function["leaping"] = mcl_potions.leaping_func get_chat_function["swiftness"] = mcl_potions.swiftness_func get_chat_function["heal"] = mcl_potions.healing_func get_chat_function["bad_omen"] = mcl_potions.bad_omen_func +get_chat_function["withering"] = mcl_potions.withering_func minetest.register_chatcommand("effect",{ params = S(" []"), diff --git a/mods/ITEMS/mcl_potions/functions.lua b/mods/ITEMS/mcl_potions/functions.lua index 20608a7df..24aa2e402 100644 --- a/mods/ITEMS/mcl_potions/functions.lua +++ b/mods/ITEMS/mcl_potions/functions.lua @@ -10,6 +10,7 @@ EF.swift = {} -- for swiftness AND slowness EF.night_vision = {} EF.fire_proof = {} EF.bad_omen = {} +EF.withering = {} local EFFECT_TYPES = 0 for _,_ in pairs(EF) do @@ -19,8 +20,11 @@ end local icon_ids = {} local function potions_set_hudbar(player) - - if EF.poisoned[player] and EF.regenerating[player] then + if EF.withering[player] and EF.regenerating[player] then + hb.change_hudbar(player, "health", nil, nil, "mcl_potions_icon_regen_wither.png", nil, "hudbars_bar_health.png") + elseif EF.withering[player] then + hb.change_hudbar(player, "health", nil, nil, "mcl_potions_icon_wither.png", nil, "hudbars_bar_health.png") + elseif EF.poisoned[player] and EF.regenerating[player] then hb.change_hudbar(player, "health", nil, nil, "hbhunger_icon_regen_poison.png", nil, "hudbars_bar_health.png") elseif EF.poisoned[player] then hb.change_hudbar(player, "health", nil, nil, "hbhunger_icon_health_poison.png", nil, "hudbars_bar_health.png") @@ -123,6 +127,33 @@ minetest.register_globalstep(function(dtime) end + -- Check for withering players + for player, vals in pairs(EF.withering) do + + is_player = player:is_player() + entity = player:get_luaentity() + + EF.withering[player].timer = EF.withering[player].timer + dtime + EF.withering[player].hit_timer = (EF.withering[player].hit_timer or 0) + dtime + + if player:get_pos() then mcl_potions._add_spawner(player, "#000000") end + + if EF.withering[player].hit_timer >= EF.withering[player].step then + if is_player or entity then mcl_util.deal_damage(player, 1, {type = "magic"}) end + if EF.withering[player] then EF.withering[player].hit_timer = 0 end + end + + if EF.withering[player] and EF.withering[player].timer >= EF.withering[player].dur then + EF.withering[player] = nil + if is_player then + meta = player:get_meta() + meta:set_string("_is_withering", minetest.serialize(EF.withering[player])) + potions_set_hud(player) + end + end + + end + -- Check for poisoned players for player, vals in pairs(EF.poisoned) do @@ -152,7 +183,7 @@ minetest.register_globalstep(function(dtime) end - -- Check for regnerating players + -- Check for regenerating players for player, vals in pairs(EF.regenerating) do is_player = player:is_player() @@ -408,6 +439,7 @@ function mcl_potions._clear_cached_player_data(player) EF.night_vision[player] = nil EF.fire_proof[player] = nil EF.bad_omen[player] = nil + EF.withering[player] = nil meta = player:get_meta() meta:set_int("night_vision", 0) @@ -452,6 +484,7 @@ function mcl_potions._save_player_effects(player) meta:set_string("_is_cat", minetest.serialize(EF.night_vision[player])) meta:set_string("_is_fire_proof", minetest.serialize(EF.fire_proof[player])) meta:set_string("_has_bad_omen", minetest.serialize(EF.bad_omen[player])) + meta:set_string("_is_withering", minetest.serialize(EF.withering[player])) end @@ -507,6 +540,10 @@ function mcl_potions._load_player_effects(player) EF.bad_omen[player] = minetest.deserialize(meta:get_string("_has_bad_omen")) end + if minetest.deserialize(meta:get_string("_is_withering")) then + EF.withering[player] = minetest.deserialize(meta:get_string("_is_withering")) + end + end -- Returns true if player has given effect @@ -693,11 +730,9 @@ end function mcl_potions.healing_func(player, hp) - local obj = player:get_luaentity() + if not player or player:get_hp() <= 0 then return false end - if player:get_hp() == 0 then - return - end + local obj = player:get_luaentity() if obj and obj.harmed_by_heal then hp = -hp end @@ -725,6 +760,11 @@ end function mcl_potions.swiftness_func(player, factor, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + if not player:get_meta() then return false end @@ -753,6 +793,11 @@ end function mcl_potions.leaping_func(player, factor, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + if not player:get_meta() then return false end @@ -781,6 +826,11 @@ end function mcl_potions.weakness_func(player, factor, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + if not EF.weak[player] then EF.weak[player] = {dur = duration, timer = 0, factor = factor} @@ -804,6 +854,11 @@ end function mcl_potions.strength_func(player, factor, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + if not EF.strong[player] then EF.strong[player] = {dur = duration, timer = 0, factor = factor} @@ -825,8 +880,41 @@ function mcl_potions.strength_func(player, factor, duration) end +function mcl_potions.withering_func(player, factor, duration) + + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and (entity.is_boss or string.find(entity.name, "wither")) then return false end + + if not EF.withering[player] then + + EF.withering[player] = {step = factor, dur = duration, timer = 0} + + else + + local victim = EF.withering[player] + + victim.step = math.min(victim.step, factor) + victim.dur = math.max(duration, victim.dur - victim.timer) + victim.timer = 0 + + end + + if player:is_player() then + potions_set_hud(player) + end + +end + + function mcl_potions.poison_func(player, factor, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and (entity.is_boss or entity.harmed_by_heal or string.find(entity.name, "spider")) then return false end + if not EF.poisoned[player] then EF.poisoned[player] = {step = factor, dur = duration, timer = 0} @@ -850,6 +938,11 @@ end function mcl_potions.regeneration_func(player, factor, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and (entity.is_boss or entity.harmed_by_heal) then return false end + if not EF.regenerating[player] then EF.regenerating[player] = {step = factor, dur = duration, timer = 0} @@ -873,6 +966,11 @@ end function mcl_potions.invisiblility_func(player, null, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + if not EF.invisible[player] then EF.invisible[player] = {dur = duration, timer = 0} @@ -895,6 +993,11 @@ end function mcl_potions.water_breathing_func(player, null, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + if not EF.water_breathing[player] then EF.water_breathing[player] = {dur = duration, timer = 0} @@ -917,6 +1020,11 @@ end function mcl_potions.fire_resistance_func(player, null, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + if not EF.fire_proof[player] then EF.fire_proof[player] = {dur = duration, timer = 0} @@ -938,6 +1046,11 @@ end function mcl_potions.night_vision_func(player, null, duration) + if not player or player:get_hp() <= 0 then return false end + + local entity = player:get_luaentity() + if entity and entity.is_boss then return false end + meta = player:get_meta() if not EF.night_vision[player] then diff --git a/mods/ITEMS/mcl_potions/init.lua b/mods/ITEMS/mcl_potions/init.lua index 2ea7e2879..73fcdae16 100644 --- a/mods/ITEMS/mcl_potions/init.lua +++ b/mods/ITEMS/mcl_potions/init.lua @@ -190,7 +190,7 @@ local function set_node_empty_bottle(itemstack, placer, pointed_thing, newitemst -- play sound minetest.sound_play("mcl_potions_bottle_pour", {pos=pointed_thing.under, gain=0.5, max_hear_range=16}, true) - -- + -- if minetest.is_creative_enabled(placer:get_player_name()) then return itemstack else @@ -356,6 +356,7 @@ local awkward_table = { ["mcl_fishing:pufferfish_raw"] = "mcl_potions:water_breathing", ["mcl_mobitems:ghast_tear"] = "mcl_potions:regeneration", ["mcl_mobitems:spider_eye"] = "mcl_potions:poison", + ["mcl_flowers:wither_rose"] = "mcl_potions:withering", ["mcl_mobitems:rabbit_foot"] = "mcl_potions:leaping", } @@ -373,7 +374,7 @@ local potions = {} for i, potion in ipairs({"healing","harming","swiftness","slowness", "leaping","poison","regeneration","invisibility","fire_resistance", -- "weakness","strength", - "water_breathing","night_vision"}) do + "water_breathing","night_vision", "withering"}) do table.insert(potions, potion) @@ -461,6 +462,31 @@ function mcl_potions.get_alchemy(ingr, pot) return false end +mcl_mobs.effect_functions["poison"] = mcl_potions.poison_func +mcl_mobs.effect_functions["regeneration"] = mcl_potions.regeneration_func +mcl_mobs.effect_functions["invisibility"] = mcl_potions.invisiblility_func +mcl_mobs.effect_functions["fire_resistance"] = mcl_potions.fire_resistance_func +mcl_mobs.effect_functions["night_vision"] = mcl_potions.night_vision_func +mcl_mobs.effect_functions["water_breathing"] = mcl_potions.water_breathing_func +mcl_mobs.effect_functions["leaping"] = mcl_potions.leaping_func +mcl_mobs.effect_functions["swiftness"] = mcl_potions.swiftness_func +mcl_mobs.effect_functions["heal"] = mcl_potions.healing_func +mcl_mobs.effect_functions["bad_omen"] = mcl_potions.bad_omen_func +mcl_mobs.effect_functions["withering"] = mcl_potions.withering_func + +-- give withering to players in a wither rose +local etime = 0 +minetest.register_globalstep(function(dtime) + etime = dtime + etime + if etime < 0.5 then return end + etime = 0 + for _,pl in pairs(minetest.get_connected_players()) do + local npos = vector.offset(pl:get_pos(), 0, 0.2, 0) + local n = minetest.get_node(npos) + if n.name == "mcl_flowers:wither_rose" then mcl_potions.withering_func(pl, 1, 2) end + end +end) + mcl_wip.register_wip_item("mcl_potions:night_vision") mcl_wip.register_wip_item("mcl_potions:night_vision_plus") mcl_wip.register_wip_item("mcl_potions:night_vision_splash") @@ -468,4 +494,4 @@ mcl_wip.register_wip_item("mcl_potions:night_vision_plus_splash") mcl_wip.register_wip_item("mcl_potions:night_vision_lingering") mcl_wip.register_wip_item("mcl_potions:night_vision_plus_lingering") mcl_wip.register_wip_item("mcl_potions:night_vision_arrow") -mcl_wip.register_wip_item("mcl_potions:night_vision_plus_arrow") \ No newline at end of file +mcl_wip.register_wip_item("mcl_potions:night_vision_plus_arrow") diff --git a/mods/ITEMS/mcl_potions/mod.conf b/mods/ITEMS/mcl_potions/mod.conf index bcb6d8ad3..91280a607 100644 --- a/mods/ITEMS/mcl_potions/mod.conf +++ b/mods/ITEMS/mcl_potions/mod.conf @@ -1,2 +1,2 @@ name = mcl_potions -depends = mcl_core, mcl_farming, mcl_mobitems, mcl_fishing, mcl_bows, mcl_end, mcl_weather, playerphysics, mcl_wip +depends = mcl_core, mcl_farming, mcl_flowers, mcl_mobitems, mcl_mobs, mcl_fishing, mcl_bows, mcl_end, mcl_weather, playerphysics, mcl_wip diff --git a/mods/ITEMS/mcl_potions/potions.lua b/mods/ITEMS/mcl_potions/potions.lua index 3d89d1d40..57c06c29d 100644 --- a/mods/ITEMS/mcl_potions/potions.lua +++ b/mods/ITEMS/mcl_potions/potions.lua @@ -79,7 +79,7 @@ local function register_potion(def) if def.is_inv then dur = dur * mcl_potions.INV_FACTOR end - if def.name == "poison" or def.name == "regeneration" then + if def.name == "poison" or def.name == "regeneration" or def.name == "withering" then dur = 45 end @@ -93,7 +93,7 @@ local function register_potion(def) local _tt if effect and def.is_dur then _tt = perc_string(effect).." | "..time_string(dur) - if def.name == "poison" or def.name == "regeneration" then + if def.name == "poison" or def.name == "regeneration" or def.name == "withering" then _tt = S("1 HP/@1s | @2", effect, time_string(dur)) end elseif def.name == "healing" or def.name == "harming" then @@ -235,6 +235,8 @@ local function register_potion(def) effect_II = def.effect*mcl_potions.II_FACTOR elseif def.name == "poison" or def.name == "regeneration" then effect_II = 1.2 + elseif def.name == "withering" then + effect_II = 2 else effect_II = def.effect^mcl_potions.II_FACTOR end @@ -327,7 +329,7 @@ local function register_potion(def) if def.is_plus then local dur_pl = dur * mcl_potions.PLUS_FACTOR - if def.name == "poison" or def.name == "regeneration" then + if def.name == "poison" or def.name == "regeneration" or def.name == "withering" then dur_pl = 90 end @@ -533,6 +535,20 @@ local leaping_def = { is_plus = true, } +local withering_def = { + name = "withering", + description = S("Withering"), + _tt = nil, + _longdesc = S("Applies the withering effect which deals damage at a regular interval and can kill."), + color = "#000000", + effect = 4, + is_dur = true, + on_use = mcl_potions.withering_func, + is_II = true, + is_plus = true, + is_inv = true, +} + local poison_def = { name = "poison", description = S("Poison"), @@ -597,7 +613,7 @@ local fire_resistance_def = { local defs = { awkward_def, mundane_def, thick_def, dragon_breath_def, healing_def, harming_def, night_vision_def, swiftness_def, - slowness_def, leaping_def, poison_def, regeneration_def, + slowness_def, leaping_def, withering_def, poison_def, regeneration_def, invisibility_def, water_breathing_def, fire_resistance_def} for _, def in ipairs(defs) do diff --git a/mods/ITEMS/mcl_potions/splash.lua b/mods/ITEMS/mcl_potions/splash.lua index 730796952..6b6238dbf 100644 --- a/mods/ITEMS/mcl_potions/splash.lua +++ b/mods/ITEMS/mcl_potions/splash.lua @@ -110,7 +110,7 @@ function mcl_potions.register_splash(name, descr, color, def) for _,obj in pairs(minetest.get_objects_inside_radius(pos, 4)) do local entity = obj:get_luaentity() - if obj:is_player() or entity.is_mob then + if obj:is_player() or entity and entity.is_mob then local pos2 = obj:get_pos() local rad = math.floor(math.sqrt((pos2.x-pos.x)^2 + (pos2.y-pos.y)^2 + (pos2.z-pos.z)^2)) diff --git a/settingtypes.txt b/settingtypes.txt index 8123ca9d9..3013b47a1 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -166,6 +166,24 @@ mcl_mob_cap_axolotl (Mob cap axolotl) int 5 0 1024 #Maximum amount of ambient mobs that will spawn near a player (default:15) mcl_mob_cap_ambient (Mob cap ambient mobs) int 15 0 1024 +#Maximum amount of wither bosses on the loaded mapchunks in the overworld that allows spawning withers in the overworld (default:3) +wither_cap_overworld (Wither cap overworld) int 3 0 2048 + +#Maximum amount of wither bosses on the loaded mapchunks in the nether that allows spawning withers in the nether (default:10) +wither_cap_nether (Wither cap nether) int 10 0 2048 + +#Maximum amount of wither bosses on the loaded mapchunks in the end that allows spawning withers in the end (default:5) +wither_cap_end (Wither cap end) int 5 0 2048 + +#Should wither follow the player who spawned him around +wither_follow_spawner (Wither following his spawner) bool false + +#Should wither strafe while in combat +wither_strafes (Wither strafes) bool true + +#Wither anti-troll measures (escaping when stuck in a block, despawning when spawner goes offline, teleporting after the spawner). When this is OFF, wither_follow_spawner has no effect. +wither_anti_troll_measures (Wither anti-troll measures) bool false + #Display mob icons in inventory instead of mc-like spawn eggs mcl_old_spawn_icons (Old spawn icons instead of eggs) bool false diff --git a/textures/mcl_potions_effect_withering.png b/textures/mcl_potions_effect_withering.png new file mode 100644 index 000000000..eed626c6b Binary files /dev/null and b/textures/mcl_potions_effect_withering.png differ diff --git a/textures/mcl_potions_icon_regen_wither.png b/textures/mcl_potions_icon_regen_wither.png new file mode 100644 index 000000000..14c6acdad Binary files /dev/null and b/textures/mcl_potions_icon_regen_wither.png differ diff --git a/textures/mcl_potions_icon_wither.png b/textures/mcl_potions_icon_wither.png new file mode 100644 index 000000000..7ccf394b9 Binary files /dev/null and b/textures/mcl_potions_icon_wither.png differ diff --git a/textures/mobs_mc_wither_projectile.png b/textures/mobs_mc_wither_projectile.png new file mode 100644 index 000000000..381a0ff8f Binary files /dev/null and b/textures/mobs_mc_wither_projectile.png differ diff --git a/textures/mobs_mc_wither_projectile_strong.png b/textures/mobs_mc_wither_projectile_strong.png new file mode 100644 index 000000000..4e4c38f73 Binary files /dev/null and b/textures/mobs_mc_wither_projectile_strong.png differ