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

This commit is contained in:
silicons
2021-01-08 00:33:57 -08:00
231 changed files with 8727 additions and 6166 deletions
+10 -2
View File
@@ -136,11 +136,15 @@
#define COMSIG_ATOM_ATTACK_HAND "atom_attack_hand" //from base of atom/attack_hand(): (mob/user)
#define COMSIG_ATOM_ATTACK_PAW "atom_attack_paw" //from base of atom/attack_paw(): (mob/user)
#define COMPONENT_NO_ATTACK_HAND 1 //works on all 3.
/////////////////
//This signal return value bitflags can be found in __DEFINES/misc.dm
#define COMSIG_ATOM_INTERCEPT_Z_FALL "movable_intercept_z_impact" //called for each movable in a turf contents on /turf/zImpact(): (atom/movable/A, levels)
/////////////////
/// Called from orbit component: (atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
#define COMSIG_ATOM_ORBIT_BEGIN "atom_orbit_begin"
/// Called from orbit component: (atom/movable/orbiter, refreshing)
#define COMSIG_ATOM_ORBIT_END "atom_orbit_end"
#define COMSIG_ENTER_AREA "enter_area" //from base of area/Entered(): (/area)
#define COMSIG_EXIT_AREA "exit_area" //from base of area/Exited(): (/area)
@@ -536,3 +540,7 @@
#define COMSIG_XENO_TURF_CLICK_SHIFT "xeno_turf_click_shift" //from turf ShiftClickOn(): (/mob)
#define COMSIG_XENO_TURF_CLICK_CTRL "xeno_turf_click_alt" //from turf AltClickOn(): (/mob)
#define COMSIG_XENO_MONKEY_CLICK_CTRL "xeno_monkey_click_ctrl" //from monkey CtrlClickOn(): (/mob)
// twitch plays
/// Returns direction: (wipe_votes)
#define COMSIG_TWITCH_PLAYS_MOVEMENT_DATA "twitch_plays_movement_data"
+3
View File
@@ -55,6 +55,9 @@
//donator items
#define LOADOUT_CATEGORY_DONATOR "Donator"
//unlockable items
#define LOADOUT_CATEGORY_UNLOCKABLE "Unlockable"
//how many prosthetics can we have
#define MAXIMUM_LOADOUT_PROSTHETICS 2
+1
View File
@@ -5,6 +5,7 @@
#define SPECIES_ANDROID "android"
#define SPECIES_ANGEL "angel"
#define SPECIES_MAMMAL "mammal"
#define SPECIES_MAMMAL_SYNTHETIC "mammal_synthetic"
#define SPECIES_ARACHNID "arachnid"
#define SPECIES_INSECT "insect"
#define SPECIES_DULLAHAN "dullahan"
+10 -10
View File
@@ -1,6 +1,6 @@
// tgstation-server DMAPI
#define TGS_DMAPI_VERSION "5.2.9"
#define TGS_DMAPI_VERSION "5.2.10"
// All functions and datums outside this document are subject to change with any version and should not be relied on.
@@ -67,7 +67,7 @@
#define TGS_EVENT_REPO_CHECKOUT 1
/// When the repository performs a fetch operation. No parameters
#define TGS_EVENT_REPO_FETCH 2
/// When the repository merges a pull request. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
/// When the repository test merges. Parameters: PR Number, PR Sha, (Nullable) Comment made by TGS user
#define TGS_EVENT_REPO_MERGE_PULL_REQUEST 3
/// Before the repository makes a sychronize operation. Parameters: Absolute repostiory path
#define TGS_EVENT_REPO_PRE_SYNCHRONIZE 4
@@ -190,21 +190,21 @@
/// Represents a merge of a GitHub pull request.
/datum/tgs_revision_information/test_merge
/// The pull request number.
/// The test merge number.
var/number
/// The pull request title when it was merged.
/// The test merge source's title when it was merged.
var/title
/// The pull request body when it was merged.
/// The test merge source's body when it was merged.
var/body
/// The GitHub username of the pull request's author.
/// The Username of the test merge source's author.
var/author
/// An http URL to the pull request.
/// An http URL to the test merge source.
var/url
/// The SHA of the pull request when that was merged.
/// The SHA of the test merge when that was merged.
var/pull_request_commit
/// ISO 8601 timestamp of when the pull request was merged.
/// ISO 8601 timestamp of when the test merge was created on TGS.
var/time_merged
/// (Nullable) Comment left by the TGS user who initiated the merge..
/// Optional comment left by the TGS user who initiated the merge.
var/comment
/// Represents a connected chat channel.
-4
View File
@@ -86,10 +86,6 @@
if (CONFIG_GET(flag/log_attack))
WRITE_LOG(GLOB.world_attack_log, "ATTACK: [text]")
/proc/log_wounded(text)
if (CONFIG_GET(flag/log_attack))
WRITE_LOG(GLOB.world_attack_log, "WOUND: [text]")
/proc/log_manifest(ckey, datum/mind/mind,mob/body, latejoin = FALSE)
if (CONFIG_GET(flag/log_manifest))
WRITE_LOG(GLOB.world_manifest_log, "[ckey] \\ [body.real_name] \\ [mind.assigned_role] \\ [mind.special_role ? mind.special_role : "NONE"] \\ [latejoin ? "LATEJOIN":"ROUNDSTART"]")
+8
View File
@@ -72,3 +72,11 @@
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(target_prefs, null, null, TRUE, TRUE)
//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)
+5 -2
View File
@@ -238,8 +238,11 @@
CHECK_TICK
SSdbcore.SetRoundEnd()
//Collects persistence features
if(mode.allow_persistence_save)
SSpersistence.CollectData()
if(mode.station_was_nuked)
SSpersistence.station_was_destroyed = TRUE
if(!mode.allow_persistence_save)
SSpersistence.station_persistence_save_disabled = TRUE
SSpersistence.CollectData()
//stop collecting feedback during grifftime
SSblackbox.Seal()
+1 -1
View File
@@ -275,7 +275,7 @@ GLOBAL_LIST_INIT(wisdoms, world.file2list("strings/wisdoms.txt"))
//LANGUAGE CHARACTER CUSTOMIZATION
GLOBAL_LIST_INIT(speech_verbs, list("default","says","gibbers", "states", "chitters", "chimpers", "declares", "bellows", "buzzes" ,"beeps", "chirps", "clicks", "hisses" ,"poofs" , "puffs", "rattles", "mewls" ,"barks", "blorbles", "squeaks", "squawks", "flutters", "warbles"))
GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/organ/tongue, "lizard tongue" = /obj/item/organ/tongue/lizard, "skeleton tongue" = /obj/item/organ/tongue/bone, "fly tongue" = /obj/item/organ/tongue/fly, "ipc tongue" = /obj/item/organ/tongue/robot/ipc))
GLOBAL_LIST_INIT(roundstart_tongues, list("default","human tongue" = /obj/item/organ/tongue, "lizard tongue" = /obj/item/organ/tongue/lizard, "skeleton tongue" = /obj/item/organ/tongue/bone, "fly tongue" = /obj/item/organ/tongue/fly, "ipc tongue" = /obj/item/organ/tongue/robot/ipc, "xeno tongue" = /obj/item/organ/tongue/alien))
//SPECIES BODYPART LISTS
//locked parts are those that your picked species requires to have
+2 -1
View File
@@ -9,5 +9,6 @@ GLOBAL_LIST_INIT(loadout_categories, list(
LOADOUT_CATEGORY_SHOES = LOADOUT_SUBCATEGORIES_NONE,
LOADOUT_CATEGORY_GLOVES = LOADOUT_SUBCATEGORIES_NONE,
LOADOUT_CATEGORY_GLASSES = LOADOUT_SUBCATEGORIES_NONE,
LOADOUT_CATEGORY_DONATOR = LOADOUT_SUBCATEGORIES_NONE
LOADOUT_CATEGORY_DONATOR = LOADOUT_SUBCATEGORIES_NONE,
LOADOUT_CATEGORY_UNLOCKABLE = LOADOUT_SUBCATEGORIES_NONE
))
@@ -566,3 +566,20 @@
config_entry_value = 0.333
min_val = 0
integer = FALSE
/// Amount of dirtyness tiles need to spawn dirt.
/datum/config_entry/number/turf_dirt_threshold
config_entry_value = 100
min_val = 1
integer = TRUE
/// Alpha dirt starts at
/datum/config_entry/number/dirt_alpha_starting
config_entry_value = 127
max_val = 255
min_val = 0
integer = TRUE
/// Dirtyness multiplier for making turfs dirty
/datum/config_entry/number/turf_dirty_multiplier
config_entry_value = 1
@@ -0,0 +1,20 @@
/// Whether or not to use the persistence system for cleanable objects
/datum/config_entry/flag/persistent_debris
config_entry_value = FALSE
/// Whether or not to nuke all roundstart debris that isn't due to persistence if the above is true
/datum/config_entry/flag/persistent_debris_only
config_entry_value = TRUE
/// Max amount of objects to store, total
/datum/config_entry/number/persistent_debris_global_max
config_entry_value = 10000
integer = TRUE
/// Max amount of objects to store per type
/datum/config_entry/number/persistent_debris_type_max
config_entry_value = 2000
integer = TRUE
/// Wipe dirty stuff on nuke
/datum/config_entry/flag/persistent_debris_wipe_on_nuke
+8 -7
View File
@@ -139,18 +139,22 @@ SUBSYSTEM_DEF(garbage)
lastlevel = level
for (var/refID in queue)
if (!refID)
//We do this rather then for(var/refID in queue) because that sort of for loop copies the whole list.
//Normally this isn't expensive, but the gc queue can grow to 40k items, and that gets costly/causes overrun.
for (var/i in 1 to length(queue))
var/list/L = queue[i]
if (length(L) < 2)
count++
if (MC_TICK_CHECK)
return
continue
var/GCd_at_time = queue[refID]
var/GCd_at_time = L[1]
if(GCd_at_time > cut_off_time)
break // Everything else is newer, skip them
count++
var/refID = L[2]
var/datum/D
D = locate(refID)
@@ -241,10 +245,7 @@ SUBSYSTEM_DEF(garbage)
D.gc_destroyed = gctime
var/list/queue = queues[level]
if (queue[refid])
queue -= refid // Removing any previous references that were GC'd so that the current object will be at the end of the list.
queue[refid] = gctime
queue[++queue.len] = list(gctime, refid) // not += for byond reasons
//this is mainly to separate things profile wise.
/datum/controller/subsystem/garbage/proc/HardDelete(datum/D)
+2
View File
@@ -698,6 +698,8 @@ SUBSYSTEM_DEF(job)
if(polychromic && istype(polychromic))
var/list/polychromic_entry = polychromic.colors_by_atom[I]
if(polychromic_entry)
if(polychromic.suits_with_helmet_typecache[I.type]) //is this one of those toggleable hood/helmet things?
polychromic.connect_helmet(I,i[LOADOUT_COLOR])
polychromic.colors_by_atom[I] = i[LOADOUT_COLOR]
I.update_icon()
else
+14
View File
@@ -41,6 +41,8 @@ SUBSYSTEM_DEF(mapping)
var/datum/space_level/transit
var/datum/space_level/empty_space
var/num_of_res_levels = 1
/// Lookup for zlevel to station z. text = num.
var/list/z_to_station_z_index
var/stat_map_name = "Loading..."
@@ -259,6 +261,16 @@ SUBSYSTEM_DEF(mapping)
INIT_ANNOUNCE("Loaded [name] in [(REALTIMEOFDAY - start_time)/10]s!")
return parsed_maps
/datum/controller/subsystem/mapping/proc/setup_station_z_index()
z_to_station_z_index = list()
var/sz = 1
var/cz = station_start
if(islist(config.map_file))
for(var/map in config.map_file)
z_to_station_z_index["[cz++]"] = sz++
else
z_to_station_z_index["[station_start]"] = 1
/datum/controller/subsystem/mapping/proc/loadWorld()
//if any of these fail, something has gone horribly, HORRIBLY, wrong
var/list/FailedZs = list()
@@ -271,6 +283,8 @@ SUBSYSTEM_DEF(mapping)
INIT_ANNOUNCE("Loading [config.map_name]...")
LoadGroup(FailedZs, "Station", config.map_path, config.map_file, config.traits, ZTRAITS_STATION, FALSE, config.orientation)
setup_station_z_index()
if(SSdbcore.Connect())
var/datum/DBQuery/query_round_map_name = SSdbcore.NewQuery("UPDATE [format_table_name("round")] SET map_name = '[config.map_name]' WHERE id = [GLOB.round_id]")
query_round_map_name.Execute()
@@ -5,18 +5,14 @@ SUBSYSTEM_DEF(persistence)
name = "Persistence"
init_order = INIT_ORDER_PERSISTENCE
flags = SS_NO_FIRE
var/list/satchel_blacklist = list() //this is a typecache
var/list/new_secret_satchels = list() //these are objects
var/list/old_secret_satchels = list()
/// Marks if the station got horribly destroyed
var/station_was_destroyed = FALSE
/// Marks if persistence save should be disabled
var/station_persistence_save_disabled = FALSE
var/list/obj/structure/chisel_message/chisel_messages = list()
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_storytellers = list("foo","bar","baz")
var/list/average_dynamic_threat = 50
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()
@@ -28,63 +24,82 @@ SUBSYSTEM_DEF(persistence)
var/list/paintings = list()
/datum/controller/subsystem/persistence/Initialize()
LoadSatchels()
LoadPoly()
LoadChiselMessages()
LoadTrophies()
LoadRecentModes()
LoadRecentStorytellers()
LoadRecentRulesets()
LoadRecentMaps()
LoadPhotoPersistence()
LoadServerPersistence()
LoadGamePersistence()
var/map_persistence_path = get_map_persistence_path()
if(map_persistence_path)
LoadMapPersistence()
return ..()
/**
* Gets the persistence path of the current map.
*/
/datum/controller/subsystem/persistence/proc/get_map_persistence_path()
ASSERT(SSmapping.config)
if(!SSmapping.config.persistence_key || (SSmapping.config.persistence_key == "NO_PERSIST"))
return null
return "data/persistence/[ckey(SSmapping.config.persistence_key)]"
/datum/controller/subsystem/persistence/proc/CollectData()
SaveServerPersistence()
if(station_persistence_save_disabled)
return
SaveGamePersistence()
var/map_persistence_path = get_map_persistence_path()
if(map_persistence_path)
SaveMapPersistence()
/**
* Loads persistent data relevant to the server: Configurations, past gamemodes, votes, antag rep, etc
*/
/datum/controller/subsystem/persistence/proc/LoadServerPersistence()
for(var/client/C in GLOB.clients)
LoadSavedVote(C.ckey)
if(CONFIG_GET(flag/use_antag_rep))
LoadAntagReputation()
LoadRandomizedRecipes()
LoadPanicBunker()
LoadPaintings()
return ..()
/datum/controller/subsystem/persistence/proc/LoadSatchels()
var/placed_satchel = 0
var/path
/**
* Saves persistent data relevant to the server: Configurations, past gamemodes, votes, antag rep, etc
*/
/datum/controller/subsystem/persistence/proc/SaveServerPersistence()
if(CONFIG_GET(flag/use_antag_rep))
CollectAntagReputation()
SaveRandomizedRecipes()
var/json_file = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
var/list/json = list()
if(fexists(json_file))
json = json_decode(file2text(json_file))
/**
* Loads persistent data relevant to the game in general: Photos, etc
*
* Legacy map persistence systems also use this.
*/
/datum/controller/subsystem/persistence/proc/LoadGamePersistence()
LoadChiselMessages()
LoadPhotoPersistence()
LoadPaintings()
old_secret_satchels = json["data"]
var/obj/item/storage/backpack/satchel/flat/F
if(old_secret_satchels && old_secret_satchels.len >= 10) //guards against low drop pools assuring that one player cannot reliably find his own gear.
var/pos = rand(1, old_secret_satchels.len)
F = new()
old_secret_satchels.Cut(pos, pos+1 % old_secret_satchels.len)
F.x = old_secret_satchels[pos]["x"]
F.y = old_secret_satchels[pos]["y"]
F.z = SSmapping.station_start
path = text2path(old_secret_satchels[pos]["saved_obj"])
/**
* Saves persistent data relevant to the game in general: Photos, etc
*
* Legacy map persistence systems also use this.
*/
/datum/controller/subsystem/persistence/proc/SaveGamePersistence()
CollectChiselMessages()
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
SavePaintings()
SaveScars()
if(F)
if(isfloorturf(F.loc) && !isplatingturf(F.loc))
F.hide(1)
if(ispath(path))
var/spawned_item = new path(F)
spawned_objects[spawned_item] = TRUE
placed_satchel++
var/free_satchels = 0
for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,SSmapping.station_start), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,SSmapping.station_start)))) //Nontrivially expensive but it's roundstart only
if(isfloorturf(T) && !isplatingturf(T))
new /obj/item/storage/backpack/satchel/flat/secret(T)
free_satchels++
if((free_satchels + placed_satchel) == 10) //ten tiles, more than enough to kill anything that moves
break
/**
* Loads persistent data relevant to the current map: Objects, etc.
*/
/datum/controller/subsystem/persistence/proc/LoadMapPersistence()
return
/datum/controller/subsystem/persistence/proc/LoadPoly()
for(var/mob/living/simple_animal/parrot/Poly/P in GLOB.alive_mob_list)
twitterize(P.speech_buffer, "polytalk")
break //Who's been duping the bird?!
/**
* Saves persistent data relevant to the current map: Objects, etc.
*/
/datum/controller/subsystem/persistence/proc/SaveMapPersistence()
return
/datum/controller/subsystem/persistence/proc/LoadChiselMessages()
var/list/saved_messages = list()
@@ -131,63 +146,6 @@ SUBSYSTEM_DEF(persistence)
log_world("Loaded [saved_messages.len] engraved messages on map [SSmapping.config.map_name]")
/datum/controller/subsystem/persistence/proc/LoadTrophies()
if(fexists("data/npc_saves/TrophyItems.sav")) //legacy compatability to convert old format to new
var/savefile/S = new /savefile("data/npc_saves/TrophyItems.sav")
var/saved_json
S >> saved_json
if(!saved_json)
return
saved_trophies = json_decode(saved_json)
fdel("data/npc_saves/TrophyItems.sav")
else
var/json_file = file("data/npc_saves/TrophyItems.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_trophies = json["data"]
SetUpTrophies(saved_trophies.Copy())
/datum/controller/subsystem/persistence/proc/LoadRecentModes()
var/json_file = file("data/RecentModes.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_modes = json["data"]
/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_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"]
if(saved_storytellers.len > 3)
average_dynamic_threat = saved_storytellers[4]
saved_storytellers.len = 3
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
var/json_file = file("data/RecentMaps.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_maps = json["maps"]
/datum/controller/subsystem/persistence/proc/LoadAntagReputation()
var/json = file2text(FILE_ANTAG_REP)
@@ -208,59 +166,6 @@ SUBSYSTEM_DEF(persistence)
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
if (T.showpiece)
continue
T.added_roundstart = TRUE
var/trophy_data = pick_n_take(trophy_items)
if(!islist(trophy_data))
continue
var/list/chosen_trophy = trophy_data
if(!chosen_trophy || isemptylist(chosen_trophy)) //Malformed
continue
var/path = text2path(chosen_trophy["path"]) //If the item no longer exist, this returns null
if(!path)
continue
T.showpiece = new /obj/item/showpiece_dummy(T, path)
T.trophy_message = chosen_trophy["message"]
T.placer_key = chosen_trophy["placer_key"]
T.update_icon()
/datum/controller/subsystem/persistence/proc/CollectData()
CollectChiselMessages()
CollectSecretSatchels()
CollectTrophies()
CollectRoundtype()
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
CollectStoryteller(mode)
CollectRulesets(mode)
RecordMaps()
SavePhotoPersistence() //THIS IS PERSISTENCE, NOT THE LOGGING PORTION.
if(CONFIG_GET(flag/use_antag_rep))
CollectAntagReputation()
SaveRandomizedRecipes()
SavePanicBunker()
SavePaintings()
SaveScars()
/datum/controller/subsystem/persistence/proc/LoadPanicBunker()
var/bunker_path = file("data/bunker_passthrough.json")
if(fexists(bunker_path))
var/list/json = json_decode(file2text(bunker_path))
GLOB.bunker_passthrough = json["data"]
for(var/ckey in GLOB.bunker_passthrough)
if(daysSince(GLOB.bunker_passthrough[ckey]) >= CONFIG_GET(number/max_bunker_days))
GLOB.bunker_passthrough -= ckey
/datum/controller/subsystem/persistence/proc/GetPhotoAlbums()
var/album_path = file("data/photo_albums.json")
if(fexists(album_path))
@@ -330,35 +235,6 @@ SUBSYSTEM_DEF(persistence)
WRITE_FILE(frame_path, frame_json)
/datum/controller/subsystem/persistence/proc/CollectSecretSatchels()
satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar))
var/list/satchels_to_add = list()
for(var/A in new_secret_satchels)
var/obj/item/storage/backpack/satchel/flat/F = A
if(QDELETED(F) || F.z != SSmapping.station_start || F.invisibility != INVISIBILITY_MAXIMUM)
continue
var/list/savable_obj = list()
for(var/obj/O in F)
if(is_type_in_typecache(O, satchel_blacklist) || (O.flags_1 & ADMIN_SPAWNED_1))
continue
if(O.persistence_replacement)
savable_obj += O.persistence_replacement
else
savable_obj += O.type
if(isemptylist(savable_obj))
continue
var/list/data = list()
data["x"] = F.x
data["y"] = F.y
data["saved_obj"] = pick(savable_obj)
satchels_to_add += list(data)
var/json_file = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
var/list/file_data = list()
fdel(json_file)
file_data["data"] = old_secret_satchels + satchels_to_add
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectChiselMessages()
var/json_file = file("data/npc_saves/ChiselMessages[SSmapping.config.map_name].json")
@@ -374,84 +250,6 @@ SUBSYSTEM_DEF(persistence)
/datum/controller/subsystem/persistence/proc/SaveChiselMessage(obj/structure/chisel_message/M)
saved_messages += list(M.pack()) // dm eats one list
/datum/controller/subsystem/persistence/proc/CollectTrophies()
var/json_file = file("data/npc_saves/TrophyItems.json")
var/list/file_data = list()
file_data["data"] = remove_duplicate_trophies(saved_trophies)
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/SavePanicBunker()
var/json_file = file("data/bunker_passthrough.json")
var/list/file_data = list()
file_data["data"] = GLOB.bunker_passthrough
fdel(json_file)
WRITE_FILE(json_file,json_encode(file_data))
/datum/controller/subsystem/persistence/proc/remove_duplicate_trophies(list/trophies)
var/list/ukeys = list()
. = list()
for(var/trophy in trophies)
var/tkey = "[trophy["path"]]-[trophy["message"]]"
if(ukeys[tkey])
continue
else
. += list(trophy)
ukeys[tkey] = TRUE
/datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T)
if(!T.added_roundstart && T.showpiece)
var/list/data = list()
data["path"] = T.showpiece.type
data["message"] = T.trophy_message
data["placer_key"] = T.placer_key
saved_trophies += list(data)
/datum/controller/subsystem/persistence/proc/CollectRoundtype()
saved_modes[3] = saved_modes[2]
saved_modes[2] = saved_modes[1]
saved_modes[1] = SSticker.mode.config_tag
var/json_file = file("data/RecentModes.json")
var/list/file_data = list()
file_data["data"] = saved_modes
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode)
saved_storytellers.len = 3
saved_storytellers[3] = saved_storytellers[2]
saved_storytellers[2] = saved_storytellers[1]
saved_storytellers[1] = mode.storyteller.name
average_dynamic_threat = (mode.max_threat + average_dynamic_threat) / 2
var/json_file = file("data/RecentStorytellers.json")
var/list/file_data = list()
file_data["data"] = saved_storytellers + average_dynamic_threat
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectRulesets(var/datum/game_mode/dynamic/mode)
saved_dynamic_rules[3] = saved_dynamic_rules[2]
saved_dynamic_rules[2] = saved_dynamic_rules[1]
saved_dynamic_rules[1] = list()
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
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/RecordMaps()
saved_maps = saved_maps?.len ? list("[SSmapping.config.map_name]") | saved_maps : list("[SSmapping.config.map_name]")
var/json_file = file("data/RecentMaps.json")
var/list/file_data = list()
file_data["maps"] = saved_maps
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectAntagReputation()
var/ANTAG_REP_MAXIMUM = CONFIG_GET(number/antag_rep_maximum)
@@ -0,0 +1,177 @@
/**
* Persistence for cleanable debris.
*/
/datum/controller/subsystem/persistence
/// tracks if we already loaded debris. Unlike everything else, this can actually be a major problem if some badmin procs it twice.
var/loaded_debris = FALSE
/datum/controller/subsystem/persistence/LoadMapPersistence()
. = ..()
if(CONFIG_GET(flag/persistent_debris))
LoadMapDebris()
/datum/controller/subsystem/persistence/SaveMapPersistence()
. = ..()
if(CONFIG_GET(flag/persistent_debris))
SaveMapDebris()
/datum/controller/subsystem/persistence/proc/LoadMapDebris()
if(CONFIG_GET(flag/persistent_debris_only))
wipe_existing_debris()
if(!fexists("[get_map_persistence_path()]/debris.json"))
return
if(loaded_debris)
return
loaded_debris = TRUE
var/list/allowed_turf_typecache = typecacheof(/turf/open) - typecacheof(/turf/open/space)
var/list/allowed_z_cache = list()
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
allowed_z_cache[num2text(z)] = TRUE
var/list/data = json_decode(file2text("[get_map_persistence_path()]/debris.json"))
var/list/z_lookup = list()
var/loaded = 0
var/list/loaded_by_type = list()
var/nopath = 0
var/badloc = 0
var/noturf = 0
/// reverse it
for(var/z in SSmapping.z_to_station_z_index)
var/sz = SSmapping.z_to_station_z_index[z]
z_lookup[num2text(sz)] = text2num(z)
for(var/z in data)
var/actual_z = z_lookup[z]
var/list/L1 = data[z]
for(var/x in L1)
var/list/L2 = data[z][x]
for(var/y in L2)
var/turf/tile = locate(text2num(x), text2num(y), actual_z)
if(!tile)
noturf++
continue
var/list/objects = data[z][x][y]
for(var/_L in objects)
var/list/objdata
var/path
if(islist(_L))
objdata = _L
path = text2path(objdata["__PATH__"])
else
path = text2path(_L)
objdata = objects[_L]
if(!path)
nopath++
continue
if(!IsValidDebrisLocation(tile, allowed_turf_typecache, allowed_z_cache, path, TRUE))
badloc++
continue
var/obj/effect/decal/cleanable/instantiated = new path(tile)
loaded_by_type[path] += 1
loaded++
if(objdata)
instantiated.PersistenceLoad(objdata)
var/list/bytype = list()
for(var/path in loaded_by_type)
bytype += "[path] - [loaded_by_type[path]]"
subsystem_log(
{"Debris loading completed:
Errors:
No path: [nopath]
Invalid location: [badloc]
No turf on map: [noturf]
Total loaded: [loaded]
By type:
[bytype.Join("\n")]"}
)
/datum/controller/subsystem/persistence/proc/SaveMapDebris()
if(fexists("[get_map_persistence_path()]/debris.json"))
fdel("[get_map_persistence_path()]/debris.json")
if(CONFIG_GET(flag/persistent_debris_wipe_on_nuke) && station_was_destroyed)
return // local janitor cheers on nukeop team to save some work
var/list/data = list()
var/list/z_lookup = SSmapping.z_to_station_z_index
var/list/debris = RelevantPersistentDebris()
var/obj/effect/decal/cleanable/saving
var/global_max = CONFIG_GET(number/persistent_debris_global_max)
var/type_max = CONFIG_GET(number/persistent_debris_type_max)
var/stored = 0
var/list/stored_by_type = list()
for(var/i in debris)
saving = i
var/list/serializing = list()
var/path = saving.PersistenceSave(serializing)
if(!path)
continue
if(stored_by_type[path] > type_max)
continue
var/text_z = num2text(z_lookup[num2text(saving.z)])
var/text_y = num2text(saving.y)
var/text_x = num2text(saving.x)
LAZYINITLIST(data[text_z])
LAZYINITLIST(data[text_z][text_x])
LAZYINITLIST(data[text_z][text_x][text_y])
if(saving.persistence_allow_stacking)
serializing["__PATH__"] = path
data[text_z][text_x][text_y] += list(serializing)
else
data[text_z][text_x][text_y][path] = serializing
stored++
if(stored > global_max)
var/w = "Persistent debris saving globally aborted due to global max >= [global_max]. Either janitors never do their jobs or something is wrong."
message_admins(w)
subsystem_log(w)
return
stored_by_type[path] = stored_by_type[path]? stored_by_type[path] + 1 : 1
if(stored_by_type[path] > type_max)
var/w = "Persistent debris saving aborted for type [path] due to type max >= [global_max]. Either janitors never do their jobs or something is wrong."
message_admins(w)
subsystem_log(w)
var/list/bytype = list()
for(var/path in stored_by_type)
bytype += "[path] - [stored_by_type[path]]"
subsystem_log(
{"Debris saving completed:
Total: [stored]
By type:
[bytype.Join("\n")]"}
)
WRITE_FILE(file("[get_map_persistence_path()]/debris.json"), json_encode(data))
/datum/controller/subsystem/persistence/proc/IsValidDebrisLocation(turf/tile, list/allowed_typecache, list/allowed_zcache, obj/effect/decal/cleanable/type, loading = FALSE)
if(!allowed_typecache[tile.type])
return FALSE
var/area/A = tile.loc
if(!A.persistent_debris_allowed)
return FALSE
if(!allowed_zcache[num2text(tile.z)])
return FALSE
if(loading)
if(!initial(type.persistence_allow_stacking))
var/obj/effect/decal/cleanable/C = locate(type) in tile
if(!QDELETED(C))
return FALSE
// Saving verifies allow stacking in the save proc.
for(var/obj/structure/window/W in tile)
if(W.fulltile)
return FALSE
return TRUE
/datum/controller/subsystem/persistence/proc/wipe_existing_debris()
var/list/existing = RelevantPersistentDebris()
QDEL_LIST(existing)
/datum/controller/subsystem/persistence/proc/RelevantPersistentDebris()
var/list/allowed_turf_typecache = typecacheof(/turf/open) - typecacheof(/turf/open/space)
var/list/allowed_z_cache = list()
for(var/z in SSmapping.levels_by_trait(ZTRAIT_STATION))
allowed_z_cache[num2text(z)] = TRUE
. = list()
for(var/obj/effect/decal/cleanable/C in world)
if(!C.loc || QDELETED(C))
continue
if(!C.persistent)
continue
if(!IsValidDebrisLocation(C.loc, allowed_turf_typecache, allowed_z_cache, C.type, FALSE))
continue
. += C
@@ -0,0 +1,26 @@
/**
* Persists panic bunker whitelisting for a configured period of time
*/
/datum/controller/subsystem/persistence/LoadServerPersistence()
. = ..()
LoadPanicBunker()
/datum/controller/subsystem/persistence/SaveServerPersistence()
. = ..()
SavePanicBunker()
/datum/controller/subsystem/persistence/proc/LoadPanicBunker()
var/bunker_path = file("data/bunker_passthrough.json")
if(fexists(bunker_path))
var/list/json = json_decode(file2text(bunker_path))
GLOB.bunker_passthrough = json["data"]
for(var/ckey in GLOB.bunker_passthrough)
if(daysSince(GLOB.bunker_passthrough[ckey]) >= CONFIG_GET(number/max_bunker_days))
GLOB.bunker_passthrough -= ckey
/datum/controller/subsystem/persistence/proc/SavePanicBunker()
var/json_file = file("data/bunker_passthrough.json")
var/list/file_data = list()
file_data["data"] = GLOB.bunker_passthrough
fdel(json_file)
WRITE_FILE(json_file,json_encode(file_data))
@@ -0,0 +1,11 @@
/**
* Persists poly messages across rounds
*/
/datum/controller/subsystem/persistence/LoadGamePersistence()
. = ..()
LoadPoly()
/datum/controller/subsystem/persistence/proc/LoadPoly()
for(var/mob/living/simple_animal/parrot/Poly/P in GLOB.alive_mob_list)
twitterize(P.speech_buffer, "polytalk")
break //Who's been duping the bird?!
@@ -0,0 +1,107 @@
/**
* Stores recently played gamemodes, maps, etc.
*/
/datum/controller/subsystem/persistence
var/list/saved_modes = list(1,2,3)
var/list/saved_dynamic_rules = list(list(),list(),list())
var/list/saved_storytellers = list("foo","bar","baz")
var/list/average_dynamic_threat = 50
var/list/saved_maps
/datum/controller/subsystem/persistence/SaveServerPersistence()
. = ..()
CollectRoundtype()
if(istype(SSticker.mode, /datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
CollectStoryteller(mode)
CollectRulesets(mode)
RecordMaps()
/datum/controller/subsystem/persistence/LoadServerPersistence()
. = ..()
LoadRecentModes()
LoadRecentStorytellers()
LoadRecentRulesets()
LoadRecentMaps()
/datum/controller/subsystem/persistence/proc/CollectRoundtype()
saved_modes[3] = saved_modes[2]
saved_modes[2] = saved_modes[1]
saved_modes[1] = SSticker.mode.config_tag
var/json_file = file("data/RecentModes.json")
var/list/file_data = list()
file_data["data"] = saved_modes
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectStoryteller(var/datum/game_mode/dynamic/mode)
saved_storytellers.len = 3
saved_storytellers[3] = saved_storytellers[2]
saved_storytellers[2] = saved_storytellers[1]
saved_storytellers[1] = mode.storyteller.name
average_dynamic_threat = (mode.max_threat + average_dynamic_threat) / 2
var/json_file = file("data/RecentStorytellers.json")
var/list/file_data = list()
file_data["data"] = saved_storytellers + average_dynamic_threat
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/CollectRulesets(var/datum/game_mode/dynamic/mode)
saved_dynamic_rules[3] = saved_dynamic_rules[2]
saved_dynamic_rules[2] = saved_dynamic_rules[1]
saved_dynamic_rules[1] = list()
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
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/RecordMaps()
saved_maps = saved_maps?.len ? list("[SSmapping.config.map_name]") | saved_maps : list("[SSmapping.config.map_name]")
var/json_file = file("data/RecentMaps.json")
var/list/file_data = list()
file_data["maps"] = saved_maps
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/LoadRecentModes()
var/json_file = file("data/RecentModes.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_modes = json["data"]
/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_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"]
if(saved_storytellers.len > 3)
average_dynamic_threat = saved_storytellers[4]
saved_storytellers.len = 3
/datum/controller/subsystem/persistence/proc/LoadRecentMaps()
var/json_file = file("data/RecentMaps.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_maps = json["maps"]
@@ -0,0 +1,79 @@
/**
* Secret satchel persistence - allows storing of items in underfloor satchels that's loaded later.
*/
/datum/controller/subsystem/persistence
var/list/satchel_blacklist = list() //this is a typecache
var/list/new_secret_satchels = list() //these are objects
var/list/old_secret_satchels = list()
/datum/controller/subsystem/persistence/LoadGamePersistence()
. = ..()
LoadSatchels()
/datum/controller/subsystem/persistence/SaveGamePersistence()
. = ..()
CollectSecretSatchels()
/datum/controller/subsystem/persistence/proc/LoadSatchels()
var/placed_satchel = 0
var/path
var/json_file = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
var/list/json = list()
if(fexists(json_file))
json = json_decode(file2text(json_file))
old_secret_satchels = json["data"]
var/obj/item/storage/backpack/satchel/flat/F
if(old_secret_satchels && old_secret_satchels.len >= 10) //guards against low drop pools assuring that one player cannot reliably find his own gear.
var/pos = rand(1, old_secret_satchels.len)
F = new()
old_secret_satchels.Cut(pos, pos+1 % old_secret_satchels.len)
F.x = old_secret_satchels[pos]["x"]
F.y = old_secret_satchels[pos]["y"]
F.z = SSmapping.station_start
path = text2path(old_secret_satchels[pos]["saved_obj"])
if(F)
if(isfloorturf(F.loc) && !isplatingturf(F.loc))
F.hide(1)
if(ispath(path))
var/spawned_item = new path(F)
spawned_objects[spawned_item] = TRUE
placed_satchel++
var/free_satchels = 0
for(var/turf/T in shuffle(block(locate(TRANSITIONEDGE,TRANSITIONEDGE,SSmapping.station_start), locate(world.maxx-TRANSITIONEDGE,world.maxy-TRANSITIONEDGE,SSmapping.station_start)))) //Nontrivially expensive but it's roundstart only
if(isfloorturf(T) && !isplatingturf(T))
new /obj/item/storage/backpack/satchel/flat/secret(T)
free_satchels++
if((free_satchels + placed_satchel) == 10) //ten tiles, more than enough to kill anything that moves
break
/datum/controller/subsystem/persistence/proc/CollectSecretSatchels()
satchel_blacklist = typecacheof(list(/obj/item/stack/tile/plasteel, /obj/item/crowbar))
var/list/satchels_to_add = list()
for(var/A in new_secret_satchels)
var/obj/item/storage/backpack/satchel/flat/F = A
if(QDELETED(F) || F.z != SSmapping.station_start || F.invisibility != INVISIBILITY_MAXIMUM)
continue
var/list/savable_obj = list()
for(var/obj/O in F)
if(is_type_in_typecache(O, satchel_blacklist) || (O.flags_1 & ADMIN_SPAWNED_1))
continue
if(O.persistence_replacement)
savable_obj += O.persistence_replacement
else
savable_obj += O.type
if(isemptylist(savable_obj))
continue
var/list/data = list()
data["x"] = F.x
data["y"] = F.y
data["saved_obj"] = pick(savable_obj)
satchels_to_add += list(data)
var/json_file = file("data/npc_saves/SecretSatchels[SSmapping.config.map_name].json")
var/list/file_data = list()
fdel(json_file)
file_data["data"] = old_secret_satchels + satchels_to_add
WRITE_FILE(json_file, json_encode(file_data))
@@ -0,0 +1,84 @@
/**
* Stores trophies in curator display cases
*/
/datum/controller/subsystem/persistence
var/list/saved_trophies = list()
/datum/controller/subsystem/persistence/LoadGamePersistence()
. = ..()
LoadTrophies()
/datum/controller/subsystem/persistence/SaveGamePersistence()
. = ..()
CollectTrophies()
/datum/controller/subsystem/persistence/proc/LoadTrophies()
if(fexists("data/npc_saves/TrophyItems.sav")) //legacy compatability to convert old format to new
var/savefile/S = new /savefile("data/npc_saves/TrophyItems.sav")
var/saved_json
S >> saved_json
if(!saved_json)
return
saved_trophies = json_decode(saved_json)
fdel("data/npc_saves/TrophyItems.sav")
else
var/json_file = file("data/npc_saves/TrophyItems.json")
if(!fexists(json_file))
return
var/list/json = json_decode(file2text(json_file))
if(!json)
return
saved_trophies = json["data"]
SetUpTrophies(saved_trophies.Copy())
/datum/controller/subsystem/persistence/proc/SetUpTrophies(list/trophy_items)
for(var/A in GLOB.trophy_cases)
var/obj/structure/displaycase/trophy/T = A
if (T.showpiece)
continue
T.added_roundstart = TRUE
var/trophy_data = pick_n_take(trophy_items)
if(!islist(trophy_data))
continue
var/list/chosen_trophy = trophy_data
if(!chosen_trophy || isemptylist(chosen_trophy)) //Malformed
continue
var/path = text2path(chosen_trophy["path"]) //If the item no longer exist, this returns null
if(!path)
continue
T.showpiece = new /obj/item/showpiece_dummy(T, path)
T.trophy_message = chosen_trophy["message"]
T.placer_key = chosen_trophy["placer_key"]
T.update_icon()
/datum/controller/subsystem/persistence/proc/CollectTrophies()
var/json_file = file("data/npc_saves/TrophyItems.json")
var/list/file_data = list()
file_data["data"] = remove_duplicate_trophies(saved_trophies)
fdel(json_file)
WRITE_FILE(json_file, json_encode(file_data))
/datum/controller/subsystem/persistence/proc/remove_duplicate_trophies(list/trophies)
var/list/ukeys = list()
. = list()
for(var/trophy in trophies)
var/tkey = "[trophy["path"]]-[trophy["message"]]"
if(ukeys[tkey])
continue
else
. += list(trophy)
ukeys[tkey] = TRUE
/datum/controller/subsystem/persistence/proc/SaveTrophy(obj/structure/displaycase/trophy/T)
if(!T.added_roundstart && T.showpiece)
var/list/data = list()
data["path"] = T.showpiece.type
data["message"] = T.trophy_message
data["placer_key"] = T.placer_key
saved_trophies += list(data)
@@ -72,6 +72,52 @@
category = CAT_WEAPONRY
subcategory = CAT_MELEE
/datum/crafting_recipe/bokken
name = "Training Bokken"
result = /obj/item/melee/bokken
tools = list(TOOL_SCREWDRIVER)
reqs = list(/obj/item/bokken_blade = 1,
/obj/item/bokken_hilt = 1,
/obj/item/stack/sheet/cloth = 2,
/obj/item/stack/sheet/leather = 1)
time = 60
category = CAT_WEAPONRY
subcategory = CAT_MELEE
/datum/crafting_recipe/bokken_steelwood
name = "Training Bokken"
result = /obj/item/melee/bokken/steelwood
tools = list(TOOL_SCREWDRIVER)
reqs = list(/obj/item/bokken_steelblade = 1,
/obj/item/bokken_hilt = 1,
/obj/item/stack/sheet/cloth = 2,
/obj/item/stack/sheet/leather = 1)
time = 60
category = CAT_WEAPONRY
subcategory = CAT_MELEE
/datum/crafting_recipe/wakibokken
name = "Training Wakizashi Bokken"
result = /obj/item/melee/bokken/waki
tools = list(TOOL_SCREWDRIVER)
reqs = list(/obj/item/wakibokken_blade = 1,
/obj/item/bokken_hilt = 1,
/obj/item/stack/sheet/cloth = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
/datum/crafting_recipe/wakibokken_steelwood
name = "Training Wakizashi Steelwood Bokken"
result = /obj/item/melee/bokken/waki/steelwood
tools = list(TOOL_SCREWDRIVER)
reqs = list(/obj/item/wakibokken_steelblade = 1,
/obj/item/bokken_hilt = 1,
/obj/item/stack/sheet/cloth = 1)
time = 40
category = CAT_WEAPONRY
subcategory = CAT_MELEE
/datum/crafting_recipe/bola
name = "Bola"
result = /obj/item/restraints/legcuffs/bola
@@ -434,3 +480,51 @@
time = 20
category = CAT_WEAPONRY
subcategory = CAT_PARTS
// BOKKEN CRAFTING
/datum/crafting_recipe/bokken_blade
name = "Training Bokken Blade"
result = /obj/item/bokken_blade
tools = list(/obj/item/hatchet)
reqs = list(/obj/item/stack/sheet/mineral/wood = 5)
time = 20
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/wakibokken_blade
name = "Training Wakizashi Bokken Blade"
result = /obj/item/wakibokken_blade
tools = list(/obj/item/hatchet)
reqs = list(/obj/item/stack/sheet/mineral/wood = 2)
time = 20
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/bokken_steelblade
name = "Training Ironwood Bokken Blade"
result = /obj/item/bokken_steelblade
tools = list(/obj/item/hatchet, TOOL_WELDER)
reqs = list(/obj/item/grown/log/steel = 2)
time = 20
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/wakibokken_blade
name = "Training Wakizashi Ironwood Bokken Blade"
result = /obj/item/wakibokken_steelblade
tools = list(/obj/item/hatchet, TOOL_WELDER)
reqs = list(/obj/item/grown/log/steel = 1)
time = 20
category = CAT_WEAPONRY
subcategory = CAT_PARTS
/datum/crafting_recipe/bokken_hilt
name = "Training Bokken hilt"
result = /obj/item/bokken_hilt
tools = list(/obj/item/hatchet)
reqs = list(/obj/item/stack/sheet/mineral/wood = 5,
/obj/item/stack/sheet/cloth = 2)
time = 20
category = CAT_WEAPONRY
subcategory = CAT_PARTS
+2
View File
@@ -54,6 +54,7 @@
move_react()
/datum/component/orbiter/proc/begin_orbit(atom/movable/orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_BEGIN, orbiter, radius, clockwise, rotation_speed, rotation_segments, pre_rotation)
if(orbiter.orbiting)
if(orbiter.orbiting == src)
orbiter.orbiting.end_orbit(orbiter, TRUE)
@@ -87,6 +88,7 @@
/datum/component/orbiter/proc/end_orbit(atom/movable/orbiter, refreshing=FALSE)
if(!orbiters[orbiter])
return
SEND_SIGNAL(parent, COMSIG_ATOM_ORBIT_END, orbiter, refreshing)
UnregisterSignal(orbiter, COMSIG_MOVABLE_MOVED)
orbiter.SpinAnimation(0, 0)
if(istype(orbiters[orbiter],/matrix)) //This is ugly.
+2 -2
View File
@@ -133,7 +133,7 @@
//KEYS
/datum/component/riding/proc/keycheck(mob/user)
return !keytype || user.is_holding_item_of_type(keytype)
return !keytype || user?.is_holding_item_of_type(keytype)
//BUCKLE HOOKS
/datum/component/riding/proc/restore_position(mob/living/buckled_mob)
@@ -153,7 +153,7 @@
/datum/component/riding/proc/handle_ride(mob/user, direction)
var/atom/movable/AM = parent
if(user.incapacitated())
if(user && user.incapacitated())
Unbuckle(user)
return
if(world.time < last_vehicle_move + ((last_move_diagonal? 2 : 1) * vehicle_move_delay))
+104
View File
@@ -0,0 +1,104 @@
/**
* Observers voting on things through orbiting
*/
/datum/component/twitch_plays
/// Observers
var/list/mob/players = list()
/datum/component/twitch_plays/Initialize(...)
. = ..()
if(!isatom(parent))
return COMPONENT_INCOMPATIBLE
RegisterSignal(parent, COMSIG_ATOM_ORBIT_BEGIN, .proc/on_start_orbit)
RegisterSignal(parent, COMSIG_ATOM_ORBIT_END, .proc/on_end_orbit)
/datum/component/twitch_plays/Destroy(force, silent)
for(var/i in players)
DetachPlayer(i)
return ..()
/datum/component/twitch_plays/proc/on_start_orbit(datum/source, atom/movable/orbiter)
if(!isobserver(orbiter))
return
AttachPlayer(orbiter)
/datum/component/twitch_plays/proc/on_end_orbit(datum/source, atom/movable/orbiter)
if(!(orbiter in players))
return
DetachPlayer(orbiter)
/datum/component/twitch_plays/proc/AttachPlayer(mob/dead/observer)
players |= observer
RegisterSignal(observer, COMSIG_PARENT_QDELETING, .proc/on_end_orbit)
/datum/component/twitch_plays/proc/DetachPlayer(mob/dead/observer)
players -= observer
UnregisterSignal(observer, COMSIG_PARENT_QDELETING)
/// Simple movement one
/datum/component/twitch_plays/simple_movement
/// Movement votes by observer
var/list/votes = list()
/// Allow diagonals
var/allow_diagonal = FALSE
/datum/component/twitch_plays/simple_movement/Initialize(...)
. = ..()
if(. & COMPONENT_INCOMPATIBLE)
return
RegisterSignal(parent, COMSIG_TWITCH_PLAYS_MOVEMENT_DATA, .proc/fetch_data)
/datum/component/twitch_plays/simple_movement/AttachPlayer(mob/dead/observer)
. = ..()
RegisterSignal(observer, COMSIG_MOVABLE_PRE_MOVE, .proc/pre_move)
/datum/component/twitch_plays/simple_movement/DetachPlayer(mob/dead/observer)
. = ..()
UnregisterSignal(observer, COMSIG_MOVABLE_PRE_MOVE)
/datum/component/twitch_plays/simple_movement/proc/pre_move(datum/source, turf/newLoc)
if(get_dist(newLoc, parent) > 1) // they're trying to escape orbit
return
. = COMPONENT_MOVABLE_BLOCK_PRE_MOVE
var/dir = get_dir(parent, newLoc)
if(!dir)
return
if(allow_diagonal || !((dir - 1) & dir))
votes[source] = dir
else // pick one or the other
votes[source] = prob(50)? (dir & ~(dir - 1)) : (dir & (dir - 1))
/datum/component/twitch_plays/simple_movement/proc/fetch_data(datum/source, wipe_votes)
if(!votes.len)
return
var/list/total = list(TEXT_NORTH, TEXT_SOUTH, TEXT_EAST, TEXT_WEST)
for(var/i in votes)
total[num2text(votes[i])] += 1
. = text2num(pickweight(total, 0))
if(wipe_votes)
votes.len = 0
/datum/component/twitch_plays/simple_movement/auto
var/move_delay = 2
var/last_move = 0
/datum/component/twitch_plays/simple_movement/auto/Initialize(...)
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
. = ..()
if(. & COMPONENT_INCOMPATIBLE)
return
START_PROCESSING(SSfastprocess, src)
/datum/component/twitch_plays/simple_movement/auto/Destroy(force, silent)
STOP_PROCESSING(SSfastprocess, src)
return ..()
/datum/component/twitch_plays/simple_movement/auto/process()
var/dir = fetch_data(null, TRUE)
if(!dir)
return
if(world.time < (last_move + move_delay))
return
last_move = world.time
step(parent, dir)
+9
View File
@@ -165,6 +165,15 @@
/datum/element/polychromic/proc/on_examine(atom/source, mob/user, list/examine_list)
examine_list += "<span class='notice'>Alt-click to recolor it.</span>"
/datum/element/polychromic/proc/connect_helmet(atom/I, var/applycolor)
if(isitem(I))
if(istype(I,/obj/item/clothing/suit/hooded))
var/obj/item/clothing/suit/hooded/Isuit = I
colors_by_atom[Isuit.hood] = applycolor
else if(istype(I,/obj/item/clothing/suit/space/hardsuit))
var/obj/item/clothing/suit/space/hardsuit/Isuit = I
colors_by_atom[Isuit.helmet] = applycolor
/datum/element/polychromic/proc/register_helmet(atom/source, obj/item/clothing/head/H)
if(!isitem(H)) //backup in case if it messes up somehow
if(istype(source,/obj/item/clothing/suit/hooded)) //so how come it be like this, where toggleable headslots are named separately (helmet/hood) anyways?
+9
View File
@@ -85,6 +85,15 @@
var/mob/M = teleatom
M.cancel_camera()
var/static/list/bread_cache = typecacheof(/obj/item/reagent_containers/food/snacks/store/bread)
var/list/breadlist = typecache_filter_list(teleatom.GetAllContents(), bread_cache)
if(breadlist.len && (channel == TELEPORT_CHANNEL_BLUESPACE || channel == TELEPORT_CHANNEL_QUANTUM))
for(var/obj/item/reagent_containers/food/snacks/store/bread/bread in breadlist)
bread.bread_teleport()
else if(istype(teleatom, /obj/item/reagent_containers/food/snacks/store/bread))
var/obj/item/reagent_containers/food/snacks/store/bread/bread = teleatom
bread.bread_teleport()
return TRUE
/proc/tele_play_specials(atom/movable/teleatom, atom/location, datum/effect_system/effect, sound)
+1
View File
@@ -7,6 +7,7 @@
id = MARTIALART_SLEEPINGCARP
allow_temp_override = FALSE
help_verb = /mob/living/carbon/human/proc/sleeping_carp_help
block_parry_data = /datum/block_parry_data/sleeping_carp
pugilist = TRUE
/datum/martial_art/the_sleeping_carp/proc/check_streak(mob/living/carbon/human/A, mob/living/carbon/human/D)
+127 -100
View File
@@ -41,6 +41,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
power_environ = FALSE
valid_territory = FALSE
outdoors = TRUE
persistent_debris_allowed = FALSE
ambientsounds = SPACE
blob_allowed = FALSE //Eating up space doesn't count for victory as a blob.
considered_hull_exterior = TRUE
@@ -329,10 +330,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Service Hallway"
icon_state = "hall_service"
/area/hallway/secondary/civilian
name = "Civilian Wing"
icon_state = "hallFS"
//Command
@@ -484,6 +481,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
sound_environment = SOUND_AREA_SMALL_SOFTFLOOR
/area/crew_quarters/arcade
name = "Arcade"
icon_state = "abandoned_g_den"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness
name = "Fitness Room"
icon_state = "fitness"
@@ -494,6 +496,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "fitness"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness/pool
name = "Pool Area"
icon_state = "pool"
/area/crew_quarters/cafeteria
name = "Cafeteria"
icon_state = "cafeteria"
@@ -798,6 +804,11 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
ambientsounds = MEDICAL
sound_environment = SOUND_AREA_STANDARD_STATION
/area/medical/clinic
name = "Clinic"
icon_state = "medbay3"
ambientsounds = MEDICAL
/area/medical/abandoned
name = "Abandoned Medbay"
icon_state = "medbay3"
@@ -1078,11 +1089,20 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
flags_1 = NONE
sound_environment = SOUND_AREA_SMALL_ENCLOSED
/area/janitor/aux
name = "Auxiliary Custodial Closet"
icon_state = "janitor"
flags_1 = NONE
/area/hydroponics
name = "Hydroponics"
icon_state = "hydro"
sound_environment = SOUND_AREA_STANDARD_STATION
/area/hydroponics/lobby
name = "Hydroponics Lobby"
icon_state = "hydro"
/area/hydroponics/garden
name = "Garden"
icon_state = "garden"
@@ -1103,6 +1123,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "toxlab"
sound_environment = SOUND_AREA_STANDARD_STATION
/area/science/observatory
name = "Research Observatory"
icon_state = "toxmisc"
/area/science/lab
name = "Research and Development"
icon_state = "toxlab"
@@ -1145,6 +1169,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Research Division Server Room"
icon_state = "server"
/area/science/server/compcore
name = "Computer Core"
icon_state = "server"
/area/science/explab
name = "Experimentation Lab"
icon_state = "toxmisc"
@@ -1234,6 +1262,10 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
icon_state = "storage"
clockwork_warp_allowed = FALSE
/area/storage/emergency
name = "Emergency Storage"
icon_state = "emergencystorage"
/area/storage/emergency/starboard
name = "Starboard Emergency Storage"
icon_state = "emergencystorage"
@@ -1452,100 +1484,6 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Telecommunications Satellite Lounge"
icon_state = "tcomsatlounge"
/area/crew_quarters/fitness/pool
name = "Pool Area"
icon_state = "pool"
/area/crew_quarters/dorms/blue
name = "Blue Dorms"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/dorms/purple
name = "Purple Dorms"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/lounge/jazz
name = "Jazz Lounge"
icon_state = "yellow"
ambientsounds = list('sound/ambience/ambidet1.ogg','sound/ambience/ambidet2.ogg')
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness/recreation
name = "Recreation Area"
icon_state = "fitness"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness/cogpool
name = "Pool"
icon_state = "fitness"
clockwork_warp_fail = "Pool's closed."
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/barbershop
name = "Barbershop"
icon_state = "blue"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/observatory
name = "Observatory"
icon_state = "Sleep"
//Engineering
/area/engine/teg_hot
name = "Hot Loop"
icon_state = "red"
/area/engine/teg_cold
name = "Cold Loop"
icon_state = "blue"
/area/engine/workshop
name = "Engineering Workshop"
icon_state = "engine"
/area/security/courtroom/jury
name = "Jury Room"
icon_state = "courtroom"
/area/quartermaster/miningdock/airless
name = "Mining Dock"
icon_state = "mining"
has_gravity = STANDARD_GRAVITY
valid_territory = FALSE
outdoors = TRUE
ambientsounds = SPACE
blob_allowed = FALSE //While part of the station, what good will it do you?
/area/quartermaster/miningdock/airless/no_grav
name = "Mining Dock"
icon_state = "mining"
dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
power_light = FALSE
power_equip = FALSE
power_environ = FALSE
valid_territory = FALSE
outdoors = TRUE
ambientsounds = SPACE
blob_allowed = FALSE //While part of the station, what good will it do you?
/area/janitor/aux
name = "Auxiliary Custodial Closet"
icon_state = "janitor"
flags_1 = NONE
/area/hydroponics/lobby
name = "Hydroponics Lobby"
icon_state = "hydro"
//Storage
/area/storage/emergency/generic
name = "Emergency Storage"
icon_state = "emergencystorage"
//Construction
@@ -1602,10 +1540,99 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
name = "Storage Wing"
icon_state = "storage_wing"
//Routers
//Areas that predominantly only apply to CogStation
/area/crew_quarters/dorms/blue
name = "Blue Dorms"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/dorms/purple
name = "Purple Dorms"
icon_state = "Sleep"
nightshift_public_area = NIGHTSHIFT_AREA_NONE
/area/crew_quarters/lounge/jazz
name = "Jazz Lounge"
icon_state = "yellow"
ambientsounds = list('sound/ambience/ambidet1.ogg','sound/ambience/ambidet2.ogg')
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/fitness/cogpool
name = "Pool"
icon_state = "fitness"
clockwork_warp_fail = "Pool's closed."
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/barbershop
name = "Barbershop"
icon_state = "blue"
nightshift_public_area = NIGHTSHIFT_AREA_RECREATION
/area/crew_quarters/observatory
name = "Observatory"
icon_state = "Sleep"
/area/hallway/secondary/civilian
name = "Civilian Wing"
icon_state = "hallFS"
/area/engine/atmos/aftair
name = "Aft Air Hookup"
icon_state = "atmos"
flags_1 = NONE
/area/engine/teg
name = "Thermo-Electric Generator"
icon_state = "engine"
/area/engine/teg/hotloop
name = "Hot Loop"
icon_state = "red"
/area/engine/teg/coldloop
name = "Cold Loop"
icon_state = "blue"
/area/engine/workshop
name = "Engineering Workshop"
icon_state = "engine"
/area/engine/substation
name = "Electrical Substation"
icon_state = "engine"
/area/security/courtroom/jury
name = "Jury Room"
icon_state = "courtroom"
/area/quartermaster/miningdock/airless
name = "Mining Dock"
icon_state = "mining"
has_gravity = STANDARD_GRAVITY
valid_territory = FALSE
outdoors = TRUE
ambientsounds = SPACE
blob_allowed = FALSE //While part of the station, what good will it do you?
/area/quartermaster/miningdock/airless/no_grav
name = "Mining Dock"
icon_state = "mining"
dynamic_lighting = DYNAMIC_LIGHTING_IFSTARLIGHT
power_light = FALSE
power_equip = FALSE
power_environ = FALSE
valid_territory = FALSE
outdoors = TRUE
ambientsounds = SPACE
blob_allowed = FALSE //While part of the station, what good will it do you?
//Routers (currently exclusive to CogStation)
/area/router
name = "Router"
name = "Routing Depot"
icon_state = "yellow"
ambientsounds = ENGINEERING
+4
View File
@@ -80,6 +80,10 @@
var/clockwork_warp_allowed = TRUE
/// Message to display when the clockwork warp fails
var/clockwork_warp_fail = "The structure there is too dense for warping to pierce. (This is normal in high-security areas.)"
/// Persistent debris alowed
var/persistent_debris_allowed = TRUE
/// Dirty flooring allowed
var/dirt_buildup_allowed = TRUE
/// If mining tunnel generation is allowed in this area
var/tunnel_allowed = FALSE
-8
View File
@@ -251,7 +251,6 @@
. = pulledby
pulledby = new_pulledby
/atom/movable/proc/Move_Pulled(atom/A)
if(!pulling)
return FALSE
@@ -271,13 +270,6 @@
pulling.Move(get_step(pulling.loc, move_dir), move_dir, glide_size)
return TRUE
/mob/living/Move_Pulled(atom/A)
. = ..()
if(!. || !isliving(A))
return
var/mob/living/L = A
set_pull_offsets(L, grab_state)
/atom/movable/proc/check_pulling()
if(pulling)
var/atom/movable/pullee = pulling
+6 -6
View File
@@ -106,15 +106,15 @@
dat += "<input type='hidden' name='matrix_paint' value='1'"
dat += "<br><br>"
dat += MATRIX_FIELD("rr", color_matrix_last[1])
dat += MATRIX_FIELD("rg", color_matrix_last[2])
dat += MATRIX_FIELD("rb", color_matrix_last[3])
dat += MATRIX_FIELD("gr", color_matrix_last[2])
dat += MATRIX_FIELD("br", color_matrix_last[3])
dat += "<br><br>"
dat += MATRIX_FIELD("gr", color_matrix_last[4])
dat += MATRIX_FIELD("rg", color_matrix_last[4])
dat += MATRIX_FIELD("gg", color_matrix_last[5])
dat += MATRIX_FIELD("gb", color_matrix_last[6])
dat += MATRIX_FIELD("bg", color_matrix_last[6])
dat += "<br><br>"
dat += MATRIX_FIELD("br", color_matrix_last[7])
dat += MATRIX_FIELD("bg", color_matrix_last[8])
dat += MATRIX_FIELD("rb", color_matrix_last[7])
dat += MATRIX_FIELD("gb", color_matrix_last[8])
dat += MATRIX_FIELD("bb", color_matrix_last[9])
dat += "<br><br>"
dat += MATRIX_FIELD("cr", color_matrix_last[10])
@@ -6,6 +6,7 @@
name = "gas sensor"
icon = 'icons/obj/stationobjs.dmi'
icon_state = "gsensor1"
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 100, "acid" = 0)
var/on = TRUE
@@ -774,6 +774,9 @@
stun_projectile_sound = 'sound/weapons/gunshot_smg.ogg'
armor = list("melee" = 50, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 80, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90)
/obj/machinery/porta_turret/syndicate/pod/toolbox
max_integrity = 100
/obj/machinery/porta_turret/syndicate/shuttle/target(atom/movable/target)
if(target)
setDir(get_dir(base, target))//even if you can't shoot, follow the target
+44 -35
View File
@@ -18,7 +18,7 @@
var/mode = HEATER_MODE_STANDBY
var/setMode = "auto" // Anything other than "heat" or "cool" is considered auto.
var/targetTemperature = T20C
var/heatingPower = 40000
var/heatingPower = 10000
var/efficiency = 20000
var/temperatureTolerance = 1
var/settableTemperatureMedian = 30 + T0C
@@ -74,46 +74,55 @@
if(cell && cell.charge > 0)
var/turf/L = loc
if(!istype(L))
if(mode != HEATER_MODE_STANDBY)
mode = HEATER_MODE_STANDBY
update_icon()
return
PerformHeating(L)
var/datum/gas_mixture/env = L.return_air()
for(var/direction in GLOB.alldirs)
L=get_step(src,direction)
if(!locate(/turf/closed) in L) // we don't want to heat walls and cause jank
PerformHeating(L)
var/newMode = HEATER_MODE_STANDBY
if(setMode != HEATER_MODE_COOL && env.return_temperature() < targetTemperature - temperatureTolerance)
newMode = HEATER_MODE_HEAT
else if(setMode != HEATER_MODE_HEAT && env.return_temperature() > targetTemperature + temperatureTolerance)
newMode = HEATER_MODE_COOL
if(mode != newMode)
mode = newMode
update_icon()
if(mode == HEATER_MODE_STANDBY)
return
var/heat_capacity = env.heat_capacity()
var/requiredPower = abs(env.return_temperature() - targetTemperature) * heat_capacity
requiredPower = min(requiredPower, heatingPower)
if(requiredPower < 1)
return
var/deltaTemperature = requiredPower / heat_capacity
if(mode == HEATER_MODE_COOL)
deltaTemperature *= -1
if(deltaTemperature)
env.set_temperature(env.return_temperature() + deltaTemperature)
air_update_turf()
cell.use(requiredPower / efficiency)
else
on = FALSE
update_icon()
return PROCESS_KILL
/obj/machinery/space_heater/proc/PerformHeating(turf/L)
if(!istype(L))
if(mode != HEATER_MODE_STANDBY)
mode = HEATER_MODE_STANDBY
update_icon()
return
var/datum/gas_mixture/env = L.return_air()
var/newMode = HEATER_MODE_STANDBY
if(setMode != HEATER_MODE_COOL && env.return_temperature() < targetTemperature - temperatureTolerance)
newMode = HEATER_MODE_HEAT
else if(setMode != HEATER_MODE_HEAT && env.return_temperature() > targetTemperature + temperatureTolerance)
newMode = HEATER_MODE_COOL
if(mode != newMode)
mode = newMode
update_icon()
if(mode == HEATER_MODE_STANDBY)
return
var/heat_capacity = env.heat_capacity()
var/requiredPower = abs(env.return_temperature() - targetTemperature) * heat_capacity
requiredPower = min(requiredPower, heatingPower)
if(requiredPower < 1)
return
var/deltaTemperature = requiredPower / heat_capacity
if(mode == HEATER_MODE_COOL)
deltaTemperature *= -1
if(deltaTemperature)
env.set_temperature(env.return_temperature() + deltaTemperature)
air_update_turf()
cell.use(requiredPower / efficiency)
/obj/machinery/space_heater/RefreshParts()
var/laser = 2
var/cap = 1
@@ -122,7 +131,7 @@
for(var/obj/item/stock_parts/capacitor/M in component_parts)
cap += M.rating
heatingPower = laser * 40000
heatingPower = laser * 10000
settableTemperatureRange = cap * 30
efficiency = (cap + 1) * 10000
+21 -2
View File
@@ -1,6 +1,13 @@
/obj/effect/decal/cleanable
gender = PLURAL
layer = ABOVE_NORMAL_TURF_LAYER
/// Is this kind of cleanable decal persistent
var/persistent = FALSE
/// Can we stack multiple in one tile?
var/persistence_allow_stacking = FALSE
/// Are we deleted by turf changes?
var/wiped_by_floor_change = FALSE
var/list/random_icon_states = null
var/blood_state = "" //I'm sorry but cleanable/blood code is ass, and so is blood_DNA
var/bloodiness = 0 //0-100, amount of blood in this decal, used for making footprints and affecting the alpha of bloody footprints
@@ -29,9 +36,21 @@
addtimer(CALLBACK(src, /datum.proc/_AddElement, list(/datum/element/beauty, beauty)), 0)
/**
* A data list is passed into this.
* This should return null to skip saving, or the type of data to save. Type must be /cleanable.
*/
/obj/effect/decal/cleanable/proc/PersistenceSave(list/data)
return type
/**
* Loads from a data list.
*/
/obj/effect/decal/cleanable/proc/PersistenceLoad(list/data)
return
/obj/effect/decal/cleanable/proc/replace_decal(obj/effect/decal/cleanable/C) // Returns true if we should give up in favor of the pre-existing decal
if(mergeable_decal)
qdel(C)
return mergeable_decal
/obj/effect/decal/cleanable/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/reagent_containers/glass) || istype(W, /obj/item/reagent_containers/food/drinks))
@@ -6,6 +6,7 @@
random_icon_states = list("gib1", "gib2", "gib3", "gib4", "gib5", "gib6")
mergeable_decal = FALSE
bloodiness = 0 //This isn't supposed to be bloody.
persistent = TRUE
var/body_colors = "#e3ba84" //a default color just in case.
var/gibs_reagent_id = /datum/reagent/liquidgibs
var/gibs_bloodtype = "A+"
@@ -31,6 +32,10 @@
flesh.color = body_colors
add_overlay(flesh)
/obj/effect/decal/cleanable/blood/gibs/PersistenceSave(list/data)
. = ..()
return /obj/effect/decal/cleanable/blood/gibs/old
/obj/effect/decal/cleanable/blood/gibs/ex_act(severity, target)
return
@@ -159,6 +164,10 @@
. = ..()
update_icon()
/obj/effect/decal/cleanable/blood/gibs/slime/PersistenceSave(list/data)
. = ..()
return type
/obj/effect/decal/cleanable/blood/gibs/slime/update_icon()
add_atom_colour(body_colors, FIXED_COLOUR_PRIORITY)
cut_overlays()
@@ -198,6 +207,10 @@
. = ..()
update_icon()
/obj/effect/decal/cleanable/blood/gibs/synth/PersistenceSave(list/data)
. = ..()
return type
//IPCs
/obj/effect/decal/cleanable/blood/gibs/ipc
desc = "They look sharp yet oozing."
@@ -209,6 +222,10 @@
. = ..()
update_icon()
/obj/effect/decal/cleanable/blood/gibs/ipc/PersistenceSave(list/data)
. = ..()
return type
/obj/effect/decal/cleanable/blood/gibs/ipc/update_icon()
add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY)
cut_overlays()
@@ -8,12 +8,18 @@
bloodiness = BLOOD_AMOUNT_PER_DECAL
color = BLOOD_COLOR_HUMAN //default so we don't have white splotches everywhere.
beauty = -100
persistent = TRUE
var/fixed_color = FALSE
/obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C)
if(C.fixed_color)
qdel(C)
return FALSE
if (C.blood_DNA)
blood_DNA |= C.blood_DNA
qdel(C)
update_icon()
..()
return FALSE
/obj/effect/decal/cleanable/blood/transfer_blood_dna()
..()
@@ -24,7 +30,22 @@
update_icon()
/obj/effect/decal/cleanable/blood/update_icon()
color = blood_DNA_to_color()
. = ..()
if(!fixed_color)
add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY)
/obj/effect/decal/cleanable/blood/PersistenceSave(list/data)
. = ..()
data["color"] = color
/obj/effect/decal/cleanable/blood/PersistenceLoad(list/data)
. = ..()
if(data["color"])
fixed_color = TRUE
add_atom_colour(data["color"], FIXED_COLOUR_PRIORITY)
name = "dried blood"
desc = "Looks like it's been here a while. Eew"
bloodiness = 0
/obj/effect/decal/cleanable/blood/old
name = "dried blood"
@@ -38,6 +59,7 @@
/obj/effect/decal/cleanable/blood/splats
random_icon_states = list("gibbl1", "gibbl2", "gibbl3", "gibbl4", "gibbl5")
persistence_allow_stacking = TRUE
/obj/effect/decal/cleanable/blood/splatter
random_icon_states = list("splatter1", "splatter2", "splatter3", "splatter4", "splatter5")
@@ -48,16 +70,43 @@
random_icon_states = null
beauty = -50
/obj/effect/decal/cleanable/blood/tracks/PersistenceSave(list/data)
. = ..()
data["dir"] = dir
/obj/effect/decal/cleanable/blood/tracks/PersistenceLoad(list/data)
. = ..()
if(data["dir"])
setDir(text2num(data["dir"]))
/obj/effect/decal/cleanable/trail_holder //not a child of blood on purpose
name = "blood"
icon_state = "ltrails_1"
desc = "Your instincts say you shouldn't be following these."
random_icon_states = null
beauty = -50
persistent = TRUE
persistence_allow_stacking = TRUE
var/list/existing_dirs = list()
var/fixed_color = FALSE
/obj/effect/decal/cleanable/trail_holder/PersistenceSave(list/data)
. = ..()
data["dir"] = dir
data["color"] = color
/obj/effect/decal/cleanable/trail_holder/PersistenceLoad(list/data)
. = ..()
if(data["dir"])
setDir(text2num(data["dir"]))
if(data["color"])
fixed_color = TRUE
add_atom_colour(data["color"], FIXED_COLOUR_PRIORITY)
/obj/effect/decal/cleanable/trail_holder/update_icon()
color = blood_DNA_to_color()
. = ..()
if(!fixed_color)
add_atom_colour(blood_DNA_to_color(), FIXED_COLOUR_PRIORITY)
/obj/effect/cleanable/trail_holder/Initialize()
. = ..()
@@ -84,8 +133,19 @@
var/entered_dirs = 0
var/exited_dirs = 0
blood_state = BLOOD_STATE_BLOOD //the icon state to load images from
persistent = TRUE
persistence_allow_stacking = TRUE
var/list/shoe_types = list()
/obj/effect/decal/cleanable/blood/footprints/PersistenceSave(list/data)
. = ..()
data["dir"] = dir
/obj/effect/decal/cleanable/blood/footprints/PersistenceLoad(list/data)
. = ..()
if(data["dir"])
setDir(text2num(data["dir"]))
/obj/effect/decal/cleanable/blood/footprints/Crossed(atom/movable/O)
if(ishuman(O))
var/mob/living/carbon/human/H = O
@@ -143,7 +203,7 @@
return
if(color != C.color)
return
..()
return ..()
/obj/effect/decal/cleanable/blood/footprints/can_bloodcrawl_in()
if((blood_state != BLOOD_STATE_OIL) && (blood_state != BLOOD_STATE_NOT_BLOODY))
@@ -12,6 +12,8 @@
icon_state = "ash"
mergeable_decal = FALSE
beauty = -50
persistent = TRUE
persistence_allow_stacking = TRUE
/obj/effect/decal/cleanable/ash/Initialize()
. = ..()
@@ -38,6 +40,8 @@
icon = 'icons/obj/shards.dmi'
icon_state = "tiny"
beauty = -100
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/glass/Initialize()
. = ..()
@@ -53,10 +57,33 @@
name = "dirt"
desc = "Someone should clean that up."
icon_state = "dirt"
alpha = 127
canSmoothWith = list(/obj/effect/decal/cleanable/dirt, /turf/closed/wall, /obj/structure/falsewall)
smooth = SMOOTH_FALSE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
beauty = -75
mergeable_decal = TRUE
persistent = TRUE
wiped_by_floor_change = TRUE
/obj/effect/decal/cleanable/dirt/Initialize(mapload)
. = ..()
alpha = CONFIG_GET(number/dirt_alpha_starting)
/obj/effect/decal/cleanable/dirt/proc/dirty(strength = 1)
if(alpha < 255)
alpha += strength
if(alpha > 255)
alpha = 255
/obj/effect/decal/cleanable/dirt/PersistenceSave(list/data)
. = ..()
data["alpha"] = alpha
/obj/effect/decal/cleanable/dirt/PersistenceLoad(list/data)
. = ..()
if(data["alpha"])
alpha = text2num(data["alpha"])
/obj/effect/decal/cleanable/dirt/Initialize()
. = ..()
@@ -94,6 +121,8 @@
light_color = LIGHT_COLOR_GREEN
icon_state = "greenglow"
beauty = -300
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/greenglow/Initialize(mapload)
. = ..()
@@ -122,6 +151,8 @@
icon_state = "molten"
mergeable_decal = FALSE
beauty = -150
persistent = TRUE
persistence_allow_stacking = TRUE
/obj/effect/decal/cleanable/molten_object/large
name = "big gooey grey mass"
@@ -136,6 +167,7 @@
icon_state = "vomit_1"
random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4")
beauty = -150
persistent = TRUE
/obj/effect/decal/cleanable/vomit/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(ishuman(user))
@@ -151,6 +183,10 @@
reagents.trans_to(H, reagents.total_volume)
qdel(src)
/obj/effect/decal/cleanable/vomit/PersistenceSave(list/data)
. = ..()
return /obj/effect/decal/cleanable/vomit/old
/obj/effect/decal/cleanable/vomit/old
name = "crusty dried vomit"
desc = "You try not to look at the chunks, and fail."
@@ -166,12 +202,16 @@
icon = 'icons/effects/tomatodecal.dmi'
random_icon_states = list("tomato_floor1", "tomato_floor2", "tomato_floor3")
beauty = -100
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/plant_smudge
name = "plant smudge"
gender = NEUTER
icon = 'icons/effects/tomatodecal.dmi'
random_icon_states = list("smashed_plant")
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/egg_smudge
name = "smashed egg"
@@ -179,6 +219,8 @@
gender = NEUTER
icon = 'icons/effects/tomatodecal.dmi'
random_icon_states = list("smashed_egg1", "smashed_egg2", "smashed_egg3")
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/pie_smudge //honk
name = "smashed pie"
@@ -186,6 +228,8 @@
gender = NEUTER
icon = 'icons/effects/tomatodecal.dmi'
random_icon_states = list("smashed_pie")
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/chem_pile
name = "chemical pile"
@@ -193,6 +237,8 @@
gender = NEUTER
icon = 'icons/obj/objects.dmi'
icon_state = "ash"
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/shreds
name = "shreds"
@@ -200,6 +246,8 @@
icon_state = "shreds"
gender = PLURAL
mergeable_decal = FALSE
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/shreds/ex_act(severity, target)
if(severity == 1) //so shreds created during an explosion aren't deleted by the explosion.
@@ -222,6 +270,8 @@
desc = "The herpes of arts and crafts."
icon = 'icons/effects/atmospherics.dmi'
gender = NEUTER
mergeable_decal = TRUE
persistent = TRUE
/obj/effect/decal/cleanable/glitter/pink
name = "pink glitter"
@@ -247,3 +297,5 @@
icon = 'icons/effects/blood.dmi'
icon_state = "xfloor1"
random_icon_states = list("xfloor1", "xfloor2", "xfloor3", "xfloor4", "xfloor5", "xfloor6", "xfloor7")
mergeable_decal = TRUE
persistent = TRUE
@@ -11,6 +11,7 @@
bloodiness = BLOOD_AMOUNT_PER_DECAL
mergeable_decal = FALSE
beauty = -50
persistent = TRUE
/obj/effect/decal/cleanable/robot_debris/Initialize(mapload, list/datum/disease/diseases)
. = ..()
@@ -52,6 +53,7 @@
blood_state = BLOOD_STATE_OIL
bloodiness = BLOOD_AMOUNT_PER_DECAL
beauty = -100
persistent = TRUE
/obj/effect/decal/cleanable/oil/Initialize()
. = ..()
@@ -61,8 +63,10 @@
/obj/effect/decal/cleanable/oil/streak
random_icon_states = list("streak1", "streak2", "streak3", "streak4", "streak5")
beauty = -50
persistent = TRUE
/obj/effect/decal/cleanable/oil/slippery
persistent = FALSE
/obj/effect/decal/cleanable/oil/slippery/Initialize()
. = ..()
@@ -5,6 +5,8 @@
icon_state = "rune1"
plane = ABOVE_WALL_PLANE //makes the graffiti visible over a wall.
gender = NEUTER
persistent = TRUE
persistence_allow_stacking = TRUE
mergeable_decal = FALSE
var/do_icon_rotate = TRUE
var/rotation = 0
@@ -29,3 +31,35 @@
if(main)
paint_colour = main
add_atom_colour(paint_colour, FIXED_COLOUR_PRIORITY)
/obj/effect/decal/cleanable/crayon/PersistenceSave(list/data)
. = ..()
if(icon != initial(icon)) // no support for alticons yet, awful system anyways
return null
data["icon_state"] = icon_state
data["paint_color"] = paint_colour
if(do_icon_rotate)
data["rotation"] = rotation
data["name"] = name
if(pixel_x != initial(pixel_x))
data["pixel_x"] = pixel_x
if(pixel_y != initial(pixel_y))
data["pixel_y"] = pixel_y
/obj/effect/decal/cleanable/crayon/PersistenceLoad(list/data)
. = ..()
if(data["name"])
name = data["name"]
if(do_icon_rotate && data["rotation"])
var/matrix/M = matrix()
M.Turn(text2num(data["rotation"]))
transform = M
if(data["paint_color"])
paint_colour = data["paint_color"]
add_atom_colour(paint_colour, FIXED_COLOUR_PRIORITY)
if(data["icon_state"])
icon_state = data["icon_state"]
if(data["pixel_x"])
pixel_x = data["pixel_x"]
if(data["pixel_y"])
pixel_y = data["pixel_y"]
@@ -80,6 +80,17 @@
to_chat(user, "<span class='notice'>You insert the credits into [src].</span>")
update_icon()
qdel(H)
if(istype(I, /obj/item/card/id))
var/obj/item/card/id/ID = I
if(!ID.registered_account)
to_chat(user, "<span class='warning'>[ID] doesn't have a linked account to deposit into!</span>")
return
for(var/obj/item/holochip/money in src.loc.contents)
ID.attackby(money, user)
for(var/obj/item/stack/spacecash/money in src.loc.contents)
ID.attackby(money, user)
for(var/obj/item/coin/money in src.loc.contents)
ID.attackby(money, user)
/obj/item/holochip/AltClick(mob/user)
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE, ismonkey(user)))
+3 -3
View File
@@ -16,9 +16,9 @@ GLOBAL_LIST_EMPTY(power_sinks)
throw_speed = 1
throw_range = 2
custom_materials = list(/datum/material/iron=750)
var/drain_rate = 1600000 // amount of power to drain per tick
var/drain_rate = 2000000 // amount of power to drain per tick. Currently 2MJ.
var/power_drained = 0 // has drained this much power
var/max_power = 1e10 // maximum power that can be drained before exploding
var/max_power = 2000000000 // maximum power that can be drained before exploding. Currently 2GJ. About 17 minutes to explode at the max rate.
var/mode = 0 // 0 = off, 1=clamped (off), 2=operating
var/admins_warned = FALSE // stop spam, only warn the admins once that we are about to boom
@@ -155,5 +155,5 @@ GLOBAL_LIST_EMPTY(power_sinks)
if(power_drained >= max_power)
STOP_PROCESSING(SSobj, src)
explosion(src.loc, 4,8,16,32)
explosion(src.loc, 3,7,14,28)
qdel(src)
@@ -264,6 +264,30 @@ effective or pretty fucking useless.
GLOB.active_jammers -= src
update_icon()
/*portable turret*/
/obj/item/storage/toolbox/emergency/turret
desc = "You feel a strange urge to hit this with a wrench."
/obj/item/storage/toolbox/emergency/turret/PopulateContents()
new /obj/item/screwdriver(src)
new /obj/item/wrench(src)
new /obj/item/weldingtool(src)
new /obj/item/crowbar(src)
new /obj/item/analyzer(src)
new /obj/item/wirecutters(src)
/obj/item/storage/toolbox/emergency/turret/attackby(obj/item/I, mob/living/user, params)
if(I.tool_behaviour == TOOL_WRENCH && user.a_intent == INTENT_HARM)
user.visible_message("<span class='danger'>[user] bashes [src] with [I]!</span>", \
"<span class='danger'>You bash [src] with [I]!</span>", null, COMBAT_MESSAGE_RANGE)
playsound(src, "sound/items/drill_use.ogg", 80, TRUE, -1)
var/obj/machinery/porta_turret/syndicate/pod/toolbox/turret = new(get_turf(loc))
turret.faction = list("[REF(user)]")
qdel(src)
return
..()
/obj/item/headsetupgrader
name = "headset upgrader"
desc = "A tool that can be used to upgrade a normal headset to be able to protect from flashbangs."
+2 -1
View File
@@ -19,6 +19,7 @@
var/boom_sizes = list(0, 0, 3)
var/can_attach_mob = FALSE
var/full_damage_on_mobs = FALSE
var/can_gib_mobs = FALSE
/obj/item/grenade/plastic/Initialize()
. = ..()
@@ -72,7 +73,7 @@
explosion(get_step(T, aim_dir), boom_sizes[1], boom_sizes[2], boom_sizes[3])
else
explosion(location, boom_sizes[1], boom_sizes[2], boom_sizes[3])
if(ismob(target))
if(ismob(target) && can_gib_mobs)
var/mob/M = target
M.gib()
qdel(src)
-14
View File
@@ -1,17 +1,3 @@
/obj/item/caution
desc = "Caution! Wet Floor!"
name = "wet floor sign"
icon = 'icons/obj/janitor.dmi'
icon_state = "caution"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
force = 1
throwforce = 3
throw_speed = 2
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
attack_verb = list("warned", "cautioned", "smashed")
/obj/item/choice_beacon
name = "choice beacon"
desc = "Hey, why are you viewing this?!! Please let Centcom know about this odd occurance."
+6 -2
View File
@@ -24,13 +24,17 @@
create_reagents(mopcap, NONE, NO_REAGENTS_VALUE)
/obj/item/mop/proc/clean(turf/A)
/obj/item/mop/proc/clean(turf/A, mob/user)
if(reagents.has_reagent(/datum/reagent/water, 1) || reagents.has_reagent(/datum/reagent/water/holywater, 1) || reagents.has_reagent(/datum/reagent/consumable/ethanol/vodka, 1) || reagents.has_reagent(/datum/reagent/space_cleaner, 1))
SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
A.clean_blood()
var/cleaned_something = FALSE
for(var/obj/effect/O in A)
if(is_cleanable(O))
cleaned_something = TRUE
qdel(O)
if(cleaned_something && user && user.client)
user.client.increment_progress("janitor", 1)
reagents.reaction(A, TOUCH, 10) //Needed for proper floor wetting.
reagents.remove_any(1) //reaction() doesn't use up the reagents
@@ -59,7 +63,7 @@
if(!L.UseStaminaBuffer(stamusage, warn = TRUE))
return
user.visible_message("[user] cleans \the [T] with [src].", "<span class='notice'>You clean \the [T] with [src].</span>")
clean(T)
clean(T, user)
user.DelayNextAction(CLICK_CD_MELEE)
user.do_attack_animation(T, used_item = src)
playsound(T, "slosh", 50, 1)
@@ -392,6 +392,7 @@
created_name = new_name
else
created_name = ""
log_game("[key_name(usr)] renamed [src] ([REF(src)]) to [created_name].")
else if(href_list["Master"])
forced_ai = select_active_ai(usr)
+14
View File
@@ -33,6 +33,20 @@
. = ..()
update_desc()
/obj/item/stack/spacecash/attackby(obj/item/W, mob/user, params)
. = ..()
if(istype(W, /obj/item/card/id))
var/obj/item/card/id/ID = W
if(!ID.registered_account)
to_chat(user, "<span class='warning'>[ID] doesn't have a linked account to deposit into!</span>")
return
for(var/obj/item/holochip/money in src.loc.contents)
ID.attackby(money, user)
for(var/obj/item/stack/spacecash/money in src.loc.contents)
ID.attackby(money, user)
for(var/obj/item/coin/money in src.loc.contents)
ID.attackby(money, user)
/obj/item/stack/spacecash/c1
icon_state = "spacecash"
singular_name = "one credit bill"
@@ -253,7 +253,6 @@ GLOBAL_LIST_INIT(wood_recipes, list ( \
new/datum/stack_recipe("painting frame", /obj/item/wallframe/painting, 1, time = 10),\
new/datum/stack_recipe("wooden buckler", /obj/item/shield/riot/buckler, 20, time = 40), \
new/datum/stack_recipe("baseball bat", /obj/item/melee/baseball_bat, 5, time = 15),\
new/datum/stack_recipe("training bokken", /obj/item/melee/bokken, 10, time = 15),\
null, \
new/datum/stack_recipe("wooden chair", /obj/structure/chair/wood/, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("winged wooden chair", /obj/structure/chair/wood/wings, 3, time = 10, one_per_turf = TRUE, on_floor = TRUE), \
@@ -794,7 +793,7 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("shower curtain", /obj/structure/curtain, 10, time = 10, one_per_turf = 1, on_floor = 1), \
new /datum/stack_recipe("duct", /obj/item/stack/ducts,1), \
new /datum/stack_recipe("laser pointer case", /obj/item/glasswork/glass_base/laserpointer_shell, 30), \
new /datum/stack_recipe("wet floor sign", /obj/item/caution, 2)))
new /datum/stack_recipe("wet floor sign", /obj/item/clothing/suit/caution, 2)))
/obj/item/stack/sheet/plastic
name = "plastic"
+3 -1
View File
@@ -802,7 +802,8 @@
. += "<span class='notice'>Alt-click it to quickly draw the blade.</span>"
/obj/item/storage/belt/sabre/PopulateContents()
new starting_sword(src)
if(starting_sword)
new starting_sword(src)
/obj/item/storage/belt/sabre/rapier
name = "rapier sheath"
@@ -864,6 +865,7 @@
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_items = 2
STR.max_w_class = WEIGHT_CLASS_BULKY + WEIGHT_CLASS_NORMAL //katana and waki.
STR.max_combined_w_class = 7
/obj/item/melee/smith/twohand/katana/on_exit_storage(datum/component/storage/S)
var/obj/item/storage/belt/sabre/twin/B = S.parent
@@ -103,6 +103,39 @@ GLOBAL_LIST_EMPTY(rubber_toolbox_icons)
/obj/item/storage/toolbox/mechanical/old/heirloom/PopulateContents()
return
/obj/item/storage/toolbox/mechanical/old/clean // the assistant traitor toolbox, damage scales with TC inside
name = "toolbox"
desc = "An old, blue toolbox. It menaces with a sickening miasma of robust energies. You sure about this, Brain?"
icon_state = "toolbox_blue_clean"
force = 19
throwforce = 22
wound_bonus = 0
bare_wound_bonus = 10
/obj/item/storage/toolbox/mechanical/old/clean/proc/calc_damage()
var/power = 0
for (var/obj/item/stack/telecrystal/TC in GetAllContents())
power += TC.amount
force = 19 + power
throwforce = 22 + power
/obj/item/storage/toolbox/mechanical/old/clean/attack(mob/target, mob/living/user)
calc_damage() // one damage for one telecrystal equals about thirty seven(?) damage if you pour ALL your tc
..()
/obj/item/storage/toolbox/mechanical/old/clean/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
calc_damage()
..()
/obj/item/storage/toolbox/mechanical/old/clean/PopulateContents()
new /obj/item/screwdriver(src)
new /obj/item/wrench(src)
new /obj/item/weldingtool(src)
new /obj/item/crowbar(src)
new /obj/item/wirecutters(src)
new /obj/item/multitool(src)
new /obj/item/clothing/gloves/color/yellow(src)
/obj/item/storage/toolbox/electrical
name = "electrical toolbox"
icon_state = "yellow"
+122 -17
View File
@@ -296,8 +296,8 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
slot_flags = ITEM_SLOT_BELT | ITEM_SLOT_BACK
w_class = WEIGHT_CLASS_BULKY
force = 7 //how much harm mode damage we do
var/stamina_damage_increment = 4 //how much extra damage do we do when in non-harm mode
force = 10 //how much harm mode damage we do
var/stamina_damage_increment = 5 //how much extra damage do we do when in non-harm mode
throwforce = 10
damtype = STAMINA
attack_verb = list("whacked", "smacked", "struck")
@@ -310,11 +310,13 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
var/quick_parry = FALSE // false = default parry, true = really small parry window
item_flags = ITEM_CAN_PARRY
block_parry_data = /datum/block_parry_data/bokken
var/default_parry_data = /datum/block_parry_data/bokken
var/quick_parry_data = /datum/block_parry_data/bokken/quick_parry
bare_wound_bonus = 0
wound_bonus = 0
/datum/block_parry_data/bokken // fucked up parry data, emphasizing quicker, shorter parries
parry_stamina_cost = 8 // be wise about when you parry, though, else you won't be able to fight enough to make it count
parry_stamina_cost = 10 // be wise about when you parry, though, else you won't be able to fight enough to make it count
parry_time_windup = 0
parry_time_active = 10 // small parry window
parry_time_spindown = 0
@@ -330,11 +332,11 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
)
parry_failed_stagger_duration = 3 SECONDS
parry_data = list(
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 10*2.5 = 25, 11*2.5 = 27.5, 12*2.5 = 30, 13*2.5 = 32.5
)
/datum/block_parry_data/bokken/quick_parry // emphasizing REALLY SHORT PARRIES
parry_stamina_cost = 6 // still more costly than most parries, but less than a full bokken parry
parry_stamina_cost = 8 // still more costly than most parries, but less than a full bokken parry
parry_time_active = 5 // REALLY small parry window
parry_time_perfect = 2.5 // however...
parry_time_perfect_leeway = 2 // the entire time, the parry is perfect
@@ -371,9 +373,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
. = ..()
quick_parry = !quick_parry
if(quick_parry)
block_parry_data = /datum/block_parry_data/bokken/quick_parry
block_parry_data = quick_parry_data
else
block_parry_data = /datum/block_parry_data/bokken
block_parry_data = default_parry_data
to_chat(user, "<span class='notice'>[src] is now [quick_parry ? "emphasizing shorter parries, forcing you to riposte or be staggered" : "emphasizing longer parries, with a shorter window to riposte but more forgiving parries"].</span>")
/obj/item/melee/bokken/attackby(obj/item/I, mob/living/user, params)
@@ -410,23 +412,76 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
if(burnt)
. += " Burned into the \"blade\" is [burned_in]."
/obj/item/melee/bokken/steelwood
name = "steelwood bokken"
desc = "A misnomer of sorts, this is effectively a blunt katana made from steelwood, a dense organic wood derived from steelcaps. Why steelwood? Druids can use it. Duh."
icon_state = "bokken_steel"
item_state = "bokken_steel"
force = 12
stamina_damage_increment = 3
/obj/item/melee/bokken/waki
name = "wakizashi bokken"
desc = "A space-Japanese training sword made of wood and shaped like a wakizashi."
icon_state = "wakibokken"
item_state = "wakibokken"
slot_flags = ITEM_SLOT_BELT
w_class = WEIGHT_CLASS_NORMAL
force = 6
stamina_damage_increment = 4
block_parry_data = /datum/block_parry_data/bokken/waki
default_parry_data = /datum/block_parry_data/bokken/waki
quick_parry_data = /datum/block_parry_data/bokken/waki/quick_parry
/datum/block_parry_data/bokken/waki // weaker parries than the bigger variant, but cheaper and faster recovery, like quick parry
parry_stamina_cost = 4
parry_time_windup = 0
parry_time_active = 6
parry_time_spindown = 0
parry_time_perfect = 1.5
parry_time_perfect_leeway = 1
parry_imperfect_falloff_percent = 7.5
parry_efficiency_to_counterattack = 120
parry_efficiency_considered_successful = 65
parry_efficiency_perfect = 120
parry_efficiency_perfect_override = list(
TEXT_ATTACK_TYPE_PROJECTILE = 30,
)
parry_failed_stagger_duration = 2 SECONDS
parry_data = list(
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 1.5, // 6*1.5 = 9, 7*1.5 = 10.5, 8*1.5 = 12, 9*1.5 = 13.5
)
/datum/block_parry_data/bokken/waki/quick_parry //For the parry spammer in you
parry_stamina_cost = 2 // Slam that parry button
parry_time_active = 2.5
parry_time_perfect = 1
parry_time_perfect_leeway = 1
parry_failed_stagger_duration = 1 SECONDS
parry_failed_clickcd_duration = 1 SECONDS
/datum/block_parry_data/bokken/waki/quick_parry/proj
parry_efficiency_perfect_override = list()
/obj/item/melee/bokken/waki/steelwood
name = "wakizashi steelwood bokken"
desc = "A misnomer of sorts, this is effectively a blunt wakizashi made from steelwood, a dense organic wood derived from steelcaps. Why steelwood? Druids can use it. Duh."
icon_state = "wakibokken_steel"
item_state = "wakibokken_steel"
force = 8
stamina_damage_increment = 2
/obj/item/melee/bokken/debug
name = "funny debug parrying stick"
desc = "if you see this you've fucked up somewhere my good man"
block_parry_data = /datum/block_parry_data/bokken/debug
/obj/item/melee/bokken/debug/AltClick(mob/user)
quick_parry = !quick_parry
if(quick_parry)
block_parry_data = /datum/block_parry_data/bokken/quick_parry/debug
else
block_parry_data = /datum/block_parry_data/bokken/debug
to_chat(user, "<span class='notice'>[src] is now [quick_parry ? "emphasizing shorter parries, forcing you to riposte or be staggered" : "emphasizing longer parries, with a shorter window to riposte but more forgiving parries"].</span>")
default_parry_data = /datum/block_parry_data/bokken/debug
quick_parry_data = /datum/block_parry_data/bokken/quick_parry/debug
/datum/block_parry_data/bokken/debug
parry_efficiency_perfect_override = list()
parry_data = list(
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 10*2.5 = 25, 11*2.5 = 27.5, 12*2.5 = 30, 13*2.5 = 32.5
PARRY_DISARM_ATTACKER = TRUE,
PARRY_KNOCKDOWN_ATTACKER = 10,
PARRY_STAGGER_ATTACKER = 10,
@@ -436,13 +491,63 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
/datum/block_parry_data/bokken/quick_parry/debug
parry_efficiency_perfect_override = list()
parry_data = list(
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 7*2.5 = 17.5, 8*2.5 = 20, 9*2.5 = 22.5, 10*2.5 = 25
PARRY_COUNTERATTACK_MELEE_ATTACK_CHAIN = 2.5, // 10*2.5 = 25, 11*2.5 = 27.5, 12*2.5 = 30, 13*2.5 = 32.5
PARRY_DISARM_ATTACKER = TRUE,
PARRY_KNOCKDOWN_ATTACKER = 10,
PARRY_STAGGER_ATTACKER = 10,
PARRY_DAZE_ATTACKER = 10,
)
/// BOKKEN CRAFTNG PIECES
/obj/item/bokken_blade
name = "training bokken wooden blade"
desc = "The blade piece of a bokken katana."
icon = 'icons/obj/smith.dmi'
icon_state = "bokken"
item_state = "bone_dagger"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
/obj/item/bokken_steelblade
name = "training bokken steelwood blade"
desc = "The blade piece of a steelwood bokken katana."
icon = 'icons/obj/smith.dmi'
icon_state = "bokken_steel"
item_state = "switchblade_ext"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
/obj/item/wakibokken_blade
name = "training bokken wooden wakizashi blade"
desc = "The blade piece of a bokken wakizashi."
icon = 'icons/obj/smith.dmi'
icon_state = "wakibokken"
item_state = "bone_dagger"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
/obj/item/wakibokken_steelblade
name = "training bokken steelwood wakizashi blade"
desc = "The blade piece of a steelwood bokken katana."
icon = 'icons/obj/smith.dmi'
icon_state = "wakibokken_steel"
item_state = "switchblade_ext"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
w_class = WEIGHT_CLASS_NORMAL
/obj/item/bokken_hilt
name = "training bokken hilt"
desc = "The hilt piece of a bokken. This hilt is appropriate for any potential blade length or material."
icon = 'icons/obj/smith.dmi'
icon_state = "bokken_hilt"
item_state = "bone_dagger"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
/obj/item/wirerod
name = "wired rod"
+25 -2
View File
@@ -243,7 +243,7 @@ LINEN BINS
/obj/item/bedsheet/random/Initialize()
..()
var/type = pick(typesof(/obj/item/bedsheet) - list(/obj/item/bedsheet/random, /obj/item/bedsheet/chameleon))
var/type = pick(typesof(/obj/item/bedsheet) - (list(/obj/item/bedsheet/random, /obj/item/bedsheet/chameleon) + typesof(/obj/item/bedsheet/unlockable)))
new type(loc)
return INITIALIZE_HINT_QDEL
@@ -257,9 +257,32 @@ LINEN BINS
chameleon_action = new(src)
chameleon_action.chameleon_type = /obj/item/bedsheet
chameleon_action.chameleon_name = "Bedsheet"
chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/bedsheet/chameleon, /obj/item/bedsheet/random), only_root_path = TRUE)
chameleon_action.chameleon_blacklist = typecacheof(list(/obj/item/bedsheet/chameleon, /obj/item/bedsheet/random, /obj/item/bedsheet/unlockable), only_root_path = FALSE)
chameleon_action.initialize_disguises()
//unlockable bedsheets
/obj/item/bedsheet/unlockable
name = "unlockable bedsheet"
desc = "this shouldn't be here!"
//janitor: clean 100 messes with mop as janitor
/obj/item/bedsheet/unlockable/janitor
name = "janitor bedsheet"
desc = "A white bedsheet, with a warning sign on the front."
icon_state = "sheetjanitor"
//cook: use microwave 100 times properly (contents must make one good item) as cook
/obj/item/bedsheet/unlockable/cook
name = "cook bedsheet"
desc = "A grey bedsheet, with a microwave on the front."
icon_state = "sheetcook"
//miner: redeem 100,000 mining points
/obj/item/bedsheet/unlockable/miner
name = "miner bedsheet"
desc = "A red and black bedsheet. It seems to be made with goliath hide."
icon_state = "sheetminer"
//bedsheet bin
/obj/structure/bedsheetbin
name = "linen bin"
@@ -70,7 +70,7 @@
new /obj/item/melee/flyswatter(src)
new /obj/item/flashlight(src)
for(var/i in 1 to 3)
new /obj/item/caution(src)
new /obj/item/clothing/suit/caution(src)
new /obj/item/holosign_creator(src)
new /obj/item/lightreplacer(src)
new /obj/item/soap(src)
@@ -47,6 +47,25 @@
new /obj/item/clothing/glasses/hud/health(src)
return
/obj/structure/closet/secure_closet/paramedic
name = "paramedic's locker"
req_access = list(ACCESS_MEDICAL)
icon_state = "paramed_secure"
/obj/structure/closet/secure_closet/paramedic/PopulateContents()
..()
new /obj/item/clothing/suit/toggle/labcoat/paramedic(src)
new /obj/item/clothing/under/rank/medical/paramedic(src)
new /obj/item/clothing/under/rank/medical/paramedic/skirt(src)
new /obj/item/radio/headset/headset_med(src)
new /obj/item/defibrillator/loaded(src)
new /obj/item/clothing/gloves/color/latex/nitrile(src)
new /obj/item/storage/belt/medical(src)
new /obj/item/clothing/glasses/hud/health(src)
new /obj/item/pinpointer/crew(src)
new /obj/item/sensor_device(src)
return
/obj/structure/closet/secure_closet/CMO
name = "\proper chief medical officer's locker"
req_access = list(ACCESS_CMO)
+2 -2
View File
@@ -73,7 +73,7 @@
l.janicart_insert(user,src)
else
to_chat(user, fail_msg)
else if(istype(I, /obj/item/caution))
else if(istype(I, /obj/item/clothing/suit/caution))
if(signs < max_signs)
put_in_cart(I, user)
signs++
@@ -104,7 +104,7 @@
items += list("Spray bottle" = image(icon = myspray.icon, icon_state = myspray.icon_state))
if(myreplacer)
items += list("Light replacer" = image(icon = myreplacer.icon, icon_state = myreplacer.icon_state))
var/obj/item/caution/sign = locate() in src
var/obj/item/clothing/suit/caution/sign = locate() in src
if(sign)
items += list("Sign" = image(icon = sign.icon, icon_state = sign.icon_state))
+7
View File
@@ -1,5 +1,12 @@
/turf/open
plane = FLOOR_PLANE
/// Does dirt buildup happen on us?
var/dirt_buildup_allowed = FALSE
/// Dirt level.
var/dirtyness = 0
/// Dirt level to spawn dirt. Null to use config.
var/dirt_spawn_threshold
/// Slowdown applied to mobs on us.
var/slowdown = 0 //negative for faster, positive for slower
var/postdig_icon_change = FALSE
+1
View File
@@ -9,6 +9,7 @@
canSmoothWith = list(/turf/open/floor/fakepit, /turf/open/chasm)
density = TRUE //This will prevent hostile mobs from pathing into chasms, while the canpass override will still let it function like an open turf
bullet_bounce_sound = null //abandon all hope ye who enter
dirt_buildup_allowed = FALSE
/turf/open/chasm/Initialize()
. = ..()
+4 -1
View File
@@ -8,6 +8,9 @@
//Making the station dirty, one tile at a time. Called by master controller's setup_objects
/turf/open/floor/proc/MakeDirty()
if(CONFIG_GET(flag/persistent_debris_only))
return
if(prob(66)) //fastest possible exit 2/3 of the time
return
@@ -38,7 +41,7 @@
return
//Construction zones. Blood, sweat, and oil. Oh, and dirt.
var/static/list/engine_dirt_areas = typecacheof(list(/area/engine,
var/static/list/engine_dirt_areas = typecacheof(list(/area/engine,
/area/crew_quarters/heads/chief,
/area/ruin/space/derelict/assembly_line,
/area/science/robotics,
+4
View File
@@ -5,6 +5,7 @@
name = "floor"
icon = 'icons/turf/floors.dmi'
baseturfs = /turf/open/floor/plating
dirt_buildup_allowed = TRUE
footstep = FOOTSTEP_FLOOR
barefootstep = FOOTSTEP_HARD_BAREFOOT
@@ -137,6 +138,9 @@
burnt = 1
/turf/open/floor/proc/make_plating()
for(var/obj/effect/decal/cleanable/C in src)
if(C.wiped_by_floor_change)
qdel(C)
return ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
/turf/open/floor/ChangeTurf(path, new_baseturf, flags)
@@ -61,6 +61,9 @@
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))
for(var/obj/effect/decal/cleanable/decal in src)
if(decal.wiped_by_floor_change)
qdel(decal)
PlaceOnTop(/turf/open/floor/engine, flags = CHANGETURF_INHERIT_AIR)
playsound(src, 'sound/items/deconstruct.ogg', 80, 1)
R.use(2)
@@ -78,6 +81,9 @@
to_chat(user, "<span class='notice'>You begin adding glass to the floor...</span>")
if(do_after(user, 5, target = src))
if (G.get_amount() >= 2 && !istype(src, /turf/open/transparent/glass))
for(var/obj/effect/decal/cleanable/decal in src)
if(decal.wiped_by_floor_change)
qdel(decal)
PlaceOnTop(/turf/open/transparent/glass, flags = CHANGETURF_INHERIT_AIR)
playsound(src, 'sound/items/deconstruct.ogg', 80, 1)
G.use(2)
@@ -95,6 +101,9 @@
to_chat(user, "<span class='notice'>You begin adding reinforced glass to the floor...</span>")
if(do_after(user, 10, target = src))
if (RG.get_amount() >= 2 && !istype(src, /turf/open/transparent/glass/reinforced))
for(var/obj/effect/decal/cleanable/decal in src)
if(decal.wiped_by_floor_change)
qdel(decal)
PlaceOnTop(/turf/open/transparent/glass/reinforced, flags = CHANGETURF_INHERIT_AIR)
playsound(src, 'sound/items/deconstruct.ogg', 80, 1)
RG.use(2)
@@ -110,6 +119,9 @@
var/obj/item/stack/tile/W = C
if(!W.use(1))
return
for(var/obj/effect/decal/cleanable/decal in src)
if(decal.wiped_by_floor_change)
qdel(decal)
if(istype(W, /obj/item/stack/tile/material))
var/turf/newturf = PlaceOnTop(/turf/open/floor/material, flags = CHANGETURF_INHERIT_AIR)
newturf.set_custom_materials(W.custom_materials)
@@ -43,6 +43,9 @@
return TRUE
if(floor_tile)
new floor_tile(src, 2)
for(var/obj/effect/decal/cleanable/C in src)
if(C.wiped_by_floor_change)
qdel(C)
ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
return TRUE
+1
View File
@@ -6,6 +6,7 @@
gender = PLURAL //"That's some lava."
baseturfs = /turf/open/lava //lava all the way down
slowdown = 2
dirt_buildup_allowed = FALSE
light_range = 2
light_power = 0.75
+1
View File
@@ -6,6 +6,7 @@
planetary_atmos = TRUE
bullet_bounce_sound = null //forever falling
tiled_dirt = FALSE
dirt_buildup_allowed = FALSE
/turf/open/indestructible/reebe_void/Initialize(mapload)
. = ..()
+1
View File
@@ -3,6 +3,7 @@
icon_state = "0"
name = "\proper space"
intact = 0
dirt_buildup_allowed = FALSE
temperature = TCMB
thermal_conductivity = OPEN_HEAT_TRANSFER_COEFFICIENT
+2
View File
@@ -48,6 +48,8 @@
if(C.prefs.toggles & SOUND_PRAYERS)
if(usr.job == "Chaplain")
SEND_SOUND(C, sound('sound/effects/pray.ogg'))
else
SEND_SOUND(C, sound('sound/effects/ding.ogg'))
to_chat(usr, "<span class='info'>You pray to the gods: \"[msg_tmp]\"</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Prayer") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -26,7 +26,7 @@
return
flick("blob_resource_glow", src)
if(overmind)
overmind.add_points(1)
overmind.add_points(2)
resource_delay = world.time + 40 + overmind.resource_blobs.len * 2.5 //4 seconds plus a quarter second for each resource blob the overmind has
else
resource_delay = world.time + 40
+2 -1
View File
@@ -147,7 +147,7 @@
set category = "Blob"
set name = "Create Factory Blob (60)"
set desc = "Create a spore tower that will spawn spores to harass your enemies."
createSpecial(60, /obj/structure/blob/factory, 7, 1)
createSpecial(60, /obj/structure/blob/factory, 5, 1)
/mob/camera/blob/verb/create_blobbernaut()
set category = "Blob"
@@ -195,6 +195,7 @@
to_chat(blobber, "The <b><font color=\"[blobstrain.color]\">[blobstrain.name]</b></font> reagent [blobstrain.shortdesc ? "[blobstrain.shortdesc]" : "[blobstrain.description]"]")
else
to_chat(src, "<span class='warning'>You could not conjure a sentience for your blobbernaut. Your points have been refunded. Try again later.</span>")
B.naut = FALSE
add_points(40)
/mob/camera/blob/verb/relocate_core()
@@ -16,7 +16,7 @@
if(user.stat != DEAD)
user.emote("deathgasp")
user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)
user.fakedeath("changeling") //play dead
user.fakedeath("changeling", TRUE) //play dead
user.update_stat()
addtimer(CALLBACK(src, .proc/ready_to_regenerate, user), LING_FAKEDEATH_TIME, TIMER_UNIQUE)
@@ -21,6 +21,7 @@
chemical_cost = 1000
dna_cost = -1
var/recharge_slowdown = 0
var/silent = FALSE
var/weapon_type
var/weapon_name_simple
@@ -37,6 +38,8 @@
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
user.visible_message("<span class='warning'>With a sickening crunch, [user] reforms [user.p_their()] [weapon_name_simple] into an arm!</span>", "<span class='notice'>We assimilate the [weapon_name_simple] back into our body.</span>", "<span class='italics>You hear organic matter ripping and tearing!</span>")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown -= recharge_slowdown
user.update_inv_hands()
return 1
@@ -57,6 +60,8 @@
user.put_in_hands(W)
if(!silent)
playsound(user, 'sound/effects/blobattack.ogg', 30, 1)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.chem_recharge_slowdown += recharge_slowdown
return W
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
@@ -141,10 +146,11 @@
name = "Arm Blade"
desc = "We reform one of our arms into a deadly blade."
helptext = "We may retract our armblade in the same manner as we form it. Cannot be used while in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 10
chemical_cost = 5
dna_cost = 2
loudness = 2
req_human = 1
recharge_slowdown = 0.6
weapon_type = /obj/item/melee/arm_blade
weapon_name_simple = "blade"
action_icon = 'icons/mob/actions/actions_changeling.dmi'
@@ -487,7 +493,7 @@
helmet_type = /obj/item/clothing/head/helmet/space/changeling
suit_name_simple = "flesh shell"
helmet_name_simple = "space helmet"
recharge_slowdown = 0.5
recharge_slowdown = 0.6
blood_on_castoff = 1
/obj/item/clothing/suit/space/changeling
@@ -532,11 +538,11 @@
name = "Chitinous Armor"
desc = "We turn our skin into tough chitin to protect us from damage."
helptext = "Upkeep of the armor requires a constant expenditure of chemicals, resulting in a reduced chemical generation. The armor is strong against brute force, but does not provide much protection from lasers. Cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 20
chemical_cost = 10
dna_cost = 1
loudness = 2
req_human = 1
recharge_slowdown = 0.5
recharge_slowdown = 0.6
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_armor"
action_background_icon_state = "bg_ling"
@@ -662,10 +668,11 @@
name = "Bone Gauntlets"
desc = "We turn our hands into solid bone and chitin, sacrificing dexterity for raw strength."
helptext = "These grotesque, bone-and-chitin gauntlets are remarkably good at beating victims senseless, and cannot be used in lesser form. This ability is loud, and might cause our blood to react violently to heat."
chemical_cost = 10 // same cost as armblade because its a sidegrade (sacrifice utility for punching people violently)
chemical_cost = 5 // same cost as armblade because its a sidegrade (sacrifice utility for punching people violently)
dna_cost = 2
loudness = 2
req_human = 1
recharge_slowdown = 0.6
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_gauntlets"
action_background_icon_state = "bg_ling"
@@ -30,6 +30,7 @@
var/mutable_appearance/alert_overlay = mutable_appearance('icons/effects/clockwork_effects.dmi', "ratvar_alert")
notify_ghosts("The Justiciar's light calls to you! Reach out to Ratvar in [get_area_name(src)] to be granted a shell to spread his glory!", null, source = src, alert_overlay = alert_overlay)
INVOKE_ASYNC(SSshuttle.emergency, /obj/docking_port/mobile/emergency.proc/request, null, 10, null, FALSE, 0)
SSpersistence.station_was_destroyed = TRUE
/obj/structure/destructible/clockwork/massive/ratvar/Destroy()
GLOB.ratvar_awakens--
@@ -2,8 +2,10 @@
name = "Codex Cicatrix"
desc = "Book describing the secrets of the veil."
icon = 'icons/obj/eldritch.dmi'
icon_state = "book"
item_state = "book"
icon_state = "codex"
item_state = "codex"
lefthand_file = 'icons/mob/inhands/misc/books_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/books_righthand.dmi'
w_class = WEIGHT_CLASS_SMALL
///Last person that touched this
var/mob/living/last_user
+3 -1
View File
@@ -42,7 +42,9 @@
/obj/screen,
/obj/singularity,
/mob/living/simple_animal/hostile/morph,
/obj/effect))
/obj/effect,
/mob/camera
))
var/playstyle_string = "<span class='big bold'>You are a morph,</span></b> an abomination of science created primarily with changeling cells. \
You may take the form of anything nearby by shift-clicking it. This process will alert any nearby \
-4
View File
@@ -68,10 +68,6 @@
R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
if(!turfing)
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1))
overlay_fullscreen("flash", /obj/screen/fullscreen/flash) // To be more true to the hentai
clear_fullscreen("flash", 20)
G.last_orgasmed = world.time
R.clear_reagents()
+4 -3
View File
@@ -40,17 +40,18 @@
var/rounded_length = round(length)
var/new_size
var/enlargement = FALSE
var/max_D = CONFIG_GET(number/penis_max_inches_prefs)
switch(rounded_length)
if(0 to 6) //If modest size
new_size = 1
if(7 to 11) //If large
new_size = 2
if(12 to 20) //If massive
if(12 to max_D) //If massive
new_size = 3
if(21 to 34) //If massive and due for large effects
if(max_D + 1 to max_D+13) //If massive and due for large effects, modified in case some server owner running recent citcode decides to be insane with dick sizes in the config
new_size = 3
enlargement = TRUE
if(35 to INFINITY) //If comical
if(max_D+14 to INFINITY) //If comical
new_size = 4 //no new sprites for anything larger yet
enlargement = TRUE
if(owner)
+4
View File
@@ -179,6 +179,10 @@
if((pack.hidden && !(obj_flags & EMAGGED)) || (pack.contraband && !contraband) || pack.DropPodOnly)
return
if(self_paid && !pack.can_private_buy)
say("This cannot be bought privately.")
return
var/name = "*None Provided*"
var/rank = "*None Provided*"
var/ckey = usr.ckey
+51 -4
View File
@@ -34,10 +34,57 @@
/datum/supply_pack/critter/parrot/generate()
. = ..()
for(var/i in 1 to 4)
new /mob/living/simple_animal/parrot(.)
if(prob(1))
new /mob/living/simple_animal/parrot/clock_hawk(.)
for(var/i in 1 to 5)
switch(rand(1, 24))
if(1)
new /mob/living/simple_animal/parrot(.)
if(2)
new /mob/living/simple_animal/parrot/clock_hawk(.)
if(3)
new /mob/living/simple_animal/parrot/kea(.)
if(4)
new /mob/living/simple_animal/parrot/eclectus(.)
if(5)
new /mob/living/simple_animal/parrot/eclectusf(.)
if(6)
new /mob/living/simple_animal/parrot/greybird(.)
if(7)
new /mob/living/simple_animal/parrot/blue_caique(.)
if(8)
new /mob/living/simple_animal/parrot/white_caique(.)
if(9)
new /mob/living/simple_animal/parrot/green_budgerigar(.)
if(10)
new /mob/living/simple_animal/parrot/blue_Budgerigar(.)
if(11)
new /mob/living/simple_animal/parrot/bluegreen_Budgerigar(.)
if(12)
new /mob/living/simple_animal/parrot/commonblackbird(.)
if(13)
new /mob/living/simple_animal/parrot/azuretit(.)
if(14)
new /mob/living/simple_animal/parrot/europeanrobin(.)
if(15)
new /mob/living/simple_animal/parrot/goldcrest(.)
if(16)
new /mob/living/simple_animal/parrot/ringneckdove(.)
if(17)
new /mob/living/simple_animal/parrot/cockatiel(.)
if(18)
new /mob/living/simple_animal/parrot/white_cockatiel(.)
if(19)
new /mob/living/simple_animal/parrot/yellowish_cockatiel(.)
if(20)
new /mob/living/simple_animal/parrot/grey_cockatiel(.)
if(21)
new /mob/living/simple_animal/parrot/too(.)
if(22)
new /mob/living/simple_animal/parrot/hooded_too(.)
if(23)
new /mob/living/simple_animal/parrot/pink_too(.)
if(24)
new /mob/living/simple_animal/parrot/eclectusr(.)
/datum/supply_pack/critter/butterfly
name = "Butterflies Crate"
+12
View File
@@ -90,6 +90,18 @@
crate_name = "medical hardsuit"
crate_type = /obj/structure/closet/crate/secure/medical
/datum/supply_pack/medical/paramedicevasuit
name = "Paramedic EVA Suit"
desc = "Greytide pushing up daisies with the carp outside the station? Chemistry vented in a meth experiment gone wrong? Be the blue saviour this station desperately needs and get your Paramedical EVA suit today! Requires medical access to open."
cost = 1975
access = ACCESS_MEDICAL
contains = list(/obj/item/tank/internals/emergency_oxygen,
/obj/item/clothing/head/helmet/space/eva/paramedic,
/obj/item/clothing/suit/space/eva/paramedic,
/obj/item/clothing/mask/breath)
crate_name = "paramedic EVA suit"
crate_type = /obj/structure/closet/crate/secure/medical
/datum/supply_pack/medical/supplies
name = "Medical Supplies Crate"
desc = "Contains seven beakers, syringes, and bodybags. Three morphine bottles, four insulin pills. Two charcoal bottles, epinephrine bottles, antitoxin bottles, and large beakers. Finally, a single roll of medical gauze, as well as a bottle of stimulant pills for long, hard work days. German doctor not included."
+3 -3
View File
@@ -261,9 +261,9 @@
/obj/item/reagent_containers/glass/bucket,
/obj/item/reagent_containers/glass/bucket,
/obj/item/mop,
/obj/item/caution,
/obj/item/caution,
/obj/item/caution,
/obj/item/clothing/suit/caution,
/obj/item/clothing/suit/caution,
/obj/item/clothing/suit/caution,
/obj/item/storage/bag/trash,
/obj/item/reagent_containers/spray/cleaner,
/obj/item/reagent_containers/rag,
+22
View File
@@ -1024,3 +1024,25 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
verb_tabs |= verb_to_init.category
verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name)
src << output("[url_encode(json_encode(verb_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
//increment progress for an unlockable loadout item
/client/proc/increment_progress(key, amount)
if(prefs)
var/savefile/S = new /savefile(prefs.path)
var/list/unlockable_loadout_data = prefs.unlockable_loadout_data
if(!length(unlockable_loadout_data))
unlockable_loadout_data = list()
unlockable_loadout_data[key] = amount
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(unlockable_loadout_data))
prefs.unlockable_loadout_data = unlockable_loadout_data
return TRUE
else
if(unlockable_loadout_data[key])
unlockable_loadout_data[key] += amount
else
unlockable_loadout_data[key] = amount
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(unlockable_loadout_data))
prefs.unlockable_loadout_data = unlockable_loadout_data
return TRUE
return FALSE
+23 -3
View File
@@ -198,6 +198,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/gear_points = 10
var/list/gear_categories
var/list/loadout_data = list()
var/list/unlockable_loadout_data = list()
var/loadout_slot = 1 //goes from 1 to MAXIMUM_LOADOUT_SAVES
var/gear_category
var/gear_subcategory
@@ -907,12 +908,14 @@ GLOBAL_LIST_EMPTY(preferences_datums)
loadout_color_non_poly = loadout_item[LOADOUT_COLOR][1]
extra_color_data += "<BR><a href='?_src_=prefs;preference=gear;loadout_color=1;loadout_gear_name=[html_encode(gear.name)];'>Color</a>"
extra_color_data += "<span style='border: 1px solid #161616; background-color: [loadout_color_non_poly];'>&nbsp;&nbsp;&nbsp;</span>"
else if(gear_points <= 0)
else if((gear_points - gear.cost) < 0)
class_link = "style='white-space:normal;' class='linkOff'"
else if(donoritem)
class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
else
else if(!istype(gear, /datum/gear/unlockable) || can_use_unlockable(gear))
class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
else
class_link = "style='white-space:normal;background:#eb2e2e;' class='linkOff'"
dat += "<tr style='vertical-align:top;'><td width=15%><a [class_link]>[name]</a>[extra_color_data]</td>"
dat += "<td width = 5% style='vertical-align:top'>[gear.cost]</td><td>"
if(islist(gear.restricted_roles))
@@ -925,7 +928,16 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<font size=2>"
dat += gear.restricted_roles.Join(";")
dat += "</font>"
dat += "</td><td><font size=2><i>[gear.description]</i></font></td></tr>"
if(!istype(gear, /datum/gear/unlockable))
dat += "</td><td><font size=2><i>[gear.description]</i></font></td></tr>"
else
//we add the user's progress to the description assuming they have progress
var/datum/gear/unlockable/unlockable = gear
var/progress_made = unlockable_loadout_data[unlockable.progress_key]
if(!progress_made)
progress_made = 0
dat += "</td><td><font size=2><i>[gear.description] Progress: [min(progress_made, unlockable.progress_required)]/[unlockable.progress_required]</i></font></td></tr>"
dat += "</table>"
if(4) // Content preferences
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
@@ -2675,6 +2687,9 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(G.donoritem && !G.donator_ckey_check(user.ckey))
to_chat(user, "<span class='danger'>This is an item intended for donator use only. You are not authorized to use this item.</span>")
return
if(istype(G, /datum/gear/unlockable) && !can_use_unlockable(G))
to_chat(user, "<span class='danger'>To use this item, you need to meet the defined requirements!</span>")
return
if(gear_points >= initial(G.cost))
var/list/new_loadout_data = list(LOADOUT_ITEM = "[G.type]")
if(length(G.loadout_initial_colors))
@@ -2960,6 +2975,11 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(find_gear)
loadout_data["SAVE_[save_slot]"] -= list(find_gear)
/datum/preferences/proc/can_use_unlockable(datum/gear/unlockable/unlockable_gear)
if(unlockable_loadout_data[unlockable_gear.progress_key] >= unlockable_gear.progress_required)
return TRUE
return FALSE
#undef DEFAULT_SLOT_AMT
#undef HANDS_SLOT_AMT
#undef BACKPACK_SLOT_AMT
+18 -2
View File
@@ -5,7 +5,7 @@
// You do not need to raise this if you are adding new values that have sane defaults.
// Only raise this value when changing the meaning/format/name/layout of an existing value
// where you would want the updater procs below to run
#define SAVEFILE_VERSION_MAX 47
#define SAVEFILE_VERSION_MAX 48
/*
SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Carn
@@ -285,6 +285,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
S["loadout"] = safe_json_encode(loadout_data)
if(current_version < 48) //unlockable loadout items but we need to clear bad data from a mistake
S["unlockable_loadout"] = list()
/datum/preferences/proc/load_path(ckey,filename="preferences.sav")
if(!ckey)
return
@@ -427,6 +430,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
verify_keybindings_valid() // one of these days this will runtime and you'll be glad that i put it in a different proc so no one gets their saves wiped
if(S["unlockable_loadout"])
unlockable_loadout_data = safe_json_decode(S["unlockable_loadout"])
else
unlockable_loadout_data = list()
if(needs_update >= 0) //save the updated version
var/old_default_slot = default_slot
var/old_max_save_slots = max_save_slots
@@ -532,6 +540,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
WRITE_FILE(S["auto_ooc"], auto_ooc)
WRITE_FILE(S["no_tetris_storage"], no_tetris_storage)
if(length(unlockable_loadout_data))
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(unlockable_loadout_data))
else
WRITE_FILE(S["unlockable_loadout"], safe_json_encode(list()))
return 1
/datum/preferences/proc/load_character(slot, bypass_cooldown = FALSE)
@@ -736,7 +749,10 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
belly_prefs = json_from_file["belly_prefs"]
//gear loadout
loadout_data = safe_json_decode(S["loadout"])
if(S["loadout"])
loadout_data = safe_json_decode(S["loadout"])
else
loadout_data = list()
//try to fix any outdated data if necessary
//preference updating will handle saving the updated data for us.
@@ -541,6 +541,21 @@
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
allowed = list(/obj/item/clothing/mask/facehugger/toy)
/obj/item/clothing/suit/caution
name = "wet floor sign"
desc = "Caution! Wet Floor!"
icon_state = "caution"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
force = 1
throwforce = 3
throw_speed = 2
throw_range = 5
w_class = WEIGHT_CLASS_SMALL
body_parts_covered = CHEST|GROIN
attack_verb = list("warned", "cautioned", "smashed")
armor = list("melee" = 5, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0)
// WINTER COATS
/obj/item/clothing/suit/hooded/wintercoat
+7 -9
View File
@@ -244,35 +244,33 @@
/obj/item/clothing/under/proc/rolldown()
if(!can_use(usr))
return
if(!can_adjust)
to_chat(usr, "<span class='warning'>You cannot wear this suit any differently!</span>")
return
if(toggle_jumpsuit_adjust())
to_chat(usr, "<span class='notice'>You adjust the suit to wear it more casually.</span>")
else
to_chat(usr, "<span class='notice'>You adjust the suit back to normal.</span>")
if(ishuman(usr))
if(toggle_jumpsuit_adjust() && ishuman(usr))
var/mob/living/carbon/human/H = usr
H.update_inv_w_uniform()
H.update_body()
/obj/item/clothing/under/proc/toggle_jumpsuit_adjust()
if(!can_adjust)
to_chat(usr, "<span class='warning'>You cannot wear this suit any differently!</span>")
return FALSE
adjusted = !adjusted
if(adjusted)
to_chat(usr, "<span class='notice'>You adjust the suit to wear it more casually.</span>")
if(fitted != FEMALE_UNIFORM_TOP)
fitted = NO_FEMALE_UNIFORM
if(!alt_covers_chest) // for the special snowflake suits that expose the chest when adjusted
body_parts_covered &= ~CHEST
mutantrace_variation &= ~USE_TAUR_CLIP_MASK //How are we supposed to see the uniform otherwise?
else
to_chat(usr, "<span class='notice'>You adjust the suit back to normal.</span>")
fitted = initial(fitted)
if(!alt_covers_chest)
body_parts_covered |= CHEST
if(initial(mutantrace_variation) & USE_TAUR_CLIP_MASK)
mutantrace_variation |= USE_TAUR_CLIP_MASK
return adjusted
return TRUE
/obj/item/clothing/under/rank
dying_key = DYE_REGISTRY_UNDER
@@ -24,7 +24,7 @@
mutantrace_variation = STYLE_DIGITIGRADE|STYLE_NO_ANTHRO_ICON
/obj/item/clothing/under/rank/civilian/util
name = "generic utility uniform"
name = "utility uniform"
desc = "A utility uniform worn by various crew."
icon_state = "utilgen"
item_state = "utilgen"
+2 -2
View File
@@ -3,11 +3,12 @@
name = "captain's jumpsuit"
icon_state = "captain"
item_state = "b_suit"
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 15)
sensor_mode = SENSOR_COORDS
random_sensor = FALSE
/obj/item/clothing/under/rank/captain/util
name = "Command Utiltiy Uniform"
name = "command utility uniform"
desc = "A utility uniform for command personnel."
icon_state = "utilcom"
item_state = "utilcom"
@@ -26,7 +27,6 @@
/obj/item/clothing/under/rank/captain/suit
name = "captain's suit"
desc = "A green suit and yellow necktie. Exemplifies authority."
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 15)
icon_state = "green_suit"
item_state = "dg_suit"
can_adjust = FALSE
+1 -1
View File
@@ -131,7 +131,7 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 10, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 5)
/obj/item/clothing/under/rank/medical/doctor/util
name = "Medical Utility Uniform"
name = "medical utility uniform"
desc = "Utility jumpsuit for medical personnel"
icon_state = "utilmed"
item_state = "utilmed"
+1 -1
View File
@@ -62,7 +62,7 @@
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 0, "wound" = 5)
/obj/item/clothing/under/rank/rnd/scientist/util
name = "Science Utility Uniform"
name = "science utility uniform"
desc = "A utility uniform for science personnel"
icon_state = "utilsci"
item_state = "utilsci"
+6 -4
View File
@@ -172,11 +172,13 @@
body_parts_covered = CHEST|GROIN
/obj/item/clothing/under/misc/gear_harness/toggle_jumpsuit_adjust()
adjusted = !adjusted
if(adjusted)
body_parts_covered = NONE
else
if(!body_parts_covered)
to_chat(usr, "<span class='notice'>Your gear harness is now covering your chest and groin.</span>")
body_parts_covered = CHEST|GROIN
else
to_chat(usr, "<span class='notice'>Your gear harness is no longer covering anything.</span>")
body_parts_covered = NONE
return TRUE
/obj/item/clothing/under/misc/durathread
name = "durathread jumpsuit"
@@ -7,6 +7,12 @@
foodtype = GRAIN
dunkable = TRUE
/obj/item/reagent_containers/food/snacks/store/bread/proc/bread_teleport()
// you did WHAT?
new /mob/living/simple_animal/hostile/bread(get_turf(src))
visible_message("<span class='warning'>[src] begins to deform and grow grotesque tumors!</span>")
qdel(src)
/obj/item/reagent_containers/food/snacks/breadslice
icon = 'icons/obj/food/burgerbread.dmi'
bitesize = 2
@@ -228,3 +234,25 @@
/obj/item/reagent_containers/food/snacks/butterdog/ComponentInitialize()
. = ..()
AddComponent(/datum/component/slippery, 80)
/obj/item/reagent_containers/food/snacks/store/bread/tumor_bread
name = "dead tumor bread"
desc = "It's still within its expiration date, right?."
icon_state = "tumorbread"
slice_path = /obj/item/reagent_containers/food/snacks/breadslice/tumor_bread
list_reagents = list(/datum/reagent/consumable/nutriment = 10, /datum/reagent/toxin = 10)
foodtype = GROSS | GRAIN
tastes = list("wheat and tumors" = 10)
//teleporting tumor bread kills it
/obj/item/reagent_containers/food/snacks/store/bread/tumor_bread/bread_teleport()
visible_message(src, "<span class='warning'>[src] explodes in a horrible mess of tumor and flour!</span>")
qdel(src)
/obj/item/reagent_containers/food/snacks/breadslice/tumor_bread
name = "tumor bread slice"
desc = "A slice of bread filled with tumors!"
icon_state = "tumorbreadslice"
filling_color = "#B2D72C"
list_reagents = list(/datum/reagent/consumable/nutriment = 2, /datum/reagent/toxin = 2)
foodtype = GROSS | GRAIN
@@ -195,7 +195,7 @@
/obj/machinery/microwave/AltClick(mob/user)
. = ..()
if(user.canUseTopic(src, !hasSiliconAccessInArea(user)))
cook()
cook(user)
return TRUE
/obj/machinery/microwave/ui_interact(mob/user)
@@ -226,7 +226,7 @@
if("eject")
eject()
if("use")
cook()
cook(user)
if("examine")
examine(user)
@@ -236,7 +236,7 @@
AM.forceMove(drop_location())
ingredients.Cut()
/obj/machinery/microwave/proc/cook()
/obj/machinery/microwave/proc/cook(mob/user)
if(stat & (NOPOWER|BROKEN))
return
if(operating || broken > 0 || panel_open || !anchored || dirty == 100)
@@ -257,7 +257,7 @@
start_can_fail()
return
break
start()
start(user)
/obj/machinery/microwave/proc/turn_on()
visible_message("\The [src] turns on.", "<span class='italics'>You hear a microwave humming.</span>")
@@ -277,9 +277,9 @@
#define MICROWAVE_MUCK 1
#define MICROWAVE_PRE 2
/obj/machinery/microwave/proc/start()
/obj/machinery/microwave/proc/start(mob/user)
turn_on()
loop(MICROWAVE_NORMAL, 10)
loop(MICROWAVE_NORMAL, 10, user = user)
/obj/machinery/microwave/proc/start_can_fail()
turn_on()
@@ -292,7 +292,7 @@
update_icon()
loop(MICROWAVE_MUCK, 4)
/obj/machinery/microwave/proc/loop(type, time, wait = max(12 - 2 * productivity, 2)) // standard wait is 10
/obj/machinery/microwave/proc/loop(type, time, wait = max(12 - 2 * productivity, 2), mob/user) // standard wait is 10
if(stat & (NOPOWER|BROKEN))
if(type == MICROWAVE_PRE)
pre_fail()
@@ -300,7 +300,7 @@
if(!time)
switch(type)
if(MICROWAVE_NORMAL)
loop_finish()
loop_finish(user)
if(MICROWAVE_MUCK)
muck_finish()
if(MICROWAVE_PRE)
@@ -308,16 +308,21 @@
return
time--
use_power(500)
addtimer(CALLBACK(src, .proc/loop, type, time, wait), wait)
addtimer(CALLBACK(src, .proc/loop, type, time, wait, user), wait)
/obj/machinery/microwave/proc/loop_finish()
/obj/machinery/microwave/proc/loop_finish(mob/user)
operating = FALSE
var/metal = 0
var/cooked_food = 0
for(var/obj/item/O in ingredients)
O.microwave_act(src)
var/cooked_result = O.microwave_act(src)
if(!istype(cooked_result, /obj/item/reagent_containers/food/snacks/badrecipe))
cooked_food += 1
if(O.custom_materials?.len)
metal += O.custom_materials[SSmaterials.GetMaterialRef(/datum/material/iron)]
if(cooked_food && user.client)
user.client.increment_progress("cook", cooked_food)
if(metal)
spark()
@@ -336,8 +341,8 @@
spark()
after_finish_loop()
/obj/machinery/microwave/proc/pre_success()
loop(MICROWAVE_NORMAL, 10)
/obj/machinery/microwave/proc/pre_success(mob/user)
loop(MICROWAVE_NORMAL, 10, user)
/obj/machinery/microwave/proc/muck_finish()
visible_message("<span class='warning'>\The [src] gets covered in muck!</span>")
+12
View File
@@ -18,6 +18,8 @@
var/map_name = "Box Station"
var/map_path = "map_files/BoxStation"
var/map_file = "BoxStation.dmm"
/// Persistence key: Defaults to ckey(map_name). If set to "NO_PERSIST", this map will have NO persistence.
var/persistence_key
var/traits = null
var/space_ruin_levels = 4
@@ -99,6 +101,16 @@
map_path = json["map_path"]
map_file = json["map_file"]
persistence_key = ckey(map_name)
var/json_persistence_key = json["persistence_key"]
if(json_persistence_key)
if(json_persistence_key == "NO_PERSIST")
persistence_key = null
else
persistence_key = json_persistence_key
// "map_file": "BoxStation.dmm"
if (istext(map_file))
if (!fexists("_maps/[map_path]/[map_file]"))
+11
View File
@@ -397,6 +397,17 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
else
to_chat(user, "<span class='warning'>You need one length of cable to attach a string to the coin!</span>")
return
else if(istype(W, /obj/item/card/id))
var/obj/item/card/id/ID = W
if(!ID.registered_account)
to_chat(user, "<span class='warning'>[ID] doesn't have a linked account to deposit into!</span>")
return
for(var/obj/item/holochip/money in src.loc.contents)
ID.attackby(money, user)
for(var/obj/item/stack/spacecash/money in src.loc.contents)
ID.attackby(money, user)
for(var/obj/item/coin/money in src.loc.contents)
ID.attackby(money, user)
else
..()
+2
View File
@@ -30,6 +30,8 @@
if(points)
if(I)
I.mining_points += points
if(usr.client)
usr.client.increment_progress("miner", points)
points = 0
else
to_chat(usr, "<span class='warning'>No ID detected.</span>")
@@ -138,6 +138,18 @@
name = "White Fly"
icon_state = "whitefly"
/datum/sprite_accessory/deco_wings/oakworm
name = "Oak Worm"
icon_state = "oakworm"
/datum/sprite_accessory/deco_wings/witchwing
name = "Witch Wing"
icon_state = "witchwing"
/datum/sprite_accessory/deco_wings/jungle
name = "Jungle"
icon_state = "jungle"
//INSECT WINGS
/datum/sprite_accessory/insect_wings
@@ -380,6 +380,8 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
/mob/dead/observer/Move(NewLoc, direct)
if (SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, NewLoc) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE)
return
if(updatedir)
setDir(direct)//only update dir if we actually need it, so overlays won't spin on base sprites that don't have directions of their own
var/oldloc = loc
@@ -68,7 +68,7 @@ Doesn't work on other aliens/AI.*/
/obj/effect/proc_holder/alien/plant
name = "Plant Weeds"
desc = "Plants some alien weeds."
desc = "Plants Alien weeds which spread resin which heals any alien. These weeds also regenerate plasma faster. Costs 50 Plasma."
plasma_cost = 50
check_turf = TRUE
action_icon_state = "alien_plant"
@@ -83,7 +83,7 @@ Doesn't work on other aliens/AI.*/
/obj/effect/proc_holder/alien/whisper
name = "Whisper"
desc = "Whisper to someone."
desc = "Whisper to someone through the hivemind. Costs 10 Plasma."
plasma_cost = 10
action_icon_state = "alien_whisper"
@@ -143,7 +143,7 @@ Doesn't work on other aliens/AI.*/
/obj/effect/proc_holder/alien/acid
name = "Corrosive Acid"
desc = "Drench an object in acid, destroying it over time."
desc = "Drench an object in acid, destroying it over time. Costs 200 Plasma."
plasma_cost = 200
action_icon_state = "alien_acid"
@@ -189,7 +189,7 @@ Doesn't work on other aliens/AI.*/
/obj/effect/proc_holder/alien/neurotoxin
name = "Spit Neurotoxin"
desc = "Spits neurotoxin at someone, paralyzing them for a short time."
desc = "Activates your Neurotoxin glands. You can shoot paralyzing shots. Each shot costs 50 Plasma."
action_icon_state = "alien_neurotoxin_0"
active = FALSE
@@ -254,7 +254,7 @@ Doesn't work on other aliens/AI.*/
/obj/effect/proc_holder/alien/resin
name = "Secrete Resin"
desc = "Secrete tough malleable resin."
desc = "Secrete tough malleable resin. Costs 55 Plasma."
plasma_cost = 55
check_turf = TRUE
var/list/structures = list(
@@ -20,7 +20,7 @@
/obj/effect/proc_holder/alien/royal/praetorian/evolve
name = "Evolve"
desc = "Produce an internal egg sac capable of spawning children. Only one queen can exist at a time."
desc = "Produce an internal egg sac capable of spawning children. Only one queen can exist at a time. Costs 500 Plasma."
plasma_cost = 500
action_icon_state = "alien_evolve_praetorian"
@@ -57,7 +57,7 @@
//Queen verbs
/obj/effect/proc_holder/alien/lay_egg
name = "Lay Egg"
desc = "Lay an egg to produce huggers to impregnate prey with."
desc = "Lay an egg to produce huggers to impregnate prey with. Costs 75 Plasma."
plasma_cost = 75
check_turf = TRUE
action_icon_state = "alien_egg"
@@ -77,7 +77,7 @@
//Button to let queen choose her praetorian.
/obj/effect/proc_holder/alien/royal/queen/promote
name = "Create Royal Parasite"
desc = "Produce a royal parasite to grant one of your children the honor of being your Praetorian."
desc = "Produce a royal parasite to grant one of your children the honor of being your Praetorian. Costs 500 Plasma."
plasma_cost = 500 //Plasma cost used on promotion, not spawning the parasite.
action_icon_state = "alien_queen_promote"

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