- Make Changelings
- Make Heretics
- Make Revs
- Make Cult
- Make Clockwork Cult
- Make Blob
- Make Wizard (Requires Ghosts)
- Make Nuke Team (Requires Ghosts)
Make Uplink CentCom Response Team (Requires Ghosts)
- Make CentCom Response Team (Requires Ghosts)
- Make Abductor Team (Requires Ghosts)
- Make Revenant (Requires Ghost)
- Make Darkspawn
- Make Vampire
- Make Infiltration Team (Requires Ghosts) + Make CentCom Response Team (Requires Ghosts) "} var/datum/browser/popup = new(usr, "oneclickantag", "Quick-Create Antagonist", 400, 400) @@ -48,263 +34,6 @@ return FALSE return !is_banned_from(applicant.ckey, list(targetrole, ROLE_SYNDICATE)) - -/datum/admins/proc/makeTraitors() - var/datum/game_mode/traitor/temp = new - - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - temp.restricted_jobs += temp.protected_jobs - - if(CONFIG_GET(flag/protect_assistant_from_antagonist)) - temp.restricted_jobs += "Assistant" - - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H = null - - for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(isReadytoRumble(applicant, ROLE_TRAITOR)) - if(temp.age_check(applicant.client)) - if(!(applicant.job in temp.restricted_jobs)) - candidates += applicant - - if(candidates.len) - var/numTraitors = min(candidates.len, 3) - - for(var/i = 0, i
") - -/datum/antagonist/hivemind/is_gamemode_hero() - return SSticker.mode.name == "Assimilation" - -/datum/antagonist/hivemind/get_preview_icon() - var/icon/hivemind_icon = icon('icons/mob/hivebot.dmi', "basic") - - hivemind_icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) - - return hivemind_icon diff --git a/code/modules/antagonists/hivemind/vessel.dm b/code/modules/antagonists/hivemind/vessel.dm deleted file mode 100644 index 4d60e268c04d..000000000000 --- a/code/modules/antagonists/hivemind/vessel.dm +++ /dev/null @@ -1,78 +0,0 @@ -/datum/team/hivemind - name = "One Mind" - -/datum/antagonist/hivevessel - name = "Awoken Vessel" - job_rank = ROLE_BRAINWASHED - antag_hud_name = "hivevessel" - roundend_category = "awoken vessels" - show_in_antagpanel = TRUE - antagpanel_category = "Other" - show_name_in_check_antagonists = TRUE - var/datum/team/hivemind/one_mind - var/mutable_appearance/glow - -/mob/living/proc/is_wokevessel() - return mind?.has_antag_datum(/datum/antagonist/hivevessel) - -/mob/living/proc/hive_awaken(objective, datum/team/hivemind/final_form) - if(!mind) - return - var/datum/mind/M = mind - var/datum/antagonist/hivevessel/vessel = M.has_antag_datum(/datum/antagonist/hivevessel) - if(!vessel) - vessel = new() - if(final_form) - vessel.objectives = list() //Reset objectives on re-awoken vessels - if(ishuman(M.current)) - vessel.glow = mutable_appearance('icons/effects/hivemind.dmi', "awoken", -BODY_BEHIND_LAYER) - M.current.add_overlay(vessel.glow) - var/datum/action/cooldown/spell/hive_comms/comms = new(src) - comms.Grant(src) - vessel.one_mind = final_form - vessel.one_mind.add_member(M) - vessel.objectives |= vessel.one_mind.objectives - else - var/datum/objective/brainwashing/obj = new(objective) - vessel.objectives += obj - var/message = " has been awoken with the following objectives: [objective]." - deadchat_broadcast(message, "[M]", follow_target = M, turf_target = get_turf(M), message_type=DEADCHAT_REGULAR) - log_game("[key_name(M)] has been awoken with the following objectives: [objective]") - if(!M.has_antag_datum(/datum/antagonist/hivevessel)) - M.add_antag_datum(vessel) - -/datum/antagonist/hivevessel/apply_innate_effects() - handle_clown_mutation(owner.current, "Our newfound powers allow us to overcome our clownish nature, allowing us to wield weapons with impunity.") - -/datum/antagonist/hivevessel/greet() - to_chat(owner, span_assimilator("Your mind is suddenly opened, as you see the pinnacle of evolution...")) - to_chat(owner, "Follow your objectives, at any cost!") - var/i = 1 - for(var/X in objectives) - var/datum/objective/O = X - to_chat(owner, "[i]. [O.explanation_text]") - i++ - -/datum/antagonist/hivevessel/on_removal() - if(owner?.current && glow) - owner.current.cut_overlay(glow) - if(one_mind && owner) - one_mind.remove_member(owner) - ..() - -/datum/antagonist/hivevessel/farewell() - to_chat(owner, span_assimilator("Your mind closes up once more...")) - to_chat(owner, "You feel the weight of your objectives disappear! You no longer have to obey them.") - -/datum/antagonist/hivevessel/roundend_report() - if(!owner) - CRASH("antagonist datum without owner") - - if(one_mind) - return printplayer(owner) - - var/list/report = list() - report += printplayer(owner) - if(objectives.len) - report += printobjectives(objectives) - return report.Join("
") diff --git a/code/modules/antagonists/monkey/monkey.dm b/code/modules/antagonists/monkey/monkey.dm deleted file mode 100644 index 50753c76cb9f..000000000000 --- a/code/modules/antagonists/monkey/monkey.dm +++ /dev/null @@ -1,214 +0,0 @@ -#define MONKEYS_ESCAPED 1 -#define MONKEYS_LIVED 2 -#define MONKEYS_DIED 3 -#define DISEASE_LIVED 4 - -/datum/antagonist/monkey - name = "Monkey" - job_rank = ROLE_MONKEY - roundend_category = "monkeys" - antagpanel_category = "Monkey" - show_to_ghosts = TRUE - var/datum/team/monkey/monkey_team - var/monkey_only = TRUE - -/datum/antagonist/monkey/can_be_owned(datum/mind/new_owner) - return ..() && (!monkey_only || ismonkey(new_owner.current)) - -/datum/antagonist/monkey/get_team() - return monkey_team - -/datum/antagonist/monkey/get_preview_icon() - // Creating a *real* monkey is fairly involved before atoms init. - var/icon/icon = icon('icons/mob/monkey.dmi', "monkey1") - - icon.Crop(4, 9, 28, 33) - icon.Scale(ANTAGONIST_PREVIEW_ICON_SIZE, ANTAGONIST_PREVIEW_ICON_SIZE) - icon.Shift(SOUTH, 10) - - return icon - -/datum/antagonist/monkey/on_gain() - . = ..() - SSticker.mode.ape_infectees += owner - owner.special_role = "Infected Monkey" - - var/datum/disease/D = new /datum/disease/transformation/jungle_fever/monkeymode - if(!owner.current.HasDisease(D)) - owner.current.ForceContractDisease(D) - else - QDEL_NULL(D) - -/datum/antagonist/monkey/greet() - to_chat(owner, "You are a monkey now!") - to_chat(owner, "Bite humans to infect them, follow the orders of the monkey leaders, and help fellow monkeys!") - to_chat(owner, "Ensure at least one infected monkey escapes on the Emergency Shuttle!") - to_chat(owner, "As an intelligent monkey, you know how to use technology and how to ventcrawl while wearing things.") - to_chat(owner, "You can use :k to talk to fellow monkeys!") - SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg')) - -/datum/antagonist/monkey/on_removal() - owner.special_role = null - SSticker.mode.ape_infectees -= owner - - var/datum/disease/transformation/jungle_fever/D = locate() in owner.current.diseases - if(D) - qdel(D) - - . = ..() - -/datum/antagonist/monkey/create_team(datum/team/monkey/new_team) - if(!new_team) - for(var/datum/antagonist/monkey/H in GLOB.antagonists) - if(!H.owner) - continue - if(H.monkey_team) - monkey_team = H.monkey_team - return - monkey_team = new /datum/team/monkey - monkey_team.update_objectives() - return - if(!istype(new_team)) - stack_trace("Wrong team type passed to [type] initialization.") - monkey_team = new_team - -/datum/antagonist/monkey/admin_remove(mob/admin) - var/mob/living/carbon/human/M = owner.current - if(ismonkey(M)) - var/admin_response = tgui_alert(admin, "Humanize?", "Humanize", list("Yes", "No")) - if(admin_response != "Yes") - return ..() - if(admin == M) - admin = M.humanize(TR_KEEPITEMS|TR_KEEPIMPLANTS|TR_KEEPORGANS|TR_KEEPDAMAGE|TR_KEEPVIRUS|TR_KEEPSTUNS|TR_KEEPREAGENTS|TR_DEFAULTMSG) - else - M.humanize(TR_KEEPITEMS|TR_KEEPIMPLANTS|TR_KEEPORGANS|TR_KEEPDAMAGE|TR_KEEPVIRUS|TR_KEEPSTUNS|TR_KEEPREAGENTS|TR_DEFAULTMSG) - return ..() - -/datum/antagonist/monkey/leader - name = "Monkey Leader" - monkey_only = FALSE - -/datum/antagonist/monkey/leader/admin_add(datum/mind/new_owner,mob/admin) - var/mob/living/carbon/human/H = new_owner.current - if(istype(H)) - var/admin_response = tgui_alert(admin, "Monkeyize?", "Monkeyize", list("Yes", "No")) - if(admin_response != "Yes") - return ..() - if(admin == H) - admin = H.monkeyize() - else - H.monkeyize() - return ..() - -/datum/antagonist/monkey/leader/on_gain() - . = ..() - var/obj/item/organ/heart/freedom/F = new - F.Insert(owner.current, drop_if_replaced = FALSE) - SSticker.mode.ape_leaders += owner - owner.special_role = "Monkey Leader" - -/datum/antagonist/monkey/leader/on_removal() - SSticker.mode.ape_leaders -= owner - var/obj/item/organ/heart/H = new - H.Insert(owner.current, drop_if_replaced = FALSE) //replace freedom heart with normal heart - - . = ..() - -/datum/antagonist/monkey/leader/greet() - to_chat(owner, "You are the Jungle Fever patient zero!!") - to_chat(owner, "You have been planted onto this station by the Animal Rights Consortium.") - to_chat(owner, "Soon the disease will transform you into an ape. Afterwards, you will be able spread the infection to others with a bite.") - to_chat(owner, "While your infection strain is undetectable by scanners, any other infectees will show up on medical equipment.") - to_chat(owner, "Your mission will be deemed a success if any of the live infected monkeys reach CentCom.") - to_chat(owner, "As an initial infectee, you will be considered a 'leader' by your fellow monkeys.") - to_chat(owner, "You can use :k to talk to fellow monkeys!") - SEND_SOUND(owner.current, sound('sound/ambience/antag/monkey.ogg')) - -/datum/objective/monkey - explanation_text = "Ensure that infected monkeys escape on the emergency shuttle!" - martyr_compatible = TRUE - var/monkeys_to_win = 1 - var/escaped_monkeys = 0 - -/datum/objective/monkey/check_completion() - if(..()) - return TRUE - var/datum/disease/D = new /datum/disease/transformation/jungle_fever() - for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list) - if (M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase())) - escaped_monkeys++ - if(escaped_monkeys >= monkeys_to_win) - return TRUE - return FALSE - -/datum/team/monkey - name = "Monkeys" - -/datum/team/monkey/proc/update_objectives() - objectives = list() - var/datum/objective/monkey/O = new() - O.team = src - objectives += O - -/datum/team/monkey/proc/infected_monkeys_alive() - var/datum/disease/D = new /datum/disease/transformation/jungle_fever() - for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list) - if(M.HasDisease(D)) - return TRUE - return FALSE - -/datum/team/monkey/proc/infected_monkeys_escaped() - var/datum/disease/D = new /datum/disease/transformation/jungle_fever() - for(var/mob/living/carbon/monkey/M in GLOB.alive_mob_list) - if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase())) - return TRUE - return FALSE - -/datum/team/monkey/proc/infected_humans_escaped() - var/datum/disease/D = new /datum/disease/transformation/jungle_fever() - for(var/mob/living/carbon/human/M in GLOB.alive_mob_list) - if(M.HasDisease(D) && (M.onCentCom() || M.onSyndieBase())) - return TRUE - return FALSE - -/datum/team/monkey/proc/infected_humans_alive() - var/datum/disease/D = new /datum/disease/transformation/jungle_fever() - for(var/mob/living/carbon/human/M in GLOB.alive_mob_list) - if(M.HasDisease(D)) - return TRUE - return FALSE - -/datum/team/monkey/proc/get_result() - if(infected_monkeys_escaped()) - return MONKEYS_ESCAPED - if(infected_monkeys_alive()) - return MONKEYS_LIVED - if(infected_humans_alive() || infected_humans_escaped()) - return DISEASE_LIVED - return MONKEYS_DIED - -/datum/team/monkey/roundend_report() - var/list/parts = list() - switch(get_result()) - if(MONKEYS_ESCAPED) - parts += "Monkey Major Victory!" - parts += span_greentext("Central Command and [station_name()] were taken over by the monkeys! Ook ook!") - if(MONKEYS_LIVED) - parts += "Monkey Minor Victory!" - parts += span_greentext("[station_name()] was taken over by the monkeys! Ook ook!") - if(DISEASE_LIVED) - parts += "Monkey Minor Defeat!" - parts += span_redtext("All the monkeys died, but the disease lives on! The future is uncertain.") - if(MONKEYS_DIED) - parts += "Monkey Major Defeat!" - parts += span_redtext("All the monkeys died, and Jungle Fever was wiped out!") - var/list/leaders = get_antag_minds(/datum/antagonist/monkey/leader, TRUE) - var/list/monkeys = get_antag_minds(/datum/antagonist/monkey, TRUE) - - if(LAZYLEN(leaders)) - parts += span_header("The monkey leaders were:") - parts += printplayerlist(SSticker.mode.ape_leaders) - if(LAZYLEN(monkeys)) - parts += span_header("The monkeys were:") - parts += printplayerlist(SSticker.mode.ape_infectees) - return "
")]
" diff --git a/code/modules/antagonists/slaughter/slaughterevent.dm b/code/modules/antagonists/slaughter/slaughterevent.dm index 1266cb04f7f6..b1d5d3437516 100644 --- a/code/modules/antagonists/slaughter/slaughterevent.dm +++ b/code/modules/antagonists/slaughter/slaughterevent.dm @@ -5,7 +5,13 @@ max_occurrences = 1 earliest_start = 1 HOURS min_players = 20 - dynamic_should_hijack = TRUE + category = EVENT_CATEGORY_ENTITIES + description = "Spawns a slaughter demon, to hunt by travelling through pools of blood." + min_wizard_trigger_potency = 6 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_EXTERNAL, TAG_MAGICAL) + checks_antag_cap = TRUE diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 391e551cb076..2e5cebd25d33 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -38,12 +38,12 @@ /obj/item/soulstone/pickup(mob/living/user) ..() - if(!iscultist(user) && !iswizard(user) && !usability) + if(!iscultist(user) && !IS_WIZARD(user) && !usability) to_chat(user, span_danger("An overwhelming feeling of dread comes over you as you pick up the soulstone. It would be wise to be rid of this quickly.")) /obj/item/soulstone/examine(mob/user) . = ..() - if(usability || iscultist(user) || iswizard(user) || isobserver(user)) + if(usability || iscultist(user) || IS_WIZARD(user) || isobserver(user)) if (old_shard) . += span_cult("A soulstone, used to capture a soul, either from dead humans or from freed shades.") else @@ -60,7 +60,7 @@ //////////////////////////////Capturing//////////////////////////////////////////////////////// /obj/item/soulstone/attack(mob/living/carbon/human/M, mob/living/user) - if(!iscultist(user) && !iswizard(user) && !usability) + if(!iscultist(user) && !IS_WIZARD(user) && !usability) user.Unconscious(10 SECONDS) to_chat(user, span_userdanger("Your body is wracked with debilitating pain!")) return @@ -89,7 +89,7 @@ /obj/item/soulstone/attack_self(mob/living/user) if(!in_range(src, user)) return - if(!iscultist(user) && !iswizard(user) && !usability) + if(!iscultist(user) && !IS_WIZARD(user) && !usability) user.Unconscious(100) to_chat(user, span_userdanger("Your body is wracked with debilitating pain!")) return @@ -109,7 +109,7 @@ else icon_state = "soulstone" name = initial(name) - if(iswizard(user) || usability) + if(IS_WIZARD(user) || usability) to_chat(A, "You have been released from your prison, but you are still bound to [user.real_name]'s will. Help [user.p_them()] succeed in [user.p_their()] goals at all costs.") else if(iscultist(user)) to_chat(A, "You have been released from your prison, but you are still bound to the cult's will. Help them succeed in their goals at all costs.") @@ -124,7 +124,7 @@ /obj/structure/constructshell/examine(mob/user) . = ..() - if(iscultist(user) || iswizard(user) || user.stat == DEAD) + if(iscultist(user) || IS_WIZARD(user) || user.stat == DEAD) . += {"A construct shell, used to house bound souls from a soulstone.\n Placing a soulstone with a soul into this shell allows you to produce your choice of the following:\n An Artificer, which can produce more shells and soulstones, as well as fortifications.\n @@ -134,7 +134,7 @@ /obj/structure/constructshell/attackby(obj/item/O, mob/user, params) if(istype(O, /obj/item/soulstone)) var/obj/item/soulstone/SS = O - if(!iscultist(user) && !iswizard(user) && !SS.purified) + if(!iscultist(user) && !IS_WIZARD(user) && !SS.purified) to_chat(user, span_danger("An overwhelming feeling of dread comes over you as you attempt to place the soulstone into the shell. It would be wise to be rid of this quickly.")) if(isliving(user)) var/mob/living/living_user = user @@ -218,7 +218,7 @@ return switch(construct_class) if("Juggernaut") - if(iscultist(user) || iswizard(user)) + if(iscultist(user) || IS_WIZARD(user)) makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored, A, user, 0, T.loc) else if(purified) @@ -226,7 +226,7 @@ else makeNewConstruct(/mob/living/simple_animal/hostile/construct/armored/noncult, A, user, 0, T.loc) if("Wraith") - if(iscultist(user) || iswizard(user)) + if(iscultist(user) || IS_WIZARD(user)) makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith, A, user, 0, T.loc) else if(purified) @@ -234,7 +234,7 @@ else makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith/noncult, A, user, 0, T.loc) if("Artificer") - if(iscultist(user) || iswizard(user)) + if(iscultist(user) || IS_WIZARD(user)) makeNewConstruct(/mob/living/simple_animal/hostile/construct/builder, A, user, 0, T.loc) else if(purified) @@ -302,7 +302,7 @@ icon_state = "purified_soulstone2" else icon_state = "soulstone2" - if(U && (iswizard(U) || usability)) + if(U && (IS_WIZARD(U) || usability)) to_chat(S, "Your soul has been captured! You are now bound to [U.real_name]'s will. Help [U.p_them()] succeed in [U.p_their()] goals at all costs.") else if(U && iscultist(U)) to_chat(S, "Your soul has been captured! You are now bound to the cult's will. Help them succeed in their goals at all costs.") diff --git a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm index 051af2f45d91..1f44c40c60ce 100644 --- a/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm +++ b/code/modules/antagonists/wizard/equipment/spellbook_entries/summons.dm @@ -20,11 +20,7 @@ There is a good chance that they will shoot each other first." /datum/spellbook_entry/summon/guns/can_be_purchased() - // Summon Guns requires 100 threat. - var/datum/game_mode/dynamic/mode = SSticker.mode - if(mode?.threat_level < MINIMUM_THREAT_FOR_RITUALS) - return FALSE - // Also must be config enabled + // must be config enabled return !CONFIG_GET(flag/no_summon_guns) /datum/spellbook_entry/summon/guns/buy_spell(mob/living/carbon/human/user,obj/item/spellbook/book) @@ -38,11 +34,7 @@ why they aren't to be trusted with it at the same time." /datum/spellbook_entry/summon/magic/can_be_purchased() - // Summon Magic requires 100 threat. - var/datum/game_mode/dynamic/mode = SSticker.mode - if(!istype(mode) || mode.threat_level < MINIMUM_THREAT_FOR_RITUALS) //YOGS - secret gamemode - return FALSE - // Also must be config enabled + // must be config enabled return !CONFIG_GET(flag/no_summon_magic) /datum/spellbook_entry/summon/magic/buy_spell(mob/living/carbon/human/user,obj/item/spellbook/book) @@ -59,11 +51,7 @@ limit = 5 // Each purchase can intensify it. /datum/spellbook_entry/summon/events/can_be_purchased() - // Summon Events requires 100 threat. - var/datum/game_mode/dynamic/mode = SSticker.mode - if(!istype(mode) || mode.threat_level < MINIMUM_THREAT_FOR_RITUALS) //YOGS - secret gamemode - return FALSE - // Also, must be config enabled + // must be config enabled return !CONFIG_GET(flag/no_summon_events) /datum/spellbook_entry/summon/events/buy_spell(mob/living/carbon/human/user, obj/item/spellbook/book) diff --git a/code/modules/antagonists/zombie/abilities/acid.dm b/code/modules/antagonists/zombie/abilities/acid.dm deleted file mode 100644 index dfcbe9987583..000000000000 --- a/code/modules/antagonists/zombie/abilities/acid.dm +++ /dev/null @@ -1,52 +0,0 @@ -/*/obj/effect/proc_holder/zombie/acid - name = "Corrosive Acid" - desc = "Drench an object in acid, destroying it over time." - button_icon_state = "alien_acid" - cooldown_time = 2.5 MINUTES - -/obj/effect/proc_holder/zombie/acid/on_gain(mob/living/carbon/user) - user.verbs.Add(/mob/living/carbon/proc/spitter_zombie_acid) - -/obj/effect/proc_holder/zombie/acid/on_lose(mob/living/carbon/user) - user.verbs.Remove(/mob/living/carbon/proc/spitter_zombie_acid) - -/obj/effect/proc_holder/zombie/acid/proc/corrode(atom/target,mob/living/carbon/user = usr) - if(target in oview(1, user)) - if(target.acid_act(200, 100)) - user.visible_message(span_alertalien("[user] vomits globs of vile stuff all over [target]. It begins to sizzle and melt under the bubbling mess of acid!")) - return TRUE - else - to_chat(user, span_noticealien("You cannot dissolve this object.")) - - - return FALSE - else - to_chat(src, span_noticealien("[target] is too far away.")) - return FALSE - - -/obj/effect/proc_holder/zombie/acid/fire(mob/living/carbon/user) - var/O = input("Select what to dissolve:", "Dissolve", null) as obj|turf in oview(1,user) - if(!O || user.incapacitated()) - return FALSE - if(corrode(O, user)) - return ..() - - -/mob/living/carbon/proc/spitter_zombie_acid(O as obj|turf in oview(1)) // right click menu verb ugh - set name = "Corrosive Acid" - - if(!isinfected(usr)) - return - var/mob/living/carbon/user = usr - var/obj/effect/proc_holder/zombie/acid/A = locate() in user.actions - if(!A) - return - - if(!A.ready) - to_chat(user, span_userdanger("Please wait. You will be able to use this ability in [(A.cooldown_ends - world.time) / 10] seconds")) - return - - if(A.corrode(O, user)) - A.start_cooldown() -*/ diff --git a/code/modules/antagonists/zombie/abilities/adrenaline.dm b/code/modules/antagonists/zombie/abilities/adrenaline.dm deleted file mode 100644 index 8b847cd1e82a..000000000000 --- a/code/modules/antagonists/zombie/abilities/adrenaline.dm +++ /dev/null @@ -1,29 +0,0 @@ -/*/obj/effect/proc_holder/zombie/adrenaline - name = "Adrenaline Boost" - desc = "Makes you able to sprint for a second or two!" - button_icon = 'icons/mob/actions/actions_changeling.dmi' - button_icon_state = "adrenaline" - cooldown_time = 3 MINUTES - var/reagent_amount = 3 - - -/obj/effect/proc_holder/zombie/adrenaline/proc/add_reagent(mob/living/carbon/user) - user.SetSleeping(0) - user.SetUnconscious(0) - user.SetStun(0) - user.SetKnockdown(0) - user.SetImmobilized(0) - user.SetParalyzed(0) - user.reagents.add_reagent(/datum/reagent/medicine/changelinghaste, reagent_amount) - user.adjustStaminaLoss(-75) - return TRUE - - - -/obj/effect/proc_holder/zombie/adrenaline/fire(mob/living/carbon/user) - if(user.incapacitated()) - return FALSE - - if(add_reagent(user)) - return ..() -*/ diff --git a/code/modules/antagonists/zombie/abilities/necromance.dm b/code/modules/antagonists/zombie/abilities/necromance.dm deleted file mode 100644 index 0999fd035040..000000000000 --- a/code/modules/antagonists/zombie/abilities/necromance.dm +++ /dev/null @@ -1,61 +0,0 @@ -/*/obj/effect/proc_holder/zombie/necromance - name = "Summon a Minion" - desc = "Summons a zombie to help you." - button_icon = 'icons/mob/actions/actions_cult.dmi' - button_icon_state = "equip" - cooldown_time = 5 MINUTES - var/list/summoned_minions = list() - var/max_minions = 5 - var/max_distance = 8 - var/necromancing = FALSE - - -/obj/effect/proc_holder/zombie/necromance/proc/necromance() - if(total_minion_count() >= max_minions) - return FALSE - if(necromancing) - return FALSE - - necromancing = TRUE - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a Necromanced Zombie?", ROLE_ZOMBIE, null, ROLE_ZOMBIE, 150) - necromancing = FALSE //Incase we runtime during the process below - - - if(LAZYLEN(candidates)) - var/mob/living/carbon/human/M = new /mob/living/carbon/human(get_turf(usr)) - M.mind_initialize() - var/mob/dead/observer/C = pick(candidates) - M.set_species(/datum/species/zombie/infectious/gamemode/necromanced_minion) - M.key = C.key - var/datum/species/zombie/infectious/gamemode/necromanced_minion/species = M.dna.species - species.master = usr - species.max_distance = max_distance - summoned_minions += M - - var/datum/game_mode/zombie/GM = SSticker.mode - if(!istype(GM)) - return - GM.add_zombie(M.mind) - usr.visible_message(span_danger("[usr] conjures up a necromanced minion!")) - to_chat(M, span_userdanger("You are bound to [usr.name]. If you get too far away from them, you will start dying.")) - return TRUE - - return FALSE - - -/obj/effect/proc_holder/zombie/necromance/proc/total_minion_count() - var/count = 0 - for(var/MU in summoned_minions) - var/mob/living/M = MU - - if(M.stat != DEAD) - count++ - return count - -/obj/effect/proc_holder/zombie/necromance/fire(mob/living/carbon/alien/user) - if(user.incapacitated()) - return FALSE - - necromance() - return ..() -*/ diff --git a/code/modules/antagonists/zombie/abilities/spit.dm b/code/modules/antagonists/zombie/abilities/spit.dm deleted file mode 100644 index 62659d97633c..000000000000 --- a/code/modules/antagonists/zombie/abilities/spit.dm +++ /dev/null @@ -1,60 +0,0 @@ -/*/obj/effect/proc_holder/zombie/spit - name = "Spit Neurotoxin" - desc = "Spits neurotoxin at someone, paralyzing them for a short time." - button_icon_state = "alien_neurotoxin_0" - active = FALSE - cooldown_time = 1 MINUTES - - -/obj/effect/proc_holder/zombie/spit/fire(mob/living/carbon/user) - if(active) - remove_ranged_ability(span_notice("You close your neurotoxin reserves.")) - else - add_ranged_ability(user, span_notice("You open your neurotoxin reserves. Left-click to fire at a target!"), TRUE) - -/obj/effect/proc_holder/zombie/spit/update_icon(updates=ALL) - . = ..() - action.button_icon_state = "alien_neurotoxin_[active]" - action.build_all_button_icons() - -/obj/effect/proc_holder/zombie/spit/InterceptClickOn(mob/living/caller, params, atom/target) - if(..()) - return FALSE - if(!isinfected(owner) || owner.stat != CONSCIOUS) - remove_ranged_ability() - return FALSE - - var/mob/living/carbon/user = owner - - if(!ready) - to_chat(user, span_warning("You cannot currently spit. You can spit again in [(cooldown_ends - world.time) / 10] seconds")) - remove_ranged_ability() - return FALSE - - var/turf/T = user.loc - var/turf/U = get_step(user, user.dir) // Get the tile infront of the move, based on their direction - if(!U || !T) - return FALSE - - user.visible_message("[user] spits neurotoxin!", span_alertalien("You spit neurotoxin.")) - var/obj/projectile/bullet/neurotoxin/spitter/A = new /obj/projectile/bullet/neurotoxin/spitter(user.loc) - A.preparePixelProjectile(target, user, params) - A.fire() - user.newtonian_move(get_dir(U, T)) - start_cooldown() - - return TRUE - -/obj/projectile/bullet/neurotoxin/spitter - name = "neurotoxin spit" - icon_state = "neurotoxin" - damage = 2 - damage_type = TOX - paralyze = 50 - -/obj/projectile/bullet/neurotoxin/spitter/on_hit(atom/target, blocked = FALSE) - if(isinfected(target)) - paralyze = 0 - nodamage = TRUE - return ..() -*/ diff --git a/code/modules/antagonists/zombie/abilities/tank.dm b/code/modules/antagonists/zombie/abilities/tank.dm deleted file mode 100644 index 4f74c6e0c195..000000000000 --- a/code/modules/antagonists/zombie/abilities/tank.dm +++ /dev/null @@ -1,30 +0,0 @@ -/*/obj/effect/proc_holder/zombie/tank - name = "Tank" - desc = "Gives you a moderate armor boost for a few seconds. Heals 60% of your brute and fire damage." - button_icon = 'icons/mob/actions/actions_changeling.dmi' - button_icon_state = "fake_death" - cooldown_time = 2.5 MINUTES - var/duration = 30 SECONDS - var/armor_boost = 12.5 - var/heal = 0.6 - - -/obj/effect/proc_holder/zombie/tank/proc/run_ability(mob/living/carbon/human/user) - addtimer(CALLBACK(src, PROC_REF(stop), user), duration) - user.dna.species.armor += armor_boost - user.adjustBruteLoss(-(heal * user.getBruteLoss())) - user.adjustFireLoss(-(heal * user.getFireLoss())) - return TRUE - -/obj/effect/proc_holder/zombie/tank/proc/stop(mob/living/carbon/human/user) - - user.dna.species.armor -= armor_boost - to_chat(user, span_userdanger("Your armor boost has ended!")) - -/obj/effect/proc_holder/zombie/tank/fire(mob/living/carbon/user) - if(user.incapacitated()) - return FALSE - - if(run_ability(user)) - return ..() -*/ diff --git a/code/modules/antagonists/zombie/abilities/uncuff.dm b/code/modules/antagonists/zombie/abilities/uncuff.dm deleted file mode 100644 index 4b3a39eb66eb..000000000000 --- a/code/modules/antagonists/zombie/abilities/uncuff.dm +++ /dev/null @@ -1,8 +0,0 @@ -/datum/action/innate/zombie/uncuff - name = "Break Free!" - desc = "Breaks you free from handcuffs." - button_icon_state = "biodegrade" - -/datum/action/innate/zombie/uncuff/Activate(forced = FALSE) - var/mob/living/carbon/human/H = usr - H.uncuff() diff --git a/code/modules/antagonists/zombie/zombie.dm b/code/modules/antagonists/zombie/zombie.dm index 21087bdeee6b..068c3e25557e 100644 --- a/code/modules/antagonists/zombie/zombie.dm +++ b/code/modules/antagonists/zombie/zombie.dm @@ -1,5 +1,7 @@ #define TIER_2_TIME 4500 +GLOBAL_LIST_EMPTY(zombies) + /datum/antagonist/zombie name = "Zombie" roundend_category = "zombies" @@ -151,7 +153,7 @@ /datum/team/zombie/proc/zombies_on_shuttle() for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) - if(isinfected(H) && (H.onCentCom() || H.onSyndieBase())) + if(IS_INFECTED(H) && (H.onCentCom() || H.onSyndieBase())) return TRUE return FALSE @@ -176,7 +178,7 @@ check_flags = AB_CHECK_HANDS_BLOCKED| AB_CHECK_IMMOBILE|AB_CHECK_CONSCIOUS /datum/action/innate/zombie/IsAvailable(feedback = FALSE) - if(!isinfected(owner)) + if(!IS_INFECTED(owner)) return FALSE return ..() @@ -230,7 +232,7 @@ my_message = "\[[title]] [findtextEx(user.name, user.real_name) ? user.name : "[user.real_name] (as [user.name])"]: [message]" for(var/i in GLOB.player_list) var/mob/M = i - if(isinfected(M)) + if(IS_INFECTED(M)) to_chat(M, my_message) else if(M in GLOB.dead_mob_list) var/link = FOLLOW_LINK(M, user) @@ -248,12 +250,12 @@ var/selected = input(usr, "Choose a class to evolve into", "Evolution") as null|anything in list("Runner", "Juggernaut", "Spitter") if(!selected || !IsAvailable(feedback = FALSE)) return - if(!isinfectedzombie(owner)) + if(!IS_INFECTEDzombie(owner)) return evolve(selected) /datum/action/innate/zombie/choose_class/IsAvailable(forced = FALSE) - if(!isinfected(owner)) + if(!IS_INFECTED(owner)) return var/datum/antagonist/zombie/Z = locate() in owner.mind.antag_datums @@ -264,7 +266,7 @@ /datum/action/innate/zombie/choose_class/proc/evolve(class) var/mob/living/carbon/human/H = owner var/datum/antagonist/zombie/Z = locate() in owner.mind.antag_datums - if(!isinfected(H)) + if(!IS_INFECTED(H)) return switch(class) if("Runner") @@ -298,7 +300,7 @@ button_icon_state = "cultfist" /datum/action/innate/zombie/choose_class/tier2/IsAvailable(feedback = FALSE) - if(!isinfected(owner)) + if(!IS_INFECTED(owner)) return var/datum/antagonist/zombie/Z = locate() in owner.mind.antag_datums @@ -313,7 +315,7 @@ var/selected = input(usr, "Choose a class to evolve into", "Evolution") as null|anything in list("Necromancer") if(!selected || !IsAvailable(feedback = FALSE)) return - if(!isinfectedzombie(owner)) + if(!IS_INFECTEDzombie(owner)) return var/datum/game_mode/zombie/mode = SSticker.mode @@ -325,7 +327,7 @@ /datum/action/innate/zombie/choose_class/tier2/evolve(class) var/mob/living/carbon/human/H = owner var/datum/antagonist/zombie/Z = locate() in owner.mind.antag_datums - if(!isinfected(H)) + if(!IS_INFECTED(H)) return switch(class) if("Necromancer") @@ -396,7 +398,7 @@ return "[(cooldown_ends - world.time) / 10] seconds" /obj/effect/proc_holder/zombie/proc/can_cast(mob/living/carbon/user) - if(!isinfected(user)) + if(!IS_INFECTED(user)) return FALSE if(user.stat) diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 4a639fea4a4b..077fda4f1b73 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -1153,3 +1153,19 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( winset(src, "mapwindow.map", "right-click=false") winset(src, "default.Shift", "is-disabled=true") winset(src, "default.ShiftUp", "is-disabled=true") + + +/// Checks if this client has met the days requirement passed in, or if +/// they are exempt from it. +/// Returns the number of days left, or 0. +/client/proc/get_remaining_days(days_needed) + if(!CONFIG_GET(flag/use_age_restriction_for_jobs)) + return 0 + + if(!isnum(player_age)) + return 0 //This is only a number if the db connection is established, otherwise it is text: "Requires database", meaning these restrictions cannot be enforced + + if(!isnum(days_needed)) + return 0 + + return max(0, days_needed - player_age) diff --git a/code/modules/client/preferences/middleware/antags.dm b/code/modules/client/preferences/middleware/antags.dm index ce409637f140..6c721ccdc9b8 100644 --- a/code/modules/client/preferences/middleware/antags.dm +++ b/code/modules/client/preferences/middleware/antags.dm @@ -59,14 +59,13 @@ /datum/preference_middleware/antags/proc/get_antag_bans() var/list/antag_bans = list() - for (var/datum/dynamic_ruleset/dynamic_ruleset as anything in subtypesof(/datum/dynamic_ruleset)) - var/antag_flag = initial(dynamic_ruleset.antag_flag) - var/antag_flag_override = initial(dynamic_ruleset.antag_flag_override) + for (var/datum/game_mode/gamemode as anything in subtypesof(/datum/game_mode)) + var/antag_flag = initial(gamemode.antag_flag) if (isnull(antag_flag)) continue - if (is_banned_from(preferences.parent.ckey, list(antag_flag_override || antag_flag, ROLE_SYNDICATE))) + if (is_banned_from(preferences.parent.ckey, list(antag_flag, ROLE_SYNDICATE))) antag_bans += serialize_antag_name(antag_flag) return antag_bans @@ -77,17 +76,12 @@ var/list/antag_days_left = list() - for (var/datum/dynamic_ruleset/dynamic_ruleset as anything in subtypesof(/datum/dynamic_ruleset)) - var/antag_flag = initial(dynamic_ruleset.antag_flag) - //var/antag_flag_override = initial(dynamic_ruleset.antag_flag_override) + for (var/datum/game_mode/gamemode as anything in subtypesof(/datum/game_mode)) + var/antag_flag = initial(gamemode.antag_flag) if (isnull(antag_flag)) continue - /*var/days_needed = preferences.parent?.get_remaining_days( - GLOB.special_roles[antag_flag_override || antag_flag] - )*/ - var/days_needed = 0 if (days_needed > 0) @@ -120,14 +114,6 @@ var/list/antagonists = GLOB.special_roles.Copy() - /*for (var/datum/dynamic_ruleset/ruleset as anything in subtypesof(/datum/dynamic_ruleset)) - var/datum/antagonist/antagonist_type = initial(ruleset.antag_datum) - if (isnull(antagonist_type)) - continue - - // antag_flag is guaranteed to be unique by unit tests. - antagonists[initial(ruleset.antag_flag)] = antagonist_type - */ var/list/generated_icons = list() var/list/to_insert = list() diff --git a/code/modules/events/_event.dm b/code/modules/events/_event.dm index 6cc47b281cd6..1eaec9861361 100644 --- a/code/modules/events/_event.dm +++ b/code/modules/events/_event.dm @@ -1,198 +1,394 @@ -#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME (20 SECONDS) +#define RANDOM_EVENT_ADMIN_INTERVENTION_TIME (10 SECONDS) //this singleton datum is used by the events controller to dictate how it selects events /datum/round_event_control - var/name //The human-readable name of the event - var/typepath //The typepath of the event datum /datum/round_event + var/name //The human-readable name of the event + var/category //The category of the event + var/description //The description of the event + var/typepath //The typepath of the event datum /datum/round_event - var/weight = 10 //The weight this event has in the random-selection process. + var/weight = 10 //The weight this event has in the random-selection process. //Higher weights are more likely to be picked. //10 is the default weight. 20 is twice more likely; 5 is half as likely as this default. //0 here does NOT disable the event, it just makes it extremely unlikely - var/earliest_start = 20 MINUTES //The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins - var/min_players = 0 //The minimum amount of alive, non-AFK human players on server required to start the event. + var/earliest_start = 20 MINUTES //The earliest world.time that an event can start (round-duration in deciseconds) default: 20 mins + var/min_players = 0 //The minimum amount of alive, non-AFK human players on server required to start the event. - var/occurrences = 0 //How many times this event has occured - var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced. + var/occurrences = 0 //How many times this event has occured + var/max_occurrences = 20 //The maximum number of times this event can occur (naturally), it can still be forced. //By setting this to 0 you can effectively disable an event. - var/holidayID = "" //string which should be in the SSeventss.holidays list if you wish this event to be holiday-specific + var/holidayID = "" //string which should be in the SSgamemodes.holidays list if you wish this event to be holiday-specific //anything with a (non-null) holidayID which does not match holiday, cannot run. var/wizardevent = FALSE - var/random = FALSE //If the event has occured randomly, or if it was forced by an admin or in-game occurance - var/alert_observers = TRUE //should we let the ghosts and admins know this event is firing + var/alert_observers = TRUE //should we let the ghosts and admins know this event is firing //should be disabled on events that fire a lot - var/list/gamemode_blacklist = list() // Event won't happen in these gamemodes - var/list/gamemode_whitelist = list() // Event will happen ONLY in these gamemodes if not empty - - var/triggering //admin cancellation - - var/max_alert = SEC_LEVEL_RED /// Highest alert level the event will trigger at - var/min_alert = SEC_LEVEL_GREEN /// Lowest alert level the event will trigger at - - /// Whether or not dynamic should hijack this event - var/dynamic_should_hijack = FALSE - -/datum/round_event_control/vv_edit_var(var_name, var_value) - if(var_name == NAMEOF(src, random) && var_value) // CAN'T LET YOU DO THAT, STAR FOX - message_admins("No, [key_name_admin(usr)], you cannot fake force a random event.") - return FALSE - . = ..() + /// Minimum wizard rituals at which to trigger this event, inclusive + var/min_wizard_trigger_potency = NEVER_TRIGGERED_BY_WIZARDS + /// Maximum wizard rituals at which to trigger this event, inclusive + var/max_wizard_trigger_potency = NEVER_TRIGGERED_BY_WIZARDS + + var/triggering //admin cancellation + + /// Datum that will handle admin options for forcing the event. + /// If there are no options, just leave it as an empty list. + var/list/datum/event_admin_setup/admin_setup = list() + /// Flags dictating whether this event should be run on certain kinds of map + var/map_flags = NONE + + // monkestation start + /// The typepath to the event group this event is a part of. + var/datum/event_group/event_group = null + + /// if this can only trigger on roundstart + var/roundstart = FALSE + + /// if this event is roundstart, it will set the roll credits icon to this + var/title_icon = null + + var/cost = 1 + var/reoccurence_penalty_multiplier = 0.75 + var/shared_occurence_type + var/track = EVENT_TRACK_MODERATE + /// Last calculated weight that the storyteller assigned this event + var/calculated_weight = 0 + var/tags = list() /// Tags of the event + /// List of the shared occurence types. + var/list/shared_occurences = list() + /// Whether a roundstart event can happen post roundstart. Very important for events which override job assignments. + var/can_run_post_roundstart = TRUE + /// If set then the type or list of types of storytellers we are restricted to being trigged by + var/list/allowed_storytellers + // monkestation end /datum/round_event_control/New() if(config && !wizardevent) // Magic is unaffected by configs earliest_start = CEILING(earliest_start * CONFIG_GET(number/events_min_time_mul), 1) min_players = CEILING(min_players * CONFIG_GET(number/events_min_players_mul), 1) + if(!length(admin_setup)) + return + var/list/admin_setup_types = admin_setup.Copy() + admin_setup.Cut() + for(var/admin_setup_type in admin_setup_types) + admin_setup += new admin_setup_type(src) /datum/round_event_control/wizard + category = EVENT_CATEGORY_WIZARD wizardevent = TRUE +/// Returns true if event can run in current map +/datum/round_event_control/proc/valid_for_map() + if (!map_flags) + return TRUE + if (SSmapping.is_planetary()) + if (map_flags & EVENT_SPACE_ONLY) + return FALSE + else + if (map_flags & EVENT_PLANETARY_ONLY) + return FALSE + return TRUE + // Checks if the event can be spawned. Used by event controller and "false alarm" event. // Admin-created events override this. -/datum/round_event_control/proc/canSpawnEvent(players_amt, gamemode) +/datum/round_event_control/proc/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) + SHOULD_CALL_PARENT(TRUE) +// monkestation start: event groups and storyteller stuff + if(event_group && !GLOB.event_groups[event_group].can_run()) + return FALSE + if(roundstart && ((SSticker.round_start_time && (world.time - SSticker.round_start_time) >= 2 MINUTES) || (SSgamemode.ran_roundstart && !fake_check))) + return FALSE + // if(istype(src, /datum/round_event_control/antagonist/solo/from_ghosts) && (SSticker.round_start_time + 85 MINUTES <= world.time)) + // return TRUE // we allow all ghost roles to run at this point and dont care about other checks +// monkestation end if(occurrences >= max_occurrences) return FALSE - if(earliest_start >= world.time-SSticker.round_start_time) + if(earliest_start >= (world.time - SSticker.round_start_time)) return FALSE - if(wizardevent != SSevents.wizardmode) + if(!allow_magic && wizardevent != SSgamemode.wizardmode) return FALSE if(players_amt < min_players) return FALSE - if(gamemode_blacklist.len && (gamemode in gamemode_blacklist)) - return FALSE - if(gamemode_whitelist.len && !(gamemode in gamemode_whitelist)) + if(holidayID && !check_holidays(holidayID)) return FALSE - if(holidayID && (!SSevents.holidays || !SSevents.holidays[holidayID])) + if(EMERGENCY_ESCAPED_OR_ENDGAMED) return FALSE if(ispath(typepath, /datum/round_event/ghost_role) && !(GLOB.ghost_role_flags & GHOSTROLE_MIDROUND_EVENT)) return FALSE - if(SSsecurity_level.get_current_level_as_number() > max_alert) + +// monkestation start: storyteller stuff + if(checks_antag_cap && !roundstart && !SSgamemode.can_inject_antags()) return FALSE - if(SSsecurity_level.get_current_level_as_number() < min_alert) + if(!check_enemies()) return FALSE - - var/datum/game_mode/dynamic/dynamic = SSticker.mode - if(istype(dynamic) && dynamic_should_hijack && dynamic.random_event_hijacked != HIJACKED_NOTHING) + if(allowed_storytellers && ((islist(allowed_storytellers) && !is_type_in_list(SSgamemode.storyteller, allowed_storytellers)) || SSgamemode.storyteller.type != allowed_storytellers)) + return FALSE + if(SSgamemode.storyteller.disable_distribution || SSgamemode.halted_storyteller) return FALSE +// monkestation end - . = TRUE + return TRUE -/datum/round_event_control/proc/preRunEvent() +/datum/round_event_control/proc/preRunEvent(forced = FALSE) if(!ispath(typepath, /datum/round_event)) return EVENT_CANT_RUN - if(SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_RANDOM_EVENT, src) & CANCEL_PRE_RANDOM_EVENT) + if (SEND_GLOBAL_SIGNAL(COMSIG_GLOB_PRE_RANDOM_EVENT, src) & CANCEL_PRE_RANDOM_EVENT) return EVENT_INTERRUPTED triggering = TRUE + + // We sleep HERE, in pre-event setup (because there's no sense doing it in run_event() since the event is already running!) for the given amount of time to make an admin has enough time to cancel an event un-fitting of the present round. if(alert_observers) - message_admins(span_bold(span_red("Random Event triggering in [RANDOM_EVENT_ADMIN_INTERVENTION_TIME/10] seconds: [name] (CANCEL)"))) - sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME) - //Yogs end - var/gamemode = SSticker.mode.config_tag + message_admins("Random Event triggering in [DisplayTimeText(RANDOM_EVENT_ADMIN_INTERVENTION_TIME)]: [name]. (CANCEL)") + if(!roundstart) + sleep(RANDOM_EVENT_ADMIN_INTERVENTION_TIME) var/players_amt = get_active_player_count(alive_check = TRUE, afk_check = TRUE, human_check = TRUE) - if(!canSpawnEvent(players_amt, gamemode)) + if(!canSpawnEvent(players_amt, fake_check = TRUE) && !forced) message_admins("Second pre-condition check for [name] failed, skipping...") return EVENT_INTERRUPTED + if(!canSpawnEvent(players_amt, fake_check = TRUE) && forced) + message_admins("Second pre-condition check for [name] failed, but event forced, running event regardless this may have issues...") if(!triggering) - return EVENT_CANCELLED //admin cancelled + return EVENT_CANCELLED //admin cancelled triggering = FALSE return EVENT_READY /datum/round_event_control/Topic(href, href_list) ..() - if(!check_rights(R_ADMIN)) - return if(href_list["cancel"]) if(!triggering) - to_chat(usr, span_admin("You are too late to cancel that event.")) + to_chat(usr, span_admin("You are too late to cancel that event")) return triggering = FALSE message_admins("[key_name_admin(usr)] cancelled event [name].") log_admin_private("[key_name(usr)] cancelled event [name].") SSblackbox.record_feedback("tally", "event_admin_cancelled", 1, typepath) -/datum/round_event_control/proc/runEvent() - var/datum/round_event/E = new typepath() - E.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) - E.control = src - SSblackbox.record_feedback("tally", "event_ran", 1, "[E]") +/* +Runs the event +* Arguments: +* - random: shows if the event was triggered randomly, or by on purpose by an admin or an item +* - announce_chance_override: if the value is not null, overrides the announcement chance when an admin calls an event +*/ +/datum/round_event_control/proc/runEvent(random = FALSE, announce_chance_override = null, admin_forced = FALSE, event_cause) + /* + * We clear our signals first so we dont cancel a wanted event by accident, + * the majority of time the admin will probably want to cancel a single midround spawned random events + * and not multiple events called by others admins + * * In the worst case scenario we can still recall a event which we cancelled by accident, which is much better then to have a unwanted event + */ + UnregisterSignal(SSdcs, COMSIG_GLOB_RANDOM_EVENT) + var/datum/round_event/round_event = new typepath(TRUE, src) + if(round_event.oshan_blocked && SSmapping.config.map_name == "Oshan Station") //we'll use this whenever we have an underwater station + return + if(admin_forced && length(admin_setup)) + //not part of the signal because it's conditional and relies on usr heavily + for(var/datum/event_admin_setup/admin_setup_datum in admin_setup) + admin_setup_datum.apply_to_event(round_event) + SEND_SIGNAL(src, COMSIG_CREATED_ROUND_EVENT, round_event) + round_event.setup() + round_event.current_players = get_active_player_count(alive_check = 1, afk_check = 1, human_check = 1) occurrences++ - testing("[time2text(world.time, "hh:mm:ss")] [E.type]") - if(random) - log_game("Random Event triggering: [name] ([typepath])") + if(announce_chance_override != null) + round_event.announce_chance = announce_chance_override + + testing("[time2text(world.time, "hh:mm:ss")] [round_event.type]") + triggering = TRUE + + if(!triggering) + RegisterSignal(SSdcs, COMSIG_GLOB_RANDOM_EVENT, PROC_REF(stop_random_event)) + round_event.cancel_event = TRUE + return round_event + + triggering = FALSE + log_game("[random ? "Random" : "Forced"] Event triggering: [name] ([typepath]).") + + // monkestation start: event groups + if(event_group) + GLOB.event_groups[event_group].on_run(src) + // monkestation end + if(alert_observers) - deadchat_broadcast(" has just been[random ? " randomly" : ""] triggered!", "[name]") //STOP ASSUMING IT'S BADMINS! - return E + round_event.announce_deadchat(random, event_cause) -//Special admins setup -/datum/round_event_control/proc/admin_setup() - return + SSblackbox.record_feedback("tally", "event_ran", 1, "[name]") + return round_event + +//Returns the component for the listener +/datum/round_event_control/proc/stop_random_event() + SIGNAL_HANDLER + return CANCEL_RANDOM_EVENT -/datum/round_event //NOTE: Times are measured in master controller ticks! +/datum/round_event //NOTE: Times are measured in master controller ticks! var/processing = TRUE var/datum/round_event_control/control - var/startWhen = 0 //When in the lifetime to call start(). - var/announceWhen = 0 //When in the lifetime to call announce(). Set an event's announceWhen to -1 if announcement should not be shown. - var/endWhen = 0 //When in the lifetime the event should end. - - var/activeFor = 0 //How long the event has existed. You don't need to change this. - var/current_players = 0 //Amount of of alive, non-AFK human players on server at the time of event start - var/fakeable = TRUE //Can be faked by fake news event. + /// When in the lifetime to call start(). + /// This is in seconds - so 1 = ~2 seconds in. + var/start_when = 0 + /// When in the lifetime to call announce(). If you don't want it to announce use announce_chance, below. + /// This is in seconds - so 1 = ~2 seconds in. + var/announce_when = 0 + /// Probability of announcing, used in prob(), 0 to 100, default 100. Called in process, and for a second time in the ion storm event. + var/announce_chance = 100 + /// When in the lifetime the event should end. + /// This is in seconds - so 1 = ~2 seconds in. + var/end_when = 0 + + /// How long the event has existed. You don't need to change this. + var/activeFor = 0 + /// Amount of of alive, non-AFK human players on server at the time of event start + var/current_players = 0 + /// Can be faked by fake news event. + var/fakeable = TRUE + /// Whether a admin wants this event to be cancelled + var/cancel_event = FALSE + //monkestation vars starts + ///canceled on oshan + var/oshan_blocked = FALSE + /// Whether the event called its start() yet or not. + var/has_started = FALSE + ///have we finished setup? + var/setup = FALSE + //monkestation vars end //Called first before processing. //Allows you to setup your event, such as randomly -//setting the startWhen and or announceWhen variables. +//setting the start_when and or announce_when variables. //Only called once. //EDIT: if there's anything you want to override within the new() call, it will not be overridden by the time this proc is called. //It will only have been overridden by the time we get to announce() start() tick() or end() (anything but setup basically). //This is really only for setting defaults which can be overridden later when New() finishes. /datum/round_event/proc/setup() + SHOULD_CALL_PARENT(FALSE) + setup = TRUE return -//Called when the tick is equal to the startWhen variable. +///Annouces the event name to deadchat, override this if what an event should show to deadchat is different to its event name. +/datum/round_event/proc/announce_deadchat(random, cause) + deadchat_broadcast(" has just been[random ? " randomly" : ""] triggered[cause ? " by [cause]" : ""]!", "[control.name]", message_type=DEADCHAT_ANNOUNCEMENT) //STOP ASSUMING IT'S BADMINS! + +//Called when the tick is equal to the start_when variable. //Allows you to start before announcing or vice versa. //Only called once. /datum/round_event/proc/start() + SHOULD_CALL_PARENT(FALSE) return +//monkestation addition starts - STORYTELLERS +/// This section of event processing is in a proc because roundstart events may get their start invoked. +/datum/round_event/proc/try_start() + if(has_started) + return + has_started = TRUE + processing = FALSE + start() + processing = TRUE + +/datum/round_event_control/roundstart + roundstart = TRUE + earliest_start = 0 + +///Adds an occurence. Has to use the setter to properly handle shared occurences +/datum/round_event_control/proc/add_occurence() + if(shared_occurence_type) + if(!shared_occurences[shared_occurence_type]) + shared_occurences[shared_occurence_type] = 0 + shared_occurences[shared_occurence_type]++ + occurrences++ + +///Subtracts an occurence. Has to use the setter to properly handle shared occurences +/datum/round_event_control/proc/subtract_occurence() + if(shared_occurence_type) + if(!shared_occurences[shared_occurence_type]) + shared_occurences[shared_occurence_type] = 0 + shared_occurences[shared_occurence_type]-- + occurrences-- + +///Gets occurences. Has to use the getter to properly handle shared occurences +/datum/round_event_control/proc/get_occurences() + if(shared_occurence_type) + if(!shared_occurences[shared_occurence_type]) + shared_occurences[shared_occurence_type] = 0 + return shared_occurences[shared_occurence_type] + return occurrences + +/// Prints the action buttons for this event. +/datum/round_event_control/proc/get_href_actions() + if(SSticker.HasRoundStarted()) + if(roundstart) + if(!can_run_post_roundstart) + return "Force Now
Schedule" + return "Force Now
Schedule" + else + return "Force Now
Force Next
Schedule" + else + if(roundstart) + return "Force Roundstart
Add Roundstart" + else + return "Force Now
Force Next
Schedule" + + +/datum/round_event_control/Topic(href, href_list) + . = ..() + if(QDELETED(src)) + return + switch(href_list["action"]) + if("schedule") + message_admins("[key_name_admin(usr)] scheduled event [src.name].") + log_admin_private("[key_name(usr)] scheduled [src.name].") + SSgamemode.storyteller.buy_event(src, src.track) + if("force_next") + if(length(src.admin_setup)) + for(var/datum/event_admin_setup/admin_setup_datum in src.admin_setup) + if(admin_setup_datum.prompt_admins() == ADMIN_CANCEL_EVENT) + return + message_admins("[key_name_admin(usr)] forced scheduled event [src.name].") + log_admin_private("[key_name(usr)] forced scheduled event [src.name].") + SSgamemode.forced_next_events[src.track] = src + if("force_now") + if(length(src.admin_setup)) + for(var/datum/event_admin_setup/admin_setup_datum in src.admin_setup) + if(admin_setup_datum.prompt_admins() == ADMIN_CANCEL_EVENT) + return + message_admins("[key_name_admin(usr)] forced event [src.name].") + log_admin_private("[key_name(usr)] forced event [src.name].") + runEvent(random = FALSE, admin_forced = TRUE) + +//monkestation addition ends - STORYTELLERS + //Called after something followable has been spawned by an event //Provides ghosts a follow link to an atom if possible //Only called once. /datum/round_event/proc/announce_to_ghosts(atom/atom_of_interest) if(control.alert_observers) - if(atom_of_interest) - //Yogs start -- Makes this a bit more specific - var/typeofthing = "object" - if(iscarbon(atom_of_interest)) - typeofthing = "person" - else if(ismob(atom_of_interest)) - typeofthing = "mob" - else if(isturf(atom_of_interest)) - typeofthing = "place" - //Yogs end - notify_ghosts("[control.name] has \a [typeofthing] of interest: [atom_of_interest]!", source=atom_of_interest, action=NOTIFY_ORBIT, header="Something's Interesting!") + if (atom_of_interest) + notify_ghosts( + "[control.name] has an object of interest: [atom_of_interest]!", + source = atom_of_interest, + action = NOTIFY_ORBIT, + header = "Something's Interesting!", + ) return -//Called when the tick is equal to the announceWhen variable. +//Called when the tick is equal to the announce_when variable. //Allows you to announce before starting or vice versa. //Only called once. /datum/round_event/proc/announce(fake) return -//Called on or after the tick counter is equal to startWhen. +//Called on or after the tick counter is equal to start_when. //You can include code related to your event or add your own //time stamped events. //Called more than once. /datum/round_event/proc/tick() return -//Called on or after the tick is equal or more than endWhen +//Called on or after the tick is equal or more than end_when //You can include code related to the event ending. //Do not place spawn() in here, instead use tick() to check for //the activeFor variable. @@ -207,31 +403,38 @@ //This proc will handle the calls to the appropiate procs. /datum/round_event/process() SHOULD_NOT_OVERRIDE(TRUE) + if(!setup) + return if(!processing) return - if(activeFor == startWhen) + if(SEND_GLOBAL_SIGNAL(COMSIG_GLOB_RANDOM_EVENT, src) & CANCEL_RANDOM_EVENT) + processing = FALSE + kill() + return + + if(activeFor == start_when) processing = FALSE start() processing = TRUE - if(activeFor == announceWhen) + if(activeFor == announce_when && prob(announce_chance)) processing = FALSE announce(FALSE) processing = TRUE - if(startWhen < activeFor && activeFor < endWhen) + if(start_when < activeFor && activeFor < end_when) processing = FALSE tick() processing = TRUE - if(activeFor == endWhen) + if(activeFor == end_when) processing = FALSE end() processing = TRUE // Everything is done, let's clean up. - if(activeFor >= endWhen && activeFor >= announceWhen && activeFor >= startWhen) + if(activeFor >= end_when && activeFor >= announce_when && activeFor >= start_when) processing = FALSE kill() @@ -242,14 +445,14 @@ //which should be the only place it's referenced. //Called when start(), announce() and end() has all been called. /datum/round_event/proc/kill() - SSevents.running -= src + SSgamemode.running -= src //Sets up the event then adds the event to the the list of running events -/datum/round_event/New(my_processing = TRUE) - setup() +/datum/round_event/New(my_processing = TRUE, datum/round_event_control/event_controller) + control = event_controller processing = my_processing - SSevents.running += src + SSgamemode.running += src return ..() #undef RANDOM_EVENT_ADMIN_INTERVENTION_TIME diff --git a/code/modules/events/_event_admin_setup.dm b/code/modules/events/_event_admin_setup.dm new file mode 100644 index 000000000000..d4774d75869d --- /dev/null +++ b/code/modules/events/_event_admin_setup.dm @@ -0,0 +1,181 @@ +/// Datum that holds a proc for additional options when running an event. +/// Prototypes are declared here, non-prototypes on the event files. +/datum/event_admin_setup + /// event control that owns this. + var/datum/round_event_control/event_control + +/datum/event_admin_setup/New(event_control) + src.event_control = event_control + +/datum/event_admin_setup/proc/prompt_admins() + SHOULD_CALL_PARENT(FALSE) + CRASH("Unimplemented prompt_admins() on [event_control]'s admin setup.") + +/datum/event_admin_setup/proc/apply_to_event(datum/round_event/event) + SHOULD_CALL_PARENT(FALSE) + CRASH("Unimplemented apply_to_event() on [event_control]'s admin setup.") + +/// A very common pattern is picking from a tgui list input, so this does that. +/// Supply a list in `get_list` and prompt admins will have the admin pick from it or cancel. +/datum/event_admin_setup/listed_options + /// Text to ask the user, for example "What deal would you like to offer the crew?" + var/input_text = "Unset Text" + /// If set, picking this will be the same as running the event without admin setup. + var/normal_run_option + /// if you want a special button, this will add it. Remember to actually handle that case for chosen in `apply_to_event` + /// Example is in scrubber_overflow.dm + var/special_run_option + /// Picked list option to be applied. + var/chosen + +/datum/event_admin_setup/listed_options/proc/get_list() + SHOULD_CALL_PARENT(FALSE) + CRASH("Unimplemented get_list() on [event_control]'s admin setup.") + +/datum/event_admin_setup/listed_options/prompt_admins() + var/list/options = get_list() + if(special_run_option) + options.Insert(1, special_run_option) + if(normal_run_option) + options.Insert(1, normal_run_option) + chosen = tgui_input_list(usr, input_text, event_control.name, options) + if(!chosen) + return ADMIN_CANCEL_EVENT + if(normal_run_option && chosen == normal_run_option) + chosen = null //no admin pick = runs as normal + +/// For admin setups that want a custom string. Suggests what the event would have picked normally. +/datum/event_admin_setup/text_input + /// Text to ask the user, for example "What horrifying truth will you reveal?" + var/input_text = "Unset Text" + /// Picked string to be applied. + var/chosen + +/// Returns a string to suggest to the admin, which would be what the event would have chosen. +/// No suggestion if an empty string, which is default behavior. +/datum/event_admin_setup/text_input/proc/get_text_suggestion() + return "" + +/datum/event_admin_setup/text_input/prompt_admins() + var/suggestion = get_text_suggestion() + chosen = tgui_input_text(usr, input_text, event_control.name, suggestion) + if(!chosen) + return ADMIN_CANCEL_EVENT + +/// Some events are not always a good idea when a game state is in a certain situation. +/// This runs a check and warns the admin. +/datum/event_admin_setup/warn_admin + /// Warning text shown to admin on the alert. + var/warning_text = "Unset warning text" + /// Message sent to other admins. Example: "has forced a shuttle catastrophe while a shuttle was already docked." + var/snitch_text = "Unset snitching text (be mad at coders AND the admin responsible)" + +/datum/event_admin_setup/warn_admin/prompt_admins() + if(!should_warn()) + return + var/mob/admin = usr + if(tgui_alert(usr, "WARNING: [warning_text]", event_control.name, list("Yes", "No")) == "Yes") + if(snitch_text) + message_admins("[admin.ckey] [snitch_text]") + else + return ADMIN_CANCEL_EVENT + +/// Returns whether the admin should get an alert. +/datum/event_admin_setup/warn_admin/proc/should_warn() + SHOULD_CALL_PARENT(FALSE) + CRASH("Unimplemented should_warn() on [event_control]'s admin setup.") + +/datum/event_admin_setup/warn_admin/apply_to_event(datum/round_event/event) + return + +/datum/event_admin_setup/set_location + ///Text shown when admins are queried about setting the target location. + var/input_text = "Aimed at the turf we're on?" + ///Turf that will be passed onto the event. + var/atom/chosen_turf + +/datum/event_admin_setup/set_location/prompt_admins() + var/set_location = tgui_alert(usr, input_text, event_control.name, list("Yes", "No", "Cancel")) + switch(set_location) + if("Yes") + chosen_turf = get_turf(usr) + if("No") + chosen_turf = null + else + return ADMIN_CANCEL_EVENT + +/datum/event_admin_setup/input_number + ///Text shown when admins are queried about what number to set. + var/input_text = "Unset text" + ///The value the number will be set to by default + var/default_value + ///The highest value setable by the admin. + var/max_value = 10000 + ///The lowest value setable by the admin + var/min_value = 0 + ///Value selected by the admin + var/chosen_value + +/datum/event_admin_setup/input_number/prompt_admins() + chosen_value = tgui_input_number(usr, input_text, event_control.name, default_value, max_value, min_value) + if(isnull(chosen_value)) + return ADMIN_CANCEL_EVENT + +///For events that mandate a set number of candidates to function +/datum/event_admin_setup/minimum_candidate_requirement + ///Text shown when there are not enough candidates + var/output_text = "There are no candidates eligible to..." + ///Minimum number of candidates for the event to function + var/min_candidates = 1 + +/datum/event_admin_setup/minimum_candidate_requirement/prompt_admins() + var/candidate_count = count_candidates() + if(candidate_count < min_candidates) + tgui_alert(usr, output_text, "Error") + return ADMIN_CANCEL_EVENT + tgui_alert(usr, "[candidate_count] candidates found!", event_control.name) + +/// Checks for candidates. Should return the total number of candidates +/datum/event_admin_setup/minimum_candidate_requirement/proc/count_candidates() + SHOULD_CALL_PARENT(FALSE) + CRASH("Unimplemented count_candidates() on [event_control]'s admin setup.") + +/datum/event_admin_setup/minimum_candidate_requirement/apply_to_event(datum/round_event/event) + return + +///For events that require a true/false question +/datum/event_admin_setup/question + ///Question shown to the admin. + var/input_text = "Are you sure you would like to do this?" + ///Value passed to the event. + var/chosen + +/datum/event_admin_setup/question/prompt_admins() + var/response = tgui_alert(usr, input_text , event_control.name , list("Yes", "No", "Cancel")) + switch(response) + if("Yes") + chosen = TRUE + if("No") + chosen = FALSE + else + return ADMIN_CANCEL_EVENT + +/datum/event_admin_setup/multiple_choice + ///Text shown to the admin when queried about which options they want to pick. + var/input_text = "Unset Text" + ///The minimum number of choices an admin must make for this event. + var/min_choices = 1 + ///The maximum number of choices that the admin can make for this event. + var/max_choices = 50 + ///List of choices returned by this setup to the event. + var/list/choices = list() + +/datum/event_admin_setup/multiple_choice/proc/get_options() + SHOULD_CALL_PARENT(FALSE) + CRASH("Unimplemented get_options() on [event_control]'s admin setup.") + +/datum/event_admin_setup/multiple_choice/prompt_admins() + var/list/options = get_options() + choices = tgui_input_checkboxes(usr, input_text, event_control.name, options, min_choices, max_choices) + if(isnull(choices)) + return ADMIN_CANCEL_EVENT diff --git a/code/modules/events/anomaly.dm b/code/modules/events/anomaly.dm deleted file mode 100644 index abb1aaca83a6..000000000000 --- a/code/modules/events/anomaly.dm +++ /dev/null @@ -1,49 +0,0 @@ -/datum/round_event_control/anomaly - name = "Anomaly: Energetic Flux" - typepath = /datum/round_event/anomaly - - min_players = 1 - max_occurrences = 0 //This one probably shouldn't occur! It'd work, but it wouldn't be very fun. - weight = 15 - -/datum/round_event/anomaly - var/area/impact_area - var/obj/effect/anomaly/anomaly_path = /obj/effect/anomaly/flux - announceWhen = 1 - - -/datum/round_event/anomaly/proc/findEventArea() - var/static/list/allowed_areas - if(!allowed_areas) - //Places that shouldn't explode - var/list/safe_area_types = typecacheof(ANOMALY_AREA_BLACKLIST) - - //Subtypes from the above that actually should explode. - var/list/unsafe_area_subtypes = typecacheof(ANOMALY_AREA_SUBTYPE_WHITELIST) - - allowed_areas = make_associative(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes - - return pick(typecache_filter_list(GLOB.areas,allowed_areas)) - -/datum/round_event/anomaly/setup() - impact_area = findEventArea() - if(!impact_area) - CRASH("No valid areas for anomaly found.") - var/list/turf_test = get_area_turfs(impact_area) - if(!turf_test.len) - CRASH("Anomaly : No valid turfs found for [impact_area] - [impact_area.type]") - -/datum/round_event/anomaly/announce(fake) - priority_announce("Localized energetic flux wave detected on long range scanners. Expected location of impact: [impact_area.name].", "Anomaly Alert") - -/datum/round_event/anomaly/start() - var/turf/T = pick(get_area_turfs(impact_area)) - var/max_rolls=0 //In case all the turfs in the area are walls, will break out of rolling for a new turf forever and will just spawn the anomaly inside a wall - while(is_anchored_dense_turf(T) && max_rolls<15) //Will roll for a new turf if the selected turf is a wall until it's not a wall - T = safepick(get_area_turfs(impact_area)) - max_rolls++ - var/newAnomaly - if(T) - newAnomaly = new anomaly_path(T) - if (newAnomaly) - announce_to_ghosts(newAnomaly) diff --git a/code/modules/events/anomaly/anomaly.dm b/code/modules/events/anomaly/anomaly.dm new file mode 100644 index 000000000000..988a66137164 --- /dev/null +++ b/code/modules/events/anomaly/anomaly.dm @@ -0,0 +1,59 @@ +/datum/round_event_control/anomaly + name = "Anomaly: Energetic Flux" + typepath = /datum/round_event/anomaly + + min_players = 1 + max_occurrences = 0 //This one probably shouldn't occur! It'd work, but it wouldn't be very fun. + weight = 15 + category = EVENT_CATEGORY_ANOMALIES + description = "This anomaly shocks and explodes. This is the base type." + admin_setup = list(/datum/event_admin_setup/set_location/anomaly) + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_MAGICAL) + event_group = /datum/event_group/anomalies + +/datum/round_event/anomaly + start_when = ANOMALY_START_HARMFUL_TIME + announce_when = ANOMALY_ANNOUNCE_HARMFUL_TIME + var/area/impact_area + var/datum/anomaly_placer/placer = new() + var/obj/effect/anomaly/anomaly_path = /obj/effect/anomaly/flux + ///The admin-chosen spawn location. + var/turf/spawn_location + +/datum/round_event/anomaly/setup() + + if(spawn_location) + impact_area = get_area(spawn_location) + else + impact_area = placer.findValidArea() + setup = TRUE //MONKESTATION ADDITION + +/datum/round_event/anomaly/announce(fake) + priority_announce("Energetic flux wave detected on [ANOMALY_ANNOUNCE_DANGEROUS_TEXT] [impact_area.name].", "Anomaly Alert") + +/datum/round_event/anomaly/start() + var/turf/anomaly_turf + + if(spawn_location) + anomaly_turf = spawn_location + else + anomaly_turf = placer.findValidTurf(impact_area) + + var/newAnomaly + if(anomaly_turf) + newAnomaly = new anomaly_path(anomaly_turf) + if (newAnomaly) + apply_anomaly_properties(newAnomaly) + announce_to_ghosts(newAnomaly) + +/// Make any further post-creation modifications to the anomaly +/datum/round_event/anomaly/proc/apply_anomaly_properties(obj/effect/anomaly/new_anomaly) + return + +/datum/event_admin_setup/set_location/anomaly + input_text = "Spawn anomaly at your current location?" + +/datum/event_admin_setup/set_location/anomaly/apply_to_event(datum/round_event/anomaly/event) + event.spawn_location = chosen_turf + diff --git a/code/modules/events/anomaly/anomaly_bluespace.dm b/code/modules/events/anomaly/anomaly_bluespace.dm new file mode 100644 index 000000000000..aa3bbf76d63e --- /dev/null +++ b/code/modules/events/anomaly/anomaly_bluespace.dm @@ -0,0 +1,17 @@ +/datum/round_event_control/anomaly/anomaly_bluespace + name = "Anomaly: Bluespace" + typepath = /datum/round_event/anomaly/anomaly_bluespace + + max_occurrences = 1 + weight = 15 + description = "This anomaly randomly teleports all items and mobs in a large area." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 2 + +/datum/round_event/anomaly/anomaly_bluespace + start_when = ANOMALY_START_MEDIUM_TIME + announce_when = ANOMALY_ANNOUNCE_MEDIUM_TIME + anomaly_path = /obj/effect/anomaly/bluespace + +/datum/round_event/anomaly/anomaly_bluespace/announce(fake) + priority_announce("Bluespace instability detected on [ANOMALY_ANNOUNCE_MEDIUM_TEXT] [impact_area.name].", "Anomaly Alert") diff --git a/code/modules/events/anomaly/anomaly_flux.dm b/code/modules/events/anomaly/anomaly_flux.dm new file mode 100644 index 000000000000..18d84d749bf5 --- /dev/null +++ b/code/modules/events/anomaly/anomaly_flux.dm @@ -0,0 +1,19 @@ +/datum/round_event_control/anomaly/anomaly_flux + name = "Anomaly: Hyper-Energetic Flux" + typepath = /datum/round_event/anomaly/anomaly_flux + + min_players = 10 + max_occurrences = 5 + weight = 20 + description = "This anomaly shocks and explodes." + min_wizard_trigger_potency = 1 + max_wizard_trigger_potency = 4 + +/datum/round_event/anomaly/anomaly_flux + start_when = ANOMALY_START_DANGEROUS_TIME + announce_when = ANOMALY_ANNOUNCE_DANGEROUS_TIME + anomaly_path = /obj/effect/anomaly/flux + oshan_blocked = TRUE + +/datum/round_event/anomaly/anomaly_flux/announce(fake) + priority_announce("Hyper-energetic flux wave detected on [ANOMALY_ANNOUNCE_DANGEROUS_TEXT]. [impact_area.name].", "Anomaly Alert") diff --git a/code/modules/events/anomaly_grav.dm b/code/modules/events/anomaly/anomaly_grav.dm similarity index 50% rename from code/modules/events/anomaly_grav.dm rename to code/modules/events/anomaly/anomaly_grav.dm index 3be3698feddc..109678ae01dd 100644 --- a/code/modules/events/anomaly_grav.dm +++ b/code/modules/events/anomaly/anomaly_grav.dm @@ -3,12 +3,12 @@ typepath = /datum/round_event/anomaly/anomaly_grav max_occurrences = 5 - weight = 20 + weight = 25 + description = "This anomaly throws things around." + min_wizard_trigger_potency = 1 + max_wizard_trigger_potency = 3 /datum/round_event/anomaly/anomaly_grav - startWhen = 3 - announceWhen = 20 + start_when = ANOMALY_START_HARMFUL_TIME + announce_when = ANOMALY_ANNOUNCE_HARMFUL_TIME anomaly_path = /obj/effect/anomaly/grav - -/datum/round_event/anomaly/anomaly_grav/announce(fake) - priority_announce("Gravitational anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") \ No newline at end of file diff --git a/code/modules/events/anomaly_hallucination.dm b/code/modules/events/anomaly/anomaly_hallucination.dm similarity index 53% rename from code/modules/events/anomaly_hallucination.dm rename to code/modules/events/anomaly/anomaly_hallucination.dm index 0f3628c7e0b8..557d511d305e 100644 --- a/code/modules/events/anomaly_hallucination.dm +++ b/code/modules/events/anomaly/anomaly_hallucination.dm @@ -5,11 +5,14 @@ min_players = 10 max_occurrences = 5 weight = 20 + description = "This anomaly causes you to hallucinate." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 2 /datum/round_event/anomaly/anomaly_hallucination - startWhen = 10 - announceWhen = 3 + start_when = ANOMALY_START_MEDIUM_TIME + announce_when = ANOMALY_ANNOUNCE_MEDIUM_TIME anomaly_path = /obj/effect/anomaly/hallucination /datum/round_event/anomaly/anomaly_hallucination/announce(fake) - priority_announce("Hallucinatory event hitting the station. Expected location: [impact_area.name].", "Anomaly Alert") + priority_announce("Hallucinatory event [ANOMALY_ANNOUNCE_MEDIUM_TEXT] [impact_area.name].", "Anomaly Alert") diff --git a/code/modules/events/anomaly/anomaly_placer.dm b/code/modules/events/anomaly/anomaly_placer.dm new file mode 100644 index 000000000000..0110fb076b02 --- /dev/null +++ b/code/modules/events/anomaly/anomaly_placer.dm @@ -0,0 +1,66 @@ +/datum/anomaly_placer + var/static/list/allowed_areas + +/** + * Returns an area which is safe to place an anomaly inside. + */ +/datum/anomaly_placer/proc/findValidArea() + if(!allowed_areas) + generateAllowedAreas() + var/list/possible_areas = typecache_filter_list(GLOB.areas, allowed_areas) + if (!length(possible_areas)) + CRASH("No valid areas for anomaly found.") + + var/area/landing_area = pick(possible_areas) + var/list/turf_test = get_area_turfs(landing_area) + if(!turf_test.len) + CRASH("Anomaly : No valid turfs found for [landing_area] - [landing_area.type]") + + return landing_area + +/** + * Returns a turf which is safe to place an anomaly on. + * + * Arguments + * * target_area - Area to return a turf from. + */ +/datum/anomaly_placer/proc/findValidTurf(area/target_area) + var/list/valid_turfs = list() + for (var/turf/try_turf as anything in get_area_turfs(target_area)) + if (!is_valid_destination(try_turf)) + continue + valid_turfs += try_turf + + if (!valid_turfs.len) + CRASH("Dimensional anomaly attempted to reach invalid location [target_area].") + + return pick(valid_turfs) + +/** + * Returns true if the provided turf is valid to place an anomaly on. + * + * Arguments + * * tested - Turf to try landing on. + */ +/datum/anomaly_placer/proc/is_valid_destination(turf/tested) + if (isspaceturf(tested)) + return FALSE + if (tested.is_blocked_turf(exclude_mobs = TRUE)) + return FALSE + if (islava(tested)) + return FALSE + if (ischasm(tested)) + return FALSE + return TRUE + +/** + * Populates the allowed areas list. + */ +/datum/anomaly_placer/proc/generateAllowedAreas() + //Places that shouldn't explode + var/static/list/safe_area_types = typecacheof(ANOMALY_AREA_BLACKLIST) + + //Subtypes from the above that actually should explode. + var/static/list/unsafe_area_subtypes = typecacheof(ANOMALY_AREA_SUBTYPE_WHITELIST) + + allowed_areas = make_associative(GLOB.the_station_areas) - safe_area_types + unsafe_area_subtypes diff --git a/code/modules/events/anomaly/anomaly_pyro.dm b/code/modules/events/anomaly/anomaly_pyro.dm new file mode 100644 index 000000000000..d88e022d93fa --- /dev/null +++ b/code/modules/events/anomaly/anomaly_pyro.dm @@ -0,0 +1,17 @@ +/datum/round_event_control/anomaly/anomaly_pyro + name = "Anomaly: Pyroclastic" + typepath = /datum/round_event/anomaly/anomaly_pyro + + max_occurrences = 5 + weight = 20 + description = "This anomaly sets things on fire, and creates a pyroclastic slime." + min_wizard_trigger_potency = 1 + max_wizard_trigger_potency = 4 + +/datum/round_event/anomaly/anomaly_pyro + start_when = ANOMALY_START_HARMFUL_TIME + announce_when = ANOMALY_ANNOUNCE_HARMFUL_TIME + anomaly_path = /obj/effect/anomaly/pyro + +/datum/round_event/anomaly/anomaly_pyro/announce(fake) + priority_announce("Pyroclastic anomaly detected on [ANOMALY_ANNOUNCE_HARMFUL_TEXT] [impact_area.name].", "Anomaly Alert") diff --git a/code/modules/events/anomaly_radiation.dm b/code/modules/events/anomaly/anomaly_radiation.dm similarity index 74% rename from code/modules/events/anomaly_radiation.dm rename to code/modules/events/anomaly/anomaly_radiation.dm index e5d631b17b98..7a06501c1140 100644 --- a/code/modules/events/anomaly_radiation.dm +++ b/code/modules/events/anomaly/anomaly_radiation.dm @@ -4,10 +4,13 @@ max_occurrences = 7 weight = 20 + description = "This anomaly irradiates everything within range." + min_wizard_trigger_potency = 1 + max_wizard_trigger_potency = 4 /datum/round_event/anomaly/anomaly_radiation - startWhen = 3 - announceWhen = 10 + start_when = ANOMALY_START_HARMFUL_TIME + announce_when = ANOMALY_ANNOUNCE_HARMFUL_TIME anomaly_path = /obj/effect/anomaly/radiation /datum/round_event/anomaly/anomaly_radiation/announce(fake) diff --git a/code/modules/events/anomaly_vortex.dm b/code/modules/events/anomaly/anomaly_vortex.dm similarity index 53% rename from code/modules/events/anomaly_vortex.dm rename to code/modules/events/anomaly/anomaly_vortex.dm index 616263f33ef1..81611eb612ca 100644 --- a/code/modules/events/anomaly_vortex.dm +++ b/code/modules/events/anomaly/anomaly_vortex.dm @@ -4,12 +4,16 @@ min_players = 20 max_occurrences = 2 - weight = 5 + weight = 4 + description = "This anomaly sucks in and detonates items." + min_wizard_trigger_potency = 3 + max_wizard_trigger_potency = 7 /datum/round_event/anomaly/anomaly_vortex - startWhen = 10 - announceWhen = 3 + start_when = ANOMALY_START_DANGEROUS_TIME + announce_when = ANOMALY_ANNOUNCE_DANGEROUS_TIME anomaly_path = /obj/effect/anomaly/bhole + oshan_blocked = TRUE /datum/round_event/anomaly/anomaly_vortex/announce(fake) - priority_announce("Localized high-intensity vortex anomaly detected on long range scanners. Expected location: [impact_area.name]", "Anomaly Alert") \ No newline at end of file + priority_announce("Localized high-intensity vortex anomaly detected on [ANOMALY_ANNOUNCE_DANGEROUS_TEXT] [impact_area.name]", "Anomaly Alert") diff --git a/code/modules/events/anomaly_bluespace.dm b/code/modules/events/anomaly_bluespace.dm deleted file mode 100644 index c3300fbb3d13..000000000000 --- a/code/modules/events/anomaly_bluespace.dm +++ /dev/null @@ -1,14 +0,0 @@ -/datum/round_event_control/anomaly/anomaly_bluespace - name = "Anomaly: Bluespace" - typepath = /datum/round_event/anomaly/anomaly_bluespace - - max_occurrences = 1 - weight = 5 - -/datum/round_event/anomaly/anomaly_bluespace - startWhen = 3 - announceWhen = 10 - anomaly_path = /obj/effect/anomaly/bluespace - -/datum/round_event/anomaly/anomaly_bluespace/announce(fake) - priority_announce("Unstable bluespace anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") \ No newline at end of file diff --git a/code/modules/events/anomaly_flux.dm b/code/modules/events/anomaly_flux.dm deleted file mode 100644 index 4ecd8a57e6d6..000000000000 --- a/code/modules/events/anomaly_flux.dm +++ /dev/null @@ -1,15 +0,0 @@ -/datum/round_event_control/anomaly/anomaly_flux - name = "Anomaly: Hyper-Energetic Flux" - typepath = /datum/round_event/anomaly/anomaly_flux - - min_players = 10 - max_occurrences = 5 - weight = 20 - -/datum/round_event/anomaly/anomaly_flux - startWhen = 10 - announceWhen = 3 - anomaly_path = /obj/effect/anomaly/flux/explosion - -/datum/round_event/anomaly/anomaly_flux/announce(fake) - priority_announce("Localized hyper-energetic flux wave detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") diff --git a/code/modules/events/anomaly_pyro.dm b/code/modules/events/anomaly_pyro.dm deleted file mode 100644 index 0606e176b5fe..000000000000 --- a/code/modules/events/anomaly_pyro.dm +++ /dev/null @@ -1,14 +0,0 @@ -/datum/round_event_control/anomaly/anomaly_pyro - name = "Anomaly: Pyroclastic" - typepath = /datum/round_event/anomaly/anomaly_pyro - - max_occurrences = 5 - weight = 20 - -/datum/round_event/anomaly/anomaly_pyro - startWhen = 3 - announceWhen = 10 - anomaly_path = /obj/effect/anomaly/pyro - -/datum/round_event/anomaly/anomaly_pyro/announce(fake) - priority_announce("Pyroclastic anomaly detected on long range scanners. Expected location: [impact_area.name].", "Anomaly Alert") diff --git a/code/modules/events/aurora_caelus.dm b/code/modules/events/aurora_caelus.dm index bac40545f858..e44e70a76584 100644 --- a/code/modules/events/aurora_caelus.dm +++ b/code/modules/events/aurora_caelus.dm @@ -4,17 +4,20 @@ max_occurrences = 1 weight = 1 earliest_start = 5 MINUTES - max_alert = SEC_LEVEL_RED + category = EVENT_CATEGORY_FRIENDLY + description = "A colourful display can be seen through select windows. And the kitchen." + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL, TAG_POSITIVE, TAG_SPACE) -/datum/round_event_control/aurora_caelus/canSpawnEvent(players, gamemode) +/datum/round_event_control/aurora_caelus/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) if(!SSmapping.empty_space) return FALSE return ..() /datum/round_event/aurora_caelus - announceWhen = 1 - startWhen = 21 - endWhen = 80 + announce_when = 1 + start_when = 21 + end_when = 80 /datum/round_event/aurora_caelus/announce() priority_announce("[station_name()]: A harmless cloud of ions is approaching your station, and will exhaust their energy battering the hull. Nanotrasen has approved a short break for all employees to relax and observe this very rare event. During this time, starlight will be bright but gentle, shifting between quiet green and blue colors. Any staff who would like to view these lights for themselves may proceed to the area nearest to them with viewing ports to open space. We hope you enjoy the lights.", @@ -47,7 +50,7 @@ /datum/round_event/aurora_caelus/tick() if(activeFor % 8 != 0) return - var/aurora_color = hsl_gradient((activeFor - startWhen) / (endWhen - startWhen), 0, "#A2FF80", 1, "#A2FFEE") + var/aurora_color = hsl_gradient((activeFor - start_when) / (end_when - start_when), 0, "#A2FF80", 1, "#A2FFEE") set_starlight(aurora_color) for(var/area/crew_quarters/kitchen/affected_area in GLOB.areas) diff --git a/code/modules/events/brain_trauma.dm b/code/modules/events/brain_trauma.dm index d6f9ebdf4c9d..af30ddb4c10d 100644 --- a/code/modules/events/brain_trauma.dm +++ b/code/modules/events/brain_trauma.dm @@ -2,6 +2,13 @@ name = "Spontaneous Brain Trauma" typepath = /datum/round_event/brain_trauma weight = 25 + category = EVENT_CATEGORY_HEALTH + description = "A crewmember gains a random trauma." + min_wizard_trigger_potency = 2 + max_wizard_trigger_potency = 6 + min_players = 15 // To make your chance of getting help a bit higher, since there's a chance you just get quad paralyze + track = EVENT_TRACK_MUNDANE + tags = list(TAG_TARGETED, TAG_MAGICAL) //magical because it can give magical traumas /datum/round_event/brain_trauma fakeable = FALSE diff --git a/code/modules/events/brand_intelligence.dm b/code/modules/events/brand_intelligence.dm index b44c04e8e71c..33a9ac8a5c76 100644 --- a/code/modules/events/brand_intelligence.dm +++ b/code/modules/events/brand_intelligence.dm @@ -3,12 +3,18 @@ typepath = /datum/round_event/brand_intelligence weight = 5 + category = EVENT_CATEGORY_AI + min_wizard_trigger_potency = 2 + max_wizard_trigger_potency = 6 min_players = 15 max_occurrences = 1 + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_COMMUNAL) + event_group = /datum/event_group/bsod /datum/round_event/brand_intelligence - announceWhen = 21 - endWhen = 1000 //Ends when all vending machines are subverted anyway. + announce_when = 21 + end_when = 1000 //Ends when all vending machines are subverted anyway. var/list/obj/machinery/vending/vendingMachines = list() var/list/obj/machinery/vending/infectedMachines = list() var/obj/machinery/vending/originMachine diff --git a/code/modules/events/bureaucratic_error.dm b/code/modules/events/bureaucratic_error.dm index 64919c6818b8..a45c3f89dd10 100644 --- a/code/modules/events/bureaucratic_error.dm +++ b/code/modules/events/bureaucratic_error.dm @@ -2,11 +2,15 @@ name = "Bureaucratic Error" typepath = /datum/round_event/bureaucratic_error max_occurrences = 1 - weight = 5 - max_alert = SEC_LEVEL_DELTA + weight = 2 + category = EVENT_CATEGORY_BUREAUCRATIC + description = "Randomly changes the overflow role." + track = EVENT_TRACK_MAJOR // if you've ever dealt with 10 mimes you understand why. + tags = list(TAG_COMMUNAL) + event_group = /datum/event_group/error /datum/round_event/bureaucratic_error - announceWhen = 1 + announce_when = 1 /datum/round_event/bureaucratic_error/announce(fake) priority_announce("A recent bureaucratic error in the Organic Resources Department may result in personnel shortages in some departments and redundant staffing in others.", "Paperwork Mishap Alert") diff --git a/code/modules/events/camerafailure.dm b/code/modules/events/camerafailure.dm index 8d7ef3204ce9..7459a575dc05 100644 --- a/code/modules/events/camerafailure.dm +++ b/code/modules/events/camerafailure.dm @@ -4,6 +4,10 @@ weight = 100 max_occurrences = 20 alert_observers = FALSE + category = EVENT_CATEGORY_ENGINEERING + description = "Turns off a random amount of cameras." + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL, TAG_SPOOKY) /datum/round_event/camera_failure fakeable = FALSE diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm index 4f30b61dd34f..ec49036fc4e2 100644 --- a/code/modules/events/carp_migration.dm +++ b/code/modules/events/carp_migration.dm @@ -2,10 +2,16 @@ name = "Carp Migration" typepath = /datum/round_event/carp_migration weight = 15 - min_players = 2 + min_players = 10 earliest_start = 10 MINUTES - max_occurrences = 6 - max_alert = SEC_LEVEL_DELTA + max_occurrences = 2 + category = EVENT_CATEGORY_ENTITIES + description = "Summons a school of space carp." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 3 + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_SPACE, TAG_EXTERNAL, TAG_ALIEN) + event_group = /datum/event_group/guests /datum/round_event_control/carp_migration/New() . = ..() @@ -16,29 +22,33 @@ earliest_start *= 0.5 /datum/round_event/carp_migration - announceWhen = 3 - startWhen = 50 + announce_when = 3 + start_when = 50 + var/carp_type = /mob/living/simple_animal/hostile/carp + var/boss_type = /mob/living/simple_animal/hostile/carp/megacarp var/hasAnnounced = FALSE /datum/round_event/carp_migration/setup() - startWhen = rand(40, 60) + start_when = rand(40, 60) + setup = TRUE /datum/round_event/carp_migration/announce(fake) priority_announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert") - /datum/round_event/carp_migration/start() - var/mob/living/simple_animal/hostile/carp/fish + var/mob/living/simple_animal/fish for(var/obj/effect/landmark/carpspawn/C in GLOB.landmarks_list) + //yogs -- Gondola Day for some goddamn reason. Can't they just let go of Oak? - if(SSevents.holidays && SSevents.holidays["Gondola Day"]) - fish = new /mob/living/simple_animal/pet/gondola - return + if(prob(20) && SSgamemode.holidays && SSgamemode.holidays["Gondola Day"]) + fish = new /mob/living/simple_animal/pet/gondola(C.loc) + continue //yogs end + if(prob(95)) - fish = new (C.loc) + fish = new carp_type(C.loc) else - fish = new /mob/living/simple_animal/hostile/carp/megacarp(C.loc) + fish = new boss_type(C.loc) fishannounce(fish) //Prefer to announce the megacarps over the regular fishies fishannounce(fish) @@ -46,3 +56,20 @@ if (!hasAnnounced) announce_to_ghosts(fish) //Only anounce the first fish hasAnnounced = TRUE + +/**---------------------------------------------------------------- + * + * Dolphins + * + */ +/datum/round_event_control/carp_migration/dolphin + name = "Dolphin Migration" + typepath = /datum/round_event/carp_migration/dolphin + max_occurrences = 6 + description = "Summons a school of dolphin." + max_wizard_trigger_potency = 2 + tags = list(TAG_SPACE, TAG_EXTERNAL, TAG_POSITIVE) + +/datum/round_event/carp_migration/dolphin + carp_type = /mob/living/simple_animal/hostile/retaliate/dolphin + boss_type = /mob/living/simple_animal/hostile/retaliate/dolphin/manatee diff --git a/code/modules/events/chem_spill.dm b/code/modules/events/chem_spill.dm index 86b2b44b44c4..9817813637c6 100644 --- a/code/modules/events/chem_spill.dm +++ b/code/modules/events/chem_spill.dm @@ -4,32 +4,33 @@ weight = 20 max_occurrences = 3 min_players = 10 - max_alert = SEC_LEVEL_DELTA + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL) + category = EVENT_CATEGORY_HEALTH + description = "a floor cluwne will spawn to seek out victims." /datum/round_event/chem_spill - announceWhen = 1 - startWhen = 1 - endWhen = 35 + announce_when = 1 + start_when = 1 + end_when = 35 var/interval = 2 var/list/filters = list() var/randomProbability = 25 var/reagentsAmount = 50 - var/list/saferChems = list(/datum/reagent/water,/datum/reagent/carbon,/datum/reagent/consumable/flour,/datum/reagent/space_cleaner,/datum/reagent/consumable/nutriment,/datum/reagent/consumable/condensedcapsaicin,/datum/reagent/drug/mushroomhallucinogen,/datum/reagent/lube,/datum/reagent/glitter/pink,/datum/reagent/cryptobiolin, - /datum/reagent/toxin/plantbgone,/datum/reagent/blood,/datum/reagent/medicine/charcoal,/datum/reagent/drug/space_drugs,/datum/reagent/medicine/morphine,/datum/reagent/water/holywater,/datum/reagent/consumable/ethanol,/datum/reagent/consumable/hot_coco,/datum/reagent/toxin/acid,/datum/reagent/toxin/mindbreaker,/datum/reagent/toxin/rotatium,/datum/reagent/bluespace, - /datum/reagent/pax,/datum/reagent/consumable/laughter,/datum/reagent/concentrated_barbers_aid,/datum/reagent/colorful_reagent,/datum/reagent/peaceborg/confuse,/datum/reagent/peaceborg/tire,/datum/reagent/consumable/sodiumchloride,/datum/reagent/consumable/ethanol/beer,/datum/reagent/hair_dye,/datum/reagent/consumable/sugar,/datum/reagent/glitter/white,/datum/reagent/growthserum) //needs to be chemid unit checked at some point /datum/round_event/chem_spill/announce() priority_announce("Due to a chemical spill, your pool[filters.len > 1 ? "s" : ""] may have been contaminated", "Hazmat alert") /datum/round_event/chem_spill/setup() - endWhen = rand(25, 100) + end_when = rand(25, 100) for(var/obj/machinery/pool_filter/filter in GLOB.pool_filters) var/turf/T = get_turf(filter) if(T && is_station_level(T.z)) filters += filter if(!filters.len) return kill() + setup = TRUE //storytellers /datum/round_event/chem_spill/start() for(var/obj/machinery/pool_filter/filter in filters) @@ -38,7 +39,7 @@ if (prob(randomProbability)) R.add_reagent(get_random_reagent_id(), reagentsAmount) else - R.add_reagent(pick(saferChems), reagentsAmount) + R.add_reagent(get_random_safe_chem(), reagentsAmount) CHECK_TICK /datum/round_event_control/chem_spill/threatening diff --git a/code/modules/events/communications_blackout.dm b/code/modules/events/communications_blackout.dm index e77b86e8a151..852be43e0158 100644 --- a/code/modules/events/communications_blackout.dm +++ b/code/modules/events/communications_blackout.dm @@ -2,9 +2,17 @@ name = "Communications Blackout" typepath = /datum/round_event/communications_blackout weight = 30 + category = EVENT_CATEGORY_ENGINEERING + description = "Heavily emps all telecommunication machines, blocking all communication for a while." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 3 + max_occurrences = 2 + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL, TAG_SPOOKY) + event_group = /datum/event_group/comms /datum/round_event/communications_blackout - announceWhen = 1 + announce_when = 1 /datum/round_event/communications_blackout/announce(fake) var/alert = pick( "Ionospheric anomalies detected. Temporary telecommunication failure imminent. Please contact you*%fj00)`5vc-BZZT", \ diff --git a/code/modules/events/creep_awakening.dm b/code/modules/events/creep_awakening.dm deleted file mode 100644 index f10c71842c25..000000000000 --- a/code/modules/events/creep_awakening.dm +++ /dev/null @@ -1,24 +0,0 @@ -/datum/round_event_control/obsessed - name = "Obsession Awakening" - typepath = /datum/round_event/obsessed - max_occurrences = 1 - min_players = 20 - -/datum/round_event/obsessed - fakeable = FALSE - -/datum/round_event/obsessed/start() - for(var/mob/living/carbon/human/H in shuffle(GLOB.player_list)) - if(!H.client || !(ROLE_OBSESSED in H.client.prefs.be_special)) - continue - if(H.stat == DEAD) - continue - if(!SSjob.GetJob(H.mind.assigned_role) || (H.mind.assigned_role in GLOB.nonhuman_positions)) //only station jobs sans nonhuman roles, prevents ashwalkers trying to stalk with crewmembers they never met - continue - if(H.mind.has_antag_datum(/datum/antagonist)) - continue - if(!H.getorgan(/obj/item/organ/brain)) - continue - H.gain_trauma(/datum/brain_trauma/special/obsessed) - announce_to_ghosts(H) - break diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index 036f976759cc..608b29fe1c64 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -4,12 +4,13 @@ max_occurrences = 1 min_players = 10 weight = 5 + category = EVENT_CATEGORY_HEALTH + track = EVENT_TRACK_MAJOR + tags = list(TAG_TARGETED, TAG_COMMUNAL, TAG_EXTERNAL, TAG_ALIEN, TAG_MAGICAL) /datum/round_event/disease_outbreak - announceWhen = 15 - + announce_when = 15 var/virus_type - var/max_severity = 3 @@ -17,8 +18,8 @@ priority_announce("Confirmed outbreak of level 7 viral biohazard aboard [station_name()]. All personnel must contain the outbreak.", "Biohazard Alert", ANNOUNCER_OUTBREAK7) /datum/round_event/disease_outbreak/setup() - announceWhen = rand(15, 30) - + announce_when = rand(15, 30) + setup = TRUE //storytellers /datum/round_event/disease_outbreak/start() var/advanced_virus = FALSE diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm deleted file mode 100644 index 724553f81614..000000000000 --- a/code/modules/events/dust.dm +++ /dev/null @@ -1,32 +0,0 @@ -/datum/round_event_control/space_dust - name = "Minor Space Dust" - typepath = /datum/round_event/space_dust - weight = 200 - max_occurrences = 1000 - earliest_start = 0 MINUTES - alert_observers = FALSE - max_alert = SEC_LEVEL_DELTA - -/datum/round_event/space_dust - startWhen = 1 - endWhen = 2 - fakeable = FALSE - -/datum/round_event/space_dust/start() - spawn_meteors(1, GLOB.meteorsC) - -/datum/round_event_control/sandstorm - name = "Sandstorm" - typepath = /datum/round_event/sandstorm - weight = 0 - max_occurrences = 0 - earliest_start = 0 MINUTES - -/datum/round_event/sandstorm - startWhen = 1 - endWhen = 150 // ~5 min - announceWhen = 0 - fakeable = FALSE - -/datum/round_event/sandstorm/tick() - spawn_meteors(10, GLOB.meteorsC) diff --git a/code/modules/events/electrical_storm.dm b/code/modules/events/electrical_storm.dm index 4739a01d2b7b..56e074a2ae5c 100644 --- a/code/modules/events/electrical_storm.dm +++ b/code/modules/events/electrical_storm.dm @@ -5,11 +5,14 @@ min_players = 5 weight = 15 alert_observers = 0 + track = EVENT_TRACK_MUNDANE + tags = list(TAG_SPOOKY) + event_group = /datum/event_group/error /datum/round_event/electrical_storm var/lightsoutAmount = 1 var/lightsoutRange = 25 - announceWhen = 1 + announce_when = 1 /datum/round_event/electrical_storm/announce(fake) priority_announce("An electrical storm has been detected in your area, please repair potential electronic overloads.", "Electrical Storm Alert") diff --git a/code/modules/events/fake_virus.dm b/code/modules/events/fake_virus.dm index aa14629a29f2..9f85a65fffce 100644 --- a/code/modules/events/fake_virus.dm +++ b/code/modules/events/fake_virus.dm @@ -1,7 +1,11 @@ /datum/round_event_control/fake_virus name = "Fake Virus" typepath = /datum/round_event/fake_virus - weight = 20 + weight = 15 + category = EVENT_CATEGORY_HEALTH + description = "Some crewmembers suffer from temporary hypochondria." + track = EVENT_TRACK_MUNDANE + tags = list(TAG_TARGETED) /datum/round_event/fake_virus/start() var/list/fake_virus_victims = list() diff --git a/code/modules/events/false_alarm.dm b/code/modules/events/false_alarm.dm index 8dfb7178bf83..bc7eaf6d601f 100644 --- a/code/modules/events/false_alarm.dm +++ b/code/modules/events/false_alarm.dm @@ -3,31 +3,36 @@ typepath = /datum/round_event/falsealarm weight = 20 max_occurrences = 5 - var/forced_type //Admin abuse - max_alert = SEC_LEVEL_DELTA - + category = EVENT_CATEGORY_BUREAUCRATIC + description = "Fakes an event announcement." + admin_setup = list(/datum/event_admin_setup/listed_options/false_alarm) + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL) + event_group = /datum/event_group/error -/datum/round_event_control/falsealarm/admin_setup() - if(!check_rights(R_FUN)) - return +/datum/event_admin_setup/listed_options/false_alarm + normal_run_option = "Random Fake Event" +/datum/event_admin_setup/listed_options/false_alarm/get_list() var/list/possible_types = list() - - for(var/datum/round_event_control/E in SSevents.control) - var/datum/round_event/event = E.typepath + for(var/datum/round_event_control/event_control in SSgamemode.control) + var/datum/round_event/event = event_control.typepath if(!initial(event.fakeable)) continue - possible_types += E + possible_types += event_control + return possible_types - forced_type = input(usr, "Select the scare.","False event") as null|anything in possible_types +/datum/event_admin_setup/listed_options/false_alarm/apply_to_event(datum/round_event/falsealarm/event) + event.forced_type = chosen -/datum/round_event_control/falsealarm/canSpawnEvent(players_amt, gamemode) +/datum/round_event_control/falsealarm/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) return ..() && length(gather_false_events()) /datum/round_event/falsealarm - announceWhen = 0 - endWhen = 1 + announce_when = 0 + end_when = 1 fakeable = FALSE + var/forced_type //Admin abuse /datum/round_event/falsealarm/announce(fake) if(fake) //What are you doing @@ -37,10 +42,8 @@ var/events_list = gather_false_events(players_amt, gamemode) var/datum/round_event_control/event_control - var/datum/round_event_control/falsealarm/C = control - if(C.forced_type) - event_control = C.forced_type - C.forced_type = null + if(forced_type) + event_control = forced_type else event_control = pick(events_list) if(event_control) @@ -51,7 +54,7 @@ /proc/gather_false_events(players_amt, gamemode) . = list() - for(var/datum/round_event_control/E in SSevents.control) + for(var/datum/round_event_control/E in SSgamemode.control) if(istype(E, /datum/round_event_control/falsealarm)) continue if(!E.canSpawnEvent(players_amt, gamemode)) diff --git a/code/modules/events/flutes.dm b/code/modules/events/flutes.dm index ac328f006291..3a102ffaca2e 100644 --- a/code/modules/events/flutes.dm +++ b/code/modules/events/flutes.dm @@ -12,7 +12,11 @@ typepath = /datum/round_event/flutes max_occurrences = 1 weight = 1 - min_players = 30 + min_players = 20 + track = EVENT_TRACK_MAJOR + tags = list(TAG_SPOOKY, TAG_MAGICAL) + category = EVENT_CATEGORY_HEALTH + description = "plays some flute music." //-------------------- //ROUND EVENT "FLUTES" @@ -31,13 +35,16 @@ continue avail_players.Add(M) shuffle_inplace(avail_players) - var/target_amount = (round((avail_players.len / 10), 1) == 0 ? 1 : round((avail_players.len / 10), 1)) + + var/target_amount = max(round((length(avail_players) / 10), 1), 1) //1 per 10 people, but at least one + for(var/mob/living/carbon/C in avail_players) C.playsound_local(C, 'sound/ambience/flutes.ogg', 20, FALSE, pressure_affected = FALSE) pick_flute_scene(C) chosen_players.Add(C) announce_to_ghosts(C) - if(chosen_players.len >= target_amount) + + if(length(chosen_players) >= target_amount) break /datum/round_event/flutes/proc/pick_flute_scene(mob/living/carbon/M) @@ -61,91 +68,95 @@ /datum/round_event/flutes/proc/flute_headache(mob/living/carbon/M) to_chat(M, span_warning("You get an intense headache!")) - M.adjust_eye_blur(15) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 20, 30) - M.adjustStaminaLoss(15) + M.adjust_eye_blur(20) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 15, 199) + M.adjustStaminaLoss(20) /datum/round_event/flutes/proc/flute_tremble(mob/living/carbon/M) to_chat(M, span_warning("Something trembles along the edge of your vision, your eyes water, with the familiar beat of blood racing through your head.")) M.adjust_eye_blur(30) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 30, 35) - M.adjustStaminaLoss(30) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 30, 199) + M.adjustStaminaLoss(40) /datum/round_event/flutes/proc/flute_chanting(mob/living/carbon/M) - to_chat(M, "You hear faint chanting.. You feel a heavy weight upon your shoulders, as something shifts it's gaze towards you..") - M.adjust_eye_blur(30) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 50, 60) + to_chat(M, span_cultlarge("You hear faint chanting.. You feel a heavy weight upon your shoulders, as something shifts it's gaze towards you..")) + M.adjust_eye_blur(50) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 50, 199) M.adjustStaminaLoss(50) ADD_TRAIT(M, TRAIT_UNSTABLE, M) - //sanity = 75 /datum/round_event/flutes/proc/flute_starlight(mob/living/carbon/M) to_chat(M, span_warning("As the nearest stars light your skin and your station, you can make out the faint whispers being spoken in turn with the monotone flutes playing beyond you. You feel so tired, as the struts of metal piping, the walls, the floor twist in unnatural ways, as the lights dim.")) - sleep(30 SECONDS) + sleep(10 SECONDS) M.adjust_eye_blur(40) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 65, 70, 80) - ADD_TRAIT(M, TRAIT_UNSTABLE, M) - //sanity = 50 - sleep(25 SECONDS) + sleep(10 SECONDS) to_chat(M, span_warning("Exhaustion takes you.")) - M.SetSleeping(70) + M.SetSleeping(10 SECONDS) to_chat(M, span_notice("You see vague, inhuman forms, in the tightest corners of your alien dreams, dancing frantically to thundering drums, in tune with yellow monotone flutes.")) - sleep(20 SECONDS) - M.adjustBruteLoss(15) + sleep(10 SECONDS) to_chat(M, span_notice("You wake from your deep dreams, blood oozing from your nose, while your head beats to a familiar tune.")) + M.adjustBruteLoss(15) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 70, 199) + ADD_TRAIT(M, TRAIT_UNSTABLE, M) /datum/round_event/flutes/proc/flute_corridor_encounter(mob/living/carbon/M) to_chat(M, span_notice("You glance upwards, down the corridor.")) + sleep(5 SECONDS) - to_chat(M, "A BRIGHT BLINDING LIGHT FILLS YOUR VISION!") - M.eye_blind = 5 - sleep(10 SECONDS) - to_chat(M, "Your mind oozes at what it cannot comprehend, you feel your soul snatching onto your brain, with chills rolling up your spine. You barely hang on.") + + to_chat(M, span_large_brass("A BRIGHT BLINDING LIGHT FILLS YOUR VISION!")) + M.adjust_blindness(5) + + sleep(5 SECONDS) + + to_chat(M, span_hypnophrase("Your mind oozes at what it cannot comprehend, you feel your soul snatching onto your brain, with chills rolling up your spine. You barely hang on.")) + to_chat(M, span_suicide("Your mind shatters.")) + M.SetSleeping(30 SECONDS) + sleep(20 SECONDS) if(HAS_TRAIT(M, TRAIT_UNSTABLE)) - to_chat(M, span_green("Your mind shatters.")) - M.SetSleeping(30) - sleep(30 SECONDS) to_chat(M, span_suicide("Your flesh undulates, and boils off your bones. You were blind, yet now you've seen a glimpse behind the cosmic curtain.")) - //sanity = 25 + sleep(2 SECONDS) REMOVE_TRAIT(M, TRAIT_UNSTABLE, M) - M.adjust_eye_blur(5) - M.adjustStaminaLoss(90) - M.adjustBruteLoss(60, 70, 75, 80, 85) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 65, 70, 75, 80, 90) M.set_species(/datum/species/skeleton) - sleep(30 SECONDS) to_chat(M, span_warning("Your mind.. Has been humbled. You have escaped by the skin of your teeth, but have lost your body, and much, much more. You feel.. Mirth?")) else - to_chat(M, span_suicide("Your mind shatters.")) ADD_TRAIT(M, TRAIT_UNSTABLE, M) - //sanity = 1 - M.SetSleeping(30) - M.adjust_eye_blur(40) - M.adjustStaminaLoss(99) to_chat(M, span_narsie("Y'HAH HT'HU THRZHZU. UA'KLL GHRT AWN ZUU!")) - M.adjustBruteLoss(60, 70, 75, 80, 85) - M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 65, 70, 75, 80, 90) - random_blackouts_enable(M) - -/datum/round_event/flutes/proc/random_blackouts_enable(mob/living/carbon/M) - ADD_TRAIT(M, RANDOM_BLACKOUTS, M) - spawn - while(HAS_TRAIT(M, RANDOM_BLACKOUTS)) - sleep(15 SECONDS) - spawn - random_blackout(M) - -/datum/round_event/flutes/proc/random_blackout(mob/living/carbon/M) + M.adjustBruteLoss(60, TRUE, TRUE) + M.adjustOrganLoss(ORGAN_SLOT_BRAIN, 65, 199) + + M.AddComponent(/datum/component/random_blackouts) + + +//apply this to an object to make nearby lights flicker randomly +/datum/component/random_blackouts + var/atom/movable/host + COOLDOWN_DECLARE(nextblackout) + +/datum/component/random_blackouts/Initialize(...) + if(!istype(parent, /atom/movable)) + return COMPONENT_INCOMPATIBLE + host = parent + +/datum/component/random_blackouts/RegisterWithParent() + START_PROCESSING(SSobj, src) + +/datum/component/random_blackouts/UnregisterFromParent() + STOP_PROCESSING(SSobj, src) + +/datum/component/random_blackouts/process(delta_time) + . = ..() + if(COOLDOWN_FINISHED(src, nextblackout)) + random_blackout() + COOLDOWN_START(src, nextblackout, (rand(10, 30) SECONDS)) + +/datum/component/random_blackouts/proc/random_blackout() + if(!host || QDELETED(host)) + message_admins("Random blackouts from flute event isn't properly cleaned up, tell molti") + return var/list/nearby_lights = list() - for(var/obj/machinery/light/L in M.loc.loc.contents) - if(L.bulb_power == 0 && L.light_power == 0) - continue - nearby_lights.Add(L) - var/obj/machinery/light/random_light = nearby_lights[rand(1, nearby_lights.len)] - random_light.light_power = 0 - random_light.bulb_power = 0 - random_light.update() - sleep(rand(8, 30) SECONDS) - random_light.light_power = 1 - random_light.bulb_power = 1 - random_light.update() + for(var/obj/machinery/light/L in get_area(host)) + if(L.on) + nearby_lights.Add(L) + var/obj/machinery/light/random_light = nearby_lights[rand(1, length(nearby_lights))] + random_light.flicker() diff --git a/code/modules/events/ghost_role.dm b/code/modules/events/ghost_role/_ghost_role.dm similarity index 100% rename from code/modules/events/ghost_role.dm rename to code/modules/events/ghost_role/_ghost_role.dm diff --git a/code/modules/events/abductor.dm b/code/modules/events/ghost_role/abductor.dm old mode 100755 new mode 100644 similarity index 82% rename from code/modules/events/abductor.dm rename to code/modules/events/ghost_role/abductor.dm index 793e7edcfd86..bfbd827a7a3d --- a/code/modules/events/abductor.dm +++ b/code/modules/events/ghost_role/abductor.dm @@ -4,7 +4,11 @@ weight = 12 max_occurrences = 1 min_players = 25 - gamemode_blacklist = list("nuclear","wizard","revolution") + category = EVENT_CATEGORY_INVASION + description = "One or more abductor teams spawns, and they plan to experiment on the crew." + track = EVENT_TRACK_MAJOR + tags = list(TAG_TARGETED, TAG_SPOOKY, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE /datum/round_event/ghost_role/abductor minimum_required = 2 diff --git a/code/modules/events/alien_infestation.dm b/code/modules/events/ghost_role/alien_infestation.dm similarity index 86% rename from code/modules/events/alien_infestation.dm rename to code/modules/events/ghost_role/alien_infestation.dm index 803c07e84431..47ef5ea7f3ac 100644 --- a/code/modules/events/alien_infestation.dm +++ b/code/modules/events/ghost_role/alien_infestation.dm @@ -2,12 +2,12 @@ name = "Alien Infestation" typepath = /datum/round_event/ghost_role/alien_infestation weight = 5 - min_players = 30 + track = EVENT_TRACK_ROLESET + tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE - dynamic_should_hijack = TRUE - -/datum/round_event_control/alien_infestation/canSpawnEvent() +/datum/round_event_control/alien_infestation/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) . = ..() if(!.) return . @@ -17,7 +17,7 @@ return FALSE /datum/round_event/ghost_role/alien_infestation - announceWhen = 400 + announce_when = 400 minimum_required = 1 role_name = "alien larva" @@ -28,9 +28,10 @@ /datum/round_event/ghost_role/alien_infestation/setup() - announceWhen = rand(announceWhen, announceWhen + 50) + announce_when = rand(announce_when, announce_when + 50) if(prob(50)) spawncount++ + setup = TRUE //storytellers /datum/round_event/ghost_role/alien_infestation/announce(fake) var/living_aliens = FALSE diff --git a/code/modules/events/blob.dm b/code/modules/events/ghost_role/blob.dm similarity index 83% rename from code/modules/events/blob.dm rename to code/modules/events/ghost_role/blob.dm index 1ef4adddc51e..242b3cd0a7a3 100644 --- a/code/modules/events/blob.dm +++ b/code/modules/events/ghost_role/blob.dm @@ -4,11 +4,14 @@ weight = 8 max_occurrences = 1 min_players = 30 - dynamic_should_hijack = TRUE - gamemode_blacklist = list("blob") //Just in case a blob survives that long + category = EVENT_CATEGORY_ENTITIES + description = "Spawns a new blob overmind." + track = EVENT_TRACK_ROLESET + tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE /datum/round_event/ghost_role/blob - announceWhen = -1 + announce_when = -1 role_name = "blob overmind" fakeable = TRUE var/pointmodifier = 1 diff --git a/code/modules/events/ghost_role/changeling_meteor.dm b/code/modules/events/ghost_role/changeling_meteor.dm new file mode 100644 index 000000000000..7de5464bdf55 --- /dev/null +++ b/code/modules/events/ghost_role/changeling_meteor.dm @@ -0,0 +1,34 @@ +/* +* Changeling midround spawn event. Takes a ghost volunteer and stuffs them into a changeling with their own identity and a flesh space suit. +* They arrive via a meateor, which collides with the station. They are expected to find their own way into the station by whatever means necessary. +* The midround changeling experience is, by nature, more difficult than playing as a roundstart crew changeling. +* +*/ + +// /datum/round_event_control/changeling +// name = "Changeling Meteor" +// typepath = /datum/round_event/ghost_role/changeling +// weight = 8 +// max_occurrences = 3 +// min_players = 20 +// category = EVENT_CATEGORY_ENTITIES +// description = "A meteor containing a changeling is summoned and thrown at the exterior of the station." +// track = EVENT_TRACK_MAJOR +// tags = list(TAG_COMBAT, TAG_SPACE, TAG_EXTERNAL, TAG_ALIEN) +// checks_antag_cap = TRUE + +// /datum/round_event/ghost_role/changeling +// minimum_required = 1 +// role_name = "space changeling" +// fakeable = FALSE + +// /datum/round_event/ghost_role/changeling/spawn_role() +// var/list/mob/dead/observer/candidate = SSpolling.poll_ghost_candidates(check_jobban = ROLE_CHANGELING, role = ROLE_CHANGELING_MIDROUND, alert_pic = /obj/item/melee/arm_blade, role_name_text = role_name) + +// if(!candidate.len) +// return NOT_ENOUGH_PLAYERS + +// spawned_mobs += generate_changeling_meteor(pick_n_take(candidate)) + +// if(spawned_mobs) +// return SUCCESSFUL_SPAWN diff --git a/code/modules/events/fugitive_spawning.dm b/code/modules/events/ghost_role/fugitives.dm similarity index 98% rename from code/modules/events/fugitive_spawning.dm rename to code/modules/events/ghost_role/fugitives.dm index a527d8a860ff..9498aaa16af2 100644 --- a/code/modules/events/fugitive_spawning.dm +++ b/code/modules/events/ghost_role/fugitives.dm @@ -4,7 +4,8 @@ max_occurrences = 1 min_players = 20 earliest_start = 30 MINUTES //deadchat sink, lets not even consider it early on. - gamemode_blacklist = list("nuclear") + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_EXTERNAL) /datum/round_event/ghost_role/fugitives minimum_required = 1 diff --git a/code/modules/events/horror.dm b/code/modules/events/ghost_role/horror.dm similarity index 87% rename from code/modules/events/horror.dm rename to code/modules/events/ghost_role/horror.dm index afa1182ff04e..1b9b1cab6434 100644 --- a/code/modules/events/horror.dm +++ b/code/modules/events/ghost_role/horror.dm @@ -4,6 +4,10 @@ max_occurrences = 2 min_players = 15 earliest_start = 20 MINUTES + track = EVENT_TRACK_MAJOR + tags = list(TAG_SPOOKY, TAG_ALIEN, TAG_MAGICAL) + category = EVENT_CATEGORY_ENTITIES + description = "Spawns a horror antag." /datum/round_event/ghost_role/horror minimum_required = 1 @@ -31,4 +35,4 @@ message_admins("[ADMIN_LOOKUPFLW(S)] has been made into an eldritch horror by an event.") log_game("[key_name(S)] was spawned as an eldritch horror by an event.") spawned_mobs += S - return SUCCESSFUL_SPAWN \ No newline at end of file + return SUCCESSFUL_SPAWN diff --git a/code/modules/events/ghost_role/morph.dm b/code/modules/events/ghost_role/morph.dm new file mode 100644 index 000000000000..d589757c494e --- /dev/null +++ b/code/modules/events/ghost_role/morph.dm @@ -0,0 +1,41 @@ +/datum/round_event_control/morph + name = "Spawn Morph" + typepath = /datum/round_event/ghost_role/morph + weight = 5 + max_occurrences = 1 + min_players = 25 + earliest_start = 30 MINUTES + category = EVENT_CATEGORY_ENTITIES + description = "Spawns a hungry shapeshifting blobby creature." + min_wizard_trigger_potency = 4 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE + +/datum/round_event/ghost_role/morph + minimum_required = 1 + role_name = "morphling" + +/datum/round_event/ghost_role/morph/spawn_role() + var/list/candidates = get_candidates(ROLE_ALIEN, null, ROLE_ALIEN) + if(!candidates.len) + return NOT_ENOUGH_PLAYERS + + var/mob/dead/selected = pick_n_take(candidates) + + var/datum/mind/player_mind = new /datum/mind(selected.key) + player_mind.active = 1 + if(!GLOB.xeno_spawn) + return MAP_ERROR + var/mob/living/simple_animal/hostile/morph/S = new /mob/living/simple_animal/hostile/morph(pick(GLOB.xeno_spawn)) + player_mind.transfer_to(S) + player_mind.assigned_role = "Morph" + player_mind.special_role = "Morph" + player_mind.add_antag_datum(/datum/antagonist/morph) + to_chat(S, S.playstyle_string) + SEND_SOUND(S, sound('sound/magic/mutate.ogg')) + message_admins("[ADMIN_LOOKUPFLW(S)] has been made into a morph by an event.") + log_game("[key_name(S)] was spawned as a morph by an event.") + spawned_mobs += S + return SUCCESSFUL_SPAWN diff --git a/code/modules/events/nightmare.dm b/code/modules/events/ghost_role/nightmare.dm similarity index 80% rename from code/modules/events/nightmare.dm rename to code/modules/events/ghost_role/nightmare.dm index 963bc78d6767..869a0e43aa63 100644 --- a/code/modules/events/nightmare.dm +++ b/code/modules/events/ghost_role/nightmare.dm @@ -2,9 +2,15 @@ name = "Spawn Nightmare" typepath = /datum/round_event/ghost_role/nightmare max_occurrences = 1 - min_players = 30 - earliest_start = 45 MINUTES - dynamic_should_hijack = TRUE + min_players = 25 + earliest_start = 40 MINUTES + category = EVENT_CATEGORY_ENTITIES + description = "Spawns a nightmare, aiming to darken the station." + min_wizard_trigger_potency = 6 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE /datum/round_event/ghost_role/nightmare minimum_required = 1 diff --git a/code/modules/events/operative.dm b/code/modules/events/ghost_role/operative.dm similarity index 85% rename from code/modules/events/operative.dm rename to code/modules/events/ghost_role/operative.dm index a114bcb36929..76219bb1049a 100644 --- a/code/modules/events/operative.dm +++ b/code/modules/events/ghost_role/operative.dm @@ -4,7 +4,9 @@ weight = 0 //Admin only max_occurrences = 1 min_players = 25 - dynamic_should_hijack = TRUE + category = EVENT_CATEGORY_INVASION + description = "A single nuclear operative assaults the station." + track = EVENT_TRACK_MAJOR //this is a safe guard and does not trigger normally(technically it can but not really) so no tags /datum/round_event/ghost_role/operative minimum_required = 1 diff --git a/code/modules/events/pirates.dm b/code/modules/events/ghost_role/pirates.dm similarity index 97% rename from code/modules/events/pirates.dm rename to code/modules/events/ghost_role/pirates.dm index 3896f42b7e53..0ca3c981fdda 100644 --- a/code/modules/events/pirates.dm +++ b/code/modules/events/ghost_role/pirates.dm @@ -2,11 +2,16 @@ name = "Space Pirates" typepath = /datum/round_event/pirates weight = 8 - max_occurrences = 1 - min_players = 10 + max_occurrences = 2 + min_players = 20 earliest_start = 30 MINUTES - dynamic_should_hijack = TRUE - gamemode_blacklist = list("nuclear") + category = EVENT_CATEGORY_INVASION + description = "The crew will either pay up, or face a pirate assault." + // admin_setup = list(/datum/event_admin_setup/listed_options/pirates) + map_flags = EVENT_SPACE_ONLY + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_COMMUNAL) + checks_antag_cap = TRUE /datum/round_event_control/pirates/preRunEvent() if (!SSmapping.empty_space) @@ -15,7 +20,7 @@ return ..() /datum/round_event/pirates - startWhen = 150 //5 minutes to answer + start_when = 150 //5 minutes to answer var/datum/comm_message/threat var/payoff = 0 var/payoff_min = 20000 @@ -28,6 +33,7 @@ var/start = pick(strings(PIRATE_NAMES_FILE, "ship_name_start")) var/end = pick(strings(PIRATE_NAMES_FILE, "ship_name_end")) ship_name = "[the][start] [end]" + setup = TRUE //storytellers /datum/round_event/pirates/announce(fake) priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", SSstation.announcer.get_rand_report_sound()) diff --git a/code/modules/events/sentience.dm b/code/modules/events/ghost_role/sentience.dm similarity index 94% rename from code/modules/events/sentience.dm rename to code/modules/events/ghost_role/sentience.dm index 9ad02840f727..5c5b1a716af8 100644 --- a/code/modules/events/sentience.dm +++ b/code/modules/events/ghost_role/sentience.dm @@ -20,7 +20,12 @@ GLOBAL_LIST_INIT(high_priority_sentience, typecacheof(list( name = "Random Human-level Intelligence" typepath = /datum/round_event/ghost_role/sentience weight = 10 - max_alert = SEC_LEVEL_DELTA + category = EVENT_CATEGORY_FRIENDLY + description = "An animal or robot becomes sentient!" + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL, TAG_SPOOKY, TAG_MAGICAL) /datum/round_event/ghost_role/sentience diff --git a/code/modules/antagonists/disease/disease_event.dm b/code/modules/events/ghost_role/sentient_disease.dm similarity index 74% rename from code/modules/antagonists/disease/disease_event.dm rename to code/modules/events/ghost_role/sentient_disease.dm index 7183ed5455e9..15ed123ca767 100644 --- a/code/modules/antagonists/disease/disease_event.dm +++ b/code/modules/events/ghost_role/sentient_disease.dm @@ -1,10 +1,16 @@ - /datum/round_event_control/sentient_disease name = "Spawn Sentient Disease" typepath = /datum/round_event/ghost_role/sentient_disease weight = 7 max_occurrences = 1 min_players = 5 + category = EVENT_CATEGORY_HEALTH + description = "Spawns a sentient disease, who wants to infect as many people as possible." + min_wizard_trigger_potency = 4 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE /datum/round_event/ghost_role/sentient_disease diff --git a/code/modules/events/space_dragon.dm b/code/modules/events/ghost_role/space_dragon.dm similarity index 82% rename from code/modules/events/space_dragon.dm rename to code/modules/events/ghost_role/space_dragon.dm index e6cb0642290e..2e29d046f26e 100644 --- a/code/modules/events/space_dragon.dm +++ b/code/modules/events/ghost_role/space_dragon.dm @@ -4,12 +4,19 @@ weight = 7 max_occurrences = 1 min_players = 20 - dynamic_should_hijack = TRUE + earliest_start = 60 MINUTES + category = EVENT_CATEGORY_ENTITIES + description = "Spawns a space dragon, which will try to take over the station." + min_wizard_trigger_potency = 6 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_ROLESET + tags = list(TAG_COMBAT, TAG_SPACE, TAG_EXTERNAL, TAG_ALIEN, TAG_MAGICAL) + checks_antag_cap = TRUE /datum/round_event/ghost_role/space_dragon minimum_required = 1 role_name = "Space Dragon" - announceWhen = 10 + announce_when = 10 /datum/round_event/ghost_role/space_dragon/announce(fake) priority_announce("A large organic energy flux has been recorded near [station_name()], please stand by.", "Lifesign Alert") diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/events/ghost_role/space_ninja.dm similarity index 95% rename from code/modules/ninja/ninja_event.dm rename to code/modules/events/ghost_role/space_ninja.dm index 9f56c30464c5..7354318ded9c 100644 --- a/code/modules/ninja/ninja_event.dm +++ b/code/modules/events/ghost_role/space_ninja.dm @@ -15,7 +15,9 @@ Contents: max_occurrences = 1 earliest_start = 40 MINUTES min_players = 15 - dynamic_should_hijack = TRUE + track = EVENT_TRACK_ROLESET + tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_EXTERNAL) + checks_antag_cap = TRUE /datum/round_event/ghost_role/ninja var/success_spawn = 0 @@ -28,6 +30,7 @@ Contents: /datum/round_event/ghost_role/ninja/setup() helping_station = rand(0,1) + setup = TRUE //storytellers /datum/round_event/ghost_role/ninja/kill() if(!success_spawn && control) diff --git a/code/modules/events/gravity_generator_blackout.dm b/code/modules/events/gravity_generator_blackout.dm new file mode 100644 index 000000000000..8e38000b51b0 --- /dev/null +++ b/code/modules/events/gravity_generator_blackout.dm @@ -0,0 +1,37 @@ +/datum/round_event_control/gravity_generator_blackout + name = "Gravity Generator Blackout" + typepath = /datum/round_event/gravity_generator_blackout + weight = 30 + category = EVENT_CATEGORY_ENGINEERING + description = "Turns off the gravity generator." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 4 + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL, TAG_SPACE) + event_group = /datum/event_group/bsod + +/datum/round_event_control/gravity_generator_blackout/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) //MONKESTATION ADDITION: fake_check = FALSE + . = ..() + if(!.) + return . + + var/station_generator_exists = FALSE + for(var/obj/machinery/gravity_generator/main/the_generator in GLOB.machines) + if(is_station_level(the_generator.z)) + station_generator_exists = TRUE + + if(!station_generator_exists) + return FALSE + +/datum/round_event/gravity_generator_blackout + announce_when = 1 + start_when = 1 + announce_chance = 33 + +/datum/round_event/gravity_generator_blackout/announce(fake) + priority_announce("Warning: Failsafes for the station's artificial gravity arrays have been triggered. Please be aware that if this problem recurs it may result in formation of gravitational anomalies. Nanotrasen wishes to remind you that the unauthorised formation of anomalies within Nanotrasen facilities is strictly prohibited by health and safety regulation [rand(99,9999)][pick("a","b","c")]:subclause[rand(1,20)][pick("a","b","c")].") + +/datum/round_event/gravity_generator_blackout/start() + for(var/obj/machinery/gravity_generator/main/the_generator in GLOB.machines) + if(is_station_level(the_generator.z)) + the_generator.blackout() diff --git a/code/modules/events/prison_break.dm b/code/modules/events/grey_tide.dm similarity index 87% rename from code/modules/events/prison_break.dm rename to code/modules/events/grey_tide.dm index 7fef0dcf9ec2..1f71e64363b2 100644 --- a/code/modules/events/prison_break.dm +++ b/code/modules/events/grey_tide.dm @@ -2,12 +2,18 @@ name = "Grey Tide" typepath = /datum/round_event/grey_tide max_occurrences = 2 - min_players = 5 - max_alert = SEC_LEVEL_DELTA + min_players = 10 + category = EVENT_CATEGORY_ENGINEERING + description = "Bolts open all doors in one or more departments." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_SPOOKY) + event_group = /datum/event_group/error /datum/round_event/grey_tide - announceWhen = 50 - endWhen = 20 + announce_when = 50 + end_when = 20 var/list/area/areasToOpen = list() var/list/potential_areas = list(/area/bridge, /area/engine, @@ -19,15 +25,15 @@ /datum/round_event/grey_tide/setup() - announceWhen = rand(50, 60) - endWhen = rand(20, 30) + announce_when = rand(50, 60) + end_when = rand(20, 30) severity = rand(1,3) for(var/i in 1 to severity) var/picked_area = pick_n_take(potential_areas) for(var/area/A as anything in GLOB.areas) if(istype(A, picked_area)) areasToOpen += A - + setup = TRUE //storytellers /datum/round_event/grey_tide/announce(fake) if(areasToOpen && areasToOpen.len > 0) diff --git a/code/modules/events/grid_check.dm b/code/modules/events/grid_check.dm index bcedb4479be2..a99b592af859 100644 --- a/code/modules/events/grid_check.dm +++ b/code/modules/events/grid_check.dm @@ -1,16 +1,30 @@ /datum/round_event_control/grid_check name = "Grid Check" typepath = /datum/round_event/grid_check - gamemode_blacklist = list("darkspawn") //no free win for darkspawns weight = 10 max_occurrences = 3 + category = EVENT_CATEGORY_ENGINEERING + description = "Turns off all APCs for a while, or until they are manually rebooted." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 4 + /// Cooldown for the announement associated with this event. + /// Necessary due to the fact that this event is player triggerable. + COOLDOWN_DECLARE(announcement_spam_protection) + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL, TAG_SPOOKY) + event_group = /datum/event_group/bsod /datum/round_event/grid_check - announceWhen = 1 - startWhen = 1 + announce_when = 1 + start_when = 1 /datum/round_event/grid_check/announce(fake) + var/datum/round_event_control/grid_check/controller = control + if(!COOLDOWN_FINISHED(controller, announcement_spam_protection) && !fake) + return priority_announce("Abnormal activity detected in [station_name()]'s powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.", "Critical Power Failure", ANNOUNCER_POWEROFF) + if(!fake) // Only start the CD if we're real + COOLDOWN_START(controller, announcement_spam_protection, 30 SECONDS) /datum/round_event/grid_check/start() power_fail(30, 120) diff --git a/code/modules/events/heart_attack.dm b/code/modules/events/heart_attack.dm index 95a5e12c8133..481bfc51c888 100644 --- a/code/modules/events/heart_attack.dm +++ b/code/modules/events/heart_attack.dm @@ -4,6 +4,12 @@ weight = 20 max_occurrences = 2 min_players = 40 // To avoid shafting lowpop + category = EVENT_CATEGORY_HEALTH + description = "A random crewmember's heart gives out." + min_wizard_trigger_potency = 6 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MODERATE + tags = list(TAG_TARGETED, TAG_MAGICAL) /datum/round_event/heart_attack/start() var/list/heart_attack_contestants = list() diff --git a/code/modules/events/high_priority_bounty.dm b/code/modules/events/high_priority_bounty.dm index ffdcd8840b7e..0b6319006837 100644 --- a/code/modules/events/high_priority_bounty.dm +++ b/code/modules/events/high_priority_bounty.dm @@ -1,9 +1,13 @@ /datum/round_event_control/high_priority_bounty name = "High Priority Bounty" + description = "provides a high priority cargo bounty." typepath = /datum/round_event/high_priority_bounty - max_occurrences = 3 weight = 20 - earliest_start = 10 + max_occurrences = 1 //only one a round, but it's very valuable to do + earliest_start = 0 + track = EVENT_TRACK_OBJECTIVES + tags = list(TAG_COMMUNAL, TAG_POSITIVE) + category = EVENT_CATEGORY_BUREAUCRATIC /datum/round_event/high_priority_bounty/announce(fake) priority_announce("Central Command has issued a high-priority cargo bounty. Details have been sent to all bounty consoles.", "Nanotrasen Bounty Program") @@ -14,7 +18,7 @@ B = random_bounty() if(!B) continue - B.mark_high_priority(3) + B.mark_high_priority(5) //5x the reward if(try_add_bounty(B)) break diff --git a/code/modules/events/holiday/xmas.dm b/code/modules/events/holiday/xmas.dm index 898b747cd1e2..e5468fc85a29 100644 --- a/code/modules/events/holiday/xmas.dm +++ b/code/modules/events/holiday/xmas.dm @@ -49,9 +49,9 @@ /obj/effect/spawner/xmastree/Initialize(mapload) ..() - if((CHRISTMAS in SSevents.holidays) && christmas_tree) + if((CHRISTMAS in SSgamemode.holidays) && christmas_tree) new christmas_tree(get_turf(src)) - else if((FESTIVE_SEASON in SSevents.holidays) && festive_tree) + else if((FESTIVE_SEASON in SSgamemode.holidays) && festive_tree) new festive_tree(get_turf(src)) return INITIALIZE_HINT_QDEL diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm index fc5634abe628..5a26e8b9f10a 100644 --- a/code/modules/events/immovable_rod.dm +++ b/code/modules/events/immovable_rod.dm @@ -13,32 +13,35 @@ In my current plan for it, 'solid' will be defined as anything with density == 1 min_players = 18 max_occurrences = 3 earliest_start = 20 MINUTES - var/atom/special_target - -/datum/round_event_control/immovable_rod/admin_setup() - if(!check_rights(R_FUN)) - return - - var/aimed = alert("Aimed at current location?","Sniperod", "Yes", "No") - if(aimed == "Yes") - var/turf/aimed_turf = get_turf(usr) - special_target = aimed_turf - message_admins("[key_name_admin(usr)] has aimed the immovable rod at [ADMIN_COORDJMP(aimed_turf)].") - log_admin("[key_name(usr)] has aimed the immovable rod at [COORD(aimed_turf)].") + category = EVENT_CATEGORY_SPACE + description = "The station passes through an immovable rod." + min_wizard_trigger_potency = 6 + max_wizard_trigger_potency = 7 + admin_setup = list(/datum/event_admin_setup/set_location/immovable_rod) + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_EXTERNAL, TAG_MAGICAL) + event_group = /datum/event_group/meteors + +/// Admins can pick a spot the rod will aim for +/datum/event_admin_setup/set_location/immovable_rod + input_text = "Aimed at current location?" + +/datum/event_admin_setup/set_location/immovable_rod/apply_to_event(datum/round_event/immovable_rod/event) + event.special_target = chosen_turf /datum/round_event/immovable_rod - announceWhen = 5 + announce_when = 5 + /// Admins can pick a spot the rod will aim for. + var/atom/special_target /datum/round_event/immovable_rod/announce(fake) priority_announce("What the fuck was that?!", "General Alert") /datum/round_event/immovable_rod/start() - var/datum/round_event_control/immovable_rod/C = control var/startside = pick(GLOB.cardinals) - var/z = pick(SSmapping.levels_by_trait(ZTRAIT_STATION)) - var/turf/startT = spaceDebrisStartLoc(startside, z) - var/turf/endT = spaceDebrisFinishLoc(startside, z) - var/atom/rod = new /obj/effect/immovablerod(startT, endT, C.special_target) + var/turf/end_turf = get_edge_target_turf(get_random_station_turf(), turn(startside, 180)) + var/turf/start_turf = spaceDebrisStartLoc(startside, end_turf.z) + var/atom/rod = new /obj/effect/immovablerod(start_turf, end_turf, special_target) announce_to_ghosts(rod) /obj/effect/immovablerod diff --git a/code/modules/events/ion_storm.dm b/code/modules/events/ion_storm.dm index bf76e07204ed..8f764b5fda3e 100644 --- a/code/modules/events/ion_storm.dm +++ b/code/modules/events/ion_storm.dm @@ -6,6 +6,13 @@ typepath = /datum/round_event/ion_storm weight = 15 min_players = 2 + category = EVENT_CATEGORY_AI + description = "Gives the AI a new, randomized law." + min_wizard_trigger_potency = 2 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MODERATE + tags = list(TAG_TARGETED, TAG_ALIEN) + event_group = /datum/event_group/bsod /datum/round_event/ion_storm /// Chance a new ion law will be added in addition to other ion effects. @@ -34,7 +41,7 @@ var/ionMessage = null /// If announceEvent is 0, what is the chance it is will accounce? var/ionAnnounceChance = 33 - announceWhen = 1 + announce_when = 1 /datum/round_event/ion_storm/add_law_only // special subtype that adds a law only replaceLawsetChance = 0 diff --git a/code/modules/events/major_dust.dm b/code/modules/events/major_dust.dm deleted file mode 100644 index 7fb00124a94d..000000000000 --- a/code/modules/events/major_dust.dm +++ /dev/null @@ -1,19 +0,0 @@ -/datum/round_event_control/meteor_wave/major_dust - name = "Major Space Dust" - typepath = /datum/round_event/meteor_wave/major_dust - weight = 8 - -/datum/round_event/meteor_wave/major_dust - wave_name = "space dust" - -/datum/round_event/meteor_wave/major_dust/announce(fake) - var/reason = pick( - "The station is passing through a debris cloud, expect minor damage \ - to external fittings and fixtures.", - "Nanotrasen Superweapons Division is testing a new prototype \ - [pick("field","projection","nova","super-colliding","reactive")] \ - [pick("cannon","artillery","tank","cruiser","\[REDACTED\]")], \ - some mild debris is expected.", - "A neighbouring station is throwing rocks at you. (Perhaps they've \ - grown tired of your messages.)") - priority_announce(pick(reason), "Collision Alert") diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm index b38f34d477f4..ba251d047807 100644 --- a/code/modules/events/mass_hallucination.dm +++ b/code/modules/events/mass_hallucination.dm @@ -4,6 +4,11 @@ weight = 10 max_occurrences = 2 min_players = 1 + category = EVENT_CATEGORY_HEALTH + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 2 + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL, TAG_MAGICAL) /datum/round_event/mass_hallucination fakeable = FALSE @@ -35,4 +40,4 @@ /datum/hallucination/delusion, /datum/hallucination/oh_yeah) for(var/mob/living/carbon/C in GLOB.alive_mob_list) - new picked_hallucination(C, TRUE) \ No newline at end of file + new picked_hallucination(C, TRUE) diff --git a/code/modules/events/meteors/dust.dm b/code/modules/events/meteors/dust.dm new file mode 100644 index 000000000000..758d0ec2731d --- /dev/null +++ b/code/modules/events/meteors/dust.dm @@ -0,0 +1,53 @@ +/datum/round_event_control/space_dust + name = "Space Dust: Minor" + typepath = /datum/round_event/space_dust + weight = 100 + max_occurrences = 10 + earliest_start = 0 MINUTES + alert_observers = FALSE + category = EVENT_CATEGORY_SPACE + description = "A single space dust is hurled at the station." + map_flags = EVENT_SPACE_ONLY + track = EVENT_TRACK_MUNDANE + tags = list(TAG_DESTRUCTIVE, TAG_SPACE, TAG_EXTERNAL) + +/datum/round_event/space_dust + start_when = 1 + end_when = 2 + fakeable = FALSE + +/datum/round_event/space_dust/start() + spawn_meteors(1, GLOB.meteors_dust) + +/datum/round_event_control/space_dust/major_dust + name = "Space Dust: Major" + typepath = /datum/round_event/space_dust/major_dust + weight = 8 + description = "The station is pelted by sand." + min_players = 15 + max_occurrences = 3 + earliest_start = 25 MINUTES + category = EVENT_CATEGORY_SPACE + min_wizard_trigger_potency = 4 + max_wizard_trigger_potency = 7 + +/datum/round_event/space_dust/major_dust + start_when = 6 + end_when = 66 + announce_when = 1 + +/datum/round_event/space_dust/major_dust/announce(fake) + var/reason = pick( + "The station is passing through a debris cloud, expect minor damage \ + to external fittings and fixtures.", + "Nanotrasen Superweapons Division is testing a new prototype \ + [pick("field","projection","nova","super-colliding","reactive")] \ + [pick("cannon","artillery","tank","cruiser","\[REDACTED\]")], \ + some mild debris is expected.", + "A neighbouring station is throwing rocks at you. (Perhaps they've \ + grown tired of your messages.)") + priority_announce(pick(reason), "Collision Alert") + +/datum/round_event/space_dust/major_dust/tick() + if(ISMULTIPLE(activeFor, 3)) + spawn_meteors(5, GLOB.meteors_dust) diff --git a/code/modules/events/meateor_wave.dm b/code/modules/events/meteors/meateor_wave.dm similarity index 100% rename from code/modules/events/meateor_wave.dm rename to code/modules/events/meteors/meateor_wave.dm diff --git a/code/modules/events/meteor_wave.dm b/code/modules/events/meteors/meteor_wave.dm similarity index 80% rename from code/modules/events/meteor_wave.dm rename to code/modules/events/meteors/meteor_wave.dm index 6a07b9913243..766f2aeaa074 100644 --- a/code/modules/events/meteor_wave.dm +++ b/code/modules/events/meteors/meteor_wave.dm @@ -4,14 +4,20 @@ name = "Meteor Wave: Normal" typepath = /datum/round_event/meteor_wave weight = 4 - min_players = 10 + min_players = 15 max_occurrences = 3 earliest_start = 25 MINUTES + category = EVENT_CATEGORY_SPACE + description = "A regular meteor wave." + map_flags = EVENT_SPACE_ONLY + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMMUNAL, TAG_SPACE, TAG_DESTRUCTIVE, TAG_EXTERNAL) + event_group = /datum/event_group/meteors /datum/round_event/meteor_wave - startWhen = 6 - endWhen = 66 - announceWhen = 1 + start_when = 6 + end_when = 66 + announce_when = 1 var/list/wave_type var/wave_name = "normal" @@ -32,14 +38,14 @@ if("threatening") wave_type = GLOB.meteors_threatening if("catastrophic") - if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + if(SSgamemode.holidays && SSgamemode.holidays[HALLOWEEN]) wave_type = GLOB.meteorsSPOOKY else wave_type = GLOB.meteors_catastrophic if("meaty") - wave_type = GLOB.meteorsB + wave_type = GLOB.meateors if("space dust") - wave_type = GLOB.meteorsC + wave_type = GLOB.meteors_dust if("halloween") wave_type = GLOB.meteorsSPOOKY else diff --git a/code/modules/events/meteors/stray_meteor_event.dm b/code/modules/events/meteors/stray_meteor_event.dm new file mode 100644 index 000000000000..4cb981183cca --- /dev/null +++ b/code/modules/events/meteors/stray_meteor_event.dm @@ -0,0 +1,46 @@ +/datum/round_event_control/stray_meteor + name = "Stray Meteor" + typepath = /datum/round_event/stray_meteor + weight = 15 //Number subject to change based on how often meteors actually collide with the station + min_players = 15 + max_occurrences = 3 + earliest_start = 20 MINUTES + category = EVENT_CATEGORY_SPACE + description = "Throw a random meteor somewhere near the station." + min_wizard_trigger_potency = 3 + max_wizard_trigger_potency = 7 + admin_setup = list(/datum/event_admin_setup/listed_options/stray_meteor) + map_flags = EVENT_SPACE_ONLY + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_SPACE, TAG_EXTERNAL) + event_group = /datum/event_group/debris + +/datum/round_event/stray_meteor + announce_when = 1 + fakeable = FALSE //Already faked by meteors that miss + ///The selected meteor type if chosen through admin setup. + var/chosen_meteor + +/datum/round_event/stray_meteor/start() + if(chosen_meteor) + var/list/chosen_meteor_list = list() + chosen_meteor_list[chosen_meteor] = 1 + spawn_meteor(chosen_meteor_list) + else + spawn_meteor(GLOB.meteors_stray) + +/datum/round_event/stray_meteor/announce(fake) + if(GLOB.meteor_list) + var/obj/effect/meteor/detected_meteor = pick(GLOB.meteor_list) //If we accidentally pick a meteor not spawned by the event, we're still technically not wrong + var/sensor_name = detected_meteor.signature + priority_announce("Our [sensor_name] sensors have detected an incoming signature approaching [GLOB.station_name]. Please brace for impact.", "Meteor Alert") + +/datum/event_admin_setup/listed_options/stray_meteor + input_text = "Select a meteor type?" + normal_run_option = "Random Meteor" + +/datum/event_admin_setup/listed_options/stray_meteor/get_list() + return subtypesof(/obj/effect/meteor) + +/datum/event_admin_setup/listed_options/stray_meteor/apply_to_event(datum/round_event/stray_meteor/event) + event.chosen_meteor = chosen diff --git a/code/modules/events/mice_migration.dm b/code/modules/events/mice_migration.dm index fdd32cd7d756..65cfe89465fc 100644 --- a/code/modules/events/mice_migration.dm +++ b/code/modules/events/mice_migration.dm @@ -2,7 +2,11 @@ name = "Mice Migration" typepath = /datum/round_event/mice_migration weight = 10 - max_alert = SEC_LEVEL_DELTA + category = EVENT_CATEGORY_ENTITIES + description = "A horde of mice arrives, and perhaps even the Rat King themselves." + track = EVENT_TRACK_MUNDANE + tags = list(TAG_DESTRUCTIVE, TAG_ALIEN) //not really alien but rat lords kind of are + event_group = /datum/event_group/guests /datum/round_event/mice_migration var/minimum_mice = 5 diff --git a/code/modules/events/monsterhunter.dm b/code/modules/events/monsterhunter.dm deleted file mode 100644 index 214987a14ff4..000000000000 --- a/code/modules/events/monsterhunter.dm +++ /dev/null @@ -1,100 +0,0 @@ -/* - * MONSTER HUNTERS: - * Their job is to hunt Monsters. - * They spawn by default 35 minutes into a Bloodsucker round, - * They also randomly spawn in other rounds, as some unique flavor. - * They can also be used as Admin-only antags during rounds such as; - * - Changeling murderboning rounds - * - Lategame Cult round - * - Ect. - */ - -/// The default, for Bloodsucker rounds. -/datum/round_event_control/bloodsucker_hunters - name = "Spawn Monster Hunter - Bloodsucker" - typepath = /datum/round_event/bloodsucker_hunters - max_occurrences = 1 // We have to see how Bloodsuckers are in game to decide if having more than 1 is beneficial. - weight = 20 - min_players = 10 - earliest_start = 35 MINUTES - gamemode_whitelist = list("bloodsucker","traitorsucker") - -/datum/round_event/bloodsucker_hunters - fakeable = FALSE - var/cancel_me = TRUE - -/datum/round_event/bloodsucker_hunters/start() - for(var/mob/living/carbon/human/all_players in GLOB.player_list) - if(IS_BLOODSUCKER(all_players)) - message_admins("BLOODSUCKER NOTICE: Monster Hunters found a valid Bloodsucker.") - cancel_me = FALSE - break - if(cancel_me) - kill() - return - for(var/mob/living/carbon/human/all_players in shuffle(GLOB.player_list)) - if(!all_players.client || !all_players.mind || !(ROLE_MONSTERHUNTER in all_players.client.prefs.be_special)) - continue - if(all_players.mind.quiet_round) - continue - if(all_players.stat == DEAD) - continue - if(!SSjob.GetJob(all_players.mind.assigned_role) || (all_players.mind.assigned_role in GLOB.nonhuman_positions)) // Only crewmembers on-station. - continue - if(!SSjob.GetJob(all_players.mind.assigned_role) || (all_players.mind.assigned_role in GLOB.command_positions)) - continue - if(!SSjob.GetJob(all_players.mind.assigned_role) || (all_players.mind.assigned_role in GLOB.security_positions)) - continue - if(IS_BLOODSUCKER(all_players) || IS_VASSAL(all_players) || IS_HERETIC(all_players) || iscultist(all_players) || iswizard(all_players) || is_servant_of_ratvar(all_players) || all_players.mind.has_antag_datum(/datum/antagonist/changeling)) - continue - if(!all_players.getorgan(/obj/item/organ/brain)) - continue - all_players.mind.add_antag_datum(/datum/antagonist/monsterhunter) - message_admins("BLOODSUCKER NOTICE: [all_players] has awoken as a Monster Hunter.") - announce_to_ghosts(all_players) - break - -/// Randomly spawned Monster hunters during TraitorChangeling, Changeling, Heretic and Cult rounds. -/datum/round_event_control/monster_hunters - name = "Spawn Monster Hunter" - typepath = /datum/round_event/monster_hunters - max_occurrences = 1 - weight = 7 - min_players = 10 - earliest_start = 25 MINUTES - gamemode_whitelist = list("traitorchan","changeling","heresy","cult","clockwork_cult") - -/datum/round_event/monster_hunters - fakeable = FALSE - var/cancel_me = TRUE - -/datum/round_event/monster_hunters/start() - for(var/mob/living/carbon/human/all_players in GLOB.player_list) - if(iscultist(all_players) || IS_HERETIC(all_players) || iswizard(all_players) || is_servant_of_ratvar(all_players) || all_players.mind.has_antag_datum(/datum/antagonist/changeling)) - message_admins("MONSTERHUNTER NOTICE: Monster Hunters found a valid Monster.") - cancel_me = FALSE - break - if(cancel_me) - kill() - return - for(var/mob/living/carbon/human/all_players in shuffle(GLOB.player_list)) - /// From obsessed - if(!all_players.client || !all_players.mind || !(ROLE_MONSTERHUNTER in all_players.client.prefs.be_special)) - continue - if(all_players.stat == DEAD) - continue - if(!SSjob.GetJob(all_players.mind.assigned_role) || (all_players.mind.assigned_role in GLOB.nonhuman_positions)) // Only crewmembers on-station. - continue - if(!SSjob.GetJob(all_players.mind.assigned_role) || (all_players.mind.assigned_role in GLOB.command_positions)) - continue - if(!SSjob.GetJob(all_players.mind.assigned_role) || (all_players.mind.assigned_role in GLOB.security_positions)) - continue - /// Bobux no IS_CHANGELING - if(IS_HERETIC(all_players) || iscultist(all_players) || iswizard(all_players) || is_servant_of_ratvar(all_players) || all_players.mind.has_antag_datum(/datum/antagonist/changeling)) - continue - if(!all_players.getorgan(/obj/item/organ/brain)) - continue - all_players.mind.add_antag_datum(/datum/antagonist/monsterhunter) - message_admins("MONSTERHUNTER NOTICE: [all_players] has awoken as a Monster Hunter.") - announce_to_ghosts(all_players) - break diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm index dbc875a3da2e..ab747cd2908d 100644 --- a/code/modules/events/portal_storm.dm +++ b/code/modules/events/portal_storm.dm @@ -1,23 +1,15 @@ -/datum/round_event/portal_storm - startWhen = 7 - endWhen = 999 - announceWhen = 1 - - var/list/boss_spawn = list() - var/list/boss_types = list() //only configure this if you have hostiles - var/number_of_bosses - var/next_boss_spawn - var/list/hostiles_spawn = list() - var/list/hostile_types = list() - var/number_of_hostiles - var/mutable_appearance/storm - /datum/round_event_control/portal_storm_syndicate name = "Portal Storm: Syndicate Shocktroops" typepath = /datum/round_event/portal_storm/syndicate_shocktroop weight = 2 min_players = 15 + max_occurrences = 1 //MONKESTATION ADDITION earliest_start = 30 MINUTES + category = EVENT_CATEGORY_ENTITIES + description = "Syndicate troops pour out of portals." + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_EXTERNAL) + event_group = /datum/event_group/guests /datum/round_event/portal_storm/syndicate_shocktroop boss_types = list(/mob/living/simple_animal/hostile/syndicate/melee/space/stormtrooper = 2) @@ -29,15 +21,38 @@ typepath = /datum/round_event/portal_storm/portal_storm_narsie weight = 0 max_occurrences = 0 + category = EVENT_CATEGORY_ENTITIES + description = "Nar'sie constructs pour out of portals." + min_wizard_trigger_potency = 5 + max_wizard_trigger_potency = 7 /datum/round_event/portal_storm/portal_storm_narsie boss_types = list(/mob/living/simple_animal/hostile/construct/builder = 6) hostile_types = list(/mob/living/simple_animal/hostile/construct/armored/hostile = 8,\ /mob/living/simple_animal/hostile/construct/wraith/hostile = 6) +/datum/round_event/portal_storm + start_when = 7 + end_when = 999 + announce_when = 1 + + var/list/boss_spawn = list() + var/list/boss_types = list() //only configure this if you have hostiles + var/number_of_bosses + var/next_boss_spawn + var/list/hostiles_spawn = list() + var/list/hostile_types = list() + var/number_of_hostiles + /// List of mutable appearances in the form (plane offset + 1 -> appearance) + var/list/mutable_appearance/storm_appearances + /datum/round_event/portal_storm/setup() - storm = mutable_appearance('icons/obj/tesla_engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER) - storm.color = "#00FF00" + storm_appearances = list() + for(var/offset in 0 to SSmapping.max_plane_offset) + var/mutable_appearance/storm = mutable_appearance('icons/obj/tesla_engine/energy_ball.dmi', "energy_ball_fast", FLY_LAYER) + SET_PLANE_W_SCALAR(storm, ABOVE_GAME_PLANE, offset) + storm.color = "#00FF00" + storm_appearances += storm number_of_bosses = 0 for(var/boss in boss_types) @@ -48,47 +63,47 @@ number_of_hostiles += hostile_types[hostile] while(number_of_bosses > boss_spawn.len) - boss_spawn += get_random_station_turf() + boss_spawn += get_safe_random_station_turf() // monkestation edit: use [get_safe_random_station_turf] so they don't spawn in weird stupid places while(number_of_hostiles > hostiles_spawn.len) - hostiles_spawn += get_random_station_turf() + hostiles_spawn += get_safe_random_station_turf() // monkestation edit: use [get_safe_random_station_turf] so they don't spawn in weird stupid places - next_boss_spawn = startWhen + CEILING(2 * number_of_hostiles / number_of_bosses, 1) + next_boss_spawn = start_when + CEILING(2 * number_of_hostiles / number_of_bosses, 1) + setup = TRUE //MONKESTATION ADDITION /datum/round_event/portal_storm/announce(fake) set waitfor = 0 - sound_to_playing_players('sound/magic/lightning_chargeup.ogg', volume = 50) + sound_to_playing_players('sound/magic/lightning_chargeup.ogg') sleep(8 SECONDS) priority_announce("Massive bluespace anomaly detected en route to [station_name()]. Brace for impact.") sleep(2 SECONDS) - sound_to_playing_players('sound/magic/lightningbolt.ogg', volume = 50) + sound_to_playing_players('sound/magic/lightningbolt.ogg') /datum/round_event/portal_storm/tick() - var/turf/T = get_safe_random_station_turf() + spawn_effects(get_safe_random_station_turf()) // monkestation edit: use [get_safe_random_station_turf] so they don't spawn in weird stupid places - if(spawn_hostile()) + if(spawn_hostile() && length(hostile_types)) var/type = pick(hostile_types) hostile_types[type] = hostile_types[type] - 1 - spawn_mob(T, type, hostiles_spawn) + spawn_mob(type, hostiles_spawn) if(!hostile_types[type]) hostile_types -= type - if(spawn_boss()) + if(spawn_boss() && length(boss_types)) var/type = pick(boss_types) boss_types[type] = boss_types[type] - 1 - spawn_mob(T, type, boss_spawn) + spawn_mob(type, boss_spawn) if(!boss_types[type]) boss_types -= type time_to_end() -/datum/round_event/portal_storm/proc/spawn_mob(turf/T, type, spawn_list) +/datum/round_event/portal_storm/proc/spawn_mob(type, spawn_list) if(!type) - log_game("Portal Storm failed to spawn mob due to an invalid mob type.") - CRASH("Cannot spawn null type!") + return + var/turf/T = pick_n_take(spawn_list) if(!T) - log_game("Portal Storm failed to spawn mob due to an invalid location.") - CRASH("Cannot spawn mobs on null turf!") + return new type(T) spawn_effects(T) @@ -97,8 +112,8 @@ log_game("Portal Storm failed to spawn effect due to an invalid location.") return T = get_step(T, SOUTHWEST) //align center of image with turf - flick_overlay_static(storm, T, 15) - playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), 1) + flick_overlay_static(storm_appearances[GET_TURF_PLANE_OFFSET(T) + 1], T, 15) + playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), TRUE) /datum/round_event/portal_storm/proc/spawn_hostile() if(!hostile_types || !hostile_types.len) @@ -107,18 +122,21 @@ /datum/round_event/portal_storm/proc/spawn_boss() if(!boss_types || !boss_types.len) - return 0 + return FALSE if(activeFor == next_boss_spawn) next_boss_spawn += CEILING(number_of_hostiles / number_of_bosses, 1) - return 1 + return TRUE + return FALSE /datum/round_event/portal_storm/proc/time_to_end() if(!hostile_types.len && !boss_types.len) - endWhen = activeFor + end_when = activeFor if(!number_of_hostiles && number_of_bosses) - endWhen = activeFor + end_when = activeFor + + // Resonance cascade event, happens after an antinoblium delamination. Better call the deathsquad. /datum/round_event_control/resonance_cascade @@ -126,8 +144,6 @@ typepath = /datum/round_event/portal_storm/resonance_cascade weight = 0 max_occurrences = 0 - random = FALSE // No. - max_alert = SEC_LEVEL_DELTA /datum/round_event/portal_storm/resonance_cascade boss_types = list( @@ -162,7 +178,7 @@ ANOMALY_HALLUCINATION = 140, ANOMALY_GRAVITATIONAL = 160, ) - endWhen = INFINITY // keep going until it's done + end_when = INFINITY // keep going until it's done /datum/round_event/portal_storm/resonance_cascade/start() . = ..() diff --git a/code/modules/events/processor_overload.dm b/code/modules/events/processor_overload.dm index 741ae0092cde..6485e825c840 100644 --- a/code/modules/events/processor_overload.dm +++ b/code/modules/events/processor_overload.dm @@ -3,9 +3,15 @@ typepath = /datum/round_event/processor_overload weight = 15 min_players = 20 + category = EVENT_CATEGORY_ENGINEERING + description = "Emps the telecomm processors, scrambling radio speech. Might blow up a few." + max_occurrences = 2 + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL) + event_group = /datum/event_group/comms /datum/round_event/processor_overload - announceWhen = 1 + announce_when = 1 /datum/round_event/processor_overload/announce(fake) var/alert = pick( "Exospheric bubble inbound. Processor overload is likely. Please contact you*%xp25)`6cq-BZZT", \ diff --git a/code/modules/events/radiation_leak.dm b/code/modules/events/radiation_leak.dm new file mode 100644 index 000000000000..36ac346c92f2 --- /dev/null +++ b/code/modules/events/radiation_leak.dm @@ -0,0 +1,174 @@ +/datum/round_event_control/radiation_leak + name = "Radiation Leak" + description = "A radiation leak happens somewhere on the station, emanating radiation around a machine in the area. \ + Engineering can stop the leak by using certain tools on it." + typepath = /datum/round_event/radiation_leak + weight = 15 + max_occurrences = 3 + category = EVENT_CATEGORY_ENGINEERING + min_wizard_trigger_potency = 3 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL) + +/datum/round_event/radiation_leak + start_when = 1 // 2 seconds in + announce_when = 10 // 20 seconds in + end_when = 150 // 300 seconds / ~5 minutes in + + /// Weakref to the machine spitting out rads + var/datum/weakref/picked_machine_ref + /// List of signals added to the picked machine, so we can clear them later + var/list/signals_to_add + +/datum/round_event/radiation_leak/setup() + // Pick a generic event spawn somewhere in the world. + // We will try to find a machine within a few turfs of it to start spewing rads from. + var/list/possible_locs = GLOB.generic_event_spawns.Copy() + while(length(possible_locs)) + var/turf/chosen_loc = get_turf(pick_n_take(possible_locs)) + for(var/obj/machinery/sick_device in range(3, chosen_loc)) + // Excludes machines that don't use power, as these are usually non-machine machinery + if(sick_device.use_power == NO_POWER_USE) + continue + // Look for dense machinery. Basically stops stuff like wall mounts and pipes, silly ones. + // But keep in vents and scrubbers. I think it's funny if they start spitting out radiation + if(!sick_device.density && !istype(sick_device, /obj/machinery/atmospherics/components/unary)) + continue + // Skip invisible stuff + if(sick_device.invisibility || !sick_device.alpha || !sick_device.mouse_opacity) + continue + // Skip undertiles + if(sick_device.IsObscured()) + continue + // Very basic check for atmos passability here + // We don't want to put our smoke inside something that can't spread it out, like airlocks + if(sick_device.can_atmos_pass() != ATMOS_PASS_YES) + continue + + // We found something, we can just return now + picked_machine_ref = WEAKREF(sick_device) + setup = TRUE //MONKESTATION ADDITION + return + +/datum/round_event/radiation_leak/announce(fake) + var/obj/machinery/the_source_of_our_problems = picked_machine_ref?.resolve() + var/area/location_descriptor + + if(fake) + location_descriptor = GLOB.areas_by_type[pick(GLOB.the_station_areas)] + + else if(the_source_of_our_problems) + location_descriptor = get_area(the_source_of_our_problems) + + priority_announce("A radiation leak has been detected in [location_descriptor || "an unknown area"]. \ + All crew are to evacuate the affected area. Our [pick("mechanics", "engineers", "scientists", "interns", "sensors", "readings")] \ + report that a machine within is causing it - repair it quickly to stop the leak.", "[command_name()] Engineering Division") + +/datum/round_event/radiation_leak/start() + var/obj/machinery/the_source_of_our_problems = picked_machine_ref?.resolve() + if(!the_source_of_our_problems) + return + + // We'll add some tool acts to the thing that allow people to "repair the machine" + // The key of this assoc list is the "method" of how they're fixing the thing (just flavor for examine), + // and the value is what tool they actually need to use on the thing to fix it + var/list/how_do_we_fix_it = list( + "wrenching a few valves" = TOOL_WRENCH, + "tightening its bolts" = TOOL_WRENCH, + "crowbaring its panel [pick("down", "up")]" = TOOL_CROWBAR, + "tightening some screws" = TOOL_SCREWDRIVER, + "checking its [pick("wires", "circuits")]" = TOOL_MULTITOOL, + "welding its panel [pick("open", "shut")]" = TOOL_WELDER, + "analyzing its readings" = TOOL_ANALYZER, + "cutting some excess wires" = TOOL_WIRECUTTER, + ) + var/list/fix_it_keys = assoc_to_keys(how_do_we_fix_it) // Returns a copy that we can pick and take from, fortunately + + // Select a few methods of how to fix it + var/list/methods_to_fix = list() + for(var/i in 1 to rand(1, 3)) + methods_to_fix += pick_n_take(fix_it_keys) + + // Construct the signals + signals_to_add = list() + for(var/tool_method in methods_to_fix) + signals_to_add += COMSIG_ATOM_TOOL_ACT(how_do_we_fix_it[tool_method]) + + the_source_of_our_problems.visible_message(span_danger("[the_source_of_our_problems] starts to emanate a horrible green gas!")) + // Add the component that makes the thing radioactive + the_source_of_our_problems.AddComponent( + /datum/component/radioactive_emitter, \ + cooldown_time = 2 SECONDS, \ + power = 500, \ + examine_text = span_green("It's emanating a green gas... You could probably stop it by [english_list(methods_to_fix, and_text = " or ")]."), \ + ) + // Register signals to make it fixable + if(length(signals_to_add)) + RegisterSignals(the_source_of_our_problems, signals_to_add, PROC_REF(on_machine_tooled)) + + // And yknow puffs some nasty reagents into the air, just to seal the deal + puff_some_smoke(the_source_of_our_problems) + // Let ghosts know + announce_to_ghosts(the_source_of_our_problems) + +/datum/round_event/radiation_leak/tick() + // Puff some smoke into the air around our machine roughly 3 times before we stop + if(activeFor % (end_when / 3) != 0) + return + + var/obj/machinery/impromptu_smoke_machine = picked_machine_ref?.resolve() + if(!impromptu_smoke_machine) + return + + puff_some_smoke(impromptu_smoke_machine) + +/datum/round_event/radiation_leak/end() + var/obj/machinery/the_end_of_our_problems = picked_machine_ref?.resolve() + if(!the_end_of_our_problems) + return + + the_end_of_our_problems.visible_message(span_notice("The gas emanating from [the_end_of_our_problems] dissipates.")) + qdel(the_end_of_our_problems.GetComponent(/datum/component/radioactive_emitter)) + if(length(signals_to_add)) + UnregisterSignal(the_end_of_our_problems, signals_to_add) + picked_machine_ref = null + signals_to_add = null + +/// Helper to shoot some smoke into the air around the passed atom +/datum/round_event/radiation_leak/proc/puff_some_smoke(atom/where) + var/turf/below_where = get_turf(where) + var/datum/effect_system/fluid_spread/smoke/chem/gross_smoke = new() + gross_smoke.chemholder.add_reagent(/datum/reagent/toxin/polonium, 10) // Polonium (it causes radiation) + gross_smoke.chemholder.add_reagent(/datum/reagent/toxin/mutagen, 10) // Mutagen (it causes mutations. Also it's green... Primarily because it's green.) + gross_smoke.attach(below_where) + gross_smoke.set_up(2, null, where, below_where, silent = TRUE) + gross_smoke.start() + playsound(below_where, 'sound/effects/smoke.ogg', 50, vary = TRUE) + +/** + * Signal proc for [COMSIG_ATOM_TOOL_ACT], from a variety of signals, registered on the machine spitting radiation + * + * We allow for someone to stop the event early by using the proper tools, hinted at in examine, on the machine + */ +/datum/round_event/radiation_leak/proc/on_machine_tooled(obj/machinery/source, mob/living/user, obj/item/tool) + SIGNAL_HANDLER + + INVOKE_ASYNC(src, PROC_REF(try_remove_radiation), source, user, tool) + return COMPONENT_BLOCK_TOOL_ATTACK + +/// Attempts a do_after, and if successful, stops the event +/datum/round_event/radiation_leak/proc/try_remove_radiation(obj/machinery/source, mob/living/user, obj/item/tool) + source.balloon_alert(user, "fixing leak...") + // Fairly long do after. It shouldn't be SUPER easy to just run in and stop it. + // A tider can fix it if they want to soak a bunch of rads and inhale noxious fumes, + // but only an equipped engineer should be able to handle it painlessly. + if(!tool.use_tool(source, user, 30 SECONDS, amount = (tool.tool_behaviour == TOOL_WELDER ? 2 : 0), volume = 50)) + source.balloon_alert(user, "interrupted!") + return + + source.balloon_alert(user, "leak repaired") + // Force end the event + processing = FALSE + end() + kill() diff --git a/code/modules/events/radiation_storm.dm b/code/modules/events/radiation_storm.dm index 720fbd7eac22..b7b3932a5684 100644 --- a/code/modules/events/radiation_storm.dm +++ b/code/modules/events/radiation_storm.dm @@ -1,7 +1,22 @@ /datum/round_event_control/radiation_storm name = "Radiation Storm" typepath = /datum/round_event/radiation_storm - max_occurrences = 2 + max_occurrences = 2 //monkestation edit - STORYTELLERS 1 ==> 2 + category = EVENT_CATEGORY_SPACE + description = "Radiation storm affects the station, forcing the crew to escape to maintenance." + min_wizard_trigger_potency = 3 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL) + +/datum/round_event/radiation_storm + + +/datum/round_event/radiation_storm/setup() + start_when = 3 + end_when = start_when + 1 + announce_when = 1 + setup = TRUE //MONKESTATION ADDITION /datum/round_event/radiation_storm/announce(fake) priority_announce("High levels of radiation detected near the station. Maintenance is best shielded from radiation.", "Anomaly Alert", ANNOUNCER_RADIATION) @@ -10,4 +25,4 @@ //sound not longer matches the text, but an audible warning is probably good /datum/round_event/radiation_storm/start() - SSweather.run_weather(/datum/weather/rad_storm)//only the station has protected areas, so only target the station. also "detected near the station" + SSweather.run_weather(/datum/weather/rad_storm) diff --git a/code/modules/events/sandstorm.dm b/code/modules/events/sandstorm.dm new file mode 100644 index 000000000000..1ff10fc3f8cf --- /dev/null +++ b/code/modules/events/sandstorm.dm @@ -0,0 +1,109 @@ +/** + * Sandstorm Event: Throws dust/sand at one side of the station. High-intensity and relatively short, + * however the incoming direction is given along with time to prepare. Damages can be reduced or + * mitigated with a few people actively working to fix things as the storm hits, but leaving the event to run on its own can lead to widespread breaches. + * + * Meant to be encountered mid-round, with enough spare manpower among the crew to properly respond. + * Anyone with a welder or metal can contribute. + */ + +/datum/round_event_control/sandstorm + name = "Sandstorm: Directional" + typepath = /datum/round_event/sandstorm + max_occurrences = 3 + min_players = 30 + earliest_start = 35 MINUTES + category = EVENT_CATEGORY_SPACE + description = "A wave of space dust continually grinds down a side of the station." + min_wizard_trigger_potency = 6 + max_wizard_trigger_potency = 7 + admin_setup = list(/datum/event_admin_setup/listed_options/sandstorm) + map_flags = EVENT_SPACE_ONLY + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_EXTERNAL) + event_group = /datum/event_group/debris + +/datum/round_event/sandstorm + start_when = 60 + end_when = 100 + announce_when = 1 + ///Which direction the storm will come from. + var/start_side + +/datum/round_event/sandstorm/setup() + start_when = rand(70, 90) + end_when = rand(110, 140) + setup = TRUE //MONKESTATION ADDITION + +/datum/round_event/sandstorm/announce(fake) + if(!start_side) + start_side = pick(GLOB.cardinals) + + /* monkestation start: use normal direction names + var/start_side_text = "unknown" + switch(start_side) + if(NORTH) + start_side_text = "fore" + if(SOUTH) + start_side_text = "aft" + if(EAST) + start_side_text = "starboard" + if(WEST) + start_side_text = "port" + else + stack_trace("Sandstorm event given [start_side] as unrecognized direction. Cancelling event...") + kill() + return + monkestation end */ + + // monkestation edit: use normal direction names + priority_announce("A large wave of space dust is approaching from the [dir2text(start_side)] side of the station. \ + Impact is expected in the next two minutes. All employees are encouranged to assist in repairs and damage mitigation if possible.", "Collision Emergency Alert") + +/datum/round_event/sandstorm/tick() + spawn_meteors(15, GLOB.meteors_sandstorm, start_side) + +/** + * The original sandstorm event. An admin-only disasterfest that sands down all sides of the station + * Uses space dust, meaning walls/rwalls are quickly chewed up very quickly. + * + * Super dangerous, super funny, preserved for future admin use in case the new event reminds + * them that this exists. It is unchanged from its original form and is arguably perfect. + */ + +/datum/round_event_control/sandstorm_classic + name = "Sandstorm: Classic" + typepath = /datum/round_event/sandstorm_classic + weight = 0 + max_occurrences = 0 + earliest_start = 0 MINUTES + category = EVENT_CATEGORY_SPACE + description = "The station is pelted by an extreme amount of dust, from all sides, for several minutes. Very destructive and likely to cause lag. Use at own risk." + map_flags = EVENT_SPACE_ONLY + +/datum/round_event/sandstorm_classic + start_when = 1 + end_when = 150 // ~5 min //I don't think this actually lasts 5 minutes unless you're including the lag it induces + announce_when = 0 + fakeable = FALSE + +/datum/round_event/sandstorm_classic/tick() + spawn_meteors(10, GLOB.meteors_dust) + +/datum/event_admin_setup/listed_options/sandstorm + input_text = "Choose a side to powersand?" + normal_run_option = "Random Sandstorm Direction" + +/datum/event_admin_setup/listed_options/sandstorm/get_list() + return list("Up", "Down", "Right", "Left") + +/datum/event_admin_setup/listed_options/sandstorm/apply_to_event(datum/round_event/sandstorm/event) + switch(chosen) + if("Up") + event.start_side = NORTH + if("Down") + event.start_side = SOUTH + if("Right") + event.start_side = EAST + if("Left") + event.start_side = WEST diff --git a/code/modules/events/scrubber_clog.dm b/code/modules/events/scrubber_clog.dm new file mode 100644 index 000000000000..5b25a9cc7370 --- /dev/null +++ b/code/modules/events/scrubber_clog.dm @@ -0,0 +1,226 @@ +// /datum/round_event_control/scrubber_clog +// name = "Scrubber Clog: Minor" +// typepath = /datum/round_event/scrubber_clog +// weight = 25 +// max_occurrences = 3 +// earliest_start = 5 MINUTES +// category = EVENT_CATEGORY_JANITORIAL +// description = "Harmless mobs climb out of a scrubber." +// track = EVENT_TRACK_MUNDANE +// tags = list(TAG_COMMUNAL, TAG_ALIEN, TAG_MAGICAL) +// event_group = /datum/event_group/guests + +// /datum/round_event/scrubber_clog +// announce_when = 10 +// start_when = 5 +// end_when = 600 + +// ///Scrubber selected for the event. +// var/obj/machinery/atmospherics/components/unary/vent_scrubber/scrubber +// ///What mob will be spawned +// var/mob/spawned_mob = /mob/living/basic/cockroach +// ///Cap on the number of spawned mobs that can be alive at once. +// var/maximum_spawns = 3 +// ///Interval between mob spawns. +// var/spawn_delay = 10 +// ///Used to track/limit produced mobs. +// var/list/living_mobs = list() +// ///Used for tracking if the clog signal should be sent. +// var/clogged = TRUE + +// /datum/round_event/scrubber_clog/announce() +// priority_announce("Minor biological obstruction detected in the ventilation network. Blockage is believed to be in the [get_area_name(scrubber)].", "Custodial Notification") + +// /datum/round_event/scrubber_clog/setup() +// scrubber = get_scrubber() +// if(!scrubber) +// kill() +// CRASH("Unable to find suitable scrubber.") + +// RegisterSignal(scrubber, COMSIG_QDELETING, PROC_REF(scrubber_move)) + +// spawned_mob = get_mob() +// end_when = rand(300, 600) +// maximum_spawns = rand(3, 5) +// spawn_delay = rand(10, 15) +// setup = TRUE //MONKESTATION ADDITION + +// /datum/round_event/scrubber_clog/start() //Sets the scrubber up for unclogging/mob production. +// scrubber.clog() +// scrubber.produce_mob(spawned_mob, living_mobs) //The first one's free! +// announce_to_ghosts(scrubber) + +// /datum/round_event/scrubber_clog/tick() //Checks if spawn_interval is met, then sends signal to scrubber to produce a mob. +// if(activeFor % spawn_delay == 0 && scrubber.clogged) +// life_check() +// if(living_mobs.len < maximum_spawns && clogged) +// scrubber.produce_mob(spawned_mob, living_mobs) + +// /datum/round_event/scrubber_clog/end() //No end announcement. If you want to take the easy way out and just leave the vent welded, you must open it at your own peril. +// scrubber.unclog() +// scrubber = null +// living_mobs.Cut() + +// /** +// * Selects which mob will be spawned for a given scrubber clog event. +// * +// * Creates a static list of mobs, which is different based on the severity of the event being run, and returns a pick() of it. +// */ + +// /datum/round_event/scrubber_clog/proc/get_mob() +// var/static/list/mob_list = list( +// /mob/living/basic/butterfly, +// /mob/living/basic/cockroach, +// /mob/living/basic/mouse, +// ) +// return pick(mob_list) + +// /** +// * Finds a valid scrubber for the scrubber clog event. +// * +// * For every scrubber in the round, checks if the scrubber turf is on-station, and is neither welded nor already clogged, and +// * adds it to a list. A random scrubber is picked from this list, and returned as the scrubber that will be used for the event. +// */ + +// /datum/round_event/scrubber_clog/proc/get_scrubber() +// var/list/scrubber_list = list() +// for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/scrubber in GLOB.machines) +// var/turf/scrubber_turf = get_turf(scrubber) +// if(scrubber_turf && is_station_level(scrubber_turf.z) && !scrubber.welded && !scrubber.clogged) +// scrubber_list += scrubber +// return pick(scrubber_list) + +// /datum/round_event_control/scrubber_clog/can_spawn_event(players_amt, allow_magic = FALSE, fake_check = FALSE) //MONKESTATION ADDITION: fake_check = FALSE +// . = ..() +// if(!.) +// return +// for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/scrubber in GLOB.machines) +// var/turf/scrubber_turf = get_turf(scrubber) +// if(scrubber_turf && is_station_level(scrubber_turf.z) && !scrubber.welded && !scrubber.clogged) +// return TRUE //make sure we have a valid scrubber to spawn from. +// return FALSE + +// /** +// * Checks which mobs in the mob spawn list are alive. +// * +// * Checks each mob in the living_mobs list, to see if they're dead or not. If dead, they're removed from the list. +// * This is used to keep new mobs spawning as the old ones die. +// */ + +// /datum/round_event/scrubber_clog/proc/life_check() +// for(var/datum/weakref/mob_ref as anything in living_mobs) +// var/mob/living/real_mob = mob_ref.resolve() +// if(QDELETED(real_mob) || real_mob.stat == DEAD) +// living_mobs -= mob_ref + +// /** +// * Finds a new scrubber for the event if the original is destroyed. +// * +// * This is used when the scrubber for the event is destroyed. It picks a new scrubber and announces that the event has moved elsewhere. +// * Handles the scrubber ref if there are no valid scrubbers to replace it with. +// */ + +// /datum/round_event/scrubber_clog/proc/scrubber_move(datum/source) +// SIGNAL_HANDLER +// scrubber = null //If by some great calamity, the last valid scrubber is destroyed, the ref is cleared. +// scrubber = get_scrubber() +// if(!scrubber) +// kill() +// CRASH("Unable to find suitable scrubber.") + +// RegisterSignal(scrubber, COMSIG_QDELETING, PROC_REF(scrubber_move)) + +// scrubber.clog() +// scrubber.produce_mob(spawned_mob, living_mobs) + +// announce_to_ghosts(scrubber) +// priority_announce("Lifesign readings have moved to a new location in the ventilation network. New Location: [prob(50) ? "Unknown.":"[get_area_name(scrubber)]."]", "Lifesign Notification") + +// /datum/round_event_control/scrubber_clog/major +// name = "Scrubber Clog: Major" +// typepath = /datum/round_event/scrubber_clog/major +// weight = 12 +// max_occurrences = 3 +// earliest_start = 10 MINUTES +// description = "Dangerous mobs climb out of a scrubber." +// min_wizard_trigger_potency = 0 +// max_wizard_trigger_potency = 4 + +// /datum/round_event/scrubber_clog/major/setup() +// . = ..() +// maximum_spawns = rand(2,4) +// spawn_delay = rand(15,20) + +// /datum/round_event/scrubber_clog/major/get_mob() +// var/static/list/mob_list = list( +// /mob/living/basic/mouse/rat, +// /mob/living/basic/bee, +// /mob/living/basic/spider/giant, +// ) +// return pick(mob_list) + +// /datum/round_event/scrubber_clog/major/announce() +// priority_announce("Major biological obstruction detected in the ventilation network. Blockage is believed to be in the [get_area_name(scrubber)] area.", "Infestation Alert") + +// /datum/round_event_control/scrubber_clog/critical +// name = "Scrubber Clog: Critical" +// typepath = /datum/round_event/scrubber_clog/critical +// weight = 8 +// min_players = 15 +// max_occurrences = 1 +// earliest_start = 25 MINUTES +// description = "Really dangerous mobs climb out of a scrubber." +// min_wizard_trigger_potency = 3 +// max_wizard_trigger_potency = 6 +// track = EVENT_TRACK_MAJOR +// tags = list(TAG_COMMUNAL, TAG_COMBAT, TAG_EXTERNAL, TAG_ALIEN, TAG_MAGICAL) + +// /datum/round_event/scrubber_clog/critical +// maximum_spawns = 3 + +// /datum/round_event/scrubber_clog/critical/setup() +// . = ..() +// spawn_delay = rand(15,25) + +// /datum/round_event/scrubber_clog/critical/announce() +// priority_announce("Potentially hazardous lifesigns detected in the [get_area_name(scrubber)] ventilation network.", "Security Alert") + +// /datum/round_event/scrubber_clog/critical/get_mob() +// var/static/list/mob_list = list( +// /mob/living/basic/carp, +// /mob/living/basic/bee/toxin, +// /mob/living/basic/cockroach/glockroach, +// /mob/living/basic/bear, //monkestation addition +// ) +// return pick(mob_list) + +// /datum/round_event_control/scrubber_clog/strange +// name = "Scrubber Clog: Strange" +// typepath = /datum/round_event/scrubber_clog/strange +// weight = 5 +// max_occurrences = 1 +// description = "Strange mobs climb out of a scrubber, harmfulness varies." +// min_wizard_trigger_potency = 0 +// max_wizard_trigger_potency = 7 + +// /datum/round_event/scrubber_clog/strange +// maximum_spawns = 3 + +// /datum/round_event/scrubber_clog/strange/setup() +// . = ..() +// end_when = rand(600, 720) +// spawn_delay = rand(6, 25) //Wide range, for maximum utility/comedy + +// /datum/round_event/scrubber_clog/strange/announce() +// priority_announce("Unusual lifesign readings detected in the [get_area_name(scrubber)] ventilation network.", "Lifesign Alert", ANNOUNCER_ALIENS) + +// /datum/round_event/scrubber_clog/strange/get_mob() +// var/static/list/mob_list = list( +// /mob/living/basic/lightgeist, +// ///mob/living/basic/bear, monkestation removal +// /mob/living/basic/mushroom, +// /mob/living/simple_animal/hostile/retaliate/goose, //Janitors HATE geese. +// /mob/living/simple_animal/pet/gondola, +// /mob/living/basic/slugcat //Monkestation edit. Let the slugs come out of the pipes! +// ) +// return pick(mob_list) diff --git a/code/modules/events/scrubber_overflow.dm b/code/modules/events/scrubber_overflow.dm index 2111cdeed91b..7fb76c3a9575 100644 --- a/code/modules/events/scrubber_overflow.dm +++ b/code/modules/events/scrubber_overflow.dm @@ -1,66 +1,84 @@ +#define BASE_EVAPORATION_MULTIPLIER 10 + +/// List of chems deemed "safer" for random generation, used by scrubber_overflow.dm and chem_spill.dm +/proc/get_random_safe_chem() + var/list/chems = list( + /datum/reagent/water, + /datum/reagent/carbon, + /datum/reagent/consumable/flour, + /datum/reagent/space_cleaner, + /datum/reagent/consumable/nutriment, + /datum/reagent/consumable/condensedcapsaicin, + /datum/reagent/drug/mushroomhallucinogen, + /datum/reagent/lube, + /datum/reagent/glitter/pink, + /datum/reagent/cryptobiolin, + /datum/reagent/toxin/plantbgone, + /datum/reagent/blood, + /datum/reagent/medicine/charcoal, + /datum/reagent/drug/space_drugs, + /datum/reagent/medicine/morphine, + /datum/reagent/water/holywater, + /datum/reagent/consumable/ethanol, + /datum/reagent/consumable/hot_coco, + /datum/reagent/toxin/acid, + /datum/reagent/toxin/mindbreaker, + /datum/reagent/toxin/rotatium, + /datum/reagent/bluespace, + /datum/reagent/pax, + /datum/reagent/consumable/laughter, + /datum/reagent/concentrated_barbers_aid, + /datum/reagent/baldium, + /datum/reagent/colorful_reagent, + /datum/reagent/peaceborg/confuse, + /datum/reagent/peaceborg/tire, + /datum/reagent/consumable/sodiumchloride, + /datum/reagent/consumable/ethanol/beer, + /datum/reagent/hair_dye, + /datum/reagent/consumable/sugar, + /datum/reagent/glitter/white, + /datum/reagent/growthserum + ) + return pick(chems) + /datum/round_event_control/scrubber_overflow name = "Scrubber Overflow: Normal" typepath = /datum/round_event/scrubber_overflow weight = 10 max_occurrences = 3 min_players = 10 + category = EVENT_CATEGORY_JANITORIAL + description = "The scrubbers release a tide of mostly harmless froth." + admin_setup = list(/datum/event_admin_setup/listed_options/scrubber_overflow) + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL) + event_group = /datum/event_group/scrubber_overflow + shared_occurence_type = SHARED_SCRUBBERS /datum/round_event/scrubber_overflow - announceWhen = 1 - startWhen = 5 - endWhen = 35 + announce_when = 1 + start_when = 5 /// The probability that the ejected reagents will be dangerous var/danger_chance = 1 /// Amount of reagents ejected from each scrubber - var/reagents_amount = 50 + var/reagents_amount = 100 + /// How quickly things evaporate + var/evaporation_multiplier = BASE_EVAPORATION_MULTIPLIER /// Probability of an individual scrubber overflowing var/overflow_probability = 50 /// Specific reagent to force all scrubbers to use, null for random reagent choice - var/forced_reagent + var/datum/reagent/forced_reagent_type /// A list of scrubbers that will have reagents ejected from them var/list/scrubbers = list() - /// The list of chems that scrubbers can produce - var/list/safer_chems = list( - /datum/reagent/water, - /datum/reagent/carbon, - /datum/reagent/consumable/flour, - /datum/reagent/space_cleaner, - /datum/reagent/consumable/nutriment, - /datum/reagent/consumable/condensedcapsaicin, - /datum/reagent/drug/mushroomhallucinogen, - /datum/reagent/lube, - /datum/reagent/glitter/pink, - /datum/reagent/cryptobiolin, - /datum/reagent/toxin/plantbgone, - /datum/reagent/blood, - /datum/reagent/medicine/charcoal, - /datum/reagent/drug/space_drugs, - /datum/reagent/medicine/morphine, - /datum/reagent/water/holywater, - /datum/reagent/consumable/ethanol, - /datum/reagent/consumable/hot_coco, - /datum/reagent/toxin/acid, - /datum/reagent/toxin/mindbreaker, - /datum/reagent/toxin/rotatium, - /datum/reagent/bluespace, - /datum/reagent/pax, - /datum/reagent/consumable/laughter, - /datum/reagent/concentrated_barbers_aid, - /datum/reagent/baldium, - /datum/reagent/colorful_reagent, - /datum/reagent/peaceborg/confuse, - /datum/reagent/peaceborg/tire, - /datum/reagent/consumable/sodiumchloride, - /datum/reagent/consumable/ethanol/beer, - /datum/reagent/hair_dye, - /datum/reagent/consumable/sugar, - /datum/reagent/glitter/white, - /datum/reagent/growthserum - ) - //needs to be chemid unit checked at some point + +/datum/round_event/scrubber_overflow/announce_deadchat(random, cause) + if(!forced_reagent_type) + //nothing out of the ordinary, so default announcement + return ..() + deadchat_broadcast(" has just been[random ? " randomly" : ""] triggered[cause ? " by [cause]" : ""]!", "Scrubber Overflow: [initial(forced_reagent_type.name)]", message_type=DEADCHAT_ANNOUNCEMENT) /datum/round_event/scrubber_overflow/announce(fake) - priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "Atmospherics alert") + priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejection of contents may occur.", "[command_name()] Engineering Division") /datum/round_event/scrubber_overflow/setup() for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/temp_vent in GLOB.machines) @@ -77,25 +95,43 @@ if(!scrubbers.len) return kill() + setup = TRUE //MONKESTATION ADDITION + +/datum/round_event_control/scrubber_overflow/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) //MONKESTATION ADDITION: fake_check = FALSE + . = ..() + if(!.) + return + for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/temp_vent in GLOB.machines) + var/turf/scrubber_turf = get_turf(temp_vent) + if(!scrubber_turf) + continue + if(!is_station_level(scrubber_turf.z)) + continue + if(temp_vent.welded) + continue + return TRUE //there's at least one. we'll let the codergods handle the rest with prob() i guess. + return FALSE + +/// proc that will run the prob check of the event and return a safe or dangerous reagent based off of that. +/datum/round_event/scrubber_overflow/proc/get_overflowing_reagent(dangerous) + return dangerous ? get_random_reagent_id() : get_random_safe_chem() /datum/round_event/scrubber_overflow/start() for(var/obj/machinery/atmospherics/components/unary/vent_scrubber/vent as anything in scrubbers) - if(!vent.loc) - CRASH("SCRUBBER SURGE: [vent] has no loc somehow?") - - var/datum/reagents/dispensed_reagent = new /datum/reagents(reagents_amount) - dispensed_reagent.my_atom = vent - if (forced_reagent) - dispensed_reagent.add_reagent(forced_reagent, reagents_amount) - else if (prob(danger_chance)) - dispensed_reagent.add_reagent(get_random_reagent_id(), reagents_amount) - new /mob/living/simple_animal/cockroach(get_turf(vent)) - new /mob/living/simple_animal/cockroach(get_turf(vent)) - else - dispensed_reagent.add_reagent(pick(safer_chems), reagents_amount) - - dispensed_reagent.create_foam(/datum/effect_system/fluid_spread/foam/short, reagents_amount) - + if(QDELETED(vent) || vent.welded) // in case it was welded after setup() but before we got to it here + continue + var/turf/vent_turf = get_turf(vent) + if(!isopenturf(vent_turf) || QDELING(vent_turf)) + continue + var/dangerous = prob(danger_chance) + var/reagent_type = forced_reagent_type || get_overflowing_reagent(dangerous) + if(dangerous) + new /mob/living/simple_animal/cockroach(vent_turf) + new /mob/living/simple_animal/cockroach(vent_turf) + vent_turf.add_liquid(reagent_type, reagents_amount, no_react = TRUE) + if(vent_turf.liquids?.liquid_group) + vent_turf.liquids.liquid_group.always_evaporates = TRUE + vent_turf.liquids.liquid_group.evaporation_multiplier += evaporation_multiplier CHECK_TICK /datum/round_event_control/scrubber_overflow/threatening @@ -105,10 +141,14 @@ min_players = 25 max_occurrences = 1 earliest_start = 35 MINUTES + description = "The scrubbers release a tide of moderately harmless froth." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 4 /datum/round_event/scrubber_overflow/threatening danger_chance = 10 - reagents_amount = 100 + reagents_amount = 150 + evaporation_multiplier = BASE_EVAPORATION_MULTIPLIER * 1.5 /datum/round_event_control/scrubber_overflow/catastrophic name = "Scrubber Overflow: Catastrophic" @@ -117,18 +157,47 @@ min_players = 35 max_occurrences = 1 earliest_start = 45 MINUTES + description = "The scrubbers release a tide of mildly harmless froth." + min_wizard_trigger_potency = 3 + max_wizard_trigger_potency = 6 /datum/round_event/scrubber_overflow/catastrophic danger_chance = 30 + reagents_amount = 200 + evaporation_multiplier = BASE_EVAPORATION_MULTIPLIER * 2 + +/datum/round_event_control/scrubber_overflow/every_vent + name = "Scrubber Overflow: Every Vent" + typepath = /datum/round_event/scrubber_overflow/every_vent + weight = 0 + max_occurrences = 0 + description = "The scrubbers release a tide of mostly harmless froth, but every scrubber is affected." + +/datum/round_event/scrubber_overflow/every_vent + overflow_probability = 100 reagents_amount = 150 + evaporation_multiplier = BASE_EVAPORATION_MULTIPLIER * 1.5 + +/datum/event_admin_setup/listed_options/scrubber_overflow + normal_run_option = "Random Reagents" + special_run_option = "Random Single Reagent" + +/datum/event_admin_setup/listed_options/scrubber_overflow/get_list() + return sort_list(subtypesof(/datum/reagent), /proc/cmp_typepaths_asc) + +/datum/event_admin_setup/listed_options/scrubber_overflow/apply_to_event(datum/round_event/scrubber_overflow/event) + if(chosen == special_run_option) + chosen = event.get_overflowing_reagent(dangerous = prob(event.danger_chance)) + event.forced_reagent_type = chosen + /datum/round_event_control/scrubber_overflow/beer // Used when the beer nuke "detonates" name = "Scrubber Overflow: Beer" typepath = /datum/round_event/scrubber_overflow/beer weight = 0 - max_occurrences = 0 /datum/round_event/scrubber_overflow/beer overflow_probability = 100 - forced_reagent = /datum/reagent/consumable/ethanol/beer reagents_amount = 100 + evaporation_multiplier = BASE_EVAPORATION_MULTIPLIER * 1.5 + forced_reagent_type = /datum/reagent/consumable/ethanol/beer diff --git a/code/modules/events/shuttle_catastrophe.dm b/code/modules/events/shuttle_catastrophe.dm index 4ed74ae0b931..c04611b1c6da 100644 --- a/code/modules/events/shuttle_catastrophe.dm +++ b/code/modules/events/shuttle_catastrophe.dm @@ -3,39 +3,83 @@ typepath = /datum/round_event/shuttle_catastrophe weight = 10 max_occurrences = 1 - max_alert = SEC_LEVEL_DELTA + category = EVENT_CATEGORY_BUREAUCRATIC + description = "Replaces the emergency shuttle with a random one." + admin_setup = list(/datum/event_admin_setup/warn_admin/shuttle_catastrophe, /datum/event_admin_setup/listed_options/shuttle_catastrophe) + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL) -/datum/round_event_control/shuttle_catastrophe/canSpawnEvent(players, gamemode) - if(!is_centcom_level(SSshuttle.emergency.z) || (SSshuttle.emergency.name == "Build Your Own Shuttle") || (SSshuttle.emergency.name == "Build Your Own Shuttle, Jr.") || (SSshuttle.emergency.mode == SHUTTLE_CALL || SSshuttle.emergency.mode == SHUTTLE_RECALL || SSshuttle.emergency.mode == SHUTTLE_DOCKED || SSshuttle.emergency.mode == SHUTTLE_STRANDED)) - return FALSE // don't undo manual player engineering, it also would unload people and ghost them, there's just a lot of problems - return ..() +/datum/round_event_control/shuttle_catastrophe/canSpawnEvent(players, allow_magic = FALSE, fake_check = FALSE) //MONKESTATION ADDITION: fake_check = FALSE + . = ..() + if(!.) + return . + + if(SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_FORCED) + return FALSE //don't do it if its already been done + if(istype(SSshuttle.emergency, /obj/docking_port/mobile/emergency/shuttle_build)) + return FALSE //don't undo manual player engineering, it also would unload people and ghost them, there's just a lot of problems + if(EMERGENCY_AT_LEAST_DOCKED) + return FALSE //don't remove all players when its already on station or going to centcom + return TRUE /datum/round_event/shuttle_catastrophe var/datum/map_template/shuttle/new_shuttle - var/list/datum/map_template/shuttle/blacklisted_shuttles = list(/datum/map_template/shuttle/emergency/arena, /datum/map_template/shuttle/emergency/construction, - /datum/map_template/shuttle/emergency/construction/small, /datum/map_template/shuttle/emergency/discoinferno, /datum/map_template/shuttle/emergency/meteor) + /datum/round_event/shuttle_catastrophe/announce(fake) var/cause = pick("was attacked by [syndicate_name()] Operatives", "mysteriously teleported away", "had its refuelling crew mutiny", - "was found with its engines stolen", "\[REDACTED\]", "flew into the sunset, and melted", "fell into a black hole", - "was stolen by the Clown Planet", "had its shuttle inspector put the shuttle in reverse instead of park, causing the shuttle to crash into the hangar") + "was found with its engines stolen", "\[REDACTED\]", "flew into the sunset, and melted", "learned something from a very wise cow, and left on its own", + "had cloning devices on it", "had its shuttle inspector put the shuttle in reverse instead of park, causing the shuttle to crash into the hangar") + var/message = "Your emergency shuttle [cause]. " - priority_announce("Your emergency shuttle [cause]. Your replacement shuttle will be the [new_shuttle.name] until further notice.", "CentCom Spacecraft Engineering") + if(SSshuttle.shuttle_insurance) + message += "Luckily, your shuttle insurance has covered the costs of repair!" + if(SSeconomy.get_dep_account(ACCOUNT_CAR)) + message += " You have been awarded a bonus from [command_name()] for smart spending." + else + message += "Your replacement shuttle will be the [new_shuttle.name] until further notice." + priority_announce(message, "[command_name()] Spacecraft Engineering") /datum/round_event/shuttle_catastrophe/setup() + if(SSshuttle.shuttle_insurance || !isnull(new_shuttle)) //If an admin has overridden it don't re-roll it + return var/list/valid_shuttle_templates = list() for(var/shuttle_id in SSmapping.shuttle_templates) var/datum/map_template/shuttle/template = SSmapping.shuttle_templates[shuttle_id] - if(template.credit_cost < INFINITY) //if we could get it from the emagged communications console, it's cool for us to get it here - if (!(template in blacklisted_shuttles)) - valid_shuttle_templates += template + if(!isnull(template.who_can_purchase) && template.credit_cost < INFINITY) //if we could get it from the communications console, it's cool for us to get it here + valid_shuttle_templates += template new_shuttle = pick(valid_shuttle_templates) + setup = TRUE //MONKESTATION ADDITION /datum/round_event/shuttle_catastrophe/start() + if(SSshuttle.shuttle_insurance) + var/datum/bank_account/station_balance = SSeconomy.get_dep_account(ACCOUNT_CAR) + station_balance?.adjust_money(8000) + return SSshuttle.shuttle_purchased = SHUTTLEPURCHASE_FORCED SSshuttle.unload_preview() - SSshuttle.load_template(new_shuttle) SSshuttle.existing_shuttle = SSshuttle.emergency - SSshuttle.emergency.name = new_shuttle.name SSshuttle.action_load(new_shuttle, replace = TRUE) - log_game("Shuttle Catastrophe set a new shuttle, [new_shuttle.name].") + log_shuttle("Shuttle Catastrophe set a new shuttle, [new_shuttle.name].") + +/datum/event_admin_setup/warn_admin/shuttle_catastrophe + warning_text = "This will unload the currently docked emergency shuttle, and ERASE ANYTHING within it. Proceed anyways?" + snitch_text = "has forced a shuttle catastrophe while a shuttle was already docked." + +/datum/event_admin_setup/warn_admin/shuttle_catastrophe/should_warn() + return EMERGENCY_AT_LEAST_DOCKED || istype(SSshuttle.emergency, /obj/docking_port/mobile/emergency/shuttle_build) + +/datum/event_admin_setup/listed_options/shuttle_catastrophe + input_text = "Select a specific shuttle?" + normal_run_option = "Random shuttle" + +/datum/event_admin_setup/listed_options/shuttle_catastrophe/get_list() + var/list/valid_shuttle_templates = list() + for(var/shuttle_id in SSmapping.shuttle_templates) + var/datum/map_template/shuttle/template = SSmapping.shuttle_templates[shuttle_id] + if(!isnull(template.who_can_purchase) && template.credit_cost < INFINITY) //Even admins cannot force the cargo shuttle to act as an escape shuttle + valid_shuttle_templates += template + return valid_shuttle_templates + +/datum/event_admin_setup/listed_options/shuttle_catastrophe/apply_to_event(datum/round_event/shuttle_catastrophe/event) + event.new_shuttle = chosen diff --git a/code/modules/events/shuttle_insurance.dm b/code/modules/events/shuttle_insurance.dm new file mode 100644 index 000000000000..7fb238d52f4f --- /dev/null +++ b/code/modules/events/shuttle_insurance.dm @@ -0,0 +1,59 @@ +/datum/round_event_control/shuttle_insurance + name = "Shuttle Insurance" + typepath = /datum/round_event/shuttle_insurance + max_occurrences = 1 + category = EVENT_CATEGORY_BUREAUCRATIC + description = "A sketchy but legit insurance offer." + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL) + +/datum/round_event_control/shuttle_insurance/canSpawnEvent(players, allow_magic = FALSE, fake_check = FALSE) //MONKESTATION ADDITION: fake_check = FALSE + . = ..() + if(!.) + return . + + if(!SSeconomy.get_dep_account(ACCOUNT_CAR)) + return FALSE //They can't pay? + if(SSshuttle.shuttle_purchased == SHUTTLEPURCHASE_FORCED) + return FALSE //don't do it if there's nothing to insure + if(istype(SSshuttle.emergency, /obj/docking_port/mobile/emergency/shuttle_build)) + return FALSE //this shuttle prevents the catastrophe event from happening making this event effectively useless + if(EMERGENCY_AT_LEAST_DOCKED) + return FALSE //catastrophes won't trigger so no point + return TRUE + +/datum/round_event/shuttle_insurance + var/ship_name = "\"In the Unlikely Event\"" + var/datum/comm_message/insurance_message + var/insurance_evaluation = 0 + +/datum/round_event/shuttle_insurance/announce(fake) + priority_announce("Incoming subspace communication. Secure channel opened at all communication consoles.", "Incoming Message", SSstation.announcer.get_rand_report_sound()) + +/datum/round_event/shuttle_insurance/setup() + ship_name = pick(strings(PIRATE_NAMES_FILE, "rogue_names")) + for(var/shuttle_id in SSmapping.shuttle_templates) + var/datum/map_template/shuttle/template = SSmapping.shuttle_templates[shuttle_id] + if(template.name == SSshuttle.emergency.name) //found you slackin + insurance_evaluation = template.credit_cost/2 + break + if(!insurance_evaluation) + insurance_evaluation = 5000 //gee i dunno + setup = TRUE //MONKESTATION ADDITION + +/datum/round_event/shuttle_insurance/start() + insurance_message = new("Shuttle Insurance", "Hey, pal, this is the [ship_name]. Can't help but notice you're rocking a wild and crazy shuttle there with NO INSURANCE! Crazy. What if something happened to it, huh?! We've done a quick evaluation on your rates in this sector and we're offering [insurance_evaluation] to cover for your shuttle in case of any disaster.", list("Purchase Insurance.","Reject Offer.")) + insurance_message.answer_callback = CALLBACK(src, PROC_REF(answered)) + SScommunications.send_message(insurance_message, unique = TRUE) + +/datum/round_event/shuttle_insurance/proc/answered() + if(EMERGENCY_AT_LEAST_DOCKED) + priority_announce("You are definitely too late to purchase insurance, my friends. Our agents don't work on site.",sender_override = ship_name, color_override = "red") + return + if(insurance_message && insurance_message.answered == 1) + var/datum/bank_account/station_balance = SSeconomy.get_dep_account(ACCOUNT_CAR) + if(!station_balance?.adjust_money(-insurance_evaluation)) + priority_announce("You didn't send us enough money for shuttle insurance. This, in the space layman's terms, is considered scamming. We're keeping your money, scammers!", sender_override = ship_name, color_override = "red") + return + priority_announce("Thank you for purchasing shuttle insurance!", sender_override = ship_name, color_override = "red") + SSshuttle.shuttle_insurance = TRUE diff --git a/code/modules/events/shuttle_loan.dm b/code/modules/events/shuttle_loan.dm deleted file mode 100644 index 3476dc2ee699..000000000000 --- a/code/modules/events/shuttle_loan.dm +++ /dev/null @@ -1,290 +0,0 @@ -#define HIJACK_SYNDIE 1 -#define RUSKY_PARTY 2 -#define SPIDER_GIFT 3 -#define DEPARTMENT_RESUPPLY 4 -#define ANTIDOTE_NEEDED 5 -#define PIZZA_DELIVERY 6 -#define ITS_HIP_TO 7 -#define MY_GOD_JC 8 -#define PETA 9 - - -/datum/round_event_control/shuttle_loan - name = "Shuttle Loan" - typepath = /datum/round_event/shuttle_loan - max_occurrences = 1 - earliest_start = 7 MINUTES - max_alert = SEC_LEVEL_DELTA - -/datum/round_event/shuttle_loan - announceWhen = 1 - endWhen = 500 - var/dispatched = 0 - var/dispatch_type = 0 - var/bonus_points = 10000 - var/thanks_msg = "The cargo shuttle should return in five minutes. Have some supply points for your trouble." - -/datum/round_event/shuttle_loan/setup() - dispatch_type = pick(HIJACK_SYNDIE, RUSKY_PARTY, SPIDER_GIFT, DEPARTMENT_RESUPPLY, ANTIDOTE_NEEDED, PIZZA_DELIVERY, ITS_HIP_TO, MY_GOD_JC, PETA) - -/datum/round_event/shuttle_loan/announce(fake) - SSshuttle.shuttle_loan = src - switch(dispatch_type) - if(HIJACK_SYNDIE) - priority_announce("Cargo: The syndicate are trying to infiltrate your station. If you let them hijack your cargo shuttle, you'll save us a headache.","CentCom Counter Intelligence") - if(RUSKY_PARTY) - priority_announce("Cargo: A group of angry Russians want to have a party. Can you send them your cargo shuttle then make them disappear?","CentCom Russian Outreach Program") - if(SPIDER_GIFT) - priority_announce("Cargo: The Spider Clan has sent us a mysterious gift. Can we ship it to you to see what's inside?","CentCom Diplomatic Corps") - if(DEPARTMENT_RESUPPLY) - priority_announce("Cargo: Seems we've ordered doubles of our department resupply packages this month. Can we send them to you?","CentCom Supply Department") - thanks_msg = "The cargo shuttle should return in 5 minutes." - bonus_points = 0 - if(ANTIDOTE_NEEDED) - priority_announce("Cargo: Your station has been chosen for an epidemiological research project. Send us your cargo shuttle to receive your research samples.", "CentCom Research Initiatives") - if(PIZZA_DELIVERY) - priority_announce("Cargo: It looks like a neighbouring station accidentally delivered their pizza to you instead.", "CentCom Spacepizza Division") - thanks_msg = "The cargo shuttle should return in 5 minutes." - bonus_points = 0 - if(ITS_HIP_TO) - priority_announce("Cargo: One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?", "CentCom Janitorial Division") - bonus_points = 20000 //Toxin bees can be unbeelievably lethal - if(MY_GOD_JC) - priority_announce("Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it.", "CentCom Security Division") - thanks_msg = "Live explosive ordnance incoming via supply shuttle. Evacuating cargo bay is recommended." - bonus_points = 45000 //If you mess up, people die and the shuttle gets turned into swiss cheese - if(PETA) - priority_announce("Cargo: A group calling themselves PETA whatever that means is asking to inspect your station. Do you want to check it out?", "CentCom Diplomatic Corps") - thanks_msg = "Inspectors Incoming." - bonus_points = 10000 // Funny hostile mobs - -/datum/round_event/shuttle_loan/proc/loan_shuttle() - priority_announce(thanks_msg, "Cargo shuttle commandeered by CentCom.") - - dispatched = 1 - var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR) - if(D) - D.adjust_money(bonus_points) - endWhen = activeFor + 1 - - SSshuttle.supply.mode = SHUTTLE_CALL - SSshuttle.supply.destination = SSshuttle.getDock("supply_home") - SSshuttle.supply.setTimer(3000) - - switch(dispatch_type) - if(HIJACK_SYNDIE) - SSshuttle.centcom_message += "Syndicate hijack team incoming." - if(RUSKY_PARTY) - SSshuttle.centcom_message += "Partying Russians incoming." - if(SPIDER_GIFT) - SSshuttle.centcom_message += "Spider Clan gift incoming." - if(DEPARTMENT_RESUPPLY) - SSshuttle.centcom_message += "Department resupply incoming." - if(ANTIDOTE_NEEDED) - SSshuttle.centcom_message += "Virus samples incoming." - if(PIZZA_DELIVERY) - SSshuttle.centcom_message += "Pizza delivery for [station_name()]" - if(ITS_HIP_TO) - SSshuttle.centcom_message += "Biohazard cleanup incoming." - if(MY_GOD_JC) - SSshuttle.centcom_message += "Live explosive ordnance incoming. Exercise extreme caution." - if(PETA) - SSshuttle.centcom_message += "Inspectors Incoming." - -/datum/round_event/shuttle_loan/tick() - if(dispatched) - if(SSshuttle.supply.mode != SHUTTLE_IDLE) - endWhen = activeFor - else - endWhen = activeFor + 1 - -/datum/round_event/shuttle_loan/end() - if(SSshuttle.shuttle_loan && SSshuttle.shuttle_loan.dispatched) - //make sure the shuttle was dispatched in time - SSshuttle.shuttle_loan = null - - var/list/empty_shuttle_turfs = list() - var/list/area/shuttle/shuttle_areas = SSshuttle.supply.shuttle_areas - for(var/place in shuttle_areas) - var/area/shuttle/shuttle_area = place - for(var/turf/open/floor/T in shuttle_area) - if(T.is_blocked_turf()) - continue - empty_shuttle_turfs += T - if(!empty_shuttle_turfs.len) - return - - var/list/shuttle_spawns = list() - switch(dispatch_type) - if(HIJACK_SYNDIE) - var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/emergency/specialops] - pack.generate(pick_n_take(empty_shuttle_turfs)) - - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) - if(prob(75)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) - if(prob(50)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/syndicate/ranged/infiltrator) - - if(RUSKY_PARTY) - var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/service/party] - pack.generate(pick_n_take(empty_shuttle_turfs)) - - shuttle_spawns.Add(/mob/living/simple_animal/hostile/russian) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/russian/ranged) //drops a mateba - shuttle_spawns.Add(/mob/living/simple_animal/hostile/bear/russian) - if(prob(75)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/russian) - if(prob(50)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/bear/russian) - - if(SPIDER_GIFT) - var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/emergency/specialops] - pack.generate(pick_n_take(empty_shuttle_turfs)) - - shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider/nurse) - if(prob(50)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/giant_spider/hunter) - - var/turf/T = pick_n_take(empty_shuttle_turfs) - - new /obj/effect/decal/remains/human(T) - new /obj/item/clothing/shoes/space_ninja(T) - new /obj/item/clothing/mask/balaclava(T) - - for(var/i in 1 to 5) - T = pick_n_take(empty_shuttle_turfs) - new /obj/structure/spider/stickyweb(T) - - if(ANTIDOTE_NEEDED) - var/obj/effect/mob_spawn/human/corpse/assistant/infected_assistant = pick(/obj/effect/mob_spawn/human/corpse/assistant/beesease_infection, /obj/effect/mob_spawn/human/corpse/assistant/brainrot_infection, /obj/effect/mob_spawn/human/corpse/assistant/spanishflu_infection) - var/turf/T - for(var/i=0, i<10, i++) - if(prob(15)) - shuttle_spawns.Add(/obj/item/reagent_containers/glass/bottle) - else if(prob(15)) - shuttle_spawns.Add(/obj/item/reagent_containers/syringe) - else if(prob(25)) - shuttle_spawns.Add(/obj/item/shard) - T = pick_n_take(empty_shuttle_turfs) - new infected_assistant(T) - shuttle_spawns.Add(/obj/structure/closet/crate) - shuttle_spawns.Add(/obj/item/reagent_containers/glass/bottle/pierrot_throat) - shuttle_spawns.Add(/obj/item/reagent_containers/glass/bottle/magnitis) - - if(DEPARTMENT_RESUPPLY) - var/list/crate_types = list( - /datum/supply_pack/emergency/equipment, - /datum/supply_pack/security/supplies, - /datum/supply_pack/organic/food, - /datum/supply_pack/emergency/weedcontrol, - /datum/supply_pack/engineering/tools, - /datum/supply_pack/engineering/engiequipment, - /datum/supply_pack/science/robotics, - /datum/supply_pack/science/plasma, - /datum/supply_pack/medical/supplies - ) - for(var/crate in crate_types) - var/datum/supply_pack/pack = SSshuttle.supply_packs[crate] - pack.generate(pick_n_take(empty_shuttle_turfs)) - - for(var/i in 1 to 5) - var/decal = pick(/obj/effect/decal/cleanable/food/flour, /obj/effect/decal/cleanable/robot_debris, /obj/effect/decal/cleanable/oil) - new decal(pick_n_take(empty_shuttle_turfs)) - if(PIZZA_DELIVERY) - var/naughtypizza = list(/obj/item/pizzabox/bomb,/obj/item/pizzabox/margherita/robo) //oh look another blaklist, for pizza nonetheless! - var/nicepizza = list(/obj/item/pizzabox/margherita, /obj/item/pizzabox/meat, /obj/item/pizzabox/vegetable, /obj/item/pizzabox/mushroom) - for(var/i in 1 to 6) - shuttle_spawns.Add(pick(prob(5) ? naughtypizza : nicepizza)) - if(ITS_HIP_TO) - var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/organic/hydroponics/beekeeping_fullkit] - pack.generate(pick_n_take(empty_shuttle_turfs)) - - shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/bee_terrorist) - shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech) - shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech) - shuttle_spawns.Add(/obj/effect/mob_spawn/human/corpse/nanotrasensoldier) - shuttle_spawns.Add(/obj/item/gun/ballistic/automatic/pistol/no_mag) - shuttle_spawns.Add(/obj/item/gun/ballistic/automatic/pistol/m1911/no_mag) - shuttle_spawns.Add(/obj/item/honey_frame) - shuttle_spawns.Add(/obj/item/honey_frame) - shuttle_spawns.Add(/obj/item/honey_frame) - shuttle_spawns.Add(/obj/structure/beebox/unwrenched) - shuttle_spawns.Add(/obj/item/queen_bee/bought) - shuttle_spawns.Add(/obj/structure/closet/crate/hydroponics) - - for(var/i in 1 to 8) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/poison/bees/toxin) - - for(var/i in 1 to 5) - var/decal = pick(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/insectguts) - new decal(pick_n_take(empty_shuttle_turfs)) - - for(var/i in 1 to 10) - var/casing = /obj/item/ammo_casing/spent - new casing(pick_n_take(empty_shuttle_turfs)) - - if(MY_GOD_JC) - shuttle_spawns.Add(/obj/machinery/syndicatebomb/shuttle_loan) - if(prob(95)) - shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb) - else - shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb/allyourbase) - - if(PETA) - for(var/i in 1 to 20) - if(prob(50)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/rat/loan) - if(prob(40)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/carp/loan) - if(prob(20)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/bear/loan) - if(prob(30)) - shuttle_spawns.Add(/mob/living/simple_animal/hostile/feral_cat/loan) - - var/false_positive = 0 - while(shuttle_spawns.len && empty_shuttle_turfs.len) - var/turf/T = pick_n_take(empty_shuttle_turfs) - if(T.contents.len && false_positive < 5) - false_positive++ - continue - - var/spawn_type = pick_n_take(shuttle_spawns) - new spawn_type(T) - -//items that appear only in shuttle loan events - -/obj/item/storage/belt/fannypack/yellow/bee_terrorist/PopulateContents() - new /obj/item/grenade/plastic/c4 (src) - new /obj/item/reagent_containers/pill/cyanide(src) - new /obj/item/grenade/chem_grenade/facid(src) - -/obj/item/paper/fluff/bee_objectives - name = "Objectives of a Bee Liberation Front Operative" - info = "Objective #1. Liberate all bees on the NT transport vessel 2416/B. Success!
Objective #2. Escape alive. Failed." - -/obj/machinery/syndicatebomb/shuttle_loan/Initialize(mapload) - . = ..() - setAnchored(TRUE) - timer_set = rand(480, 600) //once the supply shuttle docks (after 5 minutes travel time), players have between 3-5 minutes to defuse the bomb - activate() - update_appearance(UPDATE_ICON) - -/obj/item/paper/fluff/cargo/bomb - name = "hastly scribbled note" - info = "GOOD LUCK!" - -/obj/item/paper/fluff/cargo/bomb/allyourbase - info = "Somebody set us up the bomb!" - -#undef HIJACK_SYNDIE -#undef RUSKY_PARTY -#undef SPIDER_GIFT -#undef DEPARTMENT_RESUPPLY -#undef ANTIDOTE_NEEDED -#undef PIZZA_DELIVERY -#undef ITS_HIP_TO -#undef MY_GOD_JC diff --git a/code/modules/events/shuttle_loan/shuttle_loan_datum.dm b/code/modules/events/shuttle_loan/shuttle_loan_datum.dm new file mode 100644 index 000000000000..f1c62c2010ce --- /dev/null +++ b/code/modules/events/shuttle_loan/shuttle_loan_datum.dm @@ -0,0 +1,219 @@ +/// One of the potential shuttle loans you might recieve. +/datum/shuttle_loan_situation + /// Who sent the shuttle + var/sender = "Centcom" + /// What they said about it. + var/announcement_text = "Unset announcement text" + /// What the shuttle says about it. + var/shuttle_transit_text = "Unset transit text" + /// Supply points earned for taking the deal. + var/bonus_points = 10000 + /// Response for taking the deal. + var/thanks_msg = "The cargo shuttle should return in five minutes. Have some supply points for your trouble." + /// Small description of the loan for easier log reading. + var/logging_desc + +/datum/shuttle_loan_situation/New() + . = ..() + if(!logging_desc) + stack_trace("No logging blurb set for [src.type]!") + if(HAS_TRAIT(SSstation, STATION_TRAIT_LOANER_SHUTTLE)) + bonus_points *= 1.15 + +/// Spawns paths added to `spawn_list`, and passes empty shuttle turfs so you can spawn more complicated things like dead bodies. +/datum/shuttle_loan_situation/proc/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + SHOULD_CALL_PARENT(FALSE) + CRASH("Unimplemented get_spawned_items() on [src.type].") + +/datum/shuttle_loan_situation/antidote + sender = "CentCom Research Initiatives" + announcement_text = "Your station has been chosen for an epidemiological research project. Send us your cargo shuttle to receive your research samples." + shuttle_transit_text = "Virus samples incoming." + logging_desc = "Virus shuttle" + +/datum/shuttle_loan_situation/antidote/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + var/obj/effect/mob_spawn/human/corpse/assistant/infected_assistant = pick(/obj/effect/mob_spawn/human/corpse/assistant/beesease_infection, /obj/effect/mob_spawn/human/corpse/assistant/brainrot_infection, /obj/effect/mob_spawn/human/corpse/assistant/spanishflu_infection) + var/turf/T + for(var/i=0, i<10, i++) + if(prob(15)) + spawn_list.Add(/obj/item/reagent_containers/glass/bottle) + else if(prob(15)) + spawn_list.Add(/obj/item/reagent_containers/syringe) + else if(prob(25)) + spawn_list.Add(/obj/item/shard) + T = pick_n_take(empty_shuttle_turfs) + new infected_assistant(T) + spawn_list.Add(/obj/structure/closet/crate) + spawn_list.Add(/obj/item/reagent_containers/glass/bottle/pierrot_throat) + spawn_list.Add(/obj/item/reagent_containers/glass/bottle/magnitis) + + +/datum/shuttle_loan_situation/department_resupply + sender = "CentCom Supply Department" + announcement_text = "Seems we've ordered doubles of our department resupply packages this month. Can we send them to you?" + shuttle_transit_text = "Department resupply incoming." + thanks_msg = "The cargo shuttle should return in five minutes." + bonus_points = 0 + logging_desc = "Resupply packages" + +/datum/shuttle_loan_situation/department_resupply/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + var/list/crate_types = list( + /datum/supply_pack/emergency/equipment, + /datum/supply_pack/security/supplies, + /datum/supply_pack/organic/food, + /datum/supply_pack/emergency/weedcontrol, + /datum/supply_pack/engineering/tools, + /datum/supply_pack/engineering/engiequipment, + /datum/supply_pack/science/robotics, + /datum/supply_pack/science/plasma, + /datum/supply_pack/medical/supplies + ) + for(var/crate in crate_types) + var/datum/supply_pack/pack = SSshuttle.supply_packs[crate] + pack.generate(pick_n_take(empty_shuttle_turfs)) + + for(var/i in 1 to 5) + var/decal = pick(/obj/effect/decal/cleanable/food/flour, /obj/effect/decal/cleanable/robot_debris, /obj/effect/decal/cleanable/oil) + new decal(pick_n_take(empty_shuttle_turfs)) + +// /datum/shuttle_loan_situation/department_resupply +// sender = "CentCom Counterintelligence" +// announcement_text = "The syndicate are trying to infiltrate your station. If you let them hijack your cargo shuttle, you'll save us a headache." +// shuttle_transit_text = "Syndicate hijack team incoming." +// logging_desc = "Syndicate boarding party" + +// /datum/shuttle_loan_situation/department_resupply/spawn_items(list/spawn_list, list/empty_shuttle_turfs) +// var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/imports/specialops] +// pack.generate(pick_n_take(empty_shuttle_turfs)) + +// spawn_list.Add(/mob/living/basic/trooper/syndicate/ranged/infiltrator) +// spawn_list.Add(/mob/living/basic/trooper/syndicate/ranged/infiltrator) +// if(prob(75)) +// spawn_list.Add(/mob/living/basic/trooper/syndicate/ranged/infiltrator) +// if(prob(50)) +// spawn_list.Add(/mob/living/basic/trooper/syndicate/ranged/infiltrator) + + + +/datum/shuttle_loan_situation/lots_of_bees + sender = "CentCom Janitorial Division" + announcement_text = "One of our freighters carrying a bee shipment has been attacked by eco-terrorists. Can you clean up the mess for us?" + shuttle_transit_text = "Biohazard cleanup incoming." + bonus_points = 20000 //Toxin bees can be unbeelievably lethal + logging_desc = "Shuttle full of bees" + +/datum/shuttle_loan_situation/lots_of_bees/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/organic/hydroponics/beekeeping_fullkit] + pack.generate(pick_n_take(empty_shuttle_turfs)) + + spawn_list.Add(/obj/effect/mob_spawn/human/corpse/bee_terrorist) + spawn_list.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech) + spawn_list.Add(/obj/effect/mob_spawn/human/corpse/cargo_tech) + spawn_list.Add(/obj/effect/mob_spawn/human/corpse/nanotrasensoldier) + spawn_list.Add(/obj/item/gun/ballistic/automatic/pistol/no_mag) + spawn_list.Add(/obj/item/gun/ballistic/automatic/pistol/m1911/no_mag) + spawn_list.Add(/obj/item/honey_frame) + spawn_list.Add(/obj/item/honey_frame) + spawn_list.Add(/obj/item/honey_frame) + spawn_list.Add(/obj/structure/beebox/unwrenched) + spawn_list.Add(/obj/item/queen_bee/bought) + spawn_list.Add(/obj/structure/closet/crate/hydroponics) + + for(var/i in 1 to 8) + spawn_list.Add(/mob/living/simple_animal/hostile/poison/bees/toxin) + + for(var/i in 1 to 5) + var/decal = pick(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/insectguts) + new decal(pick_n_take(empty_shuttle_turfs)) + + for(var/i in 1 to 10) + var/casing = /obj/item/ammo_casing/spent + new casing(pick_n_take(empty_shuttle_turfs)) + + +/datum/shuttle_loan_situation/jc_a_bomb + sender = "CentCom Security Division" + announcement_text = "We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it." + shuttle_transit_text = "Live explosive ordnance incoming. Exercise extreme caution." + thanks_msg = "Live explosive ordnance incoming via supply shuttle. Evacuating cargo bay is recommended." + bonus_points = 45000 //If you mess up, people die and the shuttle gets turned into swiss cheese + logging_desc = "Shuttle with a ticking bomb" + +/datum/shuttle_loan_situation/jc_a_bomb/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + spawn_list.Add(/obj/machinery/syndicatebomb/shuttle_loan) + if(prob(95)) + spawn_list.Add(/obj/item/paper/fluff/cargo/bomb) + else + spawn_list.Add(/obj/item/paper/fluff/cargo/bomb/allyourbase) + +// /datum/shuttle_loan_situation/papers_please +// sender = "CentCom Paperwork Division" +// announcement_text = "A neighboring station needs some help handling some paperwork. Could you help process it for us?" +// shuttle_transit_text = "Paperwork incoming." +// thanks_msg = "The cargo shuttle should return in five minutes. Payment will be rendered when the paperwork is processed and returned." +// bonus_points = 0 //Payout is made when the stamped papers are returned +// logging_desc = "Paperwork shipment" + +// /datum/shuttle_loan_situation/papers_please/spawn_items(list/spawn_list, list/empty_shuttle_turfs) +// spawn_list += subtypesof(/obj/item/paperwork) - typesof(/obj/item/paperwork/photocopy) - typesof(/obj/item/paperwork/ancient) + +/datum/shuttle_loan_situation/pizza_delivery + sender = "CentCom Spacepizza Division" + announcement_text = "It looks like a neighbouring station accidentally delivered their pizza to you instead." + shuttle_transit_text = "Pizza delivery!" + thanks_msg = "The cargo shuttle should return in five minutes." + bonus_points = 0 + logging_desc = "Pizza delivery" + +/datum/shuttle_loan_situation/pizza_delivery/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + var/naughtypizza = list(/obj/item/pizzabox/bomb, /obj/item/pizzabox/margherita/robo) //oh look another blacklist, for pizza nonetheless! + var/nicepizza = list(/obj/item/pizzabox/margherita, /obj/item/pizzabox/meat, /obj/item/pizzabox/vegetable, /obj/item/pizzabox/mushroom) + for(var/i in 1 to 6) + spawn_list.Add(pick(prob(5) ? naughtypizza : nicepizza)) + + +/datum/shuttle_loan_situation/russian_party + sender = "CentCom Russian Outreach Program" + announcement_text = "A group of angry Russians want to have a party. Can you send them your cargo shuttle then make them disappear?" + shuttle_transit_text = "Partying Russians incoming." + logging_desc = "Russian party squad" + +/datum/shuttle_loan_situation/russian_party/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/service/party] + pack.generate(pick_n_take(empty_shuttle_turfs)) + + spawn_list.Add(/mob/living/simple_animal/hostile/russian) + spawn_list.Add(/mob/living/simple_animal/hostile/russian/ranged) //drops a mateba + spawn_list.Add(/mob/living/simple_animal/hostile/bear/russian) + if(prob(75)) + spawn_list.Add(/mob/living/simple_animal/hostile/russian) + if(prob(50)) + spawn_list.Add(/mob/living/simple_animal/hostile/bear/russian) + + + +/datum/shuttle_loan_situation/spider_gift + sender = "CentCom Diplomatic Corps" + announcement_text = "The Spider Clan has sent us a mysterious gift. Can we ship it to you to see what's inside?" + shuttle_transit_text = "Spider Clan gift incoming." + logging_desc = "Shuttle full of spiders" + +/datum/shuttle_loan_situation/spider_gift/spawn_items(list/spawn_list, list/empty_shuttle_turfs) + var/datum/supply_pack/pack = SSshuttle.supply_packs[/datum/supply_pack/emergency/specialops] + pack.generate(pick_n_take(empty_shuttle_turfs)) + + spawn_list.Add(/mob/living/simple_animal/hostile/poison/giant_spider) + spawn_list.Add(/mob/living/simple_animal/hostile/poison/giant_spider) + spawn_list.Add(/mob/living/simple_animal/hostile/poison/giant_spider/nurse) + if(prob(50)) + spawn_list.Add(/mob/living/simple_animal/hostile/poison/giant_spider/hunter) + + var/turf/victim_turf = pick_n_take(empty_shuttle_turfs) + + new /obj/effect/decal/remains/human(victim_turf) + new /obj/item/clothing/shoes/space_ninja(victim_turf) + new /obj/item/clothing/mask/balaclava(victim_turf) + + for(var/i in 1 to 5) + victim_turf = pick_n_take(empty_shuttle_turfs) + new /obj/structure/spider/stickyweb(victim_turf) diff --git a/code/modules/events/shuttle_loan/shuttle_loan_event.dm b/code/modules/events/shuttle_loan/shuttle_loan_event.dm new file mode 100644 index 000000000000..0dbf7fe101bd --- /dev/null +++ b/code/modules/events/shuttle_loan/shuttle_loan_event.dm @@ -0,0 +1,109 @@ + +/datum/round_event_control/shuttle_loan + name = "Shuttle Loan" + description = "If cargo accepts the offer, fills the shuttle with loot and/or enemies." + typepath = /datum/round_event/shuttle_loan + max_occurrences = 3 + earliest_start = 7 MINUTES + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL, TAG_EXTERNAL) + category = EVENT_CATEGORY_BUREAUCRATIC + admin_setup = list(/datum/event_admin_setup/listed_options/shuttle_loan) + ///The types of loan events already run (and to be excluded if the event triggers). + var/list/run_situations = list() + +/datum/round_event_control/shuttle_loan/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) + . = ..() + for(var/datum/round_event/running_event in SSgamemode.running) + if(istype(running_event, /datum/round_event/shuttle_loan)) //Make sure two of these don't happen at once. + return FALSE + +/datum/round_event/shuttle_loan + announce_when = 1 + end_when = 500 + /// what type of shuttle loan situation the station faces. + var/datum/shuttle_loan_situation/situation + /// Whether the station has let Centcom commandeer the shuttle yet. + var/dispatched = FALSE + +/datum/round_event/shuttle_loan/setup() + var/datum/round_event_control/shuttle_loan/loan_control = control + //by this point if situation is admin picked, it is a type, not an instance. + if(!situation) + var/list/valid_situations = subtypesof(/datum/shuttle_loan_situation) - loan_control.run_situations + if(!valid_situations.len) + //If we somehow run out of loans (fking campbell), they all become available again + loan_control.run_situations.Cut() + valid_situations = subtypesof(/datum/shuttle_loan_situation) + situation = pick(valid_situations) + + loan_control.run_situations.Add(situation) + situation = new situation() + setup = TRUE + +/datum/round_event/shuttle_loan/announce(fake) + priority_announce("Cargo: [situation.announcement_text]", situation.sender) + SSshuttle.shuttle_loan = src + +/datum/round_event/shuttle_loan/proc/loan_shuttle() + priority_announce(situation.thanks_msg, "Cargo shuttle commandeered by [command_name()].") + + dispatched = TRUE + var/datum/bank_account/dep_account = SSeconomy.get_dep_account(ACCOUNT_CAR) + dep_account?.adjust_money(situation.bonus_points) + end_when = activeFor + 1 + + SSshuttle.supply.mode = SHUTTLE_CALL + SSshuttle.supply.destination = SSshuttle.getDock("cargo_home") + SSshuttle.supply.setTimer(3000) + SSshuttle.centcom_message += situation.shuttle_transit_text + + log_game("Shuttle loan event firing with type '[situation.logging_desc]'.") + +/datum/round_event/shuttle_loan/tick() + if(dispatched) + if(SSshuttle.supply.mode != SHUTTLE_IDLE) + end_when = activeFor + else + end_when = activeFor + 1 + +/datum/round_event/shuttle_loan/end() + if(!SSshuttle.shuttle_loan || !SSshuttle.shuttle_loan.dispatched) + return + //make sure the shuttle was dispatched in time + SSshuttle.shuttle_loan = null + + //get empty turfs + var/list/empty_shuttle_turfs = list() + var/list/area/shuttle/shuttle_areas = SSshuttle.supply.shuttle_areas + for(var/area/shuttle/shuttle_area as anything in shuttle_areas) + for(var/turf/open/floor/shuttle_turf in shuttle_area) + if(shuttle_turf.is_blocked_turf()) + continue + empty_shuttle_turfs += shuttle_turf + if(!empty_shuttle_turfs.len) + return + + //let the situation spawn its items + var/list/spawn_list = list() + situation.spawn_items(spawn_list, empty_shuttle_turfs) + + var/false_positive = 0 + while(spawn_list.len && empty_shuttle_turfs.len) + var/turf/spawn_turf = pick_n_take(empty_shuttle_turfs) + if(spawn_turf.contents.len && false_positive < 5) + false_positive++ + continue + var/spawn_type = pick_n_take(spawn_list) + new spawn_type(spawn_turf) + +/datum/event_admin_setup/listed_options/shuttle_loan + input_text = "Select a loan offer?" + +/datum/event_admin_setup/listed_options/shuttle_loan/get_list() + var/datum/round_event_control/shuttle_loan/loan_event = event_control + var/list/valid_situations = subtypesof(/datum/shuttle_loan_situation) - loan_event.run_situations + return valid_situations + +/datum/event_admin_setup/listed_options/shuttle_loan/apply_to_event(datum/round_event/shuttle_loan/event) + event.situation = chosen diff --git a/code/modules/events/shuttle_loan/shuttle_loan_items.dm b/code/modules/events/shuttle_loan/shuttle_loan_items.dm new file mode 100644 index 000000000000..65190ed00291 --- /dev/null +++ b/code/modules/events/shuttle_loan/shuttle_loan_items.dm @@ -0,0 +1,23 @@ + +/obj/item/storage/belt/fannypack/yellow/bee_terrorist/PopulateContents() + new /obj/item/grenade/plastic/c4 (src) + new /obj/item/reagent_containers/pill/cyanide(src) + new /obj/item/grenade/chem_grenade/facid(src) + +/obj/item/paper/fluff/bee_objectives + name = "Objectives of a Bee Liberation Front Operative" + info = "Objective #1. Liberate all bees on the NT transport vessel 2416/B. Success!
Objective #2. Escape alive. Failed." + +/obj/machinery/syndicatebomb/shuttle_loan/Initialize(mapload) + . = ..() + anchored = TRUE + timer_set = rand(480, 600) //once the supply shuttle docks (after 5 minutes travel time), players have between 3-5 minutes to defuse the bomb + activate() + update_appearance() + +/obj/item/paper/fluff/cargo/bomb + name = "hastly scribbled note" + info = "GOOD LUCK!" + +/obj/item/paper/fluff/cargo/bomb/allyourbase + info = "Somebody set us up the bomb!" diff --git a/code/modules/events/spacevine.dm b/code/modules/events/spacevine.dm index fa1d027a9c32..4e742b3489c7 100644 --- a/code/modules/events/spacevine.dm +++ b/code/modules/events/spacevine.dm @@ -1,28 +1,120 @@ /datum/round_event_control/spacevine - name = "Spacevine" + name = "Space Vines" typepath = /datum/round_event/spacevine weight = 15 max_occurrences = 1 min_players = 25 + category = EVENT_CATEGORY_ENTITIES + description = "Kudzu begins to overtake the station. Might spawn man-traps." + min_wizard_trigger_potency = 4 + max_wizard_trigger_potency = 7 + admin_setup = list( + /datum/event_admin_setup/set_location/spacevine, + /datum/event_admin_setup/multiple_choice/spacevine, + /datum/event_admin_setup/input_number/spacevine_potency, + /datum/event_admin_setup/input_number/spacevine_production, + ) + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_ALIEN) + checks_antag_cap = TRUE + event_group = /datum/event_group/guests /datum/round_event/spacevine fakeable = FALSE + ///Override location the vines will spawn in. + var/turf/override_turf + ///used to confirm if admin selected mutations should be used or not. + var/mutations_overridden = FALSE + ///Admin selected mutations that the kudzu will spawn with, can be set to none to act as mutationless kudzu. + var/list/override_mutations = list() + ///Potency of the spawned kudzu. + var/potency + ///Production value of the spawned kuduz. + var/production /datum/round_event/spacevine/start() var/list/turfs = list() //list of all the empty floor turfs in the hallway areas - var/obj/structure/spacevine/SV = new() - for(var/area/maintenance/A in GLOB.areas) - for(var/turf/F in A.get_turfs_from_all_zlevels()) - if(F.Enter(SV)) - turfs += F + if(override_turf) + turfs += override_turf + else + var/obj/structure/spacevine/vine = new() + + for(var/area/hallway/area in GLOB.areas) + for(var/turf/open/floor in area.get_turfs_from_all_zlevels()) + if(floor.Enter(vine)) + turfs += floor + + qdel(vine) + + if(length(turfs)) //Pick a turf to spawn at if we can + var/turf/floor = pick(turfs) + var/list/selected_mutations = list() + + if(mutations_overridden == FALSE) + selected_mutations = list(pick(subtypesof(/datum/spacevine_mutation))) + else + selected_mutations = override_mutations + if(isnull(potency)) + potency = rand(50,100) + if(isnull(production)) + production = rand(1, 4) + + new /datum/spacevine_controller(floor, selected_mutations, potency, production, src) //spawn a controller at turf with randomized stats and a single random mutation + +/datum/event_admin_setup/set_location/spacevine + input_text = "Spawn vines at current location?" + +/datum/event_admin_setup/set_location/spacevine/apply_to_event(datum/round_event/spacevine/event) + event.override_turf = chosen_turf + +/datum/event_admin_setup/multiple_choice/spacevine + input_text = "Select starting mutations." + min_choices = 0 + +/datum/event_admin_setup/multiple_choice/spacevine/prompt_admins() + var/customize_mutations = tgui_alert(usr, "Select mutations?", event_control.name, list("Custom", "Random", "Cancel")) + switch(customize_mutations) + if("Custom") + return ..() + if("Random") + choices = list("[pick(subtypesof(/datum/spacevine_mutation))]") + else + return ADMIN_CANCEL_EVENT + +/datum/event_admin_setup/multiple_choice/spacevine/get_options() + return subtypesof(/datum/spacevine_mutation/) + +/datum/event_admin_setup/multiple_choice/spacevine/apply_to_event(datum/round_event/spacevine/event) + var/list/type_choices = list() + for(var/choice in choices) + type_choices += text2path(choice) + event.mutations_overridden = TRUE + event.override_mutations = type_choices + +/datum/event_admin_setup/input_number/spacevine_potency + input_text = "Set vine's potency (effects mutation frequency + max severity)" + max_value = 100 + +/datum/event_admin_setup/input_number/spacevine_potency/prompt_admins() + default_value = rand(50, 100) + return ..() + +/datum/event_admin_setup/input_number/spacevine_potency/apply_to_event(datum/round_event/spacevine/event) + event.potency = chosen_value + +/datum/event_admin_setup/input_number/spacevine_production + input_text = "Set vine's production (effects spreading cap + speed) (lower is faster)" + min_value = 1 + max_value = 10 - qdel(SV) +/datum/event_admin_setup/input_number/spacevine_production/prompt_admins() + default_value = rand(1, 4) + return ..() - if(turfs.len) //Pick a turf to spawn at if we can - var/turf/T = pick(turfs) - new /datum/spacevine_controller(T, pick(subtypesof(/datum/spacevine_mutation)), rand(10,100), rand(5,10), src) //spawn a controller at turf with randomized stats and a single random mutation +/datum/event_admin_setup/input_number/spacevine_production/apply_to_event(datum/round_event/spacevine/event) + event.production = chosen_value /datum/spacevine_mutation diff --git a/code/modules/events/spider_infestation.dm b/code/modules/events/spider_infestation.dm index dfb572c42a10..7cd00b9d0c17 100644 --- a/code/modules/events/spider_infestation.dm +++ b/code/modules/events/spider_infestation.dm @@ -4,17 +4,24 @@ weight = 5 max_occurrences = 1 min_players = 25 - dynamic_should_hijack = TRUE + category = EVENT_CATEGORY_ENTITIES + description = "Spawns spider eggs, ready to hatch." + min_wizard_trigger_potency = 5 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_EXTERNAL, TAG_ALIEN) + checks_antag_cap = TRUE /datum/round_event/spider_infestation - announceWhen = 400 + announce_when = 400 var/spawncount = 1 /datum/round_event/spider_infestation/setup() - announceWhen = rand(announceWhen, announceWhen + 50) + announce_when = rand(announce_when, announce_when + 50) spawncount = rand(5, 8) + setup = TRUE //storytellers /datum/round_event/spider_infestation/announce(fake) priority_announce("Unidentified lifesigns detected coming aboard [station_name()]. Secure any exterior access, including ducting and ventilation.", "Lifesign Alert", ANNOUNCER_ALIENS) diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm index 56660903e9f2..a9706c9baab2 100644 --- a/code/modules/events/spontaneous_appendicitis.dm +++ b/code/modules/events/spontaneous_appendicitis.dm @@ -5,6 +5,10 @@ max_occurrences = 4 earliest_start = 10 MINUTES min_players = 5 // To make your chance of getting help a bit higher. + track = EVENT_TRACK_MODERATE + tags = list(TAG_TARGETED) + category = EVENT_CATEGORY_HEALTH + description = "bestows someone with appendicitis." /datum/round_event/spontaneous_appendicitis fakeable = FALSE diff --git a/code/modules/events/stray_cargo.dm b/code/modules/events/stray_cargo.dm index bd0e32ace46b..08b7ceb82d1d 100644 --- a/code/modules/events/stray_cargo.dm +++ b/code/modules/events/stray_cargo.dm @@ -5,14 +5,45 @@ weight = 20 max_occurrences = 4 earliest_start = 10 MINUTES + category = EVENT_CATEGORY_BUREAUCRATIC + description = "A pod containing a random supply crate lands on the station." + admin_setup = list(/datum/event_admin_setup/set_location/stray_cargo, /datum/event_admin_setup/listed_options/stray_cargo) + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL) + +/datum/event_admin_setup/set_location/stray_cargo + input_text = "Aim pod at turf we're on?" + +/datum/event_admin_setup/set_location/stray_cargo/apply_to_event(datum/round_event/stray_cargo/event) + event.admin_override_turf = chosen_turf + +/datum/event_admin_setup/listed_options/stray_cargo + input_text = "Choose a cargo crate to drop." + normal_run_option = "Random Crate" + +/datum/event_admin_setup/listed_options/stray_cargo/get_list() + return sort_list(subtypesof(/datum/supply_pack), /proc/cmp_typepaths_asc) + +/datum/event_admin_setup/listed_options/stray_cargo/apply_to_event(datum/round_event/stray_cargo/event) + event.admin_override_contents = chosen + var/log_message = "[key_name_admin(usr)] has aimed a stray cargo pod at [event.admin_override_turf ? AREACOORD(event.admin_override_turf) : "a random location"]. The pod contents are [chosen ? chosen : "random"]." + message_admins(log_message) + log_admin(log_message) ///Spawns a cargo pod containing a random cargo supply pack on a random area of the station /datum/round_event/stray_cargo var/area/impact_area ///Randomly picked area + announce_chance = 75 var/list/possible_pack_types = list() ///List of possible supply packs dropped in the pod, if empty picks from the cargo list var/static/list/stray_spawnable_supply_packs = list() ///List of default spawnable supply packs, filtered from the cargo list + ///Admin setable override that is used instead of selecting a random location + var/atom/admin_override_turf + ///Admin setable override to spawn a specific cargo pack type + var/admin_override_contents /datum/round_event/stray_cargo/announce(fake) + if(fake) + impact_area = find_event_area() priority_announce("Stray cargo pod detected on long-range scanners. Expected location of impact: [impact_area.name].", "Collision Alert") /** @@ -20,8 +51,11 @@ * Also randomizes the start timer */ /datum/round_event/stray_cargo/setup() - startWhen = rand(20, 40) - impact_area = find_event_area() + start_when = rand(20, 40) + if(admin_override_turf) + impact_area = get_area(admin_override_turf) + else + impact_area = find_event_area() if(!impact_area) CRASH("No valid areas for cargo pod found.") var/list/turf_test = get_area_turfs(impact_area) @@ -34,6 +68,7 @@ var/datum/supply_pack/pack_type = pack if(initial(pack_type.special)) stray_spawnable_supply_packs -= pack + setup = TRUE //MONKESTATION ADDITION ///Spawns a random supply pack, puts it in a pod, and spawns it on a random tile of the selected area /datum/round_event/stray_cargo/start() @@ -43,18 +78,34 @@ var/turf/T = i if(T.density) valid_turfs -= T - var/turf/LZ = pick(valid_turfs) + var/turf/landing_zone + if(admin_override_turf) + landing_zone = admin_override_turf + else + landing_zone = pick(valid_turfs) var/pack_type - if(possible_pack_types.len) - pack_type = pick(possible_pack_types) + if(admin_override_contents) + pack_type = admin_override_contents else - pack_type = pick(stray_spawnable_supply_packs) - var/datum/supply_pack/SP = new pack_type - var/obj/structure/closet/crate/crate = SP.generate(null) - crate.locked = FALSE //Unlock secure crates - crate.update_appearance(UPDATE_ICON) + if(possible_pack_types.len) + pack_type = pick(possible_pack_types) + else + pack_type = pick(stray_spawnable_supply_packs) + var/datum/supply_pack/supply_pack + if(ispath(pack_type, /datum/supply_pack)) + supply_pack = new pack_type + else // treat this as a supply pack id and resolving it with SSshuttle + if(admin_override_contents) + supply_pack = admin_override_contents //Syndicate crates create a new datum while being customized which will result in this being triggered. Outside of this situation this should never trigger + else + supply_pack = SSshuttle.supply_packs[pack_type] + var/obj/structure/closet/crate/crate = supply_pack.generate(null) + if(crate) //empty supply packs are a thing! get memed on. + crate.locked = FALSE //Unlock secure crates + crate.update_appearance() var/obj/structure/closet/supplypod/pod = make_pod() - new /obj/effect/DPtarget(LZ, pod, crate) + var/obj/effect/DPtarget/landing_marker = new(landing_zone, pod, crate) + announce_to_ghosts(landing_marker) ///Handles the creation of the pod, in case it needs to be modified beforehand /datum/round_event/stray_cargo/proc/make_pod() diff --git a/code/modules/events/supermatter_surge.dm b/code/modules/events/supermatter_surge.dm index 994ab5fb6bb4..e1f7b9f4bdf6 100644 --- a/code/modules/events/supermatter_surge.dm +++ b/code/modules/events/supermatter_surge.dm @@ -4,37 +4,44 @@ weight = 15 max_occurrences = 4 earliest_start = 10 MINUTES - var/forced_power - -/datum/round_event_control/supermatter_surge/admin_setup() - if(!check_rights(R_FUN)) - return - - var/selected_power = input("How severe should it be? Between 1000 and 100000. Press cancel for random") as num|null - if(!selected_power) - selected_power = rand(1000, 100000) - selected_power = clamp(selected_power, 1000, 100000) - forced_power = selected_power - - - -/datum/round_event_control/supermatter_surge/canSpawnEvent() + category = EVENT_CATEGORY_ENGINEERING + description = "The supermatter will increase in power and heat by a random amount, and announce it." + min_wizard_trigger_potency = 4 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MODERATE + tags = list(TAG_DESTRUCTIVE, TAG_COMMUNAL) + event_group = /datum/event_group/error + admin_setup = list( + /datum/event_admin_setup/input_number/surge_spiciness, + ) + +/datum/round_event_control/supermatter_surge/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) if(GLOB.main_supermatter_engine?.has_been_powered) return ..() /datum/round_event/supermatter_surge - var/power = 2000 - announceWhen = 1 + var/power + announce_when = 1 /datum/round_event/supermatter_surge/setup() - var/datum/round_event_control/supermatter_surge/C = control - if(C.forced_power) - power = C.forced_power - else + if(!power) power = rand(1000,100000) + setup = TRUE /datum/round_event/supermatter_surge/announce(fake) priority_announce("Class [round(power/500) + 1] supermatter surge detected. Intervention may be required.", "Anomaly Alert") /datum/round_event/supermatter_surge/start() GLOB.main_supermatter_engine.surge(power) + +/datum/event_admin_setup/input_number/surge_spiciness + input_text = "Set surge power. (Higher is more severe.)" + min_value = 1000 + max_value = 100000 + +/datum/event_admin_setup/input_number/surge_spiciness/prompt_admins() + default_value = rand(1000, 100000) + return ..() + +/datum/event_admin_setup/input_number/surge_spiciness/apply_to_event(datum/round_event/supermatter_surge/event) + event.power = chosen_value diff --git a/code/modules/events/swarmer.dm b/code/modules/events/swarmer.dm index 00c4ba72eebc..70536e119e15 100644 --- a/code/modules/events/swarmer.dm +++ b/code/modules/events/swarmer.dm @@ -5,13 +5,16 @@ max_occurrences = 1 //Only once okay fam earliest_start = 30 MINUTES min_players = 15 - dynamic_should_hijack = TRUE + track = EVENT_TRACK_MAJOR + tags = list(TAG_ALIEN, TAG_DESTRUCTIVE) + category = EVENT_CATEGORY_ENTITIES + description = "spawns swamers." /datum/round_event/spawn_swarmer/announce(fake) priority_announce("Our long-range sensors have detected that your station's defenses have been breached by some sort of alien device. We suggest searching for and destroying it as soon as possible.", "[command_name()] High-Priority Update") /datum/round_event/spawn_swarmer - announceWhen = 70 + announce_when = 70 /datum/round_event/spawn_swarmer/start() var/list/spawn_locs = list() diff --git a/code/modules/events/tzimisce.dm b/code/modules/events/tzimisce.dm index 1fed38f25263..559a7e14c55a 100644 --- a/code/modules/events/tzimisce.dm +++ b/code/modules/events/tzimisce.dm @@ -4,42 +4,24 @@ max_occurrences = 2 min_players = 25 earliest_start = 45 MINUTES + track = EVENT_TRACK_ROLESET + tags = list(TAG_SPOOKY, TAG_MAGICAL, TAG_COMBAT) + category = EVENT_CATEGORY_ENTITIES + description = "spawns a bloodsucker from ghost roles." -/datum/round_event_control/tzimisce/bloodsucker - name = "Spawn Tzimisce - Bloodsucker" - max_occurrences = 1 -// weight = 2000 - typepath = /datum/round_event/ghost_role/tzimisce/bloodsucker - min_players = 25 - earliest_start = 30 MINUTES - gamemode_whitelist = list("bloodsucker","traitorsucker") - -/datum/round_event/ghost_role/tzimisce/bloodsucker - fakeable = FALSE - var/cancel_me = TRUE - -/datum/round_event/ghost_role/tzimisce/bloodsucker/start() - for(var/mob/living/carbon/human/all_players in GLOB.player_list) - if(IS_BLOODSUCKER(all_players) || IS_MONSTERHUNTER(all_players)) - message_admins("BLOODSUCKER NOTICE: Tzimisces have found a valid Target.") - cancel_me = FALSE - break - if(cancel_me) - kill() - return - try_spawning() +/datum/round_event_control/tzimisce/canSpawnEvent(players_amt, allow_magic, fake_check) + . = ..() + if(.) + for(var/mob/living/carbon/human/all_players in GLOB.player_list) + if(IS_BLOODSUCKER(all_players) || IS_MONSTERHUNTER(all_players)) + return TRUE + return FALSE /datum/round_event/ghost_role/tzimisce - var/success_spawn = 0 minimum_required = 1 role_name = "Tzimisce" fakeable = FALSE -/datum/round_event/ghost_role/tzimisce/kill() - if(!success_spawn && control) - control.occurrences-- - return ..() - /datum/round_event/ghost_role/tzimisce/spawn_role() //selecting a spawn_loc if(!SSjob.latejoin_trackers.len) diff --git a/code/modules/events/wizard/curseditems.dm b/code/modules/events/wizard/curseditems.dm index 01b30f02749a..6c22947b4cb1 100644 --- a/code/modules/events/wizard/curseditems.dm +++ b/code/modules/events/wizard/curseditems.dm @@ -40,7 +40,7 @@ for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) if(ruins_spaceworthiness && !is_station_level(H.z) || isspaceturf(H.loc) || isplasmaman(H)) continue //#savetheminers - if(ruins_wizard_loadout && iswizard(H)) + if(ruins_wizard_loadout && IS_WIZARD(H)) continue if(item_set == "catgirls2015") //Wizard code means never having to say you're sorry H.gender = FEMALE diff --git a/code/modules/events/wizard/departmentrevolt.dm b/code/modules/events/wizard/departmentrevolt.dm index 99c2ef3d4d93..7cb141cd3f8a 100644 --- a/code/modules/events/wizard/departmentrevolt.dm +++ b/code/modules/events/wizard/departmentrevolt.dm @@ -4,34 +4,50 @@ typepath = /datum/round_event/wizard/deprevolt max_occurrences = 1 earliest_start = 0 MINUTES + admin_setup = list( + /datum/event_admin_setup/listed_options/departmental_revolt, + /datum/event_admin_setup/question/departmental_revolt_annouce, + /datum/event_admin_setup/question/departmental_revolt_dangerous + ) - var/picked_department - var/announce = FALSE - var/dangerous_nation = TRUE -/datum/round_event_control/wizard/deprevolt/admin_setup() - if(!check_rights(R_FUN)) - return - var/list/options = list("Random", "Uprising of Assistants", "Medical", "Engineering", "Science", "Supply", "Service", "Security") - picked_department = input(usr,"Which department should revolt?","Select a department") as null|anything in options +///which department is revolting? +/datum/event_admin_setup/listed_options/departmental_revolt + input_text = "Which department should revolt?" + normal_run_option = "Random" - var/announce_question = tgui_alert(usr, "Announce This New Independent State?", "Secession", list("Announce", "No Announcement")) - if(announce_question == "Announce") - announce = TRUE +/datum/event_admin_setup/listed_options/departmental_revolt/get_list() + return subtypesof(/datum/job_department) + +/datum/event_admin_setup/listed_options/departmental_revolt/apply_to_event(datum/round_event/wizard/deprevolt/event) + event.picked_department = chosen - var/dangerous_question = tgui_alert(usr, "Dangerous Nation? This means they will fight other nations.", "Conquest", list("Yes", "No")) - if(dangerous_question == "No") - dangerous_nation = FALSE +/// Announce the separatist nation to the round? +/datum/event_admin_setup/question/departmental_revolt_annouce + input_text = "Announce This New Independent State?" - //this is down here to allow the random system to pick a department whilst considering other independent departments - if(!picked_department || picked_department == "Random") - picked_department = null - return +/datum/event_admin_setup/question/departmental_revolt_annouce/apply_to_event(datum/round_event/wizard/deprevolt/event) + event.announcement = chosen + +/// Is it going to try fighting other nations? +/datum/event_admin_setup/question/departmental_revolt_dangerous + input_text = "Dangerous Nation? This means they will fight other nations." + +/datum/event_admin_setup/question/departmental_revolt_dangerous/apply_to_event(datum/round_event/wizard/deprevolt/event) + event.dangerous = chosen -/datum/round_event/wizard/deprevolt/start() - var/datum/round_event_control/wizard/deprevolt/event_control = control +/datum/round_event/wizard/deprevolt + ///which department is revolting? + var/picked_department + /// Announce the separatist nation to the round? + var/announcement = FALSE + /// Is it going to try fighting other nations? + var/dangerous = TRUE + + +/datum/round_event/wizard/deprevolt/start() var/list/independent_departments = list() ///departments that are already independent, these will be disallowed to be randomly picked var/list/cannot_pick = list() ///departments that are already independent, these will be disallowed to be randomly picked for(var/datum/antagonist/separatist/separatist_datum in GLOB.antagonists) @@ -40,12 +56,9 @@ independent_departments |= separatist_datum.nation cannot_pick |= separatist_datum.nation.nation_department - var/announcement = event_control.announce - var/dangerous = event_control.dangerous_nation var/department - if(event_control.picked_department) - department = event_control.picked_department - event_control.picked_department = null + if(picked_department) + department = picked_department else department = pick(list("Uprising of Assistants", "Medical", "Engineering", "Science", "Supply", "Service", "Security") - cannot_pick) if(!department) diff --git a/code/modules/events/wizard/greentext.dm b/code/modules/events/wizard/greentext.dm index ef4cf6a488a4..01d7e18e2447 100644 --- a/code/modules/events/wizard/greentext.dm +++ b/code/modules/events/wizard/greentext.dm @@ -4,6 +4,9 @@ typepath = /datum/round_event/wizard/greentext max_occurrences = 1 earliest_start = 0 MINUTES + description = "The Green Text appears on the station, tempting people to try and pick it up." + min_wizard_trigger_potency = 5 + max_wizard_trigger_potency = 7 /datum/round_event/wizard/greentext/start() diff --git a/code/modules/events/wizard/imposter.dm b/code/modules/events/wizard/imposter.dm index 968db4909e0b..85b5d84941fb 100644 --- a/code/modules/events/wizard/imposter.dm +++ b/code/modules/events/wizard/imposter.dm @@ -1,9 +1,12 @@ /datum/round_event_control/wizard/imposter //Mirror Mania - name = "Imposter Wizard" + name = "Imposter Wizard" //sussus amogus ^how times change in 10 years weight = 1 typepath = /datum/round_event/wizard/imposter max_occurrences = 1 earliest_start = 0 MINUTES + description = "Spawns a doppelganger of the wizard." + min_wizard_trigger_potency = 2 + max_wizard_trigger_potency = 7 /datum/round_event/wizard/imposter/start() for(var/datum/mind/M in SSticker.mode.wizards) diff --git a/code/modules/events/wizard/invincible.dm b/code/modules/events/wizard/invincible.dm index e477105b4e9d..5a35cadab707 100644 --- a/code/modules/events/wizard/invincible.dm +++ b/code/modules/events/wizard/invincible.dm @@ -4,9 +4,12 @@ typepath = /datum/round_event/wizard/invincible max_occurrences = 5 earliest_start = 0 MINUTES + description = "Everyone is invincible for a short time ticks." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 7 /datum/round_event/wizard/invincible/start() for(var/mob/living/carbon/human/H in GLOB.alive_mob_list) H.reagents.add_reagent(/datum/reagent/medicine/adminordrazine, 40) //100 ticks of absolute invinciblity (barring gibs) - to_chat(H, span_notice("You feel invincible, nothing can hurt you!")) \ No newline at end of file + to_chat(H, span_notice("You feel invincible, nothing can hurt you!")) diff --git a/code/modules/events/wizard/lava.dm b/code/modules/events/wizard/lava.dm index 9a882b45dfdc..0805d54e5612 100644 --- a/code/modules/events/wizard/lava.dm +++ b/code/modules/events/wizard/lava.dm @@ -4,9 +4,12 @@ typepath = /datum/round_event/wizard/lava max_occurrences = 3 earliest_start = 0 MINUTES + description = "Turns the floor into hot lava." + min_wizard_trigger_potency = 5 + max_wizard_trigger_potency = 7 /datum/round_event/wizard/lava - endWhen = 0 + end_when = 0 var/started = FALSE /datum/round_event/wizard/lava/start() diff --git a/code/modules/events/wizard/madness.dm b/code/modules/events/wizard/madness.dm index 6c52dabe2023..95749cc74649 100644 --- a/code/modules/events/wizard/madness.dm +++ b/code/modules/events/wizard/madness.dm @@ -3,26 +3,24 @@ weight = 1 typepath = /datum/round_event/wizard/madness earliest_start = 0 MINUTES + description = "Reveals a horrifying truth to everyone, giving them a trauma." + admin_setup = list(/datum/event_admin_setup/text_input/madness) - var/forced_secret - -/datum/round_event_control/wizard/madness/admin_setup() - if(!check_rights(R_FUN)) - return - - var/suggested = pick(strings(REDPILL_FILE, "redpill_questions")) - - forced_secret = (input(usr, "What horrifying truth will you reveal?", "Curse of Madness", suggested) as text) || suggested - -/datum/round_event/wizard/madness/start() - var/datum/round_event_control/wizard/madness/C = control - +/datum/round_event/wizard/madness + /// the horrifying truth sent to the crew, can be picked by admins. var/horrifying_truth - if(C.forced_secret) - horrifying_truth = C.forced_secret - C.forced_secret = null - else +/datum/round_event/wizard/madness/start() + if(!horrifying_truth) horrifying_truth = pick(strings(REDPILL_FILE, "redpill_questions")) curse_of_madness(null, horrifying_truth) + +/datum/event_admin_setup/text_input/madness + input_text = "What horrifying truth will you reveal?" + +/datum/event_admin_setup/text_input/madness/get_text_suggestion() + return pick(strings(REDPILL_FILE, "redpill_questions")) + +/datum/event_admin_setup/text_input/madness/apply_to_event(datum/round_event/wizard/madness/event) + event.horrifying_truth = chosen diff --git a/code/modules/events/wizard/magicarp.dm b/code/modules/events/wizard/magicarp.dm index c2df58cdf4ff..2eb7a8640bef 100644 --- a/code/modules/events/wizard/magicarp.dm +++ b/code/modules/events/wizard/magicarp.dm @@ -4,13 +4,17 @@ typepath = /datum/round_event/wizard/magicarp max_occurrences = 1 earliest_start = 0 MINUTES + description = "Summons a school of carps with magic projectiles." + min_wizard_trigger_potency = 4 + max_wizard_trigger_potency = 6 /datum/round_event/wizard/magicarp - announceWhen = 3 - startWhen = 50 + announce_when = 3 + start_when = 50 /datum/round_event/wizard/magicarp/setup() - startWhen = rand(40, 60) + start_when = rand(40, 60) + setup = TRUE //storytellers /datum/round_event/wizard/magicarp/announce(fake) priority_announce("Unknown magical entities have been detected near [station_name()], please stand-by.", "Lifesign Alert") diff --git a/code/modules/events/wizard/petsplosion.dm b/code/modules/events/wizard/petsplosion.dm index fb4d433905bf..ecc1d33abb19 100644 --- a/code/modules/events/wizard/petsplosion.dm +++ b/code/modules/events/wizard/petsplosion.dm @@ -4,6 +4,9 @@ typepath = /datum/round_event/wizard/petsplosion max_occurrences = 1 //Exponential growth is nothing to sneeze at! earliest_start = 0 MINUTES + description = "Rapidly multiplies the animals on the station." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 4 var/mobs_to_dupe = 0 /datum/round_event_control/wizard/petsplosion/preRunEvent() @@ -16,7 +19,7 @@ ..() /datum/round_event/wizard/petsplosion - endWhen = 61 //1 minute (+1 tick for endWhen not to interfere with tick) + end_when = 61 //1 minute (+1 tick for end_when not to interfere with tick) var/countdown = 0 var/mobs_duped = 0 diff --git a/code/modules/events/wizard/race.dm b/code/modules/events/wizard/race.dm index e40809910413..daaa8e681eb0 100644 --- a/code/modules/events/wizard/race.dm +++ b/code/modules/events/wizard/race.dm @@ -4,6 +4,7 @@ typepath = /datum/round_event/wizard/race max_occurrences = 5 earliest_start = 0 MINUTES + description = "Gives everyone a random race." /datum/round_event/wizard/race/start() diff --git a/code/modules/events/wizard/rpgloot.dm b/code/modules/events/wizard/rpgloot.dm index d3763745c92a..c7858800916a 100644 --- a/code/modules/events/wizard/rpgloot.dm +++ b/code/modules/events/wizard/rpgloot.dm @@ -4,6 +4,9 @@ typepath = /datum/round_event/wizard/rpgloot max_occurrences = 1 earliest_start = 0 MINUTES + description = "Every item in the world will have fantastical names." + min_wizard_trigger_potency = 4 + max_wizard_trigger_potency = 7 /datum/round_event/wizard/rpgloot/start() var/upgrade_scroll_chance = 0 diff --git a/code/modules/events/wizard/shuffle.dm b/code/modules/events/wizard/shuffle.dm index 2a580193a6dd..5e10b2288d8f 100644 --- a/code/modules/events/wizard/shuffle.dm +++ b/code/modules/events/wizard/shuffle.dm @@ -7,6 +7,9 @@ typepath = /datum/round_event/wizard/shuffleloc max_occurrences = 5 earliest_start = 0 MINUTES + description = "Shuffles everyone around on the station." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 7 /datum/round_event/wizard/shuffleloc/start() var/list/moblocs = list() @@ -43,6 +46,7 @@ typepath = /datum/round_event/wizard/shufflenames max_occurrences = 5 earliest_start = 0 MINUTES + description = "Shuffles the names of everyone around the station." /datum/round_event/wizard/shufflenames/start() var/list/mobnames = list() @@ -77,12 +81,13 @@ typepath = /datum/round_event/wizard/shuffleminds max_occurrences = 3 earliest_start = 0 MINUTES + description = "Shuffles the minds of everyone around the station, except for the wizard." /datum/round_event/wizard/shuffleminds/start() var/list/mobs_to_swap = list() for(var/mob/living/carbon/human/alive_human in GLOB.alive_mob_list) - if(alive_human.stat != CONSCIOUS || !alive_human.mind || iswizard(alive_human)) + if(alive_human.stat != CONSCIOUS || !alive_human.mind || IS_WIZARD(alive_human)) continue //the wizard(s) are spared on this one mobs_to_swap += alive_human diff --git a/code/modules/events/wizard/summons.dm b/code/modules/events/wizard/summons.dm index 3a5150e39fc7..e2f69f7d3b1c 100644 --- a/code/modules/events/wizard/summons.dm +++ b/code/modules/events/wizard/summons.dm @@ -4,6 +4,7 @@ typepath = /datum/round_event/wizard/summonguns max_occurrences = 1 earliest_start = 0 MINUTES + description = "Summons a gun for everyone. Might turn people into survivalists." /datum/round_event_control/wizard/summonguns/New() if(CONFIG_GET(flag/no_summon_guns)) diff --git a/code/modules/events/wormholes.dm b/code/modules/events/wormholes.dm index 42da72fe74fb..4ad02d6f3f0f 100644 --- a/code/modules/events/wormholes.dm +++ b/code/modules/events/wormholes.dm @@ -6,11 +6,18 @@ GLOBAL_LIST_EMPTY(all_wormholes) // So we can pick wormholes to teleport to max_occurrences = 3 weight = 2 min_players = 2 - + category = EVENT_CATEGORY_SPACE + description = "Space time anomalies appear on the station, randomly teleporting people who walk into them." + min_wizard_trigger_potency = 3 + max_wizard_trigger_potency = 7 + track = EVENT_TRACK_MODERATE + tags = list(TAG_COMMUNAL, TAG_MAGICAL) + event_group = /datum/event_group/anomalies + /datum/round_event/wormholes - announceWhen = 10 - endWhen = 60 + announce_when = 10 + end_when = 60 var/list/pick_turfs = list() var/list/wormholes = list() @@ -18,8 +25,9 @@ GLOBAL_LIST_EMPTY(all_wormholes) // So we can pick wormholes to teleport to var/number_of_wormholes = 400 /datum/round_event/wormholes/setup() - announceWhen = rand(0, 20) - endWhen = rand(40, 80) + announce_when = rand(0, 20) + end_when = rand(40, 80) + setup = TRUE //storytellers /datum/round_event/wormholes/start() for(var/turf/open/floor/T in world) diff --git a/code/modules/events/zombie_infection.dm b/code/modules/events/zombie_infection.dm index 81aa39fbb88b..46fe599ef6a9 100644 --- a/code/modules/events/zombie_infection.dm +++ b/code/modules/events/zombie_infection.dm @@ -4,7 +4,12 @@ max_occurrences = 1 min_players = 20 weight = 4 - dynamic_should_hijack = TRUE + track = EVENT_TRACK_MAJOR + tags = list(TAG_SPOOKY, TAG_COMBAT) + category = EVENT_CATEGORY_ENTITIES + description = "spawns a zombie somewhere on the station." + + /datum/round_event/ghost_role/zombie minimum_required = 1 role_name = "zombie" diff --git a/code/modules/food_and_drinks/kitchen_machinery/processor.dm b/code/modules/food_and_drinks/kitchen_machinery/processor.dm index 2ec311367394..de86ae5fa25b 100644 --- a/code/modules/food_and_drinks/kitchen_machinery/processor.dm +++ b/code/modules/food_and_drinks/kitchen_machinery/processor.dm @@ -110,7 +110,7 @@ to_chat(user, span_warning("You need a better grip to do that!")) return var/mob/living/pushed_mob = user.pulling - visible_message(span_warner("[user] stuffs [pushed_mob] into [src]!")) + visible_message(span_warning("[user] stuffs [pushed_mob] into [src]!")) pushed_mob.forceMove(src) user.stop_pulling() return diff --git a/code/modules/goals/station_goals/shield.dm b/code/modules/goals/station_goals/shield.dm index fae2ca31f60e..fedbb9065f52 100644 --- a/code/modules/goals/station_goals/shield.dm +++ b/code/modules/goals/station_goals/shield.dm @@ -161,7 +161,7 @@ change_meteor_chance(0.5) /obj/machinery/satellite/meteor_shield/proc/change_meteor_chance(mod) - var/datum/round_event_control/E = locate(/datum/round_event_control/meteor_wave) in SSevents.control + var/datum/round_event_control/E = locate(/datum/round_event_control/meteor_wave) in SSgamemode.control if(E) E.weight *= mod diff --git a/code/modules/holiday/easter.dm b/code/modules/holiday/easter.dm index 46ad9c6b227c..6ced3f726d4e 100644 --- a/code/modules/holiday/easter.dm +++ b/code/modules/holiday/easter.dm @@ -5,6 +5,10 @@ weight = -1 max_occurrences = 1 earliest_start = 0 MINUTES + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL, TAG_POSITIVE) + category = EVENT_CATEGORY_BUREAUCRATIC + description = "does literally nothing aside from announce easter." /datum/round_event/easter/announce(fake) priority_announce(pick("Hip-hop into Easter!","Find some Bunny's stash!","Today is National 'Hunt a Wabbit' Day.","Be kind, give Chocolate Eggs!")) @@ -16,6 +20,10 @@ typepath = /datum/round_event/rabbitrelease weight = 5 max_occurrences = 10 + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL, TAG_POSITIVE) + category = EVENT_CATEGORY_ENTITIES + description = "spawns some rabbits." /datum/round_event/rabbitrelease/announce(fake) priority_announce("Unidentified furry objects detected coming aboard [station_name()]. Beware of Adorable-ness.", "Fluffy Alert", ANNOUNCER_ALIENS) diff --git a/code/modules/holiday/holidays.dm b/code/modules/holiday/holidays.dm index bbbd99560559..29b972f01fc5 100644 --- a/code/modules/holiday/holidays.dm +++ b/code/modules/holiday/holidays.dm @@ -103,10 +103,10 @@ if(PATTERN_RAINBOW) var/datum/holiday/pride_week/rainbow_datum = new() return rainbow_datum.get_holiday_colors(thing_to_color, PATTERN_DEFAULT) - if(!length(GLOB.holidays)) + if(!length(SSgamemode.holidays)) return - for(var/holiday_key in GLOB.holidays) - var/datum/holiday/holiday_real = GLOB.holidays[holiday_key] + for(var/holiday_key in SSgamemode.holidays) + var/datum/holiday/holiday_real = SSgamemode.holidays[holiday_key] if(!holiday_real.holiday_colors) continue return holiday_real.get_holiday_colors(thing_to_color, pattern || holiday_real.holiday_pattern) diff --git a/code/modules/meteors/meteor_spawning.dm b/code/modules/meteors/meteor_spawning.dm new file mode 100644 index 000000000000..b2da16d11469 --- /dev/null +++ b/code/modules/meteors/meteor_spawning.dm @@ -0,0 +1,118 @@ +#define MAP_EDGE_PAD 5 + +/proc/spawn_meteors(number = 10, list/meteor_types, direction) + for(var/i in 1 to number) + spawn_meteor(meteor_types, direction) + +/proc/spawn_meteor(list/meteor_types, direction, atom/target) + if (SSmapping.is_planetary()) + stack_trace("Tried to spawn meteors in a map which isn't in space.") + return // We're not going to find any space turfs here + var/turf/picked_start + var/turf/picked_goal + var/max_i = 10//number of tries to spawn meteor. + while(!isspaceturf(picked_start)) + var/start_side + if(direction) //If a direction has been specified, we set start_side to it. Otherwise, pick randomly + start_side = direction + else + start_side = pick(GLOB.cardinals) + var/start_Z = pick(SSmapping.levels_by_trait(ZTRAIT_STATION)) + picked_start = spaceDebrisStartLoc(start_side, start_Z) + if(target) + if(!isturf(target)) + target = get_turf(target) + picked_goal = target + else + picked_goal = spaceDebrisFinishLoc(start_side, start_Z) + max_i-- + if(max_i <= 0) + return + var/new_meteor = pick_weight(meteor_types) + new new_meteor(picked_start, picked_goal) + +/proc/spaceDebrisStartLoc(start_side, Z) + var/starty + var/startx + switch(start_side) + if(NORTH) + starty = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD) + startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) + if(EAST) + starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + startx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD) + if(SOUTH) + starty = (TRANSITIONEDGE + MAP_EDGE_PAD) + startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) + if(WEST) + starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + startx = (TRANSITIONEDGE + MAP_EDGE_PAD) + . = locate(startx, starty, Z) + +/proc/spaceDebrisFinishLoc(startSide, Z) + var/endy + var/endx + switch(startSide) + if(NORTH) + endy = (TRANSITIONEDGE + MAP_EDGE_PAD) + endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) + if(EAST) + endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + endx = (TRANSITIONEDGE + MAP_EDGE_PAD) + if(SOUTH) + endy = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD) + endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)) + if(WEST) + endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)) + endx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD) + . = locate(endx, endy, Z) + +/** + * Recieves a mob candidate, transforms them into a changeling, and hurls them at the station inside of a changeling meteor + * + * Takes a given candidate and turns them into a changeling, generates a changeling meteor, and throws it at the station. + * Returns the changeling generated by the event, NOT the meteor. This is so that it plays nicely with the dynamic ruleset + * while still being usable in the ghost_role event as well. + * + * Arguments: + * * candidate - The mob (player) to be transformed into a changeling and meteored. + */ +// /proc/generate_changeling_meteor(mob/dead/selected) +// var/datum/mind/player_mind = new(selected.key) +// player_mind.active = TRUE + +// var/turf/picked_start + +// if (SSmapping.is_planetary()) +// var/list/possible_start = list() +// for(var/obj/effect/landmark/carpspawn/spawn_point in GLOB.landmarks_list) +// possible_start += get_turf(spawn_point) +// picked_start = pick(possible_start) +// else +// var/start_z = pick(SSmapping.levels_by_trait(ZTRAIT_STATION)) +// var/start_side = pick(GLOB.cardinals) +// picked_start = spaceDebrisStartLoc(start_side, start_z) + +// if (!picked_start) +// stack_trace("No valid spawn location for changeling meteor") + +// var/obj/effect/meteor/meaty/changeling/changeling_meteor = new(picked_start, get_random_station_turf()) +// var/mob/living/carbon/human/new_changeling = new(picked_start) + +// new_changeling.forceMove(changeling_meteor) //Place our payload inside of its vessel + +// player_mind.transfer_to(new_changeling) +// player_mind.special_role = ROLE_CHANGELING_MIDROUND +// player_mind.add_antag_datum(/datum/antagonist/changeling/space) +// SEND_SOUND(new_changeling, 'sound/magic/mutate.ogg') +// message_admins("[ADMIN_LOOKUPFLW(new_changeling)] has been made into a space changeling by an event.") +// new_changeling.log_message("was spawned as a midround space changeling by an event.", LOG_GAME) + +// var/datum/antagonist/changeling/changeling_datum = locate() in player_mind.antag_datums +// changeling_datum.give_power(/datum/action/changeling/void_adaption) +// changeling_datum.give_power(/datum/action/changeling/weapon/arm_blade) +// new_changeling.equipOutfit(/datum/outfit/changeling_space) + +// return new_changeling + +#undef MAP_EDGE_PAD diff --git a/code/modules/meteors/meteor_types.dm b/code/modules/meteors/meteor_types.dm new file mode 100644 index 000000000000..204ddd9a56a9 --- /dev/null +++ b/code/modules/meteors/meteor_types.dm @@ -0,0 +1,457 @@ +#define DEFAULT_METEOR_LIFETIME 1800 + +/obj/effect/meteor + name = "\proper the concept of meteor" + desc = "You should probably run instead of gawking at this." + icon = 'icons/obj/meteor.dmi' + icon_state = "small" + density = TRUE + anchored = TRUE + pass_flags = PASSTABLE + + ///The resilience of our meteor + var/hits = 4 + ///Level of ex_act to be called on hit. + var/hitpwr = EXPLODE_HEAVY + //Should we shake people's screens on impact + var/heavy = FALSE + ///Sound to play when you hit something + var/meteorsound = 'sound/effects/meteorimpact.ogg' + ///Our starting z level, prevents infinite meteors + var/z_original + ///Used for determining which meteors are most interesting + var/threat = 0 + + //Potential items to spawn when you die + var/list/meteordrop = list(/obj/item/stack/ore/iron) + ///How much stuff to spawn when you die + var/dropamt = 2 + + ///The thing we're moving towards, usually a turf + var/atom/dest + ///Lifetime in seconds + var/lifetime = DEFAULT_METEOR_LIFETIME + + ///Used by Stray Meteor event to indicate meteor type (the type of sensor that "detected" it) in announcement + var/signature = "motion" + +/obj/effect/meteor/Initialize(mapload, turf/target) + . = ..() + z_original = z + GLOB.meteor_list += src + SSaugury.register_doom(src, threat) + SpinAnimation() + chase_target(target) + +/obj/effect/meteor/Destroy() + GLOB.meteor_list -= src + return ..() + +/obj/effect/meteor/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) + . = ..() + if(QDELETED(src)) + return + + if(old_loc != loc)//If did move, ram the turf we get in + var/turf/T = get_turf(loc) + ram_turf(T) + + if(prob(10) && !isspaceturf(T))//randomly takes a 'hit' from ramming + get_hit() + + if(z != z_original || loc == get_turf(dest)) + qdel(src) + return + +/obj/effect/meteor/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) + return TRUE //Keeps us from drifting for no reason + +/obj/effect/meteor/Bump(atom/A) + . = ..() //What could go wrong + if(A) + ram_turf(get_turf(A)) + playsound(src.loc, meteorsound, 40, TRUE) + get_hit() + +/obj/effect/meteor/proc/chase_target(atom/chasing, delay = 1) + set waitfor = FALSE + if(chasing) + walk_towards(src, chasing, delay) + +///Deals with what happens when we stop moving, IE we die +/obj/effect/meteor/proc/handle_stopping() + SIGNAL_HANDLER + if(!QDELETED(src)) + qdel(src) + +/obj/effect/meteor/proc/ram_turf(turf/T) + //first yell at mobs about them dying horribly + for(var/mob/living/thing in T) + thing.visible_message(span_warning("[src] slams into [thing]."), span_userdanger("[src] slams into you!.")) + + //then, ram the turf + switch(hitpwr) + if(EXPLODE_DEVASTATE) + SSexplosions.highturf += T + if(EXPLODE_HEAVY) + SSexplosions.medturf += T + if(EXPLODE_LIGHT) + SSexplosions.lowturf += T + +//process getting 'hit' by colliding with a dense object +//or randomly when ramming turfs +/obj/effect/meteor/proc/get_hit() + hits-- + if(hits <= 0) + make_debris() + meteor_effect() + qdel(src) + +/obj/effect/meteor/attackby(obj/item/I, mob/user, params) + if(I.tool_behaviour == TOOL_MINING) + make_debris() + qdel(src) + else + . = ..() + +/obj/effect/meteor/proc/make_debris() + for(var/throws = dropamt, throws > 0, throws--) + var/thing_to_spawn = pick(meteordrop) + new thing_to_spawn(get_turf(src)) + +/obj/effect/meteor/proc/meteor_effect() + if(heavy) + var/sound/meteor_sound = sound(meteorsound) + var/random_frequency = get_rand_frequency() + + for(var/mob/M in GLOB.player_list) + if((M.orbiting) && (SSaugury.watchers[M])) + continue + var/turf/T = get_turf(M) + if(!T || T.z != src.z) + continue + var/dist = get_dist(M.loc, src.loc) + shake_camera(M, dist > 20 ? 2 : 4, dist > 20 ? 1 : 3) + M.playsound_local(src.loc, meteor_sound, 50, 1, random_frequency, 10) + +/** + * Handles the meteor's interaction with meteor shields. + * + * Returns TRUE if the meteor should be destroyed. Overridable for custom shield interaction. + * Return FALSE if a meteor's interaction with meteor shields should NOT destroy it. + * + * Arguments: + * * defender - The meteor shield that is vaporizing us. + */ + +/obj/effect/meteor/proc/shield_defense(obj/machinery/satellite/meteor_shield/defender) + return TRUE + +/////////////////////// +//Meteor types +/////////////////////// + +//Sand +/obj/effect/meteor/sand + name = "space sand" + icon_state = "dust" + hits = 2 + hitpwr = EXPLODE_LIGHT + meteorsound = 'sound/items/dodgeball.ogg' + threat = 1 + +/obj/effect/meteor/sand/make_debris() + return //We drop NOTHING + +/obj/effect/meteor/sand/ram_turf(turf/turf_to_ram) + if(istype(turf_to_ram, /turf/closed/wall)) //sand is too weak to affect rwalls or walls with similar durability. + var/turf/closed/wall/wall_to_ram = turf_to_ram + if(wall_to_ram.hardness <= 25) + return + + var/area/area_to_check = get_area(turf_to_ram) + if(area_to_check.area_flags & EVENT_PROTECTED) //This event absolutely destroys arrivals, and putting latejoiners into firelock hell is cringe + return + + return ..() + +//Dust +/obj/effect/meteor/dust + name = "space dust" + icon_state = "dust" + pass_flags = PASSTABLE | PASSGRILLE + hits = 1 + hitpwr = EXPLODE_LIGHT + meteorsound = 'sound/weapons/gunshot_smg.ogg' + meteordrop = list(/obj/item/stack/ore/glass) + threat = 1 + +//Medium-sized +/obj/effect/meteor/medium + name = "meteor" + dropamt = 3 + threat = 5 + +/obj/effect/meteor/medium/meteor_effect() + ..() + explosion(src, heavy_impact_range = 1, light_impact_range = 2, flash_range = 3, adminlog = FALSE) + +//Large-sized +/obj/effect/meteor/big + name = "big meteor" + icon_state = "large" + hits = 6 + heavy = TRUE + dropamt = 4 + threat = 10 + +/obj/effect/meteor/big/meteor_effect() + ..() + explosion(src, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 3, flash_range = 4, adminlog = FALSE) + +//Flaming meteor +/obj/effect/meteor/flaming + name = "flaming meteor" + desc = "An veritable shooting star, both beautiful and frightening. You should probably keep your distance from this." + icon_state = "flaming" + hits = 5 + heavy = TRUE + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = list(/obj/item/stack/ore/plasma) + threat = 20 + signature = "thermal" + +/obj/effect/meteor/flaming/meteor_effect() + ..() + explosion(src, devastation_range = 1, heavy_impact_range = 2, light_impact_range = 3, flame_range = 5, flash_range = 4, adminlog = FALSE) + +//Radiation meteor +/obj/effect/meteor/irradiated + name = "glowing meteor" + desc = "An irradiated chunk of space rock. You could probably stop and appreciate its incandescent green glow, if it weren't moving so fast." + icon_state = "glowing" + heavy = TRUE + meteordrop = list(/obj/item/stack/ore/uranium) + threat = 15 + signature = "radiation" + +/obj/effect/meteor/irradiated/meteor_effect() + ..() + explosion(src, light_impact_range = 4, flash_range = 3, adminlog = FALSE) + new /obj/effect/decal/cleanable/greenglow(get_turf(src)) + radiation_pulse(src, 500) + +//Cluster meteor +/obj/effect/meteor/cluster + name = "cluster meteor" + desc = "A cluster of densely packed rocks, with a volatile core. You should probably get out of the way." + icon_state = "sharp" + hits = 9 + heavy = TRUE + meteorsound = 'sound/effects/break_stone.ogg' + threat = 25 + signature = "ordnance" + ///Number of fragmentation meteors to be spawned + var/cluster_count = 8 + +/obj/effect/meteor/cluster/meteor_effect() + ..() + + var/start_turf = get_turf(src) + + while(cluster_count > 0) + var/startSide = pick(GLOB.cardinals) + var/turf/destination = spaceDebrisStartLoc(startSide, z) + new /obj/effect/meteor/cluster_fragment(start_turf, destination) + cluster_count-- + + explosion(src, heavy_impact_range = 2, light_impact_range = 3, flash_range = 4, adminlog = FALSE) + +/obj/effect/meteor/cluster_fragment + name = "cluster meteor fragment" + desc = "A fast-moving fragment of exploded cluster-rock." + icon_state = "dust" + +//frozen carp "meteor" +/obj/effect/meteor/carp + name = "frozen carp" + icon_state = "carp" + desc = "Am I glad he's frozen in there, and that we're out here." + hits = 4 + meteorsound = 'sound/effects/ethereal_revive_fail.ogg' + meteordrop = list(/mob/living/simple_animal/hostile/carp) + dropamt = 1 + threat = 5 + signature = "fishing and trawling" + +/obj/effect/meteor/carp/Initialize(mapload) + if(prob(2)) + meteordrop = list(/mob/living/simple_animal/hostile/carp/megacarp) //hehe + return ..() + +//bluespace meteor +/obj/effect/meteor/bluespace + name = "bluespace meteor" + desc = "A large geode containing bluespace dust at its core, hurtling through space. That's the stuff the crew are here to research. How convenient for them." + icon_state = "bluespace" + dropamt = 3 + hits = 12 + meteordrop = list(/obj/item/stack/ore/bluespace_crystal) + threat = 15 + signature = "bluespace flux" + +/obj/effect/meteor/bluespace/Bump() + . = ..() + if(!QDELETED(src) && prob(35)) // monkestation edit: runtime fix + do_teleport(src, get_turf(src), 6, asoundin = 'sound/effects/phasein.ogg', channel = TELEPORT_CHANNEL_BLUESPACE) + +/obj/effect/meteor/banana + name = "bananium meteor" + desc = "Maybe it's a chunk blasted off of the legendary Clown Planet... How annoying." + icon_state = "bananium" + dropamt = 4 + hits = 175 //Honks everything, including space tiles. Depending on the angle/how much stuff it hits, there's a fair chance that it will spare the station from the actual explosion + meteordrop = list(/obj/item/stack/ore/bananium) + meteorsound = 'sound/items/bikehorn.ogg' + threat = 15 + movement_type = PHASING + signature = "comedy" + +/obj/effect/meteor/banana/meteor_effect() + ..() + playsound(src, 'sound/items/AirHorn.ogg', 100, TRUE, -1) + for(var/atom/movable/object in view(4, get_turf(src))) + var/turf/throwtarget = get_edge_target_turf(get_turf(src), get_dir(get_turf(src), get_step_away(object, get_turf(src)))) + object.safe_throw_at(throwtarget, 5, 1, force = MOVE_FORCE_STRONG) + +/obj/effect/meteor/banana/ram_turf(turf/bumped) + for(var/mob/living/slipped in get_turf(bumped)) + slipped.slip(100, slipped.loc,- GALOSHES_DONT_HELP|SLIDE, 0, FALSE) + slipped.visible_message(span_warning("[src] honks [slipped] to the floor!"), span_userdanger("[src] harmlessly passes through you, knocking you over.")) + get_hit() + +/obj/effect/meteor/emp + name = "electromagnetically charged meteor" + desc = "It radiates with captive energy, ready to be let loose upon the world." + icon_state = "bluespace" + hits = 6 + threat = 10 + signature = "electromagnetic interference" + +/obj/effect/meteor/emp/Move() + . = ..() + if(.) + new /obj/effect/temp_visual/impact_effect/ion(get_turf(src)) + +/obj/effect/meteor/emp/meteor_effect() + ..() + playsound(src, 'sound/weapons/zapbang.ogg', 100, TRUE, -1) + empulse(src, 3, 8) + +//Meaty Ore +/obj/effect/meteor/meaty + name = "meaty ore" + icon_state = "meateor" + desc = "Just... don't think too hard about where this thing came from." + hits = 2 + heavy = TRUE + meteorsound = 'sound/effects/blobattack.ogg' + meteordrop = list(/obj/item/reagent_containers/food/snacks/meat/slab/human, /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant, /obj/item/organ/heart, /obj/item/organ/lungs, /obj/item/organ/tongue, /obj/item/organ/appendix/, /obj/item/organ/heart/freedom) + var/meteorgibs = /obj/effect/gibspawner/generic + threat = 2 + signature = "culinary material" + +/obj/effect/meteor/meaty/Initialize(mapload) + for(var/path in meteordrop) + if(path == /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant) + meteordrop -= path + meteordrop += pick(subtypesof(path)) + + for(var/path in meteordrop) + if(path == /obj/item/organ/tongue) + meteordrop -= path + meteordrop += pick(typesof(path)) + return ..() + +/obj/effect/meteor/meaty/make_debris() + ..() + new meteorgibs(get_turf(src)) + + +/obj/effect/meteor/meaty/ram_turf(turf/T) + if(!isspaceturf(T)) + new /obj/effect/decal/cleanable/blood(T) + +/obj/effect/meteor/meaty/Bump(atom/A) + A.ex_act(hitpwr) + get_hit() + +//Meaty Ore Xeno edition +/obj/effect/meteor/meaty/xeno + color = "#5EFF00" + meteordrop = list(/obj/item/reagent_containers/food/snacks/meat/slab/xeno, /obj/item/organ/tongue/alien) + meteorgibs = /obj/effect/gibspawner/xeno + signature = "exotic culinary material" + +/obj/effect/meteor/meaty/xeno/Initialize(mapload) + meteordrop += subtypesof(/obj/item/organ/alien) + return ..() + +/obj/effect/meteor/meaty/xeno/ram_turf(turf/T) + if(!isspaceturf(T)) + new /obj/effect/decal/cleanable/xenoblood(T) + +//Station buster Tunguska +/obj/effect/meteor/tunguska + name = "tunguska meteor" + icon_state = "flaming" + desc = "Your life briefly passes before your eyes the moment you lay them on this monstrosity." + hits = 30 + hitpwr = EXPLODE_DEVASTATE + heavy = TRUE + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = list(/obj/item/stack/ore/plasma) + threat = 50 + signature = "armageddon" + +/obj/effect/meteor/tunguska/Move() + . = ..() + if(.) + new /obj/effect/temp_visual/revenant(get_turf(src)) + +/obj/effect/meteor/tunguska/meteor_effect() + ..() + explosion(src, devastation_range = 5, heavy_impact_range = 10, light_impact_range = 15, flash_range = 20, adminlog = FALSE) + +/obj/effect/meteor/tunguska/Bump() + ..() + if(prob(20)) + explosion(src, devastation_range = 2, heavy_impact_range = 4, light_impact_range = 6, flash_range = 8, adminlog = FALSE) + +/obj/effect/meteor/pumpkin + name = "PUMPKING" + desc = "THE PUMPKING'S COMING!" + icon = 'icons/obj/meteor_spooky.dmi' + icon_state = "pumpkin" + hits = 10 + heavy = TRUE + dropamt = 1 + meteordrop = list(/obj/item/clothing/head/hardhat/pumpkinhead, /obj/item/reagent_containers/food/snacks/grown/pumpkin) + threat = 100 + +/obj/effect/meteor/pumpkin/Initialize(mapload) + . = ..() + meteorsound = pick('sound/hallucinations/im_here1.ogg','sound/hallucinations/im_here2.ogg') + + +/obj/effect/meteor/guardian + name = "glowing meteor" + icon_state = "glowing" + hits = 3 + heavy = TRUE + meteorsound = 'sound/effects/bamf.ogg' + meteordrop = list(/obj/item/stand_arrow) + dropamt = 1 + threat = 100 + +#undef DEFAULT_METEOR_LIFETIME diff --git a/code/modules/meteors/meteor_waves.dm b/code/modules/meteors/meteor_waves.dm new file mode 100644 index 000000000000..3619720c6515 --- /dev/null +++ b/code/modules/meteors/meteor_waves.dm @@ -0,0 +1,63 @@ + +GLOBAL_VAR_INIT(meteor_wave_delay, 625) //minimum wait between waves in tenths of seconds +//set to at least 100 unless you want evarr ruining every round +// This spelling mistake? Name? is older then git, I'm scared to touch it + +GLOBAL_LIST_INIT(meteors_normal, list( + /obj/effect/meteor/dust=3, + /obj/effect/meteor/medium=8, + /obj/effect/meteor/big=3, + /obj/effect/meteor/flaming=1, + /obj/effect/meteor/irradiated=3, + /obj/effect/meteor/guardian=0.01 + )) //for normal meteor event + +GLOBAL_LIST_INIT(meteors_threatening, list( + /obj/effect/meteor/medium=4, + /obj/effect/meteor/big=8, + /obj/effect/meteor/flaming=3, + /obj/effect/meteor/irradiated=3, + /obj/effect/meteor/guardian=1 + )) //for threatening meteor event + +GLOBAL_LIST_INIT(meteors_catastrophic, list( + /obj/effect/meteor/medium=5, + /obj/effect/meteor/big=75, + /obj/effect/meteor/flaming=10, + /obj/effect/meteor/irradiated=10, + /obj/effect/meteor/guardian=2, + /obj/effect/meteor/tunguska = 1 + )) //for catastrophic meteor event + +GLOBAL_LIST_INIT(meateors, list( + /obj/effect/meteor/meaty=5, + /obj/effect/meteor/meaty/xeno=1 + )) //for meaty ore event + +GLOBAL_LIST_INIT(meteors_dust, list( + /obj/effect/meteor/dust = 1 + )) //for space dust event + +GLOBAL_LIST_INIT(meteors_sandstorm, list( + /obj/effect/meteor/sand=45, + /obj/effect/meteor/dust=5 + )) //for sandstorm event + +GLOBAL_LIST_INIT(meteors_stray, list( + /obj/effect/meteor/medium=15, + /obj/effect/meteor/big=10, + /obj/effect/meteor/flaming=25, + /obj/effect/meteor/irradiated=30, + /obj/effect/meteor/carp=25, + /obj/effect/meteor/bluespace=30, + /obj/effect/meteor/banana=25, + /obj/effect/meteor/meaty=10, + /obj/effect/meteor/meaty/xeno=8, + /obj/effect/meteor/emp = 30, + /obj/effect/meteor/cluster=20, + /obj/effect/meteor/tunguska=1 + )) //for stray meteor event (bigger numbers for a bit finer weighting) + +GLOBAL_LIST_INIT(meteorsSPOOKY, list( + /obj/effect/meteor/pumpkin = 1 + )) diff --git a/code/modules/mining/mine_items.dm b/code/modules/mining/mine_items.dm index 9454b1d15d25..27e84350bb26 100644 --- a/code/modules/mining/mine_items.dm +++ b/code/modules/mining/mine_items.dm @@ -81,7 +81,7 @@ GLOBAL_LIST_EMPTY(dumb_rev_heads) //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/machinery/computer/shuttle/mining/attack_hand(mob/user) - if(is_station_level(user.z) && user.mind && is_head_revolutionary(user) && !(user.mind in GLOB.dumb_rev_heads)) + if(is_station_level(user.z) && user.mind && IS_HEAD_REVOLUTIONARY(user) && !(user.mind in GLOB.dumb_rev_heads)) to_chat(user, span_warning("You get a feeling that leaving the station might be a REALLY dumb idea...")) GLOB.dumb_rev_heads += user.mind return diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index bf5037d67842..9d0c37b91246 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -312,7 +312,7 @@ return if(NOBLOOD in dna.species.species_traits) return - if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS] && mind?.assigned_role == "Clown") + if(SSgamemode.holidays && SSgamemode.holidays[APRIL_FOOLS] && mind?.assigned_role == "Clown") return /datum/reagent/colorful_reagent if(dna.species.exotic_blood) return dna.species.exotic_blood diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index f3dd1fa5f7eb..f3becbd341ee 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -193,7 +193,7 @@ // This should be a better check but this covers 99.9% of cases if(!(compatible_biotypes & C.mob_biotypes)) - to_chat(user, span_warner("This brain is incompatible with this being's biology!")) + to_chat(user, span_warning("This brain is incompatible with this being's biology!")) return if(!target_has_brain && C.is_eyes_covered() && user.zone_selected == BODY_ZONE_HEAD) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 364a31ad4603..4b709c2de1fd 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -86,13 +86,8 @@ if(SSticker.HasRoundStarted()) SSblackbox.ReportDeath(src) log_game("[key_name(src)] has died (BRUTE: [src.getBruteLoss()], BURN: [src.getFireLoss()], TOX: [src.getToxLoss()], OXY: [src.getOxyLoss()], CLONE: [src.getCloneLoss()]) ([AREACOORD(src)])") - if(is_devil(src)) - INVOKE_ASYNC(is_devil(src), TYPE_PROC_REF(/datum/antagonist/devil, beginResurrectionCheck), src) - if(is_hivemember(src)) - remove_hivemember(src) - if(is_hivehost(src)) - var/datum/antagonist/hivemind/hive = mind.has_antag_datum(/datum/antagonist/hivemind) - hive.destroy_hive() + if(IS_DEVIL(src)) + INVOKE_ASYNC(IS_DEVIL(src), TYPE_PROC_REF(/datum/antagonist/devil, beginResurrectionCheck), src) if(client) SSachievements.unlock_achievement(/datum/achievement/death, client) diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 6d4a46637004..fc80846a6d6b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -91,11 +91,6 @@ . += "Cloaker Charge: [round(100*SC.charge/SC.max_charge, 1)]%" . += "Lumens Count: [round(lumens, 0.01)]" - var/datum/antagonist/hivemind/hivemind = mind.has_antag_datum(/datum/antagonist/hivemind) - if(hivemind) - . += "" - . += "Hivemind Vessels: [hivemind.hive_size] (+[hivemind.size_mod])" - . += "Psychic Link Duration: [(hivemind.track_bonus + TRACKER_DEFAULT_TIME)/10] seconds" var/mob/living/simple_animal/horror/H = has_horror_inside() if(H && H.controlling) . += "" diff --git a/code/modules/mob/living/carbon/human/species_types/dullahan.dm b/code/modules/mob/living/carbon/human/species_types/dullahan.dm index d6fa6fd40b69..f9101b53130b 100644 --- a/code/modules/mob/living/carbon/human/species_types/dullahan.dm +++ b/code/modules/mob/living/carbon/human/species_types/dullahan.dm @@ -18,7 +18,7 @@ /datum/species/dullahan/check_roundstart_eligible() - if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + if(SSgamemode.holidays && SSgamemode.holidays[HALLOWEEN]) return TRUE return FALSE diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm index 610824967db8..93079128cad4 100644 --- a/code/modules/mob/living/carbon/human/species_types/golems.dm +++ b/code/modules/mob/living/carbon/human/species_types/golems.dm @@ -811,7 +811,7 @@ ..() /datum/species/golem/cloth/check_roundstart_eligible() - if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + if(SSgamemode.holidays && SSgamemode.holidays[HALLOWEEN]) return TRUE return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm index d9d4632000ee..a2eaf030e5d3 100644 --- a/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/shadowpeople.dm @@ -77,7 +77,7 @@ H.adjustCloneLoss(light_burning) /datum/species/shadow/check_roundstart_eligible() - if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + if(SSgamemode.holidays && SSgamemode.holidays[HALLOWEEN]) return TRUE return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/skeletons.dm b/code/modules/mob/living/carbon/human/species_types/skeletons.dm index 814f3917d8db..c89860dd3c75 100644 --- a/code/modules/mob/living/carbon/human/species_types/skeletons.dm +++ b/code/modules/mob/living/carbon/human/species_types/skeletons.dm @@ -26,7 +26,7 @@ limbs_id = SPECIES_SKELETON //they are just normal skeletons but weaker /datum/species/skeleton/lowcalcium/check_roundstart_eligible() - if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + if(SSgamemode.holidays && SSgamemode.holidays[HALLOWEEN]) return TRUE return ..() diff --git a/code/modules/mob/living/carbon/human/species_types/vampire.dm b/code/modules/mob/living/carbon/human/species_types/vampire.dm index 4b14ca2e91ab..0e021a7aac46 100644 --- a/code/modules/mob/living/carbon/human/species_types/vampire.dm +++ b/code/modules/mob/living/carbon/human/species_types/vampire.dm @@ -17,7 +17,7 @@ var/datum/action/cooldown/spell/shapeshift/bat/batform //attached to the datum itself to avoid cloning memes, and other duplicates /datum/species/vampire/check_roundstart_eligible() - if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + if(SSgamemode.holidays && SSgamemode.holidays[HALLOWEEN]) return TRUE return FALSE diff --git a/code/modules/mob/living/carbon/human/species_types/zombies.dm b/code/modules/mob/living/carbon/human/species_types/zombies.dm index 82d55c063c9e..dabe7b47900e 100644 --- a/code/modules/mob/living/carbon/human/species_types/zombies.dm +++ b/code/modules/mob/living/carbon/human/species_types/zombies.dm @@ -17,7 +17,7 @@ changesource_flags = MIRROR_BADMIN | WABBAJACK | MIRROR_PRIDE | ERT_SPAWN /datum/species/zombie/check_roundstart_eligible() - if(SSevents.holidays && SSevents.holidays[HALLOWEEN]) + if(SSgamemode.holidays && SSgamemode.holidays[HALLOWEEN]) return TRUE return ..() @@ -163,75 +163,4 @@ . = ..() mob_changing_species.faction -= "bloodhungry" -//The special zombie you get turned into in the zombie gamemode -/datum/species/zombie/infectious/gamemode - armor = 20 - brutemod = 0.925 - burnmod = 0.925 - speedmod = 1.45 - mutanthands = /obj/item/zombie_hand/gamemode - inherent_traits = list(TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTLOWPRESSURE, TRAIT_RESISTDAMAGESLOWDOWN, TRAIT_STABLELIVER, TRAIT_STABLEHEART, - TRAIT_RADIMMUNE, TRAIT_LIMBATTACHMENT, TRAIT_NOBREATH, TRAIT_NODEATH, TRAIT_FAKEDEATH, TRAIT_NOHUNGER, TRAIT_RESISTHEAT, TRAIT_SHOCKIMMUNE, TRAIT_PUSHIMMUNE, TRAIT_STUNIMMUNE, TRAIT_BADDNA, TRAIT_EASILY_WOUNDED, TRAIT_EASYDISMEMBER) - no_equip = list(ITEM_SLOT_MASK, ITEM_SLOT_EYES, ITEM_SLOT_HEAD) - -/datum/species/zombie/infectious/gamemode/runner - mutanthands = /obj/item/zombie_hand/gamemode/runner - armor = 10 // 110 damage to KO a zombie, which kills it - speedmod = 0.45 - brutemod = 1 - -/datum/species/zombie/infectious/gamemode/juggernaut - mutanthands = /obj/item/zombie_hand/gamemode/tank - armor = 30 // 135 damage to KO a zombie, which kills it - brutemod = 0.75 - speedmod = 1.3 - heal_rate = 1.20 - -/datum/species/zombie/infectious/gamemode/spitter - armor = 5 // 110 damage to KO a zombie, which kills it - brutemod = 1 - burnmod = 1 - -/datum/species/zombie/infectious/gamemode/spec_stun(mob/living/carbon/human/H,amount) - . = 0 - -/datum/species/zombie/infectious/gamemode/apply_damage(damage, damagetype = BRUTE, def_zone = null, blocked, mob/living/carbon/human/H, wound_bonus = 0, bare_wound_bonus = 0, sharpness = FALSE, attack_direction = null) - if(damagetype == STAMINA) - return - . = ..() - if(.) - regen_cooldown = world.time + REGENERATION_DELAY - - -/datum/species/zombie/infectious/gamemode/coordinator - armor = 17 - speedmod = 1.2 - -/datum/species/zombie/infectious/gamemode/necromancer - mutanthands = /obj/item/zombie_hand/gamemode/necro - armor = 10 - speedmod = 1.2 - -/datum/species/zombie/infectious/gamemode/necromanced_minion - var/mob/living/carbon/human/master - var/max_distance = 1 //Default value - armor = 10 - brutemod = 1.05 - burnmod = 1.05 - species_traits = list(NO_UNDERWEAR, NOBLOOD, NOZOMBIE, NOTRANSSTING, HAS_FLESH, HAS_BONE) - inherent_traits = list(TRAIT_EASYDISMEMBER, TRAIT_RESISTCOLD, TRAIT_RESISTHIGHPRESSURE, TRAIT_RESISTLOWPRESSURE, - TRAIT_RADIMMUNE, TRAIT_LIMBATTACHMENT, TRAIT_NOBREATH, TRAIT_NODEATH, TRAIT_FAKEDEATH, TRAIT_NOHUNGER, TRAIT_RESISTHEAT, TRAIT_SHOCKIMMUNE, TRAIT_PUSHIMMUNE, TRAIT_STUNIMMUNE, TRAIT_BADDNA, TRAIT_EASILY_WOUNDED) - -/datum/species/zombie/infectious/gamemode/necromanced_minion/spec_life(mob/living/carbon/human/H) - . = ..() - if(prob(50) && !H.stat) - if(get_dist(get_turf(master), get_turf(H)) > max_distance) - if(prob(20)) - to_chat(H, span_userdanger("You are too far away from your master! You are taking damage!")) - apply_damage(7.5, BRUTE, null, FALSE, H) - - if(master.stat == DEAD || QDELETED(master)) - to_chat(H, span_userdanger("Your master is dead. And with his death, comes yours!")) - H.dust() - #undef REGENERATION_DELAY diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 6d5a1659df43..ec3793b57f7d 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -101,11 +101,6 @@ . += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]" . += "Absorbed DNA: [changeling.absorbedcount]" -/mob/living/carbon/monkey/IsAdvancedToolUser()//Unless its monkey mode monkeys cant use advanced tools - if(mind && is_monkey(mind)) - return TRUE - return FALSE - /mob/living/carbon/monkey/reagent_check(datum/reagent/R) //can metabolize all reagents return FALSE diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 0e39d1ca6c98..e260200a73e9 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1084,7 +1084,7 @@ mind.soulOwner = mind /mob/living/proc/has_bane(banetype) - var/datum/antagonist/devil/devilInfo = is_devil(src) + var/datum/antagonist/devil/devilInfo = IS_DEVIL(src) return devilInfo && banetype == devilInfo.bane /mob/living/proc/check_weakness(obj/item/weapon, mob/living/attacker) diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm index 16de99e0c69a..9dea58248e63 100644 --- a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm +++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm @@ -29,10 +29,10 @@ /obj/item/drone_shell/proc/build_seasonal_hats() possible_seasonal_hats = list() - if(!length(SSevents.holidays)) + if(!length(SSgamemode.holidays)) return //no holidays, no hats; we'll keep the empty list so we never call this proc again - for(var/V in SSevents.holidays) - var/datum/holiday/holiday = SSevents.holidays[V] + for(var/V in SSgamemode.holidays) + var/datum/holiday/holiday = SSgamemode.holidays[V] if(holiday.drone_hat) possible_seasonal_hats += holiday.drone_hat diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index 42ccb4f5e7e7..5b05a333878e 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -298,7 +298,7 @@ if(M.mind?.special_role)//If they have a mind and special role, they are some type of traitor or antagonist. switch(SSticker.mode.config_tag) if("revolution") - if(is_revolutionary(M)) + if(IS_REVOLUTIONARY(M)) return 2 if("cult") if(M.mind in SSticker.mode.cult) @@ -310,7 +310,7 @@ if(M.mind.has_antag_datum(/datum/antagonist/changeling,TRUE)) return 2 if("wizard") - if(iswizard(M)) + if(IS_WIZARD(M)) return 2 if("apprentice") if(M.mind in SSticker.mode.apprentices) @@ -579,3 +579,15 @@ if(client?.combo_hud_enabled && client?.prefs?.toggles & COMBOHUD_LIGHTING) return LIGHTING_CUTOFF_FULLBRIGHT return initial(lighting_cutoff) + +/// Returns a client from a mob, mind or client +/proc/get_player_client(player) + if(ismob(player)) + var/mob/player_mob = player + player = player_mob.client + else if(istype(player, /datum/mind)) + var/datum/mind/player_mind = player + player = player_mind.current.client + if(!istype(player, /client)) + return + return player diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm index 4888e444770c..de62540e8d6e 100644 --- a/code/modules/ninja/energy_katana.dm +++ b/code/modules/ninja/energy_katana.dm @@ -56,7 +56,7 @@ /obj/item/energy_katana/pickup(mob/living/carbon/human/user) . = ..() - if(!is_ninja(user)) //stolen directly from the bloody bastard sword + if(!IS_SPACE_NINJA(user)) //stolen directly from the bloody bastard sword if(user.electrocute_act(15, src, 1, user.held_index_to_hand(user.active_hand_index))) // you tried to grab it with this hand, so we'll shock it to_chat(user, span_userdanger("[src] shocks you!")) user.emote("scream") diff --git a/code/modules/ninja/suit/suit.dm b/code/modules/ninja/suit/suit.dm index a705d29df522..fdbb1eac24ca 100644 --- a/code/modules/ninja/suit/suit.dm +++ b/code/modules/ninja/suit/suit.dm @@ -98,7 +98,7 @@ Contents: /obj/item/clothing/suit/space/space_ninja/proc/lock_suit(mob/living/carbon/human/H) if(!istype(H)) return FALSE - if(do_gib && !is_ninja(H)) // yogs + if(do_gib && !IS_SPACE_NINJA(H)) // yogs to_chat(H, span_danger("fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAU†HORIZED USÈ DETÈC†††eD\nCoMMÈNCING SUB-R0U†IN3 13...\nTÈRMInATING U-U-USÈR...")) H.gib() return FALSE diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 5746bdac9e60..0d5b8e59a2d1 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -135,7 +135,7 @@ /obj/item/paper/attack_self(mob/user) user.examinate(src) - if(rigged && (SSevents.holidays && SSevents.holidays[APRIL_FOOLS])) + if(rigged && (SSgamemode.holidays && SSgamemode.holidays[APRIL_FOOLS])) if(!spam_flag) spam_flag = TRUE playsound(loc, 'sound/items/bikehorn.ogg', 50, 1) diff --git a/code/modules/paperwork/paperbin.dm b/code/modules/paperwork/paperbin.dm index 5224bf678d83..d70adb4be4e2 100644 --- a/code/modules/paperwork/paperbin.dm +++ b/code/modules/paperwork/paperbin.dm @@ -89,7 +89,7 @@ else if(response == "Regular") P = new papertype(src) - if(SSevents.holidays && SSevents.holidays[APRIL_FOOLS]) + if(SSgamemode.holidays && SSgamemode.holidays[APRIL_FOOLS]) if(prob(30)) P.info = "HONK HONK HONK HONK HONK HONK HONK
HOOOOOOOOOOOOOOOOOOOOOONK
APRIL FOOLS" P.rigged = 1 diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 067bf59bf92d..e688222df6f7 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -224,7 +224,7 @@ playsound(user, 'sound/weapons/saberoff.ogg', 5, 1) to_chat(user, span_warning("[src] can now be concealed.")) else - if(!is_syndicate(user) && !is_battleroyale(user)) // this is just a normal pen to non syndicates as they don't know how to switch it on. + if(!is_syndicate(user)) // this is just a normal pen to non syndicates as they don't know how to switch it on. . = ..() return on = TRUE diff --git a/code/modules/power/gravitygenerator.dm b/code/modules/power/gravitygenerator.dm index bc59965043dd..85afae645d01 100644 --- a/code/modules/power/gravitygenerator.dm +++ b/code/modules/power/gravitygenerator.dm @@ -467,6 +467,14 @@ GLOBAL_LIST_EMPTY(gravity_generators) // We will keep track of this by adding ne investigate_log("was toggled [breaker ? "ON" : "OFF"] by [key_name(usr)].", INVESTIGATE_GRAVITY) set_power() + +/obj/machinery/gravity_generator/main/proc/blackout() + charge_count = 0 + breaker = FALSE + set_power() + disable() + investigate_log("was turned off by blackout event or a gravity anomaly detonation.", INVESTIGATE_GRAVITY) + // Misc /obj/item/paper/guides/jobs/engi/gravity_gen diff --git a/code/modules/power/supermatter/supermatter_delamination.dm b/code/modules/power/supermatter/supermatter_delamination.dm index 998882740ae6..967589433e26 100644 --- a/code/modules/power/supermatter/supermatter_delamination.dm +++ b/code/modules/power/supermatter/supermatter_delamination.dm @@ -90,7 +90,7 @@ /datum/supermatter_delamination/proc/call_cascading() sound_to_playing_players('sound/magic/lightningbolt.ogg', volume = 50) shockwave() //a pulse when sm is blown up - var/datum/round_event_control/resonance_cascade/cascade_roundevent = locate(/datum/round_event_control/resonance_cascade) in SSevents.control + var/datum/round_event_control/resonance_cascade/cascade_roundevent = locate(/datum/round_event_control/resonance_cascade) in SSgamemode.control cascade_roundevent.runEvent() message_admins("The Supermatter Crystal has caused a resonance cascade.") diff --git a/code/modules/reagents/chemistry/reagents/drug_reagents.dm b/code/modules/reagents/chemistry/reagents/drug_reagents.dm index afb00db2b5ff..06f1d08eebe0 100644 --- a/code/modules/reagents/chemistry/reagents/drug_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drug_reagents.dm @@ -722,7 +722,7 @@ H.eye_color = "5b5beb" H.dna.update_ui_block(DNA_EYE_COLOR_BLOCK) H.update_body() - if(!is_wizard(M)) + if(!IS_WIZARD(M)) M.set_blue_eye(17) M.adjust_jitter(2 SECONDS) M.adjustStaminaLoss(-2, 0) @@ -745,7 +745,7 @@ /datum/reagent/drug/blue_eye/overdose_process(mob/living/M) M.adjustToxLoss(1, 0) - if(!is_wizard(M)) + if(!IS_WIZARD(M)) M.adjustOrganLoss(ORGAN_SLOT_BRAIN, pick(0.4, 0.5, 0.6)) else M.adjustOrganLoss(ORGAN_SLOT_BRAIN, pick(0.2, 0.3, 0.4)) @@ -788,7 +788,7 @@ step(M, pick(GLOB.cardinals)) M.adjust_jitter(15 SECONDS) M.adjust_dizzy(15) - if(!is_wizard(M)) + if(!IS_WIZARD(M)) M.adjustToxLoss(3, 0) if(prob(50)) M.emote(pick("twitch","drool","moan","giggle")) diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 1383eb928dce..b7c6dff3643f 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -288,7 +288,7 @@ M.remove_status_effect(/datum/status_effect/speech/stutter) holder.remove_reagent(type, volume) // maybe this is a little too perfect and a max() cap on the statuses would be better?? return - if(ishuman(M) && is_vampire(M) && prob(80)) // Yogs Start + if(ishuman(M) && IS_VAMPIRE(M) && prob(80)) // Yogs Start var/datum/antagonist/vampire/V = M.mind.has_antag_datum(ANTAG_DATUM_VAMPIRE) if(!V.get_ability(/datum/vampire_passive/full)) switch(data) diff --git a/code/modules/reagents/reagent_containers/blood_pack.dm b/code/modules/reagents/reagent_containers/blood_pack.dm index 2e1cfeaeb9c4..29c85c0ec534 100644 --- a/code/modules/reagents/reagent_containers/blood_pack.dm +++ b/code/modules/reagents/reagent_containers/blood_pack.dm @@ -40,7 +40,7 @@ span_notice("[user] puts the [src] up to their mouth."), span_notice("You take a sip from the [src]."), ) - if(is_vampire(user)) + if(IS_VAMPIRE(user)) var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) V.usable_blood += BLOODBAG_GULP_SIZE / 4 //they should really be drinking from people, yknow, be antagonistic? diff --git a/code/modules/research/nanites/nanite_programs/buffing.dm b/code/modules/research/nanites/nanite_programs/buffing.dm index 2e1113b3bf78..c488fa547fef 100644 --- a/code/modules/research/nanites/nanite_programs/buffing.dm +++ b/code/modules/research/nanites/nanite_programs/buffing.dm @@ -106,14 +106,8 @@ //won't work if on a rev, to avoid having implanted revs. same applies for hivemind members. if(host_mob.mind.has_antag_datum(/datum/antagonist/rev, TRUE)) return - if(is_hivemember(host_mob)) - return - if(host_mob.is_wokevessel()) - return if(host_mob.mind.has_antag_datum(/datum/antagonist/brainwashed)) return - if(host_mob.mind.has_antag_datum(/datum/antagonist/gang, TRUE)) - return if(is_team_darkspawn(host_mob)) return ADD_TRAIT(host_mob, TRAIT_MINDSHIELD, "nanites") diff --git a/code/modules/spells/spell_types/hivemind.dm b/code/modules/spells/spell_types/hivemind.dm deleted file mode 100644 index f8222acbeeb2..000000000000 --- a/code/modules/spells/spell_types/hivemind.dm +++ /dev/null @@ -1,996 +0,0 @@ -/* - * ATTENTION ALL FUTURE HIVEMIND CODERS / JANITORS - * - * THINGS ARE LIKE THEY ARE RIGHT NOW BECAUSE THE GAMEMODE - * IS ABANDONED. - * - * IF YOU ARE GOING TO REFACTOR / REWORK / ANYTHING ELSE - * THAT USES THESE SPELLS, PUT THEM IN FORMAL ORDER, - * CHECK THE SPELL BELOW TO SEE HOW IT IS SUPPOSED TO BE - * ORGANIZED, THANK YOU. - * - * IF YOU DON'T DO IT I'LL FUCKING KILL YOU, IT'S BROKEN AS SHIT. - * - * ~ tatax -*/ - -/datum/action/cooldown/spell/aoe/target_hive - panel = "Hivemind Abilities" - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "spell_default" - - invocation_type = INVOCATION_NONE - - spell_requirements = SPELL_REQUIRES_HUMAN - var/target_external = 0 //Whether or not we select targets inside or outside of the hive - - -/datum/action/cooldown/spell/aoe/target_hive/cast(atom/cast_on) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive || !hive.hivemembers) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/list/possible_targets = list() - var/list/targets = list() - - if(target_external) - for(var/mob/living/carbon/H in view_or_range(aoe_radius, owner, "range")) - if(owner == H) - continue - if(!is_valid_target(H)) - continue - if(!hive.is_carbon_member(H)) - possible_targets += H - else - possible_targets = hive.get_carbon_members() - if(aoe_radius) - possible_targets &= view_or_range(aoe_radius, owner, "range") - - var/mob/living/carbon/human/H = tgui_input_list(owner, "Choose the target for the spell.", "Targeting", possible_targets) //arcaic, homeric even - if(!H) - return - targets += H - - return TRUE - -/datum/action/cooldown/spell/aoe/target_hive/hive_add - name = "Assimilate Vessel" - desc = "We silently add an unsuspecting target to the hive." - button_icon_state = "add" - - cooldown_time = 5 SECONDS - aoe_radius = 7 - target_external = 1 - var/bruteforce = FALSE - -/datum/action/cooldown/spell/aoe/target_hive/hive_add/cast_on_thing_in_aoe(atom/victim, mob/living/carbon/human/caster) - var/mob/living/carbon/target = victim - var/datum/antagonist/hivemind/hive = caster.mind.has_antag_datum(/datum/antagonist/hivemind) - - if(!target.mind || !target.client || target.stat == DEAD) - to_chat(owner, span_notice("We detect no neural activity in this body.")) - var/shielded = HAS_TRAIT(target, TRAIT_MINDSHIELD) - var/foiled = target.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0) - if(shielded && !bruteforce) - to_chat(owner, span_warning("Powerful technology protects [target.name]'s mind.")) - return - if((shielded || foiled) && bruteforce) - to_chat(owner, span_notice("We [bruteforce ? "bruteforce" : "force"] our way past the mental barriers of [target.name] and begin linking our minds!")) - else - to_chat(owner, span_notice("We begin linking our mind with [target.name]!")) - var/multiplier = (!foiled || bruteforce) ? 5 : 10 - if(!do_after(owner, multiplier*(1.5**get_dist(owner, target)), owner, timed_action_flags = IGNORE_HELD_ITEM) || !(target in view(aoe_radius))) - to_chat(owner, span_notice("We fail to connect to [target.name].")) - return - if((HAS_TRAIT(target, TRAIT_MINDSHIELD) && !bruteforce)) - to_chat(owner, span_notice("We fail to connect to [target.name].")) - return - to_chat(owner, span_notice("[target.name] was added to the Hive!")) - hive.add_to_hive(target) - hive.threat_level = max(0, hive.threat_level-0.1) - if(bruteforce) - if(target.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 6)) - target.adjustOrganLoss(ORGAN_SLOT_BRAIN, 10) - to_chat(owner, span_warning("We are briefly exhausted by the effort required by our enhanced assimilation abilities.")) - caster.Immobilize(5 SECONDS) - SEND_SIGNAL(target, COMSIG_NANITE_SET_VOLUME, 0) - for(var/obj/item/implant/mindshield/M in target.implants) - qdel(M) - else - target.can_block_magic(MAGIC_RESISTANCE_MIND) - -/datum/action/cooldown/spell/aoe/target_hive/hive_remove - name = "Release Vessel" - desc = "We silently remove a nearby target from the hive. We must be close to their body to do so." - button_icon_state = "remove" - - cooldown_time = 50 - aoe_radius = 7 - -/datum/action/cooldown/spell/aoe/target_hive/hive_remove/cast_on_thing_in_aoe(atom/victim, atom/caster) - var/mob/living/carbon/target = victim - - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/datum/mind/M = target.mind - if(!M) - return - hive.remove_from_hive(target) - hive.calc_size() - hive.threat_level += 0.1 - to_chat(owner, span_notice("We remove [target.name] from the hive")) - if(hive.active_one_mind) - var/datum/antagonist/hivevessel/woke = target.is_wokevessel() - if(woke) - hive.active_one_mind.remove_member(M) - M.remove_antag_datum(/datum/antagonist/hivevessel) - -/datum/action/cooldown/spell/aoe/target_hive/hive_see - name = "Hive Vision" - desc = "We use the eyes of one of our vessels. Use again to look through our own eyes once more." - button_icon_state = "see" - var/mob/living/carbon/vessel - var/mob/living/host //Didn't really have any other way to auto-reset the perspective if the other mob got qdeled - var/limited = FALSE - var/active = FALSE - - cooldown_time = 2 SECONDS - -/datum/action/cooldown/spell/aoe/target_hive/hive_see/Remove(mob/living/owner) - owner.reset_perspective() - owner.clear_fullscreen("hive_eyes") - return ..() - -/datum/action/cooldown/spell/aoe/target_hive/hive_see/cast_on_thing_in_aoe(atom/victim, atom/caster) - if(!active) - vessel = victim - if(vessel) - if(vessel.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) - if(get_dist(src, vessel) > 42) - to_chat(owner, span_warning("We were unable to link our view with [vessel.name]. A barrier of tinfoil prevents us to do so at this distance.")) - return - limited = TRUE - to_chat(owner, span_warning("A barrier of tinfoil drastically dampens our link with [vessel.name]. We'll be able to sustain the link as long as they remain within 42 tiles from us.")) - vessel.apply_status_effect(STATUS_EFFECT_BUGGED, owner) - owner.reset_perspective(vessel) - active = TRUE - host = owner - owner.clear_fullscreen("hive_mc") - owner.overlay_fullscreen("hive_eyes", /atom/movable/screen/fullscreen/hive_eyes) - else - vessel.remove_status_effect(STATUS_EFFECT_BUGGED) - owner.reset_perspective() - owner.clear_fullscreen("hive_eyes") - var/datum/action/cooldown/spell/aoe/target_hive/hive_control/the_spell = locate(/datum/action/cooldown/spell/aoe/target_hive/hive_control) in owner.actions - if(the_spell && the_spell.active) - owner.overlay_fullscreen("hive_mc", /atom/movable/screen/fullscreen/hive_mc) - active = FALSE - limited = FALSE - -/datum/action/cooldown/spell/aoe/target_hive/hive_see/process() - if(active && (!vessel || !is_hivemember(vessel) || QDELETED(vessel) || (limited && get_dist(vessel, host) > 42))) - to_chat(host, span_warning("Our vessel is one of us no more!")) - host.reset_perspective() - host.clear_fullscreen("hive_eyes") - active = FALSE - limited = FALSE - if(!QDELETED(vessel)) - vessel.remove_status_effect(STATUS_EFFECT_BUGGED) - ..() - -/datum/action/cooldown/spell/pointed/hive_shock - name = "Sensory Shock" - desc = "After a short charging time, we overload the mind of one of our vessels with psionic energy, temporarilly disrupting their sight, hearing, and speech." - cooldown_time = 600 - panel = "Hivemind Abilities" - invocation_type = INVOCATION_NONE - spell_requirements = SPELL_REQUIRES_HUMAN - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "shock" - cast_range = 7 - -/datum/action/cooldown/spell/pointed/hive_shock/cast(list/targets, mob/owner = usr) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive || !hive.hivemembers) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/mob/living/carbon/target = targets[1] - to_chat(owner, span_notice("We increase the psionic bandwidth between ourself and the target!")) - var/power = 1 - if(target.can_block_magic(MAGIC_RESISTANCE_MIND)) - power *= 0.5 - if(!hive.is_carbon_member(target)) - power *= 0.5 - target.blind_eyes(4*power) - target.adjust_eye_blur(30*power) - target.minimumDeafTicks(15*power) //equivalent to 30s deafness max - target.adjust_jitter(power SECONDS) - target.silent += 10*power - target.adjust_stutter(3*power SECONDS) - target.Knockdown(1*power) - target.stop_pulling() - to_chat(target, span_ownerdanger("You feel your mind start to burn!")) - - return TRUE - -/datum/action/cooldown/spell/hive_scan - name = "Psychoreception" - desc = "We release a pulse to receive information on any enemies we have previously located via Network Invasion, as well as those currently tracking us." - panel = "Hivemind Abilities" - cooldown_time = 1800 - invocation_type = INVOCATION_NONE - spell_requirements = SPELL_REQUIRES_HUMAN - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "scan" - -/datum/action/cooldown/spell/hive_scan/cast(mob/living/owner) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/message - var/distance - - for(var/datum/status_effect/hive_track/track in owner.status_effects) - var/mob/living/L = track.tracked_by - if(!L) - continue - if(!do_after(owner, 0.5 SECONDS, owner, timed_action_flags = IGNORE_HELD_ITEM)) - to_chat(owner, span_notice("Our concentration has been broken!")) - break - distance = get_dist(owner, L) - message = "[(L.is_real_hivehost()) ? "Someone": "A hivemind host"] tracking us" - if(owner.z != L.z || L.stat == DEAD) - message += " could not be found." - else - var/multiplier = L.can_block_magic(MAGIC_RESISTANCE_MIND) ? rand(0.6, 1.4) : 1 - switch(distance*multiplier) - if(0 to 2) - message += " is right next to us!" - if(2 to 14) - message += " is nearby." - if(14 to 28) - message += " isn't too far away." - if(28 to INFINITY) - message += " is quite far away." - to_chat(owner, span_assimilator("[message]")) - for(var/datum/antagonist/hivemind/enemy in hive.individual_track_bonus) - if(!do_after(owner, 0.5 SECONDS, owner, timed_action_flags = IGNORE_HELD_ITEM)) - to_chat(owner, span_notice("Our concentration has been broken!")) - break - var/mob/living/carbon/C = enemy.owner?.current - if(!C) - continue - var/multiplier = C.can_block_magic(MAGIC_RESISTANCE_MIND) ? rand(0.6, 1.4) : 1 - var/mob/living/real_enemy = C.get_real_hivehost() - distance = get_dist(owner, real_enemy) - message = "A host that we can track for [(hive.individual_track_bonus[enemy])/10] extra seconds" - if(owner.z != real_enemy.z || real_enemy.stat == DEAD) - message += " could not be found." - else - multiplier = real_enemy.can_block_magic(MAGIC_RESISTANCE_MIND) ? rand(0.6, 1.4) : 1 - switch(distance*multiplier) - if(0 to 2) - message += " is right next to us!" - if(2 to 14) - if(enemy.get_threat_multiplier() >= 0.85 && distance <= 7) - message += " is in this very room!" - else - message += " is nearby." - if(14 to 28) - message += " isn't too far away." - if(28 to INFINITY) - message += " is quite far away." - to_chat(owner, span_assimilator("[message]")) - - return TRUE - -/datum/action/cooldown/spell/hive_drain - name = "Repair Protocol" - desc = "Our many vessels sacrifice a small portion of their mind's vitality to cure us of our physical and mental ailments." - panel = "Hivemind Abilities" - cooldown_time = 600 - invocation_type = INVOCATION_NONE - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "drain" - spell_requirements = SPELL_REQUIRES_HUMAN - -/datum/action/cooldown/spell/hive_drain/cast(mob/living/carbon/human/owner) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive || !hive.hivemembers) - return - var/iterations = 0 - var/list/carbon_members = hive.get_carbon_members() - if(!carbon_members.len) - return - if(!owner.getBruteLoss() && !owner.getFireLoss() && !owner.getCloneLoss() && !owner.getOrganLoss(ORGAN_SLOT_BRAIN) && !owner.getStaminaLoss()) - to_chat(owner, span_notice("We cannot heal ourselves any more with this power!")) - to_chat(owner, span_notice("We begin siphoning power from our many vessels!")) - while(iterations < 7) - var/mob/living/carbon/target = pick(carbon_members) - if(!do_after(owner, 1 SECONDS, owner, timed_action_flags = IGNORE_HELD_ITEM)) - to_chat(owner, span_warning("Our concentration has been broken!")) - break - if(!target) - to_chat(owner, span_warning("We have run out of vessels to drain.")) - break - var/regen = target.can_block_magic(MAGIC_RESISTANCE_MIND) ? 5 : 10 - target.adjustOrganLoss(ORGAN_SLOT_BRAIN, regen/2) - if(owner.getBruteLoss() > owner.getFireLoss()) - owner.heal_ordered_damage(regen, list(CLONE, BRUTE, BURN, STAMINA)) - else - owner.heal_ordered_damage(regen, list(CLONE, BURN, BRUTE, STAMINA)) - if(!owner.getBruteLoss() && !owner.getFireLoss() && !owner.getCloneLoss() && !owner.getStaminaLoss()) //If we don't have any of these, stop looping - to_chat(owner, span_warning("We finish our healing")) - break - iterations++ - owner.setOrganLoss(ORGAN_SLOT_BRAIN, 0) - - return TRUE - -/mob/living/passenger - name = "mind control victim" - real_name = "unknown conscience" - -/mob/living/passenger/UnarmedAttack(atom/A) - return - -/mob/living/passenger/say(message, bubble_type, list/spans = list(), sanitize = TRUE, datum/language/language = null, ignore_spam = FALSE, forced = null) - to_chat(src, span_warning("You find yourself unable to speak, you aren't in control of your body!")) - return FALSE - -/mob/living/passenger/emote(act, m_type = null, message = null, intentional = FALSE, is_keybind = FALSE) - to_chat(src, span_warning("You find yourself unable to emote, you aren't in control of your body!")) - return - -/mob/living/passenger/Hear(message, atom/movable/speaker, datum/language/message_language, raw_message, radio_freq, list/spans, message_mode) - return - -/datum/action/cooldown/spell/aoe/target_hive/hive_control - name = "Mind Control" - desc = "We assume direct control of one of our vessels, leaving our current body for up to a minute. It can be cancelled at any time by casting it again. Powers can be used via our vessel, although if it dies, the entire hivemind will come down with it. Our ability to sense psionic energy is completely nullified while using this power, and it will end immediately should we attempt to move too far from our starting point." - cooldown_time = 1200 - button_icon_state = "force" - var/active = FALSE - var/mob/living/carbon/human/original_body //The original hivemind host - var/mob/living/carbon/human/vessel - var/mob/living/passenger/backseat //Storage for the mind controlled vessel - var/turf/starting_spot - var/power = 600 - var/time_initialized = 0 - var/out_of_range = FALSE - var/restricted_range = FALSE - -/datum/action/cooldown/spell/aoe/target_hive/hive_control/proc/release_control() //If the spell is active, force everybody into their original bodies if they exist, ghost them otherwise, delete the backseat - if(!active) - return - active = FALSE - if(!QDELETED(vessel)) - vessel.clear_fullscreen("hive_mc") - if(vessel.mind) - if(QDELETED(original_body)) - vessel.ghostize(0) - else - vessel.mind.transfer_to(original_body, 1) - original_body.Sleeping(vessel.AmountSleeping()) // Mirrors any sleep or unconsciousness from the vessel - original_body.Unconscious(vessel.AmountUnconscious()) - - if(!QDELETED(backseat) && backseat.mind) - if(QDELETED(vessel)) - backseat.ghostize(0) - else - backseat.mind.transfer_to(vessel,1) - vessel.visible_message(span_ownerdanger("[src] suddenly wakes up, as though he was under foreign control!")) - vessel.adjust_jitter(3 SECONDS) - message_admins("[ADMIN_LOOKUPFLW(vessel)] is no longer being controlled by [ADMIN_LOOKUPFLW(original_body)] (Hivemind Host).") - log_game("[key_name(vessel)] was released from Mind Control by [key_name(original_body)].") - - QDEL_NULL(backseat) - - if(original_body?.mind) - var/datum/antagonist/hivemind/hive = original_body.mind.has_antag_datum(/datum/antagonist/hivemind) - if(hive) - hive.threat_level += 1 - - restricted_range = FALSE - -/datum/action/cooldown/spell/aoe/target_hive/hive_control/cast_on_thing_in_aoe(atom/victim, atom/caster) - if(!active) - vessel = victim - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - original_body = owner - vessel = victim - to_chat(owner, span_notice("We begin merging our mind with [vessel.name].")) - var/timely = 50 - if(vessel.can_block_magic(MAGIC_RESISTANCE_MIND)) - timely = 100 - restricted_range = TRUE - if(!do_after(owner, timely, owner, timed_action_flags = IGNORE_HELD_ITEM)) - to_chat(owner, span_notice("We fail to assume control of the target.")) - return - if(owner.z != vessel.z || (restricted_range && get_dist(vessel, owner) > 35)) - to_chat(owner, span_notice("Our vessel is too far away to control.")) - return - for(var/datum/antagonist/hivemind/H in GLOB.antagonists) - if(H.owner == owner.mind) - continue - if(H.owner == vessel.mind) - to_chat(owner, span_danger("We have detected a foreign presence within this mind, it would be unwise to merge so intimately with it.")) - return - backseat = new /mob/living/passenger() - if(vessel && vessel.mind && backseat) - message_admins("[ADMIN_LOOKUPFLW(vessel)] has been temporarily taken over by [ADMIN_LOOKUPFLW(owner)] (Hivemind Host).") - log_game("[key_name(vessel)] was Mind Controlled by [key_name(owner)].") - - deadchat_broadcast(" has just been mind controlled!", span_name("[vessel]"), vessel) - - original_body = owner - backseat.loc = vessel - backseat.name = vessel.real_name - backseat.real_name = vessel.real_name - vessel.mind.transfer_to(backseat, 1) - owner.mind.transfer_to(vessel, 1) - backseat.blind_eyes(power) - vessel.overlay_fullscreen("hive_mc", /atom/movable/screen/fullscreen/hive_mc) - active = TRUE - out_of_range = FALSE - starting_spot = get_turf(vessel) - time_initialized = world.time - to_chat(vessel, span_assimilator("We can sustain our control for a maximum of [round(power/10)] seconds.")) - if(do_after(owner, power, owner, timed_action_flags = IGNORE_HELD_ITEM, progress = FALSE)) - to_chat(vessel, span_warning("We cannot sustain the mind control any longer and release control!")) - else - to_chat(vessel, span_warning("Our body has been disturbed, interrupting the mind control!")) - release_control() - else - to_chat(usr, span_warning("We detect no neural activity in our vessel!")) - else - release_control() - -/datum/action/cooldown/spell/aoe/target_hive/hive_control/process() - if(QDELETED(vessel)) //If we've been gibbed or otherwise deleted, ghost both of them and kill the original - original_body.adjustOrganLoss(ORGAN_SLOT_BRAIN, 200) - release_control() - else if(!is_hivemember(backseat)) //If the vessel is no longer a hive member, return to original bodies - to_chat(vessel, span_warning("Our vessel is one of us no more!")) - release_control() - else if(!QDELETED(original_body) && (!backseat.ckey || vessel.stat == DEAD)) //If the original body exists and the vessel is dead/ghosted, return both to body but not before killing the original - original_body.adjustOrganLoss(ORGAN_SLOT_BRAIN, 200) - to_chat(vessel.mind, span_warning("Our vessel is one of us no more!")) - release_control() - else if(!QDELETED(original_body) && original_body.z != vessel.z) //Return to original bodies - release_control() - to_chat(original_body, span_warning("Our vessel is too far away to control!")) - else if(QDELETED(original_body) || original_body.stat == DEAD) //Return vessel to its body, either return or ghost the original - to_chat(vessel, span_ownerdanger("Our body has been destroyed, the hive cannot survive without its host!")) - release_control() - else - var/multiplier = restricted_range ? 0.5 : 1 - if(!out_of_range && get_dist(starting_spot, vessel) > 14*multiplier) - out_of_range = TRUE - flash_color(vessel, flash_color="#800080", flash_time=10) - to_chat(vessel, span_warning("Our vessel has been moved too far away from the initial point of control, we will be disconnected if we go much further!")) - addtimer(CALLBACK(src, PROC_REF(range_check), multiplier), 30) - else if(get_dist(starting_spot, vessel) > 21*multiplier) - release_control() - - ..() - -/datum/action/cooldown/spell/aoe/target_hive/hive_control/proc/range_check(multiplier = 1) - if(get_dist(starting_spot, vessel) > 14 * multiplier) - release_control() - out_of_range = FALSE - -/datum/action/cooldown/spell/pointed/induce_panic - name = "Induce Panic" - desc = "We unleash a burst of psionic energy, inducing a debilitating fear in those around us and reducing their combat readiness. We can also briefly affect silicon-based life with this burst." - panel = "Hivemind Abilities" - cooldown_time = 600 - cast_range = 7 - invocation_type = INVOCATION_NONE - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "panic" - -/datum/action/cooldown/spell/pointed/induce_panic/cast(list/targets, mob/living/owner = usr) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - for(var/mob/living/carbon/human/target in targets) - if(target.stat == DEAD) - continue - target.adjust_jitter(14 SECONDS) - target.apply_damage(35 + rand(0,15), STAMINA, target.get_bodypart(BODY_ZONE_HEAD)) - if(target.is_real_hivehost() || target.can_block_magic(MAGIC_RESISTANCE_MIND)) - continue - if(prob(20)) - var/text = pick(":h Help!!",":h Run!",":h They're here!",":h Get out!",":h Hide!",":h Kill them!",":h Cult!",":h Changeling!",":h Traitor!",":h Nuke ops!",":h Revolutionaries!",":h Wizard!",":h Zombies!",":h Ghosts!",":h AI rogue!",":h Borgs emagged!",":h Maint!!",":h Dying!!",":h AI lock down the borgs law 1!",":h I'm losing control of the situation!!") - target.say(text, forced = "panic") - var/effect = rand(1,4) - switch(effect) - if(1) - to_chat(target, span_ownerdanger("You panic and drop everything to the ground!")) - target.drop_all_held_items() - if(2) - to_chat(target, span_ownerdanger("You panic and flail around!")) - target.click_random_mob() - addtimer(CALLBACK(target, "click_random_mob"), 0.5 SECONDS) - addtimer(CALLBACK(target, "click_random_mob"), 1 SECONDS) - addtimer(CALLBACK(target, "click_random_mob"), 1.5 SECONDS) - addtimer(CALLBACK(target, "click_random_mob"), 2 SECONDS) - addtimer(CALLBACK(target, "Stun", 3 SECONDS), 2.5 SECONDS) - target.adjust_confusion(10 SECONDS) - if(3) - to_chat(target, span_ownerdanger("You freeze up in fear!")) - target.Stun(7 SECONDS) - if(4) - to_chat(target, span_ownerdanger("You feel nauseous as dread washes over you!")) - target.adjust_dizzy(15 SECONDS) - target.apply_damage(30, STAMINA, target.get_bodypart(BODY_ZONE_HEAD)) - target.adjust_hallucinations(45 SECONDS) - - for(var/mob/living/silicon/target in targets) - target.Unconscious(50) - - return TRUE - -/datum/action/cooldown/spell/pointed/pin - name = "Psychic Pin" - desc = "We send out a controlled pulse of psionic energy, pinning everyone in sight, and knocking out silicon-based lifeforms. This is weaker against enemy hiveminds." - panel = "Hivemind Abilities" - cooldown_time = 600 - cast_range = 7 - invocation_type = INVOCATION_NONE - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "pin" - -/datum/action/cooldown/spell/pointed/pin/cast(list/targets, mob/living/owner = usr) - . = ..() - if(!.) - return FALSE - if(!targets) - to_chat(owner, span_notice("Nobody is in sight, it'd be a waste to do that now.")) - return - var/list/victims = list() - for(var/mob/living/target in targets) - if(target.stat == DEAD) - continue - if(target.is_real_hivehost() || (!iscarbon(target) && !issilicon(target))) - continue - victims += target - var/statustime = max(80,240/(1+round(victims.len/3))) - for(var/mob/living/carbon/victim in victims) - if(victim.is_real_hivehost()) - victim.Knockdown(statustime/4) - else - victim.Knockdown(statustime) - to_chat(victim, span_ownerdanger("A sudden force throws you to the ground!")) - for(var/mob/living/silicon/victim in victims) - victim.Unconscious(statustime) - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(victims.len && hive) - hive.threat_level += 1 - -/datum/action/cooldown/spell/aoe/target_hive/nightmare - name = "Living nightmares" - desc = "The target's fears break out and attack them, obscuring their vision and clawing at them." - aoe_radius = 7 - cooldown_time = 1800 - button_icon_state = "nightmare" - -/datum/action/cooldown/spell/aoe/target_hive/nightmare/cast_on_thing_in_aoe(atom/victim, atom/caster) - var/mob/living/carbon/target = victim - if(!do_after(owner, 3 SECONDS, owner, timed_action_flags = IGNORE_HELD_ITEM)) - to_chat(owner, span_notice("Our concentration has been broken!")) - return - to_chat(target, span_ownerdanger("You see dark smoke swirling around you!")) - if(target.can_block_magic(MAGIC_RESISTANCE_MIND)) - to_chat(owner, span_notice("We begin bruteforcing the tinfoil barriers of [target.name] and pulling out their nightmares.")) - if(!do_after(owner, 3 SECONDS, owner, timed_action_flags = IGNORE_HELD_ITEM) || !(target in view(aoe_radius))) - to_chat(owner, span_notice("Our concentration has been broken!")) - return - target.apply_status_effect(STATUS_EFFECT_HIVEMIND_CURSE, CURSE_SPAWNING | CURSE_BLINDING) - to_chat(owner, span_notice("We have brought forth the targets nightmares!")) - deadchat_broadcast(" is suffering corporial nightmares!", span_name("[target]"), target) - - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(hive) - hive.threat_level += 3 - -/obj/item/extendohand/hivemind - name = "Telekinetic hand" - desc = "Extends the reach of your unarmed combat. Drop to remove." - icon = 'icons/mob/actions/actions_hive.dmi' - icon_state = "hivehand" - item_state = "hivehand" - lefthand_file = 'icons/mob/inhands/misc/touchspell_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/touchspell_righthand.dmi' - - weapon_stats = list(SWING_SPEED = 1, ENCUMBRANCE = 0, ENCUMBRANCE_TIME = 0, REACH = 3, DAMAGE_LOW = 0, DAMAGE_HIGH = 0) - min_reach = -1 - item_flags = ABSTRACT | DROPDEL - -/datum/action/cooldown/spell/telekinetic_hand - name = "Telekinetic hand" - desc = "Makes a telekinetic hand to extend the reach of our unarmed combat. Drop to remove." - invocation_type = INVOCATION_NONE - cooldown_time = 200 - panel = "Hivemind Abilities" - background_icon_state = "bg_hive" - button_icon = 'icons/mob/actions/actions_hive.dmi' - button_icon_state = "hivehand" - var/spell_item = /obj/item/extendohand/hivemind - -/datum/action/cooldown/spell/telekinetic_hand/cast(mob/owner = usr) - . = ..() - if(!.) - return FALSE - if(owner.get_active_held_item()==null) - var/obj/item/W = new spell_item - owner.put_in_hands(W) - to_chat(owner, span_notice("You make a telekinetic hand!")) - else - to_chat(owner,span_notice("You cannot make a telekinetic hand while holding something!")) - -/datum/action/cooldown/spell/pointed/hive_hack - name = "Network Invasion" - desc = "We probe the mind of an adjacent target and extract valuable information on any enemy hives they may belong to. Takes longer if the target is not in our hive or wearing tinfoil protection." - panel = "Hivemind Abilities" - cooldown_time = 600 - cast_range = 1 - invocation_type = INVOCATION_NONE - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "hack" - -/datum/action/cooldown/spell/pointed/hive_hack/cast(list/targets, mob/living/owner) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/mob/living/carbon/target = targets[1] - var/in_hive = hive.is_carbon_member(target) - var/list/enemies = list() - - to_chat(owner, span_notice("We begin probing [target.name]'s mind!")) - if(do_after(owner, 10 SECONDS, target, timed_action_flags = IGNORE_HELD_ITEM)) - var/foiled = target.can_block_magic(MAGIC_RESISTANCE_MIND) - if(!in_hive || foiled) - var/timely = !in_hive ? 200 : 100 - to_chat(owner, span_notice("Their mind slowly opens up to us.")) - if(!do_after(owner, timely, target, timed_action_flags = IGNORE_HELD_ITEM)) - to_chat(owner, span_notice("Our concentration has been broken!")) - return - for(var/datum/antagonist/hivemind/enemy in GLOB.antagonists) - var/datum/mind/M = enemy.owner - if(!M?.current) - continue - if(M.current == owner) - continue - if(enemy.is_carbon_member(target)) - hive.add_track_bonus(enemy, TRACKER_BONUS_LARGE) - var/mob/living/real_enemy = (M.current.get_real_hivehost()) - enemies += real_enemy - enemy.remove_from_hive(target) - real_enemy.apply_status_effect(STATUS_EFFECT_HIVE_TRACKER, owner, hive.get_track_bonus(enemy)) - if(M.current.is_real_hivehost()) //If they were using mind control, too bad - real_enemy.apply_status_effect(STATUS_EFFECT_HIVE_RADAR) - target.apply_status_effect(STATUS_EFFECT_HIVE_TRACKER, real_enemy, enemy.get_track_bonus(hive)) - to_chat(real_enemy, span_assimilator("We detect a surge of psionic energy from a far away vessel before they disappear from the hive. Whatever happened, there's a good chance they're after us now.")) - - if(enemy.owner == M && target.is_real_hivehost()) - var/atom/throwtarget - throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(owner, src))) - SEND_SOUND(owner, sound(pick('sound/hallucinations/turn_around1.ogg','sound/hallucinations/turn_around2.ogg'),0,1,50)) - flash_color(owner, flash_color="#800080", flash_time=10) - owner.Paralyze(10) - owner.throw_at(throwtarget, 5, 1,src) - to_chat(owner, span_ownerdanger("A sudden surge of psionic energy rushes into your mind, only a Hive host could have such power!!")) - return - if(enemies.len) - hive.track_bonus += TRACKER_BONUS_SMALL - to_chat(owner, span_ownerdanger("In a moment of clarity, we see all. Another hive. Faces. Our nemesis. They have heard our call. They know we are coming.")) - to_chat(owner, span_assimilator("This vision has provided us insight on our very nature, improving our sensory abilities, particularly against the hives this vessel belonged to.")) - owner.apply_status_effect(STATUS_EFFECT_HIVE_RADAR) - else - to_chat(owner, span_notice("We peer into the inner depths of their mind and see nothing, no enemies lurk inside this mind.")) - else - to_chat(owner, span_notice("Our concentration has been broken!")) - -/datum/action/cooldown/spell/pointed/hive_reclaim - name = "Reclaim" - desc = "Allows us to instantly syphon the psionic energy from an adjacent critically injured host, killing them immediately. If it succeeds, we will be able to advance our own powers a great deal." - panel = "Hivemind Abilities" - cooldown_time = 600 - cast_range = 1 - invocation_type = INVOCATION_NONE - spell_requirements = SPELL_REQUIRES_HUMAN - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "reclaim" - -/datum/action/cooldown/spell/pointed/hive_reclaim/cast(list/targets, mob/living/owner = usr) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/found_target = FALSE - var/gibbed = FALSE - - for(var/mob/living/carbon/C in targets) - if(!is_hivehost(C)) - continue - if(C.InCritical() || (C.stat == DEAD && C.timeofdeath + 150 >= world.time) ) - C.gib() - hive.track_bonus += TRACKER_BONUS_LARGE - hive.size_mod += 5 - hive.threat_level += 1 - gibbed = TRUE - found_target = TRUE - else if(C.IsUnconscious()) - C.adjustOxyLoss(100) - found_target = TRUE - - if(!found_target) - return - - flash_color(owner, flash_color="#800080", flash_time=10) - if(gibbed) - to_chat(owner,span_assimilator("We have reclaimed what gifts weaker minds were squandering and gain ever more insight on our psionic abilities.")) - to_chat(owner,span_assimilator("Thanks to this new knowledge, our sensory powers last a great deal longer.")) - hive.check_powers() - -/datum/action/cooldown/spell/hive_wake - name = "Chaos Induction" - desc = "A one-use power, we awaken four random vessels within our hive and force them to do our bidding." - panel = "Hivemind Abilities" - cooldown_time = 1 - invocation_type = INVOCATION_NONE - spell_requirements = SPELL_REQUIRES_HUMAN - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "chaos" - -/datum/action/cooldown/spell/hive_wake/cast(mob/living/owner) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - if(!hive.hivemembers) - return - var/list/valid_targets = list() - for(var/datum/mind/M in hive.hivemembers) - var/mob/living/carbon/C = M.current - if(!C || is_hivehost(C) || C.is_wokevessel() || C.stat == DEAD || C.InCritical() || C.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 4)) - continue - valid_targets += C - - if(!valid_targets || valid_targets.len < 4) - to_chat(owner, span_assimilator("We lack the vessels to use this power.")) - return - - var/objective = stripped_input(owner, "What objective do you want to give to your vessels?", "Objective") - - if(!objective || !hive) - return - - hive.threat_level += 6 - for(var/i = 0, i < 4, i++) - var/mob/living/carbon/C = pick_n_take(valid_targets) - C.hive_awaken(objective) - - return TRUE - -/datum/action/cooldown/spell/hive_loyal - name = "Bruteforce" - desc = "Our ability to assimilate is boosted at the cost of, allowing us to crush the technology shielding the minds of savyy personnel and assimilate them. This power comes at a small price, and we will be immobilized for a few seconds after assimilation." - panel = "Hivemind Abilities" - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "loyal" - - invocation_type = INVOCATION_NONE - - cooldown_time = 1 MINUTES - spell_requirements = SPELL_REQUIRES_HUMAN - var/active = FALSE - -/datum/action/cooldown/spell/hive_loyal/cast(mob/living/owner) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/datum/action/cooldown/spell/aoe/target_hive/hive_add/the_spell = locate(/datum/action/cooldown/spell/aoe/target_hive/hive_add) in owner.actions - if(!the_spell) - to_chat(owner, span_notice("This is a bug. Error:HIVE5")) - return - the_spell.bruteforce = !active - to_chat(owner, span_notice("We [active?"let our minds rest and cancel our crushing power.":"prepare to crush mindshielding technology!"]")) - active = !active - - return TRUE - -/datum/action/cooldown/spell/forcewall/hive - name = "Telekinetic Field" - desc = "Our psionic powers form a barrier around us in the phsyical world that only we can pass through." - panel = "Hivemind Abilities" - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "forcewall" - - invocation_type = INVOCATION_NONE - - wall_type = /obj/effect/forcefield/wizard/hive - cooldown_time = 1 MINUTES - spell_requirements = SPELL_REQUIRES_HUMAN - var/wall_type_b = /obj/effect/forcefield/wizard/hive/invis - -/datum/action/cooldown/spell/forcewall/hive/cast(list/targets,mob/owner = usr) - . = ..() - if(!.) - return FALSE - new wall_type(get_turf(owner),owner) - for(var/dir in GLOB.alldirs) - new wall_type_b(get_step(owner, dir),owner) - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(hive) - hive.threat_level += 0.5 - - return TRUE - -/obj/effect/forcefield/wizard/hive - name = "Telekinetic Field" - desc = "You think, therefore it is." - pixel_x = -32 //Centres the 96x96 sprite - pixel_y = -32 - icon = 'icons/effects/96x96.dmi' - icon_state = "hive_shield" - layer = ABOVE_ALL_MOB_LAYER - mouse_opacity = MOUSE_OPACITY_TRANSPARENT - -/obj/effect/forcefield/wizard/hive/CanPass(atom/movable/mover, turf/target) - SHOULD_CALL_PARENT(FALSE) - if(IS_WEAKREF_OF(mover, caster_weakref)) - return TRUE - return FALSE - -/obj/effect/forcefield/wizard/hive/invis - icon = null - icon_state = null - pixel_x = 0 - pixel_y = 0 - invisibility = INVISIBILITY_MAXIMUM - -/datum/action/cooldown/spell/one_mind - name = "One Mind" - desc = "Our true power... finally within reach." - panel = "Hivemind Abilities" - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "assim" - - invocation_type = INVOCATION_NONE - spell_requirements = SPELL_REQUIRES_HUMAN - - cooldown_time = 0.1 SECONDS - -/datum/action/cooldown/spell/one_mind/cast(mob/living/owner) - . = ..() - if(!.) - return FALSE - var/datum/antagonist/hivemind/hive = owner.mind.has_antag_datum(/datum/antagonist/hivemind) - if(!hive) - to_chat(owner, span_notice("This is a bug. Error:HIVE1")) - return - var/mob/living/boss = owner.get_real_hivehost() - var/datum/objective/protect/new_objective = new /datum/objective/protect - new_objective.target = owner.mind - new_objective.explanation_text = "Ensure the One Mind survives under the leadership of [boss.real_name]." - var/datum/team/hivemind/one_mind_team = new /datum/team/hivemind(owner.mind) - hive.active_one_mind = one_mind_team - one_mind_team.objectives += new_objective - for(var/datum/antagonist/hivevessel/vessel in GLOB.antagonists) - var/mob/living/carbon/C = vessel.owner?.current - if(C && hive.is_carbon_member(C)) - vessel.one_mind = one_mind_team - for(var/datum/antagonist/hivemind/enemy in GLOB.antagonists) - if(enemy.owner) - for(var/datum/action/cooldown/spell/one_mind/one_mind in enemy.owner.current.actions) - one_mind.Remove(enemy.owner.current) - sound_to_playing_players('sound/effects/one_mind.ogg') - hive.glow = mutable_appearance('icons/effects/hivemind.dmi', "awoken", -BODY_BEHIND_LAYER) - addtimer(CALLBACK(owner, TYPE_PROC_REF(/atom, add_overlay), hive.glow), 150) - addtimer(CALLBACK(hive, TYPE_PROC_REF(/datum/antagonist/hivemind, awaken)), 150) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(send_to_playing_players), span_bigassimilator("THE ONE MIND RISES")), 150) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(sound_to_playing_players), 'sound/effects/magic.ogg'), 150) - for(var/datum/mind/M in hive.hivemembers) - var/mob/living/carbon/C = M.current - if(!C) - continue - if(is_hivehost(C)) - continue - if(C.stat == DEAD) - continue - C.adjust_jitter(15 SECONDS) - C.Unconscious(150) - if(C.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 6)) - continue - to_chat(C, span_boldwarning("Something's wrong...")) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), C, span_boldwarning("...your memories are becoming fuzzy.")), 45) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), C, span_boldwarning("You try to remember who you are...")), 90) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), C, span_assimilator("There is no you...")), 110) - addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(to_chat), C, span_bigassimilator("...there is only us.")), 130) - addtimer(CALLBACK(C, /mob/living/proc/hive_awaken, new_objective, one_mind_team), 150) - - return TRUE - -/datum/action/cooldown/spell/hive_comms - name = "Hive Communication" - desc = "Now that we are free we may finally share our thoughts with our many bretheren." - panel = "Hivemind Abilities" - cooldown_time = 100 - invocation_type = INVOCATION_NONE - spell_requirements = SPELL_REQUIRES_HUMAN - button_icon = 'icons/mob/actions/actions_hive.dmi' - background_icon_state = "bg_hive" - button_icon_state = "comms" - -/datum/action/cooldown/spell/hive_comms/cast(mob/living/owner = usr) - . = ..() - if(!.) - return FALSE - var/message = stripped_input(owner, "What do you want to say?", "Hive Communication") - if(!message) - return - var/title = "One Mind" - var/span = "changeling" - if(owner.mind && owner.mind.has_antag_datum(/datum/antagonist/hivemind)) - span = "assimilator" - var/my_message = "[title] [findtextEx(owner.name, owner.real_name) ? owner.name : "[owner.real_name] (as [owner.name])"]: [message]" - for(var/i in GLOB.player_list) - var/mob/M = i - if(is_hivehost(M) || is_hivemember(M)) - to_chat(M, my_message) - else if(M in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(M, owner) - to_chat(M, "[link] [my_message]") - - owner.log_talk(message, LOG_SAY, tag="hive") diff --git a/code/modules/spells/spell_types/right_and_wrong.dm b/code/modules/spells/spell_types/right_and_wrong.dm index 87a980a19bf9..ea1574fe172a 100644 --- a/code/modules/spells/spell_types/right_and_wrong.dm +++ b/code/modules/spells/spell_types/right_and_wrong.dm @@ -109,7 +109,7 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( CRASH("give_guns() was called without a summon guns global datum!") if(to_equip.stat == DEAD || !to_equip.client || !to_equip.mind) return - if(iswizard(to_equip) || to_equip.mind.has_antag_datum(/datum/antagonist/survivalist/guns)) + if(IS_WIZARD(to_equip) || to_equip.mind.has_antag_datum(/datum/antagonist/survivalist/guns)) return if(!length(to_equip.mind.antag_datums) && prob(GLOB.summon_guns.survivor_probability)) @@ -134,7 +134,7 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( CRASH("give_magic() was called without a summon magic global datum!") if(to_equip.stat == DEAD || !to_equip.client || !to_equip.mind) return - if(is_wizard(to_equip) || to_equip.mind.has_antag_datum(/datum/antagonist/survivalist/guns)) + if(IS_WIZARD(to_equip) || to_equip.mind.has_antag_datum(/datum/antagonist/survivalist/guns)) return if(!length(to_equip.mind.antag_datums) && prob(GLOB.summon_magic.survivor_probability)) @@ -157,7 +157,7 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( */ /proc/summon_ghosts(mob/user) - var/datum/round_event_control/wizard/ghost/ghost_event = locate() in SSevents.control + var/datum/round_event_control/wizard/ghost/ghost_event = locate() in SSgamemode.control if(ghost_event) if(user) to_chat(user, span_warning("You summoned ghosts!")) @@ -217,13 +217,10 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( * If Summon Events has already been triggered, speeds up the event timer. */ /proc/summon_events(mob/user) - // Already in wiz-mode? Speed er up - if(SSevents.wizardmode) - SSevents.frequency_upper -= 1 MINUTES //The upper bound falls a minute each time, making the AVERAGE time between events lessen - if(SSevents.frequency_upper < SSevents.frequency_lower) //Sanity - SSevents.frequency_upper = SSevents.frequency_lower + SSgamemode.event_frequency_multiplier *= 1.1 //gamemode generates points faster - SSevents.reschedule() + // Already in wiz-mode? different text + if(SSgamemode.wizardmode) if(user) to_chat(user, span_warning("You have intensified summon events, causing them to occur more often!")) message_admins("[ADMIN_LOOKUPFLW(user)] intensified summon events!") @@ -231,14 +228,11 @@ GLOBAL_LIST_INIT(summoned_magic_objectives, list( else log_game("Summon Events was intensified!") - message_admins("Summon Events intensifies, events will now occur every [SSevents.frequency_lower / 600] to [SSevents.frequency_upper / 600] minutes.") + message_admins("Summon Events intensifies, events will now occur even faster.") - // Not in wiz-mode? Get this show on the road + // Not in wiz-mode? toggle it else - SSevents.frequency_lower = 1 MINUTES //1 minute lower bound - SSevents.frequency_upper = 5 MINUTES //5 minutes upper bound - SSevents.toggleWizardmode() - SSevents.reschedule() + SSgamemode.toggleWizardmode() if(user) to_chat(user, span_warning("You have cast summon events!")) message_admins("[ADMIN_LOOKUPFLW(user)] summoned events!") diff --git a/code/modules/surgery/organs/vocal_cords.dm b/code/modules/surgery/organs/vocal_cords.dm index 1578891e3b2d..15d3d43019dc 100644 --- a/code/modules/surgery/organs/vocal_cords.dm +++ b/code/modules/surgery/organs/vocal_cords.dm @@ -191,7 +191,7 @@ for(var/V in listeners) var/mob/living/L = V - var/datum/antagonist/devil/devilinfo = is_devil(L) + var/datum/antagonist/devil/devilinfo = IS_DEVIL(L) if(devilinfo && findtext(message, devilinfo.truename)) var/start = findtext(message, devilinfo.truename) listeners = list(L) //Devil names are unique. @@ -388,8 +388,8 @@ for(var/V in listeners) var/mob/living/L = V var/text = "" - if(is_devil(L)) - var/datum/antagonist/devil/devilinfo = is_devil(L) + if(IS_DEVIL(L)) + var/datum/antagonist/devil/devilinfo = IS_DEVIL(L) text = devilinfo.truename else text = L.real_name diff --git a/code/modules/tgui_input/checkboxes.dm b/code/modules/tgui_input/checkboxes.dm new file mode 100644 index 000000000000..7b1129af30cc --- /dev/null +++ b/code/modules/tgui_input/checkboxes.dm @@ -0,0 +1,132 @@ +/** + * ### tgui_input_checkbox + * Opens a window with a list of checkboxes and returns a list of selected choices. + * + * user - The mob to display the window to + * message - The message inside the window + * title - The title of the window + * list/items - The list of items to display + * min_checked - The minimum number of checkboxes that must be checked (defaults to 1) + * max_checked - The maximum number of checkboxes that can be checked (optional) + * timeout - The timeout for the input (optional) + */ +/proc/tgui_input_checkboxes(mob/user, message, title = "Select", list/items, min_checked = 1, max_checked = 50, timeout = 0) + if (!user) + user = usr + if(!length(items)) + return + if (!istype(user)) + if (istype(user, /client)) + var/client/client = user + user = client.mob + else + return + if(!user.client.prefs.read_preference(/datum/preference/toggle/tgui_input)) + return input(user, message, title) as null|anything in items + var/datum/tgui_checkbox_input/input = new(user, message, title, items, min_checked, max_checked, timeout) + input.ui_interact(user) + input.wait() + if (input) + . = input.choices + qdel(input) + +/// Window for tgui_input_checkboxes +/datum/tgui_checkbox_input + /// Title of the window + var/title + /// Message to display + var/message + /// List of items to display + var/list/items + /// List of selected items + var/list/choices + /// Time when the input was created + var/start_time + /// Timeout for the input + var/timeout + /// Whether the input was closed + var/closed + /// Minimum number of checkboxes that must be checked + var/min_checked + /// Maximum number of checkboxes that can be checked + var/max_checked + +/datum/tgui_checkbox_input/New(mob/user, message, title, list/items, min_checked, max_checked, timeout) + src.title = title + src.message = message + src.items = items.Copy() + src.min_checked = min_checked + src.max_checked = max_checked + + if (timeout) + src.timeout = timeout + start_time = world.time + QDEL_IN(src, timeout) + +/datum/tgui_checkbox_input/Destroy(force, ...) + SStgui.close_uis(src) + QDEL_NULL(items) + + return ..() + +/datum/tgui_checkbox_input/proc/wait() + while (!closed && !QDELETED(src)) + stoplag(1) + +/datum/tgui_checkbox_input/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "CheckboxInput") + ui.open() + +/datum/tgui_checkbox_input/ui_close(mob/user) + . = ..() + closed = TRUE + +/datum/tgui_checkbox_input/ui_state(mob/user) + return GLOB.always_state + +/datum/tgui_checkbox_input/ui_data(mob/user) + var/list/data = list() + + if(timeout) + data["timeout"] = CLAMP01((timeout - (world.time - start_time) - 1 SECONDS) / (timeout - 1 SECONDS)) + + return data + +/datum/tgui_checkbox_input/ui_static_data(mob/user) + var/list/data = list() + + data["items"] = items + data["min_checked"] = min_checked + data["max_checked"] = max_checked + data["large_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_large) + data["message"] = message + data["swapped_buttons"] = user.client.prefs.read_preference(/datum/preference/toggle/tgui_input_swapped) + data["title"] = title + + return data + +/datum/tgui_checkbox_input/ui_act(action, list/params) + . = ..() + if (.) + return + + switch(action) + if("submit") + var/list/selections = params["entry"] + if(length(selections) >= min_checked && length(selections) <= max_checked) + set_choices(selections) + closed = TRUE + SStgui.close_uis(src) + return TRUE + + if("cancel") + closed = TRUE + SStgui.close_uis(src) + return TRUE + + return FALSE + +/datum/tgui_checkbox_input/proc/set_choices(list/selections) + src.choices = selections.Copy() diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 8b1a6e8f7ddd..d7c5b112465e 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -75,7 +75,6 @@ #include "component_tests.dm" #include "dcs_check_list_arguments.dm" #include "dragon_expiration.dm" -#include "dynamic_ruleset_sanity.dm" #include "focus_only_tests.dm" #include "greyscale_config.dm" #include "map_landmarks.dm" diff --git a/code/modules/unit_tests/dynamic_ruleset_sanity.dm b/code/modules/unit_tests/dynamic_ruleset_sanity.dm deleted file mode 100644 index 1aea082a6b92..000000000000 --- a/code/modules/unit_tests/dynamic_ruleset_sanity.dm +++ /dev/null @@ -1,13 +0,0 @@ -/// Verifies that roundstart dynamic rulesets are setup properly without external configuration. -/datum/unit_test/dynamic_roundstart_ruleset_sanity - -/datum/unit_test/dynamic_roundstart_ruleset_sanity/Run() - for (var/datum/dynamic_ruleset/roundstart/ruleset as anything in subtypesof(/datum/dynamic_ruleset/roundstart)) - - var/has_scaling_cost = initial(ruleset.scaling_cost) - var/is_lone = initial(ruleset.flags) & (LONE_RULESET | HIGH_IMPACT_RULESET) - - if (has_scaling_cost && is_lone) - TEST_FAIL("[ruleset] has a scaling_cost, but is also a lone/highlander ruleset.") - else if (!has_scaling_cost && !is_lone) - TEST_FAIL("[ruleset] has no scaling cost, but is also not a lone/highlander ruleset.") diff --git a/code/modules/uplink/uplink_devices.dm b/code/modules/uplink/uplink_devices.dm index 5c839e3e09de..17a5c0f4adab 100644 --- a/code/modules/uplink/uplink_devices.dm +++ b/code/modules/uplink/uplink_devices.dm @@ -34,7 +34,7 @@ /obj/item/uplink/nuclear/Initialize(mapload) . = ..() var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink) - hidden_uplink.set_gamemode(/datum/game_mode/nuclear) + hidden_uplink.set_antagonist(ROLE_OPERATIVE) /obj/item/uplink/nuclear/debug name = "debug nuclear uplink" @@ -42,19 +42,19 @@ /obj/item/uplink/nuclear/debug/Initialize(mapload, owner, tc_amount = 9000) . = ..() var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink) - hidden_uplink.set_gamemode(/datum/game_mode/nuclear) + hidden_uplink.set_antagonist(ROLE_OPERATIVE) hidden_uplink.name = "debug nuclear uplink" /obj/item/uplink/nuclear_restricted/Initialize(mapload) . = ..() var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink) hidden_uplink.allow_restricted = FALSE - hidden_uplink.set_gamemode(/datum/game_mode/nuclear) + hidden_uplink.set_antagonist(ROLE_OPERATIVE) /obj/item/uplink/clownop/Initialize(mapload) . = ..() var/datum/component/uplink/hidden_uplink = GetComponent(/datum/component/uplink) - hidden_uplink.set_gamemode(/datum/game_mode/nuclear/clown_ops) + hidden_uplink.set_antagonist(ROLE_CLOWNOP) /obj/item/uplink/old name = "dusty radio" diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index d213daf292e4..d72bef4f877a 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1,6 +1,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) -/proc/get_uplink_items(datum/game_mode/gamemode = null, allow_sales = TRUE, allow_restricted = TRUE, uplink_type = "Uplink") +/proc/get_uplink_items(antagonist = null, allow_sales = TRUE, allow_restricted = TRUE, uplink_type = "Uplink") var/list/filtered_uplink_items = list() var/list/sale_items = list() @@ -10,15 +10,11 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) continue if(I.include_uplinks.len && !(uplink_type in I.include_uplinks)) continue - if(I.include_modes.len) - if(!gamemode && SSticker.mode && !(SSticker.mode.type in I.include_modes)) + if(I.include_antags.len) + if(antagonist && !(antagonist in I.include_antags)) continue - if(gamemode && !(gamemode in I.include_modes)) - continue - if(I.exclude_modes.len) - if(!gamemode && SSticker.mode && (SSticker.mode.type in I.exclude_modes)) - continue - if(gamemode && (gamemode in I.exclude_modes)) + if(I.exclude_antags.len) + if(antagonist && (antagonist in I.exclude_antags)) continue if(I.player_minimum && I.player_minimum > GLOB.joined_player_list.len) continue @@ -32,7 +28,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) sale_items += I if(allow_sales) var/datum/team/nuclear/nuclear_team - if (gamemode == /datum/game_mode/nuclear) // uplink code kind of needs a redesign + if (antagonist == ROLE_OPERATIVE) // uplink code kind of needs a redesign nuclear_team = locate() in GLOB.antagonist_teams // the team discounts could be a in a GLOB with this design but it would make sense for them to be team specific... if (!nuclear_team) create_uplink_sales(3, "Discounted Gear", 1, sale_items, filtered_uplink_items) @@ -97,8 +93,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) var/cant_discount = FALSE var/limited_stock = -1 //Setting this above zero limits how many times this item can be bought by the same traitor in a round, -1 is unlimited var/list/include_uplinks = list("Uplink") // Uplink types this is in - var/list/include_modes = list() // Game modes to allow this item in. - var/list/exclude_modes = list() // Game modes to disallow this item from. + var/list/include_antags = list() // Game modes to allow this item in. + var/list/exclude_antags = list() // Game modes to disallow this item from. var/list/restricted_roles = list() //If this uplink item is only available to certain roles. Roles are dependent on the frequency chip or stored ID. var/player_minimum //The minimum crew size needed for this item to be added to uplinks. var/purchase_log_vis = TRUE // Visible in the purchase log? @@ -154,7 +150,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/backpack/duffelbag/syndie/med/bioterrorbundle cost = 30 // normally 42 manufacturer = /datum/corporation/traitor/donkco - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/bundles_TC/bulldog name = "Bulldog bundle" @@ -162,14 +158,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Bulldog shotgun, two 12g buckshot drums, and a pair of Thermal imaging goggles." item = /obj/item/storage/backpack/duffelbag/syndie/bulldogbundle cost = 13 // normally 16 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/bundles_TC/c20r name = "C-20r bundle" desc = "Old Faithful: The classic C-20r, bundled with two magazines and a (surplus) suppressor at discount price." item = /obj/item/storage/backpack/duffelbag/syndie/c20rbundle cost = 14 // normally 16 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/bundles_TC/cyber_implants name = "Cybernetic Implants Bundle" @@ -177,7 +173,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/cyber_implants cost = 40 manufacturer = /datum/corporation/traitor/cybersun - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/bundles_TC/medical name = "Medical bundle" @@ -185,7 +181,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) a Donksoft LMG, a box of riot darts and a pair of magboots to rescue your friends in no-gravity environments." item = /obj/item/storage/backpack/duffelbag/syndie/med/medicalbundle cost = 15 // normally 20 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/bundles_TC/sniper name = "Sniper bundle" @@ -194,7 +190,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) We'll throw in a free red tie if you order NOW." item = /obj/item/storage/briefcase/sniperbundle cost = 20 // normally 26 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/bundles_TC/firestarter name = "Spetsnaz Pyro bundle" @@ -203,7 +199,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Order NOW and comrade Boris will throw in an extra tracksuit." item = /obj/item/storage/backpack/duffelbag/syndie/firestarter cost = 30 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/bundles_TC/contract_kit name = "Contract Kit" @@ -214,7 +210,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/syndicate/contract_kit cost = 20 player_minimum = 20 - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP, ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/bundles_TC/contract_kit/spawn_item(spawn_path, mob/user, datum/component/uplink/U) if(is_species(user, /datum/species/plasmaman)) @@ -228,7 +224,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) you will receive. May contain discontinued and/or exotic items." item = /obj/item/storage/box/syndicate/bundle_A cost = 20 //These are 20 TC for a reason; sacrifice modularity for a pre-determined kit that will define your strategy - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_OPERATIVE, ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/bundles_TC/bundle_B name = "Syndi-kit Special" @@ -236,7 +232,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) In Syndi-kit Special, you will receive items used by famous Syndicate agents of the past. Collectively worth more than 20 telecrystals, the Syndicate loves a good throwback." item = /obj/item/storage/box/syndicate/bundle_B cost = 20 //See above - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_OPERATIVE, ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/bundles_TC/surplus name = "Syndicate Surplus Crate" @@ -245,7 +241,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/structure/closet/crate cost = 20 player_minimum = 25 - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP, ROLE_INFILTRATOR) // yogs: infiltration var/starting_crate_value = 50 /datum/uplink_item/bundles_TC/surplus/super @@ -352,7 +348,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/rocketlauncher cost = 8 surplus = 30 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/pie_cannon name = "Banana Cream Pie Cannon" @@ -361,7 +357,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) manufacturer = /datum/corporation/traitor/waffleco item = /obj/item/pneumatic_cannon/pie/selfcharge surplus = 0 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/dangerous/bananashield name = "Bananium Energy Shield" @@ -371,7 +367,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/shield/energy/bananium cost = 16 surplus = 0 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/dangerous/clownsword name = "Bananium Energy Sword" @@ -380,7 +376,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/melee/transforming/energy/sword/bananium cost = 3 surplus = 0 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/dangerous/bioterror name = "Biohazardous Chemical Sprayer" @@ -390,7 +386,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/reagent_containers/spray/chemsprayer/bioterror cost = 20 surplus = 0 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/dangerous/throwingweapons name = "Box of Throwing Weapons" @@ -406,7 +402,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/shotgun/bulldog cost = 8 surplus = 40 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/smg name = "C-20r Submachine Gun" @@ -415,7 +411,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/automatic/c20r cost = 10 surplus = 40 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/doublesword name = "Double-Bladed Energy Sword" @@ -424,7 +420,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/melee/dualsaber player_minimum = 25 cost = 16 - include_modes = list(/datum/game_mode/nuclear) // yogs: infiltration + include_antags = list(ROLE_OPERATIVE) // yogs: infiltration /datum/uplink_item/dangerous/doublesword/get_discount() return pick(4;0.8,2;0.65,1;0.5) @@ -435,7 +431,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) pocketed when inactive. Activating it produces a loud, distinctive noise." item = /obj/item/melee/transforming/energy/sword/saber cost = 8 - exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_CLOWNOP, ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/dangerous/backstab name = "Backstabbing Switchblade" @@ -450,7 +446,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A wielded wooden staff that can be used to incapacitate opponents if intending to disarm." item = /obj/item/melee/bostaff cost = 8 - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/dangerous/shield name = "Energy Shield" @@ -459,7 +455,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/shield/energy cost = 16 surplus = 20 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/flamethrower name = "Flamethrower" @@ -468,14 +464,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/flamethrower/full cost = 4 surplus = 40 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/rapid name = "Gloves of the North Star" desc = "These gloves let the user punch people very fast. Does not improve weapon attack speed or the meaty fists of a hulk." item = /obj/item/clothing/gloves/rapid cost = 8 - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/dangerous/guardian name = "Holoparasites" @@ -485,7 +481,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 15 manufacturer = /datum/corporation/traitor/cybersun surplus = 0 - exclude_modes = list(/datum/game_mode/infiltration, /datum/game_mode/traitor/internal_affairs) + exclude_antags = list(ROLE_INFILTRATOR, ROLE_INTERNAL_AFFAIRS) player_minimum = 25 restricted = TRUE refundable = TRUE @@ -493,8 +489,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) // nukies don't get the 3 TC discount /datum/uplink_item/dangerous/guardian/nuclear cost = 15 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) - exclude_modes = list() + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) + exclude_antags = list() /datum/uplink_item/dangerous/machinegun name = "L6 Squad Automatic Weapon" @@ -503,7 +499,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/automatic/l6_saw cost = 20 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/minigun name = "M-546 Osprey" @@ -515,7 +511,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 30 surplus = 0 cant_discount = TRUE - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/rifle name = "M-90gl Rifle" @@ -524,7 +520,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/automatic/m90 cost = 18 surplus = 50 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/powerfist name = "Power Fist" @@ -535,7 +531,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/clothing/gloves/powerfist cost = 6 manufacturer = /datum/corporation/traitor/waffleco - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/dangerous/vxtvulhammer name = "Vxtvul Hammer" @@ -545,7 +541,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) do sizeable damage to mechs, or shatter people off of their feet. The battery is charged by the user's concentration." item = /obj/item/melee/vxtvulhammer cost = 8 - include_modes = list(/datum/game_mode/nuclear) //Only traitor preterni can buy the implant version + include_antags = list(ROLE_OPERATIVE) //Only traitor preterni can buy the implant version /datum/uplink_item/dangerous/nukiedmr name = "K-41s Designated Marksman Rifle" @@ -553,7 +549,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) a short-range scope to better see over distances." item = /obj/item/gun/ballistic/automatic/k41s cost = 12 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/sniper name = "Sniper Rifle" @@ -561,7 +557,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/rifle/sniper_rifle/syndicate cost = 16 surplus = 25 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/pistol name = "Stechkin Pistol" @@ -569,7 +565,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) with suppressors. Ammo is included" item = /obj/item/gun/ballistic/automatic/pistol cost = 5 - exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_CLOWNOP) /datum/uplink_item/dangerous/pistol/spawn_item(spawn_path, mob/user, datum/component/uplink/U) if(HAS_TRAIT_FROM(user, TRAIT_PACIFISM, ROUNDSTART_TRAIT)) @@ -582,7 +578,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A horribly outdated bolt action weapon. You've got to be desperate to use this." item = /obj/item/gun/ballistic/rifle/boltaction cost = 1 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/dangerous/revolver name = "Syndicate Revolver" @@ -598,7 +594,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 5 manufacturer = /datum/corporation/traitor/donkco surplus = 0 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/dangerous/foammachinegun name = "Toy Machine Gun" @@ -608,7 +604,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 10 manufacturer = /datum/corporation/traitor/donkco surplus = 0 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/dangerous/foampistol name = "Toy Pistol with Riot Darts" @@ -635,7 +631,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 6 player_minimum = 25 surplus = 25 - exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_CLOWNOP) /datum/uplink_item/dangerous/nuclear_energy_fire_axe name = "Energy Fire Axe" @@ -643,7 +639,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Easier to store than a standard fire axe while inactive." item = /obj/item/fireaxe/energy cost = 10 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) surplus = 0 // Stealthy Weapons @@ -656,7 +652,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) to learn the abilities of krav maga to the wearer." item = /obj/item/clothing/gloves/krav_maga/combatglovesplus cost = 5 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) surplus = 0 /datum/uplink_item/stealthy_weapons/cqc @@ -682,7 +678,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/toy/plush/carpplushie/dehy_carp cost = 1 manufacturer = /datum/corporation/traitor/donkco - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/stealthy_weapons/derringer name = "Derringer Pistol" @@ -690,7 +686,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/ballistic/revolver/derringer cost = 3 manufacturer = /datum/corporation/traitor/donkco - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/stealthy_weapons/edagger name = "Energy Dagger" @@ -715,7 +711,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 14 player_minimum = 20 surplus = 0 - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP, ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/stealthy_weapons/crossbow name = "Miniature Energy Crossbow" @@ -728,7 +724,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/gun/energy/kinetic_accelerator/crossbow cost = 5 surplus = 30 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_antags = list(ROLE_OPERATIVE) /datum/uplink_item/stealthy_weapons/origami_kit name = "Boxed Origami Kit" @@ -738,7 +734,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 14 manufacturer = /datum/corporation/traitor/waffleco surplus = 0 - exclude_modes = list(/datum/game_mode/nuclear) //clown ops intentionally left in, because that seems like some s-tier shenanigans. + exclude_antags = list(ROLE_OPERATIVE) //clown ops intentionally left in, because that seems like some s-tier shenanigans. /datum/uplink_item/stealthy_weapons/traitor_chem_bottle name = "Poison Kit" @@ -757,7 +753,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 25 surplus = 0 //Hijack-only, don't let this exist in surplus cant_discount = TRUE - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/stealthy_weapons/sleepy_pen name = "Sleepy Pen" @@ -768,7 +764,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/pen/blue/sleepy cost = 4 manufacturer = /datum/corporation/traitor/waffleco - exclude_modes = list(/datum/game_mode/nuclear) + exclude_antags = list(ROLE_OPERATIVE) /datum/uplink_item/stealthy_weapons/suppressor name = "Suppressor" @@ -776,7 +772,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/suppressor cost = 1 surplus = 10 - exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_CLOWNOP) // Ammunition /datum/uplink_item/ammo @@ -788,7 +784,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A box that contains two additional 10-round 10mm magazines; compatible with the Stechkin Pistol." item = /obj/item/storage/box/syndie_kit/pistolammo cost = 1 - exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_CLOWNOP) /datum/uplink_item/ammo/pistol/random name = "Random 10mm Handgun Magazines" @@ -839,7 +835,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/ammo/shotgun cost = 2 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/shotgun/bag name = "12g Ammo Duffel Bag" @@ -896,7 +892,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A box with two .357 speed loaders. These speed loaders contain seven .357 rounds each; usable with the Syndicate revolver." item = /obj/item/storage/box/syndie_kit/revolverammo cost = 1 - exclude_modes = list(/datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_CLOWNOP) illegal_tech = FALSE /datum/uplink_item/ammo/revolver/random @@ -941,7 +937,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) These bullets pack less punch than 7.12x82mm rounds, but they still offer more power than .45 ammo." item = /obj/item/ammo_box/magazine/m556 cost = 4 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/rifle/ap name = "5.56mm Armor-Piercing Toploader Magazine" @@ -962,14 +958,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Your teammates will ask you to not shoot these down small hallways." item = /obj/item/ammo_casing/a40mm cost = 2 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/smg/bag name = ".45 Ammo Duffel Bag" desc = "A duffel bag containing five standard .45 magazines, two AP .45 magazines, and two HP .45 magazines for the C-20r submachine gun, bundled together at a discount." item = /obj/item/storage/backpack/duffelbag/syndie/ammo/smg cost = 20 //instead of 29 TC - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/smg/bag/random name = "Randomized .45 Ammo Duffel Bag" @@ -982,7 +978,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "An additional 24-round .45 magazine suitable for use with the C-20r submachine gun." item = /obj/item/ammo_box/magazine/smgm45 cost = 3 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/smg/ap name = ".45 Armor-Piercing SMG Magazine" @@ -1009,7 +1005,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A standard 11-round magazine for the K-41s DMR. Filled with 7.62 rounds." item = /obj/item/ammo_box/magazine/ks762 cost = 3 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/nukiedmr/raze name = "7.62 Raze Rifle Magazine" @@ -1037,7 +1033,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "An additional standard 6-round magazine for use with .50 sniper rifles." item = /obj/item/ammo_box/magazine/sniper_rounds cost = 4 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/sniper/penetrator name = ".50 Penetrator Magazine" @@ -1055,7 +1051,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/ammo/machinegun cost = 6 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/machinegun/basic name = "7.12x82mm Box Magazine" @@ -1083,7 +1079,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/ammo_box/magazine/mm712x82/incen /datum/uplink_item/ammo/rocket - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/rocket/basic name = "84mm HE Rocket" @@ -1103,7 +1099,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "An additional 15-round 9mm magazine, compatible with the Stechkin APS pistol, found in the Spetsnaz Pyro bundle." item = /obj/item/ammo_box/magazine/pistolm9mm cost = 2 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/toydarts name = "Box of Riot Darts" @@ -1121,28 +1117,28 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/syndie_kit/bioterror manufacturer = /datum/corporation/traitor/vahlen cost = 6 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/ammo/bolt_action name = "Surplus Rifle Clip" desc = "A stripper clip used to quickly load bolt action rifles. Contains 5 rounds." item = /obj/item/ammo_box/a762 cost = 1 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/dark_gygax/bag name = "Dark Gygax Ammo Bag" desc = "A duffel bag containing ammo for three full reloads of the incendiary carbine and flash bang launcher that are equipped on a standard Dark Gygax exosuit." item = /obj/item/storage/backpack/duffelbag/syndie/ammo/dark_gygax cost = 4 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/ammo/mauler/bag name = "Mauler Ammo Bag" desc = "A duffel bag containing ammo for three full reloads of the LMG, scattershot carbine, and SRM-8 missile launcher that are equipped on a standard Mauler exosuit." item = /obj/item/storage/backpack/duffelbag/syndie/ammo/mauler cost = 6 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) //Grenades and Explosives /datum/uplink_item/explosives @@ -1156,7 +1152,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/grenade/chem_grenade/bioterrorfoam cost = 5 surplus = 35 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/explosives/bombanana name = "Bombanana" @@ -1165,7 +1161,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/reagent_containers/food/snacks/grown/banana/bombanana cost = 4 //it is a bit cheaper than a minibomb because you have to take off your helmet to eat it, which is how you arm it surplus = 0 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/explosives/buzzkill name = "Buzzkill Grenade Box" @@ -1175,7 +1171,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 6 manufacturer = /datum/corporation/bolsynpowell surplus = 35 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/explosives/c4 name = "Composition C-4" @@ -1191,7 +1187,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/backpack/duffelbag/syndie/c4 cost = 8 //20% discount! cant_discount = TRUE - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/explosives/x4bag name = "Bag of X-4 explosives" @@ -1201,7 +1197,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/backpack/duffelbag/syndie/x4 cost = 4 cant_discount = TRUE - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/explosives/clown_bomb_clownops name = "Clown Bomb" @@ -1214,7 +1210,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 15 manufacturer = /datum/corporation/traitor/waffleco surplus = 0 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/explosives/detomatix name = "BomberMan Program" @@ -1225,7 +1221,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 6 manufacturer = /datum/corporation/traitor/cybersun restricted = TRUE - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/explosives/detomatix/spawn_item(spawn_path, mob/user, datum/component/uplink/U) . = ..() @@ -1256,7 +1252,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/deployablemine/traitor cost = 4 manufacturer = /datum/corporation/traitor/waffleco - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/explosives/virus_grenade name = "Fungal Tuberculosis Grenade" @@ -1266,14 +1262,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/syndie_kit/tuberculosisgrenade cost = 12 surplus = 35 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) restricted = TRUE /datum/uplink_item/explosives/grenadier name = "Grenadier's belt" desc = "A belt containing 26 lethally dangerous and destructive grenades. Comes with an extra multitool and screwdriver." item = /obj/item/storage/belt/grenade/full - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) cost = 22 surplus = 0 @@ -1284,7 +1280,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/deployablemine/traitor/bigboom cost = 10 manufacturer = /datum/corporation/traitor/waffleco - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/explosives/pizza_bomb name = "Pizza Bomb" @@ -1301,7 +1297,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/grenade/clusterbuster/soap cost = 3 manufacturer = /datum/corporation/traitor/waffleco - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/explosives/syndicate_bomb name = "Syndicate Bomb" @@ -1313,7 +1309,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) The bomb core can be pried out and manually detonated with other explosives." item = /obj/item/sbeacondrop/bomb cost = 11 - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/explosives/syndicate_bomb/emp name = "Syndicate EMP Bomb" @@ -1334,14 +1330,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) the blast radius before using the detonator." item = /obj/item/syndicatedetonator cost = 3 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/explosives/frag_grenade name = "Frag Grenade" desc = "Simple, but lethal. Anything adjacent when it explodes will be heavily damaged. Likely to cause a small hull breach." item = /obj/item/grenade/syndieminibomb/concussion/frag cost = 3 - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/explosives/syndicate_minibomb name = "Syndicate Minibomb" @@ -1349,8 +1345,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) in addition to dealing high amounts of damage to nearby personnel." item = /obj/item/grenade/syndieminibomb cost = 6 - include_modes = list(/datum/game_mode/nuclear) - exclude_modes = list(/datum/game_mode/nuclear/clown_ops, /datum/game_mode/infiltration) // yogs: infiltration + include_antags = list(ROLE_OPERATIVE) + exclude_antags = list(ROLE_CLOWNOP, ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/explosives/tearstache name = "Tearstache Grenade" @@ -1360,7 +1356,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 3 manufacturer = /datum/corporation/traitor/waffleco surplus = 0 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/explosives/viscerators name = "Viscerator Delivery Grenade" @@ -1369,7 +1365,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/grenade/spawnergrenade/manhacks cost = 5 surplus = 35 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/explosives/wheelchair name = "Explosive Wheelchair" @@ -1384,14 +1380,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/support category = UPLINK_CATEGORY_SUPPORT surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/support/clown_reinforcement name = "Clown Reinforcements" desc = "Call in an additional clown to share the fun, equipped with full starting gear, but no telecrystals." item = /obj/item/antag_spawner/nuke_ops/clown cost = 20 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) restricted = TRUE /datum/uplink_item/support/reinforcement @@ -1401,7 +1397,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/antag_spawner/nuke_ops cost = 25 refundable = TRUE - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) restricted = TRUE /datum/uplink_item/support/reinforcement/assault_borg @@ -1446,7 +1442,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A clown combat mech equipped with bombanana peel and tearstache grenade launchers, as well as the ubiquitous HoNkER BlAsT 5000." item = /obj/mecha/combat/honker/dark/loaded cost = 35 //Yogs change - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) cant_discount = TRUE /datum/uplink_item/support/mauler @@ -1502,21 +1498,21 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) any actions for the 7 second duration." item = /obj/item/pseudocider cost = 8 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_antags = list(ROLE_OPERATIVE) /datum/uplink_item/stealthy_tools/shadowcloak name = "Cloaker Belt" desc = "A tactical belt that renders the wearer invisible while active. Has a short charge that is refilled in darkness; only charges when in use." item = /obj/item/storage/belt/military/shadowcloak cost = 10 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_antags = list(ROLE_OPERATIVE) /datum/uplink_item/stealthy_tools/nuclearshadowcloak name = "Cloaker Belt" desc = "A tactical belt that renders the wearer invisible while active. Has a short charge that is refilled in darkness; only charges when in use." item = /obj/item/storage/belt/military/shadowcloak cost = 20 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/stealthy_tools/syndireverse name = "Bluespace Projectile Weapon Disrupter" @@ -1532,7 +1528,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/storage/box/syndie_kit/chameleon cost = 2 manufacturer = /datum/corporation/traitor/cybersun - exclude_modes = list(/datum/game_mode/nuclear) + exclude_antags = list(ROLE_OPERATIVE) /datum/uplink_item/stealthy_tools/chameleon/spawn_item(spawn_path, mob/user, datum/component/uplink/U) if(is_species(user, /datum/species/plasmaman)) @@ -1572,7 +1568,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/clothing/shoes/clown_shoes/banana_shoes/combat cost = 6 surplus = 0 - include_modes = list(/datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_CLOWNOP) /datum/uplink_item/stealthy_tools/emplight name = "EMP Flashlight" @@ -1597,7 +1593,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 4 manufacturer = /datum/corporation/traitor/vahlen surplus = 30 - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/stealthy_tools/syndigaloshes name = "No-Slip Chameleon Shoes" @@ -1606,13 +1602,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/clothing/shoes/chameleon/noslip/syndicate cost = 2 manufacturer = /datum/corporation/traitor/waffleco - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/stealthy_tools/syndigaloshes/nuke item = /obj/item/clothing/shoes/chameleon/noslip/syndicate cost = 4 - exclude_modes = list() - include_modes = list(/datum/game_mode/nuclear) + exclude_antags = list() + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/stealthy_tools/jammer name = "Signal Jammer" @@ -1664,7 +1660,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) sightings, however." item = /obj/item/storage/box/syndie_kit/space cost = 4 - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/suits/hardsuit name = "Syndicate Hardsuit" @@ -1675,7 +1671,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Nanotrasen crew who spot these suits are known to panic." item = /obj/item/clothing/suit/space/hardsuit/syndi cost = 8 - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/infiltration) //you can't buy it in nuke, because the elite hardsuit costs the same while being better // yogs: infiltration + exclude_antags = list(ROLE_OPERATIVE, ROLE_INFILTRATOR) //you can't buy it in nuke, because the elite hardsuit costs the same while being better // yogs: infiltration /datum/uplink_item/suits/hardsuit/elite name = "Elite Syndicate Hardsuit" @@ -1683,8 +1679,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) provides the user with superior armor and mobility compared to the standard Syndicate hardsuit." item = /obj/item/clothing/suit/space/hardsuit/syndi/elite cost = 8 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) - exclude_modes = list() + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) + exclude_antags = list() /datum/uplink_item/suits/hardsuit/shielded name = "Shielded Syndicate Hardsuit" @@ -1692,8 +1688,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) The shields can handle up to three impacts within a short duration and will rapidly recharge while not under fire." item = /obj/item/clothing/suit/space/hardsuit/shielded/syndi cost = 30 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) - exclude_modes = list() + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) + exclude_antags = list() // Devices and Tools /datum/uplink_item/device_tools @@ -1724,7 +1720,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/assault_pod cost = 30 surplus = 0 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) restricted = TRUE /datum/uplink_item/device_tools/binary @@ -1768,7 +1764,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A robust seven-slot set of webbing that is capable of holding all manner of tactical equipment. This one is capable of disguising itself." item = /obj/item/storage/belt/chameleon/syndicate cost = 1 - exclude_modes = list(/datum/game_mode/nuclear) + exclude_antags = list(ROLE_OPERATIVE) /datum/uplink_item/device_tools/emag name = "Cryptographic Sequencer" @@ -1815,7 +1811,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) manufacturer = /datum/corporation/traitor/waffleco surplus = 0 restricted = TRUE - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/infiltration) // Yogs: infiltration + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP, ROLE_INFILTRATOR) // Yogs: infiltration illegal_tech = FALSE /datum/uplink_item/device_tools/failsafe/spawn_item(spawn_path, mob/user, datum/component/uplink/U) @@ -1858,7 +1854,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/aiModule/hacked cost = 4 manufacturer = /datum/corporation/traitor/cybersun - exclude_modes = list(/datum/game_mode/infiltration) + exclude_antags = list(ROLE_INFILTRATOR) /datum/uplink_item/device_tools/hypnotic_flash name = "Hypnotic Flash" @@ -1872,7 +1868,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A design disk for an autolathe that permits it to print all types of 10mm and .357 ammunition." item = /obj/item/disk/design_disk/illegal_ammo cost = 4 - exclude_modes = list(/datum/game_mode/nuclear) //Buy your own ammo you lazy sods + exclude_antags = list(ROLE_OPERATIVE) //Buy your own ammo you lazy sods /datum/uplink_item/device_tools/illegal_ammo_disk/New() ..() @@ -1885,7 +1881,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) operatives in the fight, even while under fire. Don't cross the streams!" item = /obj/item/gun/medbeam cost = 8 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/device_tools/medgun_uber name = "Augmented Medbeam Gun" @@ -1894,7 +1890,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) This one comes uncharged, so be sure to give it a whirl before getting into combat. Goes well with a M-546 Osprey." item = /obj/item/gun/medbeam/uber cost = 25 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/device_tools/mdrive name = "Mirage Drive" @@ -1916,7 +1912,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 10 manufacturer = /datum/corporation/traitor/waffleco include_objectives = list(/datum/objective/hijack, /datum/objective/martyr, /datum/objective/nuclear) //yogs - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/device_tools/roburger_recipe name = "Roburger crafting recipe" @@ -1926,7 +1922,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/book/granter/crafting_recipe/roburgers cost = 14 include_objectives = list(/datum/objective/hijack, /datum/objective/martyr, /datum/objective/nuclear) //yogs: give sole_survivors the roburger - exclude_modes = list(/datum/game_mode/infiltration) + exclude_antags = list(ROLE_INFILTRATOR) /datum/uplink_item/device_tools/supermatter_delaminator name = "Antinoblium Shard" @@ -1938,7 +1934,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 10 manufacturer = /datum/corporation/traitor/waffleco include_objectives = list(/datum/objective/hijack, /datum/objective/martyr, /datum/objective/nuclear) //yogs - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/device_tools/powersink name = "Power Sink" @@ -1948,7 +1944,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/powersink cost = 8 manufacturer = /datum/corporation/traitor/waffleco - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/device_tools/rad_laser name = "Radioactive Microlaser" @@ -1975,7 +1971,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) dodging attacks, or suffering an EMP will reduce or remove the transperency temporarily." item = /obj/item/clothing/neck/cloak/ranger/syndie cost = 30 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/device_tools/medkit name = "Syndicate Combat Medic Kit" @@ -1984,14 +1980,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) and other supplies helpful for a field medic." item = /obj/item/storage/firstaid/tactical cost = 4 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/device_tools/hypospray_kit name = "Syndicate Combat Hypospray Kit" desc = "An advanced kit containing a combat hypospray and a wide variety of vials containing \"perfectly legal chemicals\" to treat combatants." item = /obj/item/storage/firstaid/hypospray/syndicate cost = 5 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/device_tools/soap name = "Syndicate Soap" @@ -2044,7 +2040,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) desc = "A potion recovered at great risk by undercover Syndicate operatives and then subsequently modified with Syndicate technology. \ Using it will make any animal sentient, and bound to serve you, as well as implanting an internal radio for communication and an internal ID card for opening doors." cost = 4 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) restricted = TRUE /datum/uplink_item/device_tools/potion/traitor @@ -2054,8 +2050,8 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Using it will make any animal sentient, and bound to serve you in your dastardly deeds." cost = 2 limited_stock = 2 //only buy two, prevents certain mushroom shenanigans - include_modes = list() //clear the list - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops, /datum/game_mode/infiltration) // yogs: infiltration + include_antags = list() //clear the list + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP, ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/device_tools/suspiciousphone name = "Protocol CRAB-17 Phone" @@ -2065,7 +2061,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 7 manufacturer = /datum/corporation/traitor/waffleco limited_stock = 1 - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/device_tools/syndie_bodybag name = "Syndicate Prisoner Transport Bag" @@ -2116,7 +2112,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) // TODO: When /datum/corporation/self is pickable for non-AI traitors, add it here. limited_stock = 1 // Might be too annoying if someone had mulitple. cost = 5 // Lacks the precision that a hacked law board (at 4 TCs) would give, but can be used on the go. - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) // Implants @@ -2138,7 +2134,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) manufacturer = /datum/corporation/traitor/vahlen cost = 5 // Nukies have no use for this and their autosurgeons are already multi-use - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/implants/adrenal name = "Adrenal Implant" @@ -2174,7 +2170,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) This will permanently destroy your body, however." item = /obj/item/storage/box/syndie_kit/imp_microbomb cost = 2 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/implants/macrobomb name = "Macrobomb Implant" @@ -2182,7 +2178,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) Upon death, releases a massive explosion that will wipe out everything nearby." item = /obj/item/storage/box/syndie_kit/imp_macrobomb cost = 20 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) restricted = TRUE /datum/uplink_item/implants/radio @@ -2201,7 +2197,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) manufacturer = /datum/corporation/traitor/vahlen cost = 8 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/implants/stealthimplant name = "Stealth Implant" @@ -2224,7 +2220,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/autosurgeon/thermal_eyes cost = 8 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/implants/uplink name = "Uplink Implant" @@ -2253,7 +2249,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/autosurgeon/suspicious/xray_eyes cost = 10 surplus = 0 - include_modes = list(/datum/game_mode/nuclear) + include_antags = list(ROLE_OPERATIVE) /datum/uplink_item/implants/mantis name = "G.O.R.L.E.X. Mantis Blade" @@ -2261,7 +2257,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/autosurgeon/suspicious/syndie_mantis cost = 6 surplus = 0 - exclude_modes = list(/datum/game_mode/infiltration) // yogs: infiltration + exclude_antags = list(ROLE_INFILTRATOR) // yogs: infiltration /datum/uplink_item/implants/stechkin_implant name = "Stechkin arm implant" @@ -2288,12 +2284,12 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) item = /obj/item/autosurgeon/suspicious/spinalspeed manufacturer = /datum/corporation/traitor/vahlen cost = 12 - exclude_modes = list(/datum/game_mode/infiltration, /datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_INFILTRATOR, ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/implants/spinal/nukie cost = 20 - exclude_modes = list() - include_modes = list(/datum/game_mode/infiltration, /datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list() + include_antags = list(ROLE_INFILTRATOR, ROLE_OPERATIVE, ROLE_CLOWNOP) /datum/uplink_item/implants/emp_shield name = "EMP Shield Implant" @@ -2306,7 +2302,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) // Events /datum/uplink_item/services category = "Services" - include_modes = list(/datum/game_mode/infiltration, /datum/game_mode/nuclear) + include_antags = list(ROLE_INFILTRATOR, ROLE_OPERATIVE) surplus = 0 restricted = TRUE @@ -2338,7 +2334,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) //Infiltrator shit /datum/uplink_item/infiltration category = UPLINK_CATEGORY_INFILTRATION - include_modes = list(/datum/game_mode/infiltration) + include_antags = list(ROLE_INFILTRATOR) surplus = 0 /datum/uplink_item/infiltration/extra_stealthsuit @@ -2424,7 +2420,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) // Role-specific items /datum/uplink_item/role_restricted category = UPLINK_CATEGORY_ROLE - exclude_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + exclude_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) surplus = 0 /datum/uplink_item/role_restricted/ancient_jumpsuit @@ -2786,7 +2782,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) /datum/uplink_item/badass/costumes surplus = 0 - include_modes = list(/datum/game_mode/nuclear, /datum/game_mode/nuclear/clown_ops) + include_antags = list(ROLE_OPERATIVE, ROLE_CLOWNOP) cost = 4 cant_discount = TRUE diff --git a/code/modules/zombie/items.dm b/code/modules/zombie/items.dm index c87d036ccffb..e1d468f3ea3b 100644 --- a/code/modules/zombie/items.dm +++ b/code/modules/zombie/items.dm @@ -117,22 +117,3 @@ user.updatehealth() user.adjustOrganLoss(ORGAN_SLOT_BRAIN, -hp_gained) // Zom Bee gibbers "BRAAAAISNSs!1!" user.set_nutrition(min(user.nutrition + hp_gained, NUTRITION_LEVEL_FULL)) - -/obj/item/zombie_hand/gamemode - inserted_organ = /obj/item/organ/zombie_infection/gamemode - infect_chance = 70 - scaled_infect_chance = TRUE - force = 15 - var/door_open_modifier = 1 - -/obj/item/zombie_hand/gamemode/runner - force = 10 - infect_chance = 35 - door_open_modifier = 1.1 - -/obj/item/zombie_hand/gamemode/tank - door_open_modifier = 0.8 - -/obj/item/zombie_hand/gamemode/necro - force = 7 - infect_chance = 30 diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm index 0b95860e5d89..4661d825308c 100644 --- a/code/modules/zombie/organs.dm +++ b/code/modules/zombie/organs.dm @@ -104,47 +104,3 @@ /obj/item/organ/zombie_infection/nodamage causes_damage = FALSE - -/obj/item/organ/zombie_infection/gamemode - damage_caused = 3 - -/obj/item/organ/zombie_infection/gamemode/zombify() - timer_id = null - owner.grab_ghost() - - if(!converts_living && owner.stat != DEAD) - return - - if(!iszombie(owner)) - old_species = owner.dna.species.type - owner.set_species(/datum/species/zombie/infectious/gamemode) - to_chat(owner, span_alertalien("You are now a zombie! Help your fellow allies take over the station!")) - else - to_chat(owner, span_alertalien("You rise again!")) - - var/stand_up = (owner.stat == DEAD) || (owner.stat == UNCONSCIOUS) - - //Fully heal the zombie's damage the first time they rise - owner.setToxLoss(0, 0) - owner.setOxyLoss(0, 0) - owner.heal_overall_damage(INFINITY, INFINITY, INFINITY, null, TRUE) - owner.revive() - owner.visible_message(span_danger("[owner] suddenly convulses, as [owner.p_they()][stand_up ? " stagger to [owner.p_their()] feet and" : ""] gain a ravenous hunger in [owner.p_their()] eyes!"), span_alien("You HUNGER!")) - playsound(owner.loc, 'sound/hallucinations/far_noise.ogg', 50, 1) - owner.do_jitter_animation(living_transformation_time) - owner.Stun(living_transformation_time) - - - if(!isinfected(owner)) //Makes them the *actual* antag, instead of just a zombie. - var/datum/game_mode/zombie/GM = SSticker.mode - if(!istype(GM)) - return - GM.add_zombie(owner.mind) - -// var/datum/antagonist/zombie/Z = locate() in owner.mind.antag_datums -// if(!Z.evolution.owner) -// Z.evolution.Grant(owner) - - if(owner.handcuffed) - var/obj/O = owner.get_item_by_slot(ITEM_SLOT_HANDCUFFED) - qdel(O) diff --git a/config/game_options.txt b/config/game_options.txt index 696916e974c3..cf50bbbd41c1 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -121,7 +121,6 @@ PROBABILITY VAMPIRE 0 # Group antags PROBABILITY DARKSPAWN 9 PROBABILITY REVOLUTION 8 -PROBABILITY GANG 0 PROBABILITY CULT 5 PROBABILITY CLOCKWORK_CULT 5 @@ -133,7 +132,6 @@ PROBABILITY CLOWNOPS 0 #Broke # Special PROBABILITY RAGINMAGES 1 -PROBABILITY MONKEY 0 PROBABILITY METEOR 0 PROBABILITY EXTENDED 0 PROBABILITY SECRET_EXTENDED 0 @@ -159,7 +157,6 @@ CONTINUOUS CULT CONTINUOUS CLOCKWORK_CULT CONTINUOUS CHANGELING CONTINUOUS WIZARD -CONTINUOUS HIVEMIND #CONTINUOUS MALF CONTINUOUS VAMPIRE CONTINUOUS DYNAMIC @@ -309,10 +306,6 @@ STAYED_ALIVE_BONUS 1.5 ## Uncomment to allow players to see the set odds of different rounds in secret/random in the get server revision screen. This will NOT tell the current roundtype. SHOW_GAME_TYPE_ODDS -## RANDOM EVENTS ### -## Comment this out to disable random events during the round. -ALLOW_RANDOM_EVENTS - ## Multiplier for earliest start time of dangerous events. ## Set to 0 to make dangerous events avaliable from round start. EVENTS_MIN_TIME_MUL 1 diff --git a/icons/obj/meteor.dmi b/icons/obj/meteor.dmi index 84faabcc1881..9fde3f3ef68e 100644 Binary files a/icons/obj/meteor.dmi and b/icons/obj/meteor.dmi differ diff --git a/tgui/packages/common/collections.ts b/tgui/packages/common/collections.ts index f0419253e242..bf407b112343 100644 --- a/tgui/packages/common/collections.ts +++ b/tgui/packages/common/collections.ts @@ -311,6 +311,30 @@ export const binaryInsertWith =
")
- to_chat(world, "
")
+// to_chat(world, "") +// return report.Join("
") /datum/outfit/battleroyale name = "Default Skin" @@ -409,8 +405,8 @@ GLOBAL_VAR(final_zone) if(!culprit) return - if(culprit.mind?.has_antag_datum(/datum/antagonist/battleroyale)) - last_hit = culprit.mind.has_antag_datum(/datum/antagonist/battleroyale) + if(is_battleroyale(culprit)) + last_hit = is_battleroyale(culprit) /obj/structure/battle_bus diff --git a/yogstation/code/game/gamemodes/darkspawn/darkspawn.dm b/yogstation/code/game/gamemodes/darkspawn/darkspawn.dm deleted file mode 100644 index 0262c8abc073..000000000000 --- a/yogstation/code/game/gamemodes/darkspawn/darkspawn.dm +++ /dev/null @@ -1,123 +0,0 @@ -GLOBAL_VAR_INIT(sacrament_done, FALSE) - -/datum/game_mode/darkspawn - name = "darkspawn" - config_tag = "darkspawn" - antag_flag = ROLE_DARKSPAWN - required_players = 25 - required_enemies = 2 - recommended_enemies = 1 - enemy_minimum_age = 24 //reasonably complicated antag - restricted_jobs = list("AI", "Cyborg", "Synthetic") - protected_jobs = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Research Director", "Chief Engineer", "Chief Medical Officer", "Brig Physician") //Added Brig Physician - title_icon = "darkspawn" - round_ends_with_antag_death = TRUE - var/list/datum/mind/darkspawns = list() - var/datum/team/darkspawn/team - - announce_span = "danger" - announce_text = "There are Darkspawn on the station!\n\ - Darkspawn: Consume enough lucidity to complete the Sacrament and ascend once again.\n\ - Crew: Kill the darkspawn before they can complete the Sacrament." - -//////////////////////////////////////////////////////////////////////////////////// -//-------------------------------Gamemode Setup-----------------------------------// -//////////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/darkspawn/pre_setup() - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - restricted_jobs += protected_jobs - if(CONFIG_GET(flag/protect_assistant_from_antagonist)) - restricted_jobs += "Assistant" - - //scaling number of darkspawns, at least 2, at most 4 (they get exponentially stronger per person) - // 25 players = 2 darkspawns - // 40 players = 3 darkspawns - // 55 players = 4 darkspawns - var/num_darkspawn = clamp(round((num_players()+5)/15), required_enemies, 4) - for (var/i = 1 to num_darkspawn) - if(LAZYLEN(antag_candidates) <= 0) - break - var/datum/mind/darkboi = antag_pick(antag_candidates) - antag_candidates -= darkboi - darkspawns += darkboi - darkboi.special_role = ROLE_DARKSPAWN - darkboi.restricted_roles = restricted_jobs - log_game("[key_name(darkboi)] (ckey) has been selected as a darkspawn.") - - team = new - team.update_objectives() - GLOB.thrallnet.name = "Thrall net" - - if(!team || !LAZYLEN(darkspawns)) - setup_error = "Error setting up darkspawns" - return FALSE - return TRUE - -/datum/game_mode/darkspawn/post_setup() - for(var/datum/mind/darkboi as anything in darkspawns) - darkboi.add_antag_datum(/datum/antagonist/darkspawn) - . = ..() - -//////////////////////////////////////////////////////////////////////////////////// -//----------------------------Non-Secret mode stuff-------------------------------// -//////////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/darkspawn/generate_report() - return "Sightings of strange alien creatures have been observed in your area. These aliens appear to be searching for specific patterns of brain activity, with their method for doing so causing victims to lapse into a short coma. \ - Be wary of dark areas and ensure all lights are kept well-maintained. Investigate all reports of odd or suspicious sightings in maintenance, and be on the lookout for anyone sympathizing with these aliens, as they may be compromised" - -//////////////////////////////////////////////////////////////////////////////////// -//--------------------------------Game end checks---------------------------------// -//////////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/darkspawn/are_special_antags_dead() - if(team) - return team.check_darkspawn_death() - return ..() - -//////////////////////////////////////////////////////////////////////////////////// -//----------------------------After game end stuff--------------------------------// -//////////////////////////////////////////////////////////////////////////////////// -/datum/game_mode/darkspawn/set_round_result() - ..() - if(GLOB.sacrament_done) - SSticker.mode_result = "win - the darkspawn have completed the sacrament" - else - SSticker.mode_result = "loss - staff stopped the darkspawn" - -/datum/game_mode/darkspawn/generate_credit_text() - var/list/round_credits = list() - var/len_before_addition - - round_credits += "
The Darkspawn:
" - len_before_addition = round_credits.len - if(team) - for(var/datum/mind/current in team.members) - round_credits += "[current.name] as the [current.assigned_role]
" - if(len_before_addition == round_credits.len) - round_credits += list("The Darkspawn have moved to the shadows!
", "We couldn't locate them!
") - round_credits += "" - - round_credits += ..() - return round_credits - -//////////////////////////////////////////////////////////////////////////////////// -//-------------------------------Mob assign procs---------------------------------// -//////////////////////////////////////////////////////////////////////////////////// -/mob/living/proc/add_darkspawn() - if(!istype(mind)) - return FALSE - return mind.add_antag_datum(/datum/antagonist/darkspawn) - -/mob/living/proc/remove_darkspawn() - if(!istype(mind)) - return FALSE - return mind.remove_antag_datum(/datum/antagonist/darkspawn) - -/mob/living/proc/add_thrall() - if(!istype(mind)) - return FALSE - return mind.add_antag_datum(/datum/antagonist/thrall) - -/mob/living/proc/remove_thrall() - if(!istype(mind)) - return FALSE - return mind.remove_antag_datum(/datum/antagonist/thrall) diff --git a/yogstation/code/game/gamemodes/game_mode.dm b/yogstation/code/game/gamemodes/game_mode.dm index 5d424c18559a..1ddec125bafe 100644 --- a/yogstation/code/game/gamemodes/game_mode.dm +++ b/yogstation/code/game/gamemodes/game_mode.dm @@ -1,3 +1,37 @@ +//this is stuff used by antags that's stored on the base gamemode datum +//rework those antags to not need these /datum/game_mode var/has_hijackers = FALSE + //traitor + var/traitor_name = "traitor" + var/list/datum/mind/traitors = list() + + var/datum/mind/exchange_red + var/datum/mind/exchange_blue + + //blood brothers + var/list/datum/mind/brothers = list() + var/list/datum/team/brother_team/brother_teams = list() + + //wizards + var/list/datum/mind/wizards = list() + var/list/datum/mind/apprentices = list() + + // blood cult + var/list/datum/mind/cult = list() + var/list/bloodstone_list = list() + var/anchor_bloodstone + var/anchor_time2kill = 5 MINUTES + var/bloodstone_cooldown = FALSE + + //clock cult + var/list/servants_of_ratvar = list() //The Enlightened servants of Ratvar + var/clockwork_explanation = "Defend the Ark of the Clockwork Justiciar and free Ratvar." //The description of the current objective + + // iaa + var/list/target_list = list() + var/list/late_joining_list = list() + + //vampire + var/list/datum/mind/vampires = list() diff --git a/yogstation/code/game/gamemodes/gangs/dominator.dm b/yogstation/code/game/gamemodes/gangs/dominator.dm deleted file mode 100644 index a547e09c032f..000000000000 --- a/yogstation/code/game/gamemodes/gangs/dominator.dm +++ /dev/null @@ -1,281 +0,0 @@ -#define DOM_BLOCKED_SPAM_CAP 6 -#define DOM_REQUIRED_TURFS 23 -#define DOM_HULK_HITS_REQUIRED 10 - -/obj/machinery/dominator - name = "dominator" - desc = "A visibly sinister device. Looks like you can break it if you hit it enough." - icon = 'icons/obj/machines/dominator.dmi' - icon_state = "dominator" - density = TRUE - anchored = TRUE - layer = HIGH_OBJ_LAYER - max_integrity = 300 - integrity_failure = 100 - move_resist = INFINITY - armor = list(MELEE = 20, BULLET = 50, LASER = 50, ENERGY = 50, BOMB = 10, BIO = 100, RAD = 100, FIRE = 10, ACID = 70) - var/datum/team/gang/gang - var/operating = FALSE //false=standby or broken, true=takeover - var/warned = FALSE //if this device has set off the warning at <3 minutes yet - var/spam_prevention = DOM_BLOCKED_SPAM_CAP //first message is immediate - var/datum/effect_system/spark_spread/spark_system - var/obj/effect/countdown/dominator/countdown - var/obj/item/disk/nuclear/nukedisk - -/obj/machinery/dominator/Initialize(mapload) - set_light(2) - GLOB.poi_list |= src - spark_system = new - spark_system.set_up(5, TRUE, src) - countdown = new(src) - update_appearance(UPDATE_ICON) - .=..() - -/obj/machinery/dominator/Destroy() - if(!(stat & BROKEN)) - set_broken() - GLOB.poi_list.Remove(src) - gang = null - QDEL_NULL(spark_system) - QDEL_NULL(countdown) - STOP_PROCESSING(SSmachines, src) - if(nukedisk) - nukedisk.forceMove(drop_location()) - nukedisk = null - return ..() - -/obj/machinery/dominator/emp_act(severity) - gang.domination_time += 3 * severity - ..() - -/obj/machinery/dominator/hulk_damage() - return (max_integrity - integrity_failure) / DOM_HULK_HITS_REQUIRED - -/obj/machinery/dominator/tesla_act(power, tesla_flags, shocked_targets, zap_gib = FALSE) - qdel(src) - -/obj/machinery/dominator/update_overlays() - . = ..() - if(stat & BROKEN) - return - if(operating) - var/mutable_appearance/dominator_overlay = mutable_appearance('icons/obj/machines/dominator.dmi', "dominator-overlay") - if(gang) - dominator_overlay.color = gang.color - . += dominator_overlay - if(atom_integrity/max_integrity < 0.66) - . += "damage" - -/obj/machinery/dominator/update_icon_state() - . = ..() - if(stat & BROKEN) - icon_state = "dominator-broken" - return - if(!operating) - icon_state = "dominator" - return - icon_state = "dominator-active" - -/obj/machinery/dominator/examine(mob/user) - . = ..() - if(stat & BROKEN) - return - - if(gang && gang.domination_time != NOT_DOMINATING) - if(gang.domination_time > world.time) - . += span_notice("Hostile Takeover in progress. Estimated [gang.domination_time_remaining()] seconds remain.") - else - . += span_notice("Hostile Takeover of [station_name()] successful. Have a great day.") - else - . += span_notice("System on standby.") - if(nukedisk) - . += span_notice("[nukedisk] seems to be stuck inside.") - . += span_danger("System Integrity: [round((atom_integrity/max_integrity)*100,1)]%") - -/obj/machinery/dominator/process() - ..() - if(gang && gang.domination_time != NOT_DOMINATING) - var/time_remaining = gang.domination_time_remaining() - if(time_remaining > 0) - if(excessive_walls_check()) - gang.domination_time += 20 - playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) - if(spam_prevention < DOM_BLOCKED_SPAM_CAP) - spam_prevention++ - else - gang.message_gangtools("Warning: There are too many walls around your gang's dominator, its signal is being blocked!") - say("Error: Takeover signal is currently blocked! There are too many walls within 2 standard units of this device.") - spam_prevention = 0 - return - . = TRUE - playsound(loc, 'sound/items/timer.ogg', 10, 0) - if(!warned && (time_remaining < 180)) - warned = TRUE - var/area/domloc = get_area(loc) - gang.message_gangtools("Less than 3 minutes remains in hostile takeover. Defend your dominator at [domloc.map_name]!") - for(var/G in GLOB.gangs) - var/datum/team/gang/tempgang = G - if(tempgang != gang) - tempgang.message_gangtools("WARNING: [gang.name] Gang takeover imminent. Their dominator at [domloc.map_name] must be destroyed!",1,1) - else - Cinematic(CINEMATIC_MALF,world) - gang.winner = TRUE - SSticker.force_ending = TRUE - - if(!.) - STOP_PROCESSING(SSmachines, src) - -/obj/machinery/dominator/play_attack_sound(damage_amount, damage_type = BRUTE, damage_flag = 0) - switch(damage_type) - if(BRUTE) - if(damage_amount) - playsound(src, 'sound/effects/bang.ogg', 50, 1) - else - playsound(loc, 'sound/weapons/tap.ogg', 50, 1) - if(BURN) - playsound(src.loc, 'sound/items/welder.ogg', 100, 1) - -/obj/machinery/dominator/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = TRUE, attack_dir, armour_penetration = 0) - . = ..() - if(.) - if(atom_integrity/max_integrity > 0.66) - if(prob(damage_amount*2)) - spark_system.start() - else if(!(stat & BROKEN)) - spark_system.start() - update_appearance(UPDATE_ICON) - - -/obj/machinery/dominator/atom_break(damage_flag) - . = ..() - if(!(stat & BROKEN) && !(flags_1 & NODECONSTRUCT_1)) - set_broken() - -/obj/machinery/dominator/deconstruct(disassembled = TRUE) - if(!(flags_1 & NODECONSTRUCT_1)) - if(!(stat & BROKEN)) - set_broken() - new /obj/item/stack/sheet/plasteel(src.loc) - qdel(src) - -/obj/machinery/dominator/attacked_by(obj/item/I, mob/living/user) - add_fingerprint(user) - ..() - -/obj/machinery/dominator/attack_hand(mob/user) - if(operating || (stat & BROKEN)) - examine(user) - return - - var/datum/team/gang/tempgang - - var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang) - if(GA) - tempgang = GA.gang - if(!tempgang) - examine(user) - return - - if(tempgang.domination_time != NOT_DOMINATING) - to_chat(user, span_warning("Error: Hostile Takeover is already in progress.")) - return - - if(!tempgang.dom_attempts) - to_chat(user, span_warning("Error: Unable to breach station network. Firewall has logged our signature and is blocking all further attempts.")) - return - - if(!nukedisk) - to_chat(user, span_warning("Error: Nuclear Authentication Disk required to breach station network.")) - return - - var/time = round(tempgang.determine_domination_time()/60,0.1) - if(alert(user,"A takeover will require [time] minutes.\nYour gang will be unable to gain influence while it is active.\nThe entire station will likely be alerted to it once it starts.\nYou have [tempgang.dom_attempts] attempt(s) remaining. Are you ready?","Confirm","Ready","Later") == "Ready") - if((tempgang.domination_time != NOT_DOMINATING) || !tempgang.dom_attempts || !in_range(src, user) || !isturf(loc)) - return 0 - - var/area/A = get_area(loc) - var/locname = A.map_name - - gang = tempgang - gang.dom_attempts -- - priority_announce("Network breach detected in [locname]. The [gang.name] Gang is attempting to seize control of the station!","Network Alert") - gang.domination() - SSshuttle.registerHostileEnvironment(src) - name = "[gang.name] Gang [name]" - operating = TRUE - update_appearance(UPDATE_ICON) - - countdown.color = gang.color - countdown.start() - - set_light(3) - START_PROCESSING(SSmachines, src) - - gang.message_gangtools("Hostile takeover in progress: Estimated [time] minutes until victory.[gang.dom_attempts ? "" : " This is your final attempt."]") - for(var/G in GLOB.gangs) - var/datum/team/gang/vagos = G - if(vagos != gang) - vagos.message_gangtools("Enemy takeover attempt detected in [locname]: Estimated [time] minutes until our defeat.",1,1) - -/obj/machinery/dominator/proc/excessive_walls_check() // why the fuck was this even a global proc... - var/open = FALSE - for(var/turf/T in view(2, src)) - if(!isclosedturf(T)) - open++ - if(open < DOM_REQUIRED_TURFS) - return TRUE - else - return FALSE - -/obj/machinery/dominator/proc/set_broken() - if(gang) - gang.domination_time = NOT_DOMINATING - - var/takeover_in_progress = FALSE - for(var/G in GLOB.gangs) - var/datum/team/gang/ballas = G - if(ballas.domination_time != NOT_DOMINATING) - takeover_in_progress = TRUE - break - if(!takeover_in_progress) - var/was_stranded = SSshuttle.emergency.mode == SHUTTLE_STRANDED - SSshuttle.clearHostileEnvironment(src) - if(!was_stranded) - priority_announce("All hostile activity within station systems has ceased.","Network Alert") - - if(SSsecurity_level.get_current_level_as_text() == SEC_LEVEL_DELTA) - SSsecurity_level.set_level(SEC_LEVEL_RED) - - gang.message_gangtools("Hostile takeover cancelled: Dominator is no longer operational.[gang.dom_attempts ? " You have [gang.dom_attempts] attempt remaining." : " The station network will have likely blocked any more attempts by us."]",1,1) - - set_light(0) - operating = FALSE - stat |= BROKEN - update_appearance(UPDATE_ICON) - STOP_PROCESSING(SSmachines, src) - if(nukedisk) - nukedisk.forceMove(drop_location()) - nukedisk = null - -/obj/machinery/dominator/attackby(obj/item/I, mob/user, params) - if (istype(I, /obj/item/disk/nuclear)) - if(!disk_check(I)) - return - if(!user.transferItemToLoc(I, src)) - return - nukedisk = I - add_fingerprint(user) - return - - .=..() - -/obj/machinery/dominator/proc/disk_check(obj/item/disk/nuclear/D) - if(D.fake) - say("Authentication failure; disk not recognised.") - return FALSE - else - return TRUE - -#undef DOM_BLOCKED_SPAM_CAP -#undef DOM_REQUIRED_TURFS -#undef DOM_HULK_HITS_REQUIRED diff --git a/yogstation/code/game/gamemodes/gangs/gang_items.dm b/yogstation/code/game/gamemodes/gangs/gang_items.dm deleted file mode 100644 index 9027cf043650..000000000000 --- a/yogstation/code/game/gamemodes/gangs/gang_items.dm +++ /dev/null @@ -1,395 +0,0 @@ -/datum/gang_item - var/name - var/item_path - var/cost - var/weapon_cost - var/spawn_msg - var/category - var/list/gang_whitelist = list() - var/list/gang_blacklist = list() - var/id - -/datum/gang_item/proc/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool, check_canbuy = TRUE) - if(check_canbuy && !can_buy(user, gang, gangtool)) - return FALSE - var/real_cost = get_cost(user, gang, gangtool) - if(!spawn_item(user, gang, gangtool)) - gang.adjust_influence(-real_cost) - to_chat(user, span_notice("You bought \the [name].")) - return TRUE - -/datum/gang_item/proc/weapon_purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool, check_canbuy_weapon = TRUE) - if(check_canbuy_weapon && !can_buy_weapon(user, gang, gangtool)) - return FALSE - var/actual_cost = get_weapon_cost(user, gang, gangtool) - if(!spawn_item(user, gang, gangtool)) - gang.adjust_uniform_influence(-actual_cost) - to_chat(user, span_notice("You bought \the [name].")) - return TRUE - -/datum/gang_item/proc/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) // If this returns anything other than null, something fucked up and influence won't lower. - if(item_path) - var/obj/item/O = new item_path(user.loc) - user.put_in_hands(O) - else - return TRUE - if(spawn_msg) - to_chat(user, spawn_msg) - -/datum/gang_item/proc/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return gang && (gang.influence >= get_cost(user, gang, gangtool)) && can_see(user, gang, gangtool) - -/datum/gang_item/proc/can_buy_weapon(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return gang && (gang.uniform_influence >= get_weapon_cost(user, gang, gangtool)) && can_see(user, gang, gangtool) - -/datum/gang_item/proc/can_see(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return TRUE - -/datum/gang_item/proc/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return cost - -/datum/gang_item/proc/get_weapon_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return weapon_cost - -/datum/gang_item/proc/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return "([get_cost(user, gang, gangtool)] Influence)" - -/datum/gang_item/proc/get_weapon_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return "([get_weapon_cost(user, gang, gangtool)] Supply)" - -/datum/gang_item/proc/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return name - -/datum/gang_item/proc/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - return - -/////////////////// -//CLOTHING -/////////////////// - -/datum/gang_item/clothing - category = "Purchase Gang Clothes (Only the jumpsuit and suit give you added influence):" - -/datum/gang_item/clothing/under - name = "Gang Uniform" - id = "under" - cost = 1 - -/datum/gang_item/clothing/under/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(gang.inner_outfits.len) - var/outfit = pick(gang.inner_outfits) - if(outfit) - var/obj/item/O = new outfit(user.loc) - user.put_in_hands(O) - to_chat(user, " This is your gang's official uniform, wearing it will increase your influence") - return - return TRUE - -/datum/gang_item/clothing/suit - name = "Gang Armored Outerwear" - id = "suit" - cost = 1 - -/datum/gang_item/clothing/suit/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(gang.outer_outfits.len) - var/outfit = pick(gang.outer_outfits) - if(outfit) - var/obj/item/O = new outfit(user.loc) - O.armor = O.armor.setRating(melee = 20, bullet = 35, laser = 10, energy = 10, bomb = 30, bio = 0, rad = 0, fire = 30, acid = 30) - O.desc += " Tailored for the [gang.name] Gang to offer the wearer moderate protection against ballistics and physical trauma." - user.put_in_hands(O) - to_chat(user, " This is your gang's official outerwear, wearing it will increase your influence") - return - return TRUE - - -/datum/gang_item/clothing/hat - name = "Pimp Hat" - id = "hat" - cost = 16 - item_path = /obj/item/clothing/head/collectable/petehat/gang - - -/obj/item/clothing/head/collectable/petehat/gang - name = "pimpin' hat" - desc = "The undisputed king of style." - -/datum/gang_item/clothing/mask - name = "Golden Death Mask" - id = "mask" - cost = 18 - item_path = /obj/item/clothing/mask/gskull - -/obj/item/clothing/mask/gskull - name = "golden death mask" - icon_state = "gskull" - desc = "Strike terror, and envy, into the hearts of your enemies." - -/datum/gang_item/clothing/shoes - name = "Bling Boots" - id = "boots" - cost = 22 - item_path = /obj/item/clothing/shoes/gang - -/obj/item/clothing/shoes/gang - name = "blinged-out boots" - desc = "Stand aside peasants." - icon_state = "bling" - -/datum/gang_item/clothing/neck - name = "Gold Necklace" - id = "necklace" - cost = 9 - item_path = /obj/item/clothing/neck/necklace/dope - -/datum/gang_item/clothing/hands - name = "Decorative Brass Knuckles" - id = "hand" - cost = 11 - item_path = /obj/item/clothing/gloves/gang - -/obj/item/clothing/gloves/gang - name = "braggadocio's brass knuckles" - desc = "Purely decorative, don't find out the hard way." - icon_state = "knuckles" - w_class = 3 - -/datum/gang_item/clothing/belt - name = "Badass Belt" - id = "belt" - cost = 13 - item_path = /obj/item/storage/belt/military/gang - -/obj/item/storage/belt/military/gang - name = "badass belt" - icon_state = "gangbelt" - item_state = "gang" - desc = "The belt buckle simply reads 'BAMF'." - -/////////////////// -//WEAPONS -/////////////////// - -/datum/gang_item/weapon - category = "Purchase Weapons:" - -/datum/gang_item/weapon/ammo - -/datum/gang_item/weapon/shuriken - name = "Shuriken" - id = "shuriken" - weapon_cost = 3 - item_path = /obj/item/throwing_star - -/datum/gang_item/weapon/switchblade - name = "Switchblade" - id = "switchblade" - weapon_cost = 5 - item_path = /obj/item/switchblade - -/datum/gang_item/weapon/surplus - name = "Surplus Carbine" - id = "surplus" - weapon_cost = 8 - item_path = /obj/item/gun/ballistic/automatic/surplus - -/datum/gang_item/weapon/ammo/surplus_ammo - name = "Surplus Carbine Ammo" - id = "surplus_ammo" - weapon_cost = 5 - item_path = /obj/item/ammo_box/magazine/m10mm/rifle - -/datum/gang_item/weapon/improvised - name = "Sawn-Off Improvised Shotgun" - id = "sawn" - weapon_cost = 6 - item_path = /obj/item/gun/ballistic/shotgun/doublebarrel/improvised/sawn - -/datum/gang_item/weapon/ammo/improvised_ammo - name = "Box of Buckshot" - id = "buckshot" - weapon_cost = 5 - item_path = /obj/item/storage/box/lethalshot - -/datum/gang_item/weapon/pistol - name = "10mm Pistol" - id = "pistol" - weapon_cost = 30 - item_path = /obj/item/gun/ballistic/automatic/pistol - -/datum/gang_item/weapon/ammo/pistol_ammo - name = "10mm Ammo" - id = "pistol_ammo" - weapon_cost = 10 - item_path = /obj/item/ammo_box/magazine/m10mm - -/datum/gang_item/weapon/uzi - name = "Uzi SMG" - id = "uzi" - weapon_cost = 60 - item_path = /obj/item/gun/ballistic/automatic/mini_uzi - -/datum/gang_item/weapon/ammo/uzi_ammo - name = "Uzi Ammo" - id = "uzi_ammo" - weapon_cost = 40 - item_path = /obj/item/ammo_box/magazine/uzim9mm - -/////////////////// -//EQUIPMENT -/////////////////// - -/datum/gang_item/equipment - category = "Purchase Equipment:" - - -/datum/gang_item/equipment/spraycan - name = "Territory Spraycan" - id = "spraycan" - cost = 5 - item_path = /obj/item/toy/crayon/spraycan/gang - -/datum/gang_item/equipment/spraycan/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - var/obj/item/O = new item_path(user.loc, gang) - user.put_in_hands(O) - -/datum/gang_item/equipment/pinpointer - name = "Pinpointer" - id = "pinpointer" - cost = 2 - item_path = /obj/item/pinpointer/nuke - -/datum/gang_item/equipment/sharpener - name = "Sharpener" - id = "whetstone" - cost = 3 - item_path = /obj/item/sharpener - - -/datum/gang_item/equipment/emp - name = "EMP Grenade" - id = "EMP" - cost = 5 - item_path = /obj/item/grenade/empgrenade - -/datum/gang_item/equipment/c4 - name = "C4 Explosive" - id = "c4" - cost = 7 - item_path = /obj/item/grenade/plastic/c4 - -/datum/gang_item/equipment/frag - name = "Fragmentation Grenade" - id = "frag nade" - cost = 18 - item_path = /obj/item/grenade/syndieminibomb/concussion/frag - -/datum/gang_item/equipment/force_converter - name = "Force Converter" - id = "force_converter" - cost = 20 - item_path = /obj/item/implanter/gang - spawn_msg = span_notice("The force converter is a single-use device that converts enemy gangsters.") - -/datum/gang_item/equipment/force_converter/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - var/obj/item/O = new item_path(user.loc, gang) - user.put_in_hands(O) - - -/datum/gang_item/equipment/wetwork_boots - name = "Wetwork boots" - id = "wetwork" - cost = 20 - item_path = /obj/item/clothing/shoes/combat/gang - -/obj/item/clothing/shoes/combat/gang - name = "Wetwork boots" - desc = "A gang's best hitmen are prepared for anything." - armor = list(MELEE = 25, BULLET = 25, LASER = 25, ENERGY = 25, BOMB = 50, BIO = 100, RAD = 0, FIRE = 70, ACID = 50) - clothing_flags = NOSLIP - -/datum/gang_item/equipment/pen - name = "Recruitment Pen" - id = "pen" - cost = 25 - item_path = /obj/item/pen/gang - spawn_msg = span_notice("More recruitment pens will allow you to recruit gangsters faster. Only gang leaders can recruit with pens.") - -/datum/gang_item/equipment/pen/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(..()) - gangtool.free_pen = FALSE - return TRUE - return FALSE - -/datum/gang_item/equipment/pen/get_cost(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(gangtool && gangtool.free_pen) - return 0 - return ..() - -/datum/gang_item/equipment/pen/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(gangtool && gangtool.free_pen) - return "(GET ONE FREE)" - return ..() - - -/datum/gang_item/equipment/gangtool - id = "gangtool" - cost = 10 - -/datum/gang_item/equipment/gangtool/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - var/item_type - if(gang) - item_type = /obj/item/gangtool/spare/lt - if(gang.leaders.len < MAX_LEADERS_GANG) - to_chat(user, span_notice("Gangtools allow you to promote a gangster to be your Lieutenant, enabling them to recruit and purchase items like you. Simply have them register the gangtool. You may promote up to [MAX_LEADERS_GANG-gang.leaders.len] more Lieutenants")) - else - item_type = /obj/item/gangtool/spare - var/obj/item/gangtool/spare/tool = new item_type(user.loc) - user.put_in_hands(tool) - -/datum/gang_item/equipment/gangtool/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(gang && (gang.leaders.len < gang.max_leaders)) - return "Promote a Gangster" - return "Spare Gangtool" - -/datum/gang_item/equipment/dominator - name = "Station Dominator" - id = "dominator" - cost = 30 - item_path = /obj/machinery/dominator - spawn_msg = span_notice("The dominator will secure your gang's dominance over the station. Turn it on when you are ready to defend it.") - -/datum/gang_item/equipment/dominator/can_buy(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(!gang || !gang.dom_attempts) - return FALSE - return ..() - -/datum/gang_item/equipment/dominator/get_name_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(!gang || !gang.dom_attempts) - return ..() - return "[..()]" - -/datum/gang_item/equipment/dominator/get_cost_display(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(!gang || !gang.dom_attempts) - return "(Out of stock)" - return ..() - -/datum/gang_item/equipment/dominator/get_extra_info(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - if(gang) - return "This device requires a 5x5 area clear of walls to work. (Estimated Takeover Time: [round(gang.determine_domination_time()/60,0.1)] minutes)" - -/datum/gang_item/equipment/dominator/purchase(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - var/area/userarea = get_area(user) - if(!(userarea.type in gang.territories|gang.new_territories)) - to_chat(user,span_warning("The dominator can only be bought in territory controlled by your gang!")) - return FALSE - for(var/obj/obj in get_turf(user)) - if(obj.density) - to_chat(user, span_warning("There's not enough room here!")) - return FALSE - - return ..() - -/datum/gang_item/equipment/dominator/spawn_item(mob/living/carbon/user, datum/team/gang/gang, obj/item/gangtool/gangtool) - new item_path(user.loc) - to_chat(user, spawn_msg) diff --git a/yogstation/code/game/gamemodes/gangs/gang_pen.dm b/yogstation/code/game/gamemodes/gangs/gang_pen.dm deleted file mode 100644 index 582eff8d205c..000000000000 --- a/yogstation/code/game/gamemodes/gangs/gang_pen.dm +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Gang Boss Pens - */ -/obj/item/pen/gang - var/cooldown - -/obj/item/pen/gang/attack(mob/living/M, mob/user, stealth = TRUE) - if(!istype(M)) - return - if(!ishuman(M) || !ishuman(user) || M.stat == DEAD) - return ..() - var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss) - if(!L) - return ..() - if(!..()) - return - if(cooldown) - to_chat(user, span_warning("[src] needs more time to recharge before it can be used.")) - return - if(!M.client || !M.mind) - to_chat(user, span_warning("A braindead gangster is an useless gangster!")) - return - var/datum/team/gang/gang = L.gang - if(!add_gangster(user, gang, M.mind)) - return - cooldown = TRUE - icon_state = "pen_blink" - var/cooldown_time = 600/gang.leaders.len - addtimer(CALLBACK(src, PROC_REF(cooldown)), cooldown_time) - -/obj/item/pen/gang/proc/cooldown() - cooldown = FALSE - icon_state = "pen" - var/mob/M = loc - if(istype(M)) - to_chat(M, span_notice("[icon2html(src, M)] [src] vibrates softly. It is ready to be used again.")) - -/obj/item/pen/gang/proc/add_gangster(mob/user, datum/team/gang/gang, datum/mind/gangster_mind, check = TRUE) // Basically a wrapper to add_antag_datum. - var/datum/antagonist/dudegang = gangster_mind.has_antag_datum(/datum/antagonist/gang) - if(dudegang) - if(dudegang == gang) - to_chat(user, span_danger("This mind is already controlled by your gang!")) - return - to_chat(user, span_danger("This mind is already controlled by someone else!")) - return - if(check && HAS_TRAIT(gangster_mind.current, TRAIT_MINDSHIELD)) //Check to see if the potential gangster is implanted - to_chat(user, span_danger("This mind is too strong to control!")) - return - var/mob/living/carbon/human/H = gangster_mind.current // we are sure the dude's human cause it's checked in attack() - H.silent = max(H.silent, 5) - H.Knockdown(100) - if(is_banned_from(gangster_mind.current.ckey, ROLE_GANG)) - INVOKE_ASYNC(src, TYPE_PROC_REF(/datum/game_mode, replace_jobbaned_player), gangster_mind.current, ROLE_GANG, ROLE_GANG) // will gangster_mind point to the new dude's mind? dunno honestly, i hope it does - gangster_mind.add_antag_datum(/datum/antagonist/gang, gang) - return TRUE diff --git a/yogstation/code/game/gamemodes/gangs/gangs.dm b/yogstation/code/game/gamemodes/gangs/gangs.dm deleted file mode 100644 index c1cd34e2a966..000000000000 --- a/yogstation/code/game/gamemodes/gangs/gangs.dm +++ /dev/null @@ -1,97 +0,0 @@ -//gang.dm -//Gang War Game Mode -GLOBAL_LIST_INIT(possible_gangs, subtypesof(/datum/team/gang)) -GLOBAL_LIST_EMPTY(gangs) -/datum/game_mode/gang - name = "gang war" - config_tag = "gang" - antag_flag = ROLE_GANG - restricted_jobs = list("Security Officer", "Warden", "Detective", "AI", "Cyborg","Captain", "Head of Personnel", "Head of Security", "Chief Engineer", "Research Director", "Chief Medical Officer", "Brig Physician", "Synthetic") //Added Brig Physician - required_players = 35 - required_enemies = 1 - recommended_enemies = 2 - enemy_minimum_age = 14 - title_icon = "gang" - - announce_span = "danger" - announce_text = "A violent turf war has erupted on the station!\n\ - Gangsters: Take over the station with a dominator.\n\ - Crew: Prevent the gangs from expanding and initiating takeover." - - var/list/datum/mind/gangboss_candidates = list() - -/datum/game_mode/gang/pre_setup() - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - restricted_jobs += protected_jobs - - if(CONFIG_GET(flag/protect_assistant_from_antagonist)) - restricted_jobs += "Assistant" - - //Spawn more bosses depending on server population - var/gangs_to_create = 4 - if(prob(num_players()) && num_players() > 1.5*required_players) - gangs_to_create++ - if(prob(num_players()) && num_players() > 2*required_players) - gangs_to_create++ - gangs_to_create = min(gangs_to_create, GLOB.possible_gangs.len) - - for(var/i in 1 to gangs_to_create) - if(!antag_candidates.len) - break - - //Now assign a boss for the gang - var/datum/mind/boss = antag_pick(antag_candidates) - antag_candidates -= boss - gangboss_candidates += boss - boss.special_role = ROLE_GANG - boss.restricted_roles = restricted_jobs - - if(gangboss_candidates.len < 1) //Need at least one gangs - return - - return TRUE - -/datum/game_mode/gang/post_setup() - ..() - for(var/i in gangboss_candidates) - var/datum/mind/M = i - var/datum/antagonist/gang/boss/B = new() - M.add_antag_datum(B) - B.equip_gang() - -/proc/is_gangster(mob/M) // Gangster Checks - return M?.mind?.has_antag_datum(/datum/antagonist/gang) - -/proc/is_gang_boss(mob/M) - return M?.mind?.has_antag_datum(/datum/antagonist/gang/boss) - -/datum/game_mode/gang/generate_credit_text() - var/list/round_credits = list() - var/len_before_addition - - for(var/datum/team/gang/G in GLOB.gangs) - round_credits += "
The [G.name] Gang:
" - len_before_addition = round_credits.len - for(var/datum/mind/boss in G.leaders) - round_credits += "[boss.name] as a [G.name] Gang leader
" - for(var/datum/mind/gangster in (G.members - G.leaders)) - round_credits += "[gangster.name] as a [G.name] gangster
" - if(len_before_addition == round_credits.len) - round_credits += list("The [G.name] Gang was wiped out!
", "The competition was too tough!
") - round_credits += "" - - round_credits += ..() - return round_credits - -/datum/game_mode/gang/set_round_result() - ..() - var/didGangsWin = FALSE - for(var/datum/team/gang/G in GLOB.gangs) - if(G.winner) - didGangsWin = TRUE - break - - if(didGangsWin) - SSticker.mode_result = "win - gangs dominated the station" - else - SSticker.mode_result = "loss - security stopped gangs from dominating the station" diff --git a/yogstation/code/game/gamemodes/gangs/gangtool.dm b/yogstation/code/game/gamemodes/gangs/gangtool.dm deleted file mode 100644 index 39d69b0e1d11..000000000000 --- a/yogstation/code/game/gamemodes/gangs/gangtool.dm +++ /dev/null @@ -1,277 +0,0 @@ -//gangtool device -/obj/item/gangtool - name = "suspicious device" - desc = "A strange device of sorts. Hard to really make out what it actually does if you don't know how to operate it." - icon = 'yogstation/icons/obj/device.dmi' - icon_state = "gangtool" - item_state = "radio" - lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi' - righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi' - throwforce = 0 - w_class = WEIGHT_CLASS_TINY - throw_speed = 3 - throw_range = 7 - flags_1 = CONDUCT_1 - var/datum/team/gang/gang //Which gang uses this? - var/recalling = FALSE - var/outfits = 2 - var/free_pen = FALSE - var/promotable = FALSE - var/static/list/buyable_items = list() - var/list/tags = list() - -/obj/item/gangtool/Initialize(mapload) - update_appearance(UPDATE_ICON) - if(buyable_items.len) - return ..() - for(var/i in subtypesof(/datum/gang_item)) - var/datum/gang_item/G = i - var/id = initial(G.id) - var/cat = initial(G.category) - if(id) - if(!islist(buyable_items[cat])) - buyable_items[cat] = list() - buyable_items[cat][id] = new G - .=..() - -/obj/item/gangtool/Destroy() - if(gang) - gang.gangtools -= src - return ..() - -/obj/item/gangtool/attack_self(mob/user) - ..() - if (!can_use(user)) - return - var/datum/antagonist/gang/boss/L = user.mind.has_antag_datum(/datum/antagonist/gang/boss) - var/dat - if(!gang) - dat += "This device is not registered.
" - if(L) - if(promotable && L.gang.leaders.len < L.gang.max_leaders) - dat += "Give this device to another member of your organization to use to promote them to Lieutenant.
" - dat += "If this is meant as a spare device for yourself:
" - dat += "Register Device as Spare
" - else if(promotable) - var/datum/antagonist/gang/sweet = user.mind.has_antag_datum(/datum/antagonist/gang) - if(sweet && sweet.gang.leaders.len < sweet.gang.max_leaders) - dat += "You have been selected for a promotion!
" - dat += "Accept Promotion
" - else - dat += "No promotions available: All positions filled.
" - else - dat += "This device is not authorized to promote.
" - else - if(gang.domination_time != NOT_DOMINATING) - dat += "
[DisplayTimeText(gang.domination_time_remaining() * 10)] remain
" - dat += "Organization Size: [gang.members.len] | Station Control: [gang.territories.len] territories under control. | Influence: [gang.influence] | Supply: [gang.uniform_influence]
" - dat += "Time until Influence grows: [time2text(gang.next_point_time - world.time, "mm:ss")]
" - dat += "Send message to Gang
" - dat += "Recall shuttle
" - dat += "
" - for(var/cat in buyable_items) - dat += "[cat]
" - for(var/id in buyable_items[cat]) - var/datum/gang_item/G = buyable_items[cat][id] - if(!G.can_see(user, gang, src)) - continue - if(G.category != "Purchase Weapons:") - var/cost = G.get_cost_display(user, gang, src) - if(cost) - dat += cost + " " - - var/toAdd = G.get_name_display(user, gang, src) - if(G.can_buy(user, gang, src)) - toAdd = "[toAdd]" - dat += toAdd - dat += "
" - else if(G.category == "Purchase Weapons:") - var/weapon_cost = G.get_weapon_cost_display(user, gang, src) - if(weapon_cost) - dat += weapon_cost + " " - - var/toAdd = G.get_name_display(user, gang, src) - if(G.can_buy_weapon(user, gang, src)) - toAdd = "[toAdd]" - dat += toAdd - dat += "
" - var/extra = G.get_extra_info(user, gang, src) - if(extra) - dat += "
[extra]" - dat += "
" - - dat += "Refresh
" - - var/datum/browser/popup = new(user, "gangtool", "Welcome to GangTool v4.0", 340, 625) - popup.set_content(dat) - popup.open() - -/obj/item/gangtool/Topic(href, href_list) - if(!can_use(usr)) - return - - add_fingerprint(usr) - - if(href_list["register"]) - register_device(usr) - - else if(!gang) //Gangtool must be registered before you can use the functions below - return - - if(href_list["purchase"]) - if(islist(buyable_items[href_list["cat"]])) - var/list/L = buyable_items[href_list["cat"]] - var/datum/gang_item/G = L[href_list["id"]] - if(G && G.can_buy(usr, gang, src)) - G.purchase(usr, gang, src, FALSE) - if(href_list["weapon_purchase"]) - if(islist(buyable_items[href_list["cat"]])) - var/list/L = buyable_items[href_list["cat"]] - var/datum/gang_item/G = L[href_list["id"]] - if(G && G.can_buy_weapon(usr, gang, src)) - G.weapon_purchase(usr, gang, src, FALSE) - if(href_list["commute"]) - ping_gang(usr) - if(href_list["recall"]) - recall(usr) - attack_self(usr) - -/obj/item/gangtool/update_overlays() - . = ..() - var/image/I = new(icon, "[icon_state]-overlay") - if(gang) - I.color = gang.color - . += I - -/obj/item/gangtool/proc/ping_gang(mob/user) - if(!can_use(user)) - return - var/message = stripped_input(user,"Discreetly send a gang-wide message.","Send Message") - if(!message || !can_use(user)) - return - if(!is_station_level(user.z)) - to_chat(user, span_info("[icon2html(src, user)]Error: Station out of range.")) - return - if(gang.members.len) - var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) - if(!G) - return - var/ping = span_danger("[gang.name] [G.message_name] [user.real_name]: [message]") - for(var/datum/mind/ganger in gang.members) - if(ganger.current && is_station_level(ganger.current.z) && (ganger.current.stat == CONSCIOUS)) - to_chat(ganger.current, ping) - for(var/mob/M in GLOB.dead_mob_list) - var/link = FOLLOW_LINK(M, user) - to_chat(M, "[link] [ping]") - user.log_talk(message,LOG_SAY, tag="[gang.name] gangster") - -/obj/item/gangtool/proc/register_device(mob/user) - if(gang) //It's already been registered! - return - var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) - if(G) - gang = G.gang - gang.gangtools += src - update_appearance(UPDATE_ICON) - if(!(user.mind in gang.leaders) && promotable) - G.promote() - free_pen = TRUE - gang.message_gangtools("[user] has been promoted to Lieutenant.") - to_chat(user, "The Gangtool you registered will allow you to purchase weapons and equipment, and send messages to your gang.") - to_chat(user, "Unlike regular gangsters, you may use recruitment pens to add recruits to your gang. Use them on unsuspecting crew members to recruit them. Don't forget to get your one free pen from the gangtool.") - else - to_chat(user, span_warning("ACCESS DENIED: Unauthorized user.")) - -/obj/item/gangtool/proc/recall(mob/user) - if(!recallchecks(user)) - return - if(recalling) - to_chat(user, span_warning("Error: Recall already in progress.")) - return - gang.message_gangtools("[user] is attempting to recall the emergency shuttle.") - recalling = TRUE - to_chat(user, span_info("[icon2html(src, loc)]Generating shuttle recall order with codes retrieved from last call signal...")) - addtimer(CALLBACK(src, PROC_REF(recall2), user), rand(100,300)) - -/obj/item/gangtool/proc/recall2(mob/user) - if(!recallchecks(user)) - return - to_chat(user, span_info("[icon2html(src, loc)]Shuttle recall order generated. Accessing station long-range communication arrays...")) - addtimer(CALLBACK(src, PROC_REF(recall3), user), rand(100,300)) - -/obj/item/gangtool/proc/recall3(mob/user) - if(!recallchecks(user)) - return - var/list/living_crew = list()//shamelessly copied from mulligan code, there should be a helper for this - for(var/mob/Player in GLOB.player_list) - if(Player.mind && Player.stat != DEAD && !isnewplayer(Player) && !isbrain(Player) && Player.client) - living_crew += Player - var/malc = CONFIG_GET(number/midround_antag_life_check) - if(living_crew.len / GLOB.joined_player_list.len <= malc) //Shuttle cannot be recalled if too many people died - to_chat(user, span_warning("[icon2html(src, user)]Error: Station communication systems compromised. Unable to establish connection.")) - recalling = FALSE - return - to_chat(user, span_info("[icon2html(src, loc)]Comm arrays accessed. Broadcasting recall signal...")) - addtimer(CALLBACK(src, PROC_REF(recallfinal), user), rand(100,300)) - -/obj/item/gangtool/proc/recallfinal(mob/user) - if(!recallchecks(user)) - return - recalling = FALSE - log_game("[key_name(user)] has tried to recall the shuttle with a gangtool.") - message_admins("[key_name_admin(user)] has tried to recall the shuttle with a gangtool.", 1) - if(SSshuttle.cancelEvac(user)) - gang.recalls-- - return TRUE - - to_chat(user, span_info("[icon2html(src, loc)]No response received. Emergency shuttle cannot be recalled at this time.")) - return - -/obj/item/gangtool/proc/recallchecks(mob/user) - if(!can_use(user)) - return - if(SSshuttle.emergency_no_recall) - return - if(!is_station_level(user.z)) //Shuttle can only be recalled while on station - to_chat(user, span_warning("[icon2html(src, user)]Error: Device out of range of station communication arrays.")) - recalling = FALSE - return - if(!gang.recalls) - to_chat(user, span_warning("Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.")) - return - if(SSshuttle.emergency.mode != SHUTTLE_CALL) //Shuttle can only be recalled when it's moving to the station - to_chat(user, span_warning("[icon2html(src, user)]Emergency shuttle cannot be recalled at this time.")) - recalling = FALSE - return - if(!gang.dom_attempts) - to_chat(user, span_warning("[icon2html(src, user)]Error: Unable to access communication arrays. Firewall has logged our signature and is blocking all further attempts.")) - recalling = FALSE - return - return TRUE - -/obj/item/gangtool/proc/can_use(mob/living/carbon/human/user) - if(!istype(user)) - return - if(user.incapacitated()) - return - if(!(src in user.contents)) - return - if(!user.mind) - return - var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) - if(!G) - to_chat(user, span_notice("Huh, what's this?")) - return - if(!isnull(gang) && G.gang != gang) - to_chat(user, span_danger("You cannot use gang tools owned by enemy gangs!")) - return - return TRUE - - -/obj/item/gangtool/spare - outfits = TRUE - -/obj/item/gangtool/spare/lt - promotable = TRUE diff --git a/yogstation/code/game/gamemodes/infiltration/infiltration.dm b/yogstation/code/game/gamemodes/infiltration/infiltration.dm index ea03343124e6..06b940dc295a 100644 --- a/yogstation/code/game/gamemodes/infiltration/infiltration.dm +++ b/yogstation/code/game/gamemodes/infiltration/infiltration.dm @@ -1,59 +1,54 @@ -/datum/game_mode/infiltration - name = "infiltration" - config_tag = "infiltration" - false_report_weight = 10 - required_players = 25 - required_enemies = 3 - recommended_enemies = 5 - enemy_minimum_age = 21 - antag_flag = ROLE_INFILTRATOR - - announce_span = "danger" - announce_text = "Syndicate infiltrators are attempting to board the station!\n\ - Infiltrators: Board the station stealthfully and complete your objectives!\n\ - Crew: Prevent the infiltrators from completing their objectives!" - - var/agents_possible = 5 - var/agents_left = 1 - var/list/pre_sit = list() - - var/datum/team/infiltrator/sit_team - - var/static/list/areas_that_can_finish = typecacheof(list(/area/shuttle/yogs/stealthcruiser, /area/yogs/infiltrator_base)) - -/datum/game_mode/infiltration/pre_setup() - var/n_agents = min(max(CEILING(num_players() / 7, 1), 1), antag_candidates.len, agents_possible) - if(GLOB.Debug2 || n_agents >= required_enemies) - for(var/i = 0, i < n_agents, ++i) - var/datum/mind/new_sit = pick_n_take(antag_candidates) - pre_sit += new_sit - new_sit.assigned_role = "Syndicate Infiltrator" - new_sit.special_role = "Syndicate Infiltrator" - log_game("[key_name(new_sit)] has been selected as a syndicate infiltrator") - return TRUE - setup_error = "Not enough infiltrator candidates" - message_admins("Not enough infiltrator candidates! Was making [n_agents], but we need [required_enemies]!") - return FALSE - -/datum/game_mode/infiltration/post_setup() - sit_team = new /datum/team/infiltrator - for(var/datum/mind/sit_mind in pre_sit) - sit_mind.add_antag_datum(ANTAG_DATUM_INFILTRATOR, sit_team) - sit_team.update_objectives() - return ..() - -/datum/game_mode/infiltration/generate_report() //make this less shit - return "Reports show that the Syndicate is rounding up it's elite agents, possibly for a raid on a Nanotrasen-controlled station. Keep an eye out for unusual people." - -/datum/game_mode/infiltration/set_round_result() - ..() - var/result = sit_team.get_result() - switch(result) - if(INFILTRATION_ALLCOMPLETE) - SSticker.mode_result = "major win - objectives complete" - if(INFILTRATION_MOSTCOMPLETE) - SSticker.mode_result = "minor win - most objectives complete" - if(INFILTRATION_SOMECOMPLETE) - SSticker.mode_result = "neutral - some objectives complete" - else - SSticker.mode_result = "loss - no objectives complete" +// /datum/game_mode/infiltration +// name = "infiltration" +// config_tag = "infiltration" +// false_report_weight = 10 +// required_players = 25 +// required_enemies = 3 +// recommended_enemies = 5 +// enemy_minimum_age = 21 +// antag_flag = ROLE_INFILTRATOR + +// var/agents_possible = 5 +// var/agents_left = 1 +// var/list/pre_sit = list() + +// var/datum/team/infiltrator/sit_team + +// var/static/list/areas_that_can_finish = typecacheof(list(/area/shuttle/yogs/stealthcruiser, /area/yogs/infiltrator_base)) + +// /datum/game_mode/infiltration/pre_setup() +// var/n_agents = min(max(CEILING(num_players() / 7, 1), 1), antag_candidates.len, agents_possible) +// if(GLOB.Debug2 || n_agents >= required_enemies) +// for(var/i = 0, i < n_agents, ++i) +// var/datum/mind/new_sit = pick_n_take(antag_candidates) +// pre_sit += new_sit +// new_sit.assigned_role = "Syndicate Infiltrator" +// new_sit.special_role = "Syndicate Infiltrator" +// log_game("[key_name(new_sit)] has been selected as a syndicate infiltrator") +// return TRUE +// setup_error = "Not enough infiltrator candidates" +// message_admins("Not enough infiltrator candidates! Was making [n_agents], but we need [required_enemies]!") +// return FALSE + +// /datum/game_mode/infiltration/post_setup() +// sit_team = new /datum/team/infiltrator +// for(var/datum/mind/sit_mind in pre_sit) +// sit_mind.add_antag_datum(ANTAG_DATUM_INFILTRATOR, sit_team) +// sit_team.update_objectives() +// return ..() + +// /datum/game_mode/infiltration/generate_report() //make this less shit +// return "Reports show that the Syndicate is rounding up it's elite agents, possibly for a raid on a Nanotrasen-controlled station. Keep an eye out for unusual people." + +// /datum/game_mode/infiltration/set_round_result() +// ..() +// var/result = sit_team.get_result() +// switch(result) +// if(INFILTRATION_ALLCOMPLETE) +// SSticker.mode_result = "major win - objectives complete" +// if(INFILTRATION_MOSTCOMPLETE) +// SSticker.mode_result = "minor win - most objectives complete" +// if(INFILTRATION_SOMECOMPLETE) +// SSticker.mode_result = "neutral - some objectives complete" +// else +// SSticker.mode_result = "loss - no objectives complete" diff --git a/yogstation/code/game/gamemodes/traitor/internal_affairs.dm b/yogstation/code/game/gamemodes/traitor/internal_affairs.dm deleted file mode 100644 index f2bd321ecad2..000000000000 --- a/yogstation/code/game/gamemodes/traitor/internal_affairs.dm +++ /dev/null @@ -1,81 +0,0 @@ -/datum/antagonist/traitor/internal_affairs/iaa_process() - if(owner && owner.current && !iscyborg(owner.current) && owner.current.stat!=DEAD) - var/new_objective = TRUE - for(var/objective_ in objectives) - if(istype(objective_, /datum/objective/hijack) || istype(objective_, /datum/objective/martyr) || istype(objective_, /datum/objective/block)) - new_objective = FALSE - break - if(!is_internal_objective(objective_)) - continue - var/datum/objective/assassinate/internal/objective = objective_ - if(!objective.target || objective.check_completion()) - continue - new_objective = FALSE - - if(new_objective) - var/list/other_traitors = SSticker.mode.traitors - owner - for(var/objective_ in objectives) - if(!is_internal_objective(objective_)) - continue - var/datum/objective/assassinate/internal/objective = objective_ - if(objective.target) - other_traitors -= objective.target - for(var/tator in other_traitors) - var/datum/mind/tatortottle = tator - if(!tatortottle.current || tatortottle.current.stat == DEAD || iscyborg(tatortottle.current)) - other_traitors -= tatortottle - - if(other_traitors.len) - var/datum/mind/target_mind = pick(other_traitors) - var/datum/objective/assassinate/internal/kill_objective = new - kill_objective.owner = owner - kill_objective.target = target_mind - kill_objective.update_explanation_text() - add_objective(kill_objective) - else - for(var/objective_ in objectives) - remove_objective(objective_) - - if(prob(50)) //50/50 split between glorious death and hijack, so IAA can't just go "hurr, I can kill everyone since I'll get hijack later" - var/datum/objective/martyr/martyr_objective = new - martyr_objective.owner = owner - add_objective(martyr_objective) - else - var/datum/objective/hijack/hijack_objective = new - hijack_objective.owner = owner - add_objective(hijack_objective) - - owner.announce_objectives() - -/datum/game_mode/traitor/internal_affairs/add_latejoin_traitor(datum/mind/character) - for(var/data in character.antag_datums) - if(istype(data, /datum/antagonist/traitor/internal_affairs)) - var/datum/antagonist/traitor/internal_affairs/IAAdata = data - IAAdata.latejoin() - break - -/datum/antagonist/traitor/internal_affairs/proc/latejoin() - var/list/other_traitors = SSticker.mode.traitors - owner - for(var/V in other_traitors) - var/datum/mind/traitor = V - if(!traitor.current || traitor.current.stat == DEAD) - other_traitors -= traitor - - if(other_traitors.len) - var/datum/mind/target_mind = pick(other_traitors) - var/datum/objective/assassinate/internal/kill_objective = new - kill_objective.owner = owner - kill_objective.target = target_mind - kill_objective.update_explanation_text() - add_objective(kill_objective) - else - if(prob(50)) //50/50 split between glorious death and hijack, so IAA can't just go "hurr, I can kill everyone since I'll get hijack later" - var/datum/objective/martyr/martyr_objective = new - martyr_objective.owner = owner - add_objective(martyr_objective) - else - var/datum/objective/hijack/hijack_objective = new - hijack_objective.owner = owner - add_objective(hijack_objective) - - owner.announce_objectives() diff --git a/yogstation/code/game/gamemodes/vampire/traitor_vamp.dm b/yogstation/code/game/gamemodes/vampire/traitor_vamp.dm deleted file mode 100644 index fe8f1827867f..000000000000 --- a/yogstation/code/game/gamemodes/vampire/traitor_vamp.dm +++ /dev/null @@ -1,84 +0,0 @@ -/datum/game_mode/traitor/vampire - name = "traitor+vampire" - config_tag = "traitorvamp" - false_report_weight = 10 - traitors_possible = 3 //hard limit on traitors if scaling is turned off - restricted_jobs = list("AI", "Cyborg", "Synthetic") - required_players = 15 - required_enemies = 1 // how many of each type are required - recommended_enemies = 3 - reroll_friendly = 1 - - num_modifier = -2 //less traitors to account for the vamps - var/list/possible_vampires = list() - var/const/vampire_amt = 2 //hard limit on vampires if scaling is turned off - var/list/pre_vamps = list() - -/datum/game_mode/traitor/vampire/announce() - to_chat(world, "The current game mode is - Traitor+Vampire!") - to_chat(world, "There are vampires on the station along with some syndicate operatives out for their own gain! Do not let the vampires and the traitors succeed!") - -/datum/game_mode/traitor/vampire/can_start() - if(!..()) - return 0 - possible_vampires = get_players_for_role(ROLE_VAMPIRE) - if(possible_vampires.len < required_enemies) - return 0 - return 1 - -/datum/game_mode/traitor/vampire/pre_setup() - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - restricted_jobs += protected_jobs - - if(CONFIG_GET(flag/protect_assistant_from_antagonist)) - restricted_jobs += "Assistant" - - //yay lets add chaplain here so he cant be the vampire in traitor+vamps - restricted_jobs += "Chaplain" - var/list/datum/mind/possible_vamps = get_players_for_role(ROLE_VAMPIRE) - restricted_jobs -= "Chaplain" - - var/num_vamp = 1 - - if(CONFIG_GET(number/traitor_scaling_coeff)) - num_vamp = max(1, min( round(num_players()/(CONFIG_GET(number/traitor_scaling_coeff)*4))+2, round(num_players()/(CONFIG_GET(number/traitor_scaling_coeff)*2)) )) - else - num_vamp = max(1, min(num_players(), vampire_amt/2)) - - var/list/old_antag_candidates = antag_candidates.Copy() - if(possible_vamps.len>0) - for(var/j = 0, j < num_vamp, j++) - if(!possible_vamps.len) break - var/datum/mind/vamp = pick(possible_vamps) - pre_vamps += vamp - possible_vamps -= vamp - antag_candidates -= vamp - vamp.special_role = "Vampire" - vamp.restricted_roles = restricted_jobs - antag_candidates = old_antag_candidates // So we still have a chance for a traitor vampire. - return ..() - else - return FALSE - -/datum/game_mode/traitor/vampire/post_setup() - for(var/datum/mind/vamp in pre_vamps) - vamp.add_antag_datum(/datum/antagonist/vampire) - ..() - return - -/datum/game_mode/traitor/vampire/make_antag_chance(mob/living/carbon/human/character) //Assigns vampire to latejoiners - var/vampcap = min( round(GLOB.joined_player_list.len/(CONFIG_GET(number/traitor_scaling_coeff)*4))+2, round(GLOB.joined_player_list.len/(CONFIG_GET(number/traitor_scaling_coeff)*2)) ) - if(SSticker.mode.vampires.len >= vampcap) //Caps number of latejoin antagonists - ..() - return - if(SSticker.mode.vampires.len <= (vampcap - 2) || prob(100 / (CONFIG_GET(number/traitor_scaling_coeff) * 4))) - if(ROLE_VAMPIRE in character.client.prefs.be_special) - if(!is_banned_from(character.ckey, ROLE_VAMPIRE) && !is_banned_from(character.ckey, "Syndicate")) - if(age_check(character.client)) - if(!(character.job in restricted_jobs)) - add_vampire(character) - ..() - -/datum/game_mode/traitor/vampire/generate_report() - return "We have received some fuzzy reports about the Syndicate cooperating with a bluespace demon.\ - Keep a watch out for syndicate agents, and have your Chaplain on standby." diff --git a/yogstation/code/game/gamemodes/vampire/vampire.dm b/yogstation/code/game/gamemodes/vampire/vampire.dm deleted file mode 100644 index e14a335a21f9..000000000000 --- a/yogstation/code/game/gamemodes/vampire/vampire.dm +++ /dev/null @@ -1,107 +0,0 @@ -/datum/game_mode - var/list/datum/mind/vampires = list() - -/mob/living/carbon/human/get_status_tab_items() - . = ..() - var/datum/antagonist/vampire/vamp = mind.has_antag_datum(/datum/antagonist/vampire) - if(vamp) - . += "Total Blood: [vamp.total_blood]" - . += "Usable Blood: [vamp.usable_blood]" - -/datum/game_mode/vampire - name = "vampire" - config_tag = "vampire" - antag_flag = ROLE_VAMPIRE - false_report_weight = 1 - restricted_jobs = list("AI", "Cyborg", "Synthetic") - protected_jobs = list("Head of Security", "Captain", "Head of Personnel", "Research Director", "Chief Engineer", "Chief Medical Officer", "Security Officer", "Chaplain", "Detective", "Warden", "Brig Physician") //Added Brig Physician - required_players = 15 - required_enemies = 1 - recommended_enemies = 3 - enemy_minimum_age = 0 - title_icon = "vampires" - - announce_text = "There are vampires onboard the station!\n\ - + Vampires: Suck the blood of the crew and complete your objectives!\n\ - + Crew: Kill the unholy vampires!" - - var/vampires_possible = 4 //hard limit on vampires if scaling is turned off - var/num_modifier = 0 - var/list/datum/mind/pre_vamps = list() - -/datum/game_mode/vampire/pre_setup() - - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - restricted_jobs += protected_jobs - - if(CONFIG_GET(flag/protect_assistant_from_antagonist)) - restricted_jobs += "Assistant" - - var/num_vamps = 1 - - var/tsc = CONFIG_GET(number/traitor_scaling_coeff) - if(tsc) - num_vamps = max(1, min(round(num_players() / (tsc * 2)) + 2 + num_modifier, round(num_players() / tsc) + num_modifier)) - else - num_vamps = max(1, min(num_players(), vampires_possible)) - - for(var/j = 0, j < num_vamps, j++) - if (!antag_candidates.len) - break - var/datum/mind/vamp = antag_pick(antag_candidates) - pre_vamps += vamp - vamp.special_role = "Vampire" - vamp.restricted_roles = restricted_jobs - log_game("[vamp.key] (ckey) has been selected as a Vampire") - antag_candidates.Remove(vamp) - - return pre_vamps.len > 0 - - -/datum/game_mode/vampire/post_setup() - for(var/datum/mind/vamp in pre_vamps) - vamp.add_antag_datum(/datum/antagonist/vampire) - ..() - return TRUE - -/proc/add_vampire(mob/living/L, full_vampire = TRUE) - if(!L || !L.mind || is_vampire(L)) - return FALSE - var/datum/antagonist/vampire/vamp - if(full_vampire == TRUE) - vamp = L.mind.add_antag_datum(/datum/antagonist/vampire) - else - vamp = L.mind.add_antag_datum(/datum/antagonist/vampire/new_blood) - return vamp - -/proc/remove_vampire(mob/living/L) - if(!L || !L.mind || !is_vampire(L)) - return FALSE - var/datum/antagonist/vamp = L.mind.has_antag_datum(/datum/antagonist/vampire) - vamp.on_removal() - return TRUE - -/proc/is_vampire(mob/living/M) - return M?.mind?.has_antag_datum(/datum/antagonist/vampire) - -/datum/game_mode/vampire/generate_report() - return "The Wizard Federation has created a new being based off ancient mythology. \ - These beings are known as vampires and are capable of sucking blood from crew members. \ - No further information is known at this time." - -/datum/game_mode/vampire/generate_credit_text() - var/list/round_credits = list() - var/len_before_addition - - round_credits += "
The Vampires:
" - len_before_addition = round_credits.len - for(var/datum/mind/vamp in pre_vamps) - round_credits += "[vamp.name] as a Vampire
" - if(len_before_addition == round_credits.len) - round_credits += list("The Vampires have fled to Transylvania!
", "We couldn't locate them!
") - round_credits += "" - - round_credits += ..() - return round_credits - - diff --git a/yogstation/code/game/gamemodes/vampire/vampire_bat.dm b/yogstation/code/game/gamemodes/vampire/vampire_bat.dm index 80abc4fb8fad..6fd7f25128a6 100644 --- a/yogstation/code/game/gamemodes/vampire/vampire_bat.dm +++ b/yogstation/code/game/gamemodes/vampire/vampire_bat.dm @@ -37,8 +37,10 @@ /mob/living/simple_animal/hostile/vampire_bat/CanAttack(atom/the_target) . = ..() - if(isliving(the_target) && is_vampire(the_target)) - return FALSE + if(isliving(the_target)) + var/mob/living/check = the_target + if(IS_VAMPIRE(check)) + return FALSE /mob/living/simple_animal/hostile/vampire_bat/death() if(isliving(controller)) diff --git a/yogstation/code/game/gamemodes/vampire/vampire_bite.dm b/yogstation/code/game/gamemodes/vampire/vampire_bite.dm index 5e7476baa00c..21781c2fcda5 100644 --- a/yogstation/code/game/gamemodes/vampire/vampire_bite.dm +++ b/yogstation/code/game/gamemodes/vampire/vampire_bite.dm @@ -1,11 +1,11 @@ /mob/living/carbon/human/proc/handle_vamp_biting(mob/living/carbon/human/M) - if(!is_vampire(M) || M == src || M.zone_selected != "head") + if(!IS_VAMPIRE(M) || M == src || M.zone_selected != "head") return FALSE var/datum/antagonist/vampire/V = M.mind.has_antag_datum(/datum/antagonist/vampire) if((NOBLOOD in dna.species.species_traits) || dna.species.exotic_blood || !blood_volume) to_chat(M, span_warning("They have no blood!")) return FALSE - if(is_vampire(src)) + if(IS_VAMPIRE(src)) to_chat(M, span_warning("Your fangs fail to pierce [name]'s cold flesh")) return FALSE if(dna.species.name == "skeleton") diff --git a/yogstation/code/game/gamemodes/vampire/vampire_other.dm b/yogstation/code/game/gamemodes/vampire/vampire_other.dm index 49856a26d492..8b3c69732153 100644 --- a/yogstation/code/game/gamemodes/vampire/vampire_other.dm +++ b/yogstation/code/game/gamemodes/vampire/vampire_other.dm @@ -42,7 +42,7 @@ return ..() /obj/item/clothing/suit/draculacoat/proc/dodge(mob/living/carbon/human/user, atom/movable/incoming, damage, attack_text) - if(user.incapacitated() || !COOLDOWN_FINISHED(src, dodge_cooldown) || !is_vampire(user)) + if(user.incapacitated() || !COOLDOWN_FINISHED(src, dodge_cooldown) || !IS_VAMPIRE(user)) return NONE COOLDOWN_START(src, dodge_cooldown, dodge_delay) @@ -53,16 +53,16 @@ /obj/item/clothing/suit/draculacoat/process() var/mob/living/carbon/human/user = src.loc - if(user && ishuman(user) && is_vampire(user) && (user.wear_suit == src)) + if(user && ishuman(user) && IS_VAMPIRE(user) && (user.wear_suit == src)) if(COOLDOWN_FINISHED(src, regen_cooldown)) COOLDOWN_START(src, regen_cooldown, blood_regen_delay) - var/datum/antagonist/vampire/vampire = is_vampire(user) + var/datum/antagonist/vampire/vampire = IS_VAMPIRE(user) if (vampire.total_blood >= 5 && vampire.usable_blood < vampire.total_blood) vampire.usable_blood = min(vampire.usable_blood + 5, vampire.total_blood) // 5 units every 10 seconds /obj/item/storage/book/bible/attack(mob/living/M, mob/living/carbon/human/user, heal_mode = TRUE) . = ..() - if(!(user.mind && user.mind.holy_role) && is_vampire(user)) + if(!(user.mind && user.mind.holy_role) && IS_VAMPIRE(user)) to_chat(user, span_danger("[deity_name] channels through \the [src] and sets you ablaze for your blasphemy!")) user.adjust_fire_stacks(5) user.ignite_mob() diff --git a/yogstation/code/game/gamemodes/vampire/vampire_powers.dm b/yogstation/code/game/gamemodes/vampire/vampire_powers.dm index 3b4d36996ad8..6e7772719c7d 100644 --- a/yogstation/code/game/gamemodes/vampire/vampire_powers.dm +++ b/yogstation/code/game/gamemodes/vampire/vampire_powers.dm @@ -4,7 +4,7 @@ /datum/action/cooldown/spell/can_cast_spell(feedback = TRUE) if(vamp_req) - if(!is_vampire(owner)) + if(!IS_VAMPIRE(owner)) return FALSE return ..() @@ -16,12 +16,12 @@ /datum/action/cooldown/spell/before_cast(atom/cast_on) if(vamp_req) // sanity check before we cast - if(!is_vampire(owner)) + if(!IS_VAMPIRE(owner)) return return ..() /datum/action/cooldown/spell/is_valid_target(mob/living/target) - if(vamp_req && !is_vampire(target)) + if(vamp_req && !IS_VAMPIRE(target)) return FALSE return ..() @@ -233,7 +233,7 @@ /datum/action/cooldown/spell/cloak/proc/update_name() var/mob/living/user = owner - if(!ishuman(user) || !is_vampire(user)) + if(!ishuman(user) || !IS_VAMPIRE(user)) return var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) name = "[initial(name)] ([V.iscloaking ? "Deactivate" : "Activate"])" @@ -267,7 +267,7 @@ /datum/action/cooldown/spell/revive/cast(mob/living/user) . = ..() - if(!is_vampire(user) || !isliving(user)) + if(!IS_VAMPIRE(user) || !isliving(user)) return if(user.stat != DEAD) to_chat(user, span_notice("We aren't dead enough to do that yet!")) @@ -287,7 +287,7 @@ /datum/action/cooldown/spell/revive/proc/revive(mob/living/user) if(istype(get_area(user.loc), /area/chapel)) - var/datum/antagonist/vampire/V = is_vampire(user) + var/datum/antagonist/vampire/V = IS_VAMPIRE(user) if(V && V.get_ability(/datum/vampire_passive/full)) //full blooded vampire doesn't get dusted if they try to res, it still doesn't work though to_chat(user, span_danger("The holy energies of this place prevent our revival!")) return @@ -345,7 +345,7 @@ W.take_damage(75) /datum/action/cooldown/spell/aoe/screech/cast_on_thing_in_aoe(mob/living/target, mob/living/carbon/user) - if(!target == user || !is_vampire(target)) + if(!target == user || !IS_VAMPIRE(target)) if(ishuman(target) && target.soundbang_act(1, 0)) var/mob/living/carbon/human/human_target = target to_chat(target, span_warning("You hear a ear piercing shriek and your senses dull!")) @@ -420,7 +420,7 @@ return var/mob/living/carbon/target = target_atom var/datum/antagonist/vampire/vamp = user.mind.has_antag_datum(/datum/antagonist/vampire) - if(is_vampire(target)) + if(IS_VAMPIRE(target)) to_chat(user, span_warning("They're already a vampire!")) SEND_SIGNAL(user.mind, COMSIG_MIND_SPEND_ANTAG_RESOURCE, list(ANTAG_RESOURCE_VAMPIRE = -300)) // Refund cost return FALSE @@ -478,7 +478,7 @@ /datum/action/cooldown/spell/summon_coat/cast(mob/living/user) . = ..() - if(!is_vampire(user) || !isliving(user)) + if(!IS_VAMPIRE(user) || !isliving(user)) return var/datum/antagonist/vampire/V = user.mind.has_antag_datum(/datum/antagonist/vampire) if(!V) diff --git a/yogstation/code/game/objects/effects/countdown.dm b/yogstation/code/game/objects/effects/countdown.dm deleted file mode 100644 index 0e2b67b84db6..000000000000 --- a/yogstation/code/game/objects/effects/countdown.dm +++ /dev/null @@ -1,13 +0,0 @@ -/obj/effect/countdown/dominator - name = "dominator countdown" - text_size = 1 - color = "#ff00ff" // Overwritten when the dominator starts - -/obj/effect/countdown/dominator/get_value() - var/obj/machinery/dominator/D = attached_to - if(!istype(D)) - return - else if(D.gang && D.gang.domination_time != NOT_DOMINATING) - return D.gang.domination_time_remaining() - else - return "OFFLINE" \ No newline at end of file diff --git a/yogstation/code/game/objects/effects/decals/crayon.dm b/yogstation/code/game/objects/effects/decals/crayon.dm deleted file mode 100644 index 1069b7423876..000000000000 --- a/yogstation/code/game/objects/effects/decals/crayon.dm +++ /dev/null @@ -1,35 +0,0 @@ -/obj/effect/decal/cleanable/crayon/Initialize(mapload, main, type, e_name, graf_rot, alt_icon = null) - if(type == "poseur tag") - var/datum/team/gang/gang = pick(subtypesof(/datum/team/gang)) - var/gangname = initial(gang.name) - icon = 'yogstation/icons/effects/crayondecal.dmi' - icon_state = "[gangname]" - type = null - .=..() - -/obj/effect/decal/cleanable/crayon/gang - icon = 'yogstation/icons/effects/crayondecal.dmi' - layer = ABOVE_NORMAL_TURF_LAYER //Harder to hide - plane = GAME_PLANE - do_icon_rotate = FALSE //These are designed to always face south, so no rotation please. - var/datum/team/gang/gang - -/obj/effect/decal/cleanable/crayon/gang/Initialize(mapload, datum/team/gang/G, e_name = "gang tag", rotation = 0, mob/user) - if(!G) - return INITIALIZE_HINT_QDEL - gang = G - var/newcolor = G.color - var/area/territory = get_area(src) - icon_state = G.name - G.new_territories |= list(territory.type = territory.name) - //If this isn't tagged by a specific gangster there's no bonus income. - .=..(mapload, newcolor, icon_state, e_name, rotation) - -/obj/effect/decal/cleanable/crayon/gang/Destroy() - if(gang) - var/area/territory = get_area(src) - gang.territories -= territory.type - gang.new_territories -= territory.type - gang.lost_territories |= list(territory.type = territory.name) - gang = null - return ..() diff --git a/yogstation/code/game/objects/items/crayons.dm b/yogstation/code/game/objects/items/crayons.dm deleted file mode 100644 index cd9fa6acb177..000000000000 --- a/yogstation/code/game/objects/items/crayons.dm +++ /dev/null @@ -1,123 +0,0 @@ -#define RANDOM_GRAFFITI "Random Graffiti" -#define RANDOM_LETTER "Random Letter" -#define RANDOM_NUMBER "Random Number" -#define RANDOM_ORIENTED "Random Oriented" -#define RANDOM_RUNE "Random Rune" -#define RANDOM_ANY "Random Anything" - -/obj/item/toy/crayon - var/datum/team/gang/gang //For marking territory, spraycans are gang-locked to their initial gang due to colors - -/obj/item/toy/crayon/proc/hippie_gang_check(mob/user, atom/target) // hooked into afterattack - var/gang_mode = FALSE - if(gang && user.mind) - var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) - if(G) - if(G.gang != gang) - to_chat(user, span_danger("This spraycan's color isn't your gang's one! You cannot use it.")) - return FALSE - gang_mode = TRUE - instant = FALSE - . = "gang graffiti" - // discontinue if we're not in gang modethe area isn't valid for tagging because gang "honour" - if(gang_mode && (!can_claim_for_gang(user, target))) - return FALSE - return TRUE - -/obj/item/toy/crayon/proc/gang_final(mob/user, atom/target, list/affected_turfs) // hooked into afterattack - // Double check it wasn't tagged in the meanwhile - if(!can_claim_for_gang(user, target)) - return TRUE - tag_for_gang(user, target) - affected_turfs += target - -/obj/item/toy/crayon/proc/can_claim_for_gang(mob/user, atom/target) - // Check area validity. - // Reject space, player-created areas, and non-station z-levels. - var/area/A = get_area(target) - if(!A || (!is_station_level(A.z)) || !A.valid_territory) - to_chat(user, span_warning("[A] is unsuitable for tagging.")) - return FALSE - - var/spraying_over = FALSE - for(var/G in target) - var/obj/effect/decal/cleanable/crayon/gang/gangtag = G - if(istype(gangtag)) - var/datum/antagonist/gang/GA = user.mind.has_antag_datum(/datum/antagonist/gang) - if(gangtag.gang != GA.gang) - spraying_over = TRUE - break - - for(var/obj/machinery/power/apc in target) - to_chat(user, span_warning("You can't tag an APC.")) - return FALSE - - var/occupying_gang = territory_claimed(A, user) - if(occupying_gang && !spraying_over) - to_chat(user, span_danger("[A] has already been tagged by the [occupying_gang] gang! You must get rid of or spray over the old tag first!")) - return FALSE - - // If you pass the gaunlet of checks, you're good to proceed - return TRUE - -/obj/item/toy/crayon/proc/territory_claimed(area/territory, mob/user) - for(var/datum/team/gang/G in GLOB.gangs) - if(territory.type in (G.territories|G.new_territories)) - . = G.name - break - -/obj/item/toy/crayon/proc/tag_for_gang(mob/user, atom/target) - //Delete any old markings on this tile, including other gang tags - for(var/obj/effect/decal/cleanable/crayon/old_marking in target) - qdel(old_marking) - - var/datum/antagonist/gang/G = user.mind.has_antag_datum(/datum/antagonist/gang) - var/area/territory = get_area(target) - new /obj/effect/decal/cleanable/crayon/gang(target,G.gang,"letter",0,user) - to_chat(user, span_notice("You tagged [territory] for your gang!")) - -/obj/item/toy/crayon/spraycan/gang - //desc = "A modified container containing suspicious paint." - charges = 20 - gang = TRUE - pre_noise = FALSE - post_noise = TRUE - -/obj/item/toy/crayon/spraycan/gang/Initialize(mapload, datum/team/gang/G) - .=..() - if(G) - gang = G - paint_color = G.color - update_appearance(UPDATE_ICON) - -/obj/item/toy/crayon/spraycan/gang/examine(mob/user) - . = ..() - if(user.mind && user.mind.has_antag_datum(/datum/antagonist/gang) || isobserver(user)) - . += "This spraycan has been specially modified for tagging territory." - -#undef RANDOM_GRAFFITI -#undef RANDOM_LETTER -#undef RANDOM_NUMBER -#undef RANDOM_ORIENTED -#undef RANDOM_RUNE -#undef RANDOM_ANY - -/obj/item/toy/crayon/spraycan/afterattack(atom/target, mob/user, proximity) - if(!proximity) - return - - if(is_capped) - to_chat(user, span_warning("Take the cap off first!")) - return - - if(check_empty(user)) - return - - if(istype(target, /obj/machinery/light)) - var/obj/machinery/light/light = target - if(actually_paints) - light.add_atom_colour(paint_color, WASHABLE_COLOUR_PRIORITY) - light.bulb_colour = paint_color - light.update() - . = use_charges(user, 2) - ..() diff --git a/yogstation/code/game/objects/items/implants/implant_dusting.dm b/yogstation/code/game/objects/items/implants/implant_dusting.dm index 23c396df37ff..3c920b2b2329 100644 --- a/yogstation/code/game/objects/items/implants/implant_dusting.dm +++ b/yogstation/code/game/objects/items/implants/implant_dusting.dm @@ -122,13 +122,14 @@ to_chat(user, span_notice("\The [src] transforms into \a [reward]!")) if(!user.put_in_hand(reward, hand_index)) reward.forceMove(get_turf(user)) - // Spawn new IAA - if(istype(SSticker.mode, /datum/game_mode/traitor/internal_affairs)) - var/datum/game_mode/traitor/internal_affairs/iaa_mode = SSticker.mode - var/mob/living/new_tot = iaa_mode.create_new_traitor() - if(new_tot) - to_chat(user, span_warning("You feel like someone is watching you... Keep on your guard.")) - message_admins("[ADMIN_LOOKUPFLW(new_tot)] was made into a new IAA by \a [src].") + // Spawn new IAA + // commented out for storytellers + // if(istype(SSticker.mode, ROLE_INTERNAL_AFFAIRS)) + // varROLE_INTERNAL_AFFAIRS/iaa_mode = SSticker.mode + // var/mob/living/new_tot = iaa_mode.create_new_traitor() + // if(new_tot) + // to_chat(user, span_warning("You feel like someone is watching you... Keep on your guard.")) + // message_admins("[ADMIN_LOOKUPFLW(new_tot)] was made into a new IAA by \a [src].") qdel(src) else to_chat(user, span_notice("\The [src] doesn't seem to do anything.")) diff --git a/yogstation/code/game/objects/items/implants/implant_gang.dm b/yogstation/code/game/objects/items/implants/implant_gang.dm deleted file mode 100644 index 27bb6782ab66..000000000000 --- a/yogstation/code/game/objects/items/implants/implant_gang.dm +++ /dev/null @@ -1,60 +0,0 @@ -/obj/item/implant/gang - name = "gang implant" - desc = "Makes you a gangster." - var/datum/team/gang/gang - -/obj/item/implant/gang/Initialize(mapload, setgang) - .=..() - gang = setgang - -/obj/item/implant/gang/Destroy() - gang = null - return ..() - -/obj/item/implant/gang/get_data() - var/dat = {"Implant Specifications:
- Name: Criminal brainwash implant
- Life: A few seconds after injection.
- Important Notes: Illegal
-
- Implant Details:
- Function: Contains a small pod of nanobots that change the host's brain to be loyal to a certain organization.
- Integrity: Implant's EMP function will destroy itself in the process."} - return dat - -/obj/item/implant/gang/implant(mob/living/target, mob/user, silent = FALSE, force = FALSE) - if(!target || !target.mind || target.stat == DEAD) - return FALSE - var/datum/antagonist/gang/G = target.mind.has_antag_datum(/datum/antagonist/gang) - if(G && G.gang == G) - return FALSE // it's pointless - if(..()) - if(ishuman(target)) - - var/success - if(G) - if(!istype(G, /datum/antagonist/gang/boss)) - success = TRUE //Was not a gang boss, convert as usual - target.mind.remove_antag_datum(/datum/antagonist/gang) - else - success = TRUE - - if(HAS_TRAIT(target, TRAIT_MINDSHIELD)) - success = FALSE - - if(!success) - target.visible_message(span_warning("[target] seems to resist the implant!"), span_warning("You feel the influence of your enemies try to invade your mind!")) - return FALSE - else - target.mind.add_antag_datum(/datum/antagonist/gang, gang) - qdel(src) - return TRUE - -/obj/item/implanter/gang - name = "implanter (gang)" - -/obj/item/implanter/gang/Initialize(mapload, gang) - if(!gang) - return INITIALIZE_HINT_QDEL - imp = new /obj/item/implant/gang(src,gang) - .=..() diff --git a/yogstation/code/game/objects/items/implants/implant_infiltrator.dm b/yogstation/code/game/objects/items/implants/implant_infiltrator.dm index d1eb23e0a4b8..d4738a22850b 100644 --- a/yogstation/code/game/objects/items/implants/implant_infiltrator.dm +++ b/yogstation/code/game/objects/items/implants/implant_infiltrator.dm @@ -10,7 +10,7 @@ ADD_TRAIT(src, TRAIT_EMPPROOF_SELF, "innate_empproof") ADD_TRAIT(src, TRAIT_EMPPROOF_CONTENTS, "innate_empproof") var/datum/component/uplink/uplink = AddComponent(/datum/component/uplink, _owner, TRUE, FALSE, null, 20) - uplink.set_gamemode(/datum/game_mode/infiltration) + uplink.set_antagonist(ROLE_INFILTRATOR) alert_radio = new(src) alert_radio.make_syndie() alert_radio.listening = FALSE diff --git a/yogstation/code/game/objects/structures/ghostbeacon.dm b/yogstation/code/game/objects/structures/ghostbeacon.dm index bfa3fe67713f..e2152b9d70ba 100644 --- a/yogstation/code/game/objects/structures/ghostbeacon.dm +++ b/yogstation/code/game/objects/structures/ghostbeacon.dm @@ -44,7 +44,7 @@ // Is it even a problem? var/area/A = get_area(M) if(spawnArea != A) - to_chat(M, span_warner("Your corporeal form gets ripped back!")) + to_chat(M, span_warning("Your corporeal form gets ripped back!")) M.loc = loc /obj/structure/ghostbeacon/attack_ghost(mob/user) diff --git a/yogstation/code/modules/admin/verbs/one_click_antag.dm b/yogstation/code/modules/admin/verbs/one_click_antag.dm index dd6d0a548962..07655832639e 100644 --- a/yogstation/code/modules/admin/verbs/one_click_antag.dm +++ b/yogstation/code/modules/admin/verbs/one_click_antag.dm @@ -1,52 +1,3 @@ -//Darkspawn -/datum/admins/proc/makeDarkspawn() - var/datum/game_mode/darkspawn/temp = new - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - temp.restricted_jobs += temp.protected_jobs - if(CONFIG_GET(flag/protect_assistant_from_antagonist)) - temp.restricted_jobs += "Assistant" - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H - for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if(ROLE_DARKSPAWN in applicant.client.prefs.be_special) - if(!applicant.stat || !applicant.mind) - continue - if(applicant.mind.special_role) - continue - if(is_banned_from(applicant.ckey, "darkspawn") || is_banned_from(applicant.ckey, "Syndicate")) - continue - if(!temp.age_check(applicant.client) || (applicant.job in temp.restricted_jobs)) - continue - if(is_team_darkspawn(applicant)) - continue - candidates += applicant - if(candidates.len) - H = pick(candidates) - if(H.add_darkspawn()) - message_admins("[key_name(usr)] made [ADMIN_LOOKUPFLW(H)] a darkspawn!") - log_admin("[key_name(usr)] made [ADMIN_LOOKUPFLW(H)] a darkspawn!") - return TRUE - return FALSE - -/datum/admins/proc/makeVampire() - var/datum/game_mode/vampire/temp = new - if(CONFIG_GET(flag/protect_roles_from_antagonist)) - temp.restricted_jobs += temp.protected_jobs - if(CONFIG_GET(flag/protect_assistant_from_antagonist)) - temp.restricted_jobs += "Assistant" - var/list/mob/living/carbon/human/candidates = list() - var/mob/living/carbon/human/H - for(var/mob/living/carbon/human/applicant in GLOB.player_list) - if((ROLE_VAMPIRE in applicant.client.prefs.be_special) && !applicant.stat && applicant.mind && !applicant.mind.special_role) - if(!is_banned_from(applicant.ckey, "vampire") && !is_banned_from(applicant.ckey, "Syndicate")) - if(temp.age_check(applicant.client) && !(applicant.job in temp.restricted_jobs) && !is_vampire(applicant)) - candidates += applicant - if(LAZYLEN(candidates)) - H = pick(candidates) - add_vampire(H) - return TRUE - return FALSE - /client/proc/subtlemessage_faction() // Thanks GenericDM very cool set name = "SM to Faction" set desc = "Allows you to send a mass SM to every member of a particular faction or antagonist type." @@ -102,38 +53,3 @@ if("[antagdatum]" == chosen) to_chat(player,text) break - -/datum/admins/proc/makeInfiltratorTeam() - var/datum/game_mode/infiltration/temp = new - var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a infiltration team being sent in?", ROLE_INFILTRATOR, temp) - var/list/mob/dead/observer/chosen = list() - var/mob/dead/observer/theghost = null - - if(LAZYLEN(candidates)) - var/numagents = 5 - var/agentcount = 0 - - for(var/i = 0, i
" for(var/datum/psi_web/ability as anything in class.learned_abilities) . += "[ability.name]
" + + +//////////////////////////////////////////////////////////////////////////////////// +//-------------------------------Mob assign procs---------------------------------// +//////////////////////////////////////////////////////////////////////////////////// +/mob/living/proc/add_darkspawn() + if(!istype(mind)) + return FALSE + return mind.add_antag_datum(/datum/antagonist/darkspawn) + +/mob/living/proc/remove_darkspawn() + if(!istype(mind)) + return FALSE + return mind.remove_antag_datum(/datum/antagonist/darkspawn) + +/mob/living/proc/add_thrall() + if(!istype(mind)) + return FALSE + return mind.add_antag_datum(/datum/antagonist/thrall) + +/mob/living/proc/remove_thrall() + if(!istype(mind)) + return FALSE + return mind.remove_antag_datum(/datum/antagonist/thrall) diff --git a/yogstation/code/modules/antagonists/gang/gang.dm b/yogstation/code/modules/antagonists/gang/gang.dm deleted file mode 100644 index cb8d5bb6d05f..000000000000 --- a/yogstation/code/modules/antagonists/gang/gang.dm +++ /dev/null @@ -1,529 +0,0 @@ -/datum/antagonist/gang - name = "Gangster" - roundend_category = "gangsters" - antag_hud_name = "gangster" - can_coexist_with_others = FALSE - job_rank = ROLE_GANG - antagpanel_category = "Gang" - var/message_name = "Gangster" - var/datum/team/gang/gang - preview_outfit = /datum/outfit/gangster - -/datum/antagonist/gang/can_be_owned(datum/mind/new_owner) - . = ..() - if(.) - if(new_owner.unconvertable) - return FALSE - -/datum/antagonist/gang/apply_innate_effects(mob/living/mob_override) - add_team_hud(mob_override || owner.current) - -/datum/antagonist/gang/add_team_hud(mob/target) - QDEL_NULL(team_hud_ref) - - team_hud_ref = WEAKREF(target.add_alt_appearance( - /datum/atom_hud/alternate_appearance/basic/has_antagonist, - "antag_team_hud_[REF(src)]", - hud_image_on(target), - )) - - var/datum/atom_hud/alternate_appearance/basic/has_antagonist/hud = team_hud_ref.resolve() - - var/list/mob/living/mob_list = list() - for(var/datum/mind/collegues_minds as anything in gang.members) - mob_list += collegues_minds.current - - for (var/datum/atom_hud/alternate_appearance/basic/has_antagonist/antag_hud as anything in GLOB.has_antagonist_huds) - if(!(antag_hud.target in mob_list)) - continue - antag_hud.show_to(target) - hud.show_to(antag_hud.target) - -/datum/antagonist/gang/hud_image_on(mob/hud_loc) - var/image/hud = image(hud_icon, hud_loc, antag_hud_name) - hud.color = gang.color - hud.plane = ABOVE_HUD_PLANE //not quite but needed - return hud - -/datum/antagonist/gang/get_team() - return gang - -/datum/antagonist/gang/greet() - gang.greet_gangster(owner) - -/datum/antagonist/gang/farewell() - if(ishuman(owner.current)) - owner.current.visible_message("[span_deconversion_message("[owner.current] looks like [owner.current.p_theyve()] just remembered [owner.current.p_their()] real allegiance!")]", null, null, null, owner.current) - to_chat(owner, span_userdanger("You are no longer a gangster! Your memory is hazy from the time you were a gangster...the only thing you remember is the name of the one who brainwashed you...")) - -/datum/antagonist/gang/on_gain() - if(!gang) - create_team() - ..() - add_to_gang() - -/datum/antagonist/gang/on_removal() - remove_from_gang() - ..() - -/datum/antagonist/gang/create_team(team) - if(!gang) // add_antag_datum calls create_team, so we need to avoid generating two gangs in that case - if(team) - gang = team - return - var/datum/team/gang/gangteam = pick_n_take(GLOB.possible_gangs) - if(gangteam) - gang = new gangteam - -/datum/antagonist/gang/proc/equip_gang() // Bosses get equipped with their tools - return - -/datum/antagonist/gang/proc/can_be_converted(mob/living/candidate) - if(!candidate.mind) - return FALSE - if(!can_be_owned(candidate.mind)) - return FALSE - var/mob/living/carbon/human/H = candidate - if(!istype(H)) //Can't nonhumans - return FALSE - return TRUE - -/datum/antagonist/gang/proc/promote() // Bump up to boss - var/datum/team/gang/old_gang = gang - var/datum/mind/old_owner = owner - silent = TRUE - owner.remove_antag_datum(/datum/antagonist/gang) - var/datum/antagonist/gang/boss/lieutenant/new_boss = new - new_boss.silent = TRUE - old_owner.add_antag_datum(new_boss,old_gang) - new_boss.silent = FALSE - log_game("[key_name(old_owner)] has been promoted to Lieutenant in the [old_gang.name] Gang") - to_chat(old_owner, "You have been promoted to Lieutenant!") - - -// Admin commands -/datum/antagonist/gang/get_admin_commands() - . = ..() - .["Promote"] = CALLBACK(src, PROC_REF(admin_promote)) - .["Set Influence"] = CALLBACK(src, PROC_REF(admin_adjust_influence)) - .["Set Uniform Influence"] = CALLBACK(src, PROC_REF(admin_adjust_uniform_influence)) - if(gang.domination_time != NOT_DOMINATING) - .["Set domination time left"] = CALLBACK(src, PROC_REF(set_dom_time_left)) - -/datum/antagonist/gang/admin_add(datum/mind/new_owner,mob/admin) - var/new_or_existing = input(admin, "Which gang do you want to be assigned to the user?", "Gangs") as null|anything in list("New","Existing") - if(isnull(new_or_existing)) - return - else if(new_or_existing == "New") - var/newgang = input(admin, "Select a gang, or select random to pick a random one.", "New gang") as null|anything in GLOB.possible_gangs + "Random" - if(isnull(newgang)) - return - else if(newgang == "Random") - var/datum/team/gang/G = pick_n_take(GLOB.possible_gangs) - gang = new G - else - GLOB.possible_gangs -= newgang - gang = new newgang - else - if(!GLOB.gangs.len) // no gangs exist - to_chat(admin, span_danger("No gangs exist, please create a new one instead.")) - return - var/existinggang = input(admin, "Select a gang, or select random to pick a random one.", "Existing gang") as null|anything in GLOB.gangs + "Random" - if(isnull(existinggang)) - return - else if(existinggang == "Random") - gang = pick(GLOB.gangs) - else - gang = existinggang - ..() - return TRUE - -/datum/antagonist/gang/proc/admin_promote(mob/admin) - message_admins("[key_name_admin(admin)] has promoted [owner] to gang boss.") - log_admin("[key_name(admin)] has promoted [owner] to boss.") - promote() - -/datum/antagonist/gang/proc/admin_adjust_influence() - var/inf = input("Influence for [gang.name]","Gang influence", gang.influence) as null | num - if(!isnull(inf)) - gang.influence = inf - message_admins("[key_name_admin(usr)] changed [gang.name]'s influence to [inf].") - log_admin("[key_name(usr)] changed [gang.name]'s influence to [inf].") - -/datum/antagonist/gang/proc/admin_adjust_uniform_influence() - var/inf = input("Uniform Influence for [gang.name]", "Uniform influence", gang.uniform_influence) as null | num - if(!isnull(inf)) - gang.uniform_influence = inf - message_admins("[key_name_admin(usr)] changed [gang.name]'s uniform influence to [inf].") - log_admin("[key_name(usr)] changed [gang.name]'s uniform influence to [inf].") - -/datum/antagonist/gang/proc/add_to_gang() - gang.add_member(owner) - owner.current.log_message("Has been converted to the [gang.name] gang!", INDIVIDUAL_ATTACK_LOG) - -/datum/antagonist/gang/proc/remove_from_gang() - gang.remove_member(owner) - owner.current.log_message("Has been deconverted from the [gang.name] gang!", INDIVIDUAL_ATTACK_LOG) - -/datum/antagonist/gang/proc/set_dom_time_left(mob/admin) - if(gang.domination_time == NOT_DOMINATING) - return // an admin shouldn't need this - var/seconds = input(admin, "Set the time left for the gang to win, in seconds", "Domination time left") as null|num - if(seconds && seconds > 0) - gang.domination_time = world.time + seconds*10 - gang.message_gangtools("Takeover shortened to [gang.domination_time_remaining()] seconds by your Syndicate benefactors.") - -// Boss type. Those can use gang tools to buy items for their gang, in particular the Dominator, used to win the gamemode, along with more gang tools to promote fellow gangsters to boss status. -/datum/antagonist/gang/boss - name = "Gang boss" - antag_hud_name = "gang_boss" - message_name = "Leader" - -/datum/antagonist/gang/boss/on_gain() - ..() - if(gang) - gang.leaders += owner - handle_clown_mutation(owner.current, "Your training has allowed you to overcome your clownish nature, allowing you to wield weapons without harming yourself.") - -/datum/antagonist/gang/boss/on_removal() - if(gang) - gang.leaders -= owner - ..() - -/datum/antagonist/gang/boss/antag_listing_name() - return ..() + "(Boss)" - -/datum/antagonist/gang/boss/equip_gang(gangtool = TRUE, pen = TRUE, spraycan = TRUE, hud = TRUE) // usually has to be called separately - var/mob/living/carbon/human/H = owner.current - if(!istype(H)) - return - - var/list/slots = list ( - "backpack" = ITEM_SLOT_BACKPACK, - "left pocket" = ITEM_SLOT_LPOCKET, - "right pocket" = ITEM_SLOT_RPOCKET, - "hands" = ITEM_SLOT_HANDS - ) - - if(gangtool) - var/obj/item/gangtool/G = new() - var/where = H.equip_in_one_of_slots(G, slots) - if (!where) - to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a Gangtool.") - else - G.register_device(H) - to_chat(H, "The Gangtool in your [where] will allow you to purchase weapons and equipment, send messages to your gang, and recall the emergency shuttle from anywhere on the station.") - to_chat(H, "As the gang boss, you can also promote your gang members to lieutenant. Unlike regular gangsters, Lieutenants cannot be deconverted and are able to use recruitment pens and gangtools.") - - if(pen) - var/obj/item/pen/gang/T = new() - var/where2 = H.equip_in_one_of_slots(T, slots) - if (!where2) - to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a recruitment pen to start.") - else - to_chat(H, "The recruitment pen in your [where2] will help you get your gang started. Stab unsuspecting crew members with it to recruit them.") - - if(spraycan) - var/obj/item/toy/crayon/spraycan/gang/SC = new(null,gang) - var/where3 = H.equip_in_one_of_slots(SC, slots) - if (!where3) - to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a territory spraycan to start.") - else - to_chat(H, "The territory spraycan in your [where3] can be used to claim areas of the station for your gang. The more territory your gang controls, the more influence you get. All gangsters can use these, so distribute them to grow your influence faster.") - - if(hud) - var/obj/item/clothing/glasses/hud/security/chameleon/C = new(null,gang) - var/where4 = H.equip_in_one_of_slots(C, slots) - if (!where4) - to_chat(H, "Your Syndicate benefactors were unfortunately unable to get you a chameleon security HUD.") - else - to_chat(H, "The chameleon security HUD in your [where4] will help you keep track of who is mindshield-implanted, and unable to be recruited.") - -// Admin commands for bosses -/datum/antagonist/gang/boss/admin_add(datum/mind/new_owner,mob/admin) - if(!new_owner.has_antag_datum(parent_type)) - ..() - to_chat(new_owner.current, span_userdanger("You are a member of the [gang.name] Gang leadership now!")) - return - promote() - message_admins("[key_name_admin(admin)] has made [new_owner.current] a boss of the [gang.name] gang.") - log_admin("[key_name(admin)] has made [new_owner.current] a boss of the [gang.name] gang.") - to_chat(new_owner.current, span_userdanger("You are a member of the [gang.name] Gang leadership now!")) - -/datum/antagonist/gang/boss/get_admin_commands() - . = ..() - . -= "Promote" - .["Take gangtool"] = CALLBACK(src, PROC_REF(admin_take_gangtool)) - .["Give gangtool"] = CALLBACK(src, PROC_REF(admin_give_gangtool)) - .["Demote"] = CALLBACK(src, PROC_REF(admin_demote)) - -/datum/antagonist/gang/boss/proc/demote() - var/old_gang = gang - var/datum/mind/old_owner = owner - silent = TRUE - owner.remove_antag_datum(/datum/antagonist/gang/boss) - var/datum/antagonist/gang/new_gangster = new /datum/antagonist/gang() - new_gangster.silent = TRUE - old_owner.add_antag_datum(new_gangster,old_gang) - new_gangster.silent = FALSE - log_game("[key_name(old_owner)] has been demoted to Gangster in the [gang.name] Gang") - to_chat(old_owner, span_userdanger("The gang has been disappointed by your ability to lead! You are a regular gangster now!")) - -/datum/antagonist/gang/boss/proc/admin_take_gangtool(mob/admin) - var/list/L = owner.current.get_contents() - var/obj/item/gangtool/gangtool = locate() in L - if (!gangtool) - to_chat(admin, span_danger("Deleting gangtool failed!")) - return - qdel(gangtool) - -/datum/antagonist/gang/boss/proc/admin_give_gangtool(mob/admin) - equip_gang(TRUE, FALSE, FALSE, FALSE) - -/datum/antagonist/gang/boss/proc/admin_demote(datum/mind/target,mob/user) - message_admins("[key_name_admin(user)] has demoted [owner.current] from gang boss.") - log_admin("[key_name(user)] has demoted [owner.current] from gang boss.") - admin_take_gangtool(user) - demote() - -/datum/antagonist/gang/boss/lieutenant - name = "Gang Lieutenant" - message_name = "Lieutenant" - -#define MAXIMUM_RECALLS 3 -#define INFLUENCE_INTERVAL 1800 -// Gang team datum. This handles the gang itself. -/datum/team/gang - name = "Gang" - member_name = "gangster" - var/hud_entry_num // because if you put something other than a number in GLOB.huds, god have mercy on your fucking soul friend - var/list/leaders = list() // bosses - var/max_leaders = MAX_LEADERS_GANG - var/members_amount = 0 // Counting members - var/list/territories = list() // territories owned by the gang. - var/list/lost_territories = list() // territories lost by the gang. - var/list/new_territories = list() // territories captured by the gang. - var/list/gangtools = list() - var/domination_time = NOT_DOMINATING - var/dom_attempts = INITIAL_DOM_ATTEMPTS - var/color - var/influence = 0 // influence of the gang, based on how many territories they own. Can be used to buy weapons and tools from a gang uplink. - var/uniform_influence = 0 // Influence gained from members wearing uniforms. Counts only to weapons. yogs - var/passive_uniform_income // Passive income for weapons. The more gang members you have, the less you will get. Wear your uniform. - var/winner // Once the gang wins with a dominator, this becomes true. For roundend credits purposes. - var/list/inner_outfits = list() - var/list/outer_outfits = list() - var/next_point_time - var/recalls = MAXIMUM_RECALLS // Once this reaches 0, this gang cannot force recall the shuttle with their gangtool anymore - -/datum/team/gang/New(starting_members) - . = ..() - GLOB.gangs += src - if(starting_members) - if(islist(starting_members)) - for(var/datum/mind/groveboss in starting_members) - leaders += groveboss - var/datum/antagonist/gang/boss/gb = new - groveboss.add_antag_datum(gb, src) - gb.equip_gang() - - else - var/datum/mind/CJ = starting_members - if(istype(CJ)) - leaders += CJ - var/datum/antagonist/gang/boss/bossdatum = new - CJ.add_antag_datum(bossdatum, src) - bossdatum.equip_gang() - next_point_time = world.time + INFLUENCE_INTERVAL - addtimer(CALLBACK(src, PROC_REF(handle_territories)), INFLUENCE_INTERVAL) - -/datum/team/gang/Destroy() - GLOB.gangs -= src - ..() - -/datum/team/gang/roundend_report() - var/list/report = list() - report += span_header("[name]:") - if(winner) - report += span_greentext("The [name] gang was successful!") - for(var/datum/mind/M in leaders) - SSachievements.unlock_achievement(/datum/achievement/greentext/gangleader,M.current) - for(var/datum/mind/M in members) // Leaders are included in this too - SSachievements.unlock_achievement(/datum/achievement/greentext/gang,M.current) // and so get the lower achievement, too - else - report += span_redtext("The [name] gang has failed!") - - report += "The [name] gang bosses were:" - report += printplayerlist(leaders) - report += "The [name] [member_name]s were:" - report += printplayerlist(members-leaders) - - return "
")]
*---------*
" - message += "[new_territories.len] new territories:
[added_names]
" - message += "[lost_territories.len] territories lost:
[lost_names]
" - //Clear the lists - new_territories = list() - lost_territories = list() - var/total_territories = total_claimable_territories() - var/control = round((territories.len/total_territories)*100, 1) - var/uniformed = check_clothing() - message += "Your gang now has [control]% control of the station.
*---------*
" - if(domination_time != NOT_DOMINATING) - var/new_time = max(world.time, domination_time - (uniformed * 4) - (territories.len * 2)) - if(new_time < domination_time) - message += "Takeover shortened by [(domination_time - new_time)*0.1] seconds for defending [territories.len] territories.
" - domination_time = new_time - message += "[domination_time_remaining()] seconds remain in hostile takeover.
" - else - var/new_influence = check_territory_income() - var/new_uniform_influence = check_uniform_income() - if(new_influence != influence) - message += "Gang influence has increased by [new_influence - influence] for defending [territories.len] territories
" - if(new_uniform_influence != uniform_influence) // yogs - message += "Gang supply has increased by [new_uniform_influence - uniform_influence] for having [uniformed] uniformed gangsters
" - influence = new_influence - uniform_influence = new_uniform_influence - message += "Your gang now has [influence] influence and [uniform_influence] supply points.
" - message_gangtools(message) - addtimer(CALLBACK(src, PROC_REF(handle_territories)), INFLUENCE_INTERVAL) - -/datum/team/gang/proc/total_claimable_territories() - var/list/valid_territories = list() - for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION)) //First, collect all area types on the station zlevel - for(var/ar in SSmapping.areas_in_z["[z]"]) - var/area/A = ar - if(!(A.type in valid_territories) && A.valid_territory) - valid_territories |= A.type - return valid_territories.len - -/datum/team/gang/proc/check_territory_income() - var/new_influence = min(999,influence + 15 + territories.len) - return new_influence - -/datum/team/gang/proc/check_uniform_income() - members_amount = 0 // reset so it doesnt just add last cycles to next - count_members() - var/new_uniform_influence = min(999,uniform_influence + passive_uniform_income + (check_clothing() * 5)) // 5 weapon supply points per uniformed gangster + free income per cycle based on members - return new_uniform_influence - -/datum/team/gang/proc/check_clothing() - //Count uniformed gangsters - var/uniformed = 0 - for(var/datum/mind/gangmind in members) - if(ishuman(gangmind.current)) - var/mob/living/carbon/human/gangster = gangmind.current - //Gangster must be alive and on station - if((gangster.stat == DEAD) || (!is_station_level(gangster.z))) - continue - - var/obj/item/clothing/outfit - var/obj/item/clothing/gang_outfit - if(gangster.w_uniform) - outfit = gangster.w_uniform - if(outfit.type in inner_outfits) - gang_outfit = outfit - if(gangster.wear_suit) - outfit = gangster.wear_suit - if(outfit.type in outer_outfits) - gang_outfit = outfit - - if(gang_outfit) - gangster << span_notice("The [src] Gang's influence grows as you wear [gang_outfit].") - uniformed++ - return uniformed - -/datum/team/gang/proc/adjust_influence(value) - influence = max(0, influence + value) - -/datum/team/gang/proc/adjust_uniform_influence(value) - uniform_influence = max(0, uniform_influence + value) - -/datum/outfit/gangster - name = "Gangster (Preview only)" - mask = /obj/item/clothing/mask/cigarette/cigar/havana - uniform = /obj/item/clothing/under/suit/vice - neck = /obj/item/clothing/neck/necklace/dope - head = /obj/item/clothing/head/fedora - -/datum/team/gang/proc/count_members() - for(var/datum/mind/gangmind in members) - if(ishuman(gangmind.current)) - var/mob/living/carbon/human/gangster = gangmind.current - if(gangster.stat != DEAD) - members_amount++ - switch(members_amount) - if(0 to 3) - passive_uniform_income = 15 - if(4 to 5) - passive_uniform_income = 10 - if(6 to INFINITY) - passive_uniform_income = 0 - - -/datum/team/gang/proc/message_gangtools(message) - if(!gangtools.len || !message) - return - for(var/i in gangtools) - var/obj/item/gangtool/tool = i - var/mob/living/mob = get(tool.loc, /mob/living) - if(mob && mob.mind && mob.stat == CONSCIOUS) - var/datum/antagonist/gang/gangster = mob.mind.has_antag_datum(/datum/antagonist/gang) - if(gangster.gang == src) - to_chat(mob, span_warning("[icon2html(tool, mob)] [message]")) - playsound(mob.loc, 'sound/machines/twobeep.ogg', 50, 1) - return - -/datum/team/gang/proc/domination() - domination_time = world.time + determine_domination_time()*10 - SSsecurity_level.set_level(SEC_LEVEL_DELTA) - -/datum/team/gang/proc/determine_domination_time() // calculates the value in seconds (this is the initial domination time!) - var/total_territories = total_claimable_territories() - return max(180,480 - (round((territories.len/total_territories)*100, 1) * 9)) - -/datum/team/gang/proc/domination_time_remaining() // retrieves the value from world.time based deciseconds to seconds - var/diff = domination_time - world.time - return round(diff * 0.1) - - -#undef MAXIMUM_RECALLS -#undef INFLUENCE_INTERVAL diff --git a/yogstation/code/modules/antagonists/gang/gang_datums.dm b/yogstation/code/modules/antagonists/gang/gang_datums.dm deleted file mode 100644 index 7c336579532d..000000000000 --- a/yogstation/code/modules/antagonists/gang/gang_datums.dm +++ /dev/null @@ -1,139 +0,0 @@ - -// Gang datums go here. If you want to create a new gang, you must be sure to edit: -// name -// color (must be a hex, "blue" isn't acceptable due to how spraycans are handled) -// inner_outfits (must be a list() with typepaths of the clothes in it. One is fine, but there is support for multiple: one will be picked at random when bought) -// outer_outfits (same as above) -// You also need to make a gang graffiti, that will go in crayondecal.dmi inside our icons(not tg's), with the same name of the gang it's assigned to. Nothing else,just the icon. -// Those are all required. If one is missed, stuff could break. - -/datum/team/gang/clandestine - name = "Clandestine" - color = "#FF0000" - inner_outfits = list(/obj/item/clothing/under/syndicate/combat) - outer_outfits = list(/obj/item/clothing/suit/jacket) - -/datum/team/gang/prima - name = "Prima" - color = "#FFFF00" - inner_outfits = list(/obj/item/clothing/under/color/yellow) - outer_outfits = list(/obj/item/clothing/suit/hastur) - -/datum/team/gang/zerog - name = "Zero-G" - color = "#C0C0C0" - inner_outfits = list(/obj/item/clothing/under/suit/white) - outer_outfits = list(/obj/item/clothing/suit/hooded/wintercoat) - -/datum/team/gang/max - name = "Max" - color = "#800000" - inner_outfits = list(/obj/item/clothing/under/color/maroon) - outer_outfits = list(/obj/item/clothing/suit/poncho/red) - -/datum/team/gang/blasto - name = "Blasto" - color = "#000080" - inner_outfits = list(/obj/item/clothing/under/suit/navy) - outer_outfits = list(/obj/item/clothing/suit/jacket/miljacket) - -/datum/team/gang/waffle - name = "Waffle" - color = "#808000" - inner_outfits = list(/obj/item/clothing/under/suit/green) - outer_outfits = list(/obj/item/clothing/suit/poncho) - -/datum/team/gang/north - name = "North" - color = "#00FF00" - inner_outfits = list(/obj/item/clothing/under/color/green) - outer_outfits = list(/obj/item/clothing/suit/poncho/green) - -/datum/team/gang/omni - name = "Omni" - color = "#008080" - inner_outfits = list(/obj/item/clothing/under/color/teal) - outer_outfits = list(/obj/item/clothing/suit/chaplainsuit/studentuni) - -/datum/team/gang/newton - name = "Newton" - color = "#A52A2A" - inner_outfits = list(/obj/item/clothing/under/color/brown) - outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings) - -/datum/team/gang/cyber - name = "Cyber" - color = "#808000" - inner_outfits = list(/obj/item/clothing/under/color/lightbrown) - outer_outfits = list(/obj/item/clothing/suit/nemes) - -/datum/team/gang/donk - name = "Donk" - color = "#0000FF" - inner_outfits = list(/obj/item/clothing/under/color/darkblue) - outer_outfits = list(/obj/item/clothing/suit/apron/overalls) - -/datum/team/gang/gene - name = "Gene" - color = "#00FFFF" - inner_outfits = list(/obj/item/clothing/under/color/blue) - outer_outfits = list(/obj/item/clothing/suit/apron) - -/datum/team/gang/gib - name = "Gib" - color = "#bb0a1e" - inner_outfits = list(/obj/item/clothing/under/color/black) - outer_outfits = list(/obj/item/clothing/suit/jacket/leather/overcoat) - -/datum/team/gang/tunnel - name = "Tunnel" - color = "#FF00FF" - inner_outfits = list(/obj/item/clothing/under/costume/villain) - outer_outfits = list(/obj/item/clothing/suit/poncho/ponchoshame) - -/datum/team/gang/diablo - name = "Diablo" - color = "#FF0000" - inner_outfits = list(/obj/item/clothing/under/color/red) - outer_outfits = list(/obj/item/clothing/suit/jacket/leather) - -/datum/team/gang/psyke - name = "Psyke" - color = "#808080" - inner_outfits = list(/obj/item/clothing/under/color/grey) - outer_outfits = list(/obj/item/clothing/suit/toggle/owlwings/griffinwings) - -/datum/team/gang/osiron - name = "Osiron" - color = "#FFFFFF" - inner_outfits = list(/obj/item/clothing/under/color/white) - outer_outfits = list(/obj/item/clothing/suit/toggle/labcoat) - -/datum/team/gang/sirius - name = "Sirius" - color = "#FFC0CB" - inner_outfits = list(/obj/item/clothing/under/color/pink) - -/datum/team/gang/sleepingcarp - name = "Sleeping Carp" - color = "#800080" - inner_outfits = list(/obj/item/clothing/under/color/lightpurple) - outer_outfits = list(/obj/item/clothing/suit/hooded/carp_costume) - -/datum/team/gang/h - name = "H" - color = "#993333" - inner_outfits = list(/obj/item/clothing/under/yogs/psyche) - outer_outfits = list(/obj/item/clothing/suit/yogs/jesus) // perfect - -/datum/team/gang/rigatonifamily - name = "Rigatoni family" - color = "#cc9900" // p a s t a colored - inner_outfits = list(/obj/item/clothing/under/rank/civilian/chef) - outer_outfits = list(/obj/item/clothing/suit/apron/chef) - -/datum/team/gang/weed - name = "Weed" - color = "#66ff33" - inner_outfits = list(/obj/item/clothing/under/color/darkgreen) - outer_outfits = list(/obj/item/clothing/suit/vapeshirt) diff --git a/yogstation/code/modules/antagonists/gang/gang_hud.dm b/yogstation/code/modules/antagonists/gang/gang_hud.dm deleted file mode 100644 index 8eaf6b2ed4ed..000000000000 --- a/yogstation/code/modules/antagonists/gang/gang_hud.dm +++ /dev/null @@ -1,18 +0,0 @@ -/datum/atom_hud/antag/gang - var/color = null - -/datum/atom_hud/antag/gang/add_atom_to_hud(atom/A) - if(!A) - return - var/image/holder = A.hud_list[ANTAG_HUD] - if(holder) - holder.color = color - ..() - -/datum/atom_hud/antag/gang/remove_atom_from_hud(atom/A) - if(!A) - return - var/image/holder = A.hud_list[ANTAG_HUD] - if(holder) - holder.color = null - ..() diff --git a/yogstation/code/modules/events/bad_wizard.dm b/yogstation/code/modules/events/bad_wizard.dm index 25f97d5d3f86..c2dba76dd78f 100644 --- a/yogstation/code/modules/events/bad_wizard.dm +++ b/yogstation/code/modules/events/bad_wizard.dm @@ -1,10 +1,13 @@ /datum/round_event_control/bad_wizard name = "Bad Wizard" typepath = /datum/round_event/ghost_role/bad_wizard - gamemode_blacklist = list("wizard") max_occurrences = 1 min_players = 20 weight = 5 + track = EVENT_TRACK_MAJOR + tags = list(TAG_DESTRUCTIVE, TAG_MAGICAL) + category = EVENT_CATEGORY_ENTITIES + description = "spawns a shitty wizard." /datum/round_event/ghost_role/bad_wizard minimum_required = 1 diff --git a/yogstation/code/modules/events/bureaucratic_error.dm b/yogstation/code/modules/events/bureaucratic_error.dm deleted file mode 100644 index 42a9a9947588..000000000000 --- a/yogstation/code/modules/events/bureaucratic_error.dm +++ /dev/null @@ -1,3 +0,0 @@ -/datum/round_event_control/bureaucratic_error - weight = 2 - gamemode_blacklist = list("cult","revolution") \ No newline at end of file diff --git a/yogstation/code/modules/events/dolphin_migration.dm b/yogstation/code/modules/events/dolphin_migration.dm deleted file mode 100644 index 97edef1f205f..000000000000 --- a/yogstation/code/modules/events/dolphin_migration.dm +++ /dev/null @@ -1,28 +0,0 @@ -//DOLPHIN PORT WOOOOOO -/datum/round_event_control/dolphin_migration - name = "Dolphin Migration" - typepath = /datum/round_event/dolphin_migration - weight = 15 - earliest_start = 10 MINUTES - max_occurrences = 6 - -/datum/round_event/dolphin_migration - announceWhen = 3 - startWhen = 50 - -/datum/round_event/dolphin_migration/setup() - startWhen = rand(40, 60) - -/datum/round_event/dolphin_migration/announce() - priority_announce("Unknown biological entities have been detected near [station_name()], please stand-by.", "Lifesign Alert") - - -/datum/round_event/dolphin_migration/start() - for(var/obj/effect/landmark/carpspawn/C in GLOB.landmarks_list) - if(prob(95)) - new /mob/living/simple_animal/hostile/retaliate/dolphin(C.loc) - else - new /mob/living/simple_animal/hostile/retaliate/dolphin/manatee(C.loc) - - - diff --git a/yogstation/code/modules/events/floorcluwne.dm b/yogstation/code/modules/events/floorcluwne.dm index ea8f699fcb63..c1477c6524a1 100644 --- a/yogstation/code/modules/events/floorcluwne.dm +++ b/yogstation/code/modules/events/floorcluwne.dm @@ -4,6 +4,10 @@ max_occurrences = 1 min_players = 20 weight = 5 + track = EVENT_TRACK_MAJOR + tags = list(TAG_SPOOKY, TAG_ALIEN, TAG_MAGICAL) + category = EVENT_CATEGORY_ENTITIES + description = "a floor cluwne will spawn to seek out victims." /datum/round_event/floor_cluwne/start() var/list/spawn_locs = list() diff --git a/yogstation/code/modules/events/immovable_duck.dm b/yogstation/code/modules/events/immovable_duck.dm index ec75dd4849c1..84b75d030958 100644 --- a/yogstation/code/modules/events/immovable_duck.dm +++ b/yogstation/code/modules/events/immovable_duck.dm @@ -4,24 +4,15 @@ weight = 2 max_occurrences = 3 -/datum/round_event_control/immovable_rod/duck/admin_setup() - if(!check_rights(R_FUN)) - return - - var/aimed = alert("Aimed at current location?","SniperQUACK", "Yes", "No") - if(aimed == "Yes") - special_target = get_turf(usr) - /datum/round_event/immovable_rod/duck/announce(fake) priority_announce("What the duck was that?!", "General Alert") /datum/round_event/immovable_rod/duck/start() - var/datum/round_event_control/immovable_rod/duck/C = control var/startside = pick(GLOB.cardinals) - var/z = pick(SSmapping.levels_by_trait(ZTRAIT_STATION)) - var/turf/startT = spaceDebrisStartLoc(startside, z) - var/turf/endT = spaceDebrisFinishLoc(startside, z) - new /obj/effect/immovablerod/duck(startT, endT, C.special_target) + var/turf/end_turf = get_edge_target_turf(get_random_station_turf(), turn(startside, 180)) + var/turf/start_turf = spaceDebrisStartLoc(startside, end_turf.z) + var/atom/rod = new /obj/effect/immovablerod/duck(start_turf, end_turf, special_target) + announce_to_ghosts(rod) /obj/effect/immovablerod/duck name = "immovable DUCK" diff --git a/yogstation/code/modules/events/infiltrators.dm b/yogstation/code/modules/events/infiltrators.dm index 370fb912cb4f..f4c527b59a14 100644 --- a/yogstation/code/modules/events/infiltrators.dm +++ b/yogstation/code/modules/events/infiltrators.dm @@ -5,12 +5,15 @@ weight = 0 max_occurrences = 1 earliest_start = 30 MINUTES + track = EVENT_TRACK_MAJOR + tags = list(TAG_COMBAT, TAG_EXTERNAL) + category = EVENT_CATEGORY_INVASION + description = "Infiltrators will... infiltrate." + map_flags = EVENT_SPACE_ONLY min_players = 23 - gamemode_blacklist = list("nuclear","wizard","revolution","abduction","infiltration","gang","cult","clockcult","darkspawn","dynamic") - -/datum/round_event_control/infiltrators/canSpawnEvent(players_amt, gamemode) +/datum/round_event_control/infiltrators/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) . = ..() if(!.) return . diff --git a/yogstation/code/modules/events/ion_storm.dm b/yogstation/code/modules/events/ion_storm.dm index 78403ee2ab68..7e49077b4672 100644 --- a/yogstation/code/modules/events/ion_storm.dm +++ b/yogstation/code/modules/events/ion_storm.dm @@ -1,7 +1,6 @@ #define YOGS_ION_FILE "yogs_ion_laws.json" /datum/round_event_control/ion_storm weight = 20 - max_alert = SEC_LEVEL_DELTA /proc/generate_ion_law(ionMessage) if(ionMessage) diff --git a/yogstation/code/modules/events/probabilistic_anomaly.dm b/yogstation/code/modules/events/probabilistic_anomaly.dm index 9346ea438684..608ae18bdea4 100644 --- a/yogstation/code/modules/events/probabilistic_anomaly.dm +++ b/yogstation/code/modules/events/probabilistic_anomaly.dm @@ -2,11 +2,16 @@ name = "Probabilistic Anomaly" typepath = /datum/round_event/prob_anomaly weight = 10 - max_alert = SEC_LEVEL_DELTA + category = EVENT_CATEGORY_ANOMALIES + description = "Sets rng seed for a duration." + min_wizard_trigger_potency = 0 + max_wizard_trigger_potency = 3 + track = EVENT_TRACK_MUNDANE + tags = list(TAG_COMMUNAL, TAG_MAGICAL) /datum/round_event/prob_anomaly - announceWhen = 1 - endWhen = 30 + announce_when = 1 + end_when = 30 var/seed = 0 /datum/round_event/prob_anomaly/announce(fake) @@ -15,7 +20,7 @@ priority_announce(alert) /datum/round_event/prob_anomaly/start() - endWhen = rand(120,600) // About 2 to 10 minutes, more or less + end_when = rand(120,600) // About 2 to 10 minutes, more or less seed = rand(1,1e9) rand_seed(seed) diff --git a/yogstation/code/modules/events/weightless.dm b/yogstation/code/modules/events/weightless.dm deleted file mode 100644 index 15efcfdbc30b..000000000000 --- a/yogstation/code/modules/events/weightless.dm +++ /dev/null @@ -1,30 +0,0 @@ -/datum/round_event_control/weightless - name = "Gravity Systems Failure" - typepath = /datum/round_event/weightless - weight = 15 - -/datum/round_event/weightless - startWhen = 5 - endWhen = 165 - announceWhen = 1 - -/datum/round_event/weightless/setup() - startWhen = rand(0,10) - endWhen = rand(80,120) - -/datum/round_event/weightless/announce() - priority_announce("Warning: Failsafes for the station's artificial gravity arrays have been triggered. Please be aware that if this problem recurs it may result in formation of gravitational anomalies. Nanotrasen wishes to remind you that the unauthorised formation of anomalies within Nanotrasen facilities is strictly prohibited by health and safety regulation [rand(99,9999)][pick("a","b","c")]:subclause[rand(1,20)][pick("a","b","c")].") - -/datum/round_event/weightless/start() - for(var/obj/machinery/gravity_generator/main/station/A in GLOB.machines) - if(A) - A.disable() - -/datum/round_event/weightless/end() - for(var/obj/machinery/gravity_generator/main/station/A in GLOB.machines) - if(A) - if(control && A.on) - control.weight *= 2 - A.enable() - if(announceWhen >= 0) - priority_announce("Artificial gravity arrays are now functioning within normal parameters. Please report any irregularities to your respective head of staff.") diff --git a/yogstation/code/modules/liquids/liquid_effect.dm b/yogstation/code/modules/liquids/liquid_effect.dm index 7ff9affcf2c5..81452af84945 100644 --- a/yogstation/code/modules/liquids/liquid_effect.dm +++ b/yogstation/code/modules/liquids/liquid_effect.dm @@ -43,23 +43,25 @@ /obj/effect/abstract/liquid_turf/proc/process_evaporation() - if(liquid_group.expected_turf_height > LIQUID_ANKLES_LEVEL_HEIGHT) + if(!liquid_group.always_evaporates && (liquid_group.expected_turf_height > LIQUID_ANKLES_LEVEL_HEIGHT)) SSliquids.evaporation_queue -= my_turf return //See if any of our reagents evaporates var/any_change = FALSE + var/always_evaporates = liquid_group.always_evaporates + var/evaporation_multiplier = liquid_group.evaporation_multiplier var/datum/reagent/R //Faster declaration for(var/reagent_type in liquid_group.reagents.reagent_list) R = reagent_type //We evaporate. bye bye - if(initial(R.evaporates)) - var/remove_amount = min((initial(R.evaporation_rate) * GLOB.global_evaporation_rate), R.volume, (liquid_group.reagents_per_turf / length(liquid_group.reagents.reagent_list))) + if(initial(R.evaporates) || always_evaporates) + var/remove_amount = min((initial(R.evaporation_rate) * GLOB.global_evaporation_rate * evaporation_multiplier), R.volume, (liquid_group.reagents_per_turf / length(liquid_group.reagents.reagent_list))) liquid_group.remove_specific(src, remove_amount, R, TRUE) any_change = TRUE R.evaporate(src.loc, remove_amount) if(initial(R.group_evaporation_rate)) - var/remove_amount = min((initial(R.group_evaporation_rate) * GLOB.global_evaporation_rate), liquid_group.total_reagent_volume, (liquid_group.reagents_per_turf / length(liquid_group.reagents.reagent_list))) + var/remove_amount = min((initial(R.group_evaporation_rate) * GLOB.global_evaporation_rate * evaporation_multiplier), liquid_group.total_reagent_volume, (liquid_group.reagents_per_turf / length(liquid_group.reagents.reagent_list))) liquid_group.remove_any(src, remove_amount) any_change = TRUE R.evaporate(src.loc, remove_amount) diff --git a/yogstation/code/modules/liquids/liquid_groups.dm b/yogstation/code/modules/liquids/liquid_groups.dm index 7e1c311b1f57..28d8ecb4e9f5 100644 --- a/yogstation/code/modules/liquids/liquid_groups.dm +++ b/yogstation/code/modules/liquids/liquid_groups.dm @@ -58,6 +58,10 @@ GLOBAL_VAR_INIT(liquid_debug_colors, FALSE) var/list/current_temperature_queue = list() ///do we evaporate var/evaporates = TRUE + /// Liquids in this group will always evaporate regardless of height + var/always_evaporates = FALSE + /// The multiplier added to the evaporation rate of all reagents in this group. + var/evaporation_multiplier = 1 ///can we merge? var/can_merge = TRUE ///number in decimal value that acts as a multiplier to the amount of liquids lost in applications @@ -91,6 +95,11 @@ GLOBAL_VAR_INIT(liquid_debug_colors, FALSE) burning_members = null return ..() +/datum/liquid_group/proc/copy_properties(datum/liquid_group/from) + if(isnull(from)) + return + always_evaporates = from.always_evaporates + evaporation_multiplier = from.evaporation_multiplier ///GROUP CONTROLLING /datum/liquid_group/proc/add_to_group(turf/T) @@ -155,6 +164,8 @@ GLOBAL_VAR_INIT(liquid_debug_colors, FALSE) total_reagent_volume = reagents.total_volume reagents_per_turf = total_reagent_volume / length(members) + always_evaporates ||= otherg.always_evaporates + evaporation_multiplier = (evaporation_multiplier + otherg.evaporation_multiplier) * 0.5 // average the evaporation multipliers qdel(otherg) process_group() @@ -377,6 +388,7 @@ GLOBAL_VAR_INIT(liquid_debug_colors, FALSE) /datum/liquid_group/proc/move_liquid_group(obj/effect/abstract/liquid_turf/member) remove_from_group(member.my_turf) member.liquid_group = new(1, member) + member.liquid_group.copy_properties(src) var/remove_amount = reagents_per_turf / length(reagents.reagent_list) for(var/datum/reagent/reagent_type in reagents.reagent_list) member.liquid_group.reagents.add_reagent(reagent_type, remove_amount, no_react = TRUE) diff --git a/yogstation/code/modules/ninja/suit/ninja.dm b/yogstation/code/modules/ninja/suit/ninja.dm index efe0a62c8816..82db3014ba49 100644 --- a/yogstation/code/modules/ninja/suit/ninja.dm +++ b/yogstation/code/modules/ninja/suit/ninja.dm @@ -57,7 +57,7 @@ . += M /obj/item/clothing/suit/space/space_ninja/lock_suit(mob/living/carbon/human/H) - if(!istype(H) || !is_ninja(H)) + if(!istype(H) || !IS_SPACE_NINJA(H)) return ..() if(!istype(H.wear_mask, /obj/item/clothing/mask/gas/space_ninja)) diff --git a/yogstation/code/modules/storytellers/antag_rep/helper_procs.dm b/yogstation/code/modules/storytellers/antag_rep/helper_procs.dm new file mode 100644 index 000000000000..ccaaa4deb256 --- /dev/null +++ b/yogstation/code/modules/storytellers/antag_rep/helper_procs.dm @@ -0,0 +1,5 @@ +/proc/return_antag_rep_weight(list/candidates) + var/list/returning_list = list() + for(var/client/C in candidates) + returning_list[C] = SSpersistence.antag_rep[C.ckey] + return returning_list diff --git a/yogstation/code/modules/storytellers/config.dm b/yogstation/code/modules/storytellers/config.dm new file mode 100644 index 000000000000..e147533ce202 --- /dev/null +++ b/yogstation/code/modules/storytellers/config.dm @@ -0,0 +1,123 @@ +///Gamemode related configs below +// Point Gain Multipliers +/datum/config_entry/number/mundane_point_gain_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/moderate_point_gain_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/major_point_gain_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/roleset_point_gain_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/objectives_point_gain_multiplier + config_entry_value = 1 + min_val = 0 + +// Roundstart points Multipliers +/datum/config_entry/number/mundane_roundstart_point_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/moderate_roundstart_point_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/major_roundstart_point_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/roleset_roundstart_point_multiplier + config_entry_value = 1 + min_val = 0 + +/datum/config_entry/number/objectives_roundstart_point_multiplier + config_entry_value = 1 + min_val = 0 + +// Point Thresholds +/datum/config_entry/number/mundane_point_threshold + config_entry_value = MUNDANE_POINT_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/moderate_point_threshold + config_entry_value = MODERATE_POINT_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/major_point_threshold + config_entry_value = MAJOR_POINT_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/roleset_point_threshold + config_entry_value = ROLESET_POINT_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/objectives_point_threshold + config_entry_value = OBJECTIVES_POINT_THRESHOLD + integer = TRUE + min_val = 0 + + +/datum/config_entry/flag/allow_storyteller_pop_scaling // Allows storyteller to scale down the event frequency by population + +// Pop scalling thresholds +/datum/config_entry/number/mundane_pop_scale_threshold + config_entry_value = MUNDANE_POP_SCALE_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/moderate_pop_scale_threshold + config_entry_value = MODERATE_POP_SCALE_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/major_pop_scale_threshold + config_entry_value = MAJOR_POP_SCALE_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/roleset_pop_scale_threshold + config_entry_value = ROLESET_POP_SCALE_THRESHOLD + integer = TRUE + min_val = 0 + +/datum/config_entry/number/objectives_pop_scale_threshold + config_entry_value = OBJECTIVES_POP_SCALE_THRESHOLD + integer = TRUE + min_val = 0 + +// Pop scalling penalties +/datum/config_entry/number/mundane_pop_scale_penalty + config_entry_value = MUNDANE_POP_SCALE_PENALTY + integer = TRUE + min_val = 0 + +/datum/config_entry/number/moderate_pop_scale_penalty + config_entry_value = MODERATE_POP_SCALE_PENALTY + integer = TRUE + min_val = 0 + +/datum/config_entry/number/major_pop_scale_penalty + config_entry_value = MAJOR_POP_SCALE_PENALTY + integer = TRUE + min_val = 0 + +/datum/config_entry/number/roleset_pop_scale_penalty + config_entry_value = ROLESET_POP_SCALE_PENALTY + integer = TRUE + min_val = 0 + +/datum/config_entry/number/objectives_pop_scale_penalty + config_entry_value = OBJECTIVES_POP_SCALE_PENALTY + integer = TRUE + min_val = 0 diff --git a/yogstation/code/modules/storytellers/converted_events/_base_event.dm b/yogstation/code/modules/storytellers/converted_events/_base_event.dm new file mode 100644 index 000000000000..c2037274fe3a --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/_base_event.dm @@ -0,0 +1,362 @@ +/datum/round_event_control + ///do we check against the antag cap before attempting a spawn? + var/checks_antag_cap = FALSE + /// List of enemy roles, will check if x amount of these exist exist + var/list/enemy_roles + ///required number of enemies in roles to exist + var/required_enemies = 0 + +/datum/round_event_control/proc/return_failure_string(players_amt) + var/string + if(roundstart && (world.time-SSticker.round_start_time >= 2 MINUTES)) + string += "Roundstart" + if(occurrences >= max_occurrences) + if(string) + string += ",
" + string += "Cap Reached" + if(earliest_start >= world.time-SSticker.round_start_time) + if(string) + string += ",
" + string +="Too Soon" + if(players_amt < min_players) + if(string) + string += ",
" + string += "Lack of players" + if(holidayID && !check_holidays(holidayID)) + if(string) + string += ",
" + string += "Holiday Event" + if(EMERGENCY_ESCAPED_OR_ENDGAMED) + if(string) + string += ",
" + string += "Round End" + if(checks_antag_cap) + if(!roundstart && !SSgamemode.can_inject_antags()) + if(string) + string += ",
" + string += "Too Many Antags" + return string + +/datum/round_event_control/antagonist/return_failure_string(players_amt) + . =..() + if(!check_enemies()) + if(.) + . += ",
" + . += "No Enemies" + if(!check_required()) + if(.) + . += ",
" + . += "No Required" + return . + +/datum/round_event_control/antagonist/solo/return_failure_string(players_amt) + . =..() + + var/antag_amt = get_antag_amount() + var/list/candidates = get_candidates() //we should optimize this + if(length(candidates) < antag_amt) + if(.) + . += ",
" + . += "Not Enough Candidates!" + + return . + +/datum/round_event_control/antagonist + checks_antag_cap = TRUE + track = EVENT_TRACK_ROLESET + ///list of required roles, needed for this to form + var/list/exclusive_roles + /// Protected roles from the antag roll. People will not get those roles if a config is enabled + var/list/protected_roles + /// Restricted roles from the antag roll + var/list/restricted_roles + +/datum/round_event_control/antagonist/proc/check_required() + if(!length(exclusive_roles)) + return TRUE + for (var/mob/M in SSticker.mode.current_players[CURRENT_LIVING_PLAYERS]) + if (M.stat == DEAD) + continue // Dead players cannot count as passing requirements + if(M.mind && (M.mind.assigned_role in exclusive_roles)) + return TRUE + +/datum/round_event_control/antagonist/proc/trim_candidates(list/candidates) + for(var/mob/living/player in candidates) + if(player?.mind?.quiet_round) //yogs change, quiet mode + candidates -= player + continue + + return candidates + +/// Check if our enemy_roles requirement is met, if return_players is set then we will return the list of enemy players instead +/datum/round_event_control/proc/check_enemies(return_players = FALSE) + if(!length(enemy_roles)) + return return_players ? list() : TRUE + + var/job_check = 0 + var/list/enemy_players = list() + // if(roundstart) + // for(var/enemy in enemy_roles) + // var/datum/job/enemy_job = SSjob.GetJob(enemy) + // if(enemy_job && SSjob.assigned_players_by_job[enemy_job.type]) + // job_check += length(SSjob.assigned_players_by_job[enemy_job.type]) + // enemy_players += SSjob.assigned_players_by_job[enemy_job.type] + + if (!roundstart) + for(var/mob/M in SSticker.mode.current_players[CURRENT_LIVING_PLAYERS]) + if (M.stat == DEAD) + continue // Dead players cannot count as opponents + if (M.mind && (M.mind.assigned_role in enemy_roles)) + job_check++ // Checking for "enemies" (such as sec officers). To be counters, they must either not be candidates to that + enemy_players += M + + if(job_check >= required_enemies) + return return_players ? enemy_players : TRUE + return return_players ? enemy_players : FALSE + +/datum/round_event_control/antagonist/New() + . = ..() + if(CONFIG_GET(flag/protect_roles_from_antagonist)) + restricted_roles |= protected_roles + +/datum/round_event_control/antagonist/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) + . = ..() + if(!check_required()) + return FALSE + + if(!.) + return + +/datum/round_event_control/antagonist/solo + typepath = /datum/round_event/antagonist/solo + /// How many baseline antags do we spawn + var/base_antags = 1 + /// How many maximum antags can we spawn + var/maximum_antags = 3 + /// For this many players we'll add 1 up to the maximum antag amount + var/denominator = 25 + /// The antag flag to be used + var/antag_flag + /// The antag datum to be applied + var/antag_datum + /// Prompt players for consent to turn them into antags before doing so. Dont allow this for roundstart. + var/prompted_picking = FALSE + /// A list of extra events to force whenever this one is chosen by the storyteller. + /// Can either be normal list or a weighted list. + var/list/extra_spawned_events + +/datum/round_event_control/antagonist/solo/from_ghosts/get_candidates() + var/round_started = SSticker.HasRoundStarted() + var/midround_antag_pref_arg = round_started ? FALSE : TRUE + + var/list/candidates = SSgamemode.get_candidates(antag_flag, antag_flag, observers = TRUE, midround_antag_pref = midround_antag_pref_arg, restricted_roles = restricted_roles) + candidates = trim_candidates(candidates) + return candidates + +/datum/round_event_control/antagonist/solo/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) + . = ..() + if(!.) + return + var/antag_amt = get_antag_amount() + var/list/candidates = get_candidates() + if(length(candidates) < antag_amt) + return FALSE + +/datum/round_event_control/antagonist/solo/proc/get_antag_amount() + var/people = SSgamemode.get_correct_popcount() + var/amount = base_antags + FLOOR(people / denominator, 1) + return min(amount, maximum_antags) + +/datum/round_event_control/antagonist/solo/proc/get_candidates() + var/round_started = SSticker.HasRoundStarted() + var/new_players_arg = round_started ? FALSE : TRUE + var/living_players_arg = round_started ? TRUE : FALSE + var/midround_antag_pref_arg = round_started ? FALSE : TRUE + + var/list/candidates = SSgamemode.get_candidates(antag_flag, antag_flag, FALSE, new_players_arg, living_players_arg, midround_antag_pref = midround_antag_pref_arg, restricted_roles = restricted_roles, required_roles = exclusive_roles) + candidates = trim_candidates(candidates) + return candidates + +/datum/round_event + var/excute_round_end_reports = FALSE + +/datum/round_event/proc/round_end_report() + return + +/datum/round_event/setup() + . = ..() + if(excute_round_end_reports) + SSgamemode.round_end_data |= src + +/datum/round_event/antagonist + fakeable = FALSE + end_when = 6000 //This is so prompted picking events have time to run //TODO: refactor events so they can be the masters of themselves, instead of relying on some weirdly timed vars + +/datum/round_event/antagonist/solo + // ALL of those variables are internal. Check the control event to change them + /// The antag flag passed from control + var/antag_flag + /// The antag datum passed from control + var/antag_datum + /// The antag count passed from control + var/antag_count + /// The restricted roles (jobs) passed from control + var/list/restricted_roles + /// The minds we've setup in setup() and need to finalize in start() + var/list/setup_minds = list() + /// Whether we prompt the players before picking them. + var/prompted_picking = FALSE //TODO: Implement this + /// DO NOT SET THIS MANUALLY, THIS IS INHERITED FROM THE EVENT CONTROLLER ON NEW + var/list/extra_spawned_events + +/datum/round_event/antagonist/solo/New(my_processing, datum/round_event_control/event_controller) + . = ..() + if(istype(event_controller, /datum/round_event_control/antagonist/solo)) + var/datum/round_event_control/antagonist/solo/antag_event_controller = event_controller + if(antag_event_controller?.extra_spawned_events) + extra_spawned_events = fill_with_ones(antag_event_controller.extra_spawned_events) + +/datum/round_event/antagonist/solo/setup() + var/datum/round_event_control/antagonist/solo/cast_control = control + antag_count = cast_control.get_antag_amount() + antag_flag = cast_control.antag_flag + antag_datum = cast_control.antag_datum + restricted_roles = cast_control.restricted_roles + prompted_picking = cast_control.prompted_picking + var/list/possible_candidates = cast_control.get_candidates() + var/list/candidates = list() + if(cast_control == SSgamemode.current_roundstart_event && length(SSgamemode.roundstart_antag_minds)) + log_storyteller("Running roundstart antagonist assignment, event: [src], roundstart_antag_minds: [english_list(SSgamemode.roundstart_antag_minds)]") + for(var/datum/mind/antag_mind in SSgamemode.roundstart_antag_minds) + if(!antag_mind.current) + log_storyteller("Roundstart antagonist setup error: antag_mind([antag_mind]) in roundstart_antag_minds without a set mob") + continue + candidates += antag_mind.current + SSgamemode.roundstart_antag_minds -= antag_mind + log_storyteller("Roundstart antag_mind, [antag_mind]") + + //guh + var/list/cliented_list = list() + for(var/mob/living/mob as anything in possible_candidates) + cliented_list += mob.client + + var/list/weighted_candidates = return_antag_rep_weight(cliented_list) + + while(length(weighted_candidates) && length(candidates) < antag_count) //we pick_n_take from weighted_candidates so this should be fine + var/client/picked_client = pick_n_take_weighted(weighted_candidates) + + if(!picked_client || QDELETED(picked_client) || !istype(picked_client)) //sanity check + continue + + var/mob/picked_mob = picked_client.mob + + if(!picked_mob || QDELETED(picked_mob) || !istype(picked_mob)) //if the mob has somehow died or disappeared while we were prompting a previous player + continue + + log_storyteller("[prompted_picking ? "Prompted" : "Picked"] antag event ckey: [picked_mob.ckey], antag: [antag_flag], current special role: [picked_mob.mind?.special_role ? picked_mob.mind.special_role : "none"]") + if(prompted_picking) + candidates |= pollCandidates( + Question = "Would you like to be a [cast_control.name]?", + jobbanType = antag_flag, + be_special_flag = antag_flag, + poll_time = 20 SECONDS, + group = list(picked_mob) + ) + else + candidates |= picked_mob + + + for(var/i in 1 to antag_count) + if(!length(candidates)) + message_admins("A roleset event got fewer antags then its antag_count and may not function correctly.") + log_storyteller("A roleset event got fewer antags then its antag_count and may not function correctly.") + break + + var/mob/candidate = pick_n_take(candidates) + if(!candidate || QDELETED(candidate) || !istype(candidate)) //if the mob has somehow died or disappeared while we were prompting a previous player + i-- //don't count this as an antag that was picked + continue + + log_storyteller("Antag event spawned ckey: [candidate.ckey], antag: [antag_flag], current special role: [candidate.mind?.special_role ? candidate.mind.special_role : "none"]") + + if(candidate.client) //I hate this + SSpersistence.antag_rep -= candidate.client.ckey + + if(!candidate.mind) + candidate.mind = new /datum/mind(candidate.key) + + setup_minds += candidate.mind + candidate.mind.special_role = antag_flag + candidate.mind.restricted_roles = restricted_roles + + setup = TRUE + if(LAZYLEN(extra_spawned_events)) + var/event_type = pick_weight(extra_spawned_events) + if(!event_type) + return + var/datum/round_event_control/triggered_event = locate(event_type) in SSgamemode.control + addtimer(CALLBACK(triggered_event, TYPE_PROC_REF(/datum/round_event_control, runEvent), FALSE, null, FALSE, "storyteller"), 1 SECONDS) // wait a second to avoid any potential omnitraitor bs + +/datum/round_event/antagonist/solo/proc/spawn_extra_events() + if(!LAZYLEN(extra_spawned_events)) + return + var/datum/round_event_control/event = pick_weight(extra_spawned_events) + event?.runEvent(random = FALSE, event_cause = "storyteller") + + +/datum/round_event/antagonist/solo/ghost/setup() + var/datum/round_event_control/antagonist/solo/cast_control = control + antag_count = cast_control.get_antag_amount() + antag_flag = cast_control.antag_flag + antag_datum = cast_control.antag_datum + restricted_roles = cast_control.restricted_roles + prompted_picking = cast_control.prompted_picking + var/list/candidates = cast_control.get_candidates() + + //guh + var/list/cliented_list = list() + for(var/mob/living/mob as anything in candidates) + cliented_list += mob.client + + if(prompted_picking) + candidates = pollCandidates( + Question = "Would you like to be a [cast_control.name]?", + jobbanType = antag_flag, + be_special_flag = antag_flag, + poll_time = 20 SECONDS, + group = candidates + ) + + var/list/weighted_candidates = return_antag_rep_weight(candidates) + + for(var/i in 1 to antag_count) + if(!length(weighted_candidates)) + break + + var/client/mob_client = pick_n_take_weighted(weighted_candidates) + var/mob/candidate = mob_client.mob + + if(candidate.client) //I hate this + SSpersistence.antag_rep -= candidate.client.ckey + + if(!candidate.mind) + candidate.mind = new /datum/mind(candidate.key) + + var/mob/living/carbon/human/new_human = makeBody(candidate) + new_human.mind.special_role = antag_flag + new_human.mind.restricted_roles = restricted_roles + setup_minds += new_human.mind + setup = TRUE + + +/datum/round_event/antagonist/solo/start() + for(var/datum/mind/antag_mind as anything in setup_minds) + add_datum_to_mind(antag_mind, antag_mind.current) + +/datum/round_event/antagonist/solo/proc/add_datum_to_mind(datum/mind/antag_mind) + antag_mind.add_antag_datum(antag_datum) + +/datum/round_event/antagonist/solo/ghost/start() + for(var/datum/mind/antag_mind as anything in setup_minds) + add_datum_to_mind(antag_mind) + diff --git a/yogstation/code/modules/storytellers/converted_events/solo/bloodcult.dm b/yogstation/code/modules/storytellers/converted_events/solo/bloodcult.dm new file mode 100644 index 000000000000..ce0c02d5117d --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/bloodcult.dm @@ -0,0 +1,78 @@ +/datum/round_event_control/antagonist/solo/bloodcult + name = "Blood Cult" + tags = list(TAG_SPOOKY, TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG, TAG_MAGICAL) + antag_flag = ROLE_CULTIST + antag_datum = /datum/antagonist/cult + typepath = /datum/round_event/antagonist/solo/bloodcult + shared_occurence_type = SHARED_HIGH_THREAT + restricted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_CHAPLAIN, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + required_enemies = 3 + enemy_roles = list( + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_CHAPLAIN, + ) + base_antags = 2 + maximum_antags = 4 + min_players = 25 + roundstart = TRUE + title_icon = "cult" + earliest_start = 0 SECONDS + weight = 4 + max_occurrences = 1 + +/datum/round_event/antagonist/solo/bloodcult + excute_round_end_reports = TRUE + end_when = 60000 + var/static/datum/team/cult/main_cult + +/datum/round_event/antagonist/solo/bloodcult/setup() + . = ..() + if(!main_cult) + main_cult = new() + +/datum/round_event/antagonist/solo/bloodcult/start() + . = ..() + main_cult.setup_objectives() + +/datum/round_event/antagonist/solo/bloodcult/add_datum_to_mind(datum/mind/antag_mind) + var/datum/antagonist/cult/new_cultist = new antag_datum() + new_cultist.cult_team = main_cult + new_cultist.give_equipment = TRUE + antag_mind.add_antag_datum(new_cultist) + +//TEMP REMOVAL FOR TESTING +/*/datum/round_event/antagonist/solo/bloodcult/round_end_report() + if(main_cult.check_cult_victory()) + SSticker.mode_result = "win - cult win" + SSticker.news_report = CULT_SUMMON + return + + SSticker.mode_result = "loss - staff stopped the cult" + + if(main_cult.size_at_maximum == 0) + CRASH("Cult team existed with a size_at_maximum of 0 at round end!") + + // If more than a certain ratio of our cultists have escaped, give the "cult escape" resport. + // Otherwise, give the "cult failure" report. + var/ratio_to_be_considered_escaped = 0.5 + var/escaped_cultists = 0 + for(var/datum/mind/escapee as anything in main_cult.members) + if(considered_escaped(escapee)) + escaped_cultists++ + + SSticker.news_report = (escaped_cultists / main_cult.size_at_maximum) >= ratio_to_be_considered_escaped ? CULT_ESCAPE : CULT_FAILURE*/ diff --git a/yogstation/code/modules/storytellers/converted_events/solo/bloodsuckers.dm b/yogstation/code/modules/storytellers/converted_events/solo/bloodsuckers.dm new file mode 100644 index 000000000000..8c7bd1a1f45a --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/bloodsuckers.dm @@ -0,0 +1,39 @@ +/datum/round_event_control/antagonist/solo/bloodsucker + antag_flag = ROLE_BLOODSUCKER + tags = list(TAG_COMBAT, TAG_MAGICAL) + antag_datum = /datum/antagonist/bloodsucker + protected_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG, + ) + min_players = 20 + weight = 10 + maximum_antags = 2 + +/datum/round_event_control/antagonist/solo/bloodsucker/roundstart + name = "Bloodsuckers" + roundstart = TRUE + title_icon = "cult" + earliest_start = 0 SECONDS + +/datum/round_event_control/antagonist/solo/bloodsucker/midround + typepath = /datum/round_event/antagonist/solo/bloodsucker + antag_flag = ROLE_BLOODSUCKERBREAKOUT + name = "Bloodsucker Breakout" + max_occurrences = 1 + +/datum/round_event/antagonist/solo/bloodsucker/add_datum_to_mind(datum/mind/antag_mind) + var/datum/antagonist/bloodsucker/bloodsuckerdatum = antag_mind.make_bloodsucker() + bloodsuckerdatum.bloodsucker_level_unspent = rand(2,3) diff --git a/yogstation/code/modules/storytellers/converted_events/solo/brother.dm b/yogstation/code/modules/storytellers/converted_events/solo/brother.dm new file mode 100644 index 000000000000..aa5e2446d8d1 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/brother.dm @@ -0,0 +1,55 @@ +/datum/round_event_control/antagonist/solo/brother + antag_flag = ROLE_BROTHER + antag_datum = /datum/antagonist/brother + typepath = /datum/round_event/antagonist/solo/brother + tags = list(TAG_COMBAT, TAG_TEAM_ANTAG, TAG_CREW_ANTAG) + protected_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG + ) + enemy_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_SECURITY, + JOB_DETECTIVE, + JOB_WARDEN, + JOB_SECURITY_OFFICER, + ) + min_players = 10 //need at least some players since it IS blood brothers + required_enemies = 1 + weight = 12 + maximum_antags = 2 + cost = 0.45 // so it doesn't eat up threat for a relatively low-threat antag + +/datum/round_event_control/antagonist/solo/brother/roundstart + name = "Blood Brothers" + roundstart = TRUE + earliest_start = 0 SECONDS + min_players = 15 //need more people because this always spawns another antag + extra_spawned_events = list( + /datum/round_event_control/antagonist/solo/traitor/roundstart = 12, + /datum/round_event_control/antagonist/solo/bloodsucker/roundstart = 4, + /datum/round_event_control/antagonist/solo/heretic/roundstart = 2, + ) + +/datum/round_event_control/antagonist/solo/brother/midround + name = "Sleeper Agents (Blood Brothers)" + required_enemies = 2 + min_players = 15 //need more people because this after something else has already spawned + +/datum/round_event/antagonist/solo/brother/add_datum_to_mind(datum/mind/antag_mind) + var/datum/team/brother_team/team = new + team.add_member(antag_mind) + team.forge_brother_objectives() + antag_mind.add_antag_datum(/datum/antagonist/brother, team) diff --git a/yogstation/code/modules/storytellers/converted_events/solo/changeling.dm b/yogstation/code/modules/storytellers/converted_events/solo/changeling.dm new file mode 100644 index 000000000000..a3acce20b0a5 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/changeling.dm @@ -0,0 +1,34 @@ +/datum/round_event_control/antagonist/solo/changeling + antag_flag = ROLE_CHANGELING + tags = list(TAG_COMBAT, TAG_ALIEN) + antag_datum = /datum/antagonist/changeling + protected_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG, + ) + min_players = 15 + weight = 9 + shared_occurence_type = SHARED_CHANGELING + +/datum/round_event_control/antagonist/solo/changeling/roundstart + name = "Changelings" + roundstart = TRUE + title_icon = "changeling" + earliest_start = 0 + maximum_antags = 1 + +/datum/round_event_control/antagonist/solo/changeling/midround + name = "Genome Awakening (Changelings)" + max_occurrences = 2 diff --git a/yogstation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm b/yogstation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm new file mode 100644 index 000000000000..d7c2cdbc4d03 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/clockwork_cult.dm @@ -0,0 +1,43 @@ +/datum/round_event_control/antagonist/solo/clockcult + name = "Clock Cult" + tags = list(TAG_SPOOKY, TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG, TAG_EXTERNAL, TAG_MAGICAL) + antag_flag = ROLE_SERVANT_OF_RATVAR + antag_datum = /datum/antagonist/clockcult + typepath = /datum/round_event/antagonist/solo/clockcult + shared_occurence_type = SHARED_HIGH_THREAT + restricted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_CHAPLAIN, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + enemy_roles = list( + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_CHAPLAIN, + ) + required_enemies = 3 + base_antags = 3 + maximum_antags = 4 + min_players = 25 + roundstart = TRUE + title_icon = "clockcult" + earliest_start = 0 SECONDS + weight = 4 + max_occurrences = 0 + +/datum/round_event/antagonist/solo/clockcult + end_when = 60000 + +/datum/round_event/antagonist/solo/clockcult/add_datum_to_mind(datum/mind/antag_mind) + antag_mind.special_role = ROLE_SERVANT_OF_RATVAR + antag_mind.add_antag_datum(antag_datum) diff --git a/yogstation/code/modules/storytellers/converted_events/solo/clown_operative.dm b/yogstation/code/modules/storytellers/converted_events/solo/clown_operative.dm new file mode 100644 index 000000000000..d356a738a3cf --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/clown_operative.dm @@ -0,0 +1,150 @@ +/datum/round_event_control/antagonist/solo/clown_operative + name = "Circus Assault" + tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG, TAG_EXTERNAL) + antag_flag = ROLE_CLOWNOP + antag_datum = /datum/antagonist/nukeop/clownop + typepath = /datum/round_event/antagonist/solo/clown_operative + shared_occurence_type = SHARED_HIGH_THREAT + restricted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_RESEARCH_DIRECTOR, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + base_antags = 3 + maximum_antags = 5 + enemy_roles = list( + JOB_AI, + JOB_CYBORG, + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + ) + required_enemies = 5 + // I give up, just there should be enough heads with 35 players... + min_players = 35 + roundstart = TRUE + earliest_start = 0 SECONDS + weight = 1 //these are meant to be very rare + max_occurrences = 1 + +/datum/round_event/antagonist/solo/clown_operative + excute_round_end_reports = TRUE + end_when = 60000 /// we will end on our own when revs win + var/static/datum/team/nuclear/nuke_team + var/set_leader = FALSE + var/required_role = ROLE_CLOWNOP + +/datum/round_event/antagonist/solo/clown_operative/setup() + for(var/obj/machinery/nuclearbomb/syndicate/S in GLOB.nuke_list) + var/turf/T = get_turf(S) + if(T) + qdel(S) + new /obj/machinery/nuclearbomb/syndicate/bananium(T) + return ..() + +/datum/round_event/antagonist/solo/clown_operative/add_datum_to_mind(datum/mind/antag_mind) + var/mob/living/current_mob = antag_mind.current + SSjob.FreeRole(antag_mind.assigned_role) + var/list/items = current_mob.get_equipped_items(TRUE) + current_mob.unequip_everything() + for(var/obj/item/item as anything in items) + qdel(item) + + antag_mind.special_role = ROLE_CLOWNOP + + var/datum/mind/most_experienced = get_most_experienced(setup_minds, required_role) + if(!most_experienced) + most_experienced = antag_mind + + if(!set_leader) + set_leader = TRUE + var/datum/antagonist/nukeop/leader/clownop/leader_antag_datum = new() + nuke_team = leader_antag_datum.nuke_team + most_experienced.add_antag_datum(leader_antag_datum) + + if(antag_mind == most_experienced) + return + + var/datum/antagonist/nukeop/new_op = new antag_datum() + antag_mind.add_antag_datum(new_op) + + +/datum/round_event/antagonist/solo/clown_operative/round_end_report() + var/result = nuke_team.get_result() + switch(result) + if(NUKE_RESULT_FLUKE) + SSticker.mode_result = "loss - syndicate nuked - disk secured" + SSticker.news_report = NUKE_SYNDICATE_BASE + if(NUKE_RESULT_NUKE_WIN) + SSticker.mode_result = "win - syndicate nuke" + SSticker.news_report = STATION_DESTROYED_NUKE + if(NUKE_RESULT_NOSURVIVORS) + SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time" + SSticker.news_report = STATION_DESTROYED_NUKE + if(NUKE_RESULT_WRONG_STATION) + SSticker.mode_result = "halfwin - blew wrong station" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_WRONG_STATION_DEAD) + SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD) + SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_CREW_WIN) + SSticker.mode_result = "loss - evacuation - disk secured" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_DISK_LOST) + SSticker.mode_result = "halfwin - evacuation - disk not secured" + SSticker.news_report = OPERATIVE_SKIRMISH + if(NUKE_RESULT_DISK_STOLEN) + SSticker.mode_result = "halfwin - detonation averted" + SSticker.news_report = OPERATIVE_SKIRMISH + else + SSticker.mode_result = "halfwin - interrupted" + SSticker.news_report = OPERATIVE_SKIRMISH + + +/datum/outfit/syndicate/clownop + name = "Clown Operative - Basic" + uniform = /obj/item/clothing/under/syndicate + shoes = /obj/item/clothing/shoes/clown_shoes/combat + mask = /obj/item/clothing/mask/gas/clown_hat + gloves = /obj/item/clothing/gloves/combat + back = /obj/item/storage/backpack/clown + ears = /obj/item/radio/headset/syndicate/alt + l_pocket = /obj/item/pinpointer/nuke/syndicate + r_pocket = /obj/item/bikehorn + id = /obj/item/card/id/syndicate + belt = /obj/item/modular_computer/tablet/pda/preset/clown + backpack_contents = list(/obj/item/kitchen/knife/combat/survival,\ + /obj/item/reagent_containers/spray/waterflower/lube) + implants = list(/obj/item/implant/sad_trombone) + + uplink_type = /obj/item/uplink/clownop + +/datum/outfit/syndicate/clownop/no_crystals + tc = 0 + +/datum/outfit/syndicate/clownop/post_equip(mob/living/carbon/human/H, visualsOnly = FALSE) + ..() + if(visualsOnly) + return + H.dna.add_mutation(CLOWNMUT) + +/datum/outfit/syndicate/clownop/leader + name = "Clown Operative Leader - Basic" + id = /obj/item/card/id/syndicate/nuke_leader + gloves = /obj/item/clothing/gloves/krav_maga/combatglovesplus + r_hand = /obj/item/nuclear_challenge/clownops + command_radio = TRUE diff --git a/yogstation/code/modules/storytellers/converted_events/solo/darkspawn.dm b/yogstation/code/modules/storytellers/converted_events/solo/darkspawn.dm new file mode 100644 index 000000000000..22052f830859 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/darkspawn.dm @@ -0,0 +1,57 @@ +/datum/round_event_control/antagonist/solo/darkspawn + name = "Darkspawns" + tags = list(TAG_SPOOKY, TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG, TAG_MAGICAL) + antag_flag = ROLE_DARKSPAWN + antag_datum = /datum/antagonist/darkspawn + typepath = /datum/round_event/antagonist/solo/darkspawn + shared_occurence_type = SHARED_HIGH_THREAT + restricted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_CHAPLAIN, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + required_enemies = 3 + enemy_roles = list( + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_CHAPLAIN, + ) + base_antags = 2 + maximum_antags = 4 + min_players = 25 + roundstart = TRUE + title_icon = "darkspawn" + earliest_start = 0 SECONDS + denominator = 30 //slightly more people for additional darkspawns + weight = 4 + max_occurrences = 1 + +/datum/round_event/antagonist/solo/darkspawn + excute_round_end_reports = TRUE + end_when = 60000 + var/static/datum/team/darkspawn/dark_team + +/datum/round_event/antagonist/solo/darkspawn/setup() + . = ..() + if(!dark_team) + dark_team = new() + dark_team.update_objectives() + GLOB.thrallnet.name = "Thrall net" + +/datum/round_event/antagonist/solo/darkspawn/start() + . = ..() + dark_team.update_objectives() + +/datum/round_event_control/antagonist/solo/darkspawn/midround //just for admin spawning + weight = 0 + roundstart = FALSE diff --git a/yogstation/code/modules/storytellers/converted_events/solo/ghosts/nuclear_operative_ghost.dm b/yogstation/code/modules/storytellers/converted_events/solo/ghosts/nuclear_operative_ghost.dm new file mode 100644 index 000000000000..f04d5f85b08f --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/ghosts/nuclear_operative_ghost.dm @@ -0,0 +1,106 @@ +/datum/round_event_control/antagonist/solo/from_ghosts/nuclear_operative + name = "Nuclear Emergency" + tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG, TAG_EXTERNAL) + antag_datum = /datum/antagonist/nukeop + typepath = /datum/round_event/antagonist/solo/ghost/nuclear_operative + restricted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_RESEARCH_DIRECTOR, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + base_antags = 3 + maximum_antags = 4 + enemy_roles = list( + JOB_AI, + JOB_CYBORG, + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + ) + required_enemies = 5 + // I give up, just there should be enough heads with 35 players... + min_players = 35 + earliest_start = 60 MINUTES + weight = 4 + max_occurrences = 1 + prompted_picking = TRUE + +/datum/round_event/antagonist/solo/ghost/nuclear_operative + excute_round_end_reports = TRUE + var/static/datum/team/nuclear/nuke_team + var/set_leader = FALSE + var/required_role = ROLE_OPERATIVE + var/datum/mind/most_experienced + +/datum/round_event/antagonist/solo/ghost/nuclear_operative/add_datum_to_mind(datum/mind/antag_mind) + if(most_experienced == antag_mind) + return + var/mob/living/current_mob = antag_mind.current + var/list/items = current_mob.get_equipped_items(TRUE) + current_mob.unequip_everything() + for(var/obj/item/item as anything in items) + qdel(item) + + if(!most_experienced) + most_experienced = get_most_experienced(setup_minds, required_role) + antag_mind.special_role = ROLE_OPERATIVE + + if(!most_experienced) + most_experienced = antag_mind + + if(!set_leader) + set_leader = TRUE + var/datum/antagonist/nukeop/leader/leader_antag_datum = most_experienced.add_antag_datum(/datum/antagonist/nukeop/leader) + nuke_team = leader_antag_datum.nuke_team + + if(antag_mind == most_experienced) + return + + var/datum/antagonist/nukeop/new_op = new antag_datum() + antag_mind.add_antag_datum(new_op) + +//this might be able to be kept as just calling parent +/datum/round_event/antagonist/solo/ghost/nuclear_operative/round_end_report() + var/result = nuke_team.get_result() + switch(result) + if(NUKE_RESULT_FLUKE) + SSticker.mode_result = "loss - syndicate nuked - disk secured" + SSticker.news_report = NUKE_SYNDICATE_BASE + if(NUKE_RESULT_NUKE_WIN) + SSticker.mode_result = "win - syndicate nuke" + SSticker.news_report = STATION_DESTROYED_NUKE + if(NUKE_RESULT_NOSURVIVORS) + SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time" + SSticker.news_report = STATION_DESTROYED_NUKE + if(NUKE_RESULT_WRONG_STATION) + SSticker.mode_result = "halfwin - blew wrong station" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_WRONG_STATION_DEAD) + SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD) + SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_CREW_WIN) + SSticker.mode_result = "loss - evacuation - disk secured" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_DISK_LOST) + SSticker.mode_result = "halfwin - evacuation - disk not secured" + SSticker.news_report = OPERATIVE_SKIRMISH + if(NUKE_RESULT_DISK_STOLEN) + SSticker.mode_result = "halfwin - detonation averted" + SSticker.news_report = OPERATIVE_SKIRMISH + else + SSticker.mode_result = "halfwin - interrupted" + SSticker.news_report = OPERATIVE_SKIRMISH diff --git a/yogstation/code/modules/storytellers/converted_events/solo/ghosts/wizard.dm b/yogstation/code/modules/storytellers/converted_events/solo/ghosts/wizard.dm new file mode 100644 index 000000000000..4cc8c20d2d14 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/ghosts/wizard.dm @@ -0,0 +1,38 @@ +/datum/round_event_control/antagonist/solo/from_ghosts/wizard + name = "Ghost Wizard" + tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_EXTERNAL, TAG_MAGICAL) + typepath = /datum/round_event/antagonist/solo/ghost/wizard + antag_flag = ROLE_WIZARD + antag_datum = /datum/antagonist/wizard + restricted_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_SECURITY, + ) // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted + maximum_antags = 1 + enemy_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_CHAPLAIN, + ) + required_enemies = 5 + weight = 2 + min_players = 35 + max_occurrences = 1 + prompted_picking = TRUE + +/datum/round_event_control/antagonist/solo/from_ghosts/wizard/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) + . = ..() + if(!.) + return + if(!length(GLOB.wizardstart)) + return FALSE + +/datum/round_event/antagonist/solo/ghost/wizard + +/datum/round_event/antagonist/solo/ghost/wizard/add_datum_to_mind(datum/mind/antag_mind) + . = ..() + antag_mind.current.forceMove(pick(GLOB.wizardstart)) diff --git a/yogstation/code/modules/storytellers/converted_events/solo/heretic.dm b/yogstation/code/modules/storytellers/converted_events/solo/heretic.dm new file mode 100644 index 000000000000..3fc4b62394a6 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/heretic.dm @@ -0,0 +1,31 @@ +/datum/round_event_control/antagonist/solo/heretic + antag_flag = ROLE_HERETIC + tags = list(TAG_COMBAT, TAG_SPOOKY, TAG_MAGICAL) + antag_datum = /datum/antagonist/heretic + protected_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG, + ) + weight = 6 + min_players = 15 + +/datum/round_event_control/antagonist/solo/heretic/roundstart + name = "Heretics" + roundstart = TRUE + earliest_start = 0 + +/datum/round_event_control/antagonist/solo/heretic/midround + name = "Midround Heretics" diff --git a/yogstation/code/modules/storytellers/converted_events/solo/malf.dm b/yogstation/code/modules/storytellers/converted_events/solo/malf.dm new file mode 100644 index 000000000000..63c6869dc36c --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/malf.dm @@ -0,0 +1,47 @@ +// /datum/round_event_control/antagonist/solo/malf +// antag_datum = /datum/antagonist/malf_ai +// tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_ALIEN) //not exactly alien but close enough +// antag_flag = ROLE_MALF +// enemy_roles = list( +// JOB_CHEMIST, +// JOB_CHIEF_ENGINEER, +// JOB_HEAD_OF_SECURITY, +// JOB_RESEARCH_DIRECTOR, +// JOB_SCIENTIST, +// JOB_ROBOTICIST, +// JOB_SECURITY_OFFICER, +// JOB_WARDEN, +// JOB_BRIG_PHYSICIAN, +// ) +// shared_occurence_type = SHARED_HIGH_THREAT +// maximum_antags = 1 +// exclusive_roles = list(JOB_AI) +// required_enemies = 4 +// weight = 6 +// min_players = 35 +// max_occurrences = 1 + +// /datum/round_event_control/antagonist/solo/malf/trim_candidates(list/candidates) +// for(var/mob/living/player in candidates) +// if(!isAI(player)) +// candidates -= player +// continue + +// if(is_centcom_level(player.z)) +// candidates -= player +// continue + +// if(player.mind && (player.mind.special_role || player.mind.antag_datums?.len > 0)) +// candidates -= player + +// return candidates + +// /datum/round_event_control/antagonist/solo/malf/midround +// name = "Malfunctioning AI Midround" +// antag_flag = ROLE_MALF_MIDROUND +// weight = 2 + +// /datum/round_event_control/antagonist/solo/malf/roundstart +// name = "Roundstart Malf AI" +// roundstart = TRUE +// earliest_start = 0 diff --git a/yogstation/code/modules/storytellers/converted_events/solo/monsterhunter.dm b/yogstation/code/modules/storytellers/converted_events/solo/monsterhunter.dm new file mode 100644 index 000000000000..7dd372905ea9 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/monsterhunter.dm @@ -0,0 +1,79 @@ +#define MINIMUM_MONSTERS_REQUIRED 2 + +GLOBAL_LIST_INIT(monster_hunter_prey_antags, typecacheof(list( + /datum/antagonist/bloodsucker, + /datum/antagonist/changeling, + /datum/antagonist/heretic, + /datum/antagonist/vampire, + /datum/antagonist/darkspawn +))) + +/proc/is_monster_hunter_prey(datum/mind/victim) + . = FALSE + if(isliving(victim)) + var/mob/living/living_victim = victim + victim = living_victim.mind + if(!istype(victim) || QDELING(victim)) + return FALSE + for(var/datum/antagonist/antag as anything in victim.antag_datums) + if(is_type_in_typecache(antag, GLOB.monster_hunter_prey_antags)) + return TRUE + +/proc/get_all_monster_hunter_prey(include_dead = FALSE) + . = list() + for(var/datum/antagonist/monster as anything in GLOB.antagonists) + if(QDELETED(monster?.owner?.current) || (!include_dead && monster.owner.current.stat == DEAD)) + continue + if(is_type_in_typecache(monster, GLOB.monster_hunter_prey_antags)) + . += monster.owner + + +/datum/round_event_control/antagonist/solo/monsterhunter + name = "Monster Hunters" + track = EVENT_TRACK_ROLESET + antag_flag = ROLE_MONSTERHUNTER + tags = list(TAG_MAGICAL, TAG_TARGETED, TAG_COMBAT, TAG_CREW_ANTAG) + antag_datum = /datum/antagonist/monsterhunter + protected_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG, + ) + min_players = 10 //no required enemies due to instead needing enemy antags + weight = 25 // high weight as its a threat + maximum_antags = 1 + earliest_start = 35 MINUTES + prompted_picking = TRUE + max_occurrences = 1 + checks_antag_cap = FALSE //it literally relies on other antags existing, so it should bypass the cap + +/datum/round_event_control/antagonist/solo/monsterhunter/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) + . = ..() + if(!.) + return + + var/count = 0 + for(var/datum/antagonist/monster as anything in GLOB.antagonists) + if(QDELETED(monster?.owner?.current) || monster.owner.current.stat == DEAD) + continue + + if(is_type_in_typecache(monster, GLOB.monster_hunter_prey_antags)) + count++ + + if(MINIMUM_MONSTERS_REQUIRED > count) + return FALSE + + return ..() + +#undef MINIMUM_MONSTERS_REQUIRED diff --git a/yogstation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm b/yogstation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm new file mode 100644 index 000000000000..2bd0376b05cd --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/nuclear_operative.dm @@ -0,0 +1,118 @@ +/datum/round_event_control/antagonist/solo/nuclear_operative + name = "Nuclear Assault" + tags = list(TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG, TAG_EXTERNAL) + antag_flag = ROLE_OPERATIVE + antag_datum = /datum/antagonist/nukeop + typepath = /datum/round_event/antagonist/solo/nuclear_operative + shared_occurence_type = SHARED_HIGH_THREAT + restricted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_RESEARCH_DIRECTOR, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + base_antags = 2 + maximum_antags = 5 + enemy_roles = list( + JOB_AI, + JOB_CYBORG, + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + ) + required_enemies = 3 + min_players = 20 + roundstart = TRUE + title_icon = "nukeops" + earliest_start = 0 SECONDS + weight = 4 + max_occurrences = 3 + +/datum/round_event/antagonist/solo/nuclear_operative + excute_round_end_reports = TRUE + var/static/datum/team/nuclear/nuke_team + var/set_leader = FALSE + var/required_role = ROLE_OPERATIVE + var/datum/mind/most_experienced + +/datum/round_event/antagonist/solo/nuclear_operative/add_datum_to_mind(datum/mind/antag_mind) + if(most_experienced == antag_mind) + return + var/mob/living/current_mob = antag_mind.current + SSjob.FreeRole(antag_mind.assigned_role) + var/list/items = current_mob.get_equipped_items(TRUE) + current_mob.unequip_everything() + for(var/obj/item/item as anything in items) + qdel(item) + + if(!most_experienced) + most_experienced = get_most_experienced(setup_minds, required_role) + + if(!most_experienced) + most_experienced = antag_mind + + if(!set_leader) + set_leader = TRUE + if(antag_mind != most_experienced) + var/mob/living/leader_mob = most_experienced.current + SSjob.FreeRole(most_experienced.assigned_role) + var/list/leader_items = leader_mob.get_equipped_items(TRUE) + leader_mob.unequip_everything() + for(var/obj/item/item as anything in leader_items) + qdel(item) + most_experienced.special_role = ROLE_OPERATIVE + var/datum/antagonist/nukeop/leader/leader_antag_datum = most_experienced.add_antag_datum(/datum/antagonist/nukeop/leader) + nuke_team = leader_antag_datum.nuke_team + + if(antag_mind == most_experienced) + return + + antag_mind.special_role = ROLE_OPERATIVE + + var/datum/antagonist/nukeop/new_op = new antag_datum() + antag_mind.add_antag_datum(new_op) + + +/datum/round_event/antagonist/solo/nuclear_operative/round_end_report() + var/result = nuke_team.get_result() + switch(result) + if(NUKE_RESULT_FLUKE) + SSticker.mode_result = "loss - syndicate nuked - disk secured" + SSticker.news_report = NUKE_SYNDICATE_BASE + if(NUKE_RESULT_NUKE_WIN) + SSticker.mode_result = "win - syndicate nuke" + SSticker.news_report = STATION_DESTROYED_NUKE + if(NUKE_RESULT_NOSURVIVORS) + SSticker.mode_result = "halfwin - syndicate nuke - did not evacuate in time" + SSticker.news_report = STATION_DESTROYED_NUKE + if(NUKE_RESULT_WRONG_STATION) + SSticker.mode_result = "halfwin - blew wrong station" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_WRONG_STATION_DEAD) + SSticker.mode_result = "halfwin - blew wrong station - did not evacuate in time" + SSticker.news_report = NUKE_MISS + if(NUKE_RESULT_CREW_WIN_SYNDIES_DEAD) + SSticker.mode_result = "loss - evacuation - disk secured - syndi team dead" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_CREW_WIN) + SSticker.mode_result = "loss - evacuation - disk secured" + SSticker.news_report = OPERATIVES_KILLED + if(NUKE_RESULT_DISK_LOST) + SSticker.mode_result = "halfwin - evacuation - disk not secured" + SSticker.news_report = OPERATIVE_SKIRMISH + if(NUKE_RESULT_DISK_STOLEN) + SSticker.mode_result = "halfwin - detonation averted" + SSticker.news_report = OPERATIVE_SKIRMISH + else + SSticker.mode_result = "halfwin - interrupted" + SSticker.news_report = OPERATIVE_SKIRMISH diff --git a/yogstation/code/modules/storytellers/converted_events/solo/obsessed.dm b/yogstation/code/modules/storytellers/converted_events/solo/obsessed.dm new file mode 100644 index 000000000000..31e3a4b786f6 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/obsessed.dm @@ -0,0 +1,34 @@ +/datum/round_event_control/antagonist/solo/obsessed + antag_flag = ROLE_OBSESSED + tags = list(TAG_CREW_ANTAG, TAG_TARGETED) + antag_datum = /datum/antagonist/obsessed + typepath = /datum/round_event/antagonist/solo/obsessed + restricted_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_AI, + JOB_CYBORG, + ROLE_POSIBRAIN, + ) + weight = 1 + max_occurrences = 3 + min_players = 5 + checks_antag_cap = FALSE + +/datum/round_event_control/antagonist/solo/obsessed/midround + name = "Compulsive Obsession" + maximum_antags = 1 + +/datum/round_event/antagonist/solo/obsessed + +/datum/round_event/antagonist/solo/obsessed/add_datum_to_mind(datum/mind/antag_mind) + antag_mind.add_antag_datum(antag_datum) + var/mob/living/carbon/human/current = antag_mind.current + current.gain_trauma(/datum/brain_trauma/special/obsessed) diff --git a/yogstation/code/modules/storytellers/converted_events/solo/revolutionary.dm b/yogstation/code/modules/storytellers/converted_events/solo/revolutionary.dm new file mode 100644 index 000000000000..62434f09b244 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/revolutionary.dm @@ -0,0 +1,71 @@ +/datum/round_event_control/antagonist/solo/revolutionary + name = "Revolution" + tags = list(TAG_COMMUNAL, TAG_DESTRUCTIVE, TAG_COMBAT, TAG_TEAM_ANTAG) + antag_flag = ROLE_REV_HEAD + antag_datum = /datum/antagonist/rev/head/event_trigger + typepath = /datum/round_event/antagonist/solo/revolutionary + shared_occurence_type = SHARED_HIGH_THREAT + restricted_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_CYBORG, + JOB_DETECTIVE, + JOB_HEAD_OF_PERSONNEL, + JOB_HEAD_OF_SECURITY, + JOB_RESEARCH_DIRECTOR, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + ) + base_antags = 2 + enemy_roles = list( + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + ) + required_enemies = 3 + min_players = 25 + roundstart = TRUE + earliest_start = 0 SECONDS + weight = 3 //value was 3, we need to manually test if this works or not before allowing it normally + max_occurrences = 1 + +/datum/antagonist/rev/head/event_trigger + remove_clumsy = TRUE + give_flash = TRUE + +/datum/round_event/antagonist/solo/revolutionary + excute_round_end_reports = TRUE + end_when = 60000 /// we will end on our own when revs win + var/static/datum/team/revolution/revolution + var/static/finished = FALSE + +/datum/round_event/antagonist/solo/revolutionary/setup() + . = ..() + if(!revolution) + revolution = new() + +/datum/round_event/antagonist/solo/revolutionary/add_datum_to_mind(datum/mind/antag_mind) + antag_mind.add_antag_datum(antag_datum, revolution) + if(length(revolution.members)) + revolution.update_objectives() + revolution.update_heads() + SSshuttle.registerHostileEnvironment(revolution) + + +/datum/round_event/antagonist/solo/revolutionary/round_end_report() + revolution.round_result(finished) + +// /datum/round_event/antagonist/solo/revolutionary/tick() +// if(finished) +// return +// var/winner = revolution.process_victory() +// if(isnull(winner)) +// return + +// finished = winner +// end() diff --git a/yogstation/code/modules/storytellers/converted_events/solo/traitor.dm b/yogstation/code/modules/storytellers/converted_events/solo/traitor.dm new file mode 100644 index 000000000000..d41e4210eaa1 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/traitor.dm @@ -0,0 +1,43 @@ +/datum/round_event_control/antagonist/solo/traitor + antag_flag = ROLE_TRAITOR + tags = list(TAG_COMBAT, TAG_CREW_ANTAG) + antag_datum = /datum/antagonist/traitor + protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Brig Physician") + restricted_roles = list("AI", "Cyborg", "Synthetic") + weight = 15 + +/datum/round_event_control/antagonist/solo/traitor/midround + name = "Sleeper Agents (Traitors)" + prompted_picking = TRUE + min_players = 5 + +/datum/round_event_control/antagonist/solo/traitor/roundstart + name = "Traitors" + roundstart = TRUE + title_icon = "traitor" + typepath = /datum/round_event/antagonist/solo/traitor //yogs change: uses a different typepath so antag spawning can be treated differently + earliest_start = 0 SECONDS + + +//yogs change, delayed awakening +/datum/round_event/antagonist/solo/traitor/start() + for(var/datum/mind/antag_mind as anything in setup_minds) + addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/round_event/antagonist/solo/traitor, add_traitor_delayed), antag_mind, null), rand(1 MINUTES, (3 MINUTES + 10 SECONDS))) + +/datum/round_event/antagonist/solo/traitor/proc/add_traitor_delayed(datum/mind/traitor, datum/antagonist/cached_antag = null) + if(!cached_antag) + cached_antag = new antag_datum() + cached_antag.awake_stage = ANTAG_ASLEEP + cached_antag.awake_stage++ + switch(cached_antag.awake_stage) + if(ANTAG_FIRST_WARNING) + traitor.current.playsound_local(get_turf(traitor.current), 'sound/ambience/antag/telegraph1.ogg', 100, FALSE, pressure_affected = FALSE) + to_chat(traitor.current, span_danger("You don't feel good..")) + addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/round_event/antagonist/solo/traitor, add_traitor_delayed), traitor, cached_antag), 1 MINUTES) + if(ANTAG_SECOND_WARNING) + traitor.current.playsound_local(get_turf(traitor.current), 'sound/ambience/antag/telegraph2.ogg', 100, FALSE, pressure_affected = FALSE) + to_chat(traitor.current, span_danger("Remembering a tune, you slowly find the melody. Coded phrases and dark rooms flutter behind your eyelids. What could it mean? You should probably keep this to yourself.")) + addtimer(CALLBACK(src, TYPE_PROC_REF(/datum/round_event/antagonist/solo/traitor, add_traitor_delayed), traitor, cached_antag), 1 MINUTES) + if(ANTAG_AWAKE) + traitor.current.playsound_local(get_turf(traitor.current), 'sound/ambience/antag/tatoralert_buildup.ogg', 100, FALSE, pressure_affected = FALSE) + addtimer(CALLBACK(traitor, TYPE_PROC_REF(/datum/mind, add_antag_datum), cached_antag), 2 SECONDS) diff --git a/yogstation/code/modules/storytellers/converted_events/solo/vampire.dm b/yogstation/code/modules/storytellers/converted_events/solo/vampire.dm new file mode 100644 index 000000000000..d4d5f0ecfa98 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/vampire.dm @@ -0,0 +1,35 @@ +/datum/round_event_control/antagonist/solo/vampire + antag_flag = ROLE_VAMPIRE + tags = list(TAG_COMBAT, TAG_MAGICAL, TAG_CREW_ANTAG) + antag_datum = /datum/antagonist/vampire + protected_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_PERSONNEL, + JOB_CHIEF_ENGINEER, + JOB_CHIEF_MEDICAL_OFFICER, + JOB_RESEARCH_DIRECTOR, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_BRIG_PHYSICIAN, + JOB_CHAPLAIN + ) + restricted_roles = list( + JOB_AI, + JOB_CYBORG, + ) + min_players = 15 + weight = 10 + maximum_antags = 2 + +/datum/round_event_control/antagonist/solo/vampire/roundstart + name = "Vampires" + roundstart = TRUE + title_icon = "vampires" + earliest_start = 0 SECONDS + +/datum/round_event_control/antagonist/solo/vampire/midround + antag_flag = ROLE_VAMPIRICACCIDENT + name = "Vampiric Accident" + max_occurrences = 1 diff --git a/yogstation/code/modules/storytellers/converted_events/solo/wizard.dm b/yogstation/code/modules/storytellers/converted_events/solo/wizard.dm new file mode 100644 index 000000000000..a45f295193b5 --- /dev/null +++ b/yogstation/code/modules/storytellers/converted_events/solo/wizard.dm @@ -0,0 +1,50 @@ +/datum/round_event_control/antagonist/solo/wizard + name = "Wizard" + tags = list(TAG_COMBAT, TAG_DESTRUCTIVE, TAG_EXTERNAL, TAG_MAGICAL) + typepath = /datum/round_event/antagonist/solo/wizard + antag_flag = ROLE_WIZARD + antag_datum = /datum/antagonist/wizard + shared_occurence_type = SHARED_HIGH_THREAT + restricted_roles = list( + JOB_CAPTAIN, + JOB_HEAD_OF_SECURITY, + ) // Just to be sure that a wizard getting picked won't ever imply a Captain or HoS not getting drafted + maximum_antags = 1 + enemy_roles = list( + JOB_AI, + JOB_CAPTAIN, + JOB_DETECTIVE, + JOB_HEAD_OF_SECURITY, + JOB_SECURITY_OFFICER, + JOB_WARDEN, + JOB_CHAPLAIN, + ) + required_enemies = 1 + roundstart = TRUE + title_icon = "wizard" + earliest_start = 0 SECONDS + weight = 2 + min_players = 35 + max_occurrences = 1 + +/datum/round_event_control/antagonist/solo/wizard/canSpawnEvent(players_amt, allow_magic = FALSE, fake_check = FALSE) + . = ..() + if(!.) + return + if(!length(GLOB.wizardstart)) + return FALSE + +/datum/round_event/antagonist/solo/wizard + +/datum/round_event/antagonist/solo/wizard/add_datum_to_mind(datum/mind/antag_mind) + var/mob/living/current_mob = antag_mind.current + SSjob.FreeRole(antag_mind.assigned_role) + var/list/items = current_mob.get_equipped_items(TRUE) + current_mob.unequip_everything() + for(var/obj/item/item as anything in items) + qdel(item) + + var/mob/living/carbon/human/new_player_mob = new //while funny, it would kind of suck to be a blind criple wizard + antag_mind.transfer_to(new_player_mob) + qdel(current_mob) + antag_mind.make_Wizard() diff --git a/yogstation/code/modules/storytellers/event_groups/_event_group.dm b/yogstation/code/modules/storytellers/event_groups/_event_group.dm new file mode 100644 index 000000000000..7872629f4278 --- /dev/null +++ b/yogstation/code/modules/storytellers/event_groups/_event_group.dm @@ -0,0 +1,66 @@ +/// An associative list of singleton event groups, in the format of [type] = instance. +GLOBAL_LIST_INIT_TYPED(event_groups, /datum/event_group, initialize_event_groups()) + +/datum/event_group + /// The name of the event group. + var/name + /// If set, this will limit the amount of times events in this group can run. + var/max_occurrences + /// If set, whenever an event in this group runs, no other events in this group + /// will be able to run for the specified amount of time. + /// Can be either a number for a static cooldown, or a list containing 2 numbers, + /// for a random cooldown between the given numbers, which will bias towards the upper bound + /// the more the event group has occured. + var/cooldown_time + /// The amount of time events in this group have ran. + VAR_FINAL/occurrences = 0 + /// If [cooldown_time] is set, this will be set to the minimum world.time where the next event in this group can run. + COOLDOWN_DECLARE(event_cooldown) + +/datum/event_group/Destroy(force) + if(!force && GLOB.event_groups[type] == src) + stack_trace("Something is trying to destroy the event group ([type]), which is a singleton! This is super duper bad!") + return QDEL_HINT_LETMELIVE + return ..() + +/datum/event_group/proc/get_cooldown_time() as num + if(isnum(cooldown_time)) + return cooldown_time + var/min_cooldown = cooldown_time[1] + var/max_cooldown = cooldown_time[2] + + if (max_occurrences) + var/occurrence_ratio = min(1, occurrences / max_occurrences) + return min_cooldown + (max_cooldown - min_cooldown) * occurrence_ratio + else + // If max_occurrences is not set, use a simple exponential increase + return min_cooldown + (max_cooldown - min_cooldown) * (occurrences / (occurrences + 1)) + +/datum/event_group/proc/can_run() as num + . = TRUE + if(cooldown_time && !COOLDOWN_FINISHED(src, event_cooldown)) + return FALSE + if(max_occurrences && occurrences >= max_occurrences) + return FALSE + +/datum/event_group/proc/on_run(datum/round_event_control/running_event) + if(cooldown_time) + var/cooldown = get_cooldown_time() + COOLDOWN_START(src, event_cooldown, cooldown) + for(var/datum/scheduled_event/scheduled_event in SSgamemode.scheduled_events) + if(scheduled_event.event == running_event || scheduled_event.event?.event_group != type || !scheduled_event.start_time || (scheduled_event.start_time > src.event_cooldown) || (scheduled_event.start_time <= world.time)) + continue + var/old_start_time = scheduled_event.start_time + scheduled_event.start_time += cooldown + message_admins("Scheduled event [scheduled_event.event.name] start time pushed back by [DisplayTimeText(cooldown)] ([DisplayTimeText(COOLDOWN_TIMELEFT(scheduled_event, start_time))] from now) due to event group [name] running.") + log_storyteller("Scheduled event [scheduled_event.event.name] start time pushed back by [DisplayTimeText(cooldown)] ([old_start_time] -> [scheduled_event.start_time]) due to event group [name] running.", list("group" = "[name]", "cooldown" = cooldown)) + occurrences++ + SSblackbox.record_feedback("tally", "event_group_ran", 1, "[name]") + +/proc/initialize_event_groups() as /list + RETURN_TYPE(/list) + . = list() + for(var/datum/event_group/event_group as anything in subtypesof(/datum/event_group)) + if(!event_group::name) + continue + .[event_group] = new event_group diff --git a/yogstation/code/modules/storytellers/event_groups/groups.dm b/yogstation/code/modules/storytellers/event_groups/groups.dm new file mode 100644 index 000000000000..a6952f8e0e0e --- /dev/null +++ b/yogstation/code/modules/storytellers/event_groups/groups.dm @@ -0,0 +1,44 @@ +/datum/event_group/anomalies + name = "Anomalies" + cooldown_time = list(10 MINUTES, 25 MINUTES) + +/datum/event_group/comms + name = "Communications" + cooldown_time = list(15 MINUTES, 45 MINUTES) + max_occurrences = 3 + +/// Represents small-scale technical difficulties - might annoy some people, but not everyone will notice or be affected, +/// If the event directly affects the entire crew, use [/datum/event_group/bsod]. +/// If the main effect involves telecommunications, use [/datum/event_group/comms]. +/datum/event_group/error + name = "Technical Difficulties (small scale)" + cooldown_time = list(2.5 MINUTES, 7.5 MINUTES) + +/// Represents large-scale technical difficulties - stuff that affects the whole crew. +/// If the main effect involves telecommunications, use [/datum/event_group/comms]. +/datum/event_group/bsod + name = "Technical Difficulties (large scale)" + cooldown_time = list(15 MINUTES, 25 MINUTES) + max_occurrences = 5 + +/datum/event_group/debris + name = "Space Debris" + cooldown_time = list(2 MINUTES, 10 MINUTES) + +/datum/event_group/meteors + name = "Meteors" + cooldown_time = 20 MINUTES + max_occurrences = 3 + +// needs a better name - this is basically for events focused around spawning some sort of NPCs, +// i.e vines, wisdom cow, carp, etc. +/datum/event_group/guests + name = "Guests" + cooldown_time = list(7.5 MINUTES, 20 MINUTES) + +// These event groups are somewhat specific to a single event, +// but we're using event groups to share cooldowns/occurrences between SSevents and storytellers +/datum/event_group/scrubber_overflow + name = "Scrubber Overflows" + cooldown_time = list(25 MINUTES, 45 MINUTES) + max_occurrences = 2 diff --git a/yogstation/code/modules/storytellers/gamemode_subsystem.dm b/yogstation/code/modules/storytellers/gamemode_subsystem.dm new file mode 100644 index 000000000000..fb54c18b055f --- /dev/null +++ b/yogstation/code/modules/storytellers/gamemode_subsystem.dm @@ -0,0 +1,1199 @@ +#define INIT_ORDER_GAMEMODE 70 +///how many storytellers can be voted for along with always_votable ones +#define DEFAULT_STORYTELLER_VOTE_OPTIONS 4 +///amount of players we can have before no longer running votes for storyteller +#define MAX_POP_FOR_STORYTELLER_VOTE 25 + +SUBSYSTEM_DEF(gamemode) + name = "Gamemode" + init_order = INIT_ORDER_GAMEMODE + runlevels = RUNLEVEL_GAME + flags = SS_BACKGROUND | SS_KEEP_TIMING + wait = 2 SECONDS + + /// The name of the icon shown during credit roll (only set if the roundstart antag has an icon) + var/title_icon = null + + /// List of our event tracks for fast access during for loops. + var/list/event_tracks = EVENT_TRACKS + /// Our storyteller. They progresses our trackboards and picks out events + var/datum/storyteller/storyteller + /// Result of the storyteller vote/pick. Defaults to the guide. + var/selected_storyteller = /datum/storyteller/guide + /// List of all the storytellers. Populated at init. Associative from type + var/list/storytellers = list() + /// Next process for our storyteller. The wait time is STORYTELLER_WAIT_TIME + var/next_storyteller_process = 0 + /// Associative list of even track points. + var/list/event_track_points = list( + EVENT_TRACK_MUNDANE = 0, + EVENT_TRACK_MODERATE = 0, + EVENT_TRACK_MAJOR = 0, + EVENT_TRACK_ROLESET = 0, + EVENT_TRACK_OBJECTIVES = 0 + ) + /// Last point amount gained of each track. Those are recorded for purposes of estimating how long until next event. + var/list/last_point_gains = list( + EVENT_TRACK_MUNDANE = 0, + EVENT_TRACK_MODERATE = 0, + EVENT_TRACK_MAJOR = 0, + EVENT_TRACK_ROLESET = 0, + EVENT_TRACK_OBJECTIVES = 0 + ) + /// Point thresholds at which the events are supposed to be rolled, it is also the base cost for events. + var/list/point_thresholds = list( + EVENT_TRACK_MUNDANE = MUNDANE_POINT_THRESHOLD, + EVENT_TRACK_MODERATE = MODERATE_POINT_THRESHOLD, + EVENT_TRACK_MAJOR = MAJOR_POINT_THRESHOLD, + EVENT_TRACK_ROLESET = ROLESET_POINT_THRESHOLD, + EVENT_TRACK_OBJECTIVES = OBJECTIVES_POINT_THRESHOLD + ) + + /// Configurable multipliers for point gain over time. + var/list/point_gain_multipliers = list( + EVENT_TRACK_MUNDANE = 1, + EVENT_TRACK_MODERATE = 1, + EVENT_TRACK_MAJOR = 1, + EVENT_TRACK_ROLESET = 1, + EVENT_TRACK_OBJECTIVES = 1 + ) + /// Configurable multipliers for roundstart points. + var/list/roundstart_point_multipliers = list( + EVENT_TRACK_MUNDANE = 1, + EVENT_TRACK_MODERATE = 1, + EVENT_TRACK_MAJOR = 1, + EVENT_TRACK_ROLESET = 1, + EVENT_TRACK_OBJECTIVES = 1 + ) + /// Whether we allow pop scaling. This is configured by config, or the storyteller UI + var/allow_pop_scaling = TRUE + + /// Associative list of pop scale thresholds. + var/list/pop_scale_thresholds = list( + EVENT_TRACK_MUNDANE = MUNDANE_POP_SCALE_THRESHOLD, + EVENT_TRACK_MODERATE = MODERATE_POP_SCALE_THRESHOLD, + EVENT_TRACK_MAJOR = MAJOR_POP_SCALE_THRESHOLD, + EVENT_TRACK_ROLESET = ROLESET_POP_SCALE_THRESHOLD, + EVENT_TRACK_OBJECTIVES = OBJECTIVES_POP_SCALE_THRESHOLD + ) + + /// Associative list of pop scale penalties. + var/list/pop_scale_penalties = list( + EVENT_TRACK_MUNDANE = MUNDANE_POP_SCALE_PENALTY, + EVENT_TRACK_MODERATE = MODERATE_POP_SCALE_PENALTY, + EVENT_TRACK_MAJOR = MAJOR_POP_SCALE_PENALTY, + EVENT_TRACK_ROLESET = ROLESET_POP_SCALE_PENALTY, + EVENT_TRACK_OBJECTIVES = OBJECTIVES_POP_SCALE_PENALTY + ) + + /// Associative list of active multipliers from pop scale penalty. + var/list/current_pop_scale_multipliers = list( + EVENT_TRACK_MUNDANE = 1, + EVENT_TRACK_MODERATE = 1, + EVENT_TRACK_MAJOR = 1, + EVENT_TRACK_ROLESET = 1, + EVENT_TRACK_OBJECTIVES = 1, + ) + + + + /// Associative list of control events by their track category. Compiled in Init + var/list/event_pools = list() + + /// Events that we have scheduled to run in the nearby future + var/list/scheduled_events = list() + + /// Associative list of tracks to forced event controls. For admins to force events (though they can still invoke them freely outside of the track system) + var/list/forced_next_events = list() + + var/list/control = list() //list of all datum/round_event_control. Used for selecting events based on weight and occurrences. + var/list/running = list() //list of all existing /datum/round_event + var/list/round_end_data = list() //list of all reports that need to add round end reports + var/list/currentrun = list() + + /// List of all uncategorized events, because they were wizard or holiday events + var/list/uncategorized = list() + + var/list/holidays //List of all holidays occuring today or null if no holidays + + /// Event frequency multiplier, it exists because wizard, eugh. + var/event_frequency_multiplier = 1 + + /// Current preview page for the statistics UI. + var/statistics_track_page = EVENT_TRACK_MUNDANE + /// Page of the UI panel. + var/panel_page = GAMEMODE_PANEL_MAIN + /// Whether we are viewing the roundstart events or not + var/roundstart_event_view = TRUE + + /// Whether the storyteller has been halted + var/halted_storyteller = FALSE + + /// Ready players for roundstart events. + var/ready_players = 0 + var/active_players = 0 + var/head_crew = 0 + var/eng_crew = 0 + var/sec_crew = 0 + var/med_crew = 0 + + /// Is storyteller secret or not + var/secret_storyteller = FALSE + + /// List of new player minds we currently want to give our roundstart antag to + var/list/roundstart_antag_minds = list() + + var/wizardmode = FALSE //refactor this into just being a unique storyteller + + /// What is our currently desired/selected roundstart event + var/datum/round_event_control/antagonist/solo/current_roundstart_event + var/list/last_round_events = list() + var/ran_roundstart = FALSE + var/list/triggered_round_events = list() + + var/total_valid_antags = 0 + +/datum/controller/subsystem/gamemode/Initialize(time, zlevel) + // Populate event pools + for(var/track in event_tracks) + event_pools[track] = list() + + // Populate storytellers + for(var/type in subtypesof(/datum/storyteller)) + storytellers[type] = new type() + + for(var/datum/round_event_control/event_type as anything in typesof(/datum/round_event_control)) + if(!event_type::typepath || !event_type::name) + continue + var/datum/round_event_control/event = new event_type + if(!event.valid_for_map()) + qdel(event) + continue // event isn't good for this map no point in trying to add it to the list + control += event //add it to the list of all events (controls) + getHoliday() + + load_config_vars() + load_event_config_vars() + + ///Seeding events into track event pools needs to happen after event config vars are loaded + for(var/datum/round_event_control/event as anything in control) + if(event.holidayID || event.wizardevent) + uncategorized += event + continue + event_pools[event.track] += event //Add it to the categorized event pools + + load_roundstart_data() + return SS_INIT_SUCCESS + + +/datum/controller/subsystem/gamemode/fire(resumed = FALSE) + if(!resumed) + src.currentrun = running.Copy() + + ///Handle scheduled events + for(var/datum/scheduled_event/sch_event in scheduled_events) + if(world.time >= sch_event.start_time) + sch_event.try_fire() + else if(!sch_event.alerted_admins && world.time >= sch_event.start_time - 1 MINUTES) + ///Alert admins 1 minute before running and allow them to cancel or refund the event, once again. + sch_event.alerted_admins = TRUE + message_admins("Scheduled Event: [sch_event.event] will run in [(sch_event.start_time - world.time) / 10] seconds. (CANCEL) (REFUND)") + + if(!halted_storyteller && next_storyteller_process <= world.time && storyteller) + // We update crew information here to adjust population scalling and event thresholds for the storyteller. + update_crew_infos() + next_storyteller_process = world.time + STORYTELLER_WAIT_TIME + storyteller.process(STORYTELLER_WAIT_TIME * 0.1) + + //cache for sanic speed (lists are references anyways) + var/list/currentrun = src.currentrun + + while(currentrun.len) + var/datum/thing = currentrun[currentrun.len] + currentrun.len-- + if(thing) + thing.process(wait * 0.1) + else + running.Remove(thing) + if (MC_TICK_CHECK) + return + +/// Gets the number of antagonists the antagonist injection events will stop rolling after. +/datum/controller/subsystem/gamemode/proc/get_antag_cap() + var/total_number = get_correct_popcount() + (sec_crew * 2) + var/cap = FLOOR((total_number / ANTAG_CAP_DENOMINATOR), 1) + ANTAG_CAP_FLAT + return cap + +/// Whether events can inject more antagonists into the round +/datum/controller/subsystem/gamemode/proc/can_inject_antags() + total_valid_antags = 0 + for(var/mob/checked_mob in GLOB.mob_list) + if(!checked_mob.mind) + continue + if(!checked_mob.mind.special_role) + continue + if(checked_mob.stat == DEAD) + continue + total_valid_antags++ + + + return (get_antag_cap() > total_valid_antags) + +/// Gets candidates for antagonist roles. +/datum/controller/subsystem/gamemode/proc/get_candidates(be_special, job_ban, observers, ready_newplayers, living_players, required_time, inherit_required_time = TRUE, midround_antag_pref, no_antags = TRUE, list/restricted_roles, list/required_roles) + var/list/candidates = list() + var/list/candidate_candidates = list() //lol + + for(var/mob/player as anything in GLOB.player_list) + if(player.mind && player?.client?.prefs?.read_preference(/datum/preference/toggle/quiet_mode)) //yogs: make sure to give them quiet mode, don't need to do anything else as the gamemode itself will handle that + player.mind.quiet_round = TRUE + if(ready_newplayers && isnewplayer(player)) + var/mob/dead/new_player/new_player = player + if(new_player.ready == PLAYER_READY_TO_PLAY && new_player.mind && new_player.check_preferences()) + candidate_candidates += player + else if(observers && isobserver(player)) + candidate_candidates += player + else if(living_players && isliving(player)) + if(!ishuman(player) && !isAI(player)) + continue + // I split these checks up to make the code more readable ~Lucy + var/is_on_station = is_station_level(player.z) + var/is_late_arrival = HAS_TRAIT(SSstation, STATION_TRAIT_LATE_ARRIVALS) && istype(get_area(player), /area/shuttle/arrival) + if(!is_on_station && !is_late_arrival) + continue + candidate_candidates += player + + for(var/mob/candidate as anything in candidate_candidates) + if(QDELETED(candidate) || !candidate.key || !candidate.client || (!observers && !candidate.mind)) + continue + if(!observers) + if(!ready_players && !isliving(candidate)) + continue + if(no_antags && !isnull(candidate.mind.antag_datums)) + var/real = TRUE + for(var/datum/antagonist/antag_datum as anything in candidate.mind.antag_datums) + //if(antag_datum.count_against_dynamic_roll_chance && !(antag_datum.antag_flags & FLAG_FAKE_ANTAG)) + //real = TRUE + //break + if(real) + continue + if(restricted_roles && (candidate.mind.assigned_role in restricted_roles)) + continue + if(length(required_roles) && !(candidate.mind.assigned_role in required_roles)) + continue + + if(be_special) + if(!(candidate.client.prefs) || !(be_special in candidate.client.prefs.be_special)) + continue + + var/time_to_check + if(required_time) + time_to_check = required_time + else if(inherit_required_time) + time_to_check = GLOB.special_required_days[be_special] + + if(time_to_check && candidate.client.get_remaining_days(time_to_check) > 0) + continue + + //if(midround_antag_pref) + //continue + + if(job_ban && is_banned_from(candidate.ckey, list(job_ban, ROLE_SYNDICATE))) + continue + candidates += candidate + return candidates + +/// Gets the correct popcount, returning READY people if roundstart, and active people if not. +/datum/controller/subsystem/gamemode/proc/get_correct_popcount() + if(SSticker.HasRoundStarted()) + update_crew_infos() + return active_players + else + calculate_ready_players() + return ready_players + +/// Refunds and removes a scheduled event. +/datum/controller/subsystem/gamemode/proc/refund_scheduled_event(datum/scheduled_event/refunded) + if(refunded.cost) + var/track_type = refunded.event.track + event_track_points[track_type] += refunded.cost + remove_scheduled_event(refunded) + +/// Removes a scheduled event. +/datum/controller/subsystem/gamemode/proc/remove_scheduled_event(datum/scheduled_event/removed) + scheduled_events -= removed + qdel(removed) + +/// We need to calculate ready players for the sake of roundstart events becoming eligible. +/datum/controller/subsystem/gamemode/proc/calculate_ready_players() + ready_players = 0 + for(var/mob/dead/new_player/player as anything in GLOB.new_player_list) + if(player.ready == PLAYER_READY_TO_PLAY) + ready_players++ + +/// We roll points to be spent for roundstart events, including antagonists. +/datum/controller/subsystem/gamemode/proc/roll_pre_setup_points() + if(storyteller?.disable_distribution || halted_storyteller) + return + /// Distribute points + for(var/track in event_track_points) + var/base_amt + var/gain_amt + switch(track) + if(EVENT_TRACK_MUNDANE) + base_amt = ROUNDSTART_MUNDANE_BASE + gain_amt = ROUNDSTART_MUNDANE_GAIN + if(EVENT_TRACK_MODERATE) + base_amt = ROUNDSTART_MODERATE_BASE + gain_amt = ROUNDSTART_MODERATE_GAIN + if(EVENT_TRACK_MAJOR) + base_amt = ROUNDSTART_MAJOR_BASE + gain_amt = ROUNDSTART_MAJOR_GAIN + if(EVENT_TRACK_ROLESET) + base_amt = ROUNDSTART_ROLESET_BASE + gain_amt = ROUNDSTART_ROLESET_GAIN + if(EVENT_TRACK_OBJECTIVES) + base_amt = ROUNDSTART_OBJECTIVES_BASE + gain_amt = ROUNDSTART_OBJECTIVES_GAIN + var/calc_value = base_amt + (gain_amt * ready_players) + calc_value *= roundstart_point_multipliers[track] + calc_value *= storyteller.starting_point_multipliers[track] + calc_value *= (rand(100 - storyteller.roundstart_points_variance,100 + storyteller.roundstart_points_variance)/100) + event_track_points[track] = round(calc_value) + + /// If the storyteller guarantees an antagonist roll, add points to make it so. + if(storyteller.guarantees_roundstart_roleset && event_track_points[EVENT_TRACK_ROLESET] < point_thresholds[EVENT_TRACK_ROLESET]) + event_track_points[EVENT_TRACK_ROLESET] = point_thresholds[EVENT_TRACK_ROLESET] + + /// If we have any forced events, ensure we get enough points for them + for(var/track in event_tracks) + if(forced_next_events[track] && event_track_points[track] < point_thresholds[track]) + event_track_points[track] = point_thresholds[track] + +/// At this point we've rolled roundstart events and antags and we handle leftover points here. +/datum/controller/subsystem/gamemode/proc/handle_post_setup_points() +// for(var/track in event_track_points) //Just halve the points for now. +// event_track_points[track] *= 0.5 TESTING HOW THINGS GO WITHOUT THIS HALVING OF POINTS + return + +/// Because roundstart events need 2 steps of firing for purposes of antags, here is the first step handled, happening before occupation division. +/datum/controller/subsystem/gamemode/proc/handle_pre_setup_roundstart_events() + if(storyteller?.disable_distribution) + return + if(halted_storyteller) + message_admins("WARNING: Didn't roll roundstart events (including antagonists) due to the storyteller being halted.") + return + while(TRUE) + if(!storyteller.handle_tracks()) + break + +/// Second step of handlind roundstart events, happening after people spawn. +/datum/controller/subsystem/gamemode/proc/handle_post_setup_roundstart_events() + /// Start all roundstart events on post_setup immediately + for(var/datum/round_event/event as anything in running) + if(!event.control.roundstart) + continue + ASYNC + event.try_start() + INVOKE_ASYNC(event, TYPE_PROC_REF(/datum/round_event, try_start)) + +/// Schedules an event to run later. +/datum/controller/subsystem/gamemode/proc/schedule_event(datum/round_event_control/passed_event, passed_time, passed_cost, passed_ignore, passed_announce, _forced = FALSE) + if(_forced) + passed_ignore = TRUE + var/datum/scheduled_event/scheduled = new (passed_event, world.time + passed_time, passed_cost, passed_ignore, passed_announce) + var/round_started = SSticker.HasRoundStarted() + if(round_started) + message_admins("Event: [passed_event] has been scheduled to run in [passed_time / 10] seconds. (CANCEL) (REFUND)") + else //Only roundstart events can be scheduled before round start + message_admins("Event: [passed_event] has been scheduled to run on roundstart. (CANCEL)") + scheduled_events += scheduled + +/datum/controller/subsystem/gamemode/proc/update_crew_infos() + // Very similar logic to `get_active_player_count()` + active_players = 0 + head_crew = 0 + eng_crew = 0 + med_crew = 0 + sec_crew = 0 + for(var/mob/player_mob as anything in GLOB.player_list) + if(QDELETED(player_mob)) + continue + if(!player_mob.client) + continue + if(player_mob.stat) //If they're alive + continue + if(player_mob.client.is_afk()) //If afk + continue + if(!ishuman(player_mob)) + continue + active_players++ + if(player_mob.mind?.assigned_role) + var/datum/job/player_role = SSjob.GetJob(player_mob.mind.assigned_role) + if(!player_role || !istype(player_role)) + continue + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) + head_crew++ + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_ENGINEERING) + eng_crew++ + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_MEDICAL) + med_crew++ + if(player_role.departments_bitflags & DEPARTMENT_BITFLAG_SECURITY) + sec_crew++ + update_pop_scaling() + +/datum/controller/subsystem/gamemode/proc/update_pop_scaling() + for(var/track in event_tracks) + var/high_pop_bound = pop_scale_thresholds[track] + var/scale_penalty = pop_scale_penalties[track] + + var/perceived_pop = min(active_players, high_pop_bound) + + var/divisor = high_pop_bound + /// If the bounds are equal, we'd be dividing by zero or worse, if upper is smaller than lower, we'd be increasing the factor, just make it 1 and continue. + /// this is only a problem for bad configs + if(divisor <= 0) + current_pop_scale_multipliers[track] = 1 + continue + var/scalar = perceived_pop / divisor + var/penalty = scale_penalty - (scale_penalty * scalar) + var/calculated_multiplier = 1 - (penalty / 100) + + current_pop_scale_multipliers[track] = calculated_multiplier + +/datum/controller/subsystem/gamemode/proc/TriggerEvent(datum/round_event_control/event, forced = FALSE) + log_storyteller("Event: [event] is being triggered.") + . = event.preRunEvent(forced) + if(. == EVENT_CANT_RUN)//we couldn't run this event for some reason, set its max_occurrences to 0 + event.max_occurrences = 0 + else if(. == EVENT_READY) + event.runEvent(random = TRUE) + +///Resets frequency multiplier. +/datum/controller/subsystem/gamemode/proc/resetFrequency() + event_frequency_multiplier = 1 + +/client/proc/forceEvent() + set name = "Trigger Event" + set category = "Admin.Events" + if(!holder) + return + holder.forceEvent(usr) + +/datum/admins/proc/forceEvent(mob/user) + SSgamemode.event_panel(user) + +/client/proc/forceGamemode() + set name = "Open Gamemode Panel" + set category = "Admin.Events" + if(!holder) + return + holder.forceGamemode(usr) + +/datum/admins/proc/forceGamemode(mob/user) + SSgamemode.admin_panel(user) + + +////////////// +// HOLIDAYS // +////////////// +//Uncommenting ALLOW_HOLIDAYS in config.txt will enable holidays + +//It's easy to add stuff. Just add a holiday datum in code/modules/holiday/holidays.dm +//You can then check if it's a special day in any code in the game by doing if(SSgamemode.holidays["Groundhog Day"]) + +//You can also make holiday random events easily thanks to Pete/Gia's system. +//simply make a random event normally, then assign it a holidayID string which matches the holiday's name. +//Anything with a holidayID, which isn't in the holidays list, will never occur. + +//Please, Don't spam stuff up with stupid stuff (key example being april-fools Pooh/ERP/etc), +//And don't forget: CHECK YOUR CODE!!!! We don't want any zero-day bugs which happen only on holidays and never get found/fixed! + +////////////////////////////////////////////////////////////////////////////////////////////////////////// +//ALSO, MOST IMPORTANTLY: Don't add stupid stuff! Discuss bonus content with Project-Heads first please!// +////////////////////////////////////////////////////////////////////////////////////////////////////////// + + +/proc/check_holidays(holiday_to_find) + if(!CONFIG_GET(flag/allow_holidays)) + return // Holiday stuff was not enabled in the config! + + return LAZYFIND(SSgamemode.holidays, holiday_to_find) + +//sets up the holidays and holidays list +/datum/controller/subsystem/gamemode/proc/getHoliday() + if(!CONFIG_GET(flag/allow_holidays)) + return // Holiday stuff was not enabled in the config! + for(var/H in subtypesof(/datum/holiday)) + var/datum/holiday/holiday = new H() + var/delete_holiday = TRUE + for(var/timezone in holiday.timezones) + var/time_in_timezone = world.realtime + timezone HOURS + + var/YYYY = text2num(time2text(time_in_timezone, "YYYY")) // get the current year + var/MM = text2num(time2text(time_in_timezone, "MM")) // get the current month + var/DD = text2num(time2text(time_in_timezone, "DD")) // get the current day + var/DDD = time2text(time_in_timezone, "DDD") // get the current weekday + + if(holiday.shouldCelebrate(DD, MM, YYYY, DDD)) + holiday.celebrate() + LAZYSET(holidays, holiday.name, holiday) + delete_holiday = FALSE + break + if(delete_holiday) + qdel(holiday) + + if(holidays) + holidays = shuffle(holidays) + // regenerate station name because holiday prefixes. + set_station_name(new_station_name()) + world.update_status() + +/datum/controller/subsystem/gamemode/proc/toggleWizardmode() + wizardmode = !wizardmode //TODO: decide what to do with wiz events + message_admins("Summon Events has been [wizardmode ? "enabled, events will occur [SSgamemode.event_frequency_multiplier] times as fast" : "disabled"]!") + log_game("Summon Events was [wizardmode ? "enabled" : "disabled"]!") + +///Attempts to select players for special roles the mode might have. +/datum/controller/subsystem/gamemode/proc/pre_setup() + calculate_ready_players() + roll_pre_setup_points() + //handle_pre_setup_roundstart_events() + return TRUE + +///Everyone should now be on the station and have their normal gear. This is the place to give the special roles extra things +/datum/controller/subsystem/gamemode/proc/post_setup(report) //Gamemodes can override the intercept report. Passing TRUE as the argument will force a report. + if(!report) + report = !CONFIG_GET(flag/no_intercept_report) + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(display_roundstart_logout_report)), ROUNDSTART_LOGOUT_REPORT_TIME) + + if(CONFIG_GET(flag/reopen_roundstart_suicide_roles)) + var/delay = CONFIG_GET(number/reopen_roundstart_suicide_roles_delay) + if(delay) + delay = (delay SECONDS) + else + delay = (4 MINUTES) //default to 4 minutes if the delay isn't defined. + addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(reopen_roundstart_suicide_roles)), delay) + + if(SSdbcore.Connect()) + var/list/to_set = list() + var/arguments = list() + if(storyteller) + to_set += "game_mode = :game_mode" + arguments["game_mode"] = storyteller.name + if(GLOB.revdata.originmastercommit) + to_set += "commit_hash = :commit_hash" + arguments["commit_hash"] = GLOB.revdata.originmastercommit + if(to_set.len) + arguments["round_id"] = GLOB.round_id + var/datum/DBQuery/query_round_game_mode = SSdbcore.NewQuery( + "UPDATE [format_table_name("round")] SET [to_set.Join(", ")] WHERE id = :round_id", + arguments + ) + query_round_game_mode.Execute() + qdel(query_round_game_mode) + generate_station_goals() + handle_post_setup_roundstart_events() + handle_post_setup_points() + roundstart_event_view = FALSE + return TRUE + + +///Handles late-join antag assignments +/datum/controller/subsystem/gamemode/proc/make_antag_chance(mob/living/carbon/human/character) + return + +/datum/controller/subsystem/gamemode/proc/check_finished(force_ending) //to be called by SSticker + if(!SSticker.setup_done) + return FALSE + if(SSshuttle.emergency && (SSshuttle.emergency.mode == SHUTTLE_ENDGAME)) + return TRUE + if(SSticker.mode.station_was_nuked) + return TRUE + if(force_ending) + return TRUE + +/* + * Generate a list of station goals available to purchase to report to the crew. + * + * Returns a formatted string all station goals that are available to the station. + */ +/datum/controller/subsystem/gamemode/proc/generate_station_goal_report() + if(!SSticker.mode.station_goals.len) + return + . = "
Special Orders for [station_name()]:
" + for(var/datum/station_goal/station_goal as anything in SSticker.mode.station_goals) + station_goal.on_report() + . += station_goal.get_report() + return + +/* + * Generate a list of active station traits to report to the crew. + * + * Returns a formatted string of all station traits (that are shown) affecting the station. + */ +/datum/controller/subsystem/gamemode/proc/generate_station_trait_report() + if(!SSstation.station_traits.len) + return + . = "
Identified shift divergencies:
" + for(var/datum/station_trait/station_trait as anything in SSstation.station_traits) + if(!station_trait.show_in_report) + continue + . += "[station_trait.get_report()]
" + return + +/* /proc/reopen_roundstart_suicide_roles() + var/include_command = CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_positions) + var/list/reopened_jobs = list() + for(var/mob/living/quitter in GLOB.suicided_mob_list) + var/datum/job/job = SSjob.GetJob(quitter.job) + if(!job || !(job.job_flags & JOB_REOPEN_ON_ROUNDSTART_LOSS)) + continue + if(!include_command && job.departments_bitflags & DEPARTMENT_BITFLAG_COMMAND) + continue + job.current_positions = max(job.current_positions - 1, 0) + reopened_jobs += quitter.job + if(CONFIG_GET(flag/reopen_roundstart_suicide_roles_command_report)) + if(reopened_jobs.len) + var/reopened_job_report_positions + for(var/dead_dudes_job in reopened_jobs) + reopened_job_report_positions = "[reopened_job_report_positions ? "[reopened_job_report_positions]\n":""][dead_dudes_job]" + var/suicide_command_report = "Central Command Human Resources Board
\ + Notice of Personnel Change
\ + To personnel management staff aboard [station_name()]:
\ + Our medical staff have detected a series of anomalies in the vital sensors \ + of some of the staff aboard your station.
\ + Further investigation into the situation on our end resulted in us discovering \ + a series of rather... unforturnate decisions that were made on the part of said staff.
\ + As such, we have taken the liberty to automatically reopen employment opportunities for the positions of the crew members \ + who have decided not to partake in our research. We will be forwarding their cases to our employment review board \ + to determine their eligibility for continued service with the company (and of course the \ + continued storage of cloning records within the central medical backup server.)
\ + The following positions have been reopened on our behalf:
\ + [reopened_job_report_positions]" + print_command_report(suicide_command_report, "Central Command Personnel Update") */ + +////////////////////////// +//Reports player logouts// +////////////////////////// +/* /proc/display_roundstart_logout_report() + var/list/msg = list("[SPAN_BOLDNOTICE("Roundstart logout report")]\n\n") + for(var/i in GLOB.mob_living_list) + var/mob/living/L = i + var/mob/living/carbon/C = L + if (istype(C) && !C.last_mind) + continue // never had a client + if(L.ckey && !GLOB.directory[L.ckey]) + msg += "[L.name] ([L.key]), the [L.job] (Disconnected)\n" + if(L.ckey && L.client) + var/failed = FALSE + if(L.client.inactivity >= (ROUNDSTART_LOGOUT_REPORT_TIME / 2)) //Connected, but inactive (alt+tabbed or something) + msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n" + failed = TRUE //AFK client + if(!failed && L.stat) + if(L.suiciding) //Suicider + msg += "[L.name] ([L.key]), the [L.job] ([SPAN_BOLDANNOUNCE("Suicide")])\n" + failed = TRUE //Disconnected client + if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT)) + msg += "[L.name] ([L.key]), the [L.job] (Dying)\n" + failed = TRUE //Unconscious + if(!failed && L.stat == DEAD) + msg += "[L.name] ([L.key]), the [L.job] (Dead)\n" + failed = TRUE //Dead + continue //Happy connected client + for(var/mob/dead/observer/D in GLOB.dead_mob_list) + if(D.mind && D.mind.current == L) + if(L.stat == DEAD) + if(L.suiciding) //Suicider + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([SPAN_BOLDANNOUNCE("Suicide")])\n" + continue //Disconnected client + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] (Dead)\n" + continue //Dead mob, ghost abandoned + else + if(D.can_reenter_corpse) + continue //Adminghost, or cult/wizard ghost + else + msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([SPAN_BOLDANNOUNCE("Ghosted")])\n" + continue //Ghosted while alive + for (var/C in GLOB.admins) + to_chat(C, msg.Join()) */ + +/datum/controller/subsystem/gamemode/proc/generate_station_goals() + var/list/possible = subtypesof(/datum/station_goal) + var/goal_weights = 0 + while(possible.len && goal_weights < 1) // station goal budget is 1 + var/datum/station_goal/picked = pick_n_take(possible) + goal_weights += initial(picked.weight) + SSticker.mode.station_goals += new picked + +//Set result and news report here +/datum/controller/subsystem/gamemode/proc/set_round_result() + SSticker.mode_result = "undefined" + if(SSticker.mode.station_was_nuked) + SSticker.news_report = STATION_DESTROYED_NUKE + if(EMERGENCY_ESCAPED_OR_ENDGAMED) + SSticker.news_report = STATION_EVACUATED + if(SSshuttle.emergency.is_hijacked()) + SSticker.news_report = SHUTTLE_HIJACK + +/// Loads json event config values from events.txt +/datum/controller/subsystem/gamemode/proc/load_event_config_vars() + var/json_file = file("[global.config.directory]/events.json") + if(!fexists(json_file)) + return + var/list/decoded = json_decode(file2text(json_file)) + for(var/event_text_path in decoded) + var/event_path = text2path(event_text_path) + var/datum/round_event_control/event + for(var/datum/round_event_control/iterated_event as anything in control) + if(iterated_event.type == event_path) + event = iterated_event + break + if(!event) + continue + var/list/var_list = decoded[event_text_path] + for(var/variable in var_list) + var/value = var_list[variable] + switch(variable) + if("weight") + event.weight = value + if("min_players") + event.min_players = value + if("max_occurrences") + event.max_occurrences = value + if("earliest_start") + event.earliest_start = value * (1 MINUTES) + if("track") + if(value in event_tracks) + event.track = value + if("cost") + event.cost = value + if("reoccurence_penalty_multiplier") + event.reoccurence_penalty_multiplier = value + if("shared_occurence_type") + if(!isnull(value)) + value = "[value]" + event.shared_occurence_type = value + +/// Loads config values from game_options.txt +/datum/controller/subsystem/gamemode/proc/load_config_vars() + point_gain_multipliers[EVENT_TRACK_MUNDANE] = CONFIG_GET(number/mundane_point_gain_multiplier) + point_gain_multipliers[EVENT_TRACK_MODERATE] = CONFIG_GET(number/moderate_point_gain_multiplier) + point_gain_multipliers[EVENT_TRACK_MAJOR] = CONFIG_GET(number/major_point_gain_multiplier) + point_gain_multipliers[EVENT_TRACK_ROLESET] = CONFIG_GET(number/roleset_point_gain_multiplier) + point_gain_multipliers[EVENT_TRACK_OBJECTIVES] = CONFIG_GET(number/objectives_point_gain_multiplier) + + roundstart_point_multipliers[EVENT_TRACK_MUNDANE] = CONFIG_GET(number/mundane_roundstart_point_multiplier) + roundstart_point_multipliers[EVENT_TRACK_MODERATE] = CONFIG_GET(number/moderate_roundstart_point_multiplier) + roundstart_point_multipliers[EVENT_TRACK_MAJOR] = CONFIG_GET(number/major_roundstart_point_multiplier) + roundstart_point_multipliers[EVENT_TRACK_ROLESET] = CONFIG_GET(number/roleset_roundstart_point_multiplier) + roundstart_point_multipliers[EVENT_TRACK_OBJECTIVES] = CONFIG_GET(number/objectives_roundstart_point_multiplier) + + point_thresholds[EVENT_TRACK_MUNDANE] = CONFIG_GET(number/mundane_point_threshold) + point_thresholds[EVENT_TRACK_MODERATE] = CONFIG_GET(number/moderate_point_threshold) + point_thresholds[EVENT_TRACK_MAJOR] = CONFIG_GET(number/major_point_threshold) + point_thresholds[EVENT_TRACK_ROLESET] = CONFIG_GET(number/roleset_point_threshold) + point_thresholds[EVENT_TRACK_OBJECTIVES] = CONFIG_GET(number/objectives_point_threshold) + +/datum/controller/subsystem/gamemode/proc/handle_picking_storyteller() + if(length(GLOB.clients) > MAX_POP_FOR_STORYTELLER_VOTE) + secret_storyteller = TRUE + selected_storyteller = pick_weight(get_valid_storytellers(TRUE)) + return + SSvote.initiate_vote(/datum/vote/storyteller, "pick round storyteller", forced = TRUE) + +/datum/controller/subsystem/gamemode/proc/storyteller_vote_choices() + var/list/final_choices = list() + var/list/pick_from = list() + for(var/datum/storyteller/storyboy in get_valid_storytellers()) + if(storyboy.always_votable) + final_choices[storyboy.name] = 0 + else + pick_from[storyboy.name] = storyboy.weight //might be able to refactor this to be slightly better due to get_valid_storytellers returning a weighted list + + var/added_storytellers = 0 + while(added_storytellers < DEFAULT_STORYTELLER_VOTE_OPTIONS && length(pick_from)) + added_storytellers++ + var/picked_storyteller = pick_weight(pick_from) + final_choices[picked_storyteller] = 0 + pick_from -= picked_storyteller + return final_choices + +/datum/controller/subsystem/gamemode/proc/storyteller_desc(storyteller_name) + for(var/storyteller_type in storytellers) + var/datum/storyteller/storyboy = storytellers[storyteller_type] + if(storyboy.name != storyteller_name) + continue + return storyboy.desc + + +/datum/controller/subsystem/gamemode/proc/storyteller_vote_result(winner_name) + for(var/storyteller_type in storytellers) + var/datum/storyteller/storyboy = storytellers[storyteller_type] + if(storyboy.name == winner_name) + selected_storyteller = storyteller_type + break + +///return a weighted list of all storytellers that are currently valid to roll, if return_types is set then we will return types instead of instances +/datum/controller/subsystem/gamemode/proc/get_valid_storytellers(return_types = FALSE) + var/client_amount = length(GLOB.clients) + var/list/valid_storytellers = list() + for(var/storyteller_type in storytellers) + var/datum/storyteller/storyboy = storytellers[storyteller_type] + if(storyboy.restricted || (storyboy.population_min && storyboy.population_min > client_amount) || (storyboy.population_max && storyboy.population_max < client_amount)) + continue + + valid_storytellers[return_types ? storyboy.type : storyboy] = storyboy.weight + return valid_storytellers + +/datum/controller/subsystem/gamemode/proc/init_storyteller() + set_storyteller(selected_storyteller) + +/datum/controller/subsystem/gamemode/proc/set_storyteller(passed_type) + if(!storytellers[passed_type]) + message_admins("Attempted to set an invalid storyteller type: [passed_type], force setting to guide instead.") + storyteller = storytellers[/datum/storyteller/guide] //if we dont have any then we brick, lets not do that + CRASH("Attempted to set an invalid storyteller type: [passed_type].") + storyteller = storytellers[passed_type] + if(!secret_storyteller) + send_to_playing_players(span_notice("Storyteller is [storyteller.name]!")) + send_to_playing_players(span_notice("[storyteller.welcome_text]")) + else + send_to_observers(span_boldbig("Storyteller is [storyteller.name]!")) //observers still get to know + log_storyteller("Storyteller set: [storyteller.name]") + +/// Panel containing information, variables and controls about the gamemode and scheduled event +/datum/controller/subsystem/gamemode/proc/admin_panel(mob/user) + update_crew_infos() + total_valid_antags = 0 + for(var/mob/checked_mob in GLOB.mob_list) + if(!checked_mob.mind) + continue + if(!checked_mob.mind.special_role) + continue + if(checked_mob.stat == DEAD) + continue + total_valid_antags++ + + var/round_started = SSticker.HasRoundStarted() + var/list/dat = list() + dat += "Storyteller: [storyteller ? "[storyteller.name]" : "None"] " + dat += " HALT Storyteller Event Panel Set Storyteller Refresh" + dat += "
Storyteller determines points gained, event chances, and is the entity responsible for rolling events." + dat += "
Active Players: [active_players] (Head: [head_crew], Sec: [sec_crew], Eng: [eng_crew], Med: [med_crew])" + dat += "
Antagonist Count vs Maximum: [total_valid_antags] / [get_antag_cap()]" + dat += "
" + dat += "Main" + dat += " Variables" + dat += "
" + switch(panel_page) + if(GAMEMODE_PANEL_VARIABLES) + dat += "Reload Config Vars Configs located in game_options.txt." + dat += "
Point Gains Multipliers (only over time):" + dat += "
This affects points gained over time towards scheduling new events of the tracks." + for(var/track in event_tracks) + dat += "
[track]: [point_gain_multipliers[track]]" + dat += "
" + + dat += "Roundstart Points Multipliers:" + dat += "
This affects points generated for roundstart events and antagonists." + for(var/track in event_tracks) + dat += "
[track]: [roundstart_point_multipliers[track]]" + dat += "
" + + dat += "Point Thresholds:" + dat += "
Those are thresholds the tracks require to reach with points to make an event." + for(var/track in event_tracks) + dat += "
[track]: [point_thresholds[track]]" + + if(GAMEMODE_PANEL_MAIN) + var/even = TRUE + dat += "
Event Tracks:
" + dat += "Every track represents progression towards scheduling an event of it's severity" + dat += "| Track | " + dat += "Progress | " + dat += "Next | " + dat += "Forced | " + dat += "Actions | " + dat += "
| [track] - [last_points] per process. | " //Track + dat += "[percent]% ([lower]/[upper]) | " //Progress + dat += "~[next] seconds | " //Next + var/datum/round_event_control/forced_event = forced_next_events[track] + var/forced = forced_event ? "[forced_event.name] X" : "" + dat += "[forced] | " //Forced + dat += "Set Pts. Next Event | " //Actions + dat += "
Scheduled Events:
" + dat += "| Name | " + dat += "Severity | " + dat += "Time | " + dat += "Actions | " + dat += "
| [scheduled.event.name] | " //Name + dat += "[scheduled.event.track] | " //Severity + var/time = (scheduled.event.roundstart && !round_started) ? "ROUNDSTART" : "[(scheduled.start_time - world.time) / (1 SECONDS)] s." + dat += "[time] | " //Time + dat += "[scheduled.get_href_actions()] | " //Actions + dat += "
Running Events:
" + dat += "| Name | " + dat += "Actions | " + dat += "
| [event.control.name] | " //Name + dat += "-TBA- | " //Actions + dat += "
Repetition penalty multiplier: [storyteller.event_repetition_multiplier]" + dat += "
Cost variance: [storyteller.cost_variance]" + if(storyteller.tag_multipliers) + dat += "
Tag multipliers:" + for(var/tag in storyteller.tag_multipliers) + dat += "[tag]:[storyteller.tag_multipliers[tag]] | " + storyteller.calculate_weights(statistics_track_page) + else + dat += "Storyteller: None
Weight and chance statistics will be inaccurate due to the present lack of a storyteller." + dat += "
Roundstart Events Forced Roundstart events will use rolled points, and are guaranteed to trigger (even if the used points are not enough)" + dat += "
Avg. event intervals: " + for(var/track in event_tracks) + if(last_point_gains[track]) + var/est_time = round(point_thresholds[track] / last_point_gains[track] / STORYTELLER_WAIT_TIME * 40 / 6) / 10 + dat += "[track]: ~[est_time] m. | " + dat += "
" + for(var/track in EVENT_PANEL_TRACKS) + dat += "[track]" + dat += "
" + /// Create event info and stats table + dat += "
| Name | " + dat += "Tags | " + dat += "Times | " + dat += "Max Times | " + dat += "Min Pop | " + dat += "Min Time | " + dat += "Can Occur | " + dat += "Failure Reason | " + dat += "Weight | " + dat += "Actions | " + dat += "
| [event.name] | " //Name + dat += "" //Tags + for(var/tag in event.tags) + dat += "[capitalize(tag)], " + dat += " | " + var/occurence_string = "[event.occurrences]" + if(event.shared_occurence_type) + occurence_string += " (shared: [event.get_occurences()])" + var/max_occurence_string = "[event.max_occurrences]" + dat += "[occurence_string] | " //Occurences + dat += "[max_occurence_string] | " //Max Occurences + dat += "[event.min_players] | " //Minimum pop + dat += "[event.earliest_start / (1 MINUTES)] mins | " //Minimum time + dat += "[assoc_spawn_weight[event] ? "Yes" : "No"] | " //Can happen? + dat += "[event.return_failure_string(active_players)] | " //Why can't happen? + var/weight_string = "Modified: [event.calculated_weight][weight_string] | " //Weight + dat += "[event.get_href_actions()] | " //Actions + dat += "