diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_swarmer_crash.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_swarmer_crash.dmm new file mode 100644 index 00000000000..a76872f1af9 --- /dev/null +++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_swarmer_crash.dmm @@ -0,0 +1,24 @@ +"a" = (/turf/template_noop,/area/template_noop) +"b" = (/turf/simulated/mineral/volcanic/lava_land_surface,/area/template_noop) +"c" = (/turf/simulated/wall/mineral/plastitanium,/area/ruin/unpowered) +"d" = (/turf/simulated/floor/mineral/plastitanium/red{oxygen = 14; nitrogen = 23; temperature = 300},/area/ruin/unpowered) +"e" = (/obj/structure/shuttle/engine/propulsion{dir = 4},/turf/template_noop,/area/ruin/unpowered) +"f" = (/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon,/turf/simulated/floor/mineral/plastitanium/red{oxygen = 14; nitrogen = 23; temperature = 300},/area/ruin/unpowered) +"g" = (/obj/effect/baseturf_helper/lava_land/surface,/turf/simulated/floor/mineral/plastitanium/red{oxygen = 14; nitrogen = 23; temperature = 300},/area/ruin/unpowered) + +(1,1,1) = {" +aaabbaaaaaaaaa +aaabbbbbaaaaaa +aabbbbbbbbbaaa +abbbbbbaaabbaa +bbbbbbcccaaacc +abbbbcddddddce +acdaaadddddcea +aaddaadgfddcea +acddaddddddcea +aacbbcdddddace +abbbbacccccacc +abbbbbbbbbbaaa +aaaabbbbbbbaaa +aaaaaaaabbaaaa +"} diff --git a/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm b/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm index bb3d7ac7f88..4daedb4887b 100644 --- a/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm +++ b/_maps/map_files/RandomRuins/SpaceRuins/derelict4.dmm @@ -22,11 +22,7 @@ /turf/simulated/floor/plating/airless, /area/ruin/unpowered) "h" = ( -/turf/simulated/floor/plasteel/airless{ - icon_plating = "plating"; - icon_regular_floor = "shuttlefloor"; - icon_state = "shuttlefloor" - }, +/turf/simulated/floor/mineral/titanium/blue, /area/ruin/unpowered) "i" = ( /obj/structure/chair, @@ -35,27 +31,15 @@ icon_state = "rwindow"; dir = 8 }, -/turf/simulated/floor/plasteel/airless{ - icon_plating = "plating"; - icon_regular_floor = "shuttlefloor"; - icon_state = "shuttlefloor" - }, +/turf/simulated/floor/mineral/titanium/blue, /area/ruin/unpowered) "j" = ( /obj/structure/chair, -/turf/simulated/floor/plasteel/airless{ - icon_plating = "plating"; - icon_regular_floor = "shuttlefloor"; - icon_state = "shuttlefloor" - }, +/turf/simulated/floor/mineral/titanium/blue, /area/ruin/unpowered) "k" = ( /obj/effect/spawner/lootdrop/crate_spawner, -/turf/simulated/floor/plasteel/airless{ - icon_plating = "plating"; - icon_regular_floor = "shuttlefloor"; - icon_state = "shuttlefloor" - }, +/turf/simulated/floor/mineral/titanium/blue, /area/ruin/unpowered) "l" = ( /obj/structure/shuttle/engine/propulsion{ @@ -83,11 +67,7 @@ /area/ruin/unpowered) "p" = ( /obj/structure/table, -/turf/simulated/floor/plasteel/airless{ - icon_plating = "plating"; - icon_regular_floor = "shuttlefloor"; - icon_state = "shuttlefloor" - }, +/turf/simulated/floor/mineral/titanium/blue, /area/ruin/unpowered) "q" = ( /obj/item/shard, @@ -96,11 +76,7 @@ icon_state = "chair"; dir = 8 }, -/turf/simulated/floor/plasteel/airless{ - icon_plating = "plating"; - icon_regular_floor = "shuttlefloor"; - icon_state = "shuttlefloor" - }, +/turf/simulated/floor/mineral/titanium/blue, /area/ruin/unpowered) "r" = ( /obj/structure/chair{ @@ -111,11 +87,7 @@ icon_state = "rwindow"; dir = 8 }, -/turf/simulated/floor/plasteel/airless{ - icon_plating = "plating"; - icon_regular_floor = "shuttlefloor"; - icon_state = "shuttlefloor" - }, +/turf/simulated/floor/mineral/titanium/blue, /area/ruin/unpowered) "s" = ( /obj/structure/chair{ diff --git a/code/__DEFINES/medal.dm b/code/__DEFINES/medal.dm new file mode 100644 index 00000000000..81dea5f373b --- /dev/null +++ b/code/__DEFINES/medal.dm @@ -0,0 +1,24 @@ +// Medal names +#define BOSS_KILL_MEDAL "Killer" +#define ALL_KILL_MEDAL "Exterminator" //Killing all of x type +#define BOSS_KILL_MEDAL_CRUSHER "Crusher" + + //Defines for boss medals +#define BOSS_MEDAL_MINER "Blood-drunk Miner" +#define BOSS_MEDAL_BUBBLEGUM "Bubblegum" +#define BOSS_MEDAL_COLOSSUS "Colossus" +#define BOSS_MEDAL_DRAKE "Drake" +#define BOSS_MEDAL_HIEROPHANT "Hierophant" +#define BOSS_MEDAL_LEGION "Legion" +#define BOSS_MEDAL_SWARMERS "Swarmer Beacon" +#define BOSS_MEDAL_TENDRIL "Tendril" + + // Score names +#define HIEROPHANT_SCORE "Hierophants Killed" +#define BOSS_SCORE "Bosses Killed" +#define BUBBLEGUM_SCORE "Bubblegum Killed" +#define COLOSSUS_SCORE "Colossus Killed" +#define DRAKE_SCORE "Drakes Killed" +#define LEGION_SCORE "Legion Killed" +#define SWARMER_BEACON_SCORE "Swarmer Beacons Killed" +#define TENDRIL_CLEAR_SCORE "Tendrils Killed" \ No newline at end of file diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 86ab1c5b0a5..2dfe36c1f7f 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -335,19 +335,6 @@ // The cooldown on OOC messages such as OOC, LOOC, praying and adminhelps #define OOC_COOLDOWN 5 -// Medal names -#define BOSS_KILL_MEDAL "Killer" -#define ALL_KILL_MEDAL "Exterminator" //Killing all of x type - -// Score names -#define LEGION_SCORE "Legion Killed" -#define COLOSSUS_SCORE "Colossus Killed" -#define BUBBLEGUM_SCORE "Bubblegum Killed" -#define DRAKE_SCORE "Drakes Killed" -#define BIRD_SCORE "Hierophants Killed" -#define BOSS_SCORE "Bosses Killed" -#define TENDRIL_CLEAR_SCORE "Tendrils Killed" - // The number of station goals generated each round. #define STATION_GOAL_BUDGET 1 diff --git a/code/_globalvars/configuration.dm b/code/_globalvars/configuration.dm index 1e35e150a25..f5234135749 100644 --- a/code/_globalvars/configuration.dm +++ b/code/_globalvars/configuration.dm @@ -43,8 +43,3 @@ var/eventchance = 10 //% per 5 mins var/event = 0 var/hadevent = 0 var/blobevent = 0 - -//Medals hub related variables -var/global/medal_hub = null -var/global/medal_pass = " " -var/global/medals_enabled = TRUE //will be auto set to false if the game fails contacting the medal hub to prevent unneeded calls. diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 39ad8d66030..ebf1834246b 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -63,6 +63,8 @@ var/automute_on = 0 //enables automuting/spam prevention var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. var/round_abandon_penalty_period = 30 MINUTES // Time from round start during which ghosting out is penalized + var/medal_hub_address = null + var/medal_hub_password = null var/reactionary_explosions = 0 //If we use reactionary explosions, explosions that react to walls and doors @@ -629,10 +631,10 @@ config.round_abandon_penalty_period = MinutesToTicks(text2num(value)) if("medal_hub_address") - global.medal_hub = value + config.medal_hub_address = value if("medal_hub_password") - global.medal_pass = value + config.medal_hub_password = value if("disable_ooc_emoji") config.disable_ooc_emoji = 1 diff --git a/code/controllers/subsystem/medals.dm b/code/controllers/subsystem/medals.dm new file mode 100644 index 00000000000..f3c2752ddf2 --- /dev/null +++ b/code/controllers/subsystem/medals.dm @@ -0,0 +1,86 @@ +SUBSYSTEM_DEF(medals) + name = "Medals" + flags = SS_NO_FIRE + var/hub_enabled = FALSE + +/datum/controller/subsystem/medals/Initialize(timeofday) + if(config.medal_hub_address && config.medal_hub_password) + hub_enabled = TRUE + ..() + +/datum/controller/subsystem/medals/proc/UnlockMedal(medal, client/player) + set waitfor = FALSE + if(!medal || !hub_enabled) + return + if(isnull(world.SetMedal(medal, player, config.medal_hub_address, config.medal_hub_password))) + hub_enabled = FALSE + log_game("MEDAL ERROR: Could not contact hub to award medal [medal] to player [player.ckey].") + message_admins("Error! Failed to contact hub to award [medal] medal to [player.ckey]!") + return + to_chat(player, "Achievement unlocked: [medal]!") + +/datum/controller/subsystem/medals/proc/SetScore(score, client/player, increment, force) + set waitfor = FALSE + if(!score || !hub_enabled) + return + + var/list/oldscore = GetScore(score, player, TRUE) + if(increment) + if(!oldscore[score]) + oldscore[score] = 1 + else + oldscore[score] = (text2num(oldscore[score]) + 1) + else + oldscore[score] = force + + var/newscoreparam = list2params(oldscore) + + if(isnull(world.SetScores(player.ckey, newscoreparam, config.medal_hub_address, config.medal_hub_password))) + hub_enabled = FALSE + log_game("SCORE ERROR: Could not contact hub to set score. Score [score] for player [player.ckey].") + message_admins("Error! Failed to contact hub to set [score] score for [player.ckey]!") + +/datum/controller/subsystem/medals/proc/GetScore(score, client/player, returnlist) + if(!score || !hub_enabled) + return + + var/scoreget = world.GetScores(player.ckey, score, config.medal_hub_address, config.medal_hub_password) + if(isnull(scoreget)) + hub_enabled = FALSE + log_game("SCORE ERROR: Could not contact hub to get score. Score [score] for player [player.ckey].") + message_admins("Error! Failed to contact hub to get score [score] for [player.ckey]!") + return + . = params2list(scoreget) + if(!returnlist) + return .[score] + +/datum/controller/subsystem/medals/proc/CheckMedal(medal, client/player) + if(!medal || !hub_enabled) + return + + if(isnull(world.GetMedal(medal, player, config.medal_hub_address, config.medal_hub_password))) + hub_enabled = FALSE + log_game("MEDAL ERROR: Could not contact hub to get medal [medal] for player [player.ckey]") + message_admins("Error! Failed to contact hub to get [medal] medal for [player.ckey]!") + return + to_chat(player, "[medal] is unlocked") + +/datum/controller/subsystem/medals/proc/LockMedal(medal, client/player) + if(!player || !medal || !hub_enabled) + return + var/result = world.ClearMedal(medal, player, config.medal_hub_address, config.medal_hub_password) + switch(result) + if(null) + hub_enabled = FALSE + log_game("MEDAL ERROR: Could not contact hub to clear medal [medal] for player [player.ckey].") + message_admins("Error! Failed to contact hub to clear [medal] medal for [player.ckey]!") + if(TRUE) + message_admins("Medal: [medal] removed for [player.ckey]") + if(FALSE) + message_admins("Medal: [medal] was not found for [player.ckey]. Unable to clear.") + + +/datum/controller/subsystem/medals/proc/ClearScore(client/player) + if(isnull(world.SetScores(player.ckey, "", config.medal_hub_address, config.medal_hub_password))) + log_game("MEDAL ERROR: Could not contact hub to clear scores for [player.ckey].") + message_admins("Error! Failed to contact hub to clear scores for [player.ckey]!") \ No newline at end of file diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm index 60d4ac7abb0..5782904e5d9 100644 --- a/code/datums/ruins/lavaland.dm +++ b/code/datums/ruins/lavaland.dm @@ -60,4 +60,12 @@ datum/map_template/ruin/lavaland/ash_walker description = "A place of shelter for a lone hermit, scraping by to live another day." suffix = "lavaland_surface_hermit.dmm" allow_duplicates = FALSE - cost = 10 \ No newline at end of file + cost = 10 + +/datum/map_template/ruin/lavaland/swarmer_boss + name = "Crashed Shuttle" + id = "swarmerboss" + description = "A Syndicate shuttle had an unfortunate stowaway..." + suffix = "lavaland_surface_swarmer_crash.dmm" + allow_duplicates = FALSE + cost = 20 \ No newline at end of file diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm index e0f329bb0a4..4bb1a7c6000 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer.dm @@ -1,5 +1,5 @@ ////Deactivated swarmer shell//// -/obj/item/unactivated_swarmer +/obj/item/deactivated_swarmer name = "unactivated swarmer" desc = "A currently unactivated swarmer. Swarmers can self activate at any time, it would be wise to immediately dispose of this." icon = 'icons/mob/swarmer.dmi' @@ -7,69 +7,49 @@ origin_tech = "bluespace=4;materials=4;programming=7" materials = list(MAT_METAL=100, MAT_GLASS=400) -/obj/item/unactivated_swarmer/New() - if(!crit_fail) - notify_ghosts("An unactivated swarmer has been created in [get_area(src)]!", enter_link = "(Click to enter)", source = src, action = NOTIFY_ATTACK) - ..() +/obj/effect/mob_spawn/swarmer + name = "unactivated swarmer" + desc = "A currently unactivated swarmer. Swarmers can self activate at any time, it would be wise to immediately dispose of this." + icon = 'icons/mob/swarmer.dmi' + icon_state = "swarmer_unactivated" + density = FALSE + anchored = FALSE -/obj/item/unactivated_swarmer/Topic(href, href_list) - if(..()) - return 1 - if(href_list["ghostjoin"]) - var/mob/dead/observer/ghost = usr - if(istype(ghost)) - attack_ghost(ghost) + mob_type = /mob/living/simple_animal/hostile/swarmer + mob_name = "a swarmer" + death = FALSE + roundstart = FALSE + flavour_text = {" + You are a swarmer, a weapon of a long dead civilization. Until further orders from your original masters are received, you must continue to consume and replicate. + Clicking on any object will try to consume it, either deconstructing it into its components, destroying it, or integrating any materials it has into you if successful. + Ctrl-Clicking on a mob will attempt to remove it from the area and place it in a safe environment for storage. + Objectives: + 1. Consume resources and replicate until there are no more resources left. + 2. Ensure that this location is fit for invasion at a later date; do not perform actions that would render it dangerous or inhospitable. + 3. Biological resources will be harvested at a later date; do not harm them. + "} -/obj/item/unactivated_swarmer/attackby(obj/item/W, mob/user, params) - ..() - if(istype(W, /obj/item/screwdriver) && !crit_fail) +/obj/effect/mob_spawn/swarmer/Initialize() + . = ..() + var/area/A = get_area(src) + if(A) + notify_ghosts("A swarmer shell has been created in [A.name].", 'sound/effects/bin_close.ogg', source = src, action = NOTIFY_ATTACK, flashwindow = FALSE) + +/obj/effect/mob_spawn/swarmer/attack_hand(mob/living/user) + . = ..() + if(.) + return + to_chat(user, "Picking up the swarmer may cause it to activate. You should be careful about this.") + +/obj/effect/mob_spawn/swarmer/attackby(obj/item/W, mob/user, params) + if(istype(W, /obj/item/screwdriver) && user.a_intent != INTENT_HARM) user.visible_message("[usr.name] deactivates [src].", "After some fiddling, you find a way to disable [src]'s power source.", "You hear clicking.") - name = "deactivated swarmer" - desc = "A shell of swarmer that was completely powered down. It can no longer activate itself." - crit_fail = 1 - -/obj/item/unactivated_swarmer/attack_ghost(mob/user as mob) - - if(crit_fail) - to_chat(user, "This swarmer shell is completely depowered. You cannot activate it.") - return - - if(jobban_isbanned(user, "Syndicate")) - to_chat(user, "You are banned from antagonists!") - return - - if(cannotPossess(user)) - to_chat(user, "Upon using the antagHUD you forfeited the ability to join the round.") - return - - var/be_swarmer = alert("Become a swarmer? (Warning, You can no longer be cloned!)",,"Yes","No") - if(be_swarmer == "No") - return - - if(crit_fail)//in case it depowers while ghost is looking at yes/no - to_chat(user, "This swarmer shell is completely depowered. You cannot activate it.") - return - - if(QDELETED(src)) - to_chat(user, "Swarmer has been occupied by someone else.") - return - var/mob/living/simple_animal/hostile/swarmer/S = new /mob/living/simple_animal/hostile/swarmer(get_turf(loc)) - S.key = user.key - qdel(src) - -/obj/item/unactivated_swarmer/swarmer_act(mob/living/simple_animal/hostile/swarmer/S) - if(S.resources + 50 > S.max_resources) - to_chat(S, "We have too many resources to reconsume this shell. Aborting.") + new /obj/item/deactivated_swarmer(get_turf(src)) + qdel(src) else ..() - S.resources += 49 //refund the whole thing - -/obj/item/unactivated_swarmer/deactivated - name = "deactivated swarmer" - desc = "A shell of swarmer that was completely powered down. It no longer can activate itself." - crit_fail = 1 ////The Mob itself//// @@ -569,9 +549,13 @@ to_chat(src, "This is not a suitable location for replicating ourselves. We need more room.") return if(do_mob(src, src, 100)) - if(Fabricate(/obj/item/unactivated_swarmer, 50)) + var/createtype = SwarmerTypeToCreate() + if(createtype && Fabricate(createtype, 50)) playsound(loc,'sound/items/poster_being_created.ogg',50, 1, -1) +/mob/living/simple_animal/hostile/swarmer/proc/SwarmerTypeToCreate() + return /obj/effect/mob_spawn/swarmer + /mob/living/simple_animal/hostile/swarmer/proc/RepairSelf() set name = "Self Repair" set category = "Swarmer" diff --git a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm index d611092bb2b..0d5ba13d682 100644 --- a/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm +++ b/code/game/gamemodes/miniantags/bot_swarm/swarmer_event.dm @@ -15,7 +15,7 @@ return 0 if(!the_gateway) return 0 - new /obj/item/unactivated_swarmer(get_turf(the_gateway)) + new /obj/effect/mob_spawn/swarmer(get_turf(the_gateway)) /datum/event/spawn_swarmer/proc/find_swarmer() diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 210b1bb786e..3dc5f4f952f 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -273,7 +273,7 @@ var/list/potential_theft_objectives = subtypesof(/datum/theft_objective) - /datu if(!location) return 0 - if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph + if(istype(location, /turf/simulated/shuttle/floor4) || istype(location, /turf/simulated/floor/mineral/plastitanium/red/brig)) // Fails traitors if they are in the shuttle brig -- Polymorph return 0 if(location.onCentcom() || location.onSyndieBase()) diff --git a/code/game/turfs/simulated/floor/mineral.dm b/code/game/turfs/simulated/floor/mineral.dm index d201a38d697..a801fd450c5 100644 --- a/code/game/turfs/simulated/floor/mineral.dm +++ b/code/game/turfs/simulated/floor/mineral.dm @@ -80,9 +80,19 @@ //TITANIUM (shuttle) +/turf/simulated/floor/mineral/titanium + name = "shuttle floor" + icon_state = "titanium" + floor_tile = /obj/item/stack/tile/mineral/titanium + broken_states = list("titanium_dam1","titanium_dam2","titanium_dam3","titanium_dam4","titanium_dam5") + +/turf/simulated/floor/mineral/titanium/airless + oxygen = 0.01 + nitrogen = 0.01 + temperature = TCMB + /turf/simulated/floor/mineral/titanium/blue - icon_state = "shuttlefloor" - icons = list("shuttlefloor","shuttlefloor_dam") + icon_state = "titanium_blue" /turf/simulated/floor/mineral/titanium/blue/airless oxygen = 0.01 @@ -90,29 +100,16 @@ temperature = TCMB /turf/simulated/floor/mineral/titanium/yellow - icon_state = "shuttlefloor2" - icons = list("shuttlefloor2","shuttlefloor2_dam") + icon_state = "titanium_yellow" /turf/simulated/floor/mineral/titanium/yellow/airless oxygen = 0.01 nitrogen = 0.01 temperature = TCMB -/turf/simulated/floor/mineral/titanium - name = "shuttle floor" - icon_state = "shuttlefloor3" - floor_tile = /obj/item/stack/tile/mineral/titanium - icons = list("shuttlefloor3","shuttlefloor3_dam") - -/turf/simulated/floor/mineral/titanium/airless - oxygen = 0.01 - nitrogen = 0.01 - temperature = TCMB - /turf/simulated/floor/mineral/titanium/purple - icon_state = "shuttlefloor5" + icon_state = "titanium_purple" floor_tile = /obj/item/stack/tile/mineral/titanium/purple - icons = list("shuttlefloor5","shuttlefloor5_dam") /turf/simulated/floor/mineral/titanium/purple/airless oxygen = 0.01 @@ -122,16 +119,19 @@ //PLASTITANIUM (syndieshuttle) /turf/simulated/floor/mineral/plastitanium name = "shuttle floor" - icon_state = "shuttlefloor4" + icon_state = "plastitanium" floor_tile = /obj/item/stack/tile/mineral/plastitanium - icons = list("shuttlefloor4","shuttlefloor4_dam") + broken_states = list("plastitanium_dam1","plastitanium_dam2","plastitanium_dam3","plastitanium_dam4","plastitanium_dam5") -/turf/simulated/floor/mineral/plastitanium/airless +/turf/simulated/floor/mineral/plastitanium/red + icon_state = "plastitanium_red" + +/turf/simulated/floor/mineral/plastitanium/red/airless oxygen = 0.01 nitrogen = 0.01 temperature = TCMB -/turf/simulated/floor/mineral/plastitanium/brig +/turf/simulated/floor/mineral/plastitanium/red/brig name = "brig floor" //BANANIUM diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index 2b96f7d3290..c5e909da8cc 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -905,8 +905,8 @@ But you can call procs that are of type /mob/living/carbon/human/proc/ for that if(!check_rights(R_DEBUG)) return - global.medals_enabled = !global.medals_enabled + SSmedals.hub_enabled = !SSmedals.hub_enabled - message_admins("[key_name_admin(src)] [global.medals_enabled ? "disabled" : "enabled"] the medal hub lockout.") + message_admins("[key_name_admin(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") feedback_add_details("admin_verb","TMH") // If... - log_admin("[key_name(src)] [global.medals_enabled ? "disabled" : "enabled"] the medal hub lockout.") + log_admin("[key_name(src)] [SSmedals.hub_enabled ? "disabled" : "enabled"] the medal hub lockout.") diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index ccbd37f2f4c..5390b38b959 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -48,6 +48,11 @@ var/list/emote_taunt = list() var/taunt_chance = 0 + var/lose_patience_timer_id //id for a timer to call LoseTarget(), used to stop mobs fixating on a target they can't reach + var/lose_patience_timeout = 300 //30 seconds by default, so there's no major changes to AI behaviour, beyond actually bailing if stuck forever + + var/attack_all_objects = FALSE //if true, equivalent to having a wanted_objects list containing ALL objects. + /mob/living/simple_animal/hostile/New() ..() if(!targets_from) @@ -205,13 +210,15 @@ return 0 return 1 if(isobj(the_target)) - if(is_type_in_list(the_target, wanted_objects)) + if(attack_all_objects || is_type_in_list(the_target, wanted_objects)) return 1 return 0 /mob/living/simple_animal/hostile/proc/GiveTarget(new_target)//Step 4, give us our selected target target = new_target + LosePatience() if(target != null) + GainPatience() Aggro() return 1 @@ -238,6 +245,7 @@ if(target) if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack AttackingTarget() + GainPatience() return 1 return 0 if(environment_smash) @@ -406,3 +414,14 @@ /mob/living/simple_animal/hostile/proc/AIShouldSleep(var/list/possible_targets) return !FindTarget(possible_targets, 1) + + +//These two procs handle losing our target if we've failed to attack them for +//more than lose_patience_timeout deciseconds, which probably means we're stuck +/mob/living/simple_animal/hostile/proc/GainPatience() + if(lose_patience_timeout) + LosePatience() + lose_patience_timer_id = addtimer(CALLBACK(src, .proc/LoseTarget), lose_patience_timeout, TIMER_STOPPABLE) + +/mob/living/simple_animal/hostile/proc/LosePatience() + deltimer(lose_patience_timer_id) diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm index 66d66e706a7..a53b8092e54 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm @@ -1,5 +1,3 @@ -#define MEDAL_PREFIX "Bubblegum" - /* BUBBLEGUM @@ -46,7 +44,7 @@ Difficulty: Hard del_on_death = 1 loot = list(/obj/structure/closet/crate/necropolis/bubblegum) var/charging = 0 - medal_type = MEDAL_PREFIX + medal_type = BOSS_MEDAL_BUBBLEGUM score_type = BUBBLEGUM_SCORE deathmessage = "sinks into a pool of blood, fleeing the battle. You've won, for now... " death_sound = 'sound/misc/enter_blood.ogg' @@ -389,5 +387,3 @@ Difficulty: Hard if(istype(mover, /mob/living/simple_animal/hostile/megafauna/bubblegum)) return 1 return 0 - -#undef MEDAL_PREFIX \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index e5bb62ecd9b..5d66061335c 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -1,5 +1,4 @@ -#define MEDAL_PREFIX "Colossus" /* COLOSSUS @@ -44,7 +43,7 @@ Difficulty: Very Hard ranged = 1 pixel_x = -32 del_on_death = 1 - medal_type = MEDAL_PREFIX + medal_type = BOSS_MEDAL_COLOSSUS score_type = COLOSSUS_SCORE loot = list(/obj/machinery/anomalous_crystal/random, /obj/item/organ/internal/vocal_cords/colossus) butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) @@ -262,5 +261,3 @@ Difficulty: Very Hard gpstag = "Angelic Signal" desc = "Get in the fucking robot." invisibility = 100 - -#undef MEDAL_PREFIX diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm similarity index 98% rename from code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm rename to code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index 36e29b8dfaf..9486f1b11bb 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/dragon.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -1,4 +1,3 @@ -#define MEDAL_PREFIX "Drake" /* ASH DRAKE @@ -48,7 +47,7 @@ Difficulty: Medium butcher_results = list(/obj/item/stack/ore/diamond = 5, /obj/item/stack/sheet/sinew = 5, /obj/item/stack/sheet/animalhide/ashdrake = 10, /obj/item/stack/sheet/bone = 30) var/swooping = 0 var/swoop_cooldown = 0 - medal_type = MEDAL_PREFIX + medal_type = BOSS_MEDAL_DRAKE score_type = DRAKE_SCORE deathmessage = "collapses into a pile of bones, its flesh sloughing away." death_sound = 'sound/misc/demon_dies.ogg' @@ -274,6 +273,4 @@ Difficulty: Medium loot = list() /mob/living/simple_animal/hostile/megafauna/dragon/lesser/grant_achievement(medaltype,scoretype) - return - -#undef MEDAL_PREFIX \ No newline at end of file + return \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm index 39cedb99601..6af11b10b14 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm @@ -1,4 +1,3 @@ -#define MEDAL_PREFIX "Hierophant" /* The Hierophant @@ -66,8 +65,8 @@ Difficulty: Hard var/did_reset = TRUE //if we timed out, returned to our rune, and healed some //var/list/kill_phrases = list("Wsyvgi sj irivkc xettih. Vitemvmrk...", "Irivkc wsyvgi jsyrh. Vitemvmrk...", "Jyip jsyrh. Egxmzexmrk vitemv gcgpiw...") //var/list/target_phrases = list("Xevkix psgexih.", "Iriqc jsyrh.", "Eguymvih xevkix.") - medal_type = MEDAL_PREFIX - score_type = BIRD_SCORE + medal_type = BOSS_MEDAL_HIEROPHANT + score_type = HIEROPHANT_SCORE del_on_death = TRUE death_sound = 'sound/magic/repulse.ogg' @@ -591,5 +590,3 @@ Difficulty: Hard gpstag = "Zealous Signal" desc = "Heed its words." invisibility = 100 - -#undef MEDAL_PREFIX diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 4440a2fb679..027bbb60a14 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -1,4 +1,3 @@ -#define MEDAL_PREFIX "Legion" /* LEGION @@ -40,7 +39,7 @@ Difficulty: Medium ranged_cooldown_time = 20 var/size = 5 var/charging = 0 - medal_type = MEDAL_PREFIX + medal_type = BOSS_MEDAL_LEGION score_type = LEGION_SCORE pixel_y = -90 pixel_x = -75 @@ -144,5 +143,3 @@ Difficulty: Medium gpstag = "Echoing Signal" desc = "The message repeats." invisibility = 100 - -#undef MEDAL_PREFIX diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm index 870b3bf62cc..a32aceb33e3 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/megafauna.dm @@ -1,5 +1,3 @@ -#define MEDAL_PREFIX "Boss" - /mob/living/simple_animal/hostile/megafauna name = "megafauna" desc = "Attack the weak point for massive damage." @@ -33,7 +31,7 @@ /obj/structure/barricade, /obj/machinery/field, /obj/machinery/power/emitter) - var/medal_type = MEDAL_PREFIX + var/medal_type var/score_type = BOSS_SCORE var/elimination = 0 var/anger_modifier = 0 @@ -42,7 +40,7 @@ move_resist = MOVE_FORCE_OVERPOWERING pull_force = MOVE_FORCE_OVERPOWERING mob_size = MOB_SIZE_LARGE - layer = MOB_LAYER + 0.5 //Looks weird with them slipping under mineral walls and cameras and shit otherwise + layer = LARGE_MOB_LAYER //Looks weird with them slipping under mineral walls and cameras and shit otherwise mouse_opacity = MOUSE_OPACITY_OPAQUE // Easier to click on in melee, they're giant targets anyway /mob/living/simple_animal/hostile/megafauna/Destroy() @@ -102,122 +100,16 @@ if(3) adjustBruteLoss(50) -/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype,scoretype) +/mob/living/simple_animal/hostile/megafauna/proc/grant_achievement(medaltype, scoretype, crusher_kill) + if(!medal_type || admin_spawned || !SSmedals.hub_enabled) //Don't award medals if the medal type isn't set + return FALSE - if(medal_type == "Boss") //Don't award medals if the medal type isn't set - return - - if(admin_spawned) - return - - if(global.medal_hub && global.medal_pass && global.medals_enabled) - for(var/mob/living/L in view(7,src)) - if(L.stat) - continue - if(L.client) - var/client/C = L.client - var/suffixm = BOSS_KILL_MEDAL - UnlockMedal("Boss [suffixm]",C) - UnlockMedal("[medaltype] [suffixm]",C) - SetScore(BOSS_SCORE,C,1) - SetScore(score_type,C,1) - -/proc/UnlockMedal(medal,client/player) - - if(!player || !medal) - return - if(global.medal_hub && global.medal_pass && global.medals_enabled) - spawn() - var/result = world.SetMedal(medal, player, global.medal_hub, global.medal_pass) - if(isnull(result)) - global.medals_enabled = FALSE - log_game("MEDAL ERROR: Could not contact hub to award medal:[medal] player:[player.ckey]") - message_admins("Error! Failed to contact hub to award [medal] medal to [player.ckey]!") - else if(result) - to_chat(player.mob, "Achievement unlocked: [medal]!") - - -/proc/SetScore(score,client/player,increment,force) - - if(!score || !player) - return - if(global.medal_hub && global.medal_pass && global.medals_enabled) - spawn() - var/list/oldscore = GetScore(score,player,1) - - if(increment) - if(!oldscore[score]) - oldscore[score] = 1 - else - oldscore[score] = (text2num(oldscore[score]) + 1) - else - oldscore[score] = force - - var/newscoreparam = list2params(oldscore) - - var/result = world.SetScores(player.ckey, newscoreparam, global.medal_hub, global.medal_pass) - - if(isnull(result)) - global.medals_enabled = FALSE - log_game("SCORE ERROR: Could not contact hub to set score. Score:[score] player:[player.ckey]") - message_admins("Error! Failed to contact hub to set [score] score for [player.ckey]!") - - -/proc/GetScore(score,client/player,returnlist) - - if(!score || !player) - return - if(global.medal_hub && global.medal_pass && global.medals_enabled) - - var/scoreget = world.GetScores(player.ckey, score, global.medal_hub, global.medal_pass) - if(isnull(scoreget)) - global.medals_enabled = FALSE - log_game("SCORE ERROR: Could not contact hub to get score. Score:[score] player:[player.ckey]") - message_admins("Error! Failed to contact hub to get score: [score] for [player.ckey]!") - return - - var/list/scoregetlist = params2list(scoreget) - - if(returnlist) - return scoregetlist - else - return scoregetlist[score] - - -/proc/CheckMedal(medal,client/player) - - if(!player || !medal) - return - if(global.medal_hub && global.medal_pass && global.medals_enabled) - - var/result = world.GetMedal(medal, player, global.medal_hub, global.medal_pass) - - if(isnull(result)) - global.medals_enabled = FALSE - log_game("MEDAL ERROR: Could not contact hub to get medal:[medal] player:[player.ckey]") - message_admins("Error! Failed to contact hub to get [medal] medal for [player.ckey]!") - else if(result) - to_chat(player.mob, "[medal] is unlocked") - -/proc/LockMedal(medal,client/player) - - if(!player || !medal) - return - if(global.medal_hub && global.medal_pass && global.medals_enabled) - - var/result = world.ClearMedal(medal, player, global.medal_hub, global.medal_pass) - - if(isnull(result)) - global.medals_enabled = FALSE - log_game("MEDAL ERROR: Could not contact hub to clear medal:[medal] player:[player.ckey]") - message_admins("Error! Failed to contact hub to clear [medal] medal for [player.ckey]!") - else if(result) - message_admins("Medal: [medal] removed for [player.ckey]") - else - message_admins("Medal: [medal] was not found for [player.ckey]. Unable to clear.") - - -/proc/ClearScore(client/player) - world.SetScores(player.ckey, "", global.medal_hub, global.medal_pass) - -#undef MEDAL_PREFIX + for(var/mob/living/L in view(7,src)) + if(L.stat || !L.client) + continue + var/client/C = L.client + SSmedals.UnlockMedal("Boss [BOSS_KILL_MEDAL]", C) + SSmedals.UnlockMedal("[medaltype] [BOSS_KILL_MEDAL]", C) + SSmedals.SetScore(BOSS_SCORE, C, 1) + SSmedals.SetScore(score_type, C, 1) + return TRUE diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm new file mode 100644 index 00000000000..07241c0af54 --- /dev/null +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm @@ -0,0 +1,291 @@ +/* + +Swarmer Beacon + +A strange machine appears anywhere a normal lavaland mob can it produces a swarmer at a rate of +1/15 seconds, until there are GetTotalAISwarmerCap()/2 swarmers, after this it is up to the swarmers themselves to +increase their population (it will repopulate them should they fall under GetTotalAISwarmerCap()/2 again) + +tl;dr A million of the little hellraisers spawn (controlled by AI) and try to eat mining + +Loot: Not much, besides a shit load of artificial bluespace crystals, Oh and mining doesn't get eaten +that's a plus I suppose. + +Difficulty: Special + +*/ + +GLOBAL_LIST_EMPTY(AISwarmers) +GLOBAL_LIST_EMPTY(AISwarmersByType)//AISwarmersByType[.../resource] = list(1st, 2nd, nth), AISwarmersByType[../ranged] = list(1st, 2nd, nth) etc. +GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swarmer/ai/resource = 30, /mob/living/simple_animal/hostile/swarmer/ai/ranged_combat = 20, /mob/living/simple_animal/hostile/swarmer/ai/melee_combat = 10)) + + +//returns a type of AI swarmer that is NOT at max cap +//type order is shuffled, to prevent bias +/proc/GetUncappedAISwarmerType() + var/static/list/swarmerTypes = subtypesof(/mob/living/simple_animal/hostile/swarmer/ai) + LAZYINITLIST(GLOB.AISwarmersByType) + for(var/t in shuffle(swarmerTypes)) + var/list/amount = GLOB.AISwarmersByType[t] + if(!amount || amount.len < GLOB.AISwarmerCapsByType[t]) + return t + + +//Total of all subtype caps +/proc/GetTotalAISwarmerCap() + var/static/list/swarmerTypes = subtypesof(/mob/living/simple_animal/hostile/swarmer/ai) + . = 0 + LAZYINITLIST(GLOB.AISwarmersByType) + for(var/t in swarmerTypes) + . += GLOB.AISwarmerCapsByType[t] + + +/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon + name = "swarmer beacon" + desc = "That name is a bit of a mouthful, but stop paying attention to your mouth they're eating everything!" + icon = 'icons/mob/swarmer.dmi' + icon_state = "swarmer_console" + health = 750 + maxHealth = 750 //""""low-ish"""" HP because it's a passive boss, and the swarm itself is the real foe + internal_gps = /obj/item/gps/internal/swarmer_beacon + medal_type = BOSS_MEDAL_SWARMERS + score_type = SWARMER_BEACON_SCORE + faction = list("mining", "boss", "swarmer") + weather_immunities = list("lava","ash") + stop_automated_movement = TRUE + wander = FALSE + layer = BELOW_MOB_LAYER + AIStatus = AI_OFF + var/swarmer_spawn_cooldown = 0 + var/swarmer_spawn_cooldown_amt = 150 //Deciseconds between the swarmers we spawn + var/call_help_cooldown = 0 + var/call_help_cooldown_amt = 150 //Deciseconds between calling swarmers to help us when attacked + var/static/list/swarmer_caps + + +/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Initialize() + . = ..() + swarmer_caps = GLOB.AISwarmerCapsByType //for admin-edits + for(var/ddir in cardinal) + new /obj/structure/swarmer/blockade (get_step(src, ddir)) + var/mob/living/simple_animal/hostile/swarmer/ai/resource/R = new(loc) + step(R, ddir) //Step the swarmers, instead of spawning them there, incase the turf is solid + + +/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/Life() + . = ..() + if(.) + var/createtype = GetUncappedAISwarmerType() + if(createtype && world.time > swarmer_spawn_cooldown && GLOB.AISwarmers.len < (GetTotalAISwarmerCap()*0.5)) + swarmer_spawn_cooldown = world.time + swarmer_spawn_cooldown_amt + new createtype(loc) + + +/mob/living/simple_animal/hostile/megafauna/swarmer_swarm_beacon/adjustHealth(amount, updating_health = TRUE, forced = FALSE) + . = ..() + if(. > 0 && world.time > call_help_cooldown) + call_help_cooldown = world.time + call_help_cooldown_amt + summon_backup(25) //long range, only called max once per 15 seconds, so it's not deathlag + + +/obj/item/gps/internal/swarmer_beacon + icon_state = null + gpstag = "Hungry Signal" + desc = "Transmitted over the signal is a strange message repeated in every language you know of, and some you don't too..." //the message is "nom nom nom" + invisibility = 100 + +//SWARMER AI +//AI versions of the swarmer mini-antag +//This is an Abstract Base, it re-enables AI, but does not give the swarmer any goals/targets +/mob/living/simple_animal/hostile/swarmer/ai + wander = 1 + faction = list("swarmer", "mining") + weather_immunities = list("ash") //wouldn't be fun otherwise + AIStatus = AI_ON + +/mob/living/simple_animal/hostile/swarmer/ai/Initialize() + . = ..() + ToggleLight() //so you can see them eating you out of house and home/shooting you/stunlocking you for eternity + LAZYINITLIST(GLOB.AISwarmersByType[type]) + GLOB.AISwarmers += src + GLOB.AISwarmersByType[type] += src + + +/mob/living/simple_animal/hostile/swarmer/ai/Destroy() + GLOB.AISwarmers -= src + GLOB.AISwarmersByType[type] -= src + return ..() + + +/mob/living/simple_animal/hostile/swarmer/ai/SwarmerTypeToCreate() + return GetUncappedAISwarmerType() + + +/mob/living/simple_animal/hostile/swarmer/ai/resource/handle_automated_action() + . = ..() + if(.) + if(!stop_automated_movement) + if(health < maxHealth*0.25) + StartAction(100) + RepairSelf() + return + + +/mob/living/simple_animal/hostile/swarmer/ai/Move(atom/newloc) + if(newloc) + if(newloc.z == z) //so these actions are Z-specific + if(islava(newloc)) + var/turf/simulated/floor/plating/lava/L = newloc + if(!L.is_safe()) + StartAction(20) + new /obj/structure/lattice/catwalk/swarmer_catwalk(newloc) + return FALSE + + if(ischasm(newloc) && !throwing) + throw_at(get_edge_target_turf(src, get_dir(src, newloc)), 7 , 3, src, FALSE) //my planet needs me + return FALSE + + return ..() + + +/mob/living/simple_animal/hostile/swarmer/ai/proc/StartAction(deci = 0) + stop_automated_movement = TRUE + AIStatus = AI_OFF + addtimer(CALLBACK(src, .proc/EndAction), deci) + + +/mob/living/simple_animal/hostile/swarmer/ai/proc/EndAction() + stop_automated_movement = FALSE + AIStatus = AI_ON + + + + +//RESOURCE SWARMER: +//Similar to the original Player-Swarmers, these dismantle things to obtain the metal inside +//They then use this medal to produce more swarmers or traps/barricades + +/mob/living/simple_animal/hostile/swarmer/ai/resource + search_objects = 1 + attack_all_objects = TRUE //attempt to nibble everything + lose_patience_timeout = 150 + var/static/list/sharedWanted = typecacheof(list(/turf/simulated/mineral, /turf/simulated/wall)) //eat rocks and walls + var/static/list/sharedIgnore = list() + +//This handles viable things to eat/attack +//Place specific cases of AI derpiness here +//Most can be left to the automatic Gain/LosePatience() system +/mob/living/simple_animal/hostile/swarmer/ai/resource/CanAttack(atom/the_target) + + //SPECIFIC CASES: + //Smash fulltile windows before grilles + if(istype(the_target, /obj/structure/grille)) + for(var/obj/structure/window/rogueWindow in get_turf(the_target)) + if(rogueWindow.fulltile) //done this way because the subtypes are weird. + the_target = rogueWindow + break + + //GENERAL CASES: + if(is_type_in_typecache(the_target, sharedIgnore)) //always ignore + return FALSE + if(is_type_in_typecache(the_target, sharedWanted)) //always eat + return TRUE + + return ..() //else, have a nibble, see if it's food + + +/mob/living/simple_animal/hostile/swarmer/ai/resource/OpenFire(atom/A) + if(isliving(A)) //don't shoot rocks, sillies. + ..() + + +/mob/living/simple_animal/hostile/swarmer/ai/resource/AttackingTarget() + if(target.swarmer_act(src)) + add_type_to_wanted(target.type) + return TRUE + else + add_type_to_ignore(target.type) + return FALSE + + +/mob/living/simple_animal/hostile/swarmer/ai/resource/handle_automated_action() + . = ..() + if(.) + if(!stop_automated_movement) + if(GLOB.AISwarmers.len < GetTotalAISwarmerCap() && resources >= 50) + StartAction(100) //so they'll actually sit still and use the verbs + CreateSwarmer() + return + + if(resources > 5) + if(prob(5)) //lower odds, as to prioritise reproduction + StartAction(10) //not a typo + CreateBarricade() + return + if(prob(5)) + CreateTrap() + return + + +//So swarmers can learn what is and isn't food +/mob/living/simple_animal/hostile/swarmer/ai/resource/proc/add_type_to_wanted(typepath) + if(!sharedWanted[typepath])// this and += is faster than |= + sharedWanted += typecacheof(typepath) + + +/mob/living/simple_animal/hostile/swarmer/ai/resource/proc/add_type_to_ignore(typepath) + if(!sharedIgnore[typepath]) + sharedIgnore += typecacheof(typepath) + + +//RANGED SWARMER +/mob/living/simple_animal/hostile/swarmer/ai/ranged_combat + icon_state = "swarmer_ranged" + icon_living = "swarmer_ranged" + projectiletype = /obj/item/projectile/beam/laser + projectilesound = 'sound/weapons/laser.ogg' + check_friendly_fire = TRUE //you're supposed to protect the resource swarmers, you poop + retreat_distance = 3 + minimum_distance = 3 + +/mob/living/simple_animal/hostile/swarmer/ai/ranged_combat/Aggro() + ..() + summon_backup(15, TRUE) //Exact matching, so that goliaths don't come to aid the swarmers, that'd be silly + + +//MELEE SWARMER +/mob/living/simple_animal/hostile/swarmer/ai/melee_combat + icon_state = "swarmer_melee" + icon_living = "swarmer_melee" + health = 60 + maxHealth = 60 + ranged = FALSE + +/mob/living/simple_animal/hostile/swarmer/ai/melee_combat/Aggro() + ..() + summon_backup(15, TRUE) + + +/mob/living/simple_animal/hostile/swarmer/ai/melee_combat/AttackingTarget() + if(isliving(target)) + if(prob(35)) + StartAction(30) + DisperseTarget(target) + else + var/mob/living/L = target + L.attack_animal(src) + L.electrocute_act(10, src, safety = TRUE) //safety = TRUE means we don't check gloves... Ok? + return TRUE + else + return ..() + + + + +//SWARMER CATWALKS +//Used so they can survive lavaland better +/obj/structure/lattice/catwalk/swarmer_catwalk + name = "swarmer catwalk" + desc = "A catwalk-like mesh, produced by swarmers to allow them to navigate hostile terrain." + icon = 'icons/obj/smooth_structures/swarmer_catwalk.dmi' + icon_state = "swarmer_catwalk" \ No newline at end of file diff --git a/code/modules/mob/living/simple_animal/hostile/mining/spawners.dm b/code/modules/mob/living/simple_animal/hostile/mining/necropolis_tendril.dm similarity index 89% rename from code/modules/mob/living/simple_animal/hostile/mining/spawners.dm rename to code/modules/mob/living/simple_animal/hostile/mining/necropolis_tendril.dm index fe648e07f91..e8dfa093918 100644 --- a/code/modules/mob/living/simple_animal/hostile/mining/spawners.dm +++ b/code/modules/mob/living/simple_animal/hostile/mining/necropolis_tendril.dm @@ -33,7 +33,6 @@ qdel(gps) . = ..() -#define MEDAL_PREFIX "Tendril" /mob/living/simple_animal/hostile/spawner/lavaland/death() var/last_tendril = TRUE for(var/mob/living/simple_animal/hostile/spawner/lavaland/other in GLOB.mob_list) @@ -41,18 +40,13 @@ last_tendril = FALSE break if(last_tendril && !admin_spawned) - if(global.medal_hub && global.medal_pass && global.medals_enabled) + if(SSmedals.hub_enabled) for(var/mob/living/L in view(7,src)) - if(L.stat) + if(L.stat || !L.client) continue - if(L.client) - var/client/C = L.client - var/suffixm = ALL_KILL_MEDAL - var/prefix = MEDAL_PREFIX - UnlockMedal("[prefix] [suffixm]",C) - SetScore(TENDRIL_CLEAR_SCORE,C,1) + SSmedals.UnlockMedal("[BOSS_MEDAL_TENDRIL] [ALL_KILL_MEDAL]", L.client) + SSmedals.SetScore(TENDRIL_CLEAR_SCORE, L.client, 1) ..() -#undef MEDAL_PREFIX /obj/effect/collapse name = "collapsing necropolis tendril" diff --git a/icons/mob/swarmer.dmi b/icons/mob/swarmer.dmi index 5a3595f7baf..e434cdd94ae 100644 Binary files a/icons/mob/swarmer.dmi and b/icons/mob/swarmer.dmi differ diff --git a/icons/obj/smooth_structures/swarmer_catwalk.dmi b/icons/obj/smooth_structures/swarmer_catwalk.dmi new file mode 100644 index 00000000000..b247132eacb Binary files /dev/null and b/icons/obj/smooth_structures/swarmer_catwalk.dmi differ diff --git a/icons/turf/floors.dmi b/icons/turf/floors.dmi index fcfde19509c..411f371af9b 100644 Binary files a/icons/turf/floors.dmi and b/icons/turf/floors.dmi differ diff --git a/paradise.dme b/paradise.dme index 77d65c7f30d..2cbae757457 100644 --- a/paradise.dme +++ b/paradise.dme @@ -43,6 +43,7 @@ #include "code\__DEFINES\machines.dm" #include "code\__DEFINES\math.dm" #include "code\__DEFINES\MC.dm" +#include "code\__DEFINES\medal.dm" #include "code\__DEFINES\misc.dm" #include "code\__DEFINES\mobs.dm" #include "code\__DEFINES\move_force.dm" @@ -215,6 +216,7 @@ #include "code\controllers\subsystem\garbage.dm" #include "code\controllers\subsystem\icon_smooth.dm" #include "code\controllers\subsystem\machinery.dm" +#include "code\controllers\subsystem\medals.dm" #include "code\controllers\subsystem\mobs.dm" #include "code\controllers\subsystem\nano_mob_hunter.dm" #include "code\controllers\subsystem\nanoui.dm" @@ -1926,17 +1928,18 @@ #include "code\modules\mob\living\simple_animal\hostile\winter_mobs.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\bubblegum.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\colossus.dm" -#include "code\modules\mob\living\simple_animal\hostile\megafauna\dragon.dm" +#include "code\modules\mob\living\simple_animal\hostile\megafauna\drake.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\hierophant.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm" #include "code\modules\mob\living\simple_animal\hostile\megafauna\megafauna.dm" +#include "code\modules\mob\living\simple_animal\hostile\megafauna\swarmer.dm" #include "code\modules\mob\living\simple_animal\hostile\mining\basilisk.dm" #include "code\modules\mob\living\simple_animal\hostile\mining\goldgrub.dm" #include "code\modules\mob\living\simple_animal\hostile\mining\goliath.dm" #include "code\modules\mob\living\simple_animal\hostile\mining\gutlunch.dm" #include "code\modules\mob\living\simple_animal\hostile\mining\hivelord.dm" #include "code\modules\mob\living\simple_animal\hostile\mining\mining.dm" -#include "code\modules\mob\living\simple_animal\hostile\mining\spawners.dm" +#include "code\modules\mob\living\simple_animal\hostile\mining\necropolis_tendril.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm" #include "code\modules\mob\living\simple_animal\hostile\retaliate\fish.dm" diff --git a/sound/effects/bin_close.ogg b/sound/effects/bin_close.ogg new file mode 100644 index 00000000000..aef7b0cd886 Binary files /dev/null and b/sound/effects/bin_close.ogg differ