Files
Bubberstation/code/modules/deathmatch/deathmatch_modifier.dm
2025-11-04 19:41:41 -05:00

608 lines
24 KiB
Plaintext

///Deathmatch modifiers are little options the host can choose to spice the match a bit.
/datum/deathmatch_modifier
/// The name of the modifier
var/name = "Unnamed Modifier"
/// A small description/tooltip shown in the UI
var/description = "What the heck does this do?"
/// The color of the button shown in the UI
var/color = "blue"
/// A lazylist of modifier typepaths this is incompatible with.
var/list/datum/deathmatch_modifier/blacklisted_modifiers
/// A lazylist of map typepaths this is incomptable with.
var/list/datum/lazy_template/deathmatch/blacklisted_maps
/// Is this trait exempted from the "Random Modifiers" modifier.
var/random_exempted = FALSE
///Whether or not this modifier can be selected, for both host and player-selected modifiers.
/datum/deathmatch_modifier/proc/selectable(datum/deathmatch_lobby/lobby)
SHOULD_CALL_PARENT(TRUE)
if(!random_exempted && (/datum/deathmatch_modifier/random in lobby.modifiers))
return FALSE
if(length(lobby.modifiers & blacklisted_modifiers))
return FALSE
if (map_incompatible(lobby.map))
return FALSE
for(var/modpath in lobby.modifiers)
if(src in GLOB.deathmatch_game.modifiers[modpath].blacklisted_modifiers)
return FALSE
return TRUE
/// Returns TRUE if map.type is in our blacklisted maps, FALSE otherwise.
/datum/deathmatch_modifier/proc/map_incompatible(datum/lazy_template/deathmatch/map)
if (map?.type in blacklisted_maps)
return TRUE
return FALSE
///Called when selecting the deathmatch modifier.
/datum/deathmatch_modifier/proc/on_select(datum/deathmatch_lobby/lobby)
return
///When the host changes his mind and unselects it.
/datum/deathmatch_modifier/proc/unselect(datum/deathmatch_lobby/lobby)
return
///Called when the host chooses to change map. Returns FALSE if the new map is incompatible, TRUE otherwise.
/datum/deathmatch_modifier/proc/on_map_changed(datum/deathmatch_lobby/lobby)
if (map_incompatible(lobby.map))
lobby.unselect_modifier(src)
return FALSE
return TRUE
///Called as the game is about to start.
/datum/deathmatch_modifier/proc/on_start_game(datum/deathmatch_lobby/lobby)
return
///Called as the game has ended, right before the reservation is deleted.
/datum/deathmatch_modifier/proc/on_end_game(datum/deathmatch_lobby/lobby)
return
///Apply the modifier to the newly spawned player as the game is about to start
/datum/deathmatch_modifier/proc/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
return
/datum/deathmatch_modifier/health
name = "Double-Health"
description = "Doubles your starting health"
blacklisted_modifiers = list(/datum/deathmatch_modifier/health/half, /datum/deathmatch_modifier/health/triple)
var/multiplier = 2
/datum/deathmatch_modifier/health/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.maxHealth *= multiplier
player.health *= multiplier
/datum/deathmatch_modifier/health/half
name = "Half-Health"
description = "It's your funeral"
blacklisted_modifiers = list(/datum/deathmatch_modifier/health, /datum/deathmatch_modifier/health/triple)
multiplier = 0.5
/datum/deathmatch_modifier/health/triple
name = "Triple-Health"
description = "When \"Double-Health\" isn't enough..."
multiplier = 3
blacklisted_modifiers = list(/datum/deathmatch_modifier/health, /datum/deathmatch_modifier/health/half)
/datum/deathmatch_modifier/tenacity
name = "Tenacity"
description = "Unaffected by critical condition and pain"
/datum/deathmatch_modifier/tenacity/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.add_traits(list(TRAIT_NOSOFTCRIT, TRAIT_NOHARDCRIT, TRAIT_ANALGESIA, TRAIT_NO_DAMAGE_OVERLAY), DEATHMATCH_TRAIT)
/datum/deathmatch_modifier/no_wounds
name = "No Wounds"
description = "Ah, the good ol' days when people did't have literal dents in their skulls..."
/datum/deathmatch_modifier/no_wounds/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
ADD_TRAIT(player, TRAIT_NEVER_WOUNDED, DEATHMATCH_TRAIT)
/datum/deathmatch_modifier/no_knockdown
name = "No Knockdowns"
description = "I'M FUCKING INVINCIBLE!"
/datum/deathmatch_modifier/no_knockdown/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.add_traits(list(TRAIT_STUNIMMUNE, TRAIT_SLEEPIMMUNE), DEATHMATCH_TRAIT)
/datum/deathmatch_modifier/no_slowdown
name = "No Slowdowns"
description = "You're too slow!"
/datum/deathmatch_modifier/no_slowdown/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
ADD_TRAIT(player, TRAIT_IGNORESLOWDOWN, DEATHMATCH_TRAIT)
/datum/deathmatch_modifier/teleport
name = "Random Teleports"
description = "One moment I'm here, the next I'm there"
///A lazylist of lobbies that have this modifier enabled
var/list/signed_lobbies
///The cooldown to the teleportation effect.
COOLDOWN_DECLARE(teleport_cd)
/datum/deathmatch_modifier/teleport/on_select(datum/deathmatch_lobby/lobby)
if(isnull(signed_lobbies))
START_PROCESSING(SSprocessing, src)
LAZYADD(signed_lobbies, lobby)
RegisterSignal(lobby, COMSIG_QDELETING, PROC_REF(remove_lobby))
/datum/deathmatch_modifier/teleport/unselect(datum/deathmatch_lobby/lobby)
remove_lobby(lobby)
/datum/deathmatch_modifier/teleport/proc/remove_lobby(datum/deathmatch_lobby/lobby)
SIGNAL_HANDLER
LAZYREMOVE(signed_lobbies, lobby)
UnregisterSignal(lobby, COMSIG_QDELETING)
if(isnull(signed_lobbies))
STOP_PROCESSING(SSprocessing, src)
/datum/deathmatch_modifier/teleport/process(seconds_per_tick)
if(!COOLDOWN_FINISHED(src, teleport_cd))
return
for(var/datum/deathmatch_lobby/lobby as anything in signed_lobbies)
if(lobby.playing != DEATHMATCH_PLAYING || isnull(lobby.location))
continue
for(var/ckey in lobby.players)
var/mob/living/player = lobby.players[ckey]["mob"]
if(istype(player))
continue
var/turf/destination
for(var/attempt in 1 to 5)
var/turf/possible_destination = pick(lobby.location.reserved_turfs)
if(isopenturf(destination) && !isgroundlessturf(destination))
destination = possible_destination
break
if(isnull(destination))
continue
//I want this modifier to be compatible with 'Mounts' and 'Paraplegic' wheelchairs.
var/atom/movable/currently_buckled = player.buckled
do_teleport(player, destination, 0, asoundin = 'sound/effects/phasein.ogg', forced = TRUE)
if(currently_buckled && !currently_buckled.anchored)
do_teleport(currently_buckled, destination, 0, asoundin = 'sound/effects/phasein.ogg', forced = TRUE)
currently_buckled.buckle_mob(player)
COOLDOWN_START(src, teleport_cd, rand(12 SECONDS, 24 SECONDS))
/datum/deathmatch_modifier/snail_crawl
name = "Snail Crawl"
description = "Lube the floor as you slather it with your body"
blacklisted_modifiers = list(/datum/deathmatch_modifier/no_gravity)
/datum/deathmatch_modifier/snail_crawl/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.AddElement(/datum/element/lube_walking, require_resting = TRUE)
/datum/deathmatch_modifier/blinking_and_breathing
name = "Manual Blinking/Breathing"
description = "Ruin everyone's fun by forcing them to breathe and blink manually"
/datum/deathmatch_modifier/blinking_and_breathing/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.AddComponent(/datum/component/manual_blinking)
player.AddComponent(/datum/component/manual_breathing)
/datum/deathmatch_modifier/forcefield_trail
name = "Forcefield Trail"
description = "You leave short-living unpassable forcefields in your wake"
/datum/deathmatch_modifier/forcefield_trail/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.AddElement(/datum/element/effect_trail, /obj/effect/forcefield/cosmic_field/extrafast)
/datum/deathmatch_modifier/xray
name = "X-Ray Vision"
description = "See through the cordons of the deathmatch arena!"
blacklisted_modifiers = list(/datum/deathmatch_modifier/thermal)
/datum/deathmatch_modifier/xray/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
ADD_TRAIT(player, TRAIT_XRAY_VISION, DEATHMATCH_TRAIT)
player.update_sight()
/datum/deathmatch_modifier/thermal
name = "Thermal Vision"
description = "See mobs through walls"
blacklisted_modifiers = list(/datum/deathmatch_modifier/xray)
/datum/deathmatch_modifier/thermal/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
ADD_TRAIT(player, TRAIT_THERMAL_VISION, DEATHMATCH_TRAIT)
player.update_sight()
/datum/deathmatch_modifier/regen
name = "Health Regen"
description = "The closest thing to free health insurance you can get"
/datum/deathmatch_modifier/regen/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.AddComponent(/datum/component/regenerator, regeneration_delay = 4 SECONDS, brute_per_second = 2.5, burn_per_second = 2.5, tox_per_second = 2.5)
/datum/deathmatch_modifier/nearsightness
name = "Nearsightness"
description = "Oops, I forgot my glasses at home"
/datum/deathmatch_modifier/nearsightness/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.become_nearsighted(DEATHMATCH_TRAIT)
/datum/deathmatch_modifier/ocelot
name = "Ocelot"
description = "Shoot faster, with extra ricochet and less spread. You're pretty good!"
blacklisted_modifiers = list(/datum/deathmatch_modifier/stormtrooper)
/datum/deathmatch_modifier/ocelot/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.add_traits(list(TRAIT_NICE_SHOT, TRAIT_DOUBLE_TAP), DEATHMATCH_TRAIT)
RegisterSignal(player, COMSIG_MOB_FIRED_GUN, PROC_REF(reduce_spread))
RegisterSignal(player, COMSIG_PROJECTILE_FIRER_BEFORE_FIRE, PROC_REF(apply_ricochet))
/datum/deathmatch_modifier/ocelot/proc/reduce_spread(mob/user, obj/item/gun/gun_fired, target, params, zone_override, list/bonus_spread_values)
SIGNAL_HANDLER
bonus_spread_values[MIN_BONUS_SPREAD_INDEX] -= 50
bonus_spread_values[MAX_BONUS_SPREAD_INDEX] -= 50
/datum/deathmatch_modifier/ocelot/proc/apply_ricochet(mob/user, obj/projectile/projectile, datum/fired_from, atom/clicked_atom)
SIGNAL_HANDLER
projectile.ricochets_max += 2
projectile.min_ricochets += 2
projectile.ricochet_incidence_leeway = 0
projectile.accuracy_falloff = 0
/datum/deathmatch_modifier/stormtrooper
name = "Stormtrooper Aim"
description = "Fresh out of the 'I Can't Aim For Shit' School"
blacklisted_modifiers = list(/datum/deathmatch_modifier/ocelot)
/datum/deathmatch_modifier/stormtrooper/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
RegisterSignal(player, COMSIG_MOB_FIRED_GUN, PROC_REF(increase_spread))
/datum/deathmatch_modifier/stormtrooper/proc/increase_spread(mob/user, obj/item/gun/gun_fired, target, params, zone_override, list/bonus_spread_values)
SIGNAL_HANDLER
bonus_spread_values[MIN_BONUS_SPREAD_INDEX] += 10
bonus_spread_values[MAX_BONUS_SPREAD_INDEX] += 35
/datum/deathmatch_modifier/four_hands
name = "Four Hands"
description = "When one pair isn't enough..."
/datum/deathmatch_modifier/four_hands/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.change_number_of_hands(4)
/datum/deathmatch_modifier/paraplegic
name = "Paraplegic"
description = "Wheelchairs. For. Everyone."
/datum/deathmatch_modifier/paraplegic/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.gain_trauma(/datum/brain_trauma/severe/paralysis/paraplegic, TRAUMA_RESILIENCE_ABSOLUTE)
///Mounts are being used. Do not spawn wheelchairs.
if(/datum/deathmatch_modifier/mounts in lobby.modifiers)
return
var/obj/vehicle/ridden/wheelchair/motorized/improved/wheels = new (player.loc)
wheels.setDir(player.dir)
wheels.buckle_mob(player)
/datum/deathmatch_modifier/mounts
name = "Mounts"
description = "A horse! A horse! My kingdom for a horse!"
/datum/deathmatch_modifier/mounts/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
///We do a bit of fun over balance here, some mounts may be better than others.
var/mount_path = pick(list(
/mob/living/basic/carp,
/mob/living/basic/pony,
/mob/living/basic/pony/syndicate,
/mob/living/basic/pig,
/mob/living/basic/cow,
/mob/living/basic/cow/moonicorn,
/mob/living/basic/mining/wolf,
/mob/living/basic/mining/goldgrub,
/mob/living/basic/mining/goliath/deathmatch,
))
var/mob/living/basic/mount = new mount_path (player.loc)
mount.tamed(player, null)
mount.befriend(player)
mount.buckle_mob(player)
if(HAS_TRAIT(lobby, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS))
var/obj/item/implant/explosive/deathmatch/implant = new()
implant.implant(mount, silent = TRUE, force = TRUE)
/datum/deathmatch_modifier/no_gravity
name = "No Gravity"
description = "Hone your robusting skills in zero g"
blacklisted_modifiers = list(/datum/deathmatch_modifier/mounts, /datum/deathmatch_modifier/paraplegic, /datum/deathmatch_modifier/minefield)
/datum/deathmatch_modifier/no_gravity/on_start_game(datum/deathmatch_lobby/lobby)
ASYNC
for(var/turf/turf as anything in lobby.location.reserved_turfs)
turf.AddElement(/datum/element/forced_gravity, 0)
CHECK_TICK
/datum/deathmatch_modifier/no_gravity/on_end_game(datum/deathmatch_lobby/lobby)
for(var/turf/turf as anything in lobby.location.reserved_turfs)
turf.RemoveElement(/datum/element/forced_gravity, 0)
/datum/deathmatch_modifier/drop_pod
name = "Drop Pod: Syndies"
description = "Steel Rain: Syndicate Edition"
///A lazylist of lobbies that have this modifier enabled
var/list/signed_lobbies
///The type of drop pod that'll periodically fall from the sky
var/drop_pod_type = /obj/structure/closet/supplypod/podspawn/deathmatch
///A (weighted) list of possible contents of the drop pod. Only one is picked at a time
var/list/contents
///An interval representing the min and max cooldown between each time it's fired.
var/interval = list(7 SECONDS, 12 SECONDS)
///How many (a number or a two keyed list) drop pods can be dropped at a time.
var/amount = list(1, 2)
///The cooldown for dropping pods into every affected deathmatch arena.
COOLDOWN_DECLARE(drop_pod_cd)
/datum/deathmatch_modifier/drop_pod/New()
. = ..()
populate_contents()
/datum/deathmatch_modifier/drop_pod/on_select(datum/deathmatch_lobby/lobby)
if(isnull(signed_lobbies))
START_PROCESSING(SSprocessing, src)
LAZYADD(signed_lobbies, lobby)
RegisterSignal(lobby, COMSIG_QDELETING, PROC_REF(remove_lobby))
/datum/deathmatch_modifier/drop_pod/unselect(datum/deathmatch_lobby/lobby)
remove_lobby(lobby)
/datum/deathmatch_modifier/drop_pod/proc/remove_lobby(datum/deathmatch_lobby/lobby)
SIGNAL_HANDLER
LAZYREMOVE(signed_lobbies, lobby)
UnregisterSignal(lobby, COMSIG_QDELETING)
if(isnull(signed_lobbies))
STOP_PROCESSING(SSprocessing, src)
/datum/deathmatch_modifier/drop_pod/process(seconds_per_tick)
if(!COOLDOWN_FINISHED(src, drop_pod_cd))
return
var/pod_spawned = FALSE
for(var/datum/deathmatch_lobby/lobby as anything in signed_lobbies)
if(lobby.playing != DEATHMATCH_PLAYING || isnull(lobby.location))
continue
var/yet_to_spawn = islist(amount) ? rand(amount[1], amount[2]) : amount
for(var/attempt in 1 to 10)
var/turf/to_strike = pick(lobby.location.reserved_turfs)
if(!isopenturf(to_strike) || isgroundlessturf(to_strike))
continue
var/atom/movable/to_spawn
if(length(contents))
var/spawn_path = pick_weight(contents)
to_spawn = new spawn_path (to_strike)
if(isliving(to_spawn) && HAS_TRAIT(lobby, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS))
var/obj/item/implant/explosive/deathmatch/implant = new()
implant.implant(to_spawn, silent = TRUE, force = TRUE)
podspawn(list(
"path" = drop_pod_type,
"target" = to_strike,
"spawn" = to_spawn,
))
pod_spawned = TRUE
yet_to_spawn--
if(yet_to_spawn == 0)
break
if(pod_spawned)
COOLDOWN_START(src, drop_pod_cd, rand(interval[1], interval[2]))
/datum/deathmatch_modifier/drop_pod/proc/populate_contents()
contents = typesof(/mob/living/basic/trooper/syndicate)
for(var/typepath in contents) //Make sure to set even weights for the keys or `pick_weight` won't work.
contents[typepath] = 1
/datum/deathmatch_modifier/drop_pod/monsters
name = "Drop Pod: Monsters"
description = "Monsters are raining from the sky!"
/datum/deathmatch_modifier/drop_pod/monsters/populate_contents()
contents = list(
/mob/living/basic/ant = 2,
/mob/living/basic/construct/proteon = 2,
/mob/living/basic/dark_wizard = 2,
/mob/living/basic/flesh_spider = 2,
/mob/living/basic/garden_gnome = 2,
/mob/living/basic/goose = 2,
/mob/living/basic/killer_tomato = 2,
/mob/living/basic/leaper = 1,
/mob/living/basic/mega_arachnid = 1,
/mob/living/basic/mining/goliath = 1,
/mob/living/basic/mining/ice_demon = 1,
/mob/living/basic/mining/ice_whelp = 1,
/mob/living/basic/mining/lobstrosity = 1,
/mob/living/basic/mining/mook = 2,
/mob/living/basic/mouse/rat = 2,
/mob/living/basic/vatbeast = 1,
/mob/living/basic/spider/giant/nurse/scrawny = 2,
/mob/living/basic/spider/giant/tarantula/scrawny = 2,
/mob/living/basic/spider/giant/hunter/scrawny = 2,
/mob/living/simple_animal/hostile/ooze = 1,
)
/datum/deathmatch_modifier/drop_pod/missiles
name = "Drop Pod: Cruise Missiles"
description = "You're going to get shelled hard"
drop_pod_type = /obj/structure/closet/supplypod/deadmatch_missile
interval = list(3 SECONDS, 5 SECONDS)
amount = list(1, 3)
/datum/deathmatch_modifier/drop_pod/missiles/populate_contents()
return
/datum/deathmatch_modifier/explode_on_death
name = "Explosive Death"
description = "Everyone gets a microbomb that cannot be manually activated."
/datum/deathmatch_modifier/explode_on_death/on_start_game(datum/deathmatch_lobby/lobby)
ADD_TRAIT(lobby, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS, DEATHMATCH_TRAIT)
/datum/deathmatch_modifier/explode_on_death/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
var/obj/item/implant/explosive/deathmatch/implant = new()
implant.implant(player, silent = TRUE, force = TRUE)
/datum/deathmatch_modifier/helgrasp
name = "Helgrasped"
description = "Cursed hands are being thrown at you!"
/datum/deathmatch_modifier/helgrasp/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
var/metabolism_rate = /datum/reagent/inverse/helgrasp/heretic::metabolization_rate
player.reagents.add_reagent(/datum/reagent/inverse/helgrasp/heretic, initial(lobby.map.automatic_gameend_time) / metabolism_rate)
/datum/deathmatch_modifier/wasted
name = "Wasted"
description = "You've had one drink too many"
/datum/deathmatch_modifier/wasted/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.adjust_drunk_effect(rand(30, 35))
var/metabolism_rate = /datum/reagent/consumable/ethanol/jack_rose::metabolization_rate
player.reagents.add_reagent(/datum/reagent/consumable/ethanol/jack_rose, initial(lobby.map.automatic_gameend_time) * 0.35 / metabolism_rate)
/datum/deathmatch_modifier/monkeys
name = "Monkeyfication"
description = "Go back, I want to be monkey!"
/datum/deathmatch_modifier/monkeys/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
//we don't call monkeyize(), because it'd set the player name to a generic "monkey(number)".
player.set_species(/datum/species/monkey)
/datum/deathmatch_modifier/inverted_movement
name = "Inverted Movement"
description = "Up is down, left is right"
/datum/deathmatch_modifier/inverted_movement/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.AddElement(/datum/element/inverted_movement)
/datum/deathmatch_modifier/minefield
name = "Minefield"
description = "Oh, it seems you've trotted on a mine!"
/datum/deathmatch_modifier/minefield/on_start_game(datum/deathmatch_lobby/lobby)
var/list/mines = subtypesof(/obj/effect/mine)
mines -= list(
/obj/effect/mine/explosive, //too lethal.
/obj/effect/mine/kickmine, //will kick the client, lol
/obj/effect/mine/gas, //Just spawns oxygen.
/obj/effect/mine/gas/n2o, //no sleeping please.
)
///1 every 11 turfs, but it will actually spawn fewer mines since groundless and closed turfs are skipped.
var/mines_to_spawn = length(lobby.location.reserved_turfs) * 0.09
for(var/iteration in 1 to mines_to_spawn)
var/turf/target_turf = pick(lobby.location.reserved_turfs)
if(!isopenturf(target_turf) || isgroundlessturf(target_turf))
continue
///don't spawn mine next to player spawns.
if(locate(/obj/effect/landmark/deathmatch_player_spawn) in range(1, target_turf))
continue
///skip belt loops or they'll explode right away.
if(locate(/obj/machinery/conveyor) in target_turf.contents)
continue
var/mine_path = pick(mines)
new mine_path (target_turf)
/datum/deathmatch_modifier/random
name = "Random Modifiers"
description = "Picks 3 to 5 random modifiers as the game is about to start"
random_exempted = TRUE
/datum/deathmatch_modifier/random/on_select(datum/deathmatch_lobby/lobby)
///remove any other global modifier if chosen. It'll pick random ones when the time comes.
for(var/modpath in lobby.modifiers)
var/datum/deathmatch_modifier/modifier = GLOB.deathmatch_game.modifiers[modpath]
if(modifier.random_exempted)
continue
modifier.unselect(lobby)
lobby.modifiers -= modpath
/datum/deathmatch_modifier/random/on_start_game(datum/deathmatch_lobby/lobby)
lobby.modifiers -= type //remove it before attempting to select other modifiers, or they'll fail.
var/static/list/static_pool
if(isnull(static_pool))
static_pool = subtypesof(/datum/deathmatch_modifier)
for(var/datum/deathmatch_modifier/modpath as anything in static_pool)
if(initial(modpath.random_exempted))
static_pool -= modpath
var/list/modifiers_pool = static_pool.Copy()
for(var/modpath in modifiers_pool)
var/datum/deathmatch_modifier/modifier = GLOB.deathmatch_game.modifiers[modpath]
if(!modifier.selectable(lobby))
modifiers_pool -= modpath
///Pick global modifiers at random.
for(var/iteration in 1 to rand(3, 5))
var/datum/deathmatch_modifier/modifier = GLOB.deathmatch_game.modifiers[pick_n_take(modifiers_pool)]
modifier.on_select(lobby)
modifier.on_start_game(lobby)
lobby += modifier.type
modifiers_pool -= modifier.blacklisted_modifiers
if(!length(modifiers_pool))
return
/datum/deathmatch_modifier/any_loadout
name = "Any Loadout Allowed"
description = "Watch players pick Instagib everytime"
random_exempted = TRUE
/datum/deathmatch_modifier/any_loadout/selectable(datum/deathmatch_lobby/lobby)
. = ..()
if(!.)
return
return lobby.map.allowed_loadouts
/datum/deathmatch_modifier/any_loadout/on_select(datum/deathmatch_lobby/lobby)
lobby.loadouts = GLOB.deathmatch_game.loadouts
/datum/deathmatch_modifier/any_loadout/unselect(datum/deathmatch_lobby/lobby)
lobby.loadouts = lobby.map.allowed_loadouts
/datum/deathmatch_modifier/any_loadout/on_map_changed(datum/deathmatch_lobby/lobby)
if(lobby.loadouts == GLOB.deathmatch_game.loadouts) //This arena already allows any loadout for some reason.
lobby.unselect_modifier(src)
else
lobby.loadouts = GLOB.deathmatch_game.loadouts
/datum/deathmatch_modifier/hear_global_chat
name = "Heightened Hearing"
description = "This lets you hear people through wall, as well as deadchat"
random_exempted = TRUE
/datum/deathmatch_modifier/hear_global_chat/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
player.add_traits(list(TRAIT_SIXTHSENSE, TRAIT_XRAY_HEARING), DEATHMATCH_TRAIT)
/datum/deathmatch_modifier/apply_quirks
name = "Quirks enabled"
description = "Applies selected quirks to all players"
/datum/deathmatch_modifier/apply_quirks/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
if (!player.client)
return
SSquirks.AssignQuirks(player, player.client)
/datum/deathmatch_modifier/martial_artistry
name = "Random martial arts"
description = "Everyone learns a random martial art!"
blacklisted_maps = list(/datum/lazy_template/deathmatch/meatower)
// kaza ruk excluded because its too common and too simple, mushpunch excluded because its horrible and not even funny
var/static/list/weighted_martial_arts = list(
// common
/datum/martial_art/cqc = 30,
/datum/martial_art/the_sleeping_carp = 30,
// uncommon
/datum/martial_art/boxing/evil = 20,
// LEGENDARY
/datum/martial_art/plasma_fist = 5,
/datum/martial_art/wrestling = 5, // wrestling is kinda strong ngl
/datum/martial_art/psychotic_brawling = 5, // a complete meme. sometimes you just get hardstunned. sometimes you punch someone across the room
)
/datum/deathmatch_modifier/martial_artistry/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
. = ..()
var/datum/martial_art/picked_art_path = pick_weight(weighted_martial_arts)
var/datum/martial_art/instantiated_art = new picked_art_path(player)
if (istype(instantiated_art, /datum/martial_art/boxing))
player.mind.adjust_experience(/datum/skill/athletics, SKILL_EXP_LEGENDARY)
instantiated_art.teach(player)
to_chat(player, span_revenboldnotice("Your martial art is [uppertext(instantiated_art.name)]!"))