Merge remote-tracking branch 'citadel/master' into isbanned2

This commit is contained in:
kevinz000
2020-01-06 19:49:46 -08:00
125 changed files with 1598 additions and 776 deletions
+14
View File
@@ -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
+8 -3
View File
@@ -38,8 +38,6 @@ require only minor tweaks.
#define ZTRAIT_SPACE_RUINS "Space Ruins"
#define ZTRAIT_LAVA_RUINS "Lava Ruins"
#define ZTRAIT_ISOLATED_RUINS "Isolated Ruins" //Placing ruins on z levels with this trait will use turf reservation instead of usual placement.
// prevents certain turfs from being stripped by a singularity
#define ZTRAIT_PLANET "Planet"
// number - bombcap is multiplied by this before being applied to bombs
#define ZTRAIT_BOMBCAP_MULTIPLIER "Bombcap Multiplier"
@@ -60,11 +58,18 @@ require only minor tweaks.
// CROSSLINKED - mixed in with the cross-linked space pool
#define CROSSLINKED "Cross"
// string - type path of the z-level's baseturf (defaults to space)
#define ZTRAIT_BASETURF "Baseturf"
// default trait definitions, used by SSmapping
#define ZTRAITS_CENTCOM list(ZTRAIT_CENTCOM = TRUE)
#define ZTRAITS_STATION list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_STATION = TRUE)
#define ZTRAITS_SPACE list(ZTRAIT_LINKAGE = CROSSLINKED, ZTRAIT_SPACE_RUINS = TRUE)
#define ZTRAITS_LAVALAND list(ZTRAIT_MINING = TRUE, ZTRAIT_LAVA_RUINS = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 5)
#define ZTRAITS_LAVALAND list(\
ZTRAIT_MINING = TRUE, \
ZTRAIT_LAVA_RUINS = TRUE, \
ZTRAIT_BOMBCAP_MULTIPLIER = 5, \
ZTRAIT_BASETURF = /turf/open/lava/smooth/lava_land_surface)
#define ZTRAITS_REEBE list(ZTRAIT_REEBE = TRUE, ZTRAIT_BOMBCAP_MULTIPLIER = 0.5)
#define DL_NAME "name"
+7
View File
@@ -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
+6
View File
@@ -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"))
+2
View File
@@ -157,6 +157,8 @@
WRITE_LOG(GLOB.config_error_log, text)
SEND_TEXT(world.log, text)
/proc/log_mapping(text)
WRITE_LOG(GLOB.world_map_error_log, text)
/* For logging round startup. */
/proc/start_log(log)
+62
View File
@@ -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)
+9 -1
View File
@@ -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)
-3
View File
@@ -12,6 +12,3 @@
#define is_reserved_level(z) SSmapping.level_trait(z, ZTRAIT_RESERVED)
#define is_away_level(z) SSmapping.level_trait(z, ZTRAIT_AWAY)
// If true, the singularity cannot strip away asteroid turf on this Z
#define is_planet_level(z) SSmapping.level_trait(z, ZTRAIT_PLANET)
+2
View File
@@ -28,6 +28,8 @@ GLOBAL_VAR(world_job_debug_log)
GLOBAL_PROTECT(world_job_debug_log)
GLOBAL_VAR(world_virus_log)
GLOBAL_PROTECT(world_virus_log)
GLOBAL_VAR(world_map_error_log)
GLOBAL_PROTECT(world_map_error_log)
GLOBAL_LIST_EMPTY(bombers)
GLOBAL_PROTECT(bombers)
@@ -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
+1 -1
View File
@@ -315,7 +315,7 @@ SUBSYSTEM_DEF(air)
var/starting_ats = active_turfs.len
sleep(world.tick_lag)
var/timer = world.timeofday
warning("There are [starting_ats] active turfs at roundstart, this is a mapping error caused by a difference of the air between the adjacent turfs. You can see its coordinates using \"Mapping -> Show roundstart AT list\" verb (debug verbs required)")
log_mapping("There are [starting_ats] active turfs at roundstart caused by a difference of the air between the adjacent turfs. You can see its coordinates using \"Mapping -> Show roundstart AT list\" verb (debug verbs required).")
for(var/turf/T in active_turfs)
GLOB.active_turfs_startlist += T
+2 -2
View File
@@ -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
+48 -14
View File
@@ -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))
+1 -1
View File
@@ -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()
+4 -1
View File
@@ -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
+292 -76
View File
@@ -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
+2 -1
View File
@@ -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)
+1 -1
View File
@@ -47,7 +47,7 @@
/atom/New(loc, ...)
//atom creation method that preloads variables at creation
if(GLOB.use_preloader && (src.type == GLOB._preloader.target_path))//in case the instanciated atom is creating other atoms in New()
GLOB._preloader.load(src)
world.preloader_load(src)
if(datum_flags & DF_USE_TAG)
GenerateTag()
+29 -108
View File
@@ -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
*/
@@ -108,8 +108,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 +184,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 +219,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 +244,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 +269,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,6 +299,7 @@
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)
@@ -337,6 +348,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
@@ -390,6 +402,7 @@
requirements = list(101,101,101,80,60,50,50,50,50,50)
high_population_requirement = 50
repeatable = TRUE
property_weights = list("story_potential" = -1, "trust" = 2, "chaos" = 2, "extended" = -2, "valid" = 2)
/datum/dynamic_ruleset/midround/from_ghosts/blob/ready(forced = FALSE)
if (required_candidates > (dead_players.len + list_observers.len))
@@ -421,6 +434,7 @@
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)
@@ -476,6 +490,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,6 +536,7 @@
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)
@@ -555,14 +571,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
return ..()
/datum/dynamic_ruleset/midround/from_ghosts/revenant/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
@@ -607,6 +627,7 @@
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()
@@ -659,6 +680,7 @@
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
@@ -699,6 +721,7 @@
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
@@ -745,31 +768,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
+1 -1
View File
@@ -180,7 +180,7 @@
limit--
while(!FoundDoor && limit)
if (!FoundDoor)
log_world("### MAP WARNING, [src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
log_mapping("[src] at [AREACOORD(src)] failed to find a valid airlock to cyclelink with!")
return
FoundDoor.cyclelinkedairlock = src
cyclelinkedairlock = FoundDoor
+3 -1
View File
@@ -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
@@ -262,14 +262,14 @@
occupant_message("Deconstructing [W]...")
if(do_after_cooldown(W))
chassis.spark_system.start()
W.ScrapeAway()
W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
playsound(W, 'sound/items/deconstruct.ogg', 50, 1)
else if(isfloorturf(target))
var/turf/open/floor/F = target
occupant_message("Deconstructing [F]...")
if(do_after_cooldown(target))
chassis.spark_system.start()
F.ScrapeAway()
F.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
playsound(F, 'sound/items/deconstruct.ogg', 50, 1)
else if (istype(target, /obj/machinery/door/airlock))
occupant_message("Deconstructing [target]...")
@@ -282,7 +282,7 @@
var/turf/open/space/S = target
occupant_message("Building Floor...")
if(do_after_cooldown(S))
S.PlaceOnTop(/turf/open/floor/plating)
S.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
playsound(S, 'sound/items/deconstruct.ogg', 50, 1)
chassis.spark_system.start()
else if(isfloorturf(target))
+1 -1
View File
@@ -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)
@@ -123,7 +123,7 @@
if(metal)
var/turf/T = get_turf(src)
if(isspaceturf(T)) //Block up any exposed space
T.PlaceOnTop(/turf/open/floor/plating/foam)
T.PlaceOnTop(/turf/open/floor/plating/foam, flags = CHANGETURF_INHERIT_AIR)
for(var/direction in GLOB.cardinals)
var/turf/cardinal_turf = get_step(T, direction)
if(get_area(cardinal_turf) != get_area(T)) //We're at an area boundary, so let's block off this turf!
+6 -2
View File
@@ -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!
@@ -673,7 +673,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),\
+8 -2
View File
@@ -192,9 +192,15 @@
var/obj/O
if(R.max_res_amount > 1) //Is it a stack?
O = new R.result_type(usr.drop_location(), R.res_amount * multiplier)
else if(ispath(R.result_type, /turf))
var/turf/T = usr.drop_location()
if(!isturf(T))
return
T.PlaceOnTop(R.result_type, flags = CHANGETURF_INHERIT_AIR)
else
O = new R.result_type(usr.drop_location())
O.setDir(usr.dir)
if(O)
O.setDir(usr.dir)
use(R.req_amount * multiplier)
//START: oh fuck i'm so sorry
@@ -439,4 +445,4 @@
/datum/stack_recipe_list/New(title, recipes)
src.title = title
src.recipes = recipes
src.recipes = recipes
+1 -1
View File
@@ -36,7 +36,7 @@
. = ..()
if(preload_cell_type)
if(!ispath(preload_cell_type,/obj/item/stock_parts/cell))
log_world("### MAP WARNING, [src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].")
log_mapping("[src] at [AREACOORD(src)] had an invalid preload_cell_type: [preload_cell_type].")
else
cell = new preload_cell_type(src)
update_icon()
+1 -1
View File
@@ -872,7 +872,7 @@
user.visible_message("<span class='danger'>[user] blasts \the [target] with \the [src]!</span>")
playsound(target, 'sound/magic/disintegrate.ogg', 100, 1)
W.break_wall()
W.ScrapeAway()
W.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return
//HF blade
+1 -1
View File
@@ -60,7 +60,7 @@
to_chat(user, "<span class='notice'>You build a floor.</span>")
var/turf/T = src.loc
if(isspaceturf(T))
T.PlaceOnTop(/turf/open/floor/plating)
T.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
qdel(src)
return TRUE
return FALSE
+2 -2
View File
@@ -102,13 +102,13 @@
/obj/structure/stairs/proc/force_open_above()
var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
if(T && !istype(T))
T.ChangeTurf(/turf/open/openspace)
T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR)
/obj/structure/stairs/proc/on_multiz_new(turf/source, dir)
if(dir == UP)
var/turf/open/openspace/T = get_step_multiz(get_turf(src), UP)
if(T && !istype(T))
T.ChangeTurf(/turf/open/openspace)
T.ChangeTurf(/turf/open/openspace, flags = CHANGETURF_INHERIT_AIR)
/obj/structure/stairs/intercept_zImpact(atom/movable/AM, levels = 1)
. = ..()
+3 -1
View File
@@ -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)
+6 -1
View File
@@ -10,4 +10,9 @@
/turf/baseturf_skipover/shuttle
name = "Shuttle baseturf skipover"
desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through."
desc = "Acts as the bottom of the shuttle, if this isn't here the shuttle floor is broken through."
/turf/baseturf_bottom
name = "Z-level baseturf placeholder"
desc = "Marker for z-level baseturf, usually resolves to space."
baseturfs = /turf/baseturf_bottom
+23 -15
View File
@@ -1,6 +1,7 @@
// This is a list of turf types we dont want to assign to baseturfs unless through initialization or explicitly
GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
/turf/open/space,
/turf/baseturf_bottom
)))
/turf/proc/empty(turf_type=/turf/open/space, baseturf_type, list/ignore_typecache, flags)
@@ -56,12 +57,20 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
// Creates a new turf
// new_baseturfs can be either a single type or list of types, formated the same as baseturfs. see turf.dm
/turf/proc/ChangeTurf(path, list/new_baseturfs, flags)
if(!path)
return
if(path == /turf/open/space/basic)
// basic doesn't initialize and this will cause issues
// no warning though because this can happen naturaly as a result of it being built on top of
path = /turf/open/space
switch(path)
if(null)
return
if(/turf/baseturf_bottom)
path = SSmapping.level_trait(z, ZTRAIT_BASETURF) || /turf/open/space
if (!ispath(path))
path = text2path(path)
if (!ispath(path))
warning("Z-level [z] has invalid baseturf '[SSmapping.level_trait(z, ZTRAIT_BASETURF)]'")
path = /turf/open/space
if(/turf/open/space/basic)
// basic doesn't initialize and this will cause issues
// no warning though because this can happen naturaly as a result of it being built on top of
path = /turf/open/space
if(!GLOB.use_preloader && path == type && !(flags & CHANGETURF_FORCEOP)) // Don't no-op if the map loader requires it to be reconstructed
return src
if(flags & CHANGETURF_SKIP)
@@ -128,16 +137,15 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
/turf/open/ChangeTurf(path, list/new_baseturfs, flags)
if ((flags & CHANGETURF_INHERIT_AIR) && ispath(path, /turf/open))
SSair.remove_from_active(src)
var/stashed_air = air
air = null // so that it doesn't get deleted
var/datum/gas_mixture/stashed_air = new()
stashed_air.copy_from(air)
. = ..()
if (!. || . == src) // changeturf failed or didn't do anything
air = stashed_air
if (!.) // changeturf failed or didn't do anything
QDEL_NULL(stashed_air)
return
var/turf/open/newTurf = .
if (!istype(newTurf.air, /datum/gas_mixture/immutable/space))
QDEL_NULL(newTurf.air)
newTurf.air = stashed_air
newTurf.air.copy_from(stashed_air)
QDEL_NULL(stashed_air)
SSair.add_to_active(newTurf)
else
if(ispath(path,/turf/closed))
@@ -215,7 +223,7 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
newT.assemble_baseturfs(initial(fake_turf_type.baseturfs)) // The baseturfs list is created like roundstart
if(!length(newT.baseturfs))
newT.baseturfs = list(baseturfs)
newT.baseturfs -= newT.baseturfs & GLOB.blacklisted_automated_baseturfs
newT.baseturfs -= GLOB.blacklisted_automated_baseturfs
newT.baseturfs.Insert(1, old_baseturfs) // The old baseturfs are put underneath
return newT
if(!length(baseturfs))
@@ -315,5 +323,5 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list(
SSair.add_to_active(src)
/turf/proc/ReplaceWithLattice()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
new /obj/structure/lattice(locate(x, y, z))
+1 -1
View File
@@ -23,7 +23,7 @@
icon = 'icons/turf/walls.dmi'
explosion_block = 50
/turf/closed/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE)
/turf/closed/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE)
return
/turf/closed/indestructible/acid_act(acidpwr, acid_volume, acid_id)
+1 -1
View File
@@ -58,7 +58,7 @@
/turf/open/indestructible/singularity_act()
return
/turf/open/indestructible/TerraformTurf(path, defer_change = FALSE, ignore_air = FALSE)
/turf/open/indestructible/TerraformTurf(path, new_baseturf, flags, defer_change = FALSE, ignore_air = FALSE)
return
/turf/open/indestructible/sound
+1 -1
View File
@@ -106,7 +106,7 @@
qdel(L)
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
else
+2 -2
View File
@@ -38,7 +38,7 @@
switch(passed_mode)
if(RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
@@ -70,7 +70,7 @@
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
to_chat(user, "<span class='notice'>You build a floor.</span>")
// Create a floor, which has this chasm underneath it
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
else
+11 -11
View File
@@ -62,29 +62,29 @@
if(severity != 1 && shielded && target != src)
return
if(target == src)
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return
if(target != null)
severity = 3
switch(severity)
if(1)
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
if(2)
switch(pick(1,2;75,3))
if(1)
if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
ReplaceWithLattice()
else
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
if(prob(33))
new /obj/item/stack/sheet/metal(src)
if(2)
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
if(3)
if(prob(80))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
else
break_tile()
hotspot_expose(1000,CELL_VOLUME)
@@ -135,7 +135,7 @@
burnt = 1
/turf/open/floor/proc/make_plating()
return ScrapeAway()
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/ChangeTurf(path, new_baseturf, flags)
if(!isfloorturf(src))
@@ -213,15 +213,15 @@
/turf/open/floor/narsie_act(force, ignore_mobs, probability = 20)
. = ..()
if(.)
ChangeTurf(/turf/open/floor/engine/cult)
ChangeTurf(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/ratvar_act(force, ignore_mobs)
. = ..()
if(.)
ChangeTurf(/turf/open/floor/clockwork)
ChangeTurf(/turf/open/floor/clockwork, flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/acid_melt()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/rcd_vals(mob/user, obj/item/construction/rcd/the_rcd)
switch(the_rcd.mode)
@@ -267,7 +267,7 @@
A.autoclose = TRUE
return TRUE
if(RCD_DECONSTRUCT)
if(ScrapeAway() == src)
if(ScrapeAway(flags = CHANGETURF_INHERIT_AIR) == src)
return FALSE
to_chat(user, "<span class='notice'>You deconstruct [src].</span>")
return TRUE
+7 -7
View File
@@ -11,7 +11,7 @@
name = "plating"
icon_state = "plating"
intact = FALSE
baseturfs = /turf/open/space
baseturfs = /turf/baseturf_bottom
footstep = FOOTSTEP_PLATING
barefootstep = FOOTSTEP_HARD_BAREFOOT
clawfootstep = FOOTSTEP_HARD_CLAW
@@ -61,7 +61,7 @@
to_chat(user, "<span class='notice'>You begin reinforcing the floor...</span>")
if(do_after(user, 30, target = src))
if (R.get_amount() >= 2 && !istype(src, /turf/open/floor/engine))
PlaceOnTop(/turf/open/floor/engine)
PlaceOnTop(/turf/open/floor/engine, flags = CHANGETURF_INHERIT_AIR)
playsound(src, 'sound/items/deconstruct.ogg', 80, 1)
R.use(2)
to_chat(user, "<span class='notice'>You reinforce the floor.</span>")
@@ -76,7 +76,7 @@
var/obj/item/stack/tile/W = C
if(!W.use(1))
return
var/turf/open/floor/T = PlaceOnTop(W.turf_type)
var/turf/open/floor/T = PlaceOnTop(W.turf_type, flags = CHANGETURF_INHERIT_AIR)
if(istype(W, /obj/item/stack/tile/light)) //TODO: get rid of this ugly check somehow
var/obj/item/stack/tile/light/L = W
var/turf/open/floor/light/F = T
@@ -117,7 +117,7 @@
qdel(L)
to_chat(user, "<span class='notice'>You reinforce the foamed plating with tiling.</span>")
playsound(src, 'sound/weapons/Genhit.ogg', 50, TRUE)
ChangeTurf(/turf/open/floor/plating)
ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
playsound(src, 'sound/weapons/tap.ogg', 100, TRUE) //The attack sound is muffled by the foam itself
user.changeNext_move(CLICK_CD_MELEE)
@@ -125,7 +125,7 @@
if(prob(I.force * 20 - 25))
user.visible_message("<span class='danger'>[user] smashes through [src]!</span>", \
"<span class='danger'>You smash through [src] with [I]!</span>")
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='danger'>You hit [src], to no effect!</span>")
@@ -136,13 +136,13 @@
/turf/open/floor/plating/foam/rcd_act(mob/user, obj/item/construction/rcd/the_rcd, passed_mode)
if(passed_mode == RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
ChangeTurf(/turf/open/floor/plating)
ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
/turf/open/floor/plating/foam/ex_act()
..()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/plating/foam/tool_act(mob/living/user, obj/item/I, tool_type)
return
@@ -76,11 +76,6 @@
for(var/obj/item/stack/ore/O in src)
SEND_SIGNAL(W, COMSIG_PARENT_ATTACKBY, O)
/turf/open/floor/plating/asteroid/singularity_act()
if(is_planet_level(z))
return ..()
ScrapeAway()
/turf/open/floor/plating/asteroid/ex_act(severity, target)
. = SEND_SIGNAL(src, COMSIG_ATOM_EX_ACT, severity, target)
contents_explosion(severity, target)
@@ -132,6 +127,7 @@
/turf/open/floor/plating/asteroid/airless
initial_gas_mix = AIRLESS_ATMOS
baseturfs = /turf/open/floor/plating/asteroid/airless
turf_type = /turf/open/floor/plating/asteroid/airless
@@ -43,7 +43,7 @@
return TRUE
if(floor_tile)
new floor_tile(src, 2)
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return TRUE
/turf/open/floor/engine/acid_act(acidpwr, acid_volume)
@@ -56,23 +56,23 @@
if(severity != 1 && shielded && target != src)
return
if(target == src)
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return
switch(severity)
if(1)
if(prob(80))
if(!length(baseturfs) || !ispath(baseturfs[baseturfs.len-1], /turf/open/floor))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
ReplaceWithLattice()
else
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
else if(prob(50))
ScrapeAway(2)
ScrapeAway(2, flags = CHANGETURF_INHERIT_AIR)
else
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
if(2)
if(prob(50))
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/engine/singularity_pull(S, current_size)
..()
+1 -1
View File
@@ -54,7 +54,7 @@
switch(passed_mode)
if(RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
+2 -2
View File
@@ -125,7 +125,7 @@
qdel(L)
playsound(src, 'sound/weapons/genhit.ogg', 50, 1)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
to_chat(user, "<span class='warning'>You need one floor tile to build a floor!</span>")
else
@@ -212,7 +212,7 @@
switch(passed_mode)
if(RCD_FLOORWALL)
to_chat(user, "<span class='notice'>You build a floor.</span>")
PlaceOnTop(/turf/open/floor/plating)
PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
return TRUE
return FALSE
+3 -3
View File
@@ -9,7 +9,7 @@
// A list will be created in initialization that figures out the baseturf's baseturf etc.
// In the case of a list it is sorted from bottom layer to top.
// This shouldn't be modified directly, use the helper procs.
var/list/baseturfs = /turf/open/space
var/list/baseturfs = /turf/baseturf_bottom
var/temperature = T20C
var/to_be_destroyed = 0 //Used for fire, if a melting temperature was reached, it will be destroyed
@@ -391,7 +391,7 @@
continue
if(O.invisibility == INVISIBILITY_MAXIMUM)
O.singularity_act()
ScrapeAway()
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return(2)
/turf/proc/can_have_cabling()
@@ -564,4 +564,4 @@
//Whatever happens after high temperature fire dies out or thermite reaction works.
//Should return new turf
/turf/proc/Melt()
return ScrapeAway()
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
+1
View File
@@ -108,6 +108,7 @@ GLOBAL_VAR(restart_counter)
GLOB.world_href_log = "[GLOB.log_directory]/hrefs.log"
GLOB.sql_error_log = "[GLOB.log_directory]/sql.log"
GLOB.world_qdel_log = "[GLOB.log_directory]/qdel.log"
GLOB.world_map_error_log = "[GLOB.log_directory]/map_errors.log"
GLOB.world_runtime_log = "[GLOB.log_directory]/runtime.log"
GLOB.query_debug_log = "[GLOB.log_directory]/query_debug.log"
GLOB.world_job_debug_log = "[GLOB.log_directory]/job_debug.log"
+1 -1
View File
@@ -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.")
+19
View File
@@ -35,6 +35,9 @@ GLOBAL_LIST_INIT(admin_verbs_debug_mapping, list(
/client/proc/cmd_admin_grantfullaccess,
/client/proc/cmd_admin_areatest_all,
/client/proc/cmd_admin_areatest_station,
#ifdef TESTING
/client/proc/see_dirty_varedits,
#endif
/client/proc/cmd_admin_test_atmos_controllers,
/client/proc/cmd_admin_rejuvenate,
/datum/admins/proc/show_traitor_panel,
@@ -84,8 +87,24 @@ GLOBAL_PROTECT(admin_verbs_debug_mapping)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
SSblackbox.record_feedback("tally", "admin_verb", 1, "Show Camera Range")
#ifdef TESTING
GLOBAL_LIST_EMPTY(dirty_vars)
/client/proc/see_dirty_varedits()
set category = "Mapping"
set name = "Dirty Varedits"
var/list/dat = list()
dat += "<h3>Abandon all hope ye who enter here</h3><br><br>"
for(var/thing in GLOB.dirty_vars)
dat += "[thing]<br>"
CHECK_TICK
var/datum/browser/popup = new(usr, "dirty_vars", "Dirty Varedits", 900, 750)
popup.set_content(dat.Join())
popup.open()
#endif
/client/proc/sec_camera_report()
set category = "Mapping"
set name = "Camera Report"
@@ -139,7 +139,7 @@
var/new_thing_type = fabrication_values["new_obj_type"]
if(isturf(target)) //if our target is a turf, we're just going to ChangeTurf it and assume it'll work out.
var/turf/T = target
T.ChangeTurf(new_thing_type)
T.ChangeTurf(new_thing_type, flags = CHANGETURF_INHERIT_AIR)
else
if(new_thing_type)
if(fabrication_values["dir_in_new"])
@@ -59,7 +59,7 @@
if(anchored)
T.PlaceOnTop(/turf/closed/wall/clockwork)
else
T.PlaceOnTop(/turf/open/floor/clockwork)
T.PlaceOnTop(/turf/open/floor/clockwork, flags = CHANGETURF_INHERIT_AIR)
new /obj/structure/falsewall/brass(T)
qdel(src)
else
@@ -239,9 +239,9 @@
var/turf/T = safepick(validturfs)
if(T)
if(istype(T, /turf/open/floor/plating))
T.PlaceOnTop(/turf/open/floor/engine/cult)
T.PlaceOnTop(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR)
else
T.ChangeTurf(/turf/open/floor/engine/cult)
T.ChangeTurf(/turf/open/floor/engine/cult, flags = CHANGETURF_INHERIT_AIR)
else
var/turf/open/floor/engine/cult/F = safepick(cultturfs)
if(F)
@@ -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()
@@ -57,10 +57,10 @@
if(item.parent)
var/static/pipenetwarnings = 10
if(pipenetwarnings > 0)
warning("build_pipeline(): [item.type] added to a pipenet while still having one. (pipes leading to the same spot stacking in one turf) Nearby: ([item.x], [item.y], [item.z])")
log_mapping("build_pipeline(): [item.type] added to a pipenet while still having one. (pipes leading to the same spot stacking in one turf) Nearby: ([item.x], [item.y], [item.z]).")
pipenetwarnings -= 1
if(pipenetwarnings == 0)
warning("build_pipeline(): further messages about pipenets will be suppressed")
log_mapping("build_pipeline(): further messages about pipenets will be suppressed")
members += item
possible_expansions += item
@@ -56,7 +56,7 @@
pixel_y = (new_layer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_Y
/obj/machinery/meter/process_atmos()
if(!target)
if(!(target?.flags_1 & INITIALIZED_1))
icon_state = "meterX"
return 0
+3 -3
View File
@@ -22,9 +22,9 @@
if(istype(object,/turf) && left_click && !alt_click && !ctrl_click)
var/turf/T = object
if(isspaceturf(object))
T.PlaceOnTop(/turf/open/floor/plating)
T.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else if(isplatingturf(object))
T.PlaceOnTop(/turf/open/floor/plasteel)
T.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR)
else if(isfloorturf(object))
T.PlaceOnTop(/turf/closed/wall)
else if(iswallturf(object))
@@ -35,7 +35,7 @@
log_admin("Build Mode: [key_name(c)] deleted [object] at [AREACOORD(object)]")
if(isturf(object))
var/turf/T = object
T.ScrapeAway()
T.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
else if(isobj(object))
qdel(object)
return
+12
View File
@@ -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]
-1
View File
@@ -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))
+1 -3
View File
@@ -59,9 +59,7 @@
return
var/area/AS = get_area(src)
if(istype(AS, /area/holodeck))
log_world("### MAPPING ERROR")
log_world("Holodeck computer cannot be in a holodeck.")
log_world("This would cause circular power dependency.")
log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.")
qdel(src)
return
else
+9 -2
View File
@@ -70,7 +70,7 @@
//initialize things that are normally initialized after map load
parsed.initTemplateBounds()
smooth_zlevel(world.maxz)
log_game("Z-level [name] loaded at at [x],[y],[world.maxz]")
log_game("Z-level [name] loaded at [x],[y],[world.maxz]")
return level
@@ -84,6 +84,13 @@
if(T.y+height > world.maxy)
return
var/list/border = block(locate(max(T.x-1, 1), max(T.y-1, 1), T.z),
locate(min(T.x+width+1, world.maxx), min(T.y+height+1, world.maxy), T.z))
for(var/L in border)
var/turf/turf_to_disable = L
SSair.remove_from_active(turf_to_disable) //stop processing turfs along the border to prevent runtimes, we return it in initTemplateBounds()
turf_to_disable.atmos_adjacent_turfs?.Cut()
// Accept cached maps, but don't save them automatically - we don't want
// ruins clogging up memory for the whole round.
var/datum/parsed_map/parsed = cached_map || new(file(mappath))
@@ -100,7 +107,7 @@
//initialize things that are normally initialized after map load
parsed.initTemplateBounds()
log_game("[name] loaded at at [T.x],[T.y],[T.z]")
log_game("[name] loaded at [T.x],[T.y],[T.z]")
return bounds
/datum/map_template/proc/get_affected_turfs(turf/T, centered = FALSE)
+9 -21
View File
@@ -18,7 +18,7 @@
/obj/effect/baseturf_helper/LateInitialize()
if(!baseturf_to_replace)
baseturf_to_replace = typecacheof(/turf/open/space)
baseturf_to_replace = typecacheof(list(/turf/open/space,/turf/baseturf_bottom))
else if(!length(baseturf_to_replace))
baseturf_to_replace = list(baseturf_to_replace = TRUE)
else if(baseturf_to_replace[baseturf_to_replace[1]] != TRUE) // It's not associative
@@ -45,7 +45,6 @@
thing.PlaceOnBottom(null, baseturf)
else if(baseturf_to_replace[thing.baseturfs])
thing.assemble_baseturfs(baseturf)
return
else
thing.PlaceOnBottom(null, baseturf)
@@ -107,16 +106,16 @@
/obj/effect/mapping_helpers/airlock/cyclelink_helper/Initialize(mapload)
. = ..()
if(!mapload)
log_world("### MAP WARNING, [src] spawned outside of mapload!")
log_mapping("[src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
if(airlock)
if(airlock.cyclelinkeddir)
log_world("### MAP WARNING, [src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
log_mapping("[src] at [AREACOORD(src)] tried to set [airlock] cyclelinkeddir, but it's already set!")
else
airlock.cyclelinkeddir = dir
else
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
/obj/effect/mapping_helpers/airlock/locked
@@ -126,16 +125,16 @@
/obj/effect/mapping_helpers/airlock/locked/Initialize(mapload)
. = ..()
if(!mapload)
log_world("### MAP WARNING, [src] spawned outside of mapload!")
log_mapping("[src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
if(airlock)
if(airlock.locked)
log_world("### MAP WARNING, [src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
log_mapping("[src] at [AREACOORD(src)] tried to bolt [airlock] but it's already locked!")
else
airlock.locked = TRUE
else
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
/obj/effect/mapping_helpers/airlock/unres
name = "airlock unresctricted side helper"
@@ -144,13 +143,13 @@
/obj/effect/mapping_helpers/airlock/unres/Initialize(mapload)
. = ..()
if(!mapload)
log_world("### MAP WARNING, [src] spawned outside of mapload!")
log_mapping("[src] spawned outside of mapload!")
return
var/obj/machinery/door/airlock/airlock = locate(/obj/machinery/door/airlock) in loc
if(airlock)
airlock.unres_sides ^= dir
else
log_world("### MAP WARNING, [src] failed to find an airlock at [AREACOORD(src)]")
log_mapping("[src] failed to find an airlock at [AREACOORD(src)]")
//needs to do its thing before spawn_rivers() is called
@@ -164,17 +163,6 @@ INITIALIZE_IMMEDIATE(/obj/effect/mapping_helpers/no_lava)
var/turf/T = get_turf(src)
T.flags_1 |= NO_LAVA_GEN_1
/// Adds the map it is on to the z_is_planet list
/obj/effect/mapping_helpers/planet_z
name = "planet z helper"
layer = POINT_LAYER
/obj/effect/mapping_helpers/planet_z/Initialize()
. = ..()
var/datum/space_level/S = SSmapping.get_level(z)
S.traits[ZTRAIT_PLANET] = TRUE
//This helper applies components to things on the map directly.
/obj/effect/mapping_helpers/component_injector
name = "Component Injector"
+14 -6
View File
@@ -8,18 +8,26 @@ GLOBAL_DATUM_INIT(_preloader, /datum/map_preloader, new)
var/list/attributes
var/target_path
/datum/map_preloader/proc/setup(list/the_attributes, path)
/world/proc/preloader_setup(list/the_attributes, path)
if(the_attributes.len)
GLOB.use_preloader = TRUE
attributes = the_attributes
target_path = path
var/datum/map_preloader/preloader_local = GLOB._preloader
preloader_local.attributes = the_attributes
preloader_local.target_path = path
/datum/map_preloader/proc/load(atom/what)
/world/proc/preloader_load(atom/what)
GLOB.use_preloader = FALSE
for(var/attribute in attributes)
var/value = attributes[attribute]
var/datum/map_preloader/preloader_local = GLOB._preloader
for(var/attribute in preloader_local.attributes)
var/value = preloader_local.attributes[attribute]
if(islist(value))
value = deepCopyList(value)
#ifdef TESTING
if(what.vars[attribute] == value)
var/message = "<font color=green>[what.type]</font> at [AREACOORD(what)] - <b>VAR:</b> <font color=red>[attribute] = [isnull(value) ? "null" : (isnum(value) ? value : "\"[value]\"")]</font>"
log_mapping("DIRTY VAR: [message]")
GLOB.dirty_vars += message
#endif
what.vars[attribute] = value
/area/template_noop
+4 -4
View File
@@ -306,8 +306,8 @@
//first instance the /area and remove it from the members list
index = members.len
if(members[index] != /area/template_noop)
GLOB._preloader.setup(members_attributes[index])//preloader for assigning set variables on atom creation
var/atype = members[index]
world.preloader_setup(members_attributes[index], atype)//preloader for assigning set variables on atom creation
var/atom/instance = areaCache[atype]
if (!instance)
instance = GLOB.areas_by_type[atype]
@@ -318,7 +318,7 @@
instance.contents.Add(crds)
if(GLOB.use_preloader && instance)
GLOB._preloader.load(instance)
world.preloader_load(instance)
//then instance the /turf and, if multiple tiles are presents, simulates the DMM underlays piling effect
@@ -354,7 +354,7 @@
//Instance an atom at (x,y,z) and gives it the variables in attributes
/datum/parsed_map/proc/instance_atom(path,list/attributes, turf/crds, no_changeturf, placeOnTop)
GLOB._preloader.setup(attributes, path)
world.preloader_setup(attributes, path)
if(crds)
if(ispath(path, /turf))
@@ -368,7 +368,7 @@
. = create_atom(path, crds)//first preloader pass
if(GLOB.use_preloader && .)//second preloader pass, for those atoms that don't ..() in New()
GLOB._preloader.load(.)
world.preloader_load(.)
//custom CHECK_TICK here because we don't want things created while we're sleeping to not initialize
if(TICK_CHECK)
@@ -926,7 +926,7 @@
timer = world.time + create_delay + 1
if(do_after(user, create_delay, target = T))
var/old_name = T.name
if(T.TerraformTurf(turf_type))
if(T.TerraformTurf(turf_type, flags = CHANGETURF_INHERIT_AIR))
user.visible_message("<span class='danger'>[user] turns \the [old_name] into [transform_string]!</span>")
message_admins("[ADMIN_LOOKUPFLW(user)] fired the lava staff at [ADMIN_VERBOSEJMP(T)]")
log_game("[key_name(user)] fired the lava staff at [AREACOORD(T)].")
@@ -937,7 +937,7 @@
qdel(L)
else
var/old_name = T.name
if(T.TerraformTurf(reset_turf_type))
if(T.TerraformTurf(reset_turf_type, flags = CHANGETURF_INHERIT_AIR))
user.visible_message("<span class='danger'>[user] turns \the [old_name] into [reset_string]!</span>")
timer = world.time + reset_cooldown
playsound(T,'sound/magic/fireball.ogg', 200, 1)
@@ -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
@@ -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
+1
View File
@@ -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)
. = ..()
@@ -1,31 +1,3 @@
//IMPORTANT: Multiple animate() calls do not stack well, so try to do them all at once if you can.
/mob/living/carbon/update_transform()
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/final_pixel_y = pixel_y
var/final_dir = dir
var/changed = 0
if(lying != lying_prev && rotate_on_lying)
changed++
ntransform.TurnTo(lying_prev,lying)
if(lying == 0) //Lying to standing
final_pixel_y = get_standard_pixel_y_offset()
else //if(lying != 0)
if(lying_prev == 0) //Standing to lying
pixel_y = get_standard_pixel_y_offset()
final_pixel_y = get_standard_pixel_y_offset(lying)
if(dir & (EAST|WEST)) //Facing east or west
final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass
if(resize != RESIZE_DEFAULT_SIZE)
changed++
ntransform.Scale(resize)
resize = RESIZE_DEFAULT_SIZE
if(changed)
animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT)
setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life().
/mob/living/carbon
var/list/overlays_standing[TOTAL_LAYERS]
+3 -1
View File
@@ -111,4 +111,6 @@
var/list/diseases = list() // list of all diseases in a mob
var/list/disease_resistances = list()
var/drag_slowdown = TRUE //Whether the mob is slowed down when dragging another prone mob
var/drag_slowdown = TRUE //Whether the mob is slowed down when dragging another prone mob
var/rotate_on_lying = FALSE
+8 -12
View File
@@ -260,15 +260,6 @@
viewalerts = 1
src << browse(dat, "window=aialerts&can_close=0")
/mob/living/silicon/ai/proc/ai_roster()
var/dat = "<html><head><title>Crew Roster</title></head><body><b>Crew Roster:</b><br><br>"
dat += GLOB.data_core.get_manifest()
dat += "</body></html>"
src << browse(dat, "window=airoster")
onclose(src, "airoster")
/mob/living/silicon/ai/proc/ai_call_shuttle()
if(control_disabled)
to_chat(usr, "<span class='warning'>Wireless control is disabled!</span>")
@@ -681,18 +672,23 @@
"green face" = 'icons/mob/ai.dmi',
"xeno queen" = 'icons/mob/alien.dmi',
"horror" = 'icons/mob/ai.dmi',
"creature" = 'icons/mob/ai.dmi'
"creature" = 'icons/mob/ai.dmi',
"custom"
)
input = input("Please select a hologram:") as null|anything in icon_list
if(input)
qdel(holo_icon)
switch(input)
if("xeno queen")
if("custom")
if(client?.prefs?.custom_holoform_icon)
holo_icon = client.prefs.get_filtered_holoform(HOLOFORM_FILTER_AI)
else
holo_icon = getHologramIcon(icon('icons/mob/ai.dmi', "female"))
else if("xeno queen")
holo_icon = getHologramIcon(icon(icon_list[input],"alienq"))
else
holo_icon = getHologramIcon(icon(icon_list[input], input))
return
/mob/living/silicon/ai/proc/corereturn()
set category = "Malfunction"
@@ -68,7 +68,7 @@
// Use this when setting the aiEye's location.
// It will also stream the chunk that the new loc is in.
/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE)
/mob/camera/aiEye/proc/setLoc(T, force_update = FALSE, dir)
if(ai)
if(!isturf(ai.loc))
return
@@ -88,7 +88,7 @@
//Holopad
if(istype(ai.current, /obj/machinery/holopad))
var/obj/machinery/holopad/H = ai.current
H.move_hologram(ai, T)
H.move_hologram(ai, T, dir)
if(ai.camera_light_on)
ai.light_cameras()
if(ai.master_multicam)
@@ -139,7 +139,7 @@
for(var/i = 0; i < max(user.sprint, initial); i += 20)
var/turf/step = get_turf(get_step(user.eyeobj, direct))
if(step)
user.eyeobj.setLoc(step)
user.eyeobj.setLoc(step, null, direct)
user.cooldown = world.timeofday + 5
if(user.acceleration)
@@ -0,0 +1,28 @@
/mob/living/silicon/verb/clear_custom_holoform()
set name = "Clear Custom Holoform"
set desc = "Clear your current custom holoform"
set category = "OOC"
if(!client.prefs)
to_chat(src, "<span class='boldwarning'>No preferences datum on your client, contact an admin/coder!</span>")
return
client.prefs.custom_holoform_icon = null
client.prefs.cached_holoform_icons = null
to_chat(src, "<span class='boldnotice'>Holoform removed.</span>")
/mob/living/silicon/verb/set_custom_holoform()
set name = "Set Custom Holoform"
set desc = "Set your custom holoform using your current preferences slot and a specified set of gear."
set category = "OOC"
if(!client.prefs)
to_chat(src, "<span class='boldwarning'>No preferences datum on your client, contact an admin/coder!</span>")
return
if(client.prefs.last_custom_holoform > world.time - CUSTOM_HOLOFORM_DELAY)
to_chat(src, "<span class='warning'>You are attempting to change custom holoforms too fast!</span>")
var/icon/new_holoform = user_interface_custom_holoform(client)
if(new_holoform)
client.prefs.custom_holoform_icon = new_holoform
client.prefs.cached_holoform_icons = null
client.prefs.last_custom_holoform = world.time
to_chat(src, "<span class='boldnotice'>Holoform set.</span>")
+151 -16
View File
@@ -58,10 +58,12 @@
var/canholo = TRUE
var/obj/item/card/id/access_card = null
var/chassis = "repairbot"
var/list/possible_chassis = list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE,
"fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE ,
"parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE ,
"fairy" = FALSE , "spiderbot" = FALSE) //assoc value is whether it can be picked up.
var/dynamic_chassis
var/dynamic_chassis_sit = FALSE //whether we're sitting instead of resting spritewise
var/dynamic_chassis_bellyup = FALSE //whether we're lying down bellyup
var/list/possible_chassis //initialized in initialize.
var/list/dynamic_chassis_icons //ditto.
var/list/chassis_pixel_offsets_x //stupid dogborgs
var/static/item_head_icon = 'icons/mob/pai_item_head.dmi'
var/static/item_lh_icon = 'icons/mob/pai_item_lh.dmi'
var/static/item_rh_icon = 'icons/mob/pai_item_rh.dmi'
@@ -69,22 +71,20 @@
var/emitterhealth = 20
var/emittermaxhealth = 20
var/emitterregen = 0.25
var/emitter_next_use = 0
var/emitter_emp_cd = 300
var/emittercd = 50
var/emitteroverloadcd = 100
var/emittersemicd = FALSE
var/overload_ventcrawl = 0
var/overload_bulletblock = 0 //Why is this a good idea?
var/overload_maxhealth = 0
var/radio_short = FALSE
var/radio_short_cooldown = 5 MINUTES
var/radio_short_timerid
canmove = FALSE
var/silent = FALSE
var/brightness_power = 5
/mob/living/silicon/pai/can_unbuckle()
return FALSE
/mob/living/silicon/pai/can_buckle()
return FALSE
var/icon/custom_holoform_icon
/mob/living/silicon/pai/Destroy()
if (loc != card)
@@ -118,6 +118,13 @@
pda.owner = text("[]", src)
pda.name = pda.owner + " (" + pda.ownjob + ")"
possible_chassis = typelist(NAMEOF(src, possible_chassis), list("cat" = TRUE, "mouse" = TRUE, "monkey" = TRUE, "corgi" = FALSE,
"fox" = FALSE, "repairbot" = TRUE, "rabbit" = TRUE, "borgi" = FALSE ,
"parrot" = FALSE, "bear" = FALSE , "mushroom" = FALSE, "crow" = FALSE ,
"fairy" = FALSE , "spiderbot" = FALSE)) //assoc value is whether it can be picked up.
dynamic_chassis_icons = typelist(NAMEOF(src, dynamic_chassis_icons), initialize_dynamic_chassis_icons())
chassis_pixel_offsets_x = typelist(NAMEOF(src, chassis_pixel_offsets_x), default_chassis_pixel_offsets_x())
. = ..()
var/datum/action/innate/pai/software/SW = new
@@ -133,8 +140,7 @@
AR.Grant(src)
AL.Grant(src)
ALM.Grant(src)
emittersemicd = TRUE
addtimer(CALLBACK(src, .proc/emittercool), 600)
emitter_next_use = world.time + 10 SECONDS
/mob/living/silicon/pai/Life()
if(hacking)
@@ -228,7 +234,7 @@
/datum/action/innate/pai/shell/Trigger()
..()
if(P.holoform)
P.fold_in(0)
P.fold_in(FALSE)
else
P.fold_out()
@@ -292,3 +298,132 @@
/mob/living/silicon/pai/process()
emitterhealth = CLAMP((emitterhealth + emitterregen), -50, emittermaxhealth)
/mob/living/silicon/pai/proc/short_radio()
if(radio_short_timerid)
deltimer(radio_short_timerid)
radio_short = TRUE
to_chat(src, "<span class='danger'>Your radio shorts out!</span>")
radio_short_timerid = addtimer(CALLBACK(src, .proc/unshort_radio), radio_short_cooldown, flags = TIMER_STOPPABLE)
/mob/living/silicon/pai/proc/unshort_radio()
radio_short = FALSE
to_chat(src, "<span class='danger'>You feel your radio is operational once more.</span>")
if(radio_short_timerid)
deltimer(radio_short_timerid)
/mob/living/silicon/pai/proc/initialize_dynamic_chassis_icons()
. = list()
var/icon/curr //for inserts
//This is a horrible system and I wish I was not as lazy and did something smarter, like just generating a new icon in memory which is probably more efficient.
//Basic /tg/ cyborgs
.["Cyborg - Engineering (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "engineer"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Engineering (loaderborg)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "loaderborg"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Engineering (handyeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "handyeng"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Engineering (sleekeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleekeng"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Engineering (marinaeng)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinaeng"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Medical (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "medical"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Medical (marinamed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinamed"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Medical (eyebotmed)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "eyebotmed"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Security (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "sec"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Security (sleeksec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "sleeksec"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Security (marinasec)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/robots.dmi', "marinasec"), HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Clown (default)"] = process_holoform_icon_filter(icon('icons/mob/robots.dmi', "clown"), HOLOFORM_FILTER_PAI, FALSE)
//Citadel dogborgs
//Engi
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeeng-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Engineering (dog - valeeng)"] = curr
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "pupdozer-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Engineering (dog - pupdozer)"] = curr
//Med
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihound")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihound-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Medical (dog - medihound)"] = curr
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "medihounddark-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Medical (dog - medihounddark)"] = curr
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valemed")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valemed-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Medical (dog - valemed)"] = curr
//Sec
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Security (dog - k9)"] = curr
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "k9dark-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Security (dog - k9dark)"] = curr
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesec")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesec-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Security (dog - valesec)"] = curr
//Service
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeserv-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Service (dog - valeserv)"] = curr
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valeservdark-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Service (dog - valeservdark)"] = curr
//Sci
curr = icon('modular_citadel/icons/mob/widerobot.dmi', "valesci")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-rest"), "rest")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-sit"), "sit")
curr.Insert(icon('modular_citadel/icons/mob/widerobot.dmi', "valesci-bellyup"), "bellyup")
process_holoform_icon_filter(curr, HOLOFORM_FILTER_PAI, FALSE)
.["Cyborg - Science (dog - valesci)"] = curr
//Misc
.["Cyborg - Misc (dog - blade)"] = process_holoform_icon_filter(icon('modular_citadel/icons/mob/widerobot.dmi', "blade"), HOLOFORM_FILTER_PAI, FALSE)
/mob/living/silicon/pai/proc/default_chassis_pixel_offsets_x()
. = list()
//Engi
.["Cyborg - Engineering (dog - valeeng)"] = -16
.["Cyborg - Engineering (dog - pupdozer)"] = -16
//Med
.["Cyborg - Medical (dog - medihound)"] = -16
.["Cyborg - Medical (dog - medihounddark)"] = -16
.["Cyborg - Medical (dog - valemed)"] = -16
//Sec
.["Cyborg - Security (dog - k9)"] = -16
.["Cyborg - Security (dog - valesec)"] = -16
.["Cyborg - Security (dog - k9dark)"] = -16
//Service
.["Cyborg - Service (dog - valeserv)"] = -16
.["Cyborg - Service (dog - valeservdark)"] = -16
//Sci
.["Cyborg - Security (dog - valesci)"] = -16
//Misc
.["Cyborg - Misc (dog - blade)"] = -16
@@ -8,9 +8,10 @@
return
take_holo_damage(50/severity)
Knockdown(400/severity)
silent = max(30/severity, silent)
silent = max((3 MINUTES)/severity, silent)
if(holoform)
fold_in(force = TRUE)
emitter_next_use = world.time + emitter_emp_cd
//Need more effects that aren't instadeath or permanent law corruption.
/mob/living/silicon/pai/ex_act(severity, target)
@@ -12,12 +12,11 @@
. = fold_in(force)
return
if(emittersemicd)
if(world.time < emitter_next_use)
to_chat(src, "<span class='warning'>Error: Holochassis emitters recycling. Please try again later.</span>")
return FALSE
emittersemicd = TRUE
addtimer(CALLBACK(src, .proc/emittercool), emittercd)
emitter_next_use = world.time + emittercd
canmove = TRUE
density = TRUE
if(istype(card.loc, /obj/item/pda))
@@ -46,20 +45,17 @@
visible_message("<span class='boldnotice'>[src] folds out its holochassis emitter and forms a holoshell around itself!</span>")
holoform = TRUE
/mob/living/silicon/pai/proc/emittercool()
emittersemicd = FALSE
/mob/living/silicon/pai/proc/fold_in(force = FALSE)
emittersemicd = TRUE
if(!force)
addtimer(CALLBACK(src, .proc/emittercool), emittercd)
else
addtimer(CALLBACK(src, .proc/emittercool), emitteroverloadcd)
emitter_next_use = world.time + (force? emitteroverloadcd : emittercd)
icon_state = "[chassis]"
if(!holoform)
. = fold_out(force)
return
visible_message("<span class='notice'>[src] deactivates its holochassis emitter and folds back into a compact card!</span>")
if(force)
short_radio()
visible_message("<span class='warning'>[src] shorts out, collapsing back into their storage card, sparks emitted from their radio antenna!</span>")
else
visible_message("<span class='notice'>[src] deactivates its holochassis emitter and folds back into a compact card!</span>")
stop_pulling()
if(client)
client.perspective = EYE_PERSPECTIVE
@@ -78,28 +74,39 @@
if(!isturf(loc) && loc != card)
to_chat(src, "<span class='boldwarning'>You can not change your holochassis composite while not on the ground or in your card!</span>")
return FALSE
var/choice = input(src, "What would you like to use for your holochassis composite?") as null|anything in possible_chassis
if(!choice)
var/list/choices = list("Preset - Basic", "Preset - Dynamic")
if(CONFIG_GET(flag/pai_custom_holoforms))
choices += "Custom"
var/choicetype = input(src, "What type of chassis do you want to use?") as null|anything in choices
if(!choicetype)
return FALSE
chassis = choice
icon_state = "[chassis]"
if(resting)
icon_state = "[chassis]_rest"
switch(choicetype)
if("Custom")
chassis = "custom"
if("Preset - Basic")
var/choice = input(src, "What would you like to use for your holochassis composite?") as null|anything in possible_chassis
if(!choice)
return FALSE
chassis = choice
if("Preset - Dynamic")
var/choice = input(src, "What would you like to use for your holochassis composite?") as null|anything in dynamic_chassis_icons
if(!choice)
return FALSE
chassis = "dynamic"
dynamic_chassis = choice
resist_a_rest(FALSE, TRUE)
update_icon()
to_chat(src, "<span class='boldnotice'>You switch your holochassis projection composite to [chassis]</span>")
/mob/living/silicon/pai/lay_down()
..()
update_resting_icon(resting)
/mob/living/silicon/pai/proc/update_resting_icon(rest)
if(rest)
icon_state = "[chassis]_rest"
else
icon_state = "[chassis]"
. = ..()
if(loc != card)
visible_message("<span class='notice'>[src] [rest? "lays down for a moment..." : "perks up from the ground"]</span>")
visible_message("<span class='notice'>[src] [resting? "lays down for a moment..." : "perks up from the ground"]</span>")
update_icon()
/mob/living/silicon/pai/start_pulling(atom/movable/AM)
/mob/living/silicon/pai/start_pulling(atom/movable/AM, gs)
if(ispAI(AM))
return ..()
return FALSE
/mob/living/silicon/pai/proc/toggle_integrated_light()
@@ -122,3 +129,22 @@
to_chat(user, "<span class='warning'>[src]'s current form isn't able to be carried!</span>")
return FALSE
return ..()
/mob/living/silicon/pai/verb/toggle_chassis_sit()
set name = "Toggle Chassis Sit"
set category = "IC"
set desc = "Whether or not to try to use a sitting icon versus a resting icon. Takes priority over belly-up resting."
dynamic_chassis_sit = !dynamic_chassis_sit
to_chat(usr, "<span class='boldnotice'>You are now [dynamic_chassis_sit? "sitting" : "lying down"].</span>")
update_icon()
/mob/living/silicon/pai/verb/toggle_chassis_bellyup()
set name = "Toggle Chassis Belly Up"
set category = "IC"
set desc = "Whether or not to try to use a belly up icon while resting. Overridden by sitting."
dynamic_chassis_bellyup = !dynamic_chassis_bellyup
to_chat(usr, "<span class='boldnotice'>You are now lying on your [dynamic_chassis_bellyup? "back" : "front"].</span>")
update_icon()
/mob/living/silicon/pai/can_buckle_others(mob/living/target, atom/buckle_to)
return ispAI(target) && ..()
+8 -1
View File
@@ -5,4 +5,11 @@
..(message)
/mob/living/silicon/pai/binarycheck()
return 0
return FALSE
/mob/living/silicon/pai/radio(message, message_mode, list/spans, language)
if((message_mode == "robot") || (message_mode in GLOB.radiochannels))
if(radio_short)
to_chat(src, "<span class='warning'>Your radio is shorted out!</span>")
return ITALICS | REDUCE_RANGE
return ..()
@@ -283,7 +283,10 @@
dat += "<A href='byond://?src=[REF(src)];software=refresh'>Refresh</A><br>"
// Built-in
dat += "<A href='byond://?src=[REF(src)];software=directives'>Directives</A><br>"
dat += "<A href='byond://?src=[REF(src)];software=radio;sub=0'>Radio Configuration</A><br>"
if(radio_short)
dat += "\[RADIO SHORTED - Recalibrating!\]"
else
dat += "<A href='byond://?src=[REF(src)];software=radio;sub=0'>Radio Configuration</A><br>"
dat += "<A href='byond://?src=[REF(src)];software=image'>Screen Display</A><br>"
//dat += "Text Messaging <br>"
dat += "<br>"
@@ -0,0 +1,27 @@
/mob/living/silicon/pai/update_icon()
if(chassis == "custom") //Make sure custom exists if it's set to custom
custom_holoform_icon = client?.prefs?.get_filtered_holoform(HOLOFORM_FILTER_PAI)
if(!custom_holoform_icon)
chassis = pick(possible_chassis - "custom")
if(chassis == "dynamic") //handle dynamic generated icons
icon = dynamic_chassis_icons[dynamic_chassis]
var/list/states = icon_states(icon)
icon_state = ""
if(resting) //The next line is some bullshit but I can make it worse if you want and make it a single line instead of four.. :)
if(dynamic_chassis_sit && ("sit") in states)
icon_state = "sit"
else if(dynamic_chassis_bellyup && ("bellyup") in states)
icon_state = "bellyup"
else if("rest" in states)
icon_state = "rest"
rotate_on_lying = FALSE
else if(chassis == "custom")
icon = custom_holoform_icon
icon_state = ""
rotate_on_lying = TRUE
else
icon = initial(icon)
icon_state = "[chassis][resting? "_rest" : (stat == DEAD? "_dead" : "")]"
rotate_on_lying = FALSE
pixel_x = ((chassis == "dynamic") && chassis_pixel_offsets_x[dynamic_chassis]) || 0
update_transform()
@@ -1298,4 +1298,12 @@
/mob/living/silicon/robot/adjustStaminaLossBuffered(amount, updating_health = 1)
if(istype(cell))
cell.charge -= amount*5
cell.charge -= amount*5
/mob/living/silicon/robot/verb/viewmanifest()
set category = "Robot Commands"
set name = "View Crew Manifest"
if(usr.stat == DEAD)
return //won't work if dead
ai_roster()
@@ -389,6 +389,15 @@
if (aicamera)
return aicamera.selectpicture(user)
/mob/living/silicon/proc/ai_roster()
var/dat = "<html><head><title>Crew Roster</title></head><body><b>Crew Roster:</b><br><br>"
dat += GLOB.data_core.get_manifest()
dat += "</body></html>"
src << browse(dat, "window=airoster")
onclose(src, "airoster")
/mob/living/silicon/update_transform()
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/changed = 0
@@ -318,9 +318,9 @@
sleep(50)
if(mode == BOT_REPAIRING && src.loc == target_turf)
if(autotile) //Build the floor and include a tile.
target_turf.PlaceOnTop(/turf/open/floor/plasteel)
target_turf.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR)
else //Build a hull plating without a floor tile.
target_turf.PlaceOnTop(/turf/open/floor/plating)
target_turf.PlaceOnTop(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
else
var/turf/open/floor/F = target_turf
@@ -334,7 +334,7 @@
if(mode == BOT_REPAIRING && F && src.loc == F)
F.broken = 0
F.burnt = 0
F.PlaceOnTop(/turf/open/floor/plasteel)
F.PlaceOnTop(/turf/open/floor/plasteel, flags = CHANGETURF_INHERIT_AIR)
if(replacetiles && F.type != initial(tiletype.turf_type) && specialtiles && !isplatingturf(F))
anchored = TRUE
@@ -345,7 +345,7 @@
if(mode == BOT_REPAIRING && F && src.loc == F)
F.broken = 0
F.burnt = 0
F.PlaceOnTop(initial(tiletype.turf_type))
F.PlaceOnTop(initial(tiletype.turf_type), flags = CHANGETURF_INHERIT_AIR)
specialtiles -= 1
if(specialtiles == 0)
speak("Requesting refill of custom floortiles to continue replacing.")
@@ -486,10 +486,7 @@ Difficulty: Very Hard
if(isturf(Stuff))
var/turf/T = Stuff
if((isspaceturf(T) || isfloorturf(T)) && NewTerrainFloors)
var/turf/open/O = T.ChangeTurf(NewTerrainFloors)
if(O.air)
var/datum/gas_mixture/G = O.air
G.copy_from_turf(O)
var/turf/open/O = T.ChangeTurf(NewTerrainFloors, flags = CHANGETURF_INHERIT_AIR)
if(prob(florachance) && NewFlora.len && !is_blocked_turf(O, TRUE))
var/atom/Picked = pick(NewFlora)
new Picked(O)
@@ -91,5 +91,5 @@
visible_message("<span class='boldannounce'>The tendril falls inward, the ground around it widening into a yawning chasm!</span>")
for(var/turf/T in range(2,src))
if(!T.density)
T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland)
T.TerraformTurf(/turf/open/chasm/lavaland, /turf/open/chasm/lavaland, flags = CHANGETURF_INHERIT_AIR)
qdel(src)
+26
View File
@@ -0,0 +1,26 @@
//IMPORTANT: Multiple animate() calls do not stack well, so try to do them all at once if you can.
/mob/living/update_transform()
var/matrix/ntransform = matrix(transform) //aka transform.Copy()
var/final_pixel_y = pixel_y
var/final_dir = dir
var/changed = 0
if(lying != lying_prev && rotate_on_lying)
changed++
ntransform.TurnTo(lying_prev,lying)
if(lying == 0) //Lying to standing
final_pixel_y = get_standard_pixel_y_offset()
else //if(lying != 0)
if(lying_prev == 0) //Standing to lying
pixel_y = get_standard_pixel_y_offset()
final_pixel_y = get_standard_pixel_y_offset(lying)
if(dir & (EAST|WEST)) //Facing east or west
final_dir = pick(NORTH, SOUTH) //So you fall on your side rather than your face or ass
if(resize != RESIZE_DEFAULT_SIZE)
changed++
ntransform.Scale(resize)
resize = RESIZE_DEFAULT_SIZE
if(changed)
animate(src, transform = ntransform, time = 2, pixel_y = final_pixel_y, dir = final_dir, easing = EASE_IN|EASE_OUT)
setMovetype(movement_type & ~FLOATING) // If we were without gravity, the bouncing animation got stopped, so we make sure we restart it in next life().
+5 -2
View File
@@ -802,11 +802,14 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
//can the mob be buckled to something by default?
/mob/proc/can_buckle()
return 1
return TRUE
//can the mob be unbuckled from something by default?
/mob/proc/can_unbuckle()
return 1
return TRUE
/mob/proc/can_buckle_others(mob/living/target, atom/buckle_to)
return TRUE
//Can the mob interact() with an atom?
/mob/proc/can_interact_with(atom/A)
+1 -1
View File
@@ -93,7 +93,7 @@
/obj/item/projectile/magic/door/proc/CreateDoor(turf/T)
var/door_type = pick(door_types)
var/obj/structure/mineral_door/D = new door_type(T)
T.ChangeTurf(/turf/open/floor/plating)
T.ChangeTurf(/turf/open/floor/plating, flags = CHANGETURF_INHERIT_AIR)
D.Open()
/obj/item/projectile/magic/door/proc/OpenDoor(var/obj/machinery/door/D)
@@ -1870,133 +1870,82 @@
reagent_state = LIQUID
color = "#b51d05"
taste_description = "carpet" // Your tounge feels furry.
var/carpet_type = /turf/open/floor/carpet
/datum/reagent/carpet/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet)
F.PlaceOnTop(carpet_type, flags = CHANGETURF_INHERIT_AIR)
..()
/datum/reagent/carpet/black
name = "Liquid Black Carpet"
id = "blackcarpet"
color = "#363636"
/datum/reagent/carpet/black/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/black)
..()
carpet_type = /turf/open/floor/carpet/black
/datum/reagent/carpet/blackred
name = "Liquid Red Black Carpet"
id = "blackredcarpet"
color = "#342125"
/datum/reagent/carpet/blackred/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/blackred)
..()
carpet_type = /turf/open/floor/carpet/blackred
/datum/reagent/carpet/monochrome
name = "Liquid Monochrome Carpet"
id = "monochromecarpet"
color = "#b4b4b4"
/datum/reagent/carpet/monochrome/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/monochrome)
..()
carpet_type = /turf/open/floor/carpet/monochrome
/datum/reagent/carpet/blue
name = "Liquid Blue Carpet"
id = "bluecarpet"
color = "#1256ff"
/datum/reagent/carpet/blue/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/blue)
..()
carpet_type = /turf/open/floor/carpet/blue
/datum/reagent/carpet/cyan
name = "Liquid Cyan Carpet"
id = "cyancarpet"
color = "#3acfb9"
/datum/reagent/carpet/cyan/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/cyan)
..()
carpet_type = /turf/open/floor/carpet/cyan
/datum/reagent/carpet/green
name = "Liquid Green Carpet"
id = "greencarpet"
color = "#619b62"
/datum/reagent/carpet/green/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/green)
..()
carpet_type = /turf/open/floor/carpet/green
/datum/reagent/carpet/orange
name = "Liquid Orange Carpet"
id = "orangecarpet"
color = "#cc7900"
/datum/reagent/carpet/orange/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/orange)
..()
carpet_type = /turf/open/floor/carpet/orange
/datum/reagent/carpet/purple
name = "Liquid Purple Carpet"
id = "purplecarpet"
color = "#6d3392"
carpet_type = /turf/open/floor/carpet/purple
/datum/reagent/carpet/purple/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/purple)
..()
/datum/reagent/carpet/red
name = "Liquid Red Carpet"
id = "redcarpet"
color = "#871515"
carpet_type = /turf/open/floor/carpet/red
/datum/reagent/carpet/red/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/red)
..()
/datum/reagent/carpet/royalblack
name = "Liquid Royal Black Carpet"
id = "royalblackcarpet"
color = "#483d05"
carpet_type = /turf/open/floor/carpet/royalblack
/datum/reagent/carpet/royalblack/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/royalblack)
..()
/datum/reagent/carpet/royalblue
name = "Liquid Royal Blue Carpet"
id = "royalbluecarpet"
color = "#24227e"
/datum/reagent/carpet/royalblue/reaction_turf(turf/T, reac_volume)
if(isplatingturf(T) || istype(T, /turf/open/floor/plasteel))
var/turf/open/floor/F = T
F.PlaceOnTop(/turf/open/floor/carpet/royalblue)
..()
carpet_type = /turf/open/floor/carpet/royalblue
// Virology virus food chems.
@@ -53,7 +53,7 @@
if(isplatingturf(T))
var/turf/open/floor/plating/F = T
if(prob(10 + F.burnt + 5*F.broken)) //broken or burnt plating is more susceptible to being destroyed
F.ScrapeAway()
F.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
if(isfloorturf(T))
var/turf/open/floor/F = T
if(prob(reac_volume))
@@ -123,24 +123,11 @@ GLOBAL_LIST_INIT(food_reagents, build_reagents_to_food()) //reagentid = related
/datum/chemical_reaction/randomized/proc/LoadOldRecipe(recipe_data)
created = text2num(recipe_data["timestamp"])
var/req_reag = unwrap_reagent_list(recipe_data["required_reagents"])
if(!req_reag)
return FALSE
required_reagents = req_reag
var/req_catalysts = unwrap_reagent_list(recipe_data["required_catalysts"])
if(!req_catalysts)
return FALSE
required_catalysts = req_catalysts
required_reagents = SANITIZE_LIST(recipe_data["required_reagents"])
required_catalysts = SANITIZE_LIST(recipe_data["required_catalysts"])
required_temp = recipe_data["required_temp"]
is_cold_recipe = recipe_data["is_cold_recipe"]
var/temp_results = unwrap_reagent_list(recipe_data["results"])
if(!temp_results)
return FALSE
results = temp_results
results = SANITIZE_LIST(recipe_data["results"])
var/containerpath = text2path(recipe_data["required_container"])
if(!containerpath)
return FALSE
+1 -1
View File
@@ -31,7 +31,7 @@ GLOBAL_LIST_EMPTY(conveyors_by_id)
/obj/machinery/conveyor/inverted/Initialize(mapload)
. = ..()
if(mapload && !(dir in GLOB.diagonals))
log_game("### MAPPING ERROR: [src] at [AREACOORD(src)] spawned without using a diagonal dir. Please replace with a normal version.")
log_mapping("[src] at [AREACOORD(src)] spawned without using a diagonal dir. Please replace with a normal version.")
// Auto conveyour is always on unless unpowered
@@ -108,7 +108,7 @@
if (levels.len)
dest = locate(T.x, T.y, pick(levels))
T.ChangeTurf(/turf/open/chasm)
T.ChangeTurf(/turf/open/chasm, flags = CHANGETURF_INHERIT_AIR)
var/turf/open/chasm/C = T
C.set_target(dest)
C.drop(user)

Some files were not shown because too many files have changed in this diff Show More