local fighting_players = {} local musics = {} local hud_ids = {} local animation = { idle = {{x = 1, y = 20}, 10}, walk_s = {{x = 21, y = 30}, 30}, walk = {{x = 31, y = 70}, 30}, walk_e = {{x = 71, y = 80}, 30}, jump = {{x = 81, y = 120}, 20}, punch = {{x = 121, y = 160}, 30}, grab = {{x = 161, y = 180}, 10}, throw = {{x = 181, y = 210}, 20}, throw_e = {{x = 211, y = 220}, 10}, box = {{x = 0, y = 0}, 0}, run = {{x = 0, y = 0}, 0}, } local function set_anim(obj, anim, loop) loop = loop ~= false local a = animation[anim] obj:set_animation(a[1], a[2] or 0, a[3] or 0, loop) end local function anim_time(anim) return (animation[anim][1].y - animation[anim][1].x) / animation[anim][2] end local gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81 local function apply_gravity(obj) local acc = obj:get_acceleration() acc.y = acc.y - gravity obj:set_acceleration(acc) end minetest.register_entity("bosses_farlands:cube_projectile", { hp_max = 1, physical = true, collide_with_objects = true, collisionbox = {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, visual = "cube", visual_size = {x = 1, y = 1}, textures = {}, -- +Y, -Y, +X, -X, +Z, -Z on_activate = function(self, staticdata, dtime_s) if staticdata == "" then self.object:remove() return end self.node = minetest.deserialize(staticdata) local tiles = table.copy(minetest.registered_nodes[self.node.name].tiles or {"unknown_node.png"}) local tiles_count = #tiles for i = 1, tiles_count do if type(tiles[i]) == "table" then tiles[i] = tiles[i].name end end if tiles_count < 6 then for i = tiles_count+1, 6 do tiles[i] = tiles[tiles_count] end end self.object:set_properties({textures = tiles}) apply_gravity(self.object) end, on_step = function(self, dtime) if not self.flying then return end local v = self.object:get_velocity() if v.x ~= 0 and v.y ~= 0 and v.z ~= 0 then return end local pos = vector.round(self.object:get_pos()) tnt.boom(vector.round(pos), { damage_radius = 3, radius = 1 }) minetest.set_node(pos, self.node) self.object:remove() end, }) local function die(self) local pos = self.object:get_pos() self.object:remove() --~ pos.y = pos.y + 1 --~ for i = -9, 9 do --~ if i == 0 then --~ i = i + 1 --~ end --~ for k = 1, 18/math.abs(i) do --~ local v = minetest.yaw_to_dir(k/math.abs(18/i)*360) --~ v.y = i/18 * vector.length(v) --~ v = vector.normalize(v) --~ minetest.add_particle({ --~ pos = pos, --~ velocity = v, --~ acceleration = {x=0, y=0, z=0}, --~ expirationtime = 1, --~ size = 1, --~ collisiondetection = true, --~ collision_removal = true, --~ texture = "mobs_rotten_blood.png", --~ }) --~ end --~ end -- Stop music and remove hud: for player_name, obj in pairs(fighting_players) do if obj == self.object then minetest.sound_stop(musics[player_name]) musics[player_name] = nil fighting_players[player_name] = nil minetest.sound_play("bosses_farlands_win", { to_player = player_name, gain = 1.0, }) local player = minetest.get_player_by_name(player_name) player:hud_remove(hud_ids[player_name][1]) player:hud_remove(hud_ids[player_name][2]) hud_ids[player_name] = nil end end end -- Should look for collidable nodes. local function is_node_bellow(pos, r) pos = vector.new(pos) pos.y = pos.y - 1 for x = -r, r do for z = -r, r do local p = vector.new(pos) p = vector.add(pos, {x=x,y=0,z=z}) local node = minetest.get_node(vector.round(p)) if node.name ~= "air" then return true end end end return false end local function wait(self, t, after) self.status = "wait" self.wait_timer = 0 self.wait_end = t self.after_wait = after self.wanted_vel = vector.new(0, 0, 0) end local function throw(self, pos, yaw) local node_pos = vector.round(vector.add(pos, minetest.yaw_to_dir(yaw))) local node_def = minetest.registered_nodes[minetest.get_node(node_pos).name] if node_def.drawtype ~= "normal" or minetest.get_node_timer(node_pos):is_started() then return false end local meta_table = minetest.get_meta(node_pos):to_table() if meta_table and ((meta_table.fields and #meta_table.fields > 0) or (meta_table.inventory and #meta_table.inventory > 0)) then return false end self.walked_time = 0 set_anim(self.object, "grab", false) wait(self, anim_time("grab"), function() local rock = minetest.add_entity(node_pos, "bosses_farlands:cube_projectile", minetest.serialize(minetest.get_node(node_pos))) rock:set_attach(self.object, "rock_l", {x=0,y=0,z=0}, {x=0,y=0,z=0}) rock:set_properties({visual_size = {x = 0.2, y = 0.2}}) minetest.remove_node(node_pos) if self.target then yaw = minetest.dir_to_yaw(vector.direction(pos, self.target:get_pos())) self.object:set_yaw(yaw) end set_anim(self.object, "throw", false) wait(self, anim_time("throw"), function() if self.target then yaw = minetest.dir_to_yaw(vector.direction(pos, self.target:get_pos())) self.object:set_yaw(yaw) end rock:set_detach() local rock_pos = rock:get_pos() rock_pos.y = rock_pos.y + 2 rock:set_pos(rock_pos) rock:set_properties({visual_size = {x = 1, y = 1}}) rock:set_velocity(vector.multiply(minetest.yaw_to_dir(yaw), 10)) rock:set_yaw(yaw) apply_gravity(rock) rock:get_luaentity().flying = true set_anim(self.object, "throw_e", false) wait(self, anim_time("throw_e")) end) end) return true end minetest.register_entity("bosses_farlands:zombie_brute", { hp_max = 1, physical = true, collide_with_objects = true, weight = 5, collisionbox = {-0.7,0,-0.7, 0.7,2,0.7}, visual = "mesh", visual_size = {x = 5, y = 5}, mesh = "bosses_zombie_brute.b3d", textures = {"bosses_zombie_brute.png"}, makes_footstep_sound = false, stepheight = 0, backface_culling = true, nametag = "Zombie Brute", nametag_color = "#FF0000", on_activate = function(self, staticdata, dtime_s) if staticdata == "" then self.hp = 80 else local s = minetest.deserialize(staticdata) self.hp = s.hp end self.wanted_vel = vector.new(0, 0, 0) self.status = "idle" self.walked_time = 0 self.object:set_armor_groups({fleshy = 100, immortal = 1}) apply_gravity(self.object) set_anim(self.object, "idle") end, on_step = function(self, dtime) local pos = self.object:get_pos() local yaw = self.object:get_yaw() local vel = vector.subtract(self.object:get_velocity(), self.wanted_vel) local v = vector.length(vel) -- Speed. -- Friction: if v ~= 0 then local pn = 1 if v < 0 then v = -v pn = -1 end -- Air fricion: v = v - dtime * 0.5 * v^2 -- Ground friction: if vel.y == 0 and is_node_bellow(pos, 1) then v = v - dtime v = math.floor(v*5)/5 else v = math.floor(v*100)/100 end v = math.max(v, 0) * pn end vel = vector.multiply(vector.normalize(vel), v) -- Go for target: if self.target then local target_pos = self.target:get_pos() if (self.status == "idle" or self.status == "walk") and vector.distance(pos, target_pos) <= 1.5 then set_anim(self.object, "punch", false) wait(self, anim_time("punch")) self.walked_time = 0 yaw = minetest.dir_to_yaw(vector.direction(pos, target_pos)) self.object:set_yaw(yaw) self.target:set_hp(self.target:get_hp()-3) elseif self.status == "walk" then local dir = vector.direction(pos, target_pos) self.object:set_yaw(minetest.dir_to_yaw(dir)) yaw = minetest.dir_to_yaw(vector.direction(pos, target_pos)) self.object:set_yaw(yaw) if self.walked_time >= 10 and vector.distance(pos, target_pos) <= 8 then if not throw(self, pos, yaw) then -- Do some other attack. self.walked_time = 0 end else self.wanted_vel = dir self.walked_time = self.walked_time + dtime end elseif self.status == "idle" then self.status = "walk" set_anim(self.object, "walk") elseif self.status == "wait" then self.wait_timer = self.wait_timer + dtime if self.wait_timer >= self.wait_end then self.status = "idle" if self.after_wait then self.after_wait(self) else set_anim(self.object, "idle") end end end end self.object:set_velocity(vector.add(vel, self.wanted_vel)) end, on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir) if not puncher:is_player() then return end local vel = self.object:get_velocity() local knockback = 3 local damage = 10 self.hp = self.hp - damage self.target = puncher local player_name = puncher:get_player_name() if not fighting_players[player_name] then musics[player_name] = minetest.sound_play("bosses_farlands_music", { to_player = player_name, gain = 1.0, loop = true, }) hud_ids[player_name] = { puncher:hud_add({ hud_elem_type = "image", position = {x=0.2, y=0.1}, name = "farlands_bosses_zombie_brute_health_bg", scale = {x=5, y=5}, text = "bosses_farlands_health_bg.png", direction = 0, alignment = {x=1, y=1}, offset = {x=0, y=0}, }), puncher:hud_add({ hud_elem_type = "statbar", position = {x=0.2, y=0.1}, name = "farlands_bosses_zombie_brute_health", text = "bosses_farlands_health.png", number = self.hp*2, direction = 0, offset = {x=10, y=5}, size = {x=10, y=50}, }), } else puncher:hud_change(hud_ids[player_name][2], "number", self.hp*2) end fighting_players[player_name] = self.object minetest.sound_play("default_punch", { gain = 3, object = self.object, max_hear_distance = 15, }) if dir then dir.y = dir.y + 1 dir = vector.normalize(dir) vel = vector.add(vel, vector.multiply(dir, knockback)) end minetest.chat_send_all(self.hp) if self.hp <= 0 then die(self) end self.object:set_velocity(vel) end, get_staticdata = function(self) return minetest.serialize({hp = self.hp}) end, }) bosses_farlands.register_egg("bosses_farlands:zombie_brute_egg", "bosses_farlands:zombie_brute", { description = "Zombie Brute (boss)", inventory_image = "mobs_chicken_egg.png^(default_dirt.png^[mask:mobs_chicken_egg_overlay.png)", })