diff --git a/mods/CORE/mcl_events/API.md b/mods/CORE/mcl_events/API.md new file mode 100644 index 000000000..c94328e50 --- /dev/null +++ b/mods/CORE/mcl_events/API.md @@ -0,0 +1,27 @@ +## mcl_events +### Registering Events + `mlc_events.register_event("name",def)` + +#### Event Definition + { + stage = 0, + max_stage = 1, + percent = 100, + bars = {}, + completed = false, + cond_start = function() end, + --return table of paramtables e.g. { { player = playername, pos = position, ... } }, custom parameters will be passed to the event object/table + on_step = function(event) end, + --this function is run every game step when the event is active + on_start = function(event) end, + -- this function is run when the event starts + on_stage_begin = function(event) end, + -- this function runs when a new stage of the event starts + cond_progress = function(event) end, --return false or next stage id + --this function checks if the event should progress to the next (or any other) stage + cond_complete = function(event) end, + --return true if event finished successfully +} + +### Debugging + * /event_start -- starts the given event at the current player coordinates diff --git a/mods/CORE/mcl_events/init.lua b/mods/CORE/mcl_events/init.lua new file mode 100644 index 000000000..5ec2577a9 --- /dev/null +++ b/mods/CORE/mcl_events/init.lua @@ -0,0 +1,151 @@ +mcl_events = {} +mcl_events.registered_events = {} +local disabled_events = minetest.settings:get("mcl_disabled_events") +if disabled_events then disabled_events = disabled_events:split(",") +else disabled_events = {} end +local DBG = minetest.settings:get_bool("mcl_logging_event_api",false) +local active_events = {} + +local event_tpl = { + stage = 0, + max_stage = 1, + percent = 100, + bars = {}, + completed = false, + cond_start = function(event) end, --return table of positions + on_step = function(event) end, + on_start = function(event) end, + on_stage_begin = function(event) end, + cond_progress = function(event) end, --return next stage + cond_complete = function(event) end, --return success +} + +local function mcl_log(m,l) + if DBG then + if not l then l = "action" end + minetest.log(l,"[mcl_events] "..m) + end +end + +function mcl_events.register_event(name,def) + if table.indexof(disabled_events,name) ~= -1 then return end + mcl_events.registered_events[name] = def + mcl_events.registered_events[name].name = name +end + +local function addbars(self) + if not self.enable_bossbar then return end + for _,player in pairs(minetest.get_connected_players()) do + if vector.distance(self.pos,player:get_pos()) < 64 then + local bar = mcl_bossbars.add_bar(player, {color = "red", text = self.readable_name .. ": Wave "..self.stage.." / "..self.max_stage, percentage = self.percent }, true,1) + table.insert(self.bars,bar) + end + end +end + +local function start_event(p,e) + mcl_log("[mcl_events] Event started: "..e.readable_name.." at "..minetest.pos_to_string(vector.round(p.pos))) + local idx = #active_events + 1 + active_events[idx] = table.copy(e) + setmetatable(active_events[idx],{__index = event_tpl}) + for k,v in pairs(p) do active_events[idx][k] = v end + active_events[idx].stage = 0 + active_events[idx].percent = 100 + active_events[idx].bars = {} + active_events[idx].time_start = os.time() + if active_events[idx].on_start then + active_events[idx]:on_start(p.pos) + end + addbars(active_events[idx]) +end + +local function finish_event(self,idx) + mcl_log("[mcl_events] Finished: "..self.readable_name.." at "..minetest.pos_to_string(vector.round(self.pos))) + if self.on_complete then self:on_complete() end + for _,b in pairs(self.bars) do + mcl_bossbars.remove_bar(b) + end + table.remove(active_events,idx) +end + +local etime = 0 +function check_events(dtime) + --process active events + for idx,ae in pairs(active_events) do + if ae.cond_complete and ae:cond_complete() then + ae.finished = true + finish_event(ae,idx) + elseif not ae.cond_complete and ae.max_stage and ae.max_stage <= ae.stage then + ae.finished = true + finish_event(ae,idx) + elseif not ae.finished and ae.cond_progress then + local p = ae:cond_progress() + if p == true then + ae.stage = ae.stage + 1 + if ae:on_stage_begin() == true then + mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." failed at stage_begin of stage "..ae.stage ) + active_events[idx] = nil + end + elseif tonumber(p) then + ae.stage = tonumber(p) or ae.stage + 1 + ae:on_stage_begin() + end + elseif not ae.finished and ae.on_step then + ae:on_step(dtime) + end + addbars(ae) + end + -- check if a new event should be started + etime = etime - dtime + if etime > 0 then return end + etime = 10 + for _,e in pairs(mcl_events.registered_events) do + local pp = e.cond_start() + if pp then + for _,p in pairs(pp) do + local start = true + if e.exclusive_to_area then + for _,ae in pairs(active_events) do + if e.name == ae.name and vector.distance(p.pos,ae.pos) < e.exclusive_to_area then start = false end + end + end + if start then + start_event(p,e) + elseif DBG then + mcl_log("[mcl_events] Event "..e.readable_name.." already active at "..minetest.pos_to_string(vector.round(p.pos))) + end + end + end + end + for idx,ae in pairs(active_events) do + local player_near = false + for _,pl in pairs(minetest.get_connected_players()) do + if ae.pos and vector.distance(pl:get_pos(),ae.pos) < 64 then player_near = true end + end + if ae.pos and not player_near then + mcl_log("[mcl_events] Event "..ae.readable_name.." at "..minetest.pos_to_string(vector.round(ae.pos)).." aborted - no players near." ) + active_events[idx] = nil + end + end +end + +minetest.register_globalstep(check_events) + +mcl_info.register_debug_field("Active Events",{ + level = 4, + func = function(pl,pos) + return tostring(#active_events) + end +}) + +minetest.register_chatcommand("event_start",{ + privs = {debug = true}, + description = "Debug command to start events", + func = function(pname,param) + local p = minetest.get_player_by_name(pname) + local evdef = mcl_events.registered_events[param] + if not evdef then return false,"Event "..param.." doesn't exist.'" end + start_event({pos=p:get_pos(),player=pname,factor=1},evdef) + return true,"Started event "..param + end, +}) diff --git a/mods/CORE/mcl_events/mod.conf b/mods/CORE/mcl_events/mod.conf new file mode 100644 index 000000000..29a45628a --- /dev/null +++ b/mods/CORE/mcl_events/mod.conf @@ -0,0 +1,3 @@ +name = mcl_events +author = cora +depends = mcl_mobs,mcl_bossbars, mcl_info diff --git a/mods/ENTITIES/mobs_mc/villager_evoker.lua b/mods/ENTITIES/mobs_mc/villager_evoker.lua index b85001985..242fa802a 100644 --- a/mods/ENTITIES/mobs_mc/villager_evoker.lua +++ b/mods/ENTITIES/mobs_mc/villager_evoker.lua @@ -11,6 +11,8 @@ local S = minetest.get_translator("mobs_mc") local pr = PseudoRandom(os.time()*666) +local spawned_vexes = {} --this is stored locally so the mobs engine doesn't try to store it in staticdata + mcl_mobs:register_mob("mobs_mc:evoker", { description = S("Evoker"), type = "monster", @@ -42,16 +44,24 @@ mcl_mobs:register_mob("mobs_mc:evoker", { attack_type = "dogfight", -- Summon vexes custom_attack = function(self, to_attack) - local r = pr:next(2,4) + if not spawned_vexes[self] then spawned_vexes[self] = {} end + if #spawned_vexes[self] >= 7 then return end + for k,v in pairs(spawned_vexes[self]) do + if not v or v.health <= 0 then table.remove(spawned_vexes[self],k) end + end + local r = pr:next(1,4) local basepos = self.object:get_pos() basepos.y = basepos.y + 1 for i=1, r do local spawnpos = vector.add(basepos, minetest.yaw_to_dir(pr:next(0,360))) local vex = minetest.add_entity(spawnpos, "mobs_mc:vex") local ent = vex:get_luaentity() + -- Mark vexes as summoned and start their life clock (they take damage it reaches 0) ent._summoned = true ent._lifetimer = pr:next(33, 108) + + table.insert(spawned_vexes[self],ent) end end, shoot_interval = 15, diff --git a/mods/ENVIRONMENT/mcl_raids/init.lua b/mods/ENVIRONMENT/mcl_raids/init.lua new file mode 100644 index 000000000..5a4575670 --- /dev/null +++ b/mods/ENVIRONMENT/mcl_raids/init.lua @@ -0,0 +1,393 @@ +-- mcl_raids +mcl_raids = {} +local S = minetest.get_translator(minetest.get_current_modname()) + +-- 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.player_get_effect(cmi_cause.puncher, "bad_omen") + if not lv then lv = 0 + else lv = lv.factor end + lv = math.max(5,lv + 1) + mcl_potions.bad_omen_func(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) + mcl_mobs:gopath(l,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,128,{"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 = minetest.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) + local r = {} + for _,p in pairs(minetest.get_connected_players()) do + if mcl_potions.player_has_effect(p,"bad_omen") then + local raid_pos = mcl_raids.find_village(p:get_pos()) + if raid_pos then + 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.player_get_effect(minetest.get_player_by_name(self.player), "bad_omen") + if lv and lv.factor and lv.factor > 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.player_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 +}) diff --git a/mods/ENVIRONMENT/mcl_raids/mod.conf b/mods/ENVIRONMENT/mcl_raids/mod.conf new file mode 100644 index 000000000..b4616e56b --- /dev/null +++ b/mods/ENVIRONMENT/mcl_raids/mod.conf @@ -0,0 +1,3 @@ +name = mcl_raids +author = PrairieWind +depends = mcl_events, mobs_mc, mcl_potions, mcl_bells, mcl_achievements diff --git a/mods/ENVIRONMENT/mcl_raids/textures/mcl_raids_hero_of_the_village_icon.png b/mods/ENVIRONMENT/mcl_raids/textures/mcl_raids_hero_of_the_village_icon.png new file mode 100644 index 000000000..0db5d9613 Binary files /dev/null and b/mods/ENVIRONMENT/mcl_raids/textures/mcl_raids_hero_of_the_village_icon.png differ diff --git a/mods/ENVIRONMENT/mcl_zombie_sieges/init.lua b/mods/ENVIRONMENT/mcl_zombie_sieges/init.lua new file mode 100644 index 000000000..8a43b0e7e --- /dev/null +++ b/mods/ENVIRONMENT/mcl_zombie_sieges/init.lua @@ -0,0 +1,77 @@ + +local function check_spawn_pos(pos) + return minetest.get_natural_light(pos) < 7 +end + +local function spawn_zombies(self) + local nn = minetest.find_nodes_in_area_under_air(vector.offset(self.pos,-16,-16,-16),vector.offset(self.pos,16,16,16),{"group:solid"}) + table.shuffle(nn) + for i=1,20 do + local p = vector.offset(nn[i%#nn],0,1,0) + if check_spawn_pos(p) then + local m = mcl_mobs.spawn(p,"mobs_mc:zombie") + local l = m:get_luaentity() + mcl_mobs:gopath(m:get_luaentity(),self.pos) + table.insert(self.mobs,m) + self.health_max = self.health_max + l.health + end + end +end + +mcl_events.register_event("zombie_siege",{ + readable_name = "Zombie Siege", + max_stage = 1, + health = 1, + health_max = 1, + exclusive_to_area = 128, + enable_bossbar = false, + cond_start = function(self) + local pr = PseudoRandom(minetest.get_day_count()) + local rnd = pr:next(1,10) + local t = minetest.get_timeofday() + local r = {} + for _,p in pairs(minetest.get_connected_players()) do + local village = mcl_raids.find_village(p:get_pos()) + if t < 0.04 and village and rnd == 1 then + table.insert(r,{ player = p:get_player_name(), pos = village}) + end + end + if #r > 0 then return r end + end, + on_start = function(self) + self.mobs = {} + self.health_max = 1 + self.health = 0 + end, + cond_progress = function(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 + self.mobs = m + self.health = h + self.percent = math.max(0,(self.health / self.health_max ) * 100) + if #m < 1 then + return true end + end, + on_stage_begin = spawn_zombies, + cond_complete = function(self) + local m = {} + for k,o in pairs(self.mobs) do + if o and o:get_pos() then + local l = o:get_luaentity() + table.insert(m,o) + end + end + return self.stage >= self.max_stage and #m < 1 + end, + on_complete = function(self) + --minetest.log("SIEGE complete") + --awards.unlock(self.player,"mcl:hero_of_the_village") + end, +}) diff --git a/mods/ENVIRONMENT/mcl_zombie_sieges/mod.conf b/mods/ENVIRONMENT/mcl_zombie_sieges/mod.conf new file mode 100644 index 000000000..0263d367a --- /dev/null +++ b/mods/ENVIRONMENT/mcl_zombie_sieges/mod.conf @@ -0,0 +1,3 @@ +name = mcl_zombie_sieges +author = cora +depends = mcl_events, mcl_raids diff --git a/mods/HUD/mcl_achievements/init.lua b/mods/HUD/mcl_achievements/init.lua index 53116c183..ced758948 100644 --- a/mods/HUD/mcl_achievements/init.lua +++ b/mods/HUD/mcl_achievements/init.lua @@ -529,3 +529,21 @@ awards.register_achievement("mcl:obsidian", { type = "Advancement", group = "Overworld", }) + +awards.register_achievement("mcl:hero_of_the_village", { + title = S("Hero of the Village"), + description = S("Successfully defend a village from a raid"), + icon = "mcl_raids_hero_of_the_village_icon.png", + type = "Advancement", + group = "Adventure", + secret = true, +}) + +awards.register_achievement("mcl:voluntary_exile", { + title = S("Voluntary Exile"), + description = S("Kill a raid captain. Maybe consider staying away from the local villages for the time being..."), + icon = "mcl_potions_effect_bad_omen.png", + type = "Advancement", + group = "Adventure", + secret = true, +}) diff --git a/mods/ITEMS/mcl_bows/rocket.lua b/mods/ITEMS/mcl_bows/rocket.lua index 6180472c1..e71c0c122 100644 --- a/mods/ITEMS/mcl_bows/rocket.lua +++ b/mods/ITEMS/mcl_bows/rocket.lua @@ -19,6 +19,7 @@ local function dir_to_pitch(dir) end local function damage_explosion(self, damagemulitplier) + if self._harmless then return end local p = self.object:get_pos() if not p then return end mcl_explosions.explode(p, 3, {}) diff --git a/mods/ITEMS/mcl_potions/commands.lua b/mods/ITEMS/mcl_potions/commands.lua index 1fbf591d9..76ac71e72 100644 --- a/mods/ITEMS/mcl_potions/commands.lua +++ b/mods/ITEMS/mcl_potions/commands.lua @@ -19,6 +19,7 @@ get_chat_function["water_breathing"] = mcl_potions.water_breathing_func 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 minetest.register_chatcommand("effect",{ params = S(" []"), diff --git a/mods/ITEMS/mcl_potions/functions.lua b/mods/ITEMS/mcl_potions/functions.lua index 15be8a90c..de3f6df10 100644 --- a/mods/ITEMS/mcl_potions/functions.lua +++ b/mods/ITEMS/mcl_potions/functions.lua @@ -9,6 +9,7 @@ EF.leaping = {} EF.swift = {} -- for swiftness AND slowness EF.night_vision = {} EF.fire_proof = {} +EF.bad_omen = {} local EFFECT_TYPES = 0 for _,_ in pairs(EF) do @@ -350,6 +351,26 @@ minetest.register_globalstep(function(dtime) end + -- Check for Bad Omen + for player, vals in pairs(EF.bad_omen) do + + is_player = player:is_player() + + EF.bad_omen[player].timer = EF.bad_omen[player].timer + dtime + + if player:get_pos() then mcl_potions._add_spawner(player, "#0b6138") end + + if EF.bad_omen[player] and EF.bad_omen[player].timer >= EF.bad_omen[player].dur then + EF.bad_omen[player] = nil + if is_player then + meta = player:get_meta() + meta:set_string("_has_bad_omen", minetest.serialize(EF.bad_omen[player])) + potions_set_hud(player) + end + end + + end + end) -- Prevent damage to player with Fire Resistance enabled @@ -386,7 +407,8 @@ function mcl_potions._clear_cached_player_data(player) EF.swift[player] = nil EF.night_vision[player] = nil EF.fire_proof[player] = nil - + EF.bad_omen[player] = nil + meta = player:get_meta() meta:set_int("night_vision", 0) end @@ -400,9 +422,9 @@ function mcl_potions._reset_player_effects(player, set_hud) mcl_potions.make_invisible(player, false) playerphysics.remove_physics_factor(player, "jump", "mcl_potions:leaping") - + playerphysics.remove_physics_factor(player, "speed", "mcl_potions:swiftness") - + mcl_weather.skycolor.update_sky_color({player}) mcl_potions._clear_cached_player_data(player) @@ -429,6 +451,7 @@ function mcl_potions._save_player_effects(player) meta:set_string("_is_swift", minetest.serialize(EF.swift[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])) end @@ -480,6 +503,10 @@ function mcl_potions._load_player_effects(player) EF.fire_proof[player] = minetest.deserialize(meta:get_string("_is_fire_proof")) end + if minetest.deserialize(meta:get_string("_has_bad_omen")) then + EF.bad_omen[player] = minetest.deserialize(meta:get_string("_has_bad_omen")) + end + end -- Returns true if player has given effect @@ -490,6 +517,18 @@ function mcl_potions.player_has_effect(player, effect_name) return EF[effect_name][player] ~= nil end +function mcl_potions.player_get_effect(player, effect_name) + if not EF[effect_name] or not EF[effect_name][player] then + return false + end + return EF[effect_name][player] +end + +function mcl_potions.player_clear_effect(player,effect) + EF[effect][player] = nil + potions_set_icons(player) +end + minetest.register_on_leaveplayer( function(player) mcl_potions._save_player_effects(player) mcl_potions._clear_cached_player_data(player) -- clearout the buffer to prevent looking for a player not there @@ -966,3 +1005,18 @@ function mcl_potions._extinguish_nearby_fire(pos, radius) end return exting end + +function mcl_potions.bad_omen_func(player, factor, duration) + if not EF.bad_omen[player] then + EF.bad_omen[player] = {dur = duration, timer = 0, factor = factor} + else + local victim = EF.bad_omen[player] + victim.dur = math.max(duration, victim.dur - victim.timer) + victim.timer = 0 + victim.factor = factor + end + + if player:is_player() then + potions_set_icons(player) + end +end diff --git a/mods/ITEMS/mcl_potions/textures/mcl_potions_effect_bad_omen.png b/mods/ITEMS/mcl_potions/textures/mcl_potions_effect_bad_omen.png new file mode 100644 index 000000000..dfe8f0332 Binary files /dev/null and b/mods/ITEMS/mcl_potions/textures/mcl_potions_effect_bad_omen.png differ diff --git a/settingtypes.txt b/settingtypes.txt index 39dbc2e90..78aa9d44b 100644 --- a/settingtypes.txt +++ b/settingtypes.txt @@ -42,6 +42,9 @@ mcl_tnt_griefing (TNT destroys blocks) bool true # Comma separated list of disabled structure names mcl_disabled_structures (Disabled structures) string +# Comma separated list of disabled event names +mcl_disabled_events (Disabled events) string + [Players] # If enabled, players respawn at the bed they last lay on instead of normal # spawn. @@ -233,4 +236,7 @@ mcl_logging_mapgen (Chunk generation logging) bool false mcl_logging_structures (Structure generation logging) bool true #Complete debug logging for mcl_signs events. Use this if you have issues with signs. -mcl_logging_mcl_signs (Complete debug logging for mcl_signs) bool true +mcl_logging_mcl_signs (Complete debug logging for mcl_signs) bool false + +#Debug logging for mcl_events. +mcl_logging_event_api (Debug logging for mcl_events) bool false