Merge branch 'master' into glassware
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+6418
-8246
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3528,7 +3528,6 @@
|
||||
pixel_x = -22
|
||||
},
|
||||
/obj/structure/rack,
|
||||
/obj/item/storage/fancy/donut_box,
|
||||
/obj/item/gun/energy/e_gun/dragnet,
|
||||
/obj/item/gun/energy/e_gun/dragnet,
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
@@ -4004,8 +4003,7 @@
|
||||
dir = 8
|
||||
},
|
||||
/obj/structure/rack,
|
||||
/obj/item/gun/energy/pumpaction/blaster,
|
||||
/obj/item/gun/energy/pumpaction/blaster,
|
||||
/obj/item/storage/fancy/donut_box,
|
||||
/turf/open/floor/plasteel/dark,
|
||||
/area/ai_monitored/security/armory)
|
||||
"ahJ" = (
|
||||
@@ -27117,6 +27115,10 @@
|
||||
/obj/effect/turf_decal/tile/neutral{
|
||||
dir = 2
|
||||
},
|
||||
/obj/structure/sign/departments/custodian{
|
||||
pixel_x = 32;
|
||||
pixel_y = -32
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/hallway/primary/central)
|
||||
"baI" = (
|
||||
@@ -83444,6 +83446,10 @@
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/science/lab)
|
||||
"iCn" = (
|
||||
/obj/machinery/vr_sleeper,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"iLj" = (
|
||||
/obj/structure/table,
|
||||
/turf/open/floor/plating,
|
||||
@@ -83462,6 +83468,16 @@
|
||||
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
|
||||
/turf/open/floor/plasteel/dark,
|
||||
/area/crew_quarters/fitness/recreation)
|
||||
"jhu" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/machinery/door/airlock/public/glass{
|
||||
id_tag = "permahydro";
|
||||
name = "Recreation Module"
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"jwW" = (
|
||||
/turf/closed/wall/mineral/plastitanium,
|
||||
/area/crew_quarters/fitness/recreation)
|
||||
@@ -83519,16 +83535,6 @@
|
||||
"krD" = (
|
||||
/turf/closed/wall,
|
||||
/area/science/circuit)
|
||||
"kwg" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/machinery/door/airlock/public/glass{
|
||||
id_tag = "permahydro";
|
||||
name = "Recreation Module"
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"kwI" = (
|
||||
/obj/item/wrench,
|
||||
/obj/item/clothing/suit/apron,
|
||||
@@ -83589,10 +83595,6 @@
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/science/circuit)
|
||||
"kRT" = (
|
||||
/obj/machinery/vr_sleeper,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"kVo" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
@@ -83612,13 +83614,6 @@
|
||||
/obj/item/integrated_circuit_printer,
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/science/circuit)
|
||||
"lmg" = (
|
||||
/obj/machinery/computer/arcade{
|
||||
icon_state = "arcade";
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"lsv" = (
|
||||
/obj/machinery/power/apc{
|
||||
areastring = "/area/science/circuit";
|
||||
@@ -83667,6 +83662,10 @@
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/maintenance/starboard/fore)
|
||||
"lWL" = (
|
||||
/obj/machinery/smartfridge/organ/preloaded,
|
||||
/turf/closed/wall,
|
||||
/area/medical/surgery)
|
||||
"lWY" = (
|
||||
/obj/machinery/door/airlock/hatch{
|
||||
name = "Telecomms Server Room"
|
||||
@@ -83788,13 +83787,6 @@
|
||||
/obj/item/storage/fancy/candle_box,
|
||||
/turf/open/floor/engine/cult,
|
||||
/area/library)
|
||||
"oba" = (
|
||||
/obj/structure/chair/stool,
|
||||
/obj/machinery/light/small{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"obb" = (
|
||||
/obj/structure/target_stake,
|
||||
/obj/effect/turf_decal/stripes/line{
|
||||
@@ -84063,6 +84055,20 @@
|
||||
},
|
||||
/turf/open/floor/plasteel/dark,
|
||||
/area/crew_quarters/cryopod)
|
||||
"ram" = (
|
||||
/obj/machinery/computer/arcade{
|
||||
icon_state = "arcade";
|
||||
dir = 1
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"roa" = (
|
||||
/obj/structure/chair/stool,
|
||||
/obj/machinery/light/small{
|
||||
dir = 4
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"rzX" = (
|
||||
/obj/structure/chair/office/light{
|
||||
dir = 1;
|
||||
@@ -84076,6 +84082,17 @@
|
||||
},
|
||||
/turf/open/floor/plasteel/white,
|
||||
/area/science/lab)
|
||||
"rLV" = (
|
||||
/obj/structure/table,
|
||||
/obj/item/folder,
|
||||
/obj/item/paper/guides/jobs/hydroponics,
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/item/pen,
|
||||
/obj/item/storage/crayons,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"rMS" = (
|
||||
/obj/machinery/status_display/supply,
|
||||
/turf/closed/wall,
|
||||
@@ -84271,15 +84288,6 @@
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/maintenance/starboard)
|
||||
"uHA" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "2-8"
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"uJU" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
@@ -84364,10 +84372,6 @@
|
||||
/obj/structure/lattice,
|
||||
/turf/open/space/basic,
|
||||
/area/space)
|
||||
"vPN" = (
|
||||
/obj/machinery/smartfridge/organ/preloaded,
|
||||
/turf/closed/wall,
|
||||
/area/medical/surgery)
|
||||
"wdu" = (
|
||||
/obj/structure/grille,
|
||||
/obj/structure/lattice,
|
||||
@@ -84440,17 +84444,6 @@
|
||||
/obj/machinery/atmospherics/pipe/simple/supply/hidden,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/science/misc_lab)
|
||||
"xcb" = (
|
||||
/obj/structure/table,
|
||||
/obj/item/folder,
|
||||
/obj/item/paper/guides/jobs/hydroponics,
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/item/pen,
|
||||
/obj/item/storage/crayons,
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"xeC" = (
|
||||
/obj/machinery/light,
|
||||
/turf/open/floor/plasteel/dark,
|
||||
@@ -84507,6 +84500,15 @@
|
||||
},
|
||||
/turf/open/floor/plating,
|
||||
/area/maintenance/port/aft)
|
||||
"xIi" = (
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "1-2"
|
||||
},
|
||||
/obj/structure/cable/yellow{
|
||||
icon_state = "2-8"
|
||||
},
|
||||
/turf/open/floor/plasteel,
|
||||
/area/security/prison)
|
||||
"xVl" = (
|
||||
/turf/closed/wall,
|
||||
/area/hallway/secondary/service)
|
||||
@@ -104237,7 +104239,7 @@ cia
|
||||
cia
|
||||
cpX
|
||||
cia
|
||||
vPN
|
||||
lWL
|
||||
cia
|
||||
ceu
|
||||
dyg
|
||||
@@ -106710,11 +106712,11 @@ aaa
|
||||
aaf
|
||||
aaf
|
||||
aay
|
||||
xcb
|
||||
uHA
|
||||
rLV
|
||||
xIi
|
||||
abC
|
||||
kwg
|
||||
uHA
|
||||
jhu
|
||||
xIi
|
||||
abC
|
||||
abC
|
||||
acr
|
||||
@@ -106968,10 +106970,10 @@ aaf
|
||||
aaa
|
||||
aax
|
||||
abl
|
||||
oba
|
||||
lmg
|
||||
roa
|
||||
ram
|
||||
aax
|
||||
kRT
|
||||
iCn
|
||||
aaR
|
||||
aaR
|
||||
aaI
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+416
-419
File diff suppressed because it is too large
Load Diff
@@ -177,6 +177,7 @@
|
||||
#define COMSIG_MOB_PRE_PLAYER_CHANGE "mob_pre_player_change" //sent to the target mob from base of /mob/transfer_ckey() and /mind/transfer_to(): (our_character, their_character)
|
||||
// #define COMPONENT_STOP_MIND_TRANSFER 1
|
||||
#define COMSIG_MOB_UPDATE_SIGHT "mob_update_sight" //from base of /mob/update_sight(): ()
|
||||
#define COMSIG_MOB_ON_NEW_MIND "mob_on_new_mind" //called when a new mind is assigned to a mob: ()
|
||||
#define COMSIG_MOB_SAY "mob_say" // from /mob/living/say(): (proc args list)
|
||||
#define COMPONENT_UPPERCASE_SPEECH 1
|
||||
// used to access COMSIG_MOB_SAY argslist
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#define CURRENT_LIVING_PLAYERS 1
|
||||
#define CURRENT_LIVING_ANTAGS 2
|
||||
#define CURRENT_DEAD_PLAYERS 3
|
||||
#define CURRENT_OBSERVERS 4
|
||||
|
||||
#define NO_ASSASSIN (1<<0)
|
||||
#define WAROPS_ALWAYS_ALLOWED (1<<1)
|
||||
|
||||
#define ONLY_RULESET (1<<0)
|
||||
#define HIGHLANDER_RULESET (1<<1)
|
||||
#define TRAITOR_RULESET (1<<2)
|
||||
#define MINOR_RULESET (1<<3)
|
||||
|
||||
#define RULESET_STOP_PROCESSING 1
|
||||
@@ -427,6 +427,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
|
||||
|
||||
//Dummy mob reserve slots
|
||||
#define DUMMY_HUMAN_SLOT_PREFERENCES "dummy_preference_preview"
|
||||
#define DUMMY_HUMAN_SLOT_HOLOFORM "dummy_holoform_generation"
|
||||
#define DUMMY_HUMAN_SLOT_ADMIN "admintools"
|
||||
#define DUMMY_HUMAN_SLOT_MANIFEST "dummy_manifest_generation"
|
||||
|
||||
@@ -529,4 +530,10 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S
|
||||
|
||||
#define CRYOMOBS 'icons/obj/cryo_mobs.dmi'
|
||||
|
||||
#define CUSTOM_HOLOFORM_DELAY 10 SECONDS //prevents spamming to make lag. it's pretty expensive to do this.
|
||||
|
||||
#define HOLOFORM_FILTER_AI "FILTER_AI"
|
||||
#define HOLOFORM_FILTER_PAI "FILTER_PAI"
|
||||
#define HOLOFORM_FILTER_STATIC "FILTER_STATIC"
|
||||
|
||||
#define CANT_REENTER_ROUND -1
|
||||
|
||||
@@ -172,7 +172,7 @@
|
||||
#define TRAIT_AUTO_CATCH_ITEM "auto_catch_item"
|
||||
#define TRAIT_CLOWN_MENTALITY "clown_mentality" // The future is now, clownman.
|
||||
#define TRAIT_FREESPRINT "free_sprinting"
|
||||
|
||||
#define TRAIT_NO_ALCOHOL "alcohol_intolerance"
|
||||
|
||||
// common trait sources
|
||||
#define TRAIT_GENERIC "generic"
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#define PLURALITY_VOTING 0
|
||||
#define APPROVAL_VOTING 1
|
||||
#define RANKED_CHOICE_VOTING 2
|
||||
#define SCORE_VOTING 3
|
||||
|
||||
GLOBAL_LIST_INIT(vote_score_options,list("Bad","Poor","Acceptable","Good","Great"))
|
||||
@@ -0,0 +1,62 @@
|
||||
// Generates a holoform appearance
|
||||
// Equipment list is slot = path.
|
||||
/proc/generate_custom_holoform_from_prefs(datum/preferences/prefs, list/equipment_by_slot, list/inhand_equipment, copy_job = FALSE, apply_loadout = FALSE)
|
||||
ASSERT(prefs)
|
||||
var/mob/living/carbon/human/dummy/mannequin = generate_or_wait_for_human_dummy(DUMMY_HUMAN_SLOT_HOLOFORM)
|
||||
prefs.copy_to(mannequin)
|
||||
if(apply_loadout && prefs.parent)
|
||||
SSjob.equip_loadout(prefs.parent.mob, mannequin, bypass_prereqs = TRUE)
|
||||
if(copy_job)
|
||||
var/datum/job/highest = prefs.get_highest_job()
|
||||
if(highest && !istype(highest, /datum/job/ai) && !istype(highest, /datum/job/cyborg))
|
||||
highest.equip(mannequin, TRUE, preference_source = prefs.parent)
|
||||
|
||||
if(length(equipment_by_slot))
|
||||
for(var/slot in equipment_by_slot)
|
||||
var/obj/item/I = new equipment_by_slot[slot]
|
||||
mannequin.equip_to_slot_if_possible(I, slot, TRUE, TRUE, TRUE, TRUE)
|
||||
if(length(inhand_equipment))
|
||||
for(var/path in inhand_equipment)
|
||||
var/obj/item/I = new path
|
||||
mannequin.equip_to_slot_if_possible(I, SLOT_HANDS, TRUE, TRUE, TRUE, TRUE)
|
||||
|
||||
|
||||
var/icon/combined = new
|
||||
for(var/d in GLOB.cardinals)
|
||||
mannequin.setDir(d)
|
||||
COMPILE_OVERLAYS(mannequin)
|
||||
CHECK_TICK
|
||||
var/icon/capture = getFlatIcon(mannequin)
|
||||
CHECK_TICK
|
||||
combined.Insert(capture, dir = d)
|
||||
CHECK_TICK
|
||||
|
||||
unset_busy_human_dummy(DUMMY_HUMAN_SLOT_HOLOFORM)
|
||||
return combined
|
||||
|
||||
/proc/process_holoform_icon_filter(icon/I, filter_type, clone = TRUE)
|
||||
if(clone)
|
||||
I = icon(I) //Clone
|
||||
switch(filter_type)
|
||||
if(HOLOFORM_FILTER_AI)
|
||||
I = getHologramIcon(I)
|
||||
if(HOLOFORM_FILTER_STATIC)
|
||||
I = getStaticIcon(I)
|
||||
if(HOLOFORM_FILTER_PAI)
|
||||
I = getPAIHologramIcon(I)
|
||||
return I
|
||||
|
||||
//Errors go to user.
|
||||
/proc/generate_custom_holoform_from_prefs_safe(datum/preferences/prefs, mob/user)
|
||||
if(user)
|
||||
if(user.client.prefs.last_custom_holoform > world.time - CUSTOM_HOLOFORM_DELAY)
|
||||
to_chat(user, "<span class='boldwarning'>You are attempting to set your custom holoform too fast!</span>")
|
||||
return
|
||||
return generate_custom_holoform_from_prefs(prefs, null, null, TRUE, TRUE)
|
||||
|
||||
//Prompts this client for custom holoform parameters.
|
||||
/proc/user_interface_custom_holoform(client/C)
|
||||
var/datum/preferences/target_prefs = C.prefs
|
||||
ASSERT(target_prefs)
|
||||
//In the future, maybe add custom path allowances a la admin create outfit but for now..
|
||||
return generate_custom_holoform_from_prefs_safe(target_prefs, C.mob)
|
||||
@@ -444,12 +444,8 @@
|
||||
candidates -= M
|
||||
|
||||
/proc/pollGhostCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE)
|
||||
var/list/candidates = list()
|
||||
|
||||
for(var/mob/dead/observer/G in GLOB.player_list)
|
||||
if(G.can_reenter_round(TRUE))
|
||||
candidates += G
|
||||
|
||||
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(/datum/element/ghost_role_eligibility)
|
||||
var/list/candidates = eligibility.get_all_ghost_role_eligible()
|
||||
return pollCandidates(Question, jobbanType, gametypeCheck, be_special_flag, poll_time, ignore_category, flashwindow, candidates)
|
||||
|
||||
/proc/pollCandidates(Question, jobbanType, datum/game_mode/gametypeCheck, be_special_flag = 0, poll_time = 300, ignore_category = null, flashwindow = TRUE, list/group = null)
|
||||
|
||||
@@ -939,7 +939,7 @@ world
|
||||
I.pixel_y++
|
||||
add_overlay(I)//And finally add the overlay.
|
||||
|
||||
/proc/getHologramIcon(icon/A, safety=1)//If safety is on, a new icon is not created.
|
||||
/proc/getHologramIcon(icon/A, safety = TRUE)//If safety is on, a new icon is not created.
|
||||
var/icon/flat_icon = safety ? A : new(A)//Has to be a new icon to not constantly change the same icon.
|
||||
flat_icon.ColorTone(rgb(125,180,225))//Let's make it bluish.
|
||||
flat_icon.ChangeOpacity(0.5)//Make it half transparent.
|
||||
@@ -947,6 +947,14 @@ world
|
||||
flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect.
|
||||
return flat_icon
|
||||
|
||||
/proc/getPAIHologramIcon(icon/A, safety = TRUE)
|
||||
var/icon/flat_icon = safety? A : new(A)
|
||||
flat_icon.SetIntensity(0.75, 1, 0.75)
|
||||
flat_icon.ChangeOpacity(0.7)
|
||||
var/icon/alpha_mask = new('icons/effects/effects.dmi', "scanlineslow")//Scanline effect.
|
||||
flat_icon.AddAlphaMask(alpha_mask)//Finally, let's mix in a distortion effect.
|
||||
return flat_icon
|
||||
|
||||
//What the mob looks like as animated static
|
||||
//By vg's ComicIronic
|
||||
/proc/getStaticIcon(icon/A, safety = TRUE)
|
||||
|
||||
@@ -23,6 +23,8 @@ GLOBAL_VAR_INIT(bsa_unlock, FALSE) //BSA unlocked by head ID swipes
|
||||
|
||||
GLOBAL_LIST_EMPTY(player_details) // ckey -> /datum/player_details
|
||||
|
||||
GLOBAL_LIST_EMPTY(clientless_round_timeouts) // ckey -> time that ckey can rejoin round
|
||||
|
||||
// All religion stuff
|
||||
GLOBAL_VAR(religion)
|
||||
GLOBAL_VAR(deity)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
var/list/modes // allowed modes
|
||||
var/list/gamemode_cache
|
||||
var/list/votable_modes // votable modes
|
||||
var/list/storyteller_cache
|
||||
var/list/mode_names
|
||||
var/list/mode_reports
|
||||
var/list/mode_false_report_weight
|
||||
@@ -37,6 +38,7 @@
|
||||
CRASH("/datum/controller/configuration/Load() called more than once!")
|
||||
InitEntries()
|
||||
LoadModes()
|
||||
storyteller_cache = typecacheof(/datum/dynamic_storyteller, TRUE)
|
||||
if(fexists("[directory]/config.txt") && LoadEntries("config.txt") <= 1)
|
||||
var/list/legacy_configs = list("game_options.txt", "dbconfig.txt", "comms.txt")
|
||||
for(var/I in legacy_configs)
|
||||
@@ -227,6 +229,7 @@
|
||||
for(var/T in gamemode_cache)
|
||||
// I wish I didn't have to instance the game modes in order to look up
|
||||
// their information, but it is the only way (at least that I know of).
|
||||
// for future reference: just use initial() lol
|
||||
var/datum/game_mode/M = new T()
|
||||
|
||||
if(M.config_tag)
|
||||
@@ -317,6 +320,14 @@
|
||||
return new T
|
||||
return new /datum/game_mode/extended()
|
||||
|
||||
/datum/controller/configuration/proc/pick_storyteller(storyteller_name)
|
||||
for(var/T in storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
var/name = initial(S.name)
|
||||
if(name && name == storyteller_name)
|
||||
return T
|
||||
return /datum/dynamic_storyteller/classic
|
||||
|
||||
/datum/controller/configuration/proc/get_runnable_modes()
|
||||
var/list/datum/game_mode/runnable_modes = new
|
||||
var/list/probabilities = Get(/datum/config_entry/keyed_list/probability)
|
||||
|
||||
@@ -386,6 +386,8 @@
|
||||
config_entry_value = 72000
|
||||
min_val = 0
|
||||
|
||||
/datum/config_entry/flag/pai_custom_holoforms
|
||||
|
||||
/datum/config_entry/number/marauder_delay_non_reebe
|
||||
config_entry_value = 1800
|
||||
min_val = 0
|
||||
|
||||
@@ -658,7 +658,7 @@ SUBSYSTEM_DEF(job)
|
||||
message_admins(msg)
|
||||
CRASH(msg)
|
||||
|
||||
/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff)
|
||||
/datum/controller/subsystem/job/proc/equip_loadout(mob/dead/new_player/N, mob/living/M, equipbackpackstuff, bypass_prereqs = FALSE)
|
||||
var/mob/the_mob = N
|
||||
if(!the_mob)
|
||||
the_mob = M // cause this doesn't get assigned if player is a latejoiner
|
||||
@@ -671,7 +671,7 @@ SUBSYSTEM_DEF(job)
|
||||
if(!G)
|
||||
continue
|
||||
var/permitted = TRUE
|
||||
if(G.restricted_roles && G.restricted_roles.len && !(M.mind.assigned_role in G.restricted_roles))
|
||||
if(!bypass_prereqs && G.restricted_roles && G.restricted_roles.len && !(M.mind.assigned_role in G.restricted_roles))
|
||||
permitted = FALSE
|
||||
if(G.donoritem && !G.donator_ckey_check(the_mob.client.ckey))
|
||||
permitted = FALSE
|
||||
|
||||
@@ -13,13 +13,14 @@ SUBSYSTEM_DEF(persistence)
|
||||
var/list/saved_messages = list()
|
||||
var/list/saved_modes = list(1,2,3)
|
||||
var/list/saved_dynamic_rules = list(list(),list(),list())
|
||||
var/list/saved_threat_levels = list(1,1,1)
|
||||
var/list/saved_storytellers = list("foo","bar","baz","foo again","bar again")
|
||||
var/list/saved_maps
|
||||
var/list/saved_trophies = list()
|
||||
var/list/spawned_objects = list()
|
||||
var/list/antag_rep = list()
|
||||
var/list/antag_rep_change = list()
|
||||
var/list/picture_logging_information = list()
|
||||
var/list/saved_votes = list()
|
||||
var/list/obj/structure/sign/picture_frame/photo_frames
|
||||
var/list/obj/item/storage/photo_album/photo_albums
|
||||
|
||||
@@ -29,9 +30,12 @@ SUBSYSTEM_DEF(persistence)
|
||||
LoadChiselMessages()
|
||||
LoadTrophies()
|
||||
LoadRecentModes()
|
||||
LoadRecentThreats()
|
||||
LoadRecentStorytellers()
|
||||
LoadRecentRulesets()
|
||||
LoadRecentMaps()
|
||||
LoadPhotoPersistence()
|
||||
for(var/client/C in GLOB.clients)
|
||||
LoadSavedVote(C.ckey)
|
||||
if(CONFIG_GET(flag/use_antag_rep))
|
||||
LoadAntagReputation()
|
||||
LoadRandomizedRecipes()
|
||||
@@ -169,14 +173,23 @@ SUBSYSTEM_DEF(persistence)
|
||||
return
|
||||
saved_modes = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentThreats()
|
||||
var/json_file = file("data/RecentThreatLevels.json")
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentRulesets()
|
||||
var/json_file = file("data/RecentRulesets.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
saved_threat_levels = json["data"]
|
||||
saved_dynamic_rules = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentStorytellers()
|
||||
var/json_file = file("data/RecentStorytellers.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
saved_storytellers = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
|
||||
var/json_file = file("data/RecentMaps.json")
|
||||
@@ -197,6 +210,15 @@ SUBSYSTEM_DEF(persistence)
|
||||
return
|
||||
antag_rep = json_decode(json)
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/LoadSavedVote(var/ckey)
|
||||
var/json_file = file("data/player_saves/[copytext(ckey,1,2)]/[ckey]/SavedVotes.json")
|
||||
if(!fexists(json_file))
|
||||
return
|
||||
var/list/json = json_decode(file2text(json_file))
|
||||
if(!json)
|
||||
return
|
||||
saved_votes[ckey] = json["data"]
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
|
||||
for(var/A in GLOB.trophy_cases)
|
||||
var/obj/structure/displaycase/trophy/T = A
|
||||
@@ -230,7 +252,7 @@ SUBSYSTEM_DEF(persistence)
|
||||
CollectRoundtype()
|
||||
if(istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
var/datum/game_mode/dynamic/mode = SSticker.mode
|
||||
CollectThreatLevel(mode)
|
||||
CollectStoryteller(mode)
|
||||
CollectRulesets(mode)
|
||||
RecordMaps()
|
||||
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
|
||||
@@ -388,13 +410,16 @@ SUBSYSTEM_DEF(persistence)
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/CollectThreatLevel(var/datum/game_mode/dynamic/mode)
|
||||
saved_threat_levels[3] = saved_threat_levels[2]
|
||||
saved_threat_levels[2] = saved_threat_levels [1]
|
||||
saved_threat_levels[1] = mode.threat_level
|
||||
var/json_file = file("data/RecentThreatLevels.json")
|
||||
/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode)
|
||||
saved_storytellers.len = 5
|
||||
saved_storytellers[5] = saved_storytellers[4]
|
||||
saved_storytellers[4] = saved_storytellers[3]
|
||||
saved_storytellers[3] = saved_storytellers[2]
|
||||
saved_storytellers[2] = saved_storytellers[1]
|
||||
saved_storytellers[1] = mode.storyteller.name
|
||||
var/json_file = file("data/RecentStorytellers.json")
|
||||
var/list/file_data = list()
|
||||
file_data["data"] = saved_threat_levels
|
||||
file_data["data"] = saved_storytellers
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
@@ -402,8 +427,9 @@ SUBSYSTEM_DEF(persistence)
|
||||
saved_dynamic_rules[3] = saved_dynamic_rules[2]
|
||||
saved_dynamic_rules[2] = saved_dynamic_rules[1]
|
||||
saved_dynamic_rules[1] = list()
|
||||
for(var/datum/dynamic_ruleset/ruleset in mode.executed_rules)
|
||||
saved_dynamic_rules[1] += ruleset.config_tag
|
||||
for(var/r in mode.executed_rules)
|
||||
var/datum/dynamic_ruleset/rule = r
|
||||
saved_dynamic_rules[1] += rule.config_tag
|
||||
var/json_file = file("data/RecentRulesets.json")
|
||||
var/list/file_data = list()
|
||||
file_data["data"] = saved_dynamic_rules
|
||||
@@ -473,3 +499,11 @@ SUBSYSTEM_DEF(persistence)
|
||||
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
/datum/controller/subsystem/persistence/proc/SaveSavedVotes()
|
||||
for(var/ckey in saved_votes)
|
||||
var/json_file = file("data/player_saves/[copytext(ckey,1,2)]/[ckey]/SavedVotes.json")
|
||||
var/list/file_data = list()
|
||||
file_data["data"] = saved_votes[ckey]
|
||||
fdel(json_file)
|
||||
WRITE_FILE(json_file, json_encode(file_data))
|
||||
|
||||
@@ -17,7 +17,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
|
||||
/datum/controller/subsystem/processing/quirks/Initialize(timeofday)
|
||||
if(!quirks.len)
|
||||
SetupQuirks()
|
||||
quirk_blacklist = list(list("Blind","Nearsighted"),list("Jolly","Depression","Apathetic"),list("Ageusia","Deviant Tastes"),list("Ananas Affinity","Ananas Aversion"))
|
||||
quirk_blacklist = list(list("Blind","Nearsighted"),list("Jolly","Depression","Apathetic"),list("Ageusia","Deviant Tastes"),list("Ananas Affinity","Ananas Aversion"),list("Alcohol Tolerance","Alcohol Intolerance"),list("Alcohol Intolerance","Drunken Resilience"))
|
||||
return ..()
|
||||
|
||||
/datum/controller/subsystem/processing/quirks/proc/SetupQuirks()
|
||||
@@ -46,8 +46,8 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
|
||||
badquirk = TRUE
|
||||
if(badquirk)
|
||||
cli.prefs.save_character()
|
||||
if(!silent && LAZYLEN(cut))
|
||||
to_chat(to_chat_target || user, "<span class='boldwarning'>All of your non-neutral character quirks have been cut due to these quirks conflicting with your job assignment: [english_list(cut)].</span>")
|
||||
if (!silent && LAZYLEN(cut))
|
||||
to_chat(to_chat_target || user, "<span class='boldwarning'>Some quirks have been cut from your character because of these quirks conflicting with your job assignment: [english_list(cut)].</span>")
|
||||
|
||||
/datum/controller/subsystem/processing/quirks/proc/quirk_path_by_name(name)
|
||||
return quirks[name]
|
||||
@@ -66,6 +66,7 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
|
||||
/datum/controller/subsystem/processing/quirks/proc/filter_quirks(list/our_quirks, datum/job/job)
|
||||
var/list/cut = list()
|
||||
var/list/banned_names = list()
|
||||
var/pointscut = 0
|
||||
for(var/i in job.blacklisted_quirks)
|
||||
var/name = quirk_name_by_path(i)
|
||||
if(name)
|
||||
@@ -75,7 +76,17 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
|
||||
for(var/i in blacklisted)
|
||||
our_quirks -= i
|
||||
cut += i
|
||||
|
||||
pointscut += quirk_points_by_name(i)
|
||||
if (pointscut != 0)
|
||||
for (var/i in shuffle(our_quirks))
|
||||
if (quirk_points_by_name(i) < pointscut || (pointscut < 0) ? quirk_points_by_name(i) <= 0 : quirk_points_by_name(i) >= 0)
|
||||
continue
|
||||
else
|
||||
our_quirks -= i
|
||||
cut += i
|
||||
pointscut += quirk_points_by_name(i)
|
||||
if (pointscut >= 0) //with how it works, it needs to be above zero, not below, as points for positive is positive, and negative is negative, we only want it to break if it's above zero, ie. we cut more positive than negative
|
||||
break
|
||||
/* //Code to automatically reduce positive quirks until balance is even.
|
||||
var/points_used = total_points(our_quirks)
|
||||
if(points_used > 0)
|
||||
@@ -91,10 +102,11 @@ PROCESSING_SUBSYSTEM_DEF(quirks)
|
||||
*/
|
||||
|
||||
//Nah, let's null all non-neutrals out.
|
||||
if(cut.len)
|
||||
for(var/i in our_quirks)
|
||||
if(quirk_points_by_name(i) != 0)
|
||||
//cut += i -- Commented out: Only show the ones that triggered the quirk purge.
|
||||
our_quirks -= i
|
||||
if (pointscut != 0)// only if the pointscutting didn't work.
|
||||
if(cut.len)
|
||||
for(var/i in our_quirks)
|
||||
if(quirk_points_by_name(i) != 0)
|
||||
//cut += i -- Commented out: Only show the ones that triggered the quirk purge.
|
||||
our_quirks -= i
|
||||
|
||||
return cut
|
||||
|
||||
@@ -37,7 +37,7 @@ SUBSYSTEM_DEF(shuttle)
|
||||
var/points = 5000 //number of trade-points we have
|
||||
var/centcom_message = "" //Remarks from CentCom on how well you checked the last order.
|
||||
var/list/discoveredPlants = list() //Typepaths for unusual plants we've already sent CentCom, associated with their potencies
|
||||
var/passive_supply_points_per_minute = 750
|
||||
var/passive_supply_points_per_minute = 500
|
||||
|
||||
var/list/supply_packs = list()
|
||||
var/list/shoppinglist = list()
|
||||
|
||||
@@ -483,7 +483,10 @@ SUBSYSTEM_DEF(ticker)
|
||||
SSticker.timeLeft = 900
|
||||
SSticker.modevoted = TRUE
|
||||
var/dynamic = CONFIG_GET(flag/dynamic_voting)
|
||||
SSvote.initiate_vote(dynamic ? "dynamic" : "roundtype","server",TRUE)
|
||||
if(dynamic)
|
||||
SSvote.initiate_vote("dynamic","server",hideresults=TRUE,votesystem=SCORE_VOTING,forced=TRUE,vote_time = 2 MINUTES)
|
||||
else
|
||||
SSvote.initiate_vote("roundtype","server",hideresults=TRUE,votesystem=PLURALITY_VOTING,forced=TRUE, vote_time = 2 MINUTES)
|
||||
|
||||
/datum/controller/subsystem/ticker/Recover()
|
||||
current_state = SSticker.current_state
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#define VOTE_COOLDOWN 10
|
||||
|
||||
SUBSYSTEM_DEF(vote)
|
||||
name = "Vote"
|
||||
wait = 10
|
||||
@@ -8,13 +10,17 @@ SUBSYSTEM_DEF(vote)
|
||||
|
||||
var/initiator = null
|
||||
var/started_time = null
|
||||
var/time_remaining = 0
|
||||
var/end_time = 0
|
||||
var/mode = null
|
||||
var/vote_system = PLURALITY_VOTING
|
||||
var/question = null
|
||||
var/list/choices = list()
|
||||
var/list/choice_descs = list() // optional descriptions
|
||||
var/list/voted = list()
|
||||
var/list/voting = list()
|
||||
var/list/saved = list()
|
||||
var/list/generated_actions = list()
|
||||
var/next_pop = 0
|
||||
|
||||
var/obfuscated = FALSE//CIT CHANGE - adds obfuscated/admin-only votes
|
||||
|
||||
@@ -22,28 +28,30 @@ SUBSYSTEM_DEF(vote)
|
||||
|
||||
/datum/controller/subsystem/vote/fire() //called by master_controller
|
||||
if(mode)
|
||||
time_remaining = round((started_time + CONFIG_GET(number/vote_period) - world.time)/10)
|
||||
|
||||
if(time_remaining < 0)
|
||||
if(end_time < world.time)
|
||||
result()
|
||||
SSpersistence.SaveSavedVotes()
|
||||
for(var/client/C in voting)
|
||||
C << browse(null, "window=vote;can_close=0")
|
||||
reset()
|
||||
else
|
||||
else if(next_pop < world.time)
|
||||
var/datum/browser/client_popup
|
||||
for(var/client/C in voting)
|
||||
client_popup = new(C, "vote", "Voting Panel")
|
||||
client_popup = new(C, "vote", "Voting Panel", nwidth=600,nheight=700)
|
||||
client_popup.set_window_options("can_close=0")
|
||||
client_popup.set_content(interface(C))
|
||||
client_popup.open(0)
|
||||
next_pop = world.time+VOTE_COOLDOWN
|
||||
|
||||
|
||||
|
||||
/datum/controller/subsystem/vote/proc/reset()
|
||||
initiator = null
|
||||
time_remaining = 0
|
||||
end_time = 0
|
||||
mode = null
|
||||
question = null
|
||||
choices.Cut()
|
||||
choice_descs.Cut()
|
||||
voted.Cut()
|
||||
voting.Cut()
|
||||
obfuscated = FALSE //CIT CHANGE - obfuscated votes
|
||||
@@ -84,17 +92,114 @@ SUBSYSTEM_DEF(vote)
|
||||
. += option
|
||||
return .
|
||||
|
||||
/datum/controller/subsystem/vote/proc/calculate_condorcet_votes(var/blackbox_text)
|
||||
// https://en.wikipedia.org/wiki/Schulze_method#Implementation
|
||||
var/list/d[][] = new/list(choices.len,choices.len) // the basic vote matrix, how many times a beats b
|
||||
for(var/ckey in voted)
|
||||
var/list/this_vote = voted[ckey]
|
||||
for(var/a in 1 to choices.len)
|
||||
for(var/b in a+1 to choices.len)
|
||||
var/a_rank = this_vote.Find(a)
|
||||
var/b_rank = this_vote.Find(b)
|
||||
a_rank = a_rank ? a_rank : choices.len+1
|
||||
b_rank = b_rank ? b_rank : choices.len+1
|
||||
if(a_rank<b_rank)
|
||||
d[a][b]++
|
||||
else if(b_rank<a_rank)
|
||||
d[b][a]++
|
||||
//if equal, do nothing
|
||||
var/list/p[][] = new/list(choices.len,choices.len) //matrix of shortest path from a to b
|
||||
for(var/i in 1 to choices.len)
|
||||
for(var/j in 1 to choices.len)
|
||||
if(i != j)
|
||||
var/pref_number = d[i][j]
|
||||
var/opposite_pref = d[j][i]
|
||||
if(pref_number>opposite_pref)
|
||||
p[i][j] = d[i][j]
|
||||
p[j][i] = 0
|
||||
else
|
||||
p[i][j] = 0
|
||||
p[j][i] = d[i][j]
|
||||
for(var/i in 1 to choices.len)
|
||||
for(var/j in 1 to choices.len)
|
||||
if(i != j)
|
||||
for(var/k in 1 to choices.len) // YEAH O(n^3) !!
|
||||
if(i != k && j != k)
|
||||
p[j][k] = max(p[j][k],min(p[j][i], p[i][k]))
|
||||
//one last pass, now that we've done the math
|
||||
for(var/i in 1 to choices.len)
|
||||
for(var/j in 1 to choices.len)
|
||||
if(i != j)
|
||||
SSblackbox.record_feedback("nested tally","voting",p[i][j],list(blackbox_text,"Shortest Paths",choices[i],choices[j]))
|
||||
if(p[i][j] >= p[j][i])
|
||||
choices[choices[i]]++ // higher shortest path = better candidate, so we add to choices here
|
||||
// choices[choices[i]] is the schulze ranking, here, rather than raw vote numbers
|
||||
|
||||
/datum/controller/subsystem/vote/proc/calculate_majority_judgement_vote(var/blackbox_text)
|
||||
// https://en.wikipedia.org/wiki/Majority_judgment
|
||||
var/list/scores_by_choice = list()
|
||||
for(var/choice in choices)
|
||||
scores_by_choice[choice] = list()
|
||||
for(var/ckey in voted)
|
||||
var/list/this_vote = voted[ckey]
|
||||
var/list/pretty_vote = list()
|
||||
for(var/choice in this_vote)
|
||||
sorted_insert(scores_by_choice[choice],this_vote[choice],/proc/cmp_numeric_asc)
|
||||
// START BALLOT GATHERING
|
||||
pretty_vote += choice
|
||||
pretty_vote[choice] = GLOB.vote_score_options[this_vote[choice]]
|
||||
SSblackbox.record_feedback("associative","voting_ballots",1,pretty_vote)
|
||||
// END BALLOT GATHERING
|
||||
for(var/score_name in scores_by_choice)
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
for(var/indiv_score in score)
|
||||
SSblackbox.record_feedback("nested tally","voting",1,list(blackbox_text,"Scores",score_name,GLOB.vote_score_options[indiv_score]))
|
||||
if(score.len == 0)
|
||||
scores_by_choice -= score_name
|
||||
while(scores_by_choice.len > 1)
|
||||
var/highest_median = 0
|
||||
for(var/score_name in scores_by_choice) // first get highest median
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
if(!score.len)
|
||||
scores_by_choice -= score_name
|
||||
continue
|
||||
var/median = score[max(1,round(score.len/2))]
|
||||
if(median >= highest_median)
|
||||
highest_median = median
|
||||
for(var/score_name in scores_by_choice) // then, remove
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
var/median = score[max(1,round(score.len/2))]
|
||||
if(median < highest_median)
|
||||
scores_by_choice -= score_name
|
||||
for(var/score_name in scores_by_choice) // after removals
|
||||
var/list/score = scores_by_choice[score_name]
|
||||
if(score.len == 0)
|
||||
choices[score_name] += 100 // we're in a tie situation--just go with the first one
|
||||
return
|
||||
var/median_pos = max(1,round(score.len/2))
|
||||
score.Cut(median_pos,median_pos+1)
|
||||
choices[score_name]++
|
||||
|
||||
/datum/controller/subsystem/vote/proc/announce_result()
|
||||
var/list/winners = get_result()
|
||||
var/vote_title_text
|
||||
var/text
|
||||
if(question)
|
||||
text += "<b>[question]</b>"
|
||||
vote_title_text = "[question]"
|
||||
else
|
||||
text += "<b>[capitalize(mode)] Vote</b>"
|
||||
vote_title_text = "[capitalize(mode)] Vote"
|
||||
if(vote_system == RANKED_CHOICE_VOTING)
|
||||
calculate_condorcet_votes(vote_title_text)
|
||||
if(vote_system == SCORE_VOTING)
|
||||
calculate_majority_judgement_vote(vote_title_text)
|
||||
var/list/winners = get_result()
|
||||
var/was_roundtype_vote = mode == "roundtype" || mode == "dynamic"
|
||||
if(winners.len > 0)
|
||||
if(question)
|
||||
text += "<b>[question]</b>"
|
||||
else
|
||||
text += "<b>[capitalize(mode)] Vote</b>"
|
||||
if(was_roundtype_vote)
|
||||
stored_gamemode_votes = list()
|
||||
if(!obfuscated && vote_system == RANKED_CHOICE_VOTING)
|
||||
text += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
if(!votes)
|
||||
@@ -116,17 +221,25 @@ SUBSYSTEM_DEF(vote)
|
||||
log_vote(text)
|
||||
remove_action_buttons()
|
||||
to_chat(world, "\n<font color='purple'>[text]</font>")
|
||||
switch(vote_system)
|
||||
if(APPROVAL_VOTING,PLURALITY_VOTING)
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
SSblackbox.record_feedback("nested tally","voting",choices[choices[i]],list(vote_title_text,choices[i]))
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
for(var/i=1,i<=voted.len,i++)
|
||||
var/list/myvote = voted[voted[i]]
|
||||
for(var/j=1,j<=myvote.len,j++)
|
||||
SSblackbox.record_feedback("nested tally","voting",1,list(vote_title_text,"[j]\th",choices[myvote[j]]))
|
||||
if(obfuscated) //CIT CHANGE - adds obfuscated votes. this messages admins with the vote's true results
|
||||
var/admintext = "Obfuscated results"
|
||||
if(vote_system == RANKED_CHOICE_VOTING)
|
||||
admintext += "\nIt should be noted that this is not a raw tally of votes (impossible in ranked choice) but the score determined by the schulze method of voting, so the numbers will look weird!"
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
admintext += "\n<b>[choices[i]]:</b> [votes]"
|
||||
message_admins(admintext)
|
||||
return .
|
||||
|
||||
#define PEACE "calm"
|
||||
#define CHAOS "chaotic"
|
||||
|
||||
/datum/controller/subsystem/vote/proc/result()
|
||||
. = announce_result()
|
||||
var/restart = 0
|
||||
@@ -152,33 +265,15 @@ SUBSYSTEM_DEF(vote)
|
||||
if("dynamic")
|
||||
if(SSticker.current_state > GAME_STATE_PREGAME)//Don't change the mode if the round already started.
|
||||
return message_admins("A vote has tried to change the gamemode, but the game has already started. Aborting.")
|
||||
GLOB.master_mode = "dynamic"
|
||||
if("extended" in choices)
|
||||
if(. == "extended")
|
||||
GLOB.dynamic_forced_extended = TRUE // we still do the rest of the stuff
|
||||
choices[PEACE] += choices["extended"]
|
||||
var/mean = 0
|
||||
var/voters = 0
|
||||
for(var/client/c in GLOB.clients)
|
||||
var/vote = c.prefs.preferred_chaos
|
||||
if(vote)
|
||||
voters += 1
|
||||
switch(vote)
|
||||
if(CHAOS_NONE)
|
||||
mean -= 0.1
|
||||
if(CHAOS_LOW)
|
||||
mean -= 0.05
|
||||
if(CHAOS_HIGH)
|
||||
mean += 0.05
|
||||
if(CHAOS_MAX)
|
||||
mean += 0.1
|
||||
mean/=voters
|
||||
if(voted.len != 0)
|
||||
mean += (choices[PEACE]*-1+choices[CHAOS])/voted.len
|
||||
GLOB.dynamic_curve_centre = mean*20
|
||||
GLOB.dynamic_curve_width = CLAMP(2-abs(mean*5),0.5,4)
|
||||
to_chat(world,"<span class='boldannounce'>Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width].</span>")
|
||||
log_admin("Dynamic curve centre set to [GLOB.dynamic_curve_centre] and width set to [GLOB.dynamic_curve_width]")
|
||||
if(. == "Secret")
|
||||
GLOB.master_mode = "secret"
|
||||
SSticker.save_mode(.)
|
||||
message_admins("The gamemode has been voted for, and has been changed to: [GLOB.master_mode]")
|
||||
log_admin("Gamemode has been voted for and switched to: [GLOB.master_mode].")
|
||||
else
|
||||
GLOB.master_mode = "dynamic"
|
||||
var/datum/dynamic_storyteller/S = config.pick_storyteller(.)
|
||||
GLOB.dynamic_storyteller_type = S
|
||||
if("map")
|
||||
var/datum/map_config/VM = config.maplist[.]
|
||||
message_admins("The map has been voted for and will change to: [VM.map_name]")
|
||||
@@ -196,27 +291,58 @@ SUBSYSTEM_DEF(vote)
|
||||
else
|
||||
to_chat(world, "<span style='boldannounce'>Notice:Restart vote will not restart the server automatically because there are active admins on.</span>")
|
||||
message_admins("A restart vote has passed, but there are active admins on with +server, so it has been canceled. If you wish, you may restart the server.")
|
||||
|
||||
|
||||
return .
|
||||
|
||||
/datum/controller/subsystem/vote/proc/submit_vote(vote)
|
||||
/datum/controller/subsystem/vote/proc/submit_vote(vote, score = 0)
|
||||
if(mode)
|
||||
if(CONFIG_GET(flag/no_dead_vote) && usr.stat == DEAD && !usr.client.holder)
|
||||
return 0
|
||||
if(!(usr.ckey in voted))
|
||||
if(vote && 1<=vote && vote<=choices.len)
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++ //check this
|
||||
return vote
|
||||
else if(vote && 1<=vote && vote<=choices.len)
|
||||
choices[choices[voted[usr.ckey]]]--
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++
|
||||
return vote
|
||||
if(vote && ISINRANGE(vote, 1, choices.len))
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING)
|
||||
if(usr.ckey in voted)
|
||||
choices[choices[voted[usr.ckey]]]--
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++
|
||||
return vote
|
||||
else
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = vote
|
||||
choices[choices[vote]]++ //check this
|
||||
return vote
|
||||
if(APPROVAL_VOTING)
|
||||
if(usr.ckey in voted)
|
||||
if(vote in voted[usr.ckey])
|
||||
voted[usr.ckey] -= vote
|
||||
choices[choices[vote]]--
|
||||
else
|
||||
voted[usr.ckey] += vote
|
||||
choices[choices[vote]]++
|
||||
else
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = list(vote)
|
||||
choices[choices[vote]]++
|
||||
return vote
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
if(usr.ckey in voted)
|
||||
if(vote in voted[usr.ckey])
|
||||
voted[usr.ckey] -= vote
|
||||
else
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = list()
|
||||
voted[usr.ckey] += vote
|
||||
saved -= usr.ckey
|
||||
if(SCORE_VOTING)
|
||||
if(!(usr.ckey in voted))
|
||||
voted += usr.ckey
|
||||
voted[usr.ckey] = list()
|
||||
voted[usr.ckey][choices[vote]] = score
|
||||
saved -= usr.ckey
|
||||
return 0
|
||||
|
||||
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes
|
||||
/datum/controller/subsystem/vote/proc/initiate_vote(vote_type, initiator_key, hideresults, votesystem = PLURALITY_VOTING, forced = FALSE,vote_time = -1)//CIT CHANGE - adds hideresults argument to votes to allow for obfuscated votes
|
||||
vote_system = votesystem
|
||||
if(!mode)
|
||||
if(started_time)
|
||||
var/next_allowed_time = (started_time + CONFIG_GET(number/vote_delay))
|
||||
@@ -257,11 +383,17 @@ SUBSYSTEM_DEF(vote)
|
||||
if("roundtype") //CIT CHANGE - adds the roundstart secret/extended vote
|
||||
choices.Add("secret", "extended")
|
||||
if("dynamic")
|
||||
var/saved_threats = SSpersistence.saved_threat_levels
|
||||
if((saved_threats[1]+saved_threats[2]+saved_threats[3])>150)
|
||||
choices.Add("extended",PEACE,CHAOS)
|
||||
else
|
||||
choices.Add(PEACE,CHAOS)
|
||||
for(var/T in config.storyteller_cache)
|
||||
var/datum/dynamic_storyteller/S = T
|
||||
var/recent_rounds = 0
|
||||
for(var/i in 1 to SSpersistence.saved_storytellers.len)
|
||||
if(SSpersistence.saved_storytellers[i] == initial(S.name))
|
||||
recent_rounds++
|
||||
if(recent_rounds < initial(S.weight))
|
||||
choices.Add(initial(S.name))
|
||||
choice_descs.Add(initial(S.desc))
|
||||
choices.Add("Secret")
|
||||
choice_descs.Add("Standard secret. Switches mode if it wins.")
|
||||
if("custom")
|
||||
question = stripped_input(usr,"What is the vote for?")
|
||||
if(!question)
|
||||
@@ -280,9 +412,11 @@ SUBSYSTEM_DEF(vote)
|
||||
if(mode == "custom")
|
||||
text += "\n[question]"
|
||||
log_vote(text)
|
||||
var/vp = CONFIG_GET(number/vote_period)
|
||||
var/vp = vote_time
|
||||
if(vp == -1)
|
||||
vp = CONFIG_GET(number/vote_period)
|
||||
to_chat(world, "\n<font color='purple'><b>[text]</b>\nType <b>vote</b> or click <a href='?src=[REF(src)]'>here</a> to place your votes.\nYou have [DisplayTimeText(vp)] to vote.</font>")
|
||||
time_remaining = round(vp/10)
|
||||
end_time = started_time+vp
|
||||
for(var/c in GLOB.clients)
|
||||
SEND_SOUND(c, sound('sound/misc/server-ready.ogg'))
|
||||
var/client/C = c
|
||||
@@ -292,6 +426,11 @@ SUBSYSTEM_DEF(vote)
|
||||
C.player_details.player_actions += V
|
||||
V.Grant(C.mob)
|
||||
generated_actions += V
|
||||
if(forced)
|
||||
var/datum/browser/popup = new(C, "vote", "Voting Panel",nwidth=600,nheight=700)
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(SSvote.interface(C))
|
||||
popup.open(0)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@@ -311,14 +450,71 @@ SUBSYSTEM_DEF(vote)
|
||||
. += "<h2>Vote: '[question]'</h2>"
|
||||
else
|
||||
. += "<h2>Vote: [capitalize(mode)]</h2>"
|
||||
. += "Time Left: [time_remaining] s<hr><ul>"
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
var/ivotedforthis = ((C.ckey in voted) && (voted[C.ckey] == i) ? TRUE : FALSE)
|
||||
if(!votes)
|
||||
votes = 0
|
||||
. += "<li>[ivotedforthis ? "<b>" : ""]<a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([obfuscated ? (admin ? "??? ([votes])" : "???") : votes] votes)[ivotedforthis ? "</b>" : ""]</li>" // CIT CHANGE - adds obfuscated votes
|
||||
. += "</ul><hr>"
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING)
|
||||
. += "<h3>Vote one.</h3>"
|
||||
if(APPROVAL_VOTING)
|
||||
. += "<h3>Vote any number of choices.</h3>"
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
. += "<h3>Vote by order of preference. Revoting will demote to the bottom. 1 is your favorite, and higher numbers are worse.</h3>"
|
||||
if(SCORE_VOTING)
|
||||
. += "<h3>Grade the candidates by how much you like them.</h3>"
|
||||
. += "<h3>No-votes have no power--your opinion is only heard if you vote!</h3>"
|
||||
. += "Time Left: [DisplayTimeText(end_time-world.time)]<hr><ul>"
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING, APPROVAL_VOTING)
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/votes = choices[choices[i]]
|
||||
var/ivotedforthis = FALSE
|
||||
switch(vote_system)
|
||||
if(PLURALITY_VOTING)
|
||||
ivotedforthis = ((C.ckey in voted) && (voted[C.ckey] == i))
|
||||
if(APPROVAL_VOTING)
|
||||
ivotedforthis = ((C.ckey in voted) && (i in voted[C.ckey]))
|
||||
if(!votes)
|
||||
votes = 0
|
||||
. += "<li>[ivotedforthis ? "<b>" : ""]<a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([obfuscated ? (admin ? "??? ([votes])" : "???") : votes] votes)[ivotedforthis ? "</b>" : ""]</li>" // CIT CHANGE - adds obfuscated votes
|
||||
if(choice_descs.len >= i)
|
||||
. += "<li>[choice_descs[i]]</li>"
|
||||
. += "</ul><hr>"
|
||||
if(RANKED_CHOICE_VOTING)
|
||||
var/list/myvote = voted[C.ckey]
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
var/vote = (myvote ? (myvote.Find(i)) : 0)
|
||||
if(vote)
|
||||
. += "<li><b><a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a> ([vote])</b></li>"
|
||||
else
|
||||
. += "<li><a href='?src=[REF(src)];vote=[i]'>[choices[i]]</a></li>"
|
||||
if(choice_descs.len >= i)
|
||||
. += "<li>[choice_descs[i]]</li>"
|
||||
. += "</ul><hr>"
|
||||
if(!(C.ckey in saved))
|
||||
. += "(<a href='?src=[REF(src)];vote=save'>Save vote</a>)"
|
||||
else
|
||||
. += "(Saved!)"
|
||||
. += "(<a href='?src=[REF(src)];vote=load'>Load vote from save</a>)"
|
||||
. += "(<a href='?src=[REF(src)];vote=reset'>Reset votes</a>)"
|
||||
if(SCORE_VOTING)
|
||||
var/list/myvote = voted[C.ckey]
|
||||
for(var/i=1,i<=choices.len,i++)
|
||||
. += "<li><b>[choices[i]]</b>"
|
||||
for(var/r in 1 to GLOB.vote_score_options.len)
|
||||
. += " <a href='?src=[REF(src)];vote=[i];score=[r]'>"
|
||||
if((choices[i] in myvote) && myvote[choices[i]] == r)
|
||||
. +="<b>([GLOB.vote_score_options[r]])</b>"
|
||||
else
|
||||
. +="[GLOB.vote_score_options[r]]"
|
||||
. += "</a>"
|
||||
. += "</li>"
|
||||
if(choice_descs.len >= i)
|
||||
. += "<li>[choice_descs[i]]</li>"
|
||||
. += "</ul><hr>"
|
||||
if(!(C.ckey in saved))
|
||||
. += "(<a href='?src=[REF(src)];vote=save'>Save vote</a>)"
|
||||
else
|
||||
. += "(Saved!)"
|
||||
. += "(<a href='?src=[REF(src)];vote=load'>Load vote from save</a>)"
|
||||
. += "(<a href='?src=[REF(src)];vote=reset'>Reset votes</a>)"
|
||||
if(admin)
|
||||
. += "(<a href='?src=[REF(src)];vote=cancel'>Cancel Vote</a>) "
|
||||
else
|
||||
@@ -376,8 +572,31 @@ SUBSYSTEM_DEF(vote)
|
||||
if("custom")
|
||||
if(usr.client.holder)
|
||||
initiate_vote("custom",usr.key)
|
||||
if("reset")
|
||||
if(usr.ckey in voted)
|
||||
voted -= usr.ckey
|
||||
if("save")
|
||||
if(usr.ckey in voted)
|
||||
if(!(usr.ckey in SSpersistence.saved_votes))
|
||||
SSpersistence.saved_votes[usr.ckey] = list()
|
||||
SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey]
|
||||
saved += usr.ckey
|
||||
if("load")
|
||||
if(!(usr.ckey in SSpersistence.saved_votes))
|
||||
SSpersistence.LoadSavedVote(usr.ckey)
|
||||
if(!(usr.ckey in SSpersistence.saved_votes))
|
||||
SSpersistence.saved_votes[usr.ckey] = list()
|
||||
if(usr.ckey in voted)
|
||||
SSpersistence.saved_votes[usr.ckey][mode] = voted[usr.ckey]
|
||||
else
|
||||
SSpersistence.saved_votes[usr.ckey][mode] = list()
|
||||
voted[usr.ckey] = SSpersistence.saved_votes[usr.ckey][mode]
|
||||
saved += usr.ckey
|
||||
else
|
||||
submit_vote(round(text2num(href_list["vote"])))
|
||||
if(vote_system == SCORE_VOTING)
|
||||
submit_vote(round(text2num(href_list["vote"])),round(text2num(href_list["score"])))
|
||||
else
|
||||
submit_vote(round(text2num(href_list["vote"])))
|
||||
usr.vote()
|
||||
|
||||
/datum/controller/subsystem/vote/proc/remove_action_buttons()
|
||||
@@ -392,7 +611,7 @@ SUBSYSTEM_DEF(vote)
|
||||
set category = "OOC"
|
||||
set name = "Vote"
|
||||
|
||||
var/datum/browser/popup = new(src, "vote", "Voting Panel")
|
||||
var/datum/browser/popup = new(src, "vote", "Voting Panel",nwidth=600,nheight=700)
|
||||
popup.set_window_options("can_close=0")
|
||||
popup.set_content(SSvote.interface(client))
|
||||
popup.open(0)
|
||||
@@ -419,6 +638,3 @@ SUBSYSTEM_DEF(vote)
|
||||
var/datum/player_details/P = GLOB.player_details[owner.ckey]
|
||||
if(P)
|
||||
P.player_actions -= src
|
||||
|
||||
#undef PEACE
|
||||
#undef CHAOS
|
||||
|
||||
@@ -141,6 +141,17 @@
|
||||
current_button.add_overlay(mutable_appearance(icon_icon, button_icon_state))
|
||||
current_button.button_icon_state = button_icon_state
|
||||
|
||||
/datum/action/ghost
|
||||
icon_icon = 'icons/mob/mob.dmi'
|
||||
button_icon_state = "ghost"
|
||||
name = "Ghostize"
|
||||
desc = "Turn into a ghost and freely come back to your body."
|
||||
|
||||
/datum/action/ghost/Trigger()
|
||||
if(!..())
|
||||
return 0
|
||||
var/mob/M = target
|
||||
M.ghostize(1)
|
||||
|
||||
//Presets for item actions
|
||||
/datum/action/item_action
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
/datum/component/storage/concrete/_insert_physical_item(obj/item/I, override = FALSE)
|
||||
. = TRUE
|
||||
var/atom/real_location = real_location()
|
||||
if(I.loc != real_location)
|
||||
if(I.loc != real_location && real_location)
|
||||
I.forceMove(real_location)
|
||||
refresh_mob_views()
|
||||
|
||||
|
||||
@@ -87,3 +87,12 @@
|
||||
/datum/component/storage/concrete/pockets/pocketprotector/real_location()
|
||||
// if the component is reparented to a jumpsuit, the items still go in the protector
|
||||
return original_parent
|
||||
|
||||
/datum/component/storage/concrete/pockets/small/rushelmet
|
||||
max_items = 1
|
||||
quickdraw = TRUE
|
||||
|
||||
/datum/component/storage/concrete/pockets/small/rushelmet/Initialize()
|
||||
. = ..()
|
||||
can_hold = typecacheof(list(/obj/item/reagent_containers/glass/bottle,
|
||||
/obj/item/ammo_box/a762))
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
if(type == /datum/element)
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
if(element_flags & ELEMENT_DETACH)
|
||||
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/Detach)
|
||||
RegisterSignal(target, COMSIG_PARENT_QDELETING, .proc/Detach, override = TRUE)
|
||||
|
||||
/datum/element/proc/Detach(datum/source, force)
|
||||
UnregisterSignal(source, COMSIG_PARENT_QDELETING)
|
||||
@@ -36,4 +36,4 @@
|
||||
*/
|
||||
/datum/proc/RemoveElement(eletype, ...)
|
||||
var/datum/element/ele = SSdcs.GetElement(arglist(args))
|
||||
ele.Detach(src)
|
||||
ele.Detach(src)
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/datum/element/ghost_role_eligibility
|
||||
element_flags = ELEMENT_DETACH
|
||||
var/list/timeouts = list()
|
||||
var/list/mob/eligible_mobs = list()
|
||||
|
||||
/datum/element/ghost_role_eligibility/Attach(datum/target,penalize = FALSE)
|
||||
. = ..()
|
||||
if(!ismob(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
var/mob/M = target
|
||||
if(!(M in eligible_mobs))
|
||||
eligible_mobs += M
|
||||
if(penalize) //penalizing them from making a ghost role / midround antag comeback right away.
|
||||
var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES
|
||||
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
|
||||
if(world.time < roundstart_quit_limit) //add up the time difference to their antag rolling penalty if they quit before half a (ingame) hour even passed.
|
||||
penalty += roundstart_quit_limit - world.time
|
||||
if(penalty)
|
||||
penalty += world.realtime
|
||||
if(penalty - SSshuttle.realtimeofstart > SSshuttle.auto_call + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
|
||||
penalty = CANT_REENTER_ROUND
|
||||
if(!(M.ckey in timeouts))
|
||||
timeouts += M.ckey
|
||||
timeouts[M.ckey] = 0
|
||||
timeouts[M.ckey] = max(timeouts[M.ckey],penalty)
|
||||
|
||||
/datum/element/ghost_role_eligibility/Detach(mob/M)
|
||||
. = ..()
|
||||
if(M in eligible_mobs)
|
||||
eligible_mobs -= M
|
||||
|
||||
/datum/element/ghost_role_eligibility/proc/get_all_ghost_role_eligible(silent = FALSE)
|
||||
var/list/candidates = list()
|
||||
for(var/m in eligible_mobs)
|
||||
var/mob/M = m
|
||||
if(M.can_reenter_round(TRUE))
|
||||
candidates += M
|
||||
return candidates
|
||||
|
||||
/mob/proc/can_reenter_round(silent = FALSE)
|
||||
var/datum/element/ghost_role_eligibility/eli = SSdcs.GetElement(/datum/element/ghost_role_eligibility)
|
||||
return eli.can_reenter_round(src,silent)
|
||||
|
||||
/datum/element/ghost_role_eligibility/proc/can_reenter_round(var/mob/M,silent = FALSE)
|
||||
if(!(M in eligible_mobs))
|
||||
return FALSE
|
||||
if(!(M.ckey in timeouts))
|
||||
return TRUE
|
||||
var/timeout = timeouts[M.ckey]
|
||||
if(timeout != CANT_REENTER_ROUND && timeout <= world.realtime)
|
||||
return TRUE
|
||||
if(!silent && M.client)
|
||||
to_chat(M, "<span class='warning'>You are unable to reenter the round[timeout != CANT_REENTER_ROUND ? " yet. Your ghost role blacklist will expire in [DisplayTimeText(timeout - world.realtime)]" : ""].</span>")
|
||||
return FALSE
|
||||
@@ -0,0 +1,60 @@
|
||||
|
||||
/datum/element/wuv //D'awwwww
|
||||
element_flags = ELEMENT_BESPOKE
|
||||
id_arg_index = 2
|
||||
//the for the me emote proc call when petted.
|
||||
var/pet_emote
|
||||
//whether the emote is visible or audible
|
||||
var/pet_type
|
||||
//same as above, except when harmed. "You are going into orbit, you stupid mutt!"
|
||||
var/punt_emote
|
||||
//same as pet_type
|
||||
var/punt_type
|
||||
//mood typepath for the moodlet signal when petted.
|
||||
var/pet_moodlet
|
||||
//same as above but for harm
|
||||
var/punt_moodlet
|
||||
|
||||
/datum/element/wuv/Attach(datum/target, pet, pet_t, pet_mood, punt, punt_t, punt_mood)
|
||||
. = ..()
|
||||
|
||||
if(!isliving(target))
|
||||
return ELEMENT_INCOMPATIBLE
|
||||
|
||||
pet_emote = pet
|
||||
pet_type = pet_t
|
||||
punt_emote = punt
|
||||
punt_type = punt_t
|
||||
pet_moodlet = pet_mood
|
||||
punt_moodlet = punt_mood
|
||||
|
||||
RegisterSignal(target, COMSIG_MOB_ATTACK_HAND, .proc/on_attack_hand)
|
||||
|
||||
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
|
||||
var/mob/living/L = source
|
||||
|
||||
if(L.stat == DEAD)
|
||||
return
|
||||
//we want to delay the effect to be displayed after the mob is petted, not before.
|
||||
switch(user.a_intent)
|
||||
if(INTENT_HARM, INTENT_DISARM)
|
||||
addtimer(CALLBACK(src, .proc/kick_the_dog, source, user), 1)
|
||||
if(INTENT_HELP)
|
||||
addtimer(CALLBACK(src, .proc/pet_the_dog, source, user), 1)
|
||||
|
||||
/datum/element/wuv/proc/pet_the_dog(mob/target, mob/user)
|
||||
if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
|
||||
return
|
||||
new /obj/effect/temp_visual/heart(target.loc)
|
||||
if(pet_emote)
|
||||
target.emote("me", pet_type, pet_emote)
|
||||
if(pet_moodlet && !CHECK_BITFIELD(target.flags_1, HOLOGRAM_1)) //prevents unlimited happiness petting park exploit.
|
||||
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, pet_moodlet, target)
|
||||
|
||||
/datum/element/wuv/proc/kick_the_dog(mob/target, mob/user)
|
||||
if(!QDELETED(target) || !QDELETED(user) || target.stat != CONSCIOUS)
|
||||
return
|
||||
if(punt_emote)
|
||||
target.emote("me", punt_type, punt_emote)
|
||||
if(punt_moodlet && !CHECK_BITFIELD(target.flags_1, HOLOGRAM_1))
|
||||
SEND_SIGNAL(user, COMSIG_ADD_MOOD_EVENT, target, punt_moodlet, target)
|
||||
@@ -136,9 +136,10 @@
|
||||
/datum/emote/sound
|
||||
var/sound //Sound to play when emote is called
|
||||
var/vary = FALSE //used for the honk borg emote
|
||||
var/volume = 50
|
||||
mob_type_allowed_typecache = list(/mob/living/brain, /mob/living/silicon)
|
||||
|
||||
/datum/emote/sound/run_emote(mob/user, params)
|
||||
. = ..()
|
||||
if(.)
|
||||
playsound(user.loc, sound, 50, vary)
|
||||
playsound(user.loc, sound, volume, vary)
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
L.client.prefs.chat_toggles ^= CHAT_OOC
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_MIND_TRANSFER, new_character, old_character)
|
||||
SEND_SIGNAL(new_character, COMSIG_MOB_ON_NEW_MIND)
|
||||
|
||||
/datum/mind/proc/store_memory(new_text)
|
||||
if((length(memory) + length(new_text)) <= MAX_MESSAGE_LEN)
|
||||
@@ -747,6 +748,7 @@
|
||||
else
|
||||
mind = new /datum/mind(key)
|
||||
SSticker.minds += mind
|
||||
SEND_SIGNAL(src, COMSIG_MOB_ON_NEW_MIND)
|
||||
if(!mind.name)
|
||||
mind.name = real_name
|
||||
mind.current = src
|
||||
|
||||
@@ -23,10 +23,13 @@
|
||||
mood_change = 3
|
||||
timeout = 3000
|
||||
|
||||
/datum/mood_event/pet_corgi
|
||||
description = "<span class='nicegreen'>Corgis are adorable! I can't stop petting them!</span>\n"
|
||||
mood_change = 3
|
||||
timeout = 3000
|
||||
/datum/mood_event/pet_animal
|
||||
description = "<span class='nicegreen'>Animals are adorable! I can't stop petting them!</span>\n"
|
||||
mood_change = 2
|
||||
timeout = 5 MINUTES
|
||||
|
||||
/datum/mood_event/pet_animal/add_effects(mob/animal)
|
||||
description = "<span class='nicegreen'>\The [animal.name] is adorable! I can't stop petting [animal.p_them()]!</span>\n"
|
||||
|
||||
/datum/mood_event/honk
|
||||
description = "<span class='nicegreen'>Maybe clowns aren't so bad after all. Honk!</span>\n"
|
||||
|
||||
@@ -10,6 +10,21 @@
|
||||
suffix = "Box/Engine/engine_sm.dmm"
|
||||
name = "Engine SM"
|
||||
|
||||
/datum/map_template/ruin/station/box/engine/onebythree
|
||||
id = "engine_sm_1x3"
|
||||
suffix = "Box/Engine/engine_sm_1x3.dmm"
|
||||
name = "Engine SM 1x3"
|
||||
|
||||
/datum/map_template/ruin/station/box/engine/fivebyfive
|
||||
id = "engine_sm_5x5"
|
||||
suffix = "Box/Engine/engine_sm_5x5.dmm"
|
||||
name = "Engine SM 5x5"
|
||||
|
||||
/datum/map_template/ruin/station/box/engine/threesm
|
||||
id = "engine_sm_3x"
|
||||
suffix = "Box/Engine/engine_sm_3x.dmm"
|
||||
name = "Engine SM 3x"
|
||||
|
||||
/datum/map_template/ruin/station/box/engine/singulo
|
||||
id = "engine_singulo"
|
||||
suffix = "Box/Engine/engine_singulo.dmm"
|
||||
|
||||
@@ -121,3 +121,21 @@
|
||||
mob_trait = TRAIT_EXHIBITIONIST
|
||||
gain_text = "<span class='notice'>You feel like exposing yourself to the world.</span>"
|
||||
lose_text = "<span class='notice'>Indecent exposure doesn't sound as charming to you anymore.</span>"
|
||||
|
||||
/datum/quirk/alcohol_intolerance
|
||||
name = "Alcohol Intolerance"
|
||||
desc = "You take toxin damage from alcohol rather than getting drunk."
|
||||
value = 0
|
||||
mob_trait = TRAIT_NO_ALCOHOL
|
||||
medical_record_text = "Patient's body does not react properly to ethyl alcohol."
|
||||
|
||||
/datum/quirk/alcohol_intolerance/add()
|
||||
var/mob/living/carbon/human/H = quirk_holder
|
||||
var/datum/species/species = H.dna.species
|
||||
species.disliked_food |= ALCOHOL
|
||||
|
||||
/datum/quirk/alcohol_intolerance/remove()
|
||||
var/mob/living/carbon/human/H = quirk_holder
|
||||
if(H)
|
||||
var/datum/species/species = H.dna.species
|
||||
species.disliked_food &= ~ALCOHOL
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
#define CURRENT_LIVING_PLAYERS 1
|
||||
#define CURRENT_LIVING_ANTAGS 2
|
||||
#define CURRENT_DEAD_PLAYERS 3
|
||||
#define CURRENT_OBSERVERS 4
|
||||
|
||||
#define ONLY_RULESET 1
|
||||
#define HIGHLANDER_RULESET 2
|
||||
#define TRAITOR_RULESET 4
|
||||
#define MINOR_RULESET 8
|
||||
|
||||
#define RULESET_STOP_PROCESSING 1
|
||||
|
||||
// -- Injection delays
|
||||
GLOBAL_VAR_INIT(dynamic_latejoin_delay_min, (10 MINUTES))
|
||||
@@ -52,6 +41,8 @@ GLOBAL_LIST_EMPTY(dynamic_forced_roundstart_ruleset)
|
||||
// Forced threat level, setting this to zero or higher forces the roundstart threat to the value.
|
||||
GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
|
||||
GLOBAL_VAR_INIT(dynamic_storyteller_type, null)
|
||||
|
||||
/datum/game_mode/dynamic
|
||||
name = "dynamic mode"
|
||||
config_tag = "dynamic"
|
||||
@@ -60,7 +51,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
announce_text = "Dynamic mode!" // This needs to be changed maybe
|
||||
|
||||
reroll_friendly = FALSE;
|
||||
|
||||
// Current storyteller
|
||||
var/datum/dynamic_storyteller/storyteller = null
|
||||
// Threat logging vars
|
||||
/// The "threat cap", threat shouldn't normally go above this and is used in ruleset calculations
|
||||
var/threat_level = 0
|
||||
@@ -164,6 +156,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
|
||||
dat += "Threat to Spend: <b>[threat]</b> <a href='?src=\ref[src];[HrefToken()];adjustthreat=1'>\[Adjust\]</A> <a href='?src=\ref[src];[HrefToken()];threatlog=1'>\[View Log\]</a><br/>"
|
||||
dat += "<br/>"
|
||||
dat += "Storyteller: <b>[storyteller.name]</b><br/>"
|
||||
dat += "Parameters: centre = [GLOB.dynamic_curve_centre] ; width = [GLOB.dynamic_curve_width].<br/>"
|
||||
dat += "<i>On average, <b>[peaceful_percentage]</b>% of the rounds are more peaceful.</i><br/>"
|
||||
dat += "Forced extended: <a href='?src=\ref[src];[HrefToken()];forced_extended=1'><b>[GLOB.dynamic_forced_extended ? "On" : "Off"]</b></a><br/>"
|
||||
@@ -178,7 +171,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
dat += "[DR.ruletype] - <b>[DR.name]</b><br>"
|
||||
else
|
||||
dat += "none.<br>"
|
||||
dat += "<br>Injection Timers: (<b>[get_injection_chance(TRUE)]%</b> chance)<BR>"
|
||||
dat += "<br>Injection Timers: (<b>[storyteller.get_injection_chance(TRUE)]%</b> chance)<BR>"
|
||||
dat += "Latejoin: [(latejoin_injection_cooldown-world.time)>60*10 ? "[round((latejoin_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(latejoin_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectlate=1'>\[Now!\]</a><BR>"
|
||||
dat += "Midround: [(midround_injection_cooldown-world.time)>60*10 ? "[round((midround_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(midround_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];injectmid=1'>\[Now!\]</a><BR>"
|
||||
dat += "Event: [(event_injection_cooldown-world.time)>60*10 ? "[round((event_injection_cooldown-world.time)/60/10,0.1)] minutes" : "[(event_injection_cooldown-world.time)] seconds"] <a href='?src=\ref[src];[HrefToken()];forceevent=1'>\[Now!\]</a><BR>"
|
||||
@@ -336,6 +329,9 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",peaceful_percentage,"Percent of same-vote rounds that are more peaceful")
|
||||
|
||||
/datum/game_mode/dynamic/can_start()
|
||||
storyteller = new GLOB.dynamic_storyteller_type // this is where all the initialization happens
|
||||
storyteller.on_start()
|
||||
SSblackbox.record_feedback("text","dynamic_storyteller",1,storyteller.name)
|
||||
message_admins("Dynamic mode parameters for the round:")
|
||||
message_admins("Centre is [GLOB.dynamic_curve_centre], Width is [GLOB.dynamic_curve_width], Forced extended is [GLOB.dynamic_forced_extended ? "Enabled" : "Disabled"], No stacking is [GLOB.dynamic_no_stacking ? "Enabled" : "Disabled"].")
|
||||
message_admins("Stacking limit is [GLOB.dynamic_stacking_limit], Classic secret is [GLOB.dynamic_classic_secret ? "Enabled" : "Disabled"], High population limit is [GLOB.dynamic_high_pop_limit].")
|
||||
@@ -345,19 +341,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if(GLOB.dynamic_forced_threat_level >= 0)
|
||||
threat_level = round(GLOB.dynamic_forced_threat_level, 0.1)
|
||||
threat = threat_level
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced by admins)")
|
||||
SSblackbox.record_feedback("tally","dynamic_threat",threat_level,"Threat level (forced)")
|
||||
else
|
||||
generate_threat()
|
||||
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
|
||||
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
|
||||
storyteller.start_injection_cooldowns()
|
||||
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
|
||||
midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
|
||||
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
|
||||
|
||||
log_game("DYNAMIC: Dynamic Mode initialized with a Threat Level of... [threat_level]!")
|
||||
initial_threat_level = threat_level
|
||||
return TRUE
|
||||
@@ -391,7 +380,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
log_game("DYNAMIC: [roundstart_rules.len] rules.")
|
||||
return TRUE
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Roundstart rules considered")
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_rules.len,"Players readied up")
|
||||
SSblackbox.record_feedback("tally","dynamic",roundstart_pop_ready,"Players readied up")
|
||||
if(GLOB.dynamic_forced_roundstart_ruleset.len > 0)
|
||||
rigged_roundstart()
|
||||
else
|
||||
@@ -429,13 +418,7 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (GLOB.dynamic_forced_extended)
|
||||
log_game("DYNAMIC: Starting a round of forced extended.")
|
||||
return TRUE
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/roundstart/rule in roundstart_rules)
|
||||
if (rule.acceptable(roundstart_pop_ready, threat_level) && threat >= rule.cost) // If we got the population and threat required
|
||||
rule.candidates = candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready() && rule.candidates.len > 0)
|
||||
drafted_rules[rule] = rule.weight
|
||||
var/list/drafted_rules = storyteller.roundstart_draft()
|
||||
if(!drafted_rules.len)
|
||||
message_admins("Not enough threat level for roundstart antags!")
|
||||
log_game("DYNAMIC: Not enough threat level for roundstart antags!")
|
||||
@@ -653,7 +636,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
if (rule.persistent)
|
||||
current_rules += rule
|
||||
return TRUE
|
||||
rule.clean_up()
|
||||
stack_trace("The [rule.ruletype] rule \"[rule.name]\" failed to execute.")
|
||||
return FALSE
|
||||
|
||||
@@ -667,62 +649,41 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
current_rules -= rule
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Rulesets finished")
|
||||
SSblackbox.record_feedback("associative","dynamic_rulesets_finished",1,rule.get_blackbox_info())
|
||||
|
||||
|
||||
storyteller.do_process()
|
||||
|
||||
if (midround_injection_cooldown < world.time)
|
||||
if (GLOB.dynamic_forced_extended)
|
||||
return
|
||||
|
||||
// Somehow it managed to trigger midround multiple times so this was moved here.
|
||||
// There is no way this should be able to trigger an injection twice now.
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
|
||||
midround_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max)) + world.time)
|
||||
midround_injection_cooldown = storyteller.get_midround_cooldown() + world.time
|
||||
|
||||
// Time to inject some threat into the round
|
||||
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // Unless the shuttle is gone
|
||||
return
|
||||
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
|
||||
return
|
||||
message_admins("DYNAMIC: Checking for midround injection.")
|
||||
log_game("DYNAMIC: Checking for midround injection.")
|
||||
|
||||
update_playercounts()
|
||||
if (get_injection_chance())
|
||||
if (prob(storyteller.get_injection_chance()))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted midround injections")
|
||||
var/cur_threat_frac = threat/threat_level
|
||||
var/list/drafted_rules = list()
|
||||
var/antag_num = current_players[CURRENT_LIVING_ANTAGS].len
|
||||
for (var/datum/dynamic_ruleset/midround/rule in midround_rules)
|
||||
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
|
||||
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
if(!antag_num)
|
||||
drafted_rules[rule] = round(rule.get_weight() + (rule.cost * cur_threat_frac))
|
||||
else
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
else if(threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
var/list/drafted_rules = storyteller.midround_draft()
|
||||
if (drafted_rules.len > 0)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful midround injections")
|
||||
picking_midround_latejoin_rule(drafted_rules)
|
||||
else
|
||||
midround_injection_cooldown = (midround_injection_cooldown + world.time)/2
|
||||
// get_injection_chance can do things on fail
|
||||
|
||||
if(event_injection_cooldown < world.time)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted event injections")
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
|
||||
event_injection_cooldown = storyteller.get_event_cooldown() + world.time
|
||||
message_admins("DYNAMIC: Doing event injection.")
|
||||
log_game("DYNAMIC: Doing event injection.")
|
||||
update_playercounts()
|
||||
var/list/drafted_rules = list()
|
||||
for(var/datum/dynamic_ruleset/event/rule in events)
|
||||
if(rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
if(rule.ready())
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
else if(threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
var/list/drafted_rules = storyteller.event_draft()
|
||||
if(drafted_rules.len > 0)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful event injections")
|
||||
picking_midround_latejoin_rule(drafted_rules)
|
||||
@@ -748,31 +709,6 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
continue
|
||||
current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
|
||||
|
||||
/// Gets the chance for latejoin and midround injection, the dry_run argument is only used for forced injection.
|
||||
/datum/game_mode/dynamic/proc/get_injection_chance(dry_run = FALSE)
|
||||
if(forced_injection)
|
||||
forced_injection = !dry_run
|
||||
return 100
|
||||
var/chance = 0
|
||||
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
|
||||
var/high_pop_factor = (current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
|
||||
var/max_pop_per_antag = max(5,15 - round(threat_level/10) - round(current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
|
||||
if (!current_players[CURRENT_LIVING_ANTAGS].len)
|
||||
chance += 80 // No antags at all? let's boost those odds!
|
||||
else
|
||||
var/current_pop_per_antag = current_players[CURRENT_LIVING_PLAYERS].len / current_players[CURRENT_LIVING_ANTAGS].len
|
||||
if (current_pop_per_antag > max_pop_per_antag)
|
||||
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
|
||||
else
|
||||
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
|
||||
if (current_players[CURRENT_DEAD_PLAYERS].len > current_players[CURRENT_LIVING_PLAYERS].len)
|
||||
chance -= 30 // More than half the crew died? ew, let's calm down on antags
|
||||
if (threat > 70)
|
||||
chance += 15
|
||||
if (threat < 30)
|
||||
chance -= 15
|
||||
return round(max(0,chance))
|
||||
|
||||
/// Removes type from the list
|
||||
/datum/game_mode/dynamic/proc/remove_from_list(list/type_list, type)
|
||||
for(var/I in type_list)
|
||||
@@ -803,7 +739,8 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
return
|
||||
if(EMERGENCY_ESCAPED_OR_ENDGAMED) // No more rules after the shuttle has left
|
||||
return
|
||||
|
||||
if((world.realtime - SSshuttle.realtimeofstart) > SSshuttle.auto_call) // no rules after shuttle is auto-called
|
||||
return
|
||||
update_playercounts()
|
||||
|
||||
if (forced_latejoin_rule)
|
||||
@@ -814,28 +751,12 @@ GLOBAL_VAR_INIT(dynamic_forced_threat_level, -1)
|
||||
picking_midround_latejoin_rule(list(forced_latejoin_rule), forced = TRUE)
|
||||
forced_latejoin_rule = null
|
||||
|
||||
else if (latejoin_injection_cooldown < world.time && prob(get_injection_chance()))
|
||||
else if (latejoin_injection_cooldown < world.time && prob(storyteller.get_injection_chance()))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Attempted latejoin injections")
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/latejoin/rule in latejoin_rules)
|
||||
if (rule.acceptable(current_players[CURRENT_LIVING_PLAYERS].len, threat_level) && threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
// No stacking : only one round-ender, unless threat level > stacking_limit.
|
||||
if (threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(rule.flags & HIGHLANDER_RULESET && highlander_executed)
|
||||
continue
|
||||
|
||||
rule.candidates = list(newPlayer)
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
drafted_rules[rule] = rule.get_weight()
|
||||
|
||||
var/list/drafted_rules = storyteller.latejoin_draft(newPlayer)
|
||||
if (drafted_rules.len > 0 && picking_midround_latejoin_rule(drafted_rules))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Successful latejoin injections")
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
|
||||
latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max)) + world.time
|
||||
latejoin_injection_cooldown = storyteller.get_latejoin_cooldown() + world.time
|
||||
|
||||
/// Refund threat, but no more than threat_level.
|
||||
/datum/game_mode/dynamic/proc/refund_threat(regain)
|
||||
|
||||
@@ -80,9 +80,13 @@
|
||||
/// Delay for when execute will get called from the time of post_setup (roundstart) or process (midround/latejoin).
|
||||
/// Make sure your ruleset works with execute being called during the game when using this, and that the clean_up proc reverts it properly in case of faliure.
|
||||
var/delay = 0
|
||||
/// List of tags for use in storytellers.
|
||||
var/list/property_weights = list()
|
||||
/// Whether or not recent-round weight values are taken into account for this ruleset.
|
||||
/// Weight reduction uses the same values as secret's recent-round mode weight reduction.
|
||||
var/always_max_weight = FALSE
|
||||
/// Weight reduction by recent-rounds. Saved on new.
|
||||
var/weight_mult = 1
|
||||
|
||||
/datum/dynamic_ruleset/New()
|
||||
..()
|
||||
@@ -96,13 +100,11 @@
|
||||
var/high_population_requirements = CONFIG_GET(keyed_list/dynamic_high_population_requirement)
|
||||
var/list/repeated_mode_adjust = CONFIG_GET(number_list/repeated_mode_adjust)
|
||||
if(config_tag in weights)
|
||||
var/weight_mult = 1
|
||||
if(!always_max_weight && SSpersistence.saved_dynamic_rules.len == 3 && repeated_mode_adjust.len == 3)
|
||||
var/saved_dynamic_rules = SSpersistence.saved_dynamic_rules
|
||||
for(var/i in 1 to 3)
|
||||
if(config_tag in saved_dynamic_rules[i])
|
||||
weight_mult -= (repeated_mode_adjust[i]/100)
|
||||
weight = weights[config_tag] * weight_mult
|
||||
if(config_tag in costs)
|
||||
cost = costs[config_tag]
|
||||
if(config_tag in requirementses)
|
||||
|
||||
@@ -58,6 +58,7 @@
|
||||
cost = 10
|
||||
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
|
||||
requirements = list(70,60,50,50,40,40,40,30,20,15)
|
||||
property_weights = list("story_potential" = 1, "trust" = 1, "chaos" = 1)
|
||||
high_population_requirement = 15
|
||||
|
||||
/datum/dynamic_ruleset/event/pirates/ready(forced = FALSE)
|
||||
@@ -81,6 +82,7 @@
|
||||
cost = 10
|
||||
requirements = list(70,60,50,50,40,40,40,30,20,15)
|
||||
high_population_requirement = 15
|
||||
property_weights = list("chaos" = 1, "valid" = 1)
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
@@ -100,6 +102,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5) // yes, can happen on fake-extended
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("chaos" = 1, "extended" = 2)
|
||||
|
||||
/datum/dynamic_ruleset/event/ventclog/ready()
|
||||
if(mode.threat_level > 30 && mode.threat >= 5 && prob(20))
|
||||
@@ -133,10 +136,11 @@
|
||||
required_enemies = list(1,1,0,0,0,0,0,0,0,0)
|
||||
weight = 4
|
||||
// no repeatable weight decrease. too variable to be unfun multiple times in one round
|
||||
cost = 3
|
||||
cost = 1
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = 1, "extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
//////////////////////////////////////////////
|
||||
@@ -156,6 +160,7 @@
|
||||
repeatable_weight_decrease = 2
|
||||
requirements = list(60,50,40,30,30,30,30,30,30,30)
|
||||
high_population_requirement = 30
|
||||
property_weights = list("extended" = -2)
|
||||
|
||||
/datum/dynamic_ruleset/event/meteor_wave/ready()
|
||||
if(mode.threat_level > 40 && mode.threat >= 25 && prob(20))
|
||||
@@ -190,6 +195,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_flux
|
||||
name = "Anomaly: Hyper-Energetic Flux"
|
||||
@@ -203,6 +209,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_gravitational
|
||||
name = "Anomaly: Gravitational"
|
||||
@@ -214,6 +221,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_pyroclastic
|
||||
name = "Anomaly: Pyroclastic"
|
||||
@@ -227,6 +235,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/anomaly_vortex
|
||||
name = "Anomaly: Vortex"
|
||||
@@ -240,6 +249,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
@@ -259,6 +269,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = -1, "chaos" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/carp_migration
|
||||
name = "Carp Migration"
|
||||
@@ -270,6 +281,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/communications_blackout
|
||||
name = "Communications Blackout"
|
||||
@@ -283,6 +295,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1, "chaos" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/processor_overload
|
||||
name = "Processor Overload"
|
||||
@@ -296,6 +309,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1, "chaos" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/space_dust
|
||||
@@ -310,6 +324,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/major_dust
|
||||
@@ -324,6 +339,7 @@
|
||||
requirements = list(10,10,10,10,10,10,10,10,10,10)
|
||||
high_population_requirement = 10
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/electrical_storm
|
||||
name = "Electrical Storm"
|
||||
@@ -337,6 +353,7 @@
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/event/heart_attack
|
||||
name = "Random Heart Attack"
|
||||
@@ -350,6 +367,7 @@
|
||||
requirements = list(101,101,101,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
repeatable = TRUE
|
||||
property_weights = list("extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/event/radiation_storm
|
||||
@@ -362,3 +380,4 @@
|
||||
required_enemies = list(1,1,1,1,1,1,1,1,1,1)
|
||||
requirements = list(5,5,5,5,5,5,5,5,5,5)
|
||||
high_population_requirement = 5
|
||||
property_weights = list("extended" = 1,"chaos" = 1)
|
||||
|
||||
@@ -69,8 +69,15 @@
|
||||
high_population_requirement = 15
|
||||
repeatable = TRUE
|
||||
flags = TRAITOR_RULESET
|
||||
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/infiltrator/execute()
|
||||
. = ..()
|
||||
for(var/datum/mind/M in assigned)
|
||||
log_admin("[M.name] was made into a traitor by dynamic.")
|
||||
message_admins("[M.name] was made into a traitor by dynamic.")
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// REVOLUTIONARY PROVOCATEUR //
|
||||
@@ -94,6 +101,7 @@
|
||||
requirements = list(101,101,70,40,40,40,40,40,40,40)
|
||||
high_population_requirement = 40
|
||||
flags = HIGHLANDER_RULESET
|
||||
property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1)
|
||||
var/required_heads_of_staff = 3
|
||||
var/finished = FALSE
|
||||
var/datum/team/revolution/revolution
|
||||
@@ -123,6 +131,8 @@
|
||||
revolution.update_objectives()
|
||||
revolution.update_heads()
|
||||
SSshuttle.registerHostileEnvironment(src)
|
||||
log_admin("[M.name] was made into a revolutionary by dynamic.")
|
||||
message_admins("[M.name] was made into a revolutionary by dynamic.")
|
||||
return TRUE
|
||||
else
|
||||
log_game("DYNAMIC: [ruletype] [name] discarded [M.name] from head revolutionary due to ineligibility.")
|
||||
@@ -187,30 +197,31 @@
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// VAMPIRE //
|
||||
// BLOODSUCKERS //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
/datum/dynamic_ruleset/latejoin/vampire
|
||||
name = "vampire"
|
||||
config_tag = "vampire_latejoin"
|
||||
antag_flag = ROLE_VAMPIRE
|
||||
antag_datum = ANTAG_DATUM_VAMPIRE
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain")
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker
|
||||
name = "Bloodsucker Infiltrator"
|
||||
config_tag = "latejoin_bloodsucker"
|
||||
antag_datum = ANTAG_DATUM_BLOODSUCKER
|
||||
antag_flag = ROLE_TRAITOR
|
||||
restricted_roles = list("AI", "Cyborg")
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_candidates = 1
|
||||
weight = 5
|
||||
cost = 15
|
||||
requirements = list(80,70,60,50,40,20,20,15,15,15)
|
||||
weight = 3
|
||||
cost = 10
|
||||
property_weights = list("story_potential" = 2, "extended" = 2, "trust" = -2, "valid" = 1)
|
||||
requirements = list(70,65,60,55,50,45,40,35,30,30)
|
||||
high_population_requirement = 30
|
||||
repeatable = TRUE
|
||||
high_population_requirement = 15
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/vampire/pre_execute()
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
|
||||
var/mob/M = pick(candidates)
|
||||
candidates -= M
|
||||
assigned += M.mind
|
||||
M.mind.restricted_roles = restricted_roles
|
||||
M.mind.special_role = ROLE_VAMPIRE
|
||||
M.mind.special_role = antag_flag
|
||||
if(mode.make_bloodsucker(M.mind))
|
||||
mode.bloodsuckers += M
|
||||
log_admin("[M.name] was made into a bloodsucker by dynamic.")
|
||||
message_admins("[M.name] was made into a bloodsucker by dynamic.")
|
||||
return TRUE
|
||||
*/
|
||||
|
||||
@@ -17,10 +17,10 @@
|
||||
var/list/living_antags = list()
|
||||
var/list/dead_players = list()
|
||||
var/list/list_observers = list()
|
||||
var/list/ghost_eligible = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts
|
||||
weight = 0
|
||||
required_type = /mob/dead/observer
|
||||
/// Whether the ruleset should call generate_ruleset_body or not.
|
||||
var/makeBody = TRUE
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
living_players = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS])
|
||||
living_antags = trim_list(mode.current_players[CURRENT_LIVING_ANTAGS])
|
||||
list_observers = trim_list(mode.current_players[CURRENT_OBSERVERS])
|
||||
var/datum/element/ghost_role_eligibility/eligibility = SSdcs.GetElement(/datum/element/ghost_role_eligibility)
|
||||
ghost_eligible = trim_list(eligibility.get_all_ghost_role_eligible())
|
||||
|
||||
/datum/dynamic_ruleset/midround/proc/trim_list(list/L = list())
|
||||
var/list/trimmed_list = L.Copy()
|
||||
@@ -70,6 +72,25 @@
|
||||
continue
|
||||
return trimmed_list
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/trim_list(list/L = list())
|
||||
var/list/trimmed_list = L.Copy()
|
||||
for(var/mob/M in trimmed_list)
|
||||
if (!M.client) // Are they connected?
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if(!mode.check_age(M.client, minimum_required_age))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if(antag_flag_override)
|
||||
if(!(antag_flag_override in M.client.prefs.be_special) || jobban_isbanned(M.ckey, antag_flag_override))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
else
|
||||
if(!(antag_flag in M.client.prefs.be_special) || jobban_isbanned(M.ckey, antag_flag))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
return trimmed_list
|
||||
|
||||
// You can then for example prompt dead players in execute() to join as strike teams or whatever
|
||||
// Or autotator someone
|
||||
|
||||
@@ -91,11 +112,15 @@
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/ready(forced = FALSE)
|
||||
if (required_candidates > ghost_eligible.len)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/execute()
|
||||
var/list/possible_candidates = list()
|
||||
possible_candidates.Add(dead_players)
|
||||
possible_candidates.Add(list_observers)
|
||||
var/application_successful = send_applications(possible_candidates)
|
||||
var/application_successful = send_applications(ghost_eligible)
|
||||
return assigned.len > 0 && application_successful
|
||||
|
||||
/// This sends a poll to ghosts if they want to be a ghost spawn from a ruleset.
|
||||
@@ -108,8 +133,12 @@
|
||||
|
||||
candidates = pollGhostCandidates("The mode is looking for volunteers to become a [name]", antag_flag, SSticker.mode, antag_flag, poll_time = 300)
|
||||
|
||||
if(!candidates || candidates.len <= required_candidates)
|
||||
if(!candidates || candidates.len < required_candidates)
|
||||
message_admins("The ruleset [name] did not receive enough applications.")
|
||||
if(candidates)
|
||||
message_admins("Only received [candidates.len], needed [required_candidates].")
|
||||
else
|
||||
message_admins("There were no candidates.")
|
||||
log_game("DYNAMIC: The ruleset [name] did not receive enough applications.")
|
||||
return FALSE
|
||||
|
||||
@@ -180,6 +209,7 @@
|
||||
repeatable = TRUE
|
||||
high_population_requirement = 15
|
||||
flags = TRAITOR_RULESET
|
||||
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1)
|
||||
always_max_weight = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/midround/autotraitor/acceptable(population = 0, threat = 0)
|
||||
@@ -214,6 +244,8 @@
|
||||
living_players -= M
|
||||
var/datum/antagonist/traitor/newTraitor = new
|
||||
M.mind.add_antag_datum(newTraitor)
|
||||
log_admin("[M] was made into a traitor by dynamic.")
|
||||
message_admins("[M] was made into a traitor by dynamic.")
|
||||
return TRUE
|
||||
|
||||
|
||||
@@ -237,6 +269,7 @@
|
||||
requirements = list(101,101,70,50,50,50,40,30,30,30)
|
||||
high_population_requirement = 30
|
||||
required_type = /mob/living/silicon/ai
|
||||
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2)
|
||||
var/ion_announce = 33
|
||||
var/removeDontImproveChance = 10
|
||||
|
||||
@@ -261,6 +294,8 @@
|
||||
var/datum/antagonist/traitor/AI = new
|
||||
M.mind.special_role = antag_flag
|
||||
M.mind.add_antag_datum(AI)
|
||||
log_admin("[M] was made into a malf AI by dynamic.")
|
||||
message_admins("[M] was made into a malf AI by dynamic.")
|
||||
if(prob(ion_announce))
|
||||
priority_announce("Ion storm detected near the station. Please check all AI-controlled equipment for errors.", "Anomaly Alert", "ionstorm")
|
||||
if(prob(removeDontImproveChance))
|
||||
@@ -289,12 +324,10 @@
|
||||
requirements = list(90,90,70,50,50,50,50,40,30,30)
|
||||
high_population_requirement = 30
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2)
|
||||
var/datum/mind/wizard
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/wizard/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
if(GLOB.wizardstart.len == 0)
|
||||
log_admin("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.")
|
||||
message_admins("Cannot accept Wizard ruleset. Couldn't find any wizard spawn points.")
|
||||
@@ -337,6 +370,7 @@
|
||||
cost = 35
|
||||
requirements = list(90,90,90,80,70,60,50,40,40,40)
|
||||
high_population_requirement = 40
|
||||
property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/operative_cap = list(2,2,3,3,4,5,5,5,5,5)
|
||||
var/datum/team/nuclear/nuke_team
|
||||
flags = HIGHLANDER_RULESET
|
||||
@@ -354,12 +388,6 @@
|
||||
required_candidates = operative_cap[indice_pop]
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/nuclear/finish_setup(mob/new_character, index)
|
||||
new_character.mind.special_role = "Nuclear Operative"
|
||||
new_character.mind.assigned_role = "Nuclear Operative"
|
||||
@@ -390,12 +418,7 @@
|
||||
requirements = list(101,101,101,80,60,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
repeatable = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
property_weights = list("story_potential" = -1, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/blob/generate_ruleset_body(mob/applicant)
|
||||
var/body = applicant.become_overmind()
|
||||
@@ -421,14 +444,9 @@
|
||||
high_population_requirement = 50
|
||||
repeatable_weight_decrease = 2
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = -1, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/list/vents = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/xenomorph/execute()
|
||||
// 50% chance of being incremented by one
|
||||
required_candidates += prob(50)
|
||||
@@ -476,6 +494,7 @@
|
||||
high_population_requirement = 50
|
||||
repeatable_weight_decrease = 2
|
||||
repeatable = TRUE
|
||||
property_weights = list("story_potential" = 1, "trust" = 1, "extended" = 1, "valid" = 2, "integrity" = 2)
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/nightmare/execute()
|
||||
@@ -521,14 +540,9 @@
|
||||
weight = 4
|
||||
cost = 5
|
||||
requirements = list(30,30,20,20,15,10,10,10,10,5) // yes, it can even happen in "extended"!
|
||||
property_weights = list("story_potential" = 1, "extended" = 1, "valid" = -2)
|
||||
high_population_requirement = 5
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/ready(forced = FALSE)
|
||||
if (required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/sentient_disease/generate_ruleset_body(mob/applicant)
|
||||
var/mob/camera/disease/virus = new /mob/camera/disease(SSmapping.get_station_center())
|
||||
applicant.transfer_ckey(virus, FALSE)
|
||||
@@ -555,17 +569,18 @@
|
||||
cost = 5
|
||||
requirements = list(30,30,30,30,20,15,15,15,15,15)
|
||||
high_population_requirement = 15
|
||||
property_weights = list("story_potential" = -2, "extended" = -1)
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/revenant/acceptable(population = 0,threat = 0)
|
||||
var/deadMobs = 0
|
||||
for(var/mob/M in GLOB.dead_mob_list)
|
||||
deadMobs++
|
||||
if(deadMobs < REVENANT_SPAWN_THRESHOLD)
|
||||
return FALSE
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/revenant/ready(forced = FALSE)
|
||||
for(var/mob/living/L in GLOB.dead_mob_list) //look for any dead bodies
|
||||
var/turf/T = get_turf(L)
|
||||
if(T && is_station_level(T.z))
|
||||
@@ -607,13 +622,11 @@
|
||||
weight = 4
|
||||
cost = 15
|
||||
requirements = list(101,101,101,90,80,70,60,50,40,30)
|
||||
property_weights = list("story_potential" = -2, "extended" = -2, "integrity" = 2, "valid" = 2, "trust" = 2)
|
||||
high_population_requirement = 30
|
||||
var/list/spawn_locs = list()
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/slaughter_demon/ready(forced = FALSE)
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
|
||||
if(isturf(L.loc))
|
||||
spawn_locs += L.loc
|
||||
@@ -659,13 +672,11 @@
|
||||
blocking_rules = list(/datum/dynamic_ruleset/roundstart/nuclear,/datum/dynamic_ruleset/midround/from_ghosts/nuclear)
|
||||
high_population_requirement = 15
|
||||
var/datum/team/abductor_team/team
|
||||
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 1, "trust" = -1, "chaos" = 2)
|
||||
repeatable_weight_decrease = 4
|
||||
repeatable = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/abductors/ready(forced = FALSE)
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
team = new /datum/team/abductor_team
|
||||
if(team.team_number > ABDUCTOR_MAX_TEAMS)
|
||||
return FALSE
|
||||
@@ -699,13 +710,11 @@
|
||||
cost = 15
|
||||
requirements = list(101,101,101,90,80,70,60,50,40,30)
|
||||
high_population_requirement = 30
|
||||
property_weights = list("story_potential" = 1, "extended" = -2, "valid" = 2)
|
||||
var/list/spawn_locs = list()
|
||||
var/spawn_loc
|
||||
|
||||
/datum/dynamic_ruleset/midround/from_ghosts/ninja/ready(forced = FALSE)
|
||||
if(required_candidates > (dead_players.len + list_observers.len))
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough ghosts")
|
||||
return FALSE
|
||||
if(!spawn_loc)
|
||||
var/list/spawn_locs = list()
|
||||
for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
|
||||
@@ -745,31 +754,3 @@
|
||||
|
||||
#undef ABDUCTOR_MAX_TEAMS
|
||||
#undef REVENANT_SPAWN_THRESHOLD
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
// BLOODSUCKERS //
|
||||
// //
|
||||
//////////////////////////////////////////////
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker
|
||||
name = "Bloodsucker Infiltrator"
|
||||
config_tag = "latejoin_bloodsucker"
|
||||
antag_datum = ANTAG_DATUM_BLOODSUCKER
|
||||
antag_flag = ROLE_TRAITOR
|
||||
restricted_roles = list("AI", "Cyborg")
|
||||
protected_roles = list("Security Officer", "Warden", "Detective", "Head of Security", "Captain", "Head of Personnel", "Chief Engineer", "Chief Medical Officer", "Research Director", "Quartermaster")
|
||||
required_candidates = 1
|
||||
weight = 3
|
||||
cost = 10
|
||||
requirements = list(90,80,70,60,55,50,45,40,35,30)
|
||||
high_population_requirement = 30
|
||||
repeatable = TRUE
|
||||
|
||||
/datum/dynamic_ruleset/latejoin/bloodsucker/execute()
|
||||
var/mob/M = pick(candidates)
|
||||
assigned += M.mind
|
||||
M.mind.special_role = antag_flag
|
||||
if(mode.make_bloodsucker(M.mind))
|
||||
mode.bloodsuckers += M
|
||||
return TRUE
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
requirements = list(50,50,50,50,50,50,50,50,50,50)
|
||||
high_population_requirement = 40
|
||||
antag_cap = list(1,1,1,1,2,2,2,2,3,3)
|
||||
property_weights = list("story_potential" = 2, "trust" = -1, "extended" = 1, "valid" = 1)
|
||||
always_max_weight = TRUE
|
||||
var/autotraitor_cooldown = 450 // 15 minutes (ticks once per 2 sec)
|
||||
|
||||
@@ -61,6 +62,7 @@
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
antag_cap = list(2,2,2,2,2,2,2,2,2,2) // Can pick 3 per team, but rare enough it doesn't matter.
|
||||
property_weights = list("story_potential" = 1, "trust" = -1, "extended" = 1, "valid" = 1)
|
||||
var/list/datum/team/brother_team/pre_brother_teams = list()
|
||||
var/const/min_team_size = 2
|
||||
|
||||
@@ -108,6 +110,7 @@
|
||||
cost = 15
|
||||
scaling_cost = 15
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
property_weights = list("trust" = -2, "valid" = 2)
|
||||
high_population_requirement = 10
|
||||
antag_cap = list(1,1,1,1,1,2,2,2,2,3)
|
||||
var/team_mode_probability = 30
|
||||
@@ -160,6 +163,7 @@
|
||||
cost = 30
|
||||
requirements = list(101,101,101,60,50,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
property_weights = list("story_potential" = 2, "trust" = 1, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/list/roundstart_wizards = list()
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/wizard/acceptable(population=0, threat=0)
|
||||
@@ -222,6 +226,7 @@
|
||||
weight = 3
|
||||
cost = 30
|
||||
requirements = list(101,101,101,80,70,60,50,50,50,50)
|
||||
property_weights = list("story_potential" = -1, "trust" = -1, "chaos" = 1, "conversion" = 1, "extended" = -2, "valid" = 2)
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
antag_cap = list(2,2,2,3,3,4,4,4,4,4)
|
||||
@@ -283,6 +288,7 @@
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
antag_cap = list(1,1,2,3,4,5,5,5,5,5)
|
||||
property_weights = list("story_potential" = 2, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
|
||||
var/datum/team/nuclear/nuke_team
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/nuclear/ready(forced = FALSE)
|
||||
@@ -373,6 +379,7 @@
|
||||
flags = HIGHLANDER_RULESET
|
||||
// I give up, just there should be enough heads with 35 players...
|
||||
minimum_players = 35
|
||||
property_weights = list("trust" = -2, "chaos" = 2, "extended" = -2, "valid" = 2, "conversion" = 1)
|
||||
var/datum/team/revolution/revolution
|
||||
var/finished = FALSE
|
||||
|
||||
@@ -490,6 +497,7 @@
|
||||
weight = 3
|
||||
cost = 0
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
property_weights = list("extended" = 2)
|
||||
high_population_requirement = 101
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/extended/pre_execute()
|
||||
@@ -517,6 +525,7 @@
|
||||
high_population_requirement = 50
|
||||
flags = HIGHLANDER_RULESET
|
||||
antag_cap = list(2,3,3,4,4,4,4,4,4,4)
|
||||
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "conversion" = 1, "valid" = 2)
|
||||
var/ark_time
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/clockcult/pre_execute()
|
||||
@@ -616,6 +625,8 @@
|
||||
antag_leader_datum = /datum/antagonist/nukeop/leader/clownop
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
property_weights = list("trust" = 2, "chaos" = 2, "extended" = -2, "story_potential" = 2, "valid" = 2)
|
||||
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/nuclear/clown_ops/pre_execute()
|
||||
. = ..()
|
||||
@@ -647,6 +658,7 @@
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
antag_cap = list(1,1,1,2,2,2,3,3,3,4)
|
||||
property_weights = list("extended" = 1)
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/devil/pre_execute()
|
||||
var/num_devils = antag_cap[indice_pop]
|
||||
@@ -698,6 +710,7 @@
|
||||
cost = 0
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
property_weights = list("extended" = -2, "chaos" = 2, "conversion" = 1, "valid" = 2)
|
||||
var/players_per_carrier = 30
|
||||
var/monkeys_to_win = 1
|
||||
var/escaped_monkeys = 0
|
||||
@@ -759,6 +772,7 @@
|
||||
cost = 0
|
||||
requirements = list(101,101,101,101,101,101,101,101,101,101)
|
||||
high_population_requirement = 101
|
||||
property_weights = list("extended" = -2, "chaos" = 2, "trust" = 2)
|
||||
var/meteordelay = 2000
|
||||
var/nometeors = 0
|
||||
var/rampupdelta = 5
|
||||
@@ -799,7 +813,8 @@
|
||||
weight = 2
|
||||
cost = 15
|
||||
scaling_cost = 10
|
||||
requirements = list(90,80,70,60,50,50,50,50,50,50)
|
||||
property_weights = list("story_potential" = 1, "extended" = 1, "trust" = -2, "valid" = 1)
|
||||
requirements = list(70,65,60,55,50,50,50,50,50,50)
|
||||
high_population_requirement = 50
|
||||
antag_cap = list(1,1,1,1,1,2,2,2,2,2)
|
||||
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
/datum/dynamic_storyteller
|
||||
var/name = "none"
|
||||
var/desc = "A coder's idiocy."
|
||||
var/list/property_weights = list()
|
||||
var/curve_centre = 0
|
||||
var/curve_width = 1.8
|
||||
var/forced_threat_level = -1
|
||||
var/flags = 0
|
||||
var/weight = 3 // how many rounds need to have been recently played for this storyteller to be left out of the vote
|
||||
var/datum/game_mode/dynamic/mode = null
|
||||
|
||||
/**
|
||||
Property weights are:
|
||||
"story_potential" -- essentially how many different ways the antag can be played.
|
||||
"trust" -- How much it makes the crew trust each other. Negative values means they're suspicious. Team antags are like this.
|
||||
"chaos" -- How chaotic it makes the round. Has some overlap with "valid" and somewhat contradicts "extended".
|
||||
"valid" -- How likely the non-antag-enemy crew are to get involved, e.g. nukies encouraging the warden to
|
||||
let everyone into the armory, wizard moving around and being a nuisance, nightmare busting lights.
|
||||
"extended" -- How much the antag is conducive to a long round. Nukies and cults are bad for this; Wizard is less bad; and so on.
|
||||
"conversion" -- Basically a bool. Conversion antags, well, convert. It's its own class for a good reason.
|
||||
*/
|
||||
|
||||
/datum/dynamic_storyteller/New()
|
||||
..()
|
||||
if (istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
mode = SSticker.mode
|
||||
GLOB.dynamic_curve_centre = curve_centre
|
||||
GLOB.dynamic_curve_width = curve_width
|
||||
GLOB.dynamic_forced_threat_level = forced_threat_level
|
||||
|
||||
/datum/dynamic_storyteller/proc/start_injection_cooldowns()
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_latejoin_delay_max + GLOB.dynamic_first_latejoin_delay_min)
|
||||
mode.latejoin_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_first_latejoin_delay_min, GLOB.dynamic_first_latejoin_delay_max)) + world.time
|
||||
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_first_midround_delay_min + GLOB.dynamic_first_midround_delay_max)
|
||||
mode.midround_injection_cooldown = round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_first_midround_delay_min, GLOB.dynamic_first_midround_delay_max)) + world.time
|
||||
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
mode.event_injection_cooldown = (round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max)) + world.time)
|
||||
|
||||
/datum/dynamic_storyteller/proc/do_process()
|
||||
return
|
||||
|
||||
/datum/dynamic_storyteller/proc/on_start()
|
||||
return
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_midround_cooldown()
|
||||
var/midround_injection_cooldown_middle = 0.5*(GLOB.dynamic_midround_delay_max + GLOB.dynamic_midround_delay_min)
|
||||
return round(CLAMP(EXP_DISTRIBUTION(midround_injection_cooldown_middle), GLOB.dynamic_midround_delay_min, GLOB.dynamic_midround_delay_max))
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_event_cooldown()
|
||||
var/event_injection_cooldown_middle = 0.5*(GLOB.dynamic_event_delay_max + GLOB.dynamic_event_delay_min)
|
||||
return round(CLAMP(EXP_DISTRIBUTION(event_injection_cooldown_middle), GLOB.dynamic_event_delay_min, GLOB.dynamic_event_delay_max))
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_latejoin_cooldown()
|
||||
var/latejoin_injection_cooldown_middle = 0.5*(GLOB.dynamic_latejoin_delay_max + GLOB.dynamic_latejoin_delay_min)
|
||||
return round(CLAMP(EXP_DISTRIBUTION(latejoin_injection_cooldown_middle), GLOB.dynamic_latejoin_delay_min, GLOB.dynamic_latejoin_delay_max))
|
||||
|
||||
/datum/dynamic_storyteller/proc/get_injection_chance(dry_run = FALSE)
|
||||
if(mode.forced_injection)
|
||||
mode.forced_injection = !dry_run
|
||||
return 100
|
||||
var/chance = 0
|
||||
// If the high pop override is in effect, we reduce the impact of population on the antag injection chance
|
||||
var/high_pop_factor = (mode.current_players[CURRENT_LIVING_PLAYERS].len >= GLOB.dynamic_high_pop_limit)
|
||||
var/max_pop_per_antag = max(5,15 - round(mode.threat_level/10) - round(mode.current_players[CURRENT_LIVING_PLAYERS].len/(high_pop_factor ? 10 : 5)))
|
||||
if (!mode.current_players[CURRENT_LIVING_ANTAGS].len)
|
||||
chance += 80 // No antags at all? let's boost those odds!
|
||||
else
|
||||
var/current_pop_per_antag = mode.current_players[CURRENT_LIVING_PLAYERS].len / mode.current_players[CURRENT_LIVING_ANTAGS].len
|
||||
if (current_pop_per_antag > max_pop_per_antag)
|
||||
chance += min(50, 25+10*(current_pop_per_antag-max_pop_per_antag))
|
||||
else
|
||||
chance += 25-10*(max_pop_per_antag-current_pop_per_antag)
|
||||
if (mode.current_players[CURRENT_DEAD_PLAYERS].len > mode.current_players[CURRENT_LIVING_PLAYERS].len)
|
||||
chance -= 30 // More than half the crew died? ew, let's calm down on antags
|
||||
if (mode.threat > 70)
|
||||
chance += 15
|
||||
if (mode.threat < 30)
|
||||
chance -= 15
|
||||
return round(max(0,chance))
|
||||
|
||||
/datum/dynamic_storyteller/proc/roundstart_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/roundstart/rule in mode.roundstart_rules)
|
||||
if (rule.acceptable(mode.roundstart_pop_ready, mode.threat_level) && mode.threat >= rule.cost) // If we got the population and threat required
|
||||
rule.candidates = mode.candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready() && rule.candidates.len > 0)
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights) // just treat it as 0 if it's not in there
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/proc/midround_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/midround/rule in mode.midround_rules)
|
||||
// if there are antags OR the rule is an antag rule, antag_acceptable will be true.
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights)
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
else if(mode.threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/proc/latejoin_draft(mob/living/carbon/human/newPlayer)
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/latejoin/rule in mode.latejoin_rules)
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
|
||||
// Classic secret : only autotraitor/minor roles
|
||||
if (GLOB.dynamic_classic_secret && !((rule.flags & TRAITOR_RULESET) || (rule.flags & MINOR_RULESET)))
|
||||
continue
|
||||
// No stacking : only one round-ender, unless threat level > stacking_limit.
|
||||
if (mode.threat_level > GLOB.dynamic_stacking_limit && GLOB.dynamic_no_stacking)
|
||||
if(rule.flags & HIGHLANDER_RULESET && mode.highlander_executed)
|
||||
continue
|
||||
|
||||
rule.candidates = list(newPlayer)
|
||||
rule.trim_candidates()
|
||||
if (rule.ready())
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights)
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
else if(mode.threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
return drafted_rules
|
||||
|
||||
/datum/dynamic_storyteller/proc/event_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for(var/datum/dynamic_ruleset/event/rule in mode.events)
|
||||
if(rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level) && mode.threat >= rule.cost)
|
||||
if(rule.ready())
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights)
|
||||
property_weight += rule.property_weights[property] * property_weights[property]
|
||||
drafted_rules[rule] = (rule.get_weight() + property_weight)*rule.weight_mult
|
||||
else if(mode.threat < rule.cost)
|
||||
SSblackbox.record_feedback("tally","dynamic",1,"Times rulesets rejected due to not enough threat to spend")
|
||||
return drafted_rules
|
||||
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga
|
||||
name = "Chaotic"
|
||||
curve_centre = 10
|
||||
desc = "Chaos: high. Variation: high. Likely antags: clock cult, revs, wizard."
|
||||
property_weights = list("extended" = -1, "chaos" = 10)
|
||||
weight = 2
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
var/refund_cooldown
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/get_midround_cooldown()
|
||||
return ..() / 4
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/get_latejoin_cooldown()
|
||||
return ..() / 4
|
||||
|
||||
/datum/dynamic_storyteller/cowabunga/do_process()
|
||||
if(refund_cooldown < world.time)
|
||||
mode.refund_threat(10)
|
||||
mode.log_threat("Cowabunga it is. Refunded 10 threat. Threat is now [mode.threat].")
|
||||
refund_cooldown = world.time + 300 SECONDS
|
||||
|
||||
/datum/dynamic_storyteller/team
|
||||
name = "Teamwork"
|
||||
desc = "Chaos: high. Variation: low. Likely antags: nukies, clockwork cult, wizard, blob, xenomorph."
|
||||
curve_centre = 2
|
||||
curve_width = 1.5
|
||||
weight = 2
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
property_weights = list("valid" = 3, "trust" = 5)
|
||||
|
||||
/datum/dynamic_storyteller/team/get_injection_chance(dry_run = FALSE)
|
||||
return (mode.current_players[CURRENT_LIVING_ANTAGS].len ? 0 : ..())
|
||||
|
||||
/datum/dynamic_storyteller/conversion
|
||||
name = "Conversion"
|
||||
desc = "Chaos: high. Variation: medium. Likely antags: cults, bloodsuckers, revs."
|
||||
curve_centre = 3
|
||||
curve_width = 1
|
||||
weight = 2
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
property_weights = list("valid" = 1, "conversion" = 20)
|
||||
|
||||
/datum/dynamic_storyteller/classic
|
||||
name = "Random"
|
||||
desc = "Chaos: varies. Variation: highest. No special weights attached."
|
||||
weight = 6
|
||||
curve_width = 4
|
||||
|
||||
/datum/dynamic_storyteller/memes
|
||||
name = "Story"
|
||||
desc = "Chaos: varies. Variation: high. Likely antags: abductors, nukies, wizard, traitor."
|
||||
curve_width = 4
|
||||
property_weights = list("story_potential" = 10)
|
||||
|
||||
/datum/dynamic_storyteller/suspicion
|
||||
name = "Intrigue"
|
||||
desc = "Chaos: low. Variation: high. Likely antags: traitor, bloodsucker. Rare: revs, blood cult."
|
||||
curve_width = 4
|
||||
property_weights = list("trust" = -5)
|
||||
|
||||
/datum/dynamic_storyteller/liteextended
|
||||
name = "Calm"
|
||||
desc = "Chaos: low. Variation: medium. Likely antags: bloodsuckers, traitors, sentient disease, revenant."
|
||||
curve_centre = -5
|
||||
curve_width = 0.5
|
||||
flags = NO_ASSASSIN
|
||||
weight = 2
|
||||
property_weights = list("extended" = 1, "chaos" = -1, "valid" = -1, "story_potential" = 1, "conversion" = -10)
|
||||
|
||||
/datum/dynamic_storyteller/liteextended/get_injection_chance(dry_run = FALSE)
|
||||
return ..()/2
|
||||
|
||||
/datum/dynamic_storyteller/extended
|
||||
name = "Extended"
|
||||
desc = "Chaos: none. Variation: none. Likely antags: none."
|
||||
curve_centre = -20
|
||||
weight = 2
|
||||
curve_width = 0.5
|
||||
|
||||
/datum/dynamic_storyteller/extended/on_start()
|
||||
GLOB.dynamic_forced_extended = TRUE
|
||||
@@ -14,7 +14,7 @@
|
||||
circuit = /obj/item/circuitboard/machine/sleeper
|
||||
req_access = list(ACCESS_CMO) //Used for reagent deletion and addition of non medicines
|
||||
var/efficiency = 1
|
||||
var/min_health = -25
|
||||
var/min_health = 30
|
||||
var/list/available_chems
|
||||
var/controls_inside = FALSE
|
||||
var/list/possible_chems = list(
|
||||
|
||||
@@ -491,4 +491,4 @@
|
||||
|
||||
//Attacks/effects.
|
||||
/obj/machinery/cryopod/blob_act()
|
||||
return //Sorta gamey, but we don't really want these to be destroyed.
|
||||
return //Sorta gamey, but we don't really want these to be destroyed.
|
||||
|
||||
@@ -7,26 +7,51 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
|
||||
icon_state = "tdoppler"
|
||||
density = TRUE
|
||||
var/integrated = FALSE
|
||||
var/list_limit = 100
|
||||
var/cooldown = 10
|
||||
var/next_announce = 0
|
||||
var/max_dist = 150
|
||||
verb_say = "states coldly"
|
||||
var/list/message_log = list()
|
||||
|
||||
/obj/machinery/doppler_array/Initialize()
|
||||
. = ..()
|
||||
GLOB.doppler_arrays += src
|
||||
|
||||
/obj/machinery/doppler_array/ComponentInitialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/simple_rotation,ROTATION_ALTCLICK | ROTATION_CLOCKWISE,null,null,CALLBACK(src,.proc/rot_message))
|
||||
|
||||
/obj/machinery/doppler_array/Destroy()
|
||||
GLOB.doppler_arrays -= src
|
||||
return ..()
|
||||
|
||||
/obj/machinery/doppler_array/examine(mob/user)
|
||||
/obj/machinery/doppler_array/ui_interact(mob/user)
|
||||
. = ..()
|
||||
. += "<span class='notice'>Its dish is facing to the [dir2text(dir)].</span>"
|
||||
if(stat)
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/doppler_array/process()
|
||||
return PROCESS_KILL
|
||||
var/list/dat = list()
|
||||
for(var/i in 1 to LAZYLEN(message_log))
|
||||
dat += "Log recording #[i]: [message_log[i]]<br/><br>"
|
||||
dat += "<A href='?src=[REF(src)];delete_log=1'>Delete logs</A><br>"
|
||||
dat += "<hr>"
|
||||
dat += "<A href='?src=[REF(src)];refresh=1'>(Refresh)</A><br>"
|
||||
dat += "</body></html>"
|
||||
var/datum/browser/popup = new(user, "computer", name, 400, 500)
|
||||
popup.set_content(dat.Join(" "))
|
||||
popup.open()
|
||||
|
||||
/obj/machinery/doppler_array/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
if(href_list["delete_log"])
|
||||
LAZYCLEARLIST(message_log)
|
||||
if(href_list["refresh"])
|
||||
updateUsrDialog()
|
||||
|
||||
updateUsrDialog()
|
||||
return
|
||||
|
||||
/obj/machinery/doppler_array/attackby(obj/item/I, mob/user, params)
|
||||
if(istype(I, /obj/item/wrench))
|
||||
@@ -46,15 +71,18 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
|
||||
to_chat(user, "<span class='notice'>You adjust [src]'s dish to face to the [dir2text(dir)].</span>")
|
||||
playsound(src, 'sound/items/screwdriver2.ogg', 50, 1)
|
||||
|
||||
/obj/machinery/doppler_array/proc/sense_explosion(turf/epicenter,devastation_range,heavy_impact_range,light_impact_range,
|
||||
took,orig_dev_range,orig_heavy_range,orig_light_range)
|
||||
/obj/machinery/doppler_array/proc/sense_explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range,
|
||||
took, orig_dev_range, orig_heavy_range, orig_light_range)
|
||||
if(stat & NOPOWER)
|
||||
return FALSE
|
||||
var/turf/zone = get_turf(src)
|
||||
|
||||
if(zone.z != epicenter.z)
|
||||
return FALSE
|
||||
|
||||
if(next_announce > world.time)
|
||||
return FALSE
|
||||
next_announce = world.time + cooldown
|
||||
|
||||
var/distance = get_dist(epicenter, zone)
|
||||
var/direct = get_dir(zone, epicenter)
|
||||
|
||||
@@ -80,8 +108,19 @@ GLOBAL_LIST_EMPTY(doppler_arrays)
|
||||
else
|
||||
for(var/message in messages)
|
||||
say(message)
|
||||
if(LAZYLEN(message_log) > list_limit)
|
||||
say("Storage buffer is full! Clearing buffers...")
|
||||
LAZYCLEARLIST(message_log)
|
||||
LAZYADD(message_log, messages.Join(" "))
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/doppler_array/examine(mob/user)
|
||||
. = ..()
|
||||
. += "<span class='notice'>Its dish is facing to the [dir2text(dir)].</span>"
|
||||
|
||||
/obj/machinery/doppler_array/process()
|
||||
return PROCESS_KILL
|
||||
|
||||
/obj/machinery/doppler_array/power_change()
|
||||
if(stat & BROKEN)
|
||||
icon_state = "[initial(icon_state)]-broken"
|
||||
|
||||
@@ -256,7 +256,6 @@
|
||||
return
|
||||
return ..()
|
||||
|
||||
|
||||
/obj/machinery/firealarm/take_damage(damage_amount, damage_type = BRUTE, damage_flag = 0, sound_effect = 1, attack_dir)
|
||||
. = ..()
|
||||
if(.) //damage received
|
||||
|
||||
@@ -500,7 +500,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
else
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf)
|
||||
/obj/machinery/holopad/proc/move_hologram(mob/living/user, turf/new_turf, direction)
|
||||
if(LAZYLEN(masters) && masters[user])
|
||||
var/obj/effect/overlay/holo_pad_hologram/holo = masters[user]
|
||||
var/transfered = FALSE
|
||||
@@ -512,6 +512,8 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
|
||||
transfered = TRUE
|
||||
//All is good.
|
||||
holo.forceMove(new_turf)
|
||||
if(direction)
|
||||
holo.setDir(direction)
|
||||
if(!transfered)
|
||||
update_holoray(user,new_turf)
|
||||
return TRUE
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
|
||||
//Wrapper procs that handle sanity and user feedback
|
||||
/atom/movable/proc/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE)
|
||||
if(!in_range(user, src) || !isturf(user.loc) || user.incapacitated() || M.anchored)
|
||||
if(!in_range(user, src) || !isturf(user.loc) || user.incapacitated() || M.anchored || !user.can_buckle_others(M, src))
|
||||
return FALSE
|
||||
|
||||
add_fingerprint(user)
|
||||
|
||||
@@ -260,9 +260,8 @@
|
||||
var/position = vending_names_paths.Find(build_path)
|
||||
position = (position == vending_names_paths.len) ? 1 : (position + 1)
|
||||
var/typepath = vending_names_paths[position]
|
||||
|
||||
to_chat(user, "<span class='notice'>You set the board to \"[vending_names_paths[typepath]]\".</span>")
|
||||
set_type(typepath)
|
||||
to_chat(user, "<span class='notice'>You set the board to \"[vending_names_paths[typepath]]\".</span>")
|
||||
else
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -700,10 +700,10 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
U << browse(null, "window=pda")
|
||||
return
|
||||
|
||||
/obj/item/pda/proc/remove_id()
|
||||
if(issilicon(usr) || !usr.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
||||
/obj/item/pda/proc/remove_id(mob/user)
|
||||
if(issilicon(user) || !user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
||||
return
|
||||
do_remove_id(usr)
|
||||
do_remove_id(user)
|
||||
|
||||
/obj/item/pda/proc/do_remove_id(mob/user)
|
||||
if(!id)
|
||||
@@ -827,23 +827,23 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
/obj/item/pda/proc/create_message(mob/living/U, obj/item/pda/P)
|
||||
send_message(U,list(P))
|
||||
|
||||
/obj/item/pda/AltClick()
|
||||
/obj/item/pda/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(id)
|
||||
remove_id()
|
||||
remove_id(user)
|
||||
playsound(src, 'sound/machines/terminal_eject_disc.ogg', 50, 1)
|
||||
else
|
||||
remove_pen()
|
||||
remove_pen(user)
|
||||
playsound(src, 'sound/machines/button4.ogg', 50, 1)
|
||||
return TRUE
|
||||
|
||||
/obj/item/pda/CtrlClick()
|
||||
/obj/item/pda/CtrlClick(mob/user)
|
||||
..()
|
||||
|
||||
if(isturf(loc)) //stops the user from dragging the PDA by ctrl-clicking it.
|
||||
return
|
||||
|
||||
remove_pen()
|
||||
remove_pen(user)
|
||||
|
||||
/obj/item/pda/verb/verb_toggle_light()
|
||||
set category = "Object"
|
||||
@@ -857,7 +857,7 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
set src in usr
|
||||
|
||||
if(id)
|
||||
remove_id()
|
||||
remove_id(usr)
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>This PDA does not have an ID in it!</span>")
|
||||
|
||||
@@ -896,7 +896,7 @@ GLOBAL_LIST_EMPTY(PDAs)
|
||||
/obj/item/pda/proc/id_check(mob/user, obj/item/card/id/I)
|
||||
if(!I)
|
||||
if(id && (src in user.contents))
|
||||
remove_id()
|
||||
remove_id(user)
|
||||
return TRUE
|
||||
else
|
||||
var/obj/item/card/id/C = user.get_active_held_item()
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
slot_flags = ITEM_SLOT_BELT
|
||||
var/mob/living/silicon/pai/pai
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF | INDESTRUCTIBLE
|
||||
resistance_flags = FIRE_PROOF | ACID_PROOF
|
||||
max_integrity = 200
|
||||
|
||||
/obj/item/paicard/suicide_act(mob/living/user)
|
||||
user.visible_message("<span class='suicide'>[user] is staring sadly at [src]! [user.p_they()] can't keep living without real human intimacy!</span>")
|
||||
@@ -45,6 +46,8 @@
|
||||
dat += "<b>Radio Uplink</b><br>"
|
||||
dat += "Transmit: <A href='byond://?src=[REF(src)];wires=[WIRE_TX]'>[(pai.radio.wires.is_cut(WIRE_TX)) ? "Disabled" : "Enabled"]</A><br>"
|
||||
dat += "Receive: <A href='byond://?src=[REF(src)];wires=[WIRE_RX]'>[(pai.radio.wires.is_cut(WIRE_RX)) ? "Disabled" : "Enabled"]</A><br>"
|
||||
if(pai.radio_short)
|
||||
dat += "<font color='red'>Reset radio short: <a href='byond://?src=[REF(src)];reset_radio_short=1'>\[RESET\]</a><br>"
|
||||
else
|
||||
dat += "<b>Radio Uplink</b><br>"
|
||||
dat += "<font color=red><i>Radio firmware not loaded. Please install a pAI personality to load firmware.</i></font><br>"
|
||||
@@ -82,7 +85,6 @@
|
||||
pai.master = M.real_name
|
||||
pai.master_dna = M.dna.unique_enzymes
|
||||
to_chat(pai, "<span class='notice'>You have been bound to a new master.</span>")
|
||||
pai.emittersemicd = FALSE
|
||||
if(href_list["wipe"])
|
||||
var/confirm = input("Are you CERTAIN you wish to delete the current personality? This action cannot be undone.", "Personality Wipe") in list("Yes", "No")
|
||||
if(confirm == "Yes")
|
||||
@@ -96,6 +98,8 @@
|
||||
var/wire = text2num(href_list["wires"])
|
||||
if(pai.radio)
|
||||
pai.radio.wires.cut(wire)
|
||||
if(href_list["reset_radio_short"])
|
||||
pai.unshort_radio()
|
||||
if(href_list["setlaws"])
|
||||
var/newlaws = copytext(sanitize(input("Enter any additional directives you would like your pAI personality to follow. Note that these directives will not override the personality's allegiance to its imprinted master. Conflicting directives will be ignored.", "pAI Directive Configuration", pai.laws.supplied[1]) as message),1,MAX_MESSAGE_LEN)
|
||||
if(newlaws && pai)
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
/obj/item/grenade/flashbang/proc/flashbang_mobs(turf/source, range)
|
||||
var/list/banged = get_hearers_in_view(range, source)
|
||||
var/list/flashed = viewers(range, source)
|
||||
for(var/i in banged)
|
||||
bang(i, source)
|
||||
for(var/i in flashed)
|
||||
flash(i, source)
|
||||
for(var/mob/living/l in banged)
|
||||
bang(l, source)
|
||||
for(var/mob/living/l in flashed)
|
||||
flash(l, source)
|
||||
|
||||
/obj/item/grenade/flashbang/proc/bang(mob/living/M, turf/source)
|
||||
if(M.stat == DEAD) //They're dead!
|
||||
|
||||
@@ -479,10 +479,10 @@
|
||||
|
||||
possessed = TRUE
|
||||
|
||||
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE)
|
||||
var/list/mob/candidates = pollGhostCandidates("Do you want to play as the spirit of [user.real_name]'s blade?", ROLE_PAI, null, FALSE, 100, POLL_IGNORE_POSSESSED_BLADE)
|
||||
|
||||
if(LAZYLEN(candidates))
|
||||
var/mob/dead/observer/C = pick(candidates)
|
||||
var/mob/C = pick(candidates)
|
||||
var/mob/living/simple_animal/shade/S = new(src)
|
||||
S.real_name = name
|
||||
S.name = name
|
||||
|
||||
@@ -50,7 +50,6 @@
|
||||
if(tank)
|
||||
updateTank(tank, 1, user)
|
||||
|
||||
|
||||
/obj/item/melee/powerfist/proc/updateTank(obj/item/tank/internals/thetank, removing = 0, mob/living/carbon/human/user)
|
||||
if(removing)
|
||||
if(!tank)
|
||||
@@ -71,26 +70,42 @@
|
||||
|
||||
|
||||
/obj/item/melee/powerfist/attack(mob/living/target, mob/living/user)
|
||||
if(!tank)
|
||||
to_chat(user, "<span class='warning'>\The [src] can't operate without a source of gas!</span>")
|
||||
return
|
||||
if(tank && !tank.air_contents.remove(gasperfist * fisto_setting))
|
||||
to_chat(user, "<span class='warning'>\The [src]'s piston-ram lets out a weak hiss, it needs more gas!</span>")
|
||||
playsound(loc, 'sound/effects/refill.ogg', 50, 1)
|
||||
return
|
||||
target.apply_damage(force * fisto_setting, BRUTE)
|
||||
target.visible_message("<span class='danger'>[user]'s powerfist lets out a loud hiss as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
|
||||
"<span class='userdanger'>You cry out in pain as [user]'s punch flings you backwards!</span>")
|
||||
new /obj/effect/temp_visual/kinetic_blast(target.loc)
|
||||
playsound(loc, 'sound/weapons/resonator_blast.ogg', 50, 1)
|
||||
playsound(loc, 'sound/weapons/genhit2.ogg', 50, 1)
|
||||
if(!tank)
|
||||
to_chat(user, "<span class='warning'>\The [src] can't operate without a source of gas!</span>")
|
||||
return
|
||||
var/datum/gas_mixture/gasused = tank.air_contents.remove(gasperfist * fisto_setting)
|
||||
var/turf/T = get_turf(src)
|
||||
if(!T)
|
||||
return
|
||||
T.assume_air(gasused)
|
||||
T.air_update_turf()
|
||||
if(!gasused)
|
||||
to_chat(user, "<span class='warning'>\The [src]'s tank is empty!</span>")
|
||||
target.apply_damage((force / 5), BRUTE)
|
||||
playsound(loc, 'sound/weapons/punch1.ogg', 50, 1)
|
||||
target.visible_message("<span class='danger'>[user]'s powerfist lets out a dull thunk as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
|
||||
"<span class='userdanger'>[user]'s punches you!</span>")
|
||||
return
|
||||
if(gasused.total_moles() < gasperfist * fisto_setting)
|
||||
to_chat(user, "<span class='warning'>\The [src]'s piston-ram lets out a weak hiss, it needs more gas!</span>")
|
||||
playsound(loc, 'sound/weapons/punch4.ogg', 50, 1)
|
||||
target.apply_damage((force / 2), BRUTE)
|
||||
target.visible_message("<span class='danger'>[user]'s powerfist lets out a weak hiss as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
|
||||
"<span class='userdanger'>[user]'s punch strikes with force!</span>")
|
||||
return
|
||||
target.apply_damage(force * fisto_setting, BRUTE)
|
||||
target.visible_message("<span class='danger'>[user]'s powerfist lets out a loud hiss as [user.p_they()] punch[user.p_es()] [target.name]!</span>", \
|
||||
"<span class='userdanger'>You cry out in pain as [user]'s punch flings you backwards!</span>")
|
||||
new /obj/effect/temp_visual/kinetic_blast(target.loc)
|
||||
playsound(loc, 'sound/weapons/resonator_blast.ogg', 50, 1)
|
||||
playsound(loc, 'sound/weapons/genhit2.ogg', 50, 1)
|
||||
|
||||
var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
|
||||
var/atom/throw_target = get_edge_target_turf(target, get_dir(src, get_step_away(target, src)))
|
||||
|
||||
target.throw_at(throw_target, 5 * fisto_setting, 0.2)
|
||||
target.throw_at(throw_target, 5 * fisto_setting, 0.5 + (fisto_setting / 2))
|
||||
|
||||
log_combat(user, target, "power fisted", src)
|
||||
log_combat(user, target, "power fisted", src)
|
||||
|
||||
user.changeNext_move(CLICK_CD_MELEE * click_delay)
|
||||
user.changeNext_move(CLICK_CD_MELEE * click_delay)
|
||||
|
||||
return
|
||||
return
|
||||
|
||||
@@ -401,39 +401,64 @@ GLOBAL_LIST_INIT(cloth_recipes, list ( \
|
||||
* Cardboard
|
||||
*/
|
||||
GLOBAL_LIST_INIT(cardboard_recipes, list ( \
|
||||
new/datum/stack_recipe("box", /obj/item/storage/box), \
|
||||
new/datum/stack_recipe("sec box", /obj/item/storage/box/seclooking), \
|
||||
new/datum/stack_recipe("light tubes", /obj/item/storage/box/lights/tubes), \
|
||||
new/datum/stack_recipe("light bulbs", /obj/item/storage/box/lights/bulbs), \
|
||||
new/datum/stack_recipe("mouse traps", /obj/item/storage/box/mousetraps), \
|
||||
new/datum/stack_recipe("pizza box", /obj/item/pizzabox), \
|
||||
new/datum/stack_recipe("power cell", /obj/item/storage/box/cells), \
|
||||
new/datum/stack_recipe("02", /obj/item/storage/box/otwo), \
|
||||
null, \
|
||||
new/datum/stack_recipe("lethal ammo box", /obj/item/storage/box/lethalshot), \
|
||||
new/datum/stack_recipe("rubber shot ammo box", /obj/item/storage/box/rubbershot), \
|
||||
new/datum/stack_recipe("bean bag ammo box", /obj/item/storage/box/beanbag), \
|
||||
new/datum/stack_recipe("12g ammo box", /obj/item/storage/box/lethalslugs), \
|
||||
new/datum/stack_recipe("stun slug ammo box", /obj/item/storage/box/stunslug), \
|
||||
new/datum/stack_recipe("tech shell ammo box", /obj/item/storage/box/techsslug), \
|
||||
new/datum/stack_recipe("incendiary ammo box", /obj/item/storage/box/fireshot), \
|
||||
new/datum/stack_recipe("firing pins", /obj/item/storage/box/firingpins), \
|
||||
new/datum/stack_recipe("loose ammo", /obj/item/storage/box/ammoshells), \
|
||||
null, \
|
||||
new/datum/stack_recipe("cardborg suit", /obj/item/clothing/suit/cardborg, 3), \
|
||||
new/datum/stack_recipe("cardborg helmet", /obj/item/clothing/head/cardborg), \
|
||||
new/datum/stack_recipe("folder", /obj/item/folder), \
|
||||
new/datum/stack_recipe("large box", /obj/structure/closet/cardboard, 4), \
|
||||
new/datum/stack_recipe("cardboard cutout", /obj/item/cardboard_cutout, 5), \
|
||||
null, \
|
||||
new/datum/stack_recipe("colored brown", /obj/item/storage/box/brown), \
|
||||
new/datum/stack_recipe("colored green", /obj/item/storage/box/green), \
|
||||
new/datum/stack_recipe("colored red", /obj/item/storage/box/blue), \
|
||||
new/datum/stack_recipe("colored blue", /obj/item/storage/box/red), \
|
||||
new/datum/stack_recipe("colored yellow", /obj/item/storage/box/yellow), \
|
||||
new/datum/stack_recipe("colored pink", /obj/item/storage/box/pink), \
|
||||
new/datum/stack_recipe("colored purple", /obj/item/storage/box/purple), \
|
||||
))
|
||||
new/datum/stack_recipe("box", /obj/item/storage/box), \
|
||||
new/datum/stack_recipe("cardborg suit", /obj/item/clothing/suit/cardborg, 3), \
|
||||
new/datum/stack_recipe("cardborg helmet", /obj/item/clothing/head/cardborg), \
|
||||
new/datum/stack_recipe("large box", /obj/structure/closet/cardboard, 4), \
|
||||
new/datum/stack_recipe("cardboard cutout", /obj/item/cardboard_cutout, 5), \
|
||||
new/datum/stack_recipe("pizza box", /obj/item/pizzabox), \
|
||||
new/datum/stack_recipe("folder", /obj/item/folder), \
|
||||
// holy fuck why are there so many boxes
|
||||
new/datum/stack_recipe_list("fancy boxes", list ( \
|
||||
new /datum/stack_recipe("donut box", /obj/item/storage/fancy/donut_box), \
|
||||
new /datum/stack_recipe("egg box", /obj/item/storage/fancy/egg_box), \
|
||||
new /datum/stack_recipe("donk-pockets box", /obj/item/storage/box/donkpockets), \
|
||||
new /datum/stack_recipe("monkey cube box", /obj/item/storage/box/monkeycubes), \
|
||||
new /datum/stack_recipe("box (internals)", /obj/item/storage/box/otwo), \
|
||||
null, \
|
||||
new /datum/stack_recipe("security-styled box", /obj/item/storage/box/seclooking), \
|
||||
new /datum/stack_recipe("buckshot shell box", /obj/item/storage/box/lethalshot), \
|
||||
new /datum/stack_recipe("rubber shot shell box", /obj/item/storage/box/rubbershot), \
|
||||
new /datum/stack_recipe("beanbag shell box", /obj/item/storage/box/beanbag), \
|
||||
new /datum/stack_recipe("slug shell box", /obj/item/storage/box/lethalslugs), \
|
||||
new /datum/stack_recipe("stunslug shell box", /obj/item/storage/box/stunslug), \
|
||||
new /datum/stack_recipe("tech shell box", /obj/item/storage/box/techsslug), \
|
||||
new /datum/stack_recipe("incendiary ammo box", /obj/item/storage/box/fireshot), \
|
||||
new /datum/stack_recipe("loose ammo box", /obj/item/storage/box/ammoshells), \
|
||||
new /datum/stack_recipe("firing pins box", /obj/item/storage/box/firingpins), \
|
||||
null, \
|
||||
new /datum/stack_recipe("flashbang box", /obj/item/storage/box/flashbangs), \
|
||||
new /datum/stack_recipe("flashes box", /obj/item/storage/box/flashes), \
|
||||
new /datum/stack_recipe("handcuffs box", /obj/item/storage/box/handcuffs), \
|
||||
new /datum/stack_recipe("ID card box", /obj/item/storage/box/ids), \
|
||||
new /datum/stack_recipe("PDA box", /obj/item/storage/box/PDAs), \
|
||||
null, \
|
||||
new /datum/stack_recipe("pill bottle box", /obj/item/storage/box/pillbottles), \
|
||||
new /datum/stack_recipe("beaker box", /obj/item/storage/box/beakers), \
|
||||
new /datum/stack_recipe("syringe box", /obj/item/storage/box/syringes), \
|
||||
new /datum/stack_recipe("latex gloves box", /obj/item/storage/box/gloves), \
|
||||
new /datum/stack_recipe("sterile masks box", /obj/item/storage/box/masks), \
|
||||
new /datum/stack_recipe("body bag box", /obj/item/storage/box/bodybags), \
|
||||
new /datum/stack_recipe("prescription glasses box", /obj/item/storage/box/rxglasses), \
|
||||
null, \
|
||||
new /datum/stack_recipe("disk box", /obj/item/storage/box/disks), \
|
||||
new /datum/stack_recipe("light tubes box", /obj/item/storage/box/lights/tubes), \
|
||||
new /datum/stack_recipe("light bulbs box", /obj/item/storage/box/lights/bulbs), \
|
||||
new /datum/stack_recipe("mixed lights box", /obj/item/storage/box/lights/mixed), \
|
||||
new /datum/stack_recipe("power cell box", /obj/item/storage/box/cells), \
|
||||
new /datum/stack_recipe("mouse traps box", /obj/item/storage/box/mousetraps), \
|
||||
new /datum/stack_recipe("candle box", /obj/item/storage/fancy/candle_box), \
|
||||
null, \
|
||||
new /datum/stack_recipe("box (brown)", /obj/item/storage/box/brown), \
|
||||
new /datum/stack_recipe("box (green)", /obj/item/storage/box/green), \
|
||||
new /datum/stack_recipe("box (blue)", /obj/item/storage/box/blue), \
|
||||
new /datum/stack_recipe("box (red)", /obj/item/storage/box/red), \
|
||||
new /datum/stack_recipe("box (yellow)", /obj/item/storage/box/yellow), \
|
||||
new /datum/stack_recipe("box (pink)", /obj/item/storage/box/pink), \
|
||||
new /datum/stack_recipe("box (purple)", /obj/item/storage/box/purple), \
|
||||
)),
|
||||
null, \
|
||||
))
|
||||
|
||||
/obj/item/stack/sheet/cardboard //BubbleWrap //it's cardboard you fuck
|
||||
name = "cardboard"
|
||||
@@ -459,7 +484,7 @@ GLOBAL_LIST_INIT(cardboard_recipes, list ( \
|
||||
var/atom/droploc = drop_location()
|
||||
if(use(1))
|
||||
playsound(I, 'sound/items/bikehorn.ogg', 50, 1, -1)
|
||||
to_chat(user, "<span class='notice'>You stamp the cardboard! Its a clown box! Honk!</span>")
|
||||
to_chat(user, "<span class='notice'>You stamp the cardboard! It's a clown box! Honk!</span>")
|
||||
if (amount >= 0)
|
||||
new/obj/item/storage/box/clown(droploc) //bugfix
|
||||
else
|
||||
@@ -524,31 +549,30 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
|
||||
*/
|
||||
GLOBAL_LIST_INIT(brass_recipes, list ( \
|
||||
new/datum/stack_recipe("wall gear", /obj/structure/destructible/clockwork/wall_gear, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
null,
|
||||
null, \
|
||||
new/datum/stack_recipe("brass pinion airlock", /obj/machinery/door/airlock/clockwork, 5, time = 50, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("brass pinion airlock - windowed", /obj/machinery/door/airlock/clockwork/brass, 5, time = 50, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("brass windoor", /obj/machinery/door/window/clockwork, 2, time = 30, on_floor = TRUE, window_checks = TRUE), \
|
||||
null,
|
||||
null, \
|
||||
new/datum/stack_recipe("brass reflector", /obj/structure/destructible/clockwork/reflector, 10, time = 100, one_per_turf = TRUE, on_floor = TRUE, window_checks = TRUE), \
|
||||
null,
|
||||
null, \
|
||||
new/datum/stack_recipe("brass window - directional", /obj/structure/window/reinforced/clockwork/unanchored, time = 0, on_floor = TRUE, window_checks = TRUE), \
|
||||
new/datum/stack_recipe("brass window - fulltile", /obj/structure/window/reinforced/clockwork/fulltile/unanchored, 2, time = 0, on_floor = TRUE, window_checks = TRUE), \
|
||||
new/datum/stack_recipe("brass chair", /obj/structure/chair/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("brass bar stool", /obj/structure/chair/stool/bar/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("brass stool", /obj/structure/chair/stool/brass, 1, time = 0, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("brass table frame", /obj/structure/table_frame/brass, 1, time = 5, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
null,
|
||||
null, \
|
||||
new/datum/stack_recipe("sender - pressure sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("sender - mech sensor", /obj/structure/destructible/clockwork/trap/trigger/pressure_sensor/mech, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("sender - lever", /obj/structure/destructible/clockwork/trap/trigger/lever, 1, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
new/datum/stack_recipe("sender - repeater", /obj/structure/destructible/clockwork/trap/trigger/repeater, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE), \
|
||||
null,
|
||||
null, \
|
||||
new/datum/stack_recipe("receiver - brass skewer", /obj/structure/destructible/clockwork/trap/brass_skewer, 2, time = 20, one_per_turf = TRUE, on_floor = TRUE, placement_checks = STACK_CHECK_ADJACENT), \
|
||||
new/datum/stack_recipe("receiver - steam vent", /obj/structure/destructible/clockwork/trap/steam_vent, 3, time = 30, one_per_turf = TRUE, on_floor = TRUE, placement_checks = STACK_CHECK_CARDINALS), \
|
||||
new/datum/stack_recipe("receiver - power nullifier", /obj/structure/destructible/clockwork/trap/power_nullifier, 5, time = 20, one_per_turf = TRUE, on_floor = TRUE, placement_checks = STACK_CHECK_CARDINALS), \
|
||||
null,
|
||||
null, \
|
||||
new/datum/stack_recipe("brass flask", /obj/item/reagent_containers/food/drinks/bottle/holyoil/empty), \
|
||||
|
||||
))
|
||||
|
||||
/obj/item/stack/tile/brass
|
||||
@@ -676,7 +700,8 @@ GLOBAL_LIST_INIT(bronze_recipes, list ( \
|
||||
merge_type = /obj/item/stack/sheet/bone
|
||||
|
||||
GLOBAL_LIST_INIT(plastic_recipes, list(
|
||||
new /datum/stack_recipe("plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
|
||||
new /datum/stack_recipe("see-through plastic flaps", /obj/structure/plasticflaps, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
|
||||
new /datum/stack_recipe("opaque plastic flaps", /obj/structure/plasticflaps/opaque, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
|
||||
new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \
|
||||
new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \
|
||||
new /datum/stack_recipe("large trash cart", /obj/structure/closet/crate/bin,50),\
|
||||
|
||||
@@ -197,6 +197,7 @@
|
||||
if(!isturf(T))
|
||||
return
|
||||
T.PlaceOnTop(R.result_type, flags = CHANGETURF_INHERIT_AIR)
|
||||
else
|
||||
O = new R.result_type(usr.drop_location())
|
||||
if(O)
|
||||
O.setDir(usr.dir)
|
||||
@@ -444,4 +445,4 @@
|
||||
|
||||
/datum/stack_recipe_list/New(title, recipes)
|
||||
src.title = title
|
||||
src.recipes = recipes
|
||||
src.recipes = recipes
|
||||
|
||||
@@ -225,16 +225,31 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
|
||||
STR.max_items = 10
|
||||
|
||||
/obj/item/storage/toolbox/artistic/PopulateContents()
|
||||
new/obj/item/storage/crayons(src)
|
||||
new/obj/item/crowbar(src)
|
||||
new/obj/item/stack/cable_coil/red(src)
|
||||
new/obj/item/stack/cable_coil/yellow(src)
|
||||
new/obj/item/stack/cable_coil/blue(src)
|
||||
new/obj/item/stack/cable_coil/green(src)
|
||||
new/obj/item/stack/cable_coil/pink(src)
|
||||
new/obj/item/stack/cable_coil/orange(src)
|
||||
new/obj/item/stack/cable_coil/cyan(src)
|
||||
new/obj/item/stack/cable_coil/white(src)
|
||||
new /obj/item/storage/crayons(src)
|
||||
new /obj/item/crowbar(src)
|
||||
new /obj/item/stack/cable_coil/red(src)
|
||||
new /obj/item/stack/cable_coil/yellow(src)
|
||||
new /obj/item/stack/cable_coil/blue(src)
|
||||
new /obj/item/stack/cable_coil/green(src)
|
||||
new /obj/item/stack/cable_coil/pink(src)
|
||||
new /obj/item/stack/cable_coil/orange(src)
|
||||
new /obj/item/stack/cable_coil/cyan(src)
|
||||
new /obj/item/stack/cable_coil/white(src)
|
||||
|
||||
/obj/item/storage/toolbox/ammo
|
||||
name = "ammo box"
|
||||
desc = "It contains a few clips."
|
||||
icon_state = "ammobox"
|
||||
item_state = "ammobox"
|
||||
|
||||
/obj/item/storage/toolbox/ammo/PopulateContents()
|
||||
new /obj/item/ammo_box/a762(src)
|
||||
new /obj/item/ammo_box/a762(src)
|
||||
new /obj/item/ammo_box/a762(src)
|
||||
new /obj/item/ammo_box/a762(src)
|
||||
new /obj/item/ammo_box/a762(src)
|
||||
new /obj/item/ammo_box/a762(src)
|
||||
new /obj/item/ammo_box/a762(src)
|
||||
|
||||
/obj/item/storage/toolbox/gold_real
|
||||
name = "golden toolbox"
|
||||
@@ -314,4 +329,4 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
|
||||
generate_rubber_toolbox_icon()
|
||||
icon = GLOB.rubber_toolbox_icons[icon_state]
|
||||
. = ..()
|
||||
AddComponent(/datum/component/bouncy)
|
||||
AddComponent(/datum/component/bouncy)
|
||||
|
||||
@@ -88,6 +88,28 @@
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/chair/alt_attack_hand(mob/living/user)
|
||||
if(Adjacent(user) && istype(user))
|
||||
if(!item_chair || !user.can_hold_items() || !has_buckled_mobs() || buckled_mobs.len > 1 || dir != user.dir || flags_1 & NODECONSTRUCT_1)
|
||||
return TRUE
|
||||
if(!user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
|
||||
to_chat(user, "<span class='warning'>You can't do that right now!</span>")
|
||||
return TRUE
|
||||
if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)
|
||||
to_chat(user, "<span class='warning'>You're too exhausted for that.</span>")
|
||||
return TRUE
|
||||
var/mob/living/poordude = buckled_mobs[1]
|
||||
if(!istype(poordude))
|
||||
return TRUE
|
||||
user.visible_message("<span class='notice'>[user] pulls [src] out from under [poordude].</span>", "<span class='notice'>You pull [src] out from under [poordude].</span>")
|
||||
var/C = new item_chair(loc)
|
||||
user.put_in_hands(C)
|
||||
poordude.Knockdown(20)//rip in peace
|
||||
user.adjustStaminaLoss(5)
|
||||
unbuckle_all_mobs(TRUE)
|
||||
qdel(src)
|
||||
return TRUE
|
||||
|
||||
/obj/structure/chair/attack_tk(mob/user)
|
||||
if(!anchored || has_buckled_mobs() || !isturf(user.loc))
|
||||
..()
|
||||
|
||||
@@ -237,6 +237,12 @@
|
||||
start_showpiece_type = /obj/item/clothing/mask/facehugger/lamarr
|
||||
req_access = list(ACCESS_RD)
|
||||
|
||||
/obj/structure/displaycase/clown
|
||||
desc = "In the event of clown, honk glass."
|
||||
alert = TRUE
|
||||
start_showpiece_type = /obj/item/bikehorn
|
||||
req_access = list(ACCESS_CENT_GENERAL)
|
||||
|
||||
/obj/structure/displaycase/trophy
|
||||
name = "trophy display case"
|
||||
desc = "Store your trophies of accomplishment in here, and they will stay forever."
|
||||
|
||||
@@ -626,7 +626,13 @@
|
||||
O.equip(new_spawn, FALSE, new_spawn.client)
|
||||
SSjob.equip_loadout(null, new_spawn, FALSE)
|
||||
SSquirks.AssignQuirks(new_spawn, new_spawn.client, TRUE, TRUE, null, FALSE, new_spawn)
|
||||
new_spawn.AddElement(/datum/element/ghost_role_eligibility)
|
||||
ADD_TRAIT(new_spawn, TRAIT_SIXTHSENSE, GHOSTROLE_TRAIT)
|
||||
ADD_TRAIT(new_spawn,TRAIT_EXEMPT_HEALTH_EVENTS,GHOSTROLE_TRAIT)
|
||||
to_chat(new_spawn,"<span class='boldwarning'>You maybe sharing your cafe with some ninja-captured individuals, so make sure to only interact with the ghosts you hear as a ghost!</span>")
|
||||
to_chat(new_spawn,"<span class='boldwarning'>You can turn yourself into a ghost and freely reenter your body with the ghost action.</span>")
|
||||
var/datum/action/ghost/G = new(new_spawn)
|
||||
G.Grant(new_spawn)
|
||||
|
||||
/datum/outfit/ghostcafe
|
||||
name = "ID, jumpsuit and shoes"
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
|
||||
/obj/structure/grille/attack_animal(mob/user)
|
||||
. = ..()
|
||||
if(!shock(user, 70))
|
||||
if(!shock(user, 70) && !QDELETED(src)) //Last hit still shocks but shouldn't deal damage to the grille)
|
||||
take_damage(rand(5,10), BRUTE, "melee", 1)
|
||||
|
||||
/obj/structure/grille/attack_paw(mob/user)
|
||||
|
||||
@@ -182,6 +182,17 @@
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/structure/table/alt_attack_hand(mob/user)
|
||||
if(user && Adjacent(user) && !user.incapacitated())
|
||||
user.setClickCooldown(CLICK_CD_MELEE)
|
||||
if(istype(user) && user.a_intent == INTENT_HARM)
|
||||
user.visible_message("<span class='warning'>[user] slams [user.p_their()] palms down on [src].</span>", "<span class='warning'>You slam your palms down on [src].</span>")
|
||||
playsound(src, 'sound/weapons/sonic_jackhammer.ogg', 50, 1)
|
||||
else
|
||||
user.visible_message("<span class='notice'>[user] slaps [user.p_their()] hands on [src].</span>", "<span class='notice'>You slap your hands on [src].</span>")
|
||||
playsound(src, 'sound/weapons/tap.ogg', 50, 1)
|
||||
user.do_attack_animation(src)
|
||||
return TRUE
|
||||
|
||||
/obj/structure/table/deconstruct(disassembled = TRUE, wrench_disassembly = 0)
|
||||
if(!(flags_1 & NODECONSTRUCT_1))
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
icon_state = "toilet00"
|
||||
density = FALSE
|
||||
anchored = TRUE
|
||||
can_buckle = TRUE
|
||||
buckle_lying = 0
|
||||
var/open = FALSE //if the lid is up
|
||||
var/cistern = 0 //if the cistern bit is open
|
||||
var/w_items = 0 //the combined w_class of all the items in the cistern
|
||||
@@ -682,4 +684,4 @@
|
||||
else
|
||||
playsound(loc, 'sound/weapons/tap.ogg', 50, 1)
|
||||
if(BURN)
|
||||
playsound(loc, 'sound/items/welder.ogg', 80, 1)
|
||||
playsound(loc, 'sound/items/welder.ogg', 80, 1)
|
||||
|
||||
@@ -16,8 +16,16 @@
|
||||
if (text2num(computer_id) == 2147483647) //this cid causes stickybans to go haywire
|
||||
log_access("Failed Login (invalid cid): [key] [address]-[computer_id]")
|
||||
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.)")
|
||||
var/admin = 0
|
||||
|
||||
if (type == "world")
|
||||
return ..() //shunt world topic banchecks to purely to byond's internal ban system
|
||||
|
||||
var/ckey = ckey(key)
|
||||
var/client/C = GLOB.directory[ckey]
|
||||
if (C && ckey == C.ckey && computer_id == C.computer_id && address == C.address)
|
||||
return //don't recheck connected clients.
|
||||
|
||||
var/admin = FALSE
|
||||
if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey])
|
||||
admin = 1
|
||||
|
||||
@@ -126,7 +134,6 @@
|
||||
bannedckey = ban["ckey"]
|
||||
|
||||
var/newmatch = FALSE
|
||||
var/client/C = GLOB.directory[ckey]
|
||||
var/cachedban = SSstickyban.cache[bannedckey]
|
||||
|
||||
//rogue ban in the process of being reverted.
|
||||
|
||||
@@ -693,7 +693,7 @@
|
||||
var/prev_dynamic_voting = CONFIG_GET(flag/dynamic_voting)
|
||||
CONFIG_SET(flag/dynamic_voting,!prev_dynamic_voting)
|
||||
if (!prev_dynamic_voting)
|
||||
to_chat(world, "<B>Vote is now between extended and dynamic chaos.</B>")
|
||||
to_chat(world, "<B>Vote is now a ranked choice of dynamic storytellers.</B>")
|
||||
else
|
||||
to_chat(world, "<B>Vote is now between extended and secret.</B>")
|
||||
log_admin("[key_name(usr)] [prev_dynamic_voting ? "disabled" : "enabled"] dynamic voting.")
|
||||
|
||||
@@ -137,9 +137,9 @@
|
||||
|
||||
/datum/admins/proc/makeWizard()
|
||||
|
||||
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", ROLE_WIZARD, null)
|
||||
var/list/mob/candidates = pollGhostCandidates("Do you wish to be considered for the position of a Wizard Foundation 'diplomat'?", ROLE_WIZARD, null)
|
||||
|
||||
var/mob/dead/observer/selected = pick_n_take(candidates)
|
||||
var/mob/selected = pick_n_take(candidates)
|
||||
|
||||
var/mob/living/carbon/human/new_character = makeBody(selected)
|
||||
new_character.mind.make_Wizard()
|
||||
@@ -214,9 +214,9 @@
|
||||
|
||||
/datum/admins/proc/makeNukeTeam()
|
||||
var/datum/game_mode/nuclear/temp = new
|
||||
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, temp)
|
||||
var/list/mob/dead/observer/chosen = list()
|
||||
var/mob/dead/observer/theghost = null
|
||||
var/list/mob/candidates = pollGhostCandidates("Do you wish to be considered for a nuke team being sent in?", ROLE_OPERATIVE, temp)
|
||||
var/list/mob/chosen = list()
|
||||
var/mob/theghost = null
|
||||
|
||||
if(candidates.len)
|
||||
var/numagents = 5
|
||||
@@ -378,7 +378,7 @@
|
||||
ertemplate.enforce_human = prefs["enforce_human"]["value"] == "Yes" ? TRUE : FALSE
|
||||
ertemplate.opendoors = prefs["open_armory"]["value"] == "Yes" ? TRUE : FALSE
|
||||
|
||||
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", "deathsquad", null)
|
||||
var/list/mob/candidates = pollGhostCandidates("Do you wish to be considered for [ertemplate.polldesc] ?", "deathsquad", null)
|
||||
var/teamSpawned = FALSE
|
||||
|
||||
if(candidates.len > 0)
|
||||
@@ -404,7 +404,7 @@
|
||||
numagents--
|
||||
continue // This guy's unlucky, not enough spawn points, we skip him.
|
||||
var/spawnloc = spawnpoints[numagents]
|
||||
var/mob/dead/observer/chosen_candidate = pick(candidates)
|
||||
var/mob/chosen_candidate = pick(candidates)
|
||||
candidates -= chosen_candidate
|
||||
if(!chosen_candidate.key)
|
||||
continue
|
||||
|
||||
@@ -166,7 +166,7 @@
|
||||
if(!can_buy(40))
|
||||
return
|
||||
|
||||
var/list/mob/dead/observer/candidates = pollGhostCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
|
||||
var/list/mob/candidates = pollGhostCandidates("Do you want to play as a [blob_reagent_datum.name] blobbernaut?", ROLE_BLOB, null, ROLE_BLOB, 50) //players must answer rapidly
|
||||
if(LAZYLEN(candidates)) //if we got at least one candidate, they're a blobbernaut now.
|
||||
B.max_integrity = initial(B.max_integrity) * 0.25 //factories that produced a blobbernaut have much lower health
|
||||
B.obj_integrity = min(B.obj_integrity, B.max_integrity)
|
||||
@@ -181,7 +181,7 @@
|
||||
blobber.update_icons()
|
||||
blobber.adjustHealth(blobber.maxHealth * 0.5)
|
||||
blob_mobs += blobber
|
||||
var/mob/dead/observer/C = pick(candidates)
|
||||
var/mob/C = pick(candidates)
|
||||
C.transfer_ckey(blobber)
|
||||
SEND_SOUND(blobber, sound('sound/effects/blobattack.ogg'))
|
||||
SEND_SOUND(blobber, sound('sound/effects/attackblob.ogg'))
|
||||
|
||||
@@ -76,9 +76,10 @@ GLOBAL_VAR_INIT(war_declared, FALSE)
|
||||
CONFIG_SET(number/shuttle_refuel_delay, max(CONFIG_GET(number/shuttle_refuel_delay), CHALLENGE_SHUTTLE_DELAY))
|
||||
if(istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
var/datum/game_mode/dynamic/mode = SSticker.mode
|
||||
var/threat_spent = CONFIG_GET(number/dynamic_warops_cost)
|
||||
mode.spend_threat(threat_spent)
|
||||
mode.log_threat("Nuke ops spent [threat_spent] on war ops.")
|
||||
if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED))
|
||||
var/threat_spent = CONFIG_GET(number/dynamic_warops_cost)
|
||||
mode.spend_threat(threat_spent)
|
||||
mode.log_threat("Nuke ops spent [threat_spent] on war ops.")
|
||||
SSblackbox.record_feedback("amount", "nuclear_challenge_mode", 1)
|
||||
|
||||
qdel(src)
|
||||
@@ -101,12 +102,13 @@ GLOBAL_VAR_INIT(war_declared, FALSE)
|
||||
return FALSE
|
||||
if(istype(SSticker.mode, /datum/game_mode/dynamic))
|
||||
var/datum/game_mode/dynamic/mode = SSticker.mode
|
||||
if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement))
|
||||
to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.")
|
||||
return FALSE
|
||||
else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost))
|
||||
to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.")
|
||||
return FALSE
|
||||
if(!(mode.storyteller.flags & WAROPS_ALWAYS_ALLOWED))
|
||||
if(mode.threat_level < CONFIG_GET(number/dynamic_warops_requirement))
|
||||
to_chat(user, "Due to the dynamic space in which the station resides, you are too deep into Nanotrasen territory to reasonably go loud.")
|
||||
return FALSE
|
||||
else if(mode.threat < CONFIG_GET(number/dynamic_warops_cost))
|
||||
to_chat(user, "Due to recent threats on the station, Nanotrasen is looking too closely for a war declaration to be wise.")
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/item/nuclear_challenge/clownops
|
||||
|
||||
@@ -80,6 +80,8 @@
|
||||
if(istype(SSticker.mode,/datum/game_mode/dynamic))
|
||||
mode = SSticker.mode
|
||||
is_dynamic = TRUE
|
||||
if(mode.storyteller.flags & NO_ASSASSIN)
|
||||
is_hijacker = FALSE
|
||||
if(GLOB.joined_player_list.len>=GLOB.dynamic_high_pop_limit)
|
||||
is_hijacker = (prob(10) && mode.threat_level > CONFIG_GET(number/dynamic_hijack_high_population_requirement))
|
||||
else
|
||||
@@ -180,7 +182,7 @@
|
||||
destroy_objective.owner = owner
|
||||
destroy_objective.find_target()
|
||||
add_objective(destroy_objective)
|
||||
else if(prob(30))
|
||||
else if(prob(30) || (mode.storyteller.flags & NO_ASSASSIN))
|
||||
var/datum/objective/maroon/maroon_objective = new
|
||||
maroon_objective.owner = owner
|
||||
maroon_objective.find_target()
|
||||
|
||||
@@ -61,8 +61,6 @@
|
||||
//Actually transfer the gas
|
||||
var/datum/gas_mixture/removed = air2.remove(transfer_moles)
|
||||
|
||||
removed.react(src)
|
||||
|
||||
update_parents()
|
||||
|
||||
return removed
|
||||
|
||||
@@ -93,9 +93,12 @@
|
||||
|
||||
/datum/supply_pack/security/armory/trackingimp
|
||||
name = "Tracking Implants Crate"
|
||||
desc = "Contains four tracking implants. Requires Armory access to open."
|
||||
cost = 1050
|
||||
contains = list(/obj/item/storage/box/trackimp)
|
||||
desc = "Contains four tracking implants and three tracking speedloaders of tracing .38 ammo. Requires Armory access to open."
|
||||
cost = 1100
|
||||
contains = list(/obj/item/storage/box/trackimp,
|
||||
/obj/item/ammo_box/c38/trac,
|
||||
/obj/item/ammo_box/c38/trac,
|
||||
/obj/item/ammo_box/c38/trac)
|
||||
crate_name = "tracking implant crate"
|
||||
|
||||
/datum/supply_pack/security/armory/fire
|
||||
@@ -172,6 +175,32 @@
|
||||
/obj/item/storage/box/beanbag)
|
||||
crate_name = "riot shotgun crate"
|
||||
|
||||
/datum/supply_pack/security/armory/russian
|
||||
name = "Russian Surplus Crate"
|
||||
desc = "Hello Comrade, we have the most modern russian military equipment the black market can offer, for the right price of course. Sadly we couldnt remove the lock so it requires Armory access to open."
|
||||
cost = 5000
|
||||
contraband = TRUE
|
||||
contains = list(/obj/item/reagent_containers/food/snacks/rationpack,
|
||||
/obj/item/ammo_box/a762,
|
||||
/obj/item/storage/toolbox/ammo,
|
||||
/obj/item/clothing/suit/armor/vest/russian,
|
||||
/obj/item/clothing/head/helmet/rus_helmet,
|
||||
/obj/item/clothing/shoes/russian,
|
||||
/obj/item/clothing/gloves/combat,
|
||||
/obj/item/clothing/under/syndicate/rus_army,
|
||||
/obj/item/clothing/under/soviet,
|
||||
/obj/item/clothing/mask/russian_balaclava,
|
||||
/obj/item/clothing/head/helmet/rus_ushanka,
|
||||
/obj/item/clothing/suit/armor/vest/russian_coat,
|
||||
/obj/item/gun/ballistic/shotgun/boltaction,
|
||||
/obj/item/gun/ballistic/shotgun/boltaction)
|
||||
crate_name = "surplus military crate"
|
||||
|
||||
/datum/supply_pack/security/armory/russian/fill(obj/structure/closet/crate/C)
|
||||
for(var/i in 1 to 10)
|
||||
var/item = pick(contains)
|
||||
new item(C)
|
||||
|
||||
/datum/supply_pack/security/armory/spinfusor
|
||||
name = "Stormhammer Spinfusor Crate"
|
||||
cost = 14000
|
||||
@@ -239,7 +268,7 @@
|
||||
/obj/item/ammo_box/magazine/wt550m9)
|
||||
crate_name = "auto rifle ammo crate"
|
||||
|
||||
/datum/supply_pack/security/armory/wt550ammo_nonlethal // Takes around 12 shots to stun crit someone
|
||||
/datum/supply_pack/security/armory/wt550ammo_nonlethal // Takes around 12 shots to stamcrit someone
|
||||
name = "WT-550 Semi-Auto SMG Non-Lethal Ammo Crate"
|
||||
desc = "Contains four 20-round magazines for the WT-550 Semi-Auto SMG. Each magazine is designed to facilitate rapid tactical reloads. Requires Armory access to open."
|
||||
cost = 1000
|
||||
|
||||
@@ -23,6 +23,21 @@
|
||||
crate_name = "Biker Kit"
|
||||
crate_type = /obj/structure/closet/crate/large
|
||||
|
||||
/datum/supply_pack/emergency/bio
|
||||
name = "Biological Emergency Crate"
|
||||
desc = "This crate holds 2 full bio suits which will protect you from viruses, along with a bio bag and two spaceacillin syringes."
|
||||
cost = 2000
|
||||
contains = list(/obj/item/clothing/head/bio_hood,
|
||||
/obj/item/clothing/head/bio_hood,
|
||||
/obj/item/clothing/suit/bio_suit,
|
||||
/obj/item/clothing/suit/bio_suit,
|
||||
/obj/item/storage/bag/bio,
|
||||
/obj/item/reagent_containers/syringe/antiviral,
|
||||
/obj/item/reagent_containers/syringe/antiviral,
|
||||
/obj/item/clothing/gloves/color/latex/nitrile,
|
||||
/obj/item/clothing/gloves/color/latex/nitrile)
|
||||
crate_name = "bio suit crate"
|
||||
|
||||
/datum/supply_pack/emergency/equipment
|
||||
name = "Emergency Bot/Internals Crate"
|
||||
desc = "Explosions got you down? These supplies are guaranteed to patch up holes, in stations and people alike! Comes with two floorbots, two medbots, five oxygen masks and five small oxygen tanks."
|
||||
@@ -112,8 +127,8 @@
|
||||
crate_type = /obj/structure/closet/crate/internals
|
||||
|
||||
/datum/supply_pack/emergency/soft_suit
|
||||
name = "Emergency Space Suit "
|
||||
desc = "Are there bombs going off left and right? Are there meteors shooting around the station? Well then! Here's two fragile space suit for emergencies. Comes with air and masks."
|
||||
name = "Emergency Space Suit"
|
||||
desc = "Are there bombs going off left and right? Are there meteors shooting around the station? Well then! Here's two fragile space suits for emergencies. Comes with air and masks."
|
||||
cost = 1200
|
||||
contains = list(/obj/item/tank/internals/air,
|
||||
/obj/item/tank/internals/air,
|
||||
@@ -126,6 +141,18 @@
|
||||
crate_name = "emergency crate"
|
||||
crate_type = /obj/structure/closet/crate/internals
|
||||
|
||||
/datum/supply_pack/emergency/bomb
|
||||
name = "Explosive Emergency Crate"
|
||||
desc = "Science gone bonkers? Beeping behind the airlock? Buy now and be the hero the station des... I mean needs! (Time not included.)"
|
||||
cost = 1500
|
||||
contains = list(/obj/item/clothing/head/bomb_hood,
|
||||
/obj/item/clothing/suit/bomb_suit,
|
||||
/obj/item/clothing/mask/gas,
|
||||
/obj/item/screwdriver,
|
||||
/obj/item/wirecutters,
|
||||
/obj/item/multitool)
|
||||
crate_name = "bomb suit crate"
|
||||
|
||||
/datum/supply_pack/emergency/firefighting
|
||||
name = "Firefighting Crate"
|
||||
desc = "Only you can prevent station fires. Partner up with two firefighter suits, gas masks, flashlights, large oxygen tanks, extinguishers, and hardhats!"
|
||||
@@ -307,4 +334,3 @@
|
||||
/obj/item/reagent_containers/spray/plantbgone)
|
||||
crate_name = "weed control crate"
|
||||
crate_type = /obj/structure/closet/crate/secure/hydroponics
|
||||
|
||||
|
||||
@@ -25,6 +25,20 @@
|
||||
new /obj/item/reagent_containers/food/snacks/grown/wheat(.)
|
||||
|
||||
|
||||
/datum/supply_pack/critter/parrot
|
||||
name = "Bird Crate"
|
||||
desc = "Contains five expert telecommunication birds."
|
||||
cost = 4000
|
||||
contains = list(/mob/living/simple_animal/parrot)
|
||||
crate_name = "parrot crate"
|
||||
|
||||
/datum/supply_pack/critter/parrot/generate()
|
||||
. = ..()
|
||||
for(var/i in 1 to 4)
|
||||
new /mob/living/simple_animal/parrot(.)
|
||||
if(prob(1))
|
||||
new /mob/living/simple_animal/parrot/clock_hawk(.)
|
||||
|
||||
/datum/supply_pack/critter/butterfly
|
||||
name = "Butterflies Crate"
|
||||
desc = "Not a very dangerous insect, but they do give off a better image than, say, flies or cockroaches."//is that a motherfucking worm reference
|
||||
|
||||
@@ -57,6 +57,27 @@
|
||||
crate_name = "blood freezer"
|
||||
crate_type = /obj/structure/closet/crate/freezer
|
||||
|
||||
/datum/supply_pack/medical/chemical
|
||||
name = "Chemical Starter Kit Crate"
|
||||
desc = "Contains twelve different chemicals, for all the fun experiments you can make."
|
||||
cost = 1700
|
||||
contains = list(/obj/item/reagent_containers/glass/bottle/hydrogen,
|
||||
/obj/item/reagent_containers/glass/bottle/carbon,
|
||||
/obj/item/reagent_containers/glass/bottle/nitrogen,
|
||||
/obj/item/reagent_containers/glass/bottle/oxygen,
|
||||
/obj/item/reagent_containers/glass/bottle/fluorine,
|
||||
/obj/item/reagent_containers/glass/bottle/phosphorus,
|
||||
/obj/item/reagent_containers/glass/bottle/silicon,
|
||||
/obj/item/reagent_containers/glass/bottle/chlorine,
|
||||
/obj/item/reagent_containers/glass/bottle/radium,
|
||||
/obj/item/reagent_containers/glass/bottle/sacid,
|
||||
/obj/item/reagent_containers/glass/bottle/ethanol,
|
||||
/obj/item/reagent_containers/glass/bottle/potassium,
|
||||
/obj/item/clothing/glasses/science,
|
||||
/obj/item/reagent_containers/dropper,
|
||||
/obj/item/storage/box/beakers)
|
||||
crate_name = "chemical crate"
|
||||
|
||||
/datum/supply_pack/medical/defibs
|
||||
name = "Defibrillator Crate"
|
||||
desc = "Contains two defibrillators for bringing the recently deceased back to life."
|
||||
@@ -218,7 +239,7 @@
|
||||
|
||||
/datum/supply_pack/medical/anitvirus
|
||||
name = "Virus Containment Crate"
|
||||
desc = "Viro let out a death plague Mk II again? Someone didnt wash their hands? Old plagues born anew? Well this crate is for you! Hope you cure it before it breaks out of the station... This crate needs medical access to open and has two bio suits, a box of needles and beakers, five spaceacillin needles, and a medibot."
|
||||
desc = "Viro let out a death plague Mk II again? Someone didn't wash their hands? Old plagues born anew? Well, this crate is for you! Hope you cure it before it breaks out of the station... This crate needs medical access to open and has two bio suits, a box of needles and beakers, five spaceacillin needles, and a medibot."
|
||||
cost = 3000
|
||||
access = ACCESS_MEDICAL
|
||||
contains = list(/mob/living/simple_animal/bot/medbot,
|
||||
|
||||
@@ -288,8 +288,7 @@
|
||||
/obj/item/storage/book/bible/booze,
|
||||
/obj/item/storage/book/bible/booze,
|
||||
/obj/item/clothing/suit/hooded/chaplain_hoodie,
|
||||
/obj/item/clothing/suit/hooded/chaplain_hoodie
|
||||
)
|
||||
/obj/item/clothing/suit/hooded/chaplain_hoodie)
|
||||
crate_name = "religious supplies crate"
|
||||
|
||||
/datum/supply_pack/misc/shower
|
||||
|
||||
@@ -53,6 +53,46 @@
|
||||
var/item = pick_n_take(L)
|
||||
new item(C)
|
||||
|
||||
/datum/supply_pack/organic/randomized/chef
|
||||
name = "Excellent Meat Crate"
|
||||
desc = "The best cuts in the whole galaxy."
|
||||
cost = 2000
|
||||
contains = list(/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/killertomato,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/bear,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/xeno,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/spider,
|
||||
/obj/item/reagent_containers/food/snacks/meat/rawbacon,
|
||||
/obj/item/reagent_containers/food/snacks/spiderleg,
|
||||
/obj/item/reagent_containers/food/snacks/carpmeat,
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/human)
|
||||
crate_name = "food crate"
|
||||
|
||||
/datum/supply_pack/organic/randomized/chef/fill(obj/structure/closet/crate/C)
|
||||
for(var/i in 1 to 15)
|
||||
var/item = pick(contains)
|
||||
new item(C)
|
||||
|
||||
/datum/supply_pack/organic/exoticseeds
|
||||
name = "Exotic Seeds Crate"
|
||||
desc = "Any entrepreneuring botanist's dream. Contains twelve different seeds, including three replica-pod seeds and two mystery seeds!"
|
||||
cost = 1500
|
||||
contains = list(/obj/item/seeds/nettle,
|
||||
/obj/item/seeds/replicapod,
|
||||
/obj/item/seeds/replicapod,
|
||||
/obj/item/seeds/replicapod,
|
||||
/obj/item/seeds/plump,
|
||||
/obj/item/seeds/liberty,
|
||||
/obj/item/seeds/amanita,
|
||||
/obj/item/seeds/reishi,
|
||||
/obj/item/seeds/banana,
|
||||
/obj/item/seeds/bamboo,
|
||||
/obj/item/seeds/eggplant/eggy,
|
||||
/obj/item/seeds/random,
|
||||
/obj/item/seeds/random)
|
||||
crate_name = "exotic seeds crate"
|
||||
crate_type = /obj/structure/closet/crate/hydroponics
|
||||
|
||||
/datum/supply_pack/organic/food
|
||||
name = "Food Crate"
|
||||
desc = "Get things cooking with this crate full of useful ingredients! Contains a two dozen eggs, three bananas, and two bags of flour and rice, two cartons of milk, soymilk, as well as salt and pepper shakers, an enzyme and sugar bottle, and three slabs of monkeymeat."
|
||||
@@ -78,6 +118,26 @@
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana)
|
||||
crate_name = "food crate"
|
||||
|
||||
/datum/supply_pack/organic/randomized/chef/fruits
|
||||
name = "Fruit Crate"
|
||||
desc = "Rich in vitamins, may contain oranges."
|
||||
cost = 1500
|
||||
contains = list(/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/orange,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana,
|
||||
/obj/item/reagent_containers/food/snacks/grown/watermelon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/apple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/berries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cherries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes/green,
|
||||
/obj/item/reagent_containers/food/snacks/grown/eggplant,
|
||||
/obj/item/reagent_containers/food/snacks/grown/peach,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry)
|
||||
crate_name = "food crate"
|
||||
|
||||
/datum/supply_pack/organic/fiestatortilla
|
||||
name = "Fiesta Crate"
|
||||
desc = "Spice up the kitchen with this fiesta themed food order! Contains 8 tortilla based food items, as well as a sombrero, moustache, and cloak!"
|
||||
@@ -97,71 +157,6 @@
|
||||
/obj/item/reagent_containers/glass/bottle/capsaicin)
|
||||
crate_name = "fiesta crate"
|
||||
|
||||
/datum/supply_pack/organic/fruit_1
|
||||
name = "Fruit Basic Crate"
|
||||
desc = "Getting scurvy on the station? Well heres your fixing! Contains three of each - bananas, watermelons, limes, lemons, oranges and even three pineapple."
|
||||
cost = 2250
|
||||
contains = list(/obj/item/reagent_containers/food/snacks/grown/watermelon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/watermelon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/watermelon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lime,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/orange,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/orange,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/orange,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana)
|
||||
crate_name = "fruit crate"
|
||||
|
||||
/datum/supply_pack/organic/fruit_2
|
||||
name = "Fruit Delux Crate"
|
||||
desc = "Getting tired of the basic fruits and want to have something a bit more decadent! This crate is for you! Contains three of each - bunches of berries, apples, pineapples, cherries, green & red grapes, eggplants, bananas, peaches, and lastly seven strawberry. Includes one serving tray."
|
||||
cost = 3500
|
||||
contains = list(/obj/item/reagent_containers/food/snacks/grown/berries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/berries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/berries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/apple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/apple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/apple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pineapple,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cherries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cherries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/cherries,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes/green,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes/green,
|
||||
/obj/item/reagent_containers/food/snacks/grown/grapes/green,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana,
|
||||
/obj/item/reagent_containers/food/snacks/grown/banana,
|
||||
/obj/item/reagent_containers/food/snacks/grown/eggplant,
|
||||
/obj/item/reagent_containers/food/snacks/grown/eggplant,
|
||||
/obj/item/reagent_containers/food/snacks/grown/eggplant,
|
||||
/obj/item/reagent_containers/food/snacks/grown/peach,
|
||||
/obj/item/reagent_containers/food/snacks/grown/peach,
|
||||
/obj/item/reagent_containers/food/snacks/grown/peach,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/reagent_containers/food/snacks/grown/strawberry,
|
||||
/obj/item/storage/bag/tray)
|
||||
crate_name = "fruit crate"
|
||||
|
||||
/datum/supply_pack/organic/grill
|
||||
name = "Grilling Starter Kit"
|
||||
desc = "Hey dad I'm Hungry. Hi Hungry I'm THE NEW GRILLING STARTER KIT ONLY 5000 BUX GET NOW! Contains a cooking grill and five fuel coal sheets."
|
||||
@@ -292,6 +287,19 @@
|
||||
considered <b>\[REDACTED\]</b> and returned at your leisure. Note that objects the anomaly produces are specifically attuned exactly to the individual opening the anomaly; regardless \
|
||||
of species, the individual will find the object edible and it will taste great according to their personal definitions, which vary significantly based on person and species.")
|
||||
|
||||
/datum/supply_pack/organic/randomized/chef/vegetables
|
||||
name = "Vegetables Crate"
|
||||
desc = "Grown in vats."
|
||||
cost = 1300
|
||||
contains = list(/obj/item/reagent_containers/food/snacks/grown/chili,
|
||||
/obj/item/reagent_containers/food/snacks/grown/corn,
|
||||
/obj/item/reagent_containers/food/snacks/grown/tomato,
|
||||
/obj/item/reagent_containers/food/snacks/grown/potato,
|
||||
/obj/item/reagent_containers/food/snacks/grown/carrot,
|
||||
/obj/item/reagent_containers/food/snacks/grown/mushroom/chanterelle,
|
||||
/obj/item/reagent_containers/food/snacks/grown/onion,
|
||||
/obj/item/reagent_containers/food/snacks/grown/pumpkin)
|
||||
crate_name = "food crate"
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//////////////////////////// Hydroponics /////////////////////////////////////
|
||||
@@ -364,6 +372,8 @@
|
||||
/obj/item/hatchet,
|
||||
/obj/item/cultivator,
|
||||
/obj/item/plant_analyzer,
|
||||
/obj/item/clothing/gloves/botanic_leather,
|
||||
/obj/item/clothing/suit/apron,
|
||||
/obj/item/flashlight,
|
||||
/obj/item/seeds/carrot,
|
||||
/obj/item/seeds/carrot,
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
/datum/supply_pack/science/robotics/mecha_odysseus
|
||||
name = "Circuit Crate (Odysseus)"
|
||||
desc = "Ever wanted to build your own giant medical robot? Well, now you can! Contains the Odysseus main control board and Odysseus peripherals board. Requires Robotics access to open."
|
||||
cost = 2500
|
||||
cost = 1500
|
||||
access = ACCESS_ROBOTICS
|
||||
contains = list(/obj/item/circuitboard/mecha/odysseus/peripherals,
|
||||
/obj/item/circuitboard/mecha/odysseus/main)
|
||||
@@ -63,7 +63,7 @@
|
||||
/datum/supply_pack/science/robotics/mecha_ripley
|
||||
name = "Circuit Crate (Ripley APLU)"
|
||||
desc = "Rip apart rocks and xenomorphs alike with the Ripley APLU. Contains the Main Ripley control board, as well as the Ripley Peripherals board. Requires Robotics access to open."
|
||||
cost = 3000
|
||||
cost = 1200
|
||||
access = ACCESS_ROBOTICS
|
||||
contains = list(/obj/item/book/manual/ripley_build_and_repair,
|
||||
/obj/item/circuitboard/mecha/ripley/main,
|
||||
@@ -173,6 +173,13 @@
|
||||
crate_name = "robotics assembly crate"
|
||||
crate_type = /obj/structure/closet/crate/secure/science
|
||||
|
||||
/datum/supply_pack/science/rped
|
||||
name = "RPED crate"
|
||||
desc = "Need to rebuild the ORM but science got annihilated after a bomb test? Buy this for the most advanced parts NT can give you."
|
||||
cost = 1500
|
||||
contains = list(/obj/item/storage/part_replacer/cargo)
|
||||
crate_name = "\improper RPED crate"
|
||||
|
||||
/datum/supply_pack/science/shieldwalls
|
||||
name = "Shield Generator Crate"
|
||||
desc = "These high powered Shield Wall Generators are guaranteed to keep any unwanted lifeforms on the outside, where they belong! Contains four shield wall generators. Requires Teleporter access to open."
|
||||
@@ -208,7 +215,7 @@
|
||||
/datum/supply_pack/science/tablets
|
||||
name = "Tablet Crate"
|
||||
desc = "What's a computer? Contains five cargo tablets."
|
||||
cost = 3000
|
||||
cost = 1500
|
||||
contains = list(/obj/item/modular_computer/tablet/preset/cargo,
|
||||
/obj/item/modular_computer/tablet/preset/cargo,
|
||||
/obj/item/modular_computer/tablet/preset/cargo,
|
||||
|
||||
@@ -11,6 +11,23 @@
|
||||
access = ACCESS_SECURITY
|
||||
crate_type = /obj/structure/closet/crate/secure/gear
|
||||
|
||||
/datum/supply_pack/security/ammo
|
||||
name = "Ammo Crate - General Purpose"
|
||||
desc = "Contains two 20-round magazines for the WT-550 Auto Rifle, three boxes of buckshot ammo, three boxes of rubber ammo and special .38 speedloarders. Requires Security access to open."
|
||||
cost = 2500
|
||||
contains = list(/obj/item/ammo_box/magazine/wt550m9,
|
||||
/obj/item/ammo_box/magazine/wt550m9,
|
||||
/obj/item/storage/box/lethalshot,
|
||||
/obj/item/storage/box/lethalshot,
|
||||
/obj/item/storage/box/lethalshot,
|
||||
/obj/item/storage/box/rubbershot,
|
||||
/obj/item/storage/box/rubbershot,
|
||||
/obj/item/storage/box/rubbershot,
|
||||
/obj/item/ammo_box/c38/trac,
|
||||
/obj/item/ammo_box/c38/hotshot,
|
||||
/obj/item/ammo_box/c38/iceblox)
|
||||
crate_name = "ammo crate"
|
||||
|
||||
/datum/supply_pack/security/armor
|
||||
name = "Armor Crate"
|
||||
desc = "Three vests of well-rounded, decently-protective armor. Requires Security access to open."
|
||||
@@ -146,6 +163,13 @@
|
||||
/obj/item/storage/box/handcuffs)
|
||||
crate_name = "security supply crate"
|
||||
|
||||
/datum/supply_pack/security/vending/security
|
||||
name = "SecTech Supply Crate"
|
||||
desc = "Officer Paul bought all the donuts? Then refill the security vendor with ths crate."
|
||||
cost = 1500
|
||||
contains = list(/obj/machinery/vending/security)
|
||||
crate_name = "SecTech supply crate"
|
||||
|
||||
/datum/supply_pack/security/firingpins
|
||||
name = "Standard Firing Pins Crate"
|
||||
desc = "Upgrade your arsenal with 10 standard firing pins. Requires Security access to open."
|
||||
|
||||
@@ -103,7 +103,7 @@
|
||||
|
||||
/datum/supply_pack/service/cutlery
|
||||
name = "Kitchen Cutlery Deluxe Set"
|
||||
desc = "Need to slice and dice away those ''Tomatoes''? Well we got what you need! From a nice set of knifes, forks, plates, glasses, and a whetstone for when you got some grizzle that is a bit harder to slice then normal."
|
||||
desc = "Need to slice and dice away those \"Tomatoes\"? Well we got what you need! From a nice set of knifes, forks, plates, glasses, and a whetstone for when you got some grizzle that is a bit harder to slice then normal."
|
||||
cost = 10000
|
||||
contraband = TRUE
|
||||
contains = list(/obj/item/sharpener, //Deluxe for a reason
|
||||
@@ -127,6 +127,16 @@
|
||||
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass)
|
||||
crate_name = "kitchen cutlery deluxe set"
|
||||
|
||||
/datum/supply_pack/service/replacementdb
|
||||
name = "Replacement Defensive Bar Shotgun"
|
||||
desc = "Someone stole the Bartender's twin-barreled possession? Give them another one at a significant markup. Comes with one unused double-barrel shotgun, shells not included. Requires bartender access to open."
|
||||
cost = 2200
|
||||
access = ACCESS_BAR
|
||||
contraband = TRUE
|
||||
contains = list(/obj/item/gun/ballistic/revolver/doublebarrel)
|
||||
crate_name = "replacement double-barrel crate"
|
||||
crate_type = /obj/structure/closet/crate/secure
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////// Janitor //////////////////////////////////////
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -80,7 +80,3 @@
|
||||
var/next_keysend_reset = 0
|
||||
var/next_keysend_trip_reset = 0
|
||||
var/keysend_tripped = FALSE
|
||||
|
||||
// stops players from coming back through ghost/midround roles after suicide/cryo
|
||||
// for a duration set by CONFIG_GET(number/suicide_reenter_round_timer) and CONFIG_GET(number/roundstart_suicide_time_limit)
|
||||
var/reenter_round_timeout = 0
|
||||
|
||||
@@ -21,6 +21,10 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
var/last_ip
|
||||
var/last_id
|
||||
|
||||
var/icon/custom_holoform_icon
|
||||
var/list/cached_holoform_icons
|
||||
var/last_custom_holoform = 0
|
||||
|
||||
//Cooldowns for saving/loading. These are four are all separate due to loading code calling these one after another
|
||||
var/saveprefcooldown
|
||||
var/loadprefcooldown
|
||||
@@ -2422,3 +2426,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
|
||||
return
|
||||
else
|
||||
custom_names[name_id] = sanitized_name
|
||||
|
||||
/datum/preferences/proc/get_filtered_holoform(filter_type)
|
||||
if(!custom_holoform_icon)
|
||||
return
|
||||
LAZYINITLIST(cached_holoform_icons)
|
||||
if(!cached_holoform_icons[filter_type])
|
||||
cached_holoform_icons[filter_type] = process_holoform_icon_filter(custom_holoform_icon, filter_type)
|
||||
return cached_holoform_icons[filter_type]
|
||||
|
||||
@@ -57,7 +57,6 @@
|
||||
//No idea what this is but eh -tori
|
||||
var/force_alternate_icon = FALSE
|
||||
|
||||
|
||||
/obj/item/clothing/Initialize()
|
||||
. = ..()
|
||||
if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE))
|
||||
|
||||
@@ -337,10 +337,28 @@
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
|
||||
|
||||
/obj/item/clothing/head/helmet/durathread
|
||||
name = "makeshift helmet"
|
||||
desc = "A hardhat with strips of leather and durathread for additional blunt protection."
|
||||
icon_state = "durathread"
|
||||
item_state = "durathread"
|
||||
armor = list("melee" = 25, "bullet" = 10, "laser" = 20,"energy" = 10, "bomb" = 30, "bio" = 15, "rad" = 20, "fire" = 100, "acid" = 50)
|
||||
|
||||
/obj/item/clothing/head/helmet/rus_helmet
|
||||
name = "russian helmet"
|
||||
desc = "It can hold a bottle of vodka."
|
||||
icon_state = "rus_helmet"
|
||||
item_state = "rus_helmet"
|
||||
armor = list("melee" = 30, "bullet" = 25, "laser" = 20,"energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 20, "fire" = 30, "acid" = 50)
|
||||
pocket_storage_component_path = /datum/component/storage/concrete/pockets/small/rushelmet
|
||||
|
||||
/obj/item/clothing/head/helmet/rus_ushanka
|
||||
name = "battle ushanka"
|
||||
desc = "100% bear."
|
||||
icon_state = "rus_ushanka"
|
||||
item_state = "rus_ushanka"
|
||||
clothing_flags = THICKMATERIAL
|
||||
body_parts_covered = HEAD
|
||||
cold_protection = HEAD
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
|
||||
armor = list("melee" = 10, "bullet" = 5, "laser" = 5,"energy" = 5, "bomb" = 5, "bio" = 50, "rad" = 20, "fire" = -10, "acid" = 0)
|
||||
|
||||
@@ -57,3 +57,12 @@
|
||||
desc = "Worn by robust fighters who are willing to do anything to win."
|
||||
icon_state = "luchar"
|
||||
item_state = "luchar"
|
||||
|
||||
/obj/item/clothing/mask/russian_balaclava
|
||||
name = "russian balaclava"
|
||||
desc = "Protects your face from snow."
|
||||
icon_state = "rus_balaclava"
|
||||
item_state = "rus_balaclava"
|
||||
flags_inv = HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
|
||||
visor_flags_inv = HIDEFACE|HIDEFACIALHAIR
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
|
||||
@@ -327,6 +327,13 @@
|
||||
lightCycle = 0
|
||||
active = FALSE
|
||||
|
||||
/obj/item/clothing/shoes/russian
|
||||
name = "russian boots"
|
||||
desc = "Comfy shoes."
|
||||
icon_state = "rus_shoes"
|
||||
item_state = "rus_shoes"
|
||||
pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
|
||||
|
||||
// kevin is into feet
|
||||
/obj/item/clothing/shoes/wraps
|
||||
name = "gilded leg wraps"
|
||||
@@ -347,4 +354,4 @@
|
||||
/obj/item/clothing/shoes/wraps/blue
|
||||
name = "blue leg wraps"
|
||||
desc = "Ankle coverings. Hang ten, brother."
|
||||
icon_state = "bluecuffs"
|
||||
icon_state = "bluecuffs"
|
||||
|
||||
@@ -627,7 +627,9 @@
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank/internals, /obj/item/storage, /obj/item/construction/rcd, /obj/item/pipe_dispenser)
|
||||
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/ancient/mason
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | IMMUTABLE_SLOW
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
flags_1 = TESLA_IGNORE_1
|
||||
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/ancient/mason
|
||||
name = "M.A.S.O.N RIG helmet"
|
||||
@@ -637,12 +639,13 @@
|
||||
armor = list("melee" = 20, "bullet" = 15, "laser" = 15, "energy" = 45, "bomb" = 100, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 100)
|
||||
item_color = "ancient"
|
||||
brightness_on = 16
|
||||
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT | ALLOWINTERNALS | SCAN_REAGENTS
|
||||
flash_protect = 5 //We will not be flash by bombs
|
||||
tint = 1
|
||||
var/obj/machinery/doppler_array/integrated/bomb_radar
|
||||
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
|
||||
clothing_flags = STOPSPRESSUREDAMAGE | THICKMATERIAL | BLOCK_GAS_SMOKE_EFFECT | ALLOWINTERNALS | SCAN_REAGENTS
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
flags_1 = TESLA_IGNORE_1
|
||||
|
||||
/obj/item/clothing/head/helmet/space/hardsuit/ancient/mason/Initialize()
|
||||
. = ..()
|
||||
|
||||
@@ -248,3 +248,20 @@
|
||||
max_integrity = 200
|
||||
resistance_flags = FLAMMABLE
|
||||
armor = list("melee" = 20, "bullet" = 10, "laser" = 30, "energy" = 5, "bomb" = 15, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 50)
|
||||
|
||||
/obj/item/clothing/suit/armor/vest/russian
|
||||
name = "russian vest"
|
||||
desc = "A bulletproof vest with forest camo. Good thing there's plenty of forests to hide in around here, right?"
|
||||
icon_state = "rus_armor"
|
||||
item_state = "rus_armor"
|
||||
armor = list("melee" = 25, "bullet" = 30, "laser" = 0, "energy" = 15, "bomb" = 10, "bio" = 0, "rad" = 20, "fire" = 20, "acid" = 50)
|
||||
/obj/item/clothing/suit/armor/vest/russian_coat
|
||||
name = "russian battle coat"
|
||||
desc = "Used in extremly cold fronts, made out of real bears."
|
||||
icon_state = "rus_coat"
|
||||
item_state = "rus_coat"
|
||||
clothing_flags = THICKMATERIAL
|
||||
body_parts_covered = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
cold_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
|
||||
min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT
|
||||
armor = list("melee" = 25, "bullet" = 20, "laser" = 20, "energy" = 10, "bomb" = 20, "bio" = 50, "rad" = 20, "fire" = -10, "acid" = 50)
|
||||
|
||||
@@ -68,9 +68,18 @@
|
||||
item_color = "syndicate_combat"
|
||||
can_adjust = FALSE
|
||||
|
||||
/obj/item/clothing/under/syndicate/rus_army
|
||||
name = "advanced military tracksuit"
|
||||
desc = "Military grade tracksuits for frontline squatting."
|
||||
icon_state = "rus_under"
|
||||
item_color = "rus_under"
|
||||
can_adjust = FALSE
|
||||
armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
|
||||
resistance_flags = NONE
|
||||
|
||||
/obj/item/clothing/under/syndicate/baseball
|
||||
name = "major league, number unknown"
|
||||
desc = "A major league outfit with the number faded number on the back. Seems rather robust for just a game"
|
||||
desc = "A major league outfit with the number faded number on the back. Seems rather robust for just a game..."
|
||||
icon_state = "syndicatebaseball"
|
||||
item_state = "syndicatebaseball"
|
||||
item_color = "syndicatebaseball"
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
/datum/round_event/santa/start()
|
||||
var/list/candidates = pollGhostCandidates("Santa is coming to town! Do you want to be Santa?", poll_time=150)
|
||||
if(LAZYLEN(candidates))
|
||||
var/mob/dead/observer/C = pick(candidates)
|
||||
var/mob/C = pick(candidates)
|
||||
santa = new /mob/living/carbon/human(pick(GLOB.blobstart))
|
||||
C.transfer_ckey(santa, FALSE)
|
||||
|
||||
|
||||
@@ -439,7 +439,6 @@
|
||||
desc = "Quinine tastes funny, but at least it'll keep that Space Malaria away."
|
||||
icon_state = "tonic"
|
||||
list_reagents = list("tonic" = 50)
|
||||
foodtype = ALCOHOL
|
||||
|
||||
/obj/item/reagent_containers/food/drinks/soda_cans/sodawater
|
||||
name = "soda water"
|
||||
|
||||
@@ -84,14 +84,6 @@
|
||||
required_temp = 413
|
||||
mob_react = FALSE
|
||||
|
||||
/datum/chemical_reaction/caramel_burned
|
||||
name = "Caramel burned"
|
||||
id = "caramel_burned"
|
||||
results = list("carbon" = 1)
|
||||
required_reagents = list("caramel" = 1)
|
||||
required_temp = 483
|
||||
mob_react = FALSE
|
||||
|
||||
/datum/chemical_reaction/cheesewheel
|
||||
name = "Cheesewheel"
|
||||
id = "cheesewheel"
|
||||
@@ -182,4 +174,4 @@
|
||||
name = "BBQ Sauce"
|
||||
id = "bbqsauce"
|
||||
results = list("bbqsauce" = 5)
|
||||
required_reagents = list("ash" = 1, "tomatojuice" = 1, "salglu_solution" = 3, "blackpepper" = 1)
|
||||
required_reagents = list("ash" = 1, "tomatojuice" = 1, "salglu_solution" = 3, "blackpepper" = 1)
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
anchored = TRUE
|
||||
max_integrity = 30
|
||||
layer = LATTICE_LAYER
|
||||
light_power = 0.3
|
||||
|
||||
var/hidden_message
|
||||
var/creator_key
|
||||
|
||||
@@ -27,12 +27,7 @@
|
||||
|
||||
/datum/preferences/proc/update_preview_icon(equip_job = TRUE)
|
||||
// Determine what job is marked as 'High' priority, and dress them up as such.
|
||||
var/datum/job/previewJob
|
||||
var/highest_pref = 0
|
||||
for(var/job in job_preferences)
|
||||
if(job_preferences["[job]"] > highest_pref)
|
||||
previewJob = SSjob.GetJob(job)
|
||||
highest_pref = job_preferences["[job]"]
|
||||
var/datum/job/previewJob = get_highest_job()
|
||||
|
||||
if(previewJob)
|
||||
// Silicons only need a very basic preview since there is no customization for them.
|
||||
@@ -57,3 +52,11 @@
|
||||
parent.show_character_previews(new /mutable_appearance(mannequin))
|
||||
unset_busy_human_dummy(DUMMY_HUMAN_SLOT_PREFERENCES)
|
||||
|
||||
/datum/preferences/proc/get_highest_job()
|
||||
var/highest_pref = 0
|
||||
var/datum/job/highest_job
|
||||
for(var/job in job_preferences)
|
||||
if(job_preferences["[job]"] > highest_pref)
|
||||
highest_job = SSjob.GetJob(job)
|
||||
highest_pref = job_preferences["[job]"]
|
||||
return highest_job
|
||||
|
||||
@@ -18,6 +18,3 @@
|
||||
|
||||
update_icon(preferred_form)
|
||||
updateghostimages()
|
||||
|
||||
client.reenter_round_timeout = max(client.reenter_round_timeout, clientless_round_timeout)
|
||||
clientless_round_timeout = client.reenter_round_timeout
|
||||
|
||||
@@ -19,7 +19,6 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
hud_type = /datum/hud/ghost
|
||||
movement_type = GROUND | FLYING
|
||||
var/can_reenter_corpse
|
||||
var/clientless_round_timeout = 0 //mobs will lack a client as long as their player is disconnected. See client_defines.dm "reenter_round_timeout"
|
||||
var/datum/hud/living/carbon/hud = null // hud
|
||||
var/bootime = 0
|
||||
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
|
||||
@@ -135,7 +134,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
|
||||
AA.onNewMob(src)
|
||||
|
||||
. = ..()
|
||||
|
||||
AddElement(/datum/element/ghost_role_eligibility)
|
||||
grant_all_languages()
|
||||
|
||||
/mob/dead/observer/get_photo_description(obj/item/camera/camera)
|
||||
@@ -270,23 +269,12 @@ Works together with spawning an observer, noted above.
|
||||
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
|
||||
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
|
||||
ghost.can_reenter_corpse = can_reenter_corpse
|
||||
if(penalize) //penalizing them from making a ghost role / midround antag comeback right away.
|
||||
var/penalty = CONFIG_GET(number/suicide_reenter_round_timer) MINUTES
|
||||
var/roundstart_quit_limit = CONFIG_GET(number/roundstart_suicide_time_limit) MINUTES
|
||||
if(world.time < roundstart_quit_limit) //add up the time difference to their antag rolling penalty if they quit before half a (ingame) hour even passed.
|
||||
penalty += roundstart_quit_limit - world.time
|
||||
if(penalty)
|
||||
penalty += world.realtime
|
||||
if(penalty - SSshuttle.realtimeofstart > SSshuttle.auto_call + SSshuttle.emergencyCallTime + SSshuttle.emergencyDockTime + SSshuttle.emergencyEscapeTime)
|
||||
penalty = CANT_REENTER_ROUND
|
||||
if(client)
|
||||
client.reenter_round_timeout = penalty
|
||||
else //A disconnected player (quite likely for cryopods)
|
||||
ghost.clientless_round_timeout = penalty
|
||||
if (client && client.prefs && client.prefs.auto_ooc)
|
||||
if (!(client.prefs.chat_toggles & CHAT_OOC))
|
||||
client.prefs.chat_toggles ^= CHAT_OOC
|
||||
transfer_ckey(ghost, FALSE)
|
||||
ghost.AddElement(/datum/element/ghost_role_eligibility,penalize) // technically already run earlier, but this adds the penalty
|
||||
// needs to be done AFTER the ckey transfer, too
|
||||
return ghost
|
||||
|
||||
/*
|
||||
@@ -343,15 +331,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
return
|
||||
ghostize(0, penalize = TRUE)
|
||||
|
||||
/mob/dead/observer/proc/can_reenter_round(silent = FALSE)
|
||||
var/timeout = clientless_round_timeout
|
||||
if(client)
|
||||
timeout = client.reenter_round_timeout
|
||||
if(timeout != CANT_REENTER_ROUND && timeout <= world.realtime)
|
||||
return TRUE
|
||||
if(!silent && client)
|
||||
to_chat(src, "<span class='warning'>You are unable to reenter the round[timeout != CANT_REENTER_ROUND ? " yet. Your ghost role blacklist will expire in [DisplayTimeText(timeout - world.realtime)]" : ""].</span>")
|
||||
return FALSE
|
||||
|
||||
|
||||
/mob/dead/observer/Move(NewLoc, direct)
|
||||
if(updatedir)
|
||||
|
||||
@@ -14,10 +14,9 @@
|
||||
var/max_grown = 100
|
||||
var/time_of_birth
|
||||
|
||||
rotate_on_lying = 0
|
||||
rotate_on_lying = FALSE
|
||||
bodyparts = list(/obj/item/bodypart/chest/larva, /obj/item/bodypart/head/larva)
|
||||
|
||||
|
||||
//This is fine right now, if we're adding organ specific damage this needs to be updated
|
||||
/mob/living/carbon/alien/larva/Initialize()
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
|
||||
var/gib_type = /obj/effect/decal/cleanable/blood/gibs
|
||||
|
||||
var/rotate_on_lying = 1
|
||||
rotate_on_lying = TRUE
|
||||
|
||||
var/tinttotal = 0 // Total level of visualy impairing items
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
muzzle_ignore = TRUE
|
||||
restraint_check = TRUE
|
||||
emote_type = EMOTE_AUDIBLE
|
||||
mob_type_allowed_typecache = list(/mob/living/carbon, /mob/living/silicon/pai)
|
||||
|
||||
/datum/emote/living/carbon/clap/run_emote(mob/living/user, params)
|
||||
. = ..()
|
||||
|
||||
@@ -333,7 +333,7 @@
|
||||
switch (severity)
|
||||
if (1)
|
||||
if(bomb_armor)
|
||||
b_loss = 500*bomb_armor
|
||||
b_loss = (350*bomb_armor)+150
|
||||
var/atom/throw_target = get_edge_target_turf(src, get_dir(src, get_step_away(src, src)))
|
||||
throw_at(throw_target, 200, 4)
|
||||
damage_clothes(400*bomb_armor, BRUTE, "bomb")
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user