mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-07-19 03:55:11 +01:00
Deathmatch modifiers (#81673)
## About The Pull Request This PR adds the base to simple modifiers that the host can select to make the minigame ~~worse~~ more entertaining for everyone. Here's the screenshot of the UI (without a few modifiers I added later):  ## Why It's Good For The Game I've seen this minigame is frankly popular lately, so I thought I could contribute to it. ## Changelog 🆑 add: Added 20+ modifiers to the deathmatch minigame, which can be enabled by the host. /🆑
This commit is contained in:
@@ -385,3 +385,8 @@ GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
|
||||
#define SHOVE_BLOCKED (1<<5)
|
||||
///If the obstacle is an object at the border of the turf (so no signal from being sent to the other turf)
|
||||
#define SHOVE_DIRECTIONAL_BLOCKED (1<<6)
|
||||
|
||||
///Deathmatch lobby current status
|
||||
#define DEATHMATCH_NOT_PLAYING 0
|
||||
#define DEATHMATCH_PRE_PLAYING 1
|
||||
#define DEATHMATCH_PLAYING 2
|
||||
|
||||
@@ -951,6 +951,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
|
||||
#define STATION_TRAIT_UNNATURAL_ATMOSPHERE "station_trait_unnatural_atmosphere"
|
||||
#define STATION_TRAIT_VENDING_SHORTAGE "station_trait_vending_shortage"
|
||||
|
||||
///Deathmatch traits
|
||||
#define TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS "deathmath_explosive_implants"
|
||||
|
||||
/// This atom is currently spinning.
|
||||
#define TRAIT_SPINNING "spinning"
|
||||
|
||||
|
||||
@@ -88,6 +88,8 @@
|
||||
#define MAFIA_TRAIT "mafia"
|
||||
/// Trait associated with ctf
|
||||
#define CTF_TRAIT "ctf"
|
||||
/// Trait associated with deathmatch
|
||||
#define DEATHMATCH_TRAIT "deathmatch"
|
||||
/// Trait associated with highlander
|
||||
#define HIGHLANDER_TRAIT "highlander"
|
||||
/// Trait given from playing pretend with baguettes
|
||||
|
||||
@@ -192,3 +192,6 @@
|
||||
/// Orders mobs by health
|
||||
/proc/cmp_mob_health(mob/living/mob_a, mob/living/mob_b)
|
||||
return mob_b.health - mob_a.health
|
||||
|
||||
/proc/cmp_deathmatch_mods(datum/deathmatch_modifier/a, datum/deathmatch_modifier/b)
|
||||
return sorttext(b.name, a.name)
|
||||
|
||||
@@ -100,6 +100,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
|
||||
"STATION_TRAIT_UNNATURAL_ATMOSPHERE" = STATION_TRAIT_UNNATURAL_ATMOSPHERE,
|
||||
"STATION_TRAIT_VENDING_SHORTAGE" = STATION_TRAIT_VENDING_SHORTAGE,
|
||||
),
|
||||
/datum/deathmatch_lobby = list(
|
||||
"TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS" = TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS,
|
||||
),
|
||||
/datum/wound = list(
|
||||
"TRAIT_WOUND_SCANNED" = TRAIT_WOUND_SCANNED,
|
||||
),
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
/// Cooldown for the echolocation.
|
||||
COOLDOWN_DECLARE(cooldown_last)
|
||||
|
||||
/datum/component/echolocation/Initialize(echo_range, cooldown_time, image_expiry_time, fade_in_time, fade_out_time, images_are_static, blocking_trait, echo_group, echo_icon, color_path)
|
||||
/datum/component/echolocation/Initialize(echo_range, cooldown_time, image_expiry_time, fade_in_time, fade_out_time, images_are_static, blocking_trait, echo_group, echo_icon = "echo", color_path)
|
||||
. = ..()
|
||||
var/mob/living/echolocator = parent
|
||||
if(!istype(echolocator))
|
||||
|
||||
@@ -10,10 +10,8 @@
|
||||
var/bonus_tame_chance
|
||||
///Current chance to tame on interaction
|
||||
var/current_tame_chance
|
||||
///For effects once soemthing is tamed
|
||||
var/datum/callback/after_tame
|
||||
|
||||
/datum/component/tameable/Initialize(food_types, tame_chance, bonus_tame_chance, datum/callback/after_tame, unique = TRUE)
|
||||
/datum/component/tameable/Initialize(food_types, tame_chance, bonus_tame_chance, unique = TRUE)
|
||||
if(!isatom(parent)) //yes, you could make a tameable toolbox.
|
||||
return COMPONENT_INCOMPATIBLE
|
||||
|
||||
@@ -24,18 +22,12 @@
|
||||
src.current_tame_chance = tame_chance
|
||||
if(bonus_tame_chance)
|
||||
src.bonus_tame_chance = bonus_tame_chance
|
||||
if(after_tame)
|
||||
src.after_tame = after_tame
|
||||
src.unique = unique
|
||||
|
||||
RegisterSignal(parent, COMSIG_ATOM_ATTACKBY, PROC_REF(try_tame))
|
||||
RegisterSignal(parent, COMSIG_SIMPLEMOB_SENTIENCEPOTION, PROC_REF(on_tame)) //Instantly succeeds
|
||||
RegisterSignal(parent, COMSIG_SIMPLEMOB_TRANSFERPOTION, PROC_REF(on_tame)) //Instantly succeeds
|
||||
|
||||
/datum/component/tameable/Destroy(force)
|
||||
after_tame = null
|
||||
return ..()
|
||||
|
||||
/datum/component/tameable/proc/try_tame(datum/source, obj/item/food, mob/living/attacker, params)
|
||||
SIGNAL_HANDLER
|
||||
if(!is_type_in_list(food, food_types))
|
||||
@@ -70,9 +62,9 @@
|
||||
return living_parent.faction.Find(REF(potential_friend))
|
||||
|
||||
///Ran once taming succeeds
|
||||
/datum/component/tameable/proc/on_tame(atom/source, mob/living/tamer, atom/food, inform_tamer = FALSE)
|
||||
/datum/component/tameable/proc/on_tame(atom/source, mob/living/tamer, obj/item/food, inform_tamer = FALSE)
|
||||
SIGNAL_HANDLER
|
||||
after_tame?.Invoke(tamer, food)//Run custom behavior if needed
|
||||
source.tamed(tamer, food)//Run custom behavior if needed
|
||||
|
||||
if(isliving(parent) && isliving(tamer))
|
||||
INVOKE_ASYNC(source, TYPE_PROC_REF(/mob/living, befriend), tamer)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/datum/element/inverted_movement
|
||||
|
||||
/datum/element/inverted_movement/Attach(datum/target)
|
||||
. = ..()
|
||||
if(!isliving(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
RegisterSignal(target, COMSIG_MOB_CLIENT_PRE_MOVE, PROC_REF(invert_movement))
|
||||
|
||||
/datum/element/inverted_movement/Detach(datum/source)
|
||||
UnregisterSignal(source, COMSIG_MOB_CLIENT_PRE_MOVE)
|
||||
return ..()
|
||||
|
||||
/datum/element/inverted_movement/proc/invert_movement(mob/living/source, move_args)
|
||||
move_args[MOVE_ARG_DIRECTION] = REVERSE_DIR(move_args[MOVE_ARG_DIRECTION])
|
||||
@@ -794,6 +794,10 @@
|
||||
/atom/proc/setClosed()
|
||||
return
|
||||
|
||||
///Called after the atom is 'tamed' for type-specific operations, Usually called by the tameable component but also other things.
|
||||
/atom/proc/tamed(mob/living/tamer, obj/item/food)
|
||||
return
|
||||
|
||||
/**
|
||||
* Used to attempt to charge an object with a payment component.
|
||||
*
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
var/active = FALSE
|
||||
///The final countdown (delay before we explode)
|
||||
var/delay = MICROBOMB_DELAY
|
||||
///If the delay is equal or lower to MICROBOMB_DELAY (0.7 sec), the explosion will be instantaneous.
|
||||
var/instant_explosion = TRUE
|
||||
///Radius of weak devastation explosive impact
|
||||
var/explosion_light = MICROBOMB_EXPLOSION_LIGHT
|
||||
///Radius of medium devastation explosive impact
|
||||
@@ -33,7 +35,8 @@
|
||||
var/no_paralyze = FALSE
|
||||
///Do we override other explosive implants?
|
||||
var/master_implant = FALSE
|
||||
|
||||
///Will this implant notify ghosts when activated?
|
||||
var/notify_ghosts = TRUE
|
||||
|
||||
/obj/item/implant/explosive/proc/on_death(datum/source, gibbed)
|
||||
SIGNAL_HANDLER
|
||||
@@ -73,7 +76,7 @@
|
||||
var/turf/boomturf = get_turf(imp_in)
|
||||
message_admins("[ADMIN_LOOKUPFLW(imp_in)] has activated their [name] at [ADMIN_VERBOSEJMP(boomturf)], with cause of [cause].")
|
||||
//If the delay is shorter or equal to the default delay, just blow up already jeez
|
||||
if(delay <= MICROBOMB_DELAY)
|
||||
if(delay <= MICROBOMB_DELAY && instant_explosion)
|
||||
explode()
|
||||
return
|
||||
timed_explosion()
|
||||
@@ -119,14 +122,15 @@
|
||||
/obj/item/implant/explosive/proc/timed_explosion()
|
||||
imp_in.visible_message(span_warning("[imp_in] starts beeping ominously!"))
|
||||
|
||||
notify_ghosts(
|
||||
"[imp_in] is about to detonate their explosive implant!",
|
||||
source = src,
|
||||
header = "Tick Tick Tick...",
|
||||
notify_flags = NOTIFY_CATEGORY_NOFLASH,
|
||||
ghost_sound = 'sound/machines/warning-buzzer.ogg',
|
||||
notify_volume = 75,
|
||||
)
|
||||
if(notify_ghosts)
|
||||
notify_ghosts(
|
||||
"[imp_in] is about to detonate their explosive implant!",
|
||||
source = src,
|
||||
header = "Tick Tick Tick...",
|
||||
notify_flags = NOTIFY_CATEGORY_NOFLASH,
|
||||
ghost_sound = 'sound/machines/warning-buzzer.ogg',
|
||||
notify_volume = 75,
|
||||
)
|
||||
|
||||
playsound(loc, 'sound/items/timer.ogg', 30, FALSE)
|
||||
if(!panic_beep_sound)
|
||||
@@ -203,6 +207,13 @@
|
||||
if(source.health < source.crit_threshold)
|
||||
INVOKE_ASYNC(src, PROC_REF(activate), "deniability")
|
||||
|
||||
/obj/item/implant/explosive/deathmatch
|
||||
name = "deathmatch microbomb implant"
|
||||
delay = 0.5 SECONDS
|
||||
actions_types = null
|
||||
instant_explosion = FALSE
|
||||
notify_ghosts = FALSE
|
||||
|
||||
/obj/item/implanter/explosive
|
||||
name = "implanter (microbomb)"
|
||||
imp_type = /obj/item/implant/explosive
|
||||
|
||||
@@ -35,6 +35,9 @@
|
||||
wound_bonus = 30
|
||||
embedding = list(embed_chance=70, ignore_throwspeed_threshold=TRUE, fall_chance=1)
|
||||
|
||||
/obj/projectile/bullet/shrapnel/short_range
|
||||
range = 5
|
||||
|
||||
/obj/projectile/bullet/shrapnel/mega
|
||||
name = "flying shrapnel hunk"
|
||||
range = 45
|
||||
|
||||
@@ -70,6 +70,16 @@
|
||||
bluespace = TRUE
|
||||
explosionSize = list(0,0,0,0)
|
||||
|
||||
/obj/structure/closet/supplypod/podspawn/deathmatch
|
||||
desc = "A blood-red styled drop pod."
|
||||
specialised = TRUE
|
||||
|
||||
/obj/structure/closet/supplypod/podspawn/deathmatch/Entered(atom/movable/arrived)
|
||||
. = ..()
|
||||
if(isliving(arrived))
|
||||
var/mob/living/critter = arrived
|
||||
critter.faction = list(FACTION_HOSTILE) //No infighting, but also KILL!!
|
||||
|
||||
/obj/structure/closet/supplypod/extractionpod
|
||||
name = "Syndicate Extraction Pod"
|
||||
desc = "A specalised, blood-red styled pod for extracting high-value targets out of active mission areas. <b>Targets must be manually stuffed inside the pod for proper delivery.</b>"
|
||||
@@ -98,6 +108,20 @@
|
||||
style = STYLE_SYNDICATE
|
||||
specialised = TRUE
|
||||
|
||||
/obj/structure/closet/supplypod/deadmatch_missile
|
||||
name = "cruise missile"
|
||||
desc = "A big ass missile, likely launched from some far-off deep space missile silo."
|
||||
decal = null
|
||||
door = null
|
||||
fin_mask = null
|
||||
explosionSize = list(0,1,2,2)
|
||||
effectShrapnel = TRUE
|
||||
rubble_type = RUBBLE_THIN
|
||||
specialised = TRUE
|
||||
delays = list(POD_TRANSIT = 2.6 SECONDS, POD_FALLING = 0.4 SECONDS)
|
||||
effectMissile = TRUE
|
||||
shrapnel_type = /obj/projectile/bullet/shrapnel/short_range
|
||||
|
||||
/datum/armor/closet_supplypod
|
||||
melee = 30
|
||||
bullet = 50
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
var/list/datum/lazy_template/deathmatch/maps = list()
|
||||
/// All loadouts
|
||||
var/list/datum/outfit/loadouts
|
||||
/// All modifiers
|
||||
var/list/datum/deathmatch_modifier/modifiers
|
||||
|
||||
/datum/deathmatch_controller/New()
|
||||
. = ..()
|
||||
@@ -17,6 +19,7 @@
|
||||
var/map_name = initial(template.name)
|
||||
maps[map_name] = new template
|
||||
loadouts = subtypesof(/datum/outfit/deathmatch_loadout)
|
||||
modifiers = sortTim(init_subtypes_w_path_keys(/datum/deathmatch_modifier), GLOBAL_PROC_REF(cmp_deathmatch_mods), associative = TRUE)
|
||||
|
||||
/datum/deathmatch_controller/proc/create_new_lobby(mob/host)
|
||||
lobbies[host.ckey] = new /datum/deathmatch_lobby(host)
|
||||
@@ -87,7 +90,7 @@
|
||||
var/datum/deathmatch_lobby/chosen_lobby = lobbies[params["id"]]
|
||||
if (!isnull(playing_lobby) && playing_lobby != chosen_lobby)
|
||||
playing_lobby.leave(usr.ckey)
|
||||
|
||||
|
||||
if(isnull(playing_lobby))
|
||||
log_game("[usr.ckey] joined deathmatch lobby [params["id"]] as a player.")
|
||||
chosen_lobby.join(usr)
|
||||
|
||||
@@ -12,13 +12,15 @@
|
||||
/// Whether players hear deadchat and people through walls
|
||||
var/global_chat = FALSE
|
||||
/// Whether the lobby is currently playing
|
||||
var/playing = FALSE
|
||||
var/playing = DEATHMATCH_NOT_PLAYING
|
||||
/// Number of total ready players
|
||||
var/ready_count
|
||||
/// List of loadouts, either gotten from the deathmatch controller or the map
|
||||
var/list/loadouts
|
||||
/// Current map player spawn locations, cleared after spawning
|
||||
var/list/player_spawns = list()
|
||||
/// A list of paths of modifiers enabled for the match.
|
||||
var/list/modifiers = list()
|
||||
|
||||
/datum/deathmatch_lobby/New(mob/player)
|
||||
. = ..()
|
||||
@@ -48,11 +50,12 @@
|
||||
map = null
|
||||
location = null
|
||||
loadouts = null
|
||||
modifiers = null
|
||||
|
||||
/datum/deathmatch_lobby/proc/start_game()
|
||||
if (playing)
|
||||
return
|
||||
playing = TRUE
|
||||
playing = DEATHMATCH_PRE_PLAYING
|
||||
|
||||
RegisterSignal(map, COMSIG_LAZY_TEMPLATE_LOADED, PROC_REF(find_spawns_and_start_delay))
|
||||
location = map.lazy_load()
|
||||
@@ -78,6 +81,9 @@
|
||||
playing = FALSE
|
||||
return FALSE
|
||||
|
||||
for(var/modpath in modifiers)
|
||||
GLOB.deathmatch_game.modifiers[modpath].on_start_game(src)
|
||||
|
||||
for (var/key in players)
|
||||
var/mob/dead/observer/observer = players[key]["mob"]
|
||||
if (isnull(observer) || !observer.client)
|
||||
@@ -97,19 +103,26 @@
|
||||
var/mob/observer = observers[observer_key]["mob"]
|
||||
observer.forceMove(pick(location.reserved_turfs))
|
||||
|
||||
playing = DEATHMATCH_PLAYING
|
||||
addtimer(CALLBACK(src, PROC_REF(game_took_too_long)), initial(map.automatic_gameend_time))
|
||||
log_game("Deathmatch game [host] started.")
|
||||
announce(span_reallybig("GO!"))
|
||||
if(length(modifiers))
|
||||
var/list/modifier_names = list()
|
||||
for(var/datum/deathmatch_modifier/modifier in modifiers)
|
||||
modifier_names += uppertext(initial(modifier.name))
|
||||
announce(span_boldnicegreen("THIS MATCH MODIFIERS: [english_list(modifier_names, and_text = " ,")]."))
|
||||
return TRUE
|
||||
|
||||
/datum/deathmatch_lobby/proc/spawn_observer_as_player(ckey, loc)
|
||||
var/mob/dead/observer/observer = players[ckey]["mob"]
|
||||
var/list/players_info = players[ckey]
|
||||
var/mob/dead/observer/observer = players_info["mob"]
|
||||
if (isnull(observer) || !observer.client)
|
||||
remove_ckey_from_play(ckey)
|
||||
return
|
||||
|
||||
// equip player
|
||||
var/datum/outfit/deathmatch_loadout/loadout = players[ckey]["loadout"]
|
||||
var/datum/outfit/deathmatch_loadout/loadout = players_info["loadout"]
|
||||
if (!(loadout in loadouts))
|
||||
loadout = loadouts[1]
|
||||
|
||||
@@ -126,7 +139,10 @@
|
||||
)
|
||||
new_player.equipOutfit(loadout) // Loadout
|
||||
new_player.key = ckey
|
||||
players[ckey]["mob"] = new_player
|
||||
players_info["mob"] = new_player
|
||||
|
||||
for(var/datum/deathmatch_modifier/modifier as anything in modifiers)
|
||||
GLOB.deathmatch_game.modifiers[modifier].apply(new_player, src)
|
||||
|
||||
// register death handling.
|
||||
RegisterSignals(new_player, list(COMSIG_LIVING_DEATH, COMSIG_MOB_GHOSTIZED, COMSIG_QDELETING), PROC_REF(player_died))
|
||||
@@ -158,6 +174,9 @@
|
||||
loser.ghostize()
|
||||
qdel(loser)
|
||||
|
||||
for(var/datum/deathmatch_modifier/modifier in modifiers)
|
||||
GLOB.deathmatch_game.modifiers[modifier].on_end_game(src)
|
||||
|
||||
clear_reservation()
|
||||
GLOB.deathmatch_game.remove_lobby(host)
|
||||
log_game("Deathmatch game [host] ended.")
|
||||
@@ -187,7 +206,8 @@
|
||||
announce(span_reallybig("[player.real_name] HAS DIED.<br>[players.len] REMAIN."))
|
||||
|
||||
if(!gibbed && !QDELING(player)) // for some reason dusting or deleting in chasm storage messes up tgui bad
|
||||
player.dust(TRUE, TRUE, TRUE)
|
||||
if(!HAS_TRAIT(src, TRAIT_DEATHMATCH_EXPLOSIVE_IMPLANTS))
|
||||
player.dust(TRUE, TRUE, TRUE)
|
||||
if (players.len <= 1)
|
||||
end_game()
|
||||
|
||||
@@ -277,6 +297,9 @@
|
||||
continue
|
||||
players[player_key]["loadout"] = loadouts[1]
|
||||
|
||||
for(var/deathmatch_mod in modifiers)
|
||||
GLOB.deathmatch_game.modifiers[deathmatch_mod].on_map_changed(src)
|
||||
|
||||
/datum/deathmatch_lobby/proc/clear_reservation()
|
||||
if(isnull(location) || isnull(map))
|
||||
return
|
||||
@@ -312,11 +335,15 @@
|
||||
for (var/map_key in GLOB.deathmatch_game.maps)
|
||||
.["maps"] += map_key
|
||||
|
||||
|
||||
/datum/deathmatch_lobby/ui_data(mob/user)
|
||||
. = list()
|
||||
var/is_player = !isnull(players[user.ckey])
|
||||
var/is_host = (user.ckey == host)
|
||||
var/is_admin = check_rights_for(user.client, R_ADMIN)
|
||||
.["self"] = user.ckey
|
||||
.["host"] = (user.ckey == host)
|
||||
.["admin"] = check_rights_for(user.client, R_ADMIN)
|
||||
.["host"] = is_host
|
||||
.["admin"] = is_admin
|
||||
.["global_chat"] = global_chat
|
||||
.["playing"] = playing
|
||||
.["loadouts"] = list("Randomize")
|
||||
@@ -328,7 +355,27 @@
|
||||
.["map"]["time"] = map.automatic_gameend_time
|
||||
.["map"]["min_players"] = map.min_players
|
||||
.["map"]["max_players"] = map.max_players
|
||||
if(!isnull(players[user.ckey]) && !isnull(players[user.ckey]["loadout"]))
|
||||
|
||||
.["mod_menu_open"] = FALSE
|
||||
if((is_host || is_admin) && players[user.ckey]["mod_menu_open"])
|
||||
.["mod_menu_open"] = TRUE
|
||||
for(var/modpath in GLOB.deathmatch_game.modifiers)
|
||||
var/datum/deathmatch_modifier/mod = GLOB.deathmatch_game.modifiers[modpath]
|
||||
.["modifiers"] += list(list(
|
||||
"name" = mod.name,
|
||||
"desc" = mod.description,
|
||||
"modpath" = "[modpath]",
|
||||
"selected" = (modpath in modifiers),
|
||||
"selectable" = is_host && mod.selectable(src),
|
||||
))
|
||||
.["active_mods"] = "No modifiers selected"
|
||||
if(length(modifiers))
|
||||
var/list/mod_names = list()
|
||||
for(var/datum/deathmatch_modifier/modpath as anything in modifiers)
|
||||
mod_names += initial(modpath.name)
|
||||
.["active_mods"] = "Selected modifiers: [english_list(mod_names)]"
|
||||
|
||||
if(is_player && !isnull(players[user.ckey]["loadout"]))
|
||||
var/datum/outfit/deathmatch_loadout/loadout = players[user.ckey]["loadout"]
|
||||
.["loadoutdesc"] = initial(loadout.desc)
|
||||
else
|
||||
@@ -437,6 +484,31 @@
|
||||
if ("global_chat")
|
||||
global_chat = !global_chat
|
||||
return TRUE
|
||||
if("open_mod_menu")
|
||||
players[usr.ckey]["mod_menu_open"] = TRUE
|
||||
return TRUE
|
||||
if("exit_mod_menu")
|
||||
players[usr.ckey] -= "mod_menu_open"
|
||||
return TRUE
|
||||
if("toggle_modifier")
|
||||
var/datum/deathmatch_modifier/modpath = text2path(params["modpath"])
|
||||
if(!ispath(modpath))
|
||||
return TRUE
|
||||
var/global_mod = params["global_mod"]
|
||||
if(global_mod)
|
||||
if(usr.ckey != host && !check_rights(R_ADMIN))
|
||||
return TRUE
|
||||
else if(!(usr.ckey in players))
|
||||
return TRUE
|
||||
var/datum/deathmatch_modifier/chosen_modifier = GLOB.deathmatch_game.modifiers[modpath]
|
||||
if(modpath in modifiers)
|
||||
chosen_modifier.unselect(src)
|
||||
modifiers -= modpath
|
||||
return TRUE
|
||||
else if(chosen_modifier.selectable(src))
|
||||
chosen_modifier.on_select(src)
|
||||
modifiers += modpath
|
||||
return TRUE
|
||||
if ("admin") // Admin functions
|
||||
if (!check_rights(R_ADMIN))
|
||||
message_admins("[usr.key] has attempted to use admin functions in a deathmatch lobby!")
|
||||
@@ -447,4 +519,7 @@
|
||||
log_admin("[key_name(usr)] force started deathmatch lobby [host].")
|
||||
start_game()
|
||||
|
||||
|
||||
/datum/deathmatch_lobby/ui_close(mob/user)
|
||||
. = ..()
|
||||
if(players[user.ckey])
|
||||
players[user.ckey] -= "mod_menu_open"
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
///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 list of modifiers this is incompatible with.
|
||||
var/list/blacklisted_modifiers
|
||||
///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
|
||||
for(var/modpath in lobby.modifiers)
|
||||
if(src in GLOB.deathmatch_game.modifiers[modpath].blacklisted_modifiers)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
///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.
|
||||
/datum/deathmatch_modifier/proc/on_map_changed(datum/deathmatch_lobby/lobby)
|
||||
return
|
||||
|
||||
///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/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/triple
|
||||
name = "Triple-Health"
|
||||
description = "When \"Double-Health\" isn't enough..."
|
||||
multiplier = 3
|
||||
blacklisted_modifiers = list(/datum/deathmatch_modifier/health)
|
||||
|
||||
/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), 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/xray
|
||||
name = "X-Ray Vision"
|
||||
description = "See through the cordons of the deathmatch arena!"
|
||||
blacklisted_modifiers = list(/datum/deathmatch_modifier/thermal, /datum/deathmatch_modifier/echolocation)
|
||||
|
||||
/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/echolocation)
|
||||
|
||||
/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"
|
||||
blacklisted_modifiers = list(/datum/deathmatch_modifier/echolocation)
|
||||
|
||||
/datum/deathmatch_modifier/nearsightness/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
|
||||
player.become_nearsighted(DEATHMATCH_TRAIT)
|
||||
|
||||
/datum/deathmatch_modifier/echolocation
|
||||
name = "Echolocation"
|
||||
description = "On one hand, you're blind, but on the other..."
|
||||
blacklisted_modifiers = list(/datum/deathmatch_modifier/nearsightness, /datum/deathmatch_modifier/xray, /datum/deathmatch_modifier/thermal)
|
||||
|
||||
/datum/deathmatch_modifier/echolocation/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
|
||||
player.AddComponent(/datum/component/echolocation)
|
||||
|
||||
/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
|
||||
ADD_TRAIT(projectile, TRAIT_ALWAYS_HIT_ZONE, DEATHMATCH_TRAIT)
|
||||
|
||||
/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."
|
||||
blacklisted_modifiers = list(/datum/deathmatch_modifier/mounts)
|
||||
|
||||
/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)
|
||||
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!"
|
||||
blacklisted_modifiers = list(/datum/deathmatch_modifier/paraplegic)
|
||||
|
||||
/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/saddled,
|
||||
))
|
||||
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)
|
||||
|
||||
/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/flesh_spider = 2,
|
||||
/mob/living/basic/garden_gnome = 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/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/dark_wizard = 2,
|
||||
/mob/living/simple_animal/hostile/retaliate/goose = 2,
|
||||
/mob/living/simple_animal/hostile/ooze = 1,
|
||||
/mob/living/simple_animal/hostile/vatbeast = 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/kahlua::metabolization_rate
|
||||
player.reagents.add_reagent(/datum/reagent/consumable/ethanol/kahlua, 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.
|
||||
)
|
||||
|
||||
///1 every 10 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.1
|
||||
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
|
||||
var/mine_path = pick(mines)
|
||||
new mine_path (target_turf)
|
||||
|
||||
/datum/deathmatch_modifier/flipping
|
||||
name = "Perma-Flipping"
|
||||
description = "You're constantly flipping, however it's purely cosmetic"
|
||||
|
||||
/datum/deathmatch_modifier/flipping/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
|
||||
player.SpinAnimation(speed = 0.9 SECONDS, loops = -1)
|
||||
|
||||
/datum/deathmatch_modifier/screen_flipping
|
||||
name = "Rotating Screen"
|
||||
description = "♪ You spin me right round, baby right round ♪"
|
||||
|
||||
/datum/deathmatch_modifier/screen_flipping/apply(mob/living/carbon/player, datum/deathmatch_lobby/lobby)
|
||||
var/atom/movable/plane_master_controller/pm_controller = player.hud_used.plane_master_controllers[PLANE_MASTERS_GAME]
|
||||
var/clockwise = prob(50)
|
||||
for(var/atom/movable/screen/plane_master/plane as anything in pm_controller.get_planes())
|
||||
plane.SpinAnimation(4.5 SECONDS, clockwise = clockwise)
|
||||
|
||||
/datum/deathmatch_modifier/random
|
||||
name = "Random Modifiers"
|
||||
description = "Picks 3 to 5 random modifiers as the game is about to start"
|
||||
|
||||
/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 -= 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(!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()
|
||||
|
||||
///Pick global modifiers at random.
|
||||
for(var/iteration in rand(3, 5))
|
||||
var/mod_len = length(modifiers_pool)
|
||||
if(!mod_len)
|
||||
break
|
||||
var/datum/deathmatch_modifier/modifier
|
||||
if(mod_len > 1)
|
||||
modifier = GLOB.deathmatch_game.modifiers[pick_n_take(modifiers_pool)]
|
||||
else //pick() throws errors if the list has only one element iirc.
|
||||
modifier = GLOB.deathmatch_game.modifiers[modifiers_pool[1]]
|
||||
modifiers_pool = null
|
||||
if(!modifier.selectable(lobby))
|
||||
continue
|
||||
modifier.on_select(lobby)
|
||||
modifier.on_start_game(lobby)
|
||||
lobby += modifier
|
||||
modifiers_pool -= modifier.blacklisted_modifiers
|
||||
|
||||
/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.modifiers -= type
|
||||
else
|
||||
lobby.loadouts = GLOB.deathmatch_game.loadouts
|
||||
@@ -403,7 +403,7 @@
|
||||
GRANT_ACTION(/datum/action/cooldown/regurgitate)
|
||||
|
||||
AddElement(/datum/element/swabable, CELL_LINE_TABLE_GLUTTON, CELL_VIRUS_TABLE_GENERIC_MOB, 1, 5)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheesiehonkers, /obj/item/food/cornchips), tame_chance = 30, bonus_tame_chance = 0, after_tame = CALLBACK(src, PROC_REF(tamed)))
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheesiehonkers, /obj/item/food/cornchips), tame_chance = 30, bonus_tame_chance = 0)
|
||||
AddElement(/datum/element/damage_threshold, 10) //lots of fat to cushion blows.
|
||||
|
||||
/mob/living/basic/clown/mutant/glutton/attacked_by(obj/item/item, mob/living/user)
|
||||
@@ -461,7 +461,7 @@
|
||||
playsound(loc,'sound/items/eatfood.ogg', rand(30,50), TRUE)
|
||||
flick("glutton_mouth", src)
|
||||
|
||||
/mob/living/basic/clown/mutant/glutton/proc/tamed(mob/living/tamer)
|
||||
/mob/living/basic/clown/mutant/glutton/tamed(mob/living/tamer, atom/food)
|
||||
buckle_lying = 0
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/glutton)
|
||||
|
||||
|
||||
@@ -61,10 +61,10 @@
|
||||
var/static/list/food_types
|
||||
if(!food_types)
|
||||
food_types = src.food_types.Copy()
|
||||
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)))
|
||||
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
|
||||
AddElement(/datum/element/basic_eating, food_types = food_types)
|
||||
|
||||
/mob/living/basic/cow/proc/tamed(mob/living/tamer)
|
||||
/mob/living/basic/cow/tamed(mob/living/tamer, atom/food)
|
||||
buckle_lying = 0
|
||||
visible_message("[src] [tame_message] as it seems to bond with [tamer].", "You [self_tame_message], recognizing [tamer] as your new pal.")
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/cow)
|
||||
|
||||
@@ -35,9 +35,9 @@
|
||||
if(!food_types)
|
||||
food_types = src.food_types.Copy()
|
||||
AddElement(/datum/element/basic_eating, food_types = food_types)
|
||||
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)))
|
||||
AddComponent(/datum/component/tameable, food_types = food_types, tame_chance = 25, bonus_tame_chance = 15)
|
||||
|
||||
/mob/living/basic/cow/moonicorn/tamed(mob/living/tamer)
|
||||
/mob/living/basic/cow/moonicorn/tamed(mob/living/tamer, atom/food)
|
||||
. = ..()
|
||||
///stop killing my FRIENDS
|
||||
faction |= tamer.faction
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
|
||||
///wrapper for the tameable component addition so you can have non tamable cow subtypes
|
||||
/mob/living/basic/pig/proc/make_tameable()
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)))
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 25, bonus_tame_chance = 15)
|
||||
|
||||
/mob/living/basic/pig/proc/tamed(mob/living/tamer)
|
||||
/mob/living/basic/pig/tamed(mob/living/tamer, atom/food)
|
||||
can_buckle = TRUE
|
||||
buckle_lying = 0
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/pig)
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
AddElement(/datum/element/ai_retaliate)
|
||||
AddElement(/datum/element/ai_flee_while_injured)
|
||||
AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 25, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)), unique = unique_tamer)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 25, bonus_tame_chance = 15, unique = unique_tamer)
|
||||
|
||||
/mob/living/basic/pony/proc/tamed(mob/living/tamer)
|
||||
/mob/living/basic/pony/tamed(mob/living/tamer, atom/food)
|
||||
can_buckle = TRUE
|
||||
buckle_lying = 0
|
||||
playsound(src, 'sound/creatures/pony/snort.ogg', 50)
|
||||
@@ -151,4 +151,4 @@
|
||||
ponycolors = list("#5d566f", pick_weight(mane_colors))
|
||||
name = pick("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday")
|
||||
// Only one person can tame these fellas, and they only need one apple
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 100, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)), unique = unique_tamer)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/apple), tame_chance = 100, bonus_tame_chance = 15, unique = unique_tamer)
|
||||
|
||||
@@ -65,15 +65,9 @@
|
||||
make_tameable()
|
||||
|
||||
/mob/living/basic/mining/wolf/proc/make_tameable()
|
||||
AddComponent(\
|
||||
/datum/component/tameable,\
|
||||
food_types = list(/obj/item/food/meat/slab),\
|
||||
tame_chance = 15,\
|
||||
bonus_tame_chance = 5,\
|
||||
after_tame = CALLBACK(src, PROC_REF(tame_wolf)),\
|
||||
)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat/slab), tame_chance = 15, bonus_tame_chance = 5)
|
||||
|
||||
/mob/living/basic/mining/wolf/proc/tame_wolf()
|
||||
/mob/living/basic/mining/wolf/tamed(mob/living/tamer, atom/food)
|
||||
new /obj/effect/temp_visual/heart(src.loc)
|
||||
// ride wolf, life good
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/wolf)
|
||||
|
||||
@@ -105,15 +105,9 @@
|
||||
return ..()
|
||||
|
||||
/mob/living/basic/mining/goldgrub/proc/make_tameable()
|
||||
AddComponent(\
|
||||
/datum/component/tameable,\
|
||||
food_types = list(/obj/item/stack/ore),\
|
||||
tame_chance = 25,\
|
||||
bonus_tame_chance = 5,\
|
||||
after_tame = CALLBACK(src, PROC_REF(tame_grub)),\
|
||||
)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/stack/ore), tame_chance = 25, bonus_tame_chance = 5)
|
||||
|
||||
/mob/living/basic/mining/goldgrub/proc/tame_grub()
|
||||
/mob/living/basic/mining/goldgrub/tamed(mob/living/tamer, atom/food)
|
||||
new /obj/effect/temp_visual/heart(src.loc)
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/goldgrub)
|
||||
AddComponent(/datum/component/obeys_commands, pet_commands)
|
||||
|
||||
@@ -68,13 +68,7 @@
|
||||
AddComponent(/datum/component/basic_mob_attack_telegraph)
|
||||
AddComponentFrom(INNATE_TRAIT, /datum/component/shovel_hands)
|
||||
if (tameable)
|
||||
AddComponent(\
|
||||
/datum/component/tameable,\
|
||||
food_types = list(/obj/item/food/grown/ash_flora),\
|
||||
tame_chance = 10,\
|
||||
bonus_tame_chance = 5,\
|
||||
after_tame = CALLBACK(src, PROC_REF(tamed)),\
|
||||
)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/ash_flora), tame_chance = 10, bonus_tame_chance = 5)
|
||||
|
||||
tentacles = new (src)
|
||||
tentacles.Grant(src)
|
||||
@@ -120,6 +114,9 @@
|
||||
return
|
||||
balloon_alert(user, "ready to ride")
|
||||
qdel(attacking_item)
|
||||
make_rideable()
|
||||
|
||||
/mob/living/basic/mining/goliath/proc/make_rideable()
|
||||
saddled = TRUE
|
||||
buckle_lying = 0
|
||||
add_overlay("goliath_saddled")
|
||||
@@ -149,7 +146,7 @@
|
||||
icon_state = tentacle_warning_state
|
||||
|
||||
/// Get ready for mounting
|
||||
/mob/living/basic/mining/goliath/proc/tamed()
|
||||
/mob/living/basic/mining/goliath/tamed(mob/living/tamer, atom/food)
|
||||
tamed = TRUE
|
||||
|
||||
// Copy entire faction rather than just placing user into faction, to avoid tentacle peril on station
|
||||
@@ -163,6 +160,12 @@
|
||||
/mob/living/basic/mining/goliath/ranged_secondary_attack(atom/atom_target, modifiers)
|
||||
tentacle_line?.Trigger(target = atom_target)
|
||||
|
||||
/mob/living/basic/mining/goliath/saddled
|
||||
|
||||
/mob/living/basic/mining/goliath/saddled/Initialize(mapload)
|
||||
. = ..()
|
||||
make_rideable()
|
||||
|
||||
/// Legacy Goliath mob with different sprites, largely the same behaviour
|
||||
/mob/living/basic/mining/goliath/ancient
|
||||
name = "ancient goliath"
|
||||
|
||||
@@ -53,13 +53,7 @@
|
||||
AddElement(/datum/element/death_drops, death_drops)
|
||||
add_traits(list(TRAIT_LAVA_IMMUNE, TRAIT_ASHSTORM_IMMUNE), INNATE_TRAIT)
|
||||
AddElement(/datum/element/footstep, FOOTSTEP_OBJ_ROBOT, 1, -6, sound_vary = TRUE)
|
||||
AddComponent(\
|
||||
/datum/component/tameable,\
|
||||
food_types = list(/obj/item/stack/ore),\
|
||||
tame_chance = 100,\
|
||||
bonus_tame_chance = 5,\
|
||||
after_tame = CALLBACK(src, PROC_REF(activate_bot)),\
|
||||
)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/stack/ore), tame_chance = 100, bonus_tame_chance = 5)
|
||||
|
||||
var/static/list/innate_actions = list(
|
||||
/datum/action/cooldown/mob_cooldown/minedrone/toggle_light = BB_MINEBOT_LIGHT_ABILITY,
|
||||
@@ -154,7 +148,7 @@
|
||||
return ACCESS_ALLOWED
|
||||
return ACCESS_DISALLOWED
|
||||
|
||||
/mob/living/basic/mining_drone/proc/activate_bot()
|
||||
/mob/living/basic/mining_drone/tamed(mob/living/tamer, atom/food)
|
||||
AddComponent(/datum/component/obeys_commands, pet_commands)
|
||||
|
||||
/mob/living/basic/mining_drone/death(gibbed)
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
AddElement(/datum/element/pet_bonus, "woofs happily!")
|
||||
AddElement(/datum/element/footstep, FOOTSTEP_MOB_CLAW)
|
||||
AddElement(/datum/element/unfriend_attacker, untamed_reaction = "%SOURCE% fixes %TARGET% with a look of betrayal.")
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat/slab/human/mutant/skeleton, /obj/item/stack/sheet/bone), tame_chance = 30, bonus_tame_chance = 15, after_tame = CALLBACK(src, PROC_REF(tamed)), unique = FALSE)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat/slab/human/mutant/skeleton, /obj/item/stack/sheet/bone), tame_chance = 30, bonus_tame_chance = 15, unique = FALSE)
|
||||
AddComponent(/datum/component/obeys_commands, pet_commands)
|
||||
var/dog_area = get_area(src)
|
||||
for(var/obj/structure/bed/dogbed/dog_bed in dog_area)
|
||||
@@ -64,7 +64,7 @@
|
||||
speech.emote_see = string_list(list("shakes [p_their()] head.", "chases [p_their()] tail.","shivers."))
|
||||
|
||||
///Proc to run on a successful taming attempt
|
||||
/mob/living/basic/pet/dog/proc/tamed(mob/living/tamer)
|
||||
/mob/living/basic/pet/dog/tamed(mob/living/tamer, atom/food)
|
||||
visible_message(span_notice("[src] licks at [tamer] in a friendly manner!"))
|
||||
|
||||
/// A dog bone fully heals a dog, and befriends it if it's not your friend.
|
||||
|
||||
@@ -91,13 +91,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
|
||||
AddElement(/datum/element/strippable, GLOB.strippable_parrot_items)
|
||||
AddElement(/datum/element/simple_flying)
|
||||
AddComponent(/datum/component/listen_and_repeat, desired_phrases = get_static_list_of_phrases(), blackboard_key = BB_PARROT_REPEAT_STRING)
|
||||
AddComponent(\
|
||||
/datum/component/tameable,\
|
||||
food_types = edibles,\
|
||||
tame_chance = 100,\
|
||||
bonus_tame_chance = 0,\
|
||||
after_tame = CALLBACK(src, PROC_REF(tamed)),\
|
||||
)
|
||||
AddComponent(/datum/component/tameable, food_types = edibles, tame_chance = 100, bonus_tame_chance = 0)
|
||||
|
||||
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attacking))
|
||||
RegisterSignal(src, COMSIG_MOB_CLICKON, PROC_REF(on_click))
|
||||
@@ -445,7 +439,7 @@ GLOBAL_LIST_INIT(strippable_parrot_items, create_strippable_list(list(
|
||||
|
||||
return returnable_list
|
||||
|
||||
/mob/living/basic/parrot/proc/tamed()
|
||||
/mob/living/basic/parrot/tamed(mob/living/tamer, atom/food)
|
||||
new /obj/effect/temp_visual/heart(drop_location())
|
||||
|
||||
/mob/living/basic/parrot/proc/drop_item_on_signal(mob/living/user)
|
||||
|
||||
@@ -95,10 +95,10 @@
|
||||
AddComponent(/datum/component/aggro_emote, emote_list = string_list(list("gnashes")))
|
||||
AddComponent(/datum/component/regenerator, outline_colour = regenerate_colour)
|
||||
if (tamer)
|
||||
on_tamed(tamer, feedback = FALSE)
|
||||
tamed(tamer, feedback = FALSE)
|
||||
befriend(tamer)
|
||||
else
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat), tame_chance = 10, bonus_tame_chance = 5, after_tame = CALLBACK(src, PROC_REF(on_tamed)))
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/meat), tame_chance = 10, bonus_tame_chance = 5)
|
||||
|
||||
teleport = new(src)
|
||||
teleport.Grant(src)
|
||||
@@ -119,7 +119,7 @@
|
||||
set_greyscale(colors = list(pick_weight(GLOB.carp_colors)))
|
||||
|
||||
/// Called when another mob has forged a bond of friendship with this one, passed the taming mob as 'tamer'
|
||||
/mob/living/basic/carp/proc/on_tamed(mob/tamer, feedback = TRUE)
|
||||
/mob/living/basic/carp/tamed(mob/living/tamer, atom/food, feedback = TRUE)
|
||||
buckle_lying = 0
|
||||
AddElement(/datum/element/ridable, ridable_data)
|
||||
AddComponent(/datum/component/obeys_commands, tamed_commands)
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
grant_actions_by_list(innate_actions)
|
||||
|
||||
AddElement(/datum/element/simple_flying)
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 100, after_tame = CALLBACK(src, PROC_REF(on_tame)))
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/grown/carrot), tame_chance = 100)
|
||||
ADD_TRAIT(src, TRAIT_SPACEWALK, INNATE_TRAIT)
|
||||
RegisterSignal(src, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(pre_attack))
|
||||
on_hit_overlay = mutable_appearance(icon, "[icon_state]_crying")
|
||||
@@ -119,7 +119,7 @@
|
||||
befriend(target)
|
||||
COOLDOWN_START(src, eye_healing, 15 SECONDS)
|
||||
|
||||
/mob/living/basic/eyeball/proc/on_tame(mob/tamer)
|
||||
/mob/living/basic/eyeball/tamed(mob/living/tamer, atom/food)
|
||||
spin(spintime = 2 SECONDS, speed = 1)
|
||||
//become passive to the humens
|
||||
faction |= tamer.faction
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
if (tame)
|
||||
faction |= FACTION_NEUTRAL
|
||||
else
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheese), tame_chance = 100, after_tame = CALLBACK(src, PROC_REF(tamed)))
|
||||
AddComponent(/datum/component/tameable, food_types = list(/obj/item/food/cheese), tame_chance = 100)
|
||||
|
||||
/mob/living/basic/mouse/Destroy()
|
||||
SSmobs.cheeserats -= src
|
||||
@@ -148,7 +148,7 @@
|
||||
to_chat(entered, span_notice("[icon2html(src, entered)] Squeak!"))
|
||||
|
||||
/// Called when a mouse is hand-fed some cheese, it will stop being afraid of humans
|
||||
/mob/living/basic/mouse/proc/tamed(mob/living/tamer, obj/item/food/cheese/cheese)
|
||||
/mob/living/basic/mouse/tamed(mob/living/tamer, obj/item/food/cheese/cheese)
|
||||
new /obj/effect/temp_visual/heart(loc)
|
||||
faction |= FACTION_NEUTRAL
|
||||
tame = TRUE
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
GRANT_ACTION(/datum/action/cooldown/tentacle_slap)
|
||||
|
||||
add_cell_sample()
|
||||
AddComponent(/datum/component/tameable, list(/obj/item/food/fries, /obj/item/food/cheesyfries, /obj/item/food/cornchips, /obj/item/food/carrotfries), tame_chance = 30, bonus_tame_chance = 0, after_tame = CALLBACK(src, PROC_REF(tamed)))
|
||||
AddComponent(/datum/component/tameable, list(/obj/item/food/fries, /obj/item/food/cheesyfries, /obj/item/food/cornchips, /obj/item/food/carrotfries), tame_chance = 30, bonus_tame_chance = 0)
|
||||
|
||||
/mob/living/simple_animal/hostile/vatbeast/proc/tamed(mob/living/tamer)
|
||||
/mob/living/simple_animal/hostile/vatbeast/tamed(mob/living/tamer, obj/item/food)
|
||||
buckle_lying = 0
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/creature/vatbeast)
|
||||
faction = list(FACTION_NEUTRAL)
|
||||
|
||||
@@ -26,12 +26,15 @@
|
||||
|
||||
/obj/vehicle/ridden/wheelchair/motorized/Initialize(mapload)
|
||||
. = ..()
|
||||
add_component_parts()
|
||||
refresh_parts()
|
||||
|
||||
/obj/vehicle/ridden/wheelchair/motorized/proc/add_component_parts()
|
||||
// Add tier 1 stock parts so that non-crafted wheelchairs aren't empty
|
||||
component_parts += GLOB.stock_part_datums[/datum/stock_part/capacitor]
|
||||
component_parts += GLOB.stock_part_datums[/datum/stock_part/servo]
|
||||
component_parts += GLOB.stock_part_datums[/datum/stock_part/servo]
|
||||
power_cell = new /obj/item/stock_parts/cell(src)
|
||||
refresh_parts()
|
||||
|
||||
/obj/vehicle/ridden/wheelchair/motorized/make_ridable()
|
||||
AddElement(/datum/element/ridable, /datum/component/riding/vehicle/wheelchair/motorized)
|
||||
@@ -221,3 +224,12 @@
|
||||
visible_message(span_warning("A bomb appears in [src], what the fuck?"))
|
||||
obj_flags |= EMAGGED
|
||||
return TRUE
|
||||
|
||||
///Version with slightly better components. Used by deathmatches.
|
||||
/obj/vehicle/ridden/wheelchair/motorized/improved
|
||||
|
||||
/obj/vehicle/ridden/wheelchair/motorized/improved/add_component_parts()
|
||||
component_parts += GLOB.stock_part_datums[/datum/stock_part/capacitor/tier2]
|
||||
component_parts += GLOB.stock_part_datums[/datum/stock_part/servo/tier2]
|
||||
component_parts += GLOB.stock_part_datums[/datum/stock_part/servo/tier2]
|
||||
power_cell = new /obj/item/stock_parts/cell/upgraded/plus(src)
|
||||
|
||||
@@ -1425,6 +1425,7 @@
|
||||
#include "code\datums\elements\hostile_machine.dm"
|
||||
#include "code\datums\elements\human_biter.dm"
|
||||
#include "code\datums\elements\immerse.dm"
|
||||
#include "code\datums\elements\inverted_movement.dm"
|
||||
#include "code\datums\elements\item_fov.dm"
|
||||
#include "code\datums\elements\item_scaling.dm"
|
||||
#include "code\datums\elements\kneecapping.dm"
|
||||
@@ -3836,6 +3837,7 @@
|
||||
#include "code\modules\deathmatch\deathmatch_lobby.dm"
|
||||
#include "code\modules\deathmatch\deathmatch_mapping.dm"
|
||||
#include "code\modules\deathmatch\deathmatch_maps.dm"
|
||||
#include "code\modules\deathmatch\deathmatch_modifier.dm"
|
||||
#include "code\modules\detectivework\evidence.dm"
|
||||
#include "code\modules\detectivework\scanner.dm"
|
||||
#include "code\modules\discord\accountlink.dm"
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
Dropdown,
|
||||
Flex,
|
||||
Icon,
|
||||
Modal,
|
||||
Section,
|
||||
Table,
|
||||
} from '../components';
|
||||
@@ -21,6 +22,16 @@ type PlayerLike = {
|
||||
};
|
||||
};
|
||||
|
||||
type Modifier = {
|
||||
name: string;
|
||||
desc: string;
|
||||
modpath: string;
|
||||
selected: BooleanLike;
|
||||
selectable: BooleanLike;
|
||||
player_selected: BooleanLike;
|
||||
player_selectable: BooleanLike;
|
||||
};
|
||||
|
||||
type Data = {
|
||||
self: string;
|
||||
host: BooleanLike;
|
||||
@@ -36,6 +47,9 @@ type Data = {
|
||||
min_players: number;
|
||||
max_players: number;
|
||||
};
|
||||
mod_menu_open: BooleanLike;
|
||||
modifiers: Modifier[];
|
||||
active_mods: string;
|
||||
loadoutdesc: string;
|
||||
players: PlayerLike[];
|
||||
observers: PlayerLike[];
|
||||
@@ -43,8 +57,10 @@ type Data = {
|
||||
|
||||
export const DeathmatchLobby = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
const { modifiers = [] } = data;
|
||||
return (
|
||||
<Window title="Deathmatch Lobby" width={560} height={420}>
|
||||
<Window title="Deathmatch Lobby" width={560} height={480}>
|
||||
<ModSelector />
|
||||
<Window.Content>
|
||||
<Flex height="94%">
|
||||
<Flex.Item width="63%">
|
||||
@@ -170,6 +186,19 @@ export const DeathmatchLobby = (props) => {
|
||||
}
|
||||
/>
|
||||
<Divider />
|
||||
<Box textAlign="center">{data.active_mods}</Box>
|
||||
{(!!data.admin || !!data.host) && (
|
||||
<>
|
||||
<Divider />
|
||||
<Button
|
||||
textAlign="center"
|
||||
fluid
|
||||
content="Toggle Modifiers"
|
||||
onClick={() => act('open_mod_menu')}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Divider />
|
||||
<Box textAlign="center">Loadout Description</Box>
|
||||
<Divider />
|
||||
<Box textAlign="center">{data.loadoutdesc}</Box>
|
||||
@@ -211,3 +240,39 @@ export const DeathmatchLobby = (props) => {
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const ModSelector = (props) => {
|
||||
const { act, data } = useBackend<Data>();
|
||||
const { admin, host, mod_menu_open, modifiers = [] } = data;
|
||||
if (!mod_menu_open || !host || !admin) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<Modal>
|
||||
<Button
|
||||
fluid
|
||||
content="Go Back"
|
||||
color="bad"
|
||||
onClick={() => act('exit_mod_menu')}
|
||||
/>
|
||||
{modifiers.map((mod, index) => {
|
||||
return (
|
||||
<Button.Checkbox
|
||||
key={index}
|
||||
mb={2}
|
||||
checked={mod.selected}
|
||||
content={mod.name}
|
||||
tooltip={mod.desc}
|
||||
color={mod.selected ? 'green' : 'blue'}
|
||||
disabled={!mod.selected && !mod.selectable}
|
||||
onClick={() =>
|
||||
act('toggle_modifier', {
|
||||
modpath: mod.modpath,
|
||||
})
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user