-- mcl_raids mcl_raids = {} local S = minetest.get_translator(minetest.get_current_modname()) local gamerule_disableRaids = false vl_tuning.setting("gamerule:disableRaids", "bool", { description = S("Whether raids are disabled"), default = false, set = function(val) gamerule_disableRaids = val end, get = function() return gamerule_disableRaids end, }) -- Define the amount of illagers to spawn each wave. local waves = { { ["mobs_mc:pillager"] = 5, ["mobs_mc:vindicator"] = 1, }, { ["mobs_mc:pillager"] = 4, ["mobs_mc:vindicator"] = 3, }, { ["mobs_mc:pillager"] = 4, ["mobs_mc:vindicator"] = 1, ["mobs_mc:witch"] = 1, --["mobs_mc:ravager"] = 1, }, { ["mobs_mc:pillager"] = 5, ["mobs_mc:vindicator"] = 2, ["mobs_mc:witch"] = 3, }, { ["mobs_mc:pillager"] = 5, ["mobs_mc:vindicator"] = 5, ["mobs_mc:witch"] = 1, ["mobs_mc:evoker"] = 1, }, } local extra_wave = { ["mobs_mc:pillager"] = 5, ["mobs_mc:vindicator"] = 5, ["mobs_mc:witch"] = 1, ["mobs_mc:evoker"] = 1, --["mobs_mc:ravager"] = 2, } local oban_layers = { { pattern = "rhombus", color = "unicolor_cyan" }, { color = "unicolor_grey", pattern = "stripe_bottom" }, { pattern = "stripe_center", color = "unicolor_darkgrey" }, { color = "unicolor_black", pattern = "stripe_middle" }, { pattern = "half_horizontal", color = "unicolor_grey" }, { color = "unicolor_grey", pattern = "circle" }, { pattern = "border", color = "unicolor_black" } } local oban_def = table.copy(minetest.registered_entities["mcl_banners:standing_banner"]) oban_def.visual_size = { x=1, y=1 } local old_step = oban_def.on_step oban_def.on_step = function(self,dtime) if not self.object:get_attach() then return self.object:remove() end if old_step then return old_step(self.dtime) end end minetest.register_entity(":mcl_raids:ominous_banner",oban_def) function mcl_raids.drop_obanner(pos) local it = ItemStack("mcl_banners:banner_item_white") it:get_meta():set_string("layers",minetest.serialize(oban_layers)) it:get_meta():set_string("name",S("Ominous Banner")) minetest.add_item(pos,it) end function mcl_raids.promote_to_raidcaptain(c) -- object if not c or not c:get_pos() then return end local pos = c:get_pos() local l = c:get_luaentity() l._banner = minetest.add_entity(pos,"mcl_raids:ominous_banner") l._banner:set_properties({textures = {mcl_banners.make_banner_texture("unicolor_white", oban_layers)}}) l._banner:set_attach(c,"",vector.new(-1,5.5,0),vector.new(0,0,0),true) l._raidcaptain = true local old_ondie = l.on_die l.on_die = function(self, pos, cmi_cause) if l._banner then l._banner:remove() l._banner = nil mcl_raids.drop_obanner(pos) if cmi_cause and cmi_cause.type == "punch" and cmi_cause.puncher:is_player() then awards.unlock(cmi_cause.puncher:get_player_name(), "mcl:voluntary_exile") local lv = mcl_potions.get_effect_level(cmi_cause.puncher, "bad_omen") lv = math.max(5,lv + 1) mcl_potions.give_effect_by_level("bad_omen", cmi_cause.puncher, lv, 6000) end end if old_ondie then return old_ondie(self,pos,cmi_cause) end end end function mcl_raids.is_raidcaptain_near(pos) for k,v in pairs(minetest.get_objects_inside_radius(pos,32)) do local l = v:get_luaentity() if l and l._raidcaptain then return true end end end function mcl_raids.register_possible_raidcaptain(mob) local old_on_spawn = minetest.registered_entities[mob].on_spawn local old_on_pick_up = minetest.registered_entities[mob].on_pick_up if not minetest.registered_entities[mob].pick_up then minetest.registered_entities[mob].pick_up = {} end table.insert(minetest.registered_entities[mob].pick_up,"mcl_banners:banner_item_white") minetest.registered_entities[mob].on_pick_up = function(self,e) local stack = ItemStack(e.itemstring) if not self._raidcaptain and stack:get_meta():get_string("name"):find("Ominous Banner") then stack:take_item(1) mcl_raids.promote_to_raidcaptain(self.object) return stack end if old_on_pick_up then return old_on_pick_up(self,e) end end minetest.registered_entities[mob].on_spawn = function(self) if not mcl_raids.is_raidcaptain_near(self.object:get_pos()) then mcl_raids.promote_to_raidcaptain(self.object) end if old_on_spawn then return old_on_spawn(self) end end end mcl_raids.register_possible_raidcaptain("mobs_mc:pillager") mcl_raids.register_possible_raidcaptain("mobs_mc:vindicator") mcl_raids.register_possible_raidcaptain("mobs_mc:evoker") function mcl_raids.spawn_raid(event) local pos = event.pos local wave = event.stage local illager_count = 0 local spawnable = false local r = 32 local n = 12 local i = math.random(1, n) local raid_pos = vector.offset(pos,r * math.cos(((i-1)/n) * (2*math.pi)),0, r * math.sin(((i-1)/n) * (2*math.pi))) local sn = minetest.find_nodes_in_area_under_air(vector.offset(raid_pos,-5,-50,-5), vector.offset(raid_pos,5,50,5), {"group:grass_block", "group:grass_block_snow", "group:snow_cover", "group:sand", "mcl_core:ice"}) mcl_bells.ring_once(pos) if sn and #sn > 0 then local spawn_pos = sn[math.random(#sn)] if spawn_pos then minetest.log("action", "[mcl_raids] Raid Spawn Position chosen at " .. minetest.pos_to_string(spawn_pos) .. ".") event.health_max = 0 local w if event.stage <= #waves then w= waves[event.stage] else w = extra_wave end for m,c in pairs(w) do for i=1,c do local p = vector.offset(spawn_pos,0,1,0) local mob = mcl_mobs.spawn(p,m) local l = mob:get_luaentity() if l then l.raidmob = true event.health_max = event.health_max + l.health table.insert(event.mobs,mob) --minetest.log("action", "[mcl_raids] Here we go. Raid time") l:gopath(pos) end end end if event.stage == 1 then table.shuffle(event.mobs) mcl_raids.promote_to_raidcaptain(event.mobs[1]) end minetest.log("action", "[mcl_raids] Raid Spawned. Illager Count: " .. #event.mobs .. ".") return #event.mobs == 0 else minetest.log("action", "[mcl_raids] Raid Spawn Postion not chosen.") end elseif not sn then minetest.log("action", "[mcl_raids] Raid Spawn Position error, no appropriate site found.") end return true end function mcl_raids.find_villager(pos) local obj = minetest.get_objects_inside_radius(pos, 8) for _, objects in ipairs(obj) do local object = objects:get_luaentity() if object then if object.name ~= "mobs_mc:villager" then return elseif object.name == "mobs_mc:villager" then --minetest.log("action", "[mcl_raids] Villager Found.") return true else --minetest.log("action", "[mcl_raids] No Villager Found.") return false end end end end function mcl_raids.find_bed(pos) return minetest.find_node_near(pos,32,{"mcl_beds:bed_red_bottom"}) end function mcl_raids.find_village(pos) local bed = mcl_raids.find_bed(pos) if bed and mcl_raids.find_villager(bed) then return bed end end local function get_point_on_circle(pos,r,n) local rt = {} for i=1, n do table.insert(rt,vector.offset(pos,r * math.cos(((i-1)/n) * (2*math.pi)),0, r* math.sin(((i-1)/n) * (2*math.pi)) )) end table.shuffle(rt) return rt[1] end local function start_firework_rocket(pos) local p = get_point_on_circle(pos,math.random(32,64),32) local n = minetest.get_node(p) local l = mcl_util.get_natural_light(pos,0.5) if n.name ~= "air" or l <= minetest.LIGHT_MAX then return end local o = minetest.add_entity(p,"mcl_bows:rocket_entity") o:get_luaentity()._harmless = true o:set_acceleration(vector.new(math.random(0,2),math.random(30,50),math.random(0,2))) end local function make_firework(pos,stime) if os.time() - stime > 60 then return end for i=1,math.random(25) do minetest.after(math.random(i),start_firework_rocket,pos) end minetest.after(10,make_firework,pos,stime) end local function is_player_near(self) for _,pl in pairs(minetest.get_connected_players()) do if self.pos and vector.distance(pl:get_pos(),self.pos) < 64 then return true end end end local function check_mobs(self) local m = {} local h = 0 for k,o in pairs(self.mobs) do if o and o:get_pos() then local l = o:get_luaentity() h = h + l.health table.insert(m,o) end end if #m == 0 then --if no valid mobs in table search if there are any (reloaded ones) in the area for k,o in pairs(minetest.get_objects_inside_radius(self.pos,64)) do local l = o:get_luaentity() if l and l.raidmob then local l = o:get_luaentity() h = h + l.health table.insert(m,o) end end end self.mobs = m return h end mcl_events.register_event("raid",{ readable_name = "Raid", max_stage = 5, health = 1, health_max = 1, exclusive_to_area = 128, enable_bossbar = true, cond_start = function(self) if gamerule_disableRaids then return false end --minetest.log("Cond start raid") local r = {} for _,p in pairs(minetest.get_connected_players()) do if mcl_potions.has_effect(p,"bad_omen") then local raid_pos = mcl_raids.find_village(p:get_pos()) if raid_pos then --minetest.log("We have a raid position. Start raid") table.insert(r,{ player = p:get_player_name(), pos = raid_pos }) end end end if #r > 0 then return r end end, on_start = function(self) self.mobs = {} self.health_max = 1 self.health = 0 local lv = mcl_potions.get_effect_level(minetest.get_player_by_name(self.player), "bad_omen") if lv > 1 then self.max_stage = 6 end end, cond_progress = function(self) if not is_player_near(self) then return false end self.health = check_mobs(self) self.percent = math.max(0,(self.health / self.health_max ) * 100) if #self.mobs < 1 then return true end end, on_stage_begin = mcl_raids.spawn_raid, cond_complete = function(self) if not is_player_near(self) then return false end --let the event api handle cancel the event when no players are near --without this check it would sort out the unloaded mob entities and --think the raid is defeated. check_mobs(self) return self.stage >= self.max_stage and #self.mobs < 1 end, on_complete = function(self) awards.unlock(self.player,"mcl:hero_of_the_village") mcl_potions.clear_effect(minetest.get_player_by_name(self.player),"bad_omen") make_firework(self.pos,os.time()) end, }) minetest.register_chatcommand("raidcap",{ privs = {debug = true}, func = function(pname,param) local c = minetest.add_entity(minetest.get_player_by_name(pname):get_pos(),"mobs_mc:pillager") mcl_raids.promote_to_raidcaptain(c) end, }) minetest.register_chatcommand("dump_banner_layers",{ privs = {debug = true}, func = function(pname,param) local p = minetest.get_player_by_name(pname) mcl_raids.drop_obanner(vector.offset(p:get_pos(),1,1,1)) for k,v in pairs(minetest.get_objects_inside_radius(p:get_pos(),5)) do local l = v:get_luaentity() if l and l.name == "mcl_banners:standing_banner" then minetest.log(dump(l._base_color)) minetest.log(dump(l._layers)) end end end }) local function is_new_years() local d = os.date("*t") return d.month == 1 and d.day == 1 and d.hour < 1 end mcl_events.register_event("new_years",{ stage = 0, max_stage = 1, readable_name = "New Years", pos = vector.new(0,0,0), exclusive_to_area = 256, cond_start = function(event) if not is_new_years() then return false end local r = {} for _,p in pairs(minetest.get_connected_players()) do table.insert(r,{ player = p:get_player_name(), pos = p:get_pos()}) end return r end, on_start = function(self) minetest.chat_send_player(self.player," Happy new year <3") end, on_step = function(self,dtime) if not self.timer or self.timer < 0 then self.timer = math.random(1,5) for i=1,math.random(8) do minetest.after(math.random(i),start_firework_rocket,minetest.get_player_by_name(self.player):get_pos()) end end self.timer = self.timer - dtime end, cond_complete = function(event) return not is_new_years() end, --return success })