Conflict fix

This commit is contained in:
Artur
2020-02-13 17:52:28 +02:00
157 changed files with 1830 additions and 882 deletions
@@ -735,7 +735,7 @@
/obj/machinery/atmospherics/components/unary/outlet_injector{
dir = 1
},
/turf/open/space/basic,
/turf/open/floor/plating/airless,
/area/engine/engineering)
"Si" = (
/obj/structure/cable/yellow{
@@ -240,7 +240,7 @@
/obj/machinery/atmospherics/components/unary/outlet_injector{
dir = 1
},
/turf/open/space/basic,
/turf/open/floor/plating/airless,
/area/engine/engineering)
"nR" = (
/obj/structure/cable{
@@ -905,14 +905,6 @@
},
/turf/open/space/basic,
/area/space/nearstation)
"VR" = (
/obj/structure/lattice/catwalk,
/obj/machinery/camera/emp_proof{
c_tag = "Tesla Engine Northeast";
network = list("ss13","engine")
},
/turf/open/space/basic,
/area/space/nearstation)
"Wf" = (
/obj/effect/turf_decal/stripes/line{
dir = 9
@@ -1728,7 +1720,7 @@ dz
dz
FP
FP
VR
FP
dz
dz
Yl
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -21911,7 +21911,7 @@
/obj/machinery/atmospherics/pipe/simple/supply/hidden{
dir = 4
},
/obj/machinery/computer/arcade/minesweeper,
/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/wood,
/area/crew_quarters/heads/captain)
"bdj" = (
@@ -55854,6 +55854,10 @@
},
/turf/open/floor/plasteel,
/area/crew_quarters/dorms)
"nZL" = (
/obj/machinery/computer/arcade/minesweeper,
/turf/open/floor/wood,
/area/crew_quarters/heads/captain)
"oax" = (
/obj/structure/table/wood/fancy,
/obj/machinery/light,
@@ -90865,7 +90869,7 @@ aVv
aXg
aYF
aZV
bbw
nZL
bcn
tgH
ben
@@ -62353,7 +62353,7 @@
/turf/open/floor/wood,
/area/crew_quarters/heads/captain/private)
"cbd" = (
/obj/effect/landmark/event_spawn,
/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/wood,
/area/crew_quarters/heads/captain/private)
"cbe" = (
@@ -127169,6 +127169,13 @@
dir = 9
},
/area/science/circuit)
"rjr" = (
/obj/machinery/atmospherics/pipe/manifold/supply/hidden{
dir = 1
},
/obj/effect/landmark/event_spawn,
/turf/open/floor/wood,
/area/crew_quarters/heads/captain/private)
"rqh" = (
/obj/structure/lattice,
/turf/open/space/basic,
@@ -169426,7 +169433,7 @@ bUO
bXf
bUQ
cbe
ccP
rjr
ceH
cgu
cij
+15 -1
View File
@@ -28355,6 +28355,7 @@
/obj/structure/disposalpipe/segment{
dir = 6
},
/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/carpet,
/area/crew_quarters/heads/captain/private)
"bdL" = (
@@ -81645,6 +81646,19 @@
/obj/machinery/door/firedoor,
/turf/open/floor/plating,
/area/crew_quarters/cryopod)
"eQf" = (
/obj/machinery/computer/shuttle/mining/common{
dir = 1
},
/obj/structure/window/reinforced,
/obj/structure/window/reinforced{
dir = 8
},
/obj/structure/window/reinforced{
dir = 4
},
/turf/open/floor/plating,
/area/hallway/primary/port)
"eZe" = (
/obj/structure/cable/yellow{
icon_state = "1-2"
@@ -98511,7 +98525,7 @@ bgA
biz
bkb
blZ
bnM
eQf
bqf
bqf
bqf
+6 -13
View File
@@ -9830,6 +9830,7 @@
/obj/structure/cable{
icon_state = "0-2"
},
/obj/item/twohanded/required/kirbyplants/random,
/turf/open/floor/carpet,
/area/crew_quarters/heads/captain)
"axT" = (
@@ -11162,10 +11163,7 @@
/obj/structure/extinguisher_cabinet{
pixel_x = -24
},
/obj/item/twohanded/required/kirbyplants{
icon_state = "plant-20";
pixel_y = 3
},
/obj/machinery/vending/wardrobe/cap_wardrobe,
/turf/open/floor/plasteel/dark,
/area/crew_quarters/heads/captain)
"aBl" = (
@@ -28097,11 +28095,6 @@
},
/turf/open/floor/plasteel/dark,
/area/science/robotics/lab)
"boY" = (
/obj/effect/turf_decal/bot,
/obj/effect/landmark/start/cyborg,
/turf/open/floor/plasteel,
/area/science/robotics/lab)
"boZ" = (
/obj/structure/table,
/obj/machinery/cell_charger,
@@ -98811,8 +98804,8 @@ beI
bkt
blH
bmO
boY
boY
bnQ
bnQ
bqi
bry
bsZ
@@ -99068,8 +99061,8 @@ bju
bkt
blI
bmO
boY
boY
bnQ
bnQ
bqi
brz
bta
+1 -1
View File
@@ -20,7 +20,7 @@
#define NEW_SS_GLOBAL(varname) if(varname != src){if(istype(varname)){Recover();qdel(varname);}varname = src;}
#define START_PROCESSING(Processor, Datum) if (!(Datum.datum_flags & DF_ISPROCESSING)) {Datum.datum_flags |= DF_ISPROCESSING;Processor.processing += Datum}
#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum
#define STOP_PROCESSING(Processor, Datum) Datum.datum_flags &= ~DF_ISPROCESSING;Processor.processing -= Datum;Processor.currentrun -= Datum
//SubSystem flags (Please design any new flags so that the default is off, to make adding flags to subsystems easier)
+1
View File
@@ -34,6 +34,7 @@
#define CAN_MASTURBATE_WITH (1<<5)
#define MASTURBATE_LINKED_ORGAN (1<<6) //used to pass our mission to the linked organ
#define CAN_CLIMAX_WITH (1<<7)
#define GENITAL_CAN_AROUSE (1<<8)
#define COCK_SIZE_MIN 1
#define COCK_SIZE_MAX 20
+3 -1
View File
@@ -199,4 +199,6 @@
#define LORENTZ_CUMULATIVE_DISTRIBUTION(x, y, s) ( (1/PI)*TORADIANS(arctan((x-y)/s)) + 1/2 )
#define RULE_OF_THREE(a, b, x) ((a*x)/b)
// )
// )
#define MANHATTAN_DISTANCE(a, b) (abs(a.x - b.x) + abs(a.y - b.y))
+2 -2
View File
@@ -1,5 +1,5 @@
#define PROFILE_START ;PROFILE_STORE = list();PROFILE_SET;
#define PROFILE_STOP ;PROFILE_STORE = null;
#define LINE_PROFILE_START ;PROFILE_STORE = list();PROFILE_SET;
#define LINE_PROFILE_STOP ;PROFILE_STORE = null;
#define PROFILE_SET ;PROFILE_TIME = TICK_USAGE_REAL; PROFILE_LINE = __LINE__; PROFILE_FILE = __FILE__; PROFILE_SLEEPCHECK = world.time;
+1
View File
@@ -47,6 +47,7 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_PROFILER 101
#define INIT_ORDER_FAIL2TOPIC 22
#define INIT_ORDER_TITLE 20
#define INIT_ORDER_GARBAGE 19
+1
View File
@@ -1,4 +1,5 @@
#define TGS_EXTERNAL_CONFIGURATION
#define TGS_V3_API
#define TGS_DEFINE_AND_SET_GLOBAL(Name, Value) GLOBAL_VAR_INIT(##Name, ##Value); GLOBAL_PROTECT(##Name)
#define TGS_READ_GLOBAL(Name) GLOB.##Name
#define TGS_WRITE_GLOBAL(Name, Value) GLOB.##Name = ##Value
+21 -5
View File
@@ -107,6 +107,22 @@
var/commit //full sha of compiled commit
var/origin_commit //full sha of last known remote commit. This may be null if the TGS repository is not currently tracking a remote branch
//represents a version of tgstation-server
/datum/tgs_version
var/suite //The suite version, can be >=3
//this group of variables can be null to represent a wild card
var/major //The major version
var/minor //The minor version
var/patch //The patch version
var/raw_parameter //The unparsed parameter
var/deprefixed_parameter //The version only bit of raw_parameter
//if the tgs_version is a wildcard version
/datum/tgs_version/proc/Wildcard()
return
//represents a merge of a GitHub pull request
/datum/tgs_revision_information/test_merge
var/number //pull request number
@@ -155,22 +171,22 @@
//FUNCTIONS
//Returns the respective string version of the API
//Returns the respective supported /datum/tgs_version of the API
/world/proc/TgsMaximumAPIVersion()
return
/world/proc/TgsMinimumAPIVersion()
return
//Gets the current version of the server tools running the server
/world/proc/TgsVersion()
return
//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
//No function below this succeeds if it returns FALSE
/world/proc/TgsAvailable()
return
//Gets the current /datum/tgs_version of the server tools running the server
/world/proc/TgsVersion()
return
/world/proc/TgsInstanceName()
return
+1 -4
View File
@@ -57,14 +57,11 @@ GLOBAL_LIST_EMPTY(ipc_antennas_list)
//Genitals and Arousal Lists
GLOBAL_LIST_EMPTY(genitals_list)
GLOBAL_LIST_EMPTY(cock_shapes_list)//global_lists.dm for the list initializations //Now also _DATASTRUCTURES globals.dm
GLOBAL_LIST_EMPTY(cock_shapes_icons) //Associated list for names->icon_states for cockshapes.
GLOBAL_LIST_EMPTY(cock_shapes_list)
GLOBAL_LIST_EMPTY(gentlemans_organ_names)
GLOBAL_LIST_EMPTY(balls_shapes_list)
GLOBAL_LIST_EMPTY(balls_shapes_icons)
GLOBAL_LIST_EMPTY(breasts_size_list)
GLOBAL_LIST_EMPTY(breasts_shapes_list)
GLOBAL_LIST_EMPTY(breasts_shapes_icons)
GLOBAL_LIST_EMPTY(vagina_shapes_list)
GLOBAL_LIST_INIT(cum_into_containers_list, list(/obj/item/reagent_containers/food/snacks/pie)) //Yer fuggin snowflake name list jfc
GLOBAL_LIST_INIT(dick_nouns, list("dick","cock","member","shaft"))
+1 -12
View File
@@ -51,26 +51,15 @@
init_sprite_accessory_subtypes(/datum/sprite_accessory/antenna, GLOB.ipc_antennas_list, roundstart = TRUE)
//genitals
init_sprite_accessory_subtypes(/datum/sprite_accessory/penis, GLOB.cock_shapes_list)
for(var/K in GLOB.cock_shapes_list)
var/datum/sprite_accessory/penis/value = GLOB.cock_shapes_list[K]
GLOB.cock_shapes_icons[K] = value.icon_state
init_sprite_accessory_subtypes(/datum/sprite_accessory/vagina, GLOB.vagina_shapes_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/breasts, GLOB.breasts_shapes_list)
init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list)
GLOB.breasts_size_list = list ("a", "b", "c", "d", "e") //We need the list to choose from initialized, but it's no longer a sprite_accessory thing.
GLOB.gentlemans_organ_names = list("phallus", "willy", "dick", "prick", "member", "tool", "gentleman's organ",
"cock", "wang", "knob", "dong", "joystick", "pecker", "johnson", "weenie", "tadger", "schlong", "thirsty ferret",
"baloney pony", "schlanger", "Mutton dagger", "old blind bob","Hanging Johnny", "fishing rod", "Tally whacker", "polly rocket",
"One eyed trouser trout", "Ding dong", "ankle spanker", "Pork sword", "engine cranker", "Harry hot dog", "Davy Crockett",
"Kidney cracker", "Heat seeking moisture missile", "Giggle stick", "love whistle", "Tube steak", "Uncle Dick", "Purple helmet warrior")
for(var/K in GLOB.breasts_shapes_list)
var/datum/sprite_accessory/breasts/value = GLOB.breasts_shapes_list[K]
GLOB.breasts_shapes_icons[K] = value.icon_state
init_sprite_accessory_subtypes(/datum/sprite_accessory/testicles, GLOB.balls_shapes_list)
for(var/K in GLOB.balls_shapes_list)
var/datum/sprite_accessory/testicles/value = GLOB.balls_shapes_list[K]
GLOB.balls_shapes_icons[K] = value.icon_state
for(var/gpath in subtypesof(/obj/item/organ/genital))
var/obj/item/organ/genital/G = gpath
+22
View File
@@ -1543,3 +1543,25 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
for(var/each_item in items_list)
for(var/i in 1 to items_list[each_item])
new each_item(where_to)
//sends a message to chat
//config_setting should be one of the following
//null - noop
//empty string - use TgsTargetBroadcast with admin_only = FALSE
//other string - use TgsChatBroadcast with the tag that matches config_setting, only works with TGS4, if using TGS3 the above method is used
/proc/send2chat(message, config_setting)
if(config_setting == null || !world.TgsAvailable())
return
var/datum/tgs_version/version = world.TgsVersion()
if(config_setting == "" || version.suite == 3)
world.TgsTargetedChatBroadcast(message, FALSE)
return
var/list/channels_to_use = list()
for(var/I in world.TgsChatChannelInfo())
var/datum/tgs_chat_channel/channel = I
if(channel.tag == config_setting)
channels_to_use += channel
if(channels_to_use.len)
world.TgsChatBroadcast()
+57
View File
@@ -0,0 +1,57 @@
// Basic geometry things.
/datum/vector/
var/x = 0
var/y = 0
/datum/vector/New(var/x, var/y)
src.x = x
src.y = y
/datum/vector/proc/duplicate()
return new /datum/vector(x, y)
/datum/vector/proc/euclidian_norm()
return sqrt(x*x + y*y)
/datum/vector/proc/squared_norm()
return x*x + y*y
/datum/vector/proc/normalize()
var/norm = euclidian_norm()
x = x/norm
y = y/norm
return src
/datum/vector/proc/chebyshev_norm()
return max(abs(x), abs(y))
/datum/vector/proc/chebyshev_normalize()
var/norm = chebyshev_norm()
x = x/norm
y = y/norm
return src
/datum/vector/proc/is_integer()
return ISINTEGER(x) && ISINTEGER(y)
/atom/movable/proc/vector_translate(var/datum/vector/V, var/delay)
var/turf/T = get_turf(src)
var/turf/destination = locate(T.x + V.x, T.y + V.y, z)
var/datum/vector/V_norm = V.duplicate()
V_norm.chebyshev_normalize()
if (!V_norm.is_integer())
return
var/turf/destination_temp
while (destination_temp != destination)
destination_temp = locate(T.x + V_norm.x, T.y + V_norm.y, z)
forceMove(destination_temp)
T = get_turf(src)
sleep(delay + world.tick_lag) // Shortest possible time to sleep
/atom/proc/get_translated_turf(var/datum/vector/V)
var/turf/T = get_turf(src)
return locate(T.x + V.x, T.y + V.y, z)
/proc/atoms2vector(var/atom/A, var/atom/B)
return new /datum/vector((B.x - A.x), (B.y - A.y)) // Vector from A -> B
+21 -18
View File
@@ -1,19 +1,22 @@
/**
*This is the proc that handles the order of an item_attack.
*The order of procs called is:
*tool_act on the target. If it returns TRUE, the chain will be stopped.
*pre_attack() on src. If this returns TRUE, the chain will be stopped.
*attackby on the target. If it returns TRUE, the chain will be stopped.
*and lastly
*afterattack. The return value does not matter.
*/
/obj/item/proc/melee_attack_chain(mob/user, atom/target, params)
if(!tool_attack_chain(user, target) && pre_attack(target, user, params))
// Return 1 in attackby() to prevent afterattack() effects (when safely moving items for example)
var/resolved = target.attackby(src, user, params)
if(!resolved && target && !QDELETED(src))
afterattack(target, user, 1, params) // 1: clicking something Adjacent
//Checks if the item can work as a tool, calling the appropriate tool behavior on the target
/obj/item/proc/tool_attack_chain(mob/user, atom/target)
if(!tool_behaviour)
return FALSE
return target.tool_act(user, src, tool_behaviour)
if(tool_behaviour && target.tool_act(user, src, tool_behaviour))
return
if(pre_attack(target, user, params))
return
if(target.attackby(src,user, params))
return
if(QDELETED(src) || QDELETED(target))
return
afterattack(target, user, TRUE, params)
// Called when the item is in the active hand, and clicked; alternately, there is an 'activate held object' verb or you can hit pagedown.
/obj/item/proc/attack_self(mob/user)
@@ -23,8 +26,8 @@
/obj/item/proc/pre_attack(atom/A, mob/living/user, params) //do stuff before attackby!
if(SEND_SIGNAL(src, COMSIG_ITEM_PRE_ATTACK, A, user, params) & COMPONENT_NO_ATTACK)
return FALSE
return TRUE //return FALSE to avoid calling attackby after this proc does stuff
return TRUE
return FALSE //return TRUE to avoid calling attackby after this proc does stuff
// No comment
/atom/proc/attackby(obj/item/W, mob/user, params)
@@ -112,7 +115,7 @@
send_item_attack_message(I, user)
if(I.force)
apply_damage(totitemdamage, I.damtype) //CIT CHANGE - replaces I.force with totitemdamage
if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW))
if(I.damtype == BRUTE && !HAS_TRAIT(src, TRAIT_NOMARROW))
if(prob(33))
I.add_mob_blood(src)
var/turf/location = get_turf(src)
@@ -1,3 +1,5 @@
/datum/config_entry/flag/auto_profile // Automatically start profiler on server start
/datum/config_entry/flag/autoadmin // if autoadmin is enabled
protection = CONFIG_ENTRY_LOCKED
@@ -392,6 +394,13 @@
config_entry_value = 50
/datum/config_entry/flag/irc_announce_new_game
deprecated_by = /datum/config_entry/string/chat_announce_new_game
/datum/config_entry/flag/irc_announce_new_game/DeprecationUpdate(value)
return "" //default broadcast
/datum/config_entry/string/chat_announce_new_game
config_entry_value = null
/datum/config_entry/flag/debug_admin_hrefs
@@ -4,25 +4,16 @@
#define END_STAGE 4
//Used for all kinds of weather, ex. lavaland ash storms.
SUBSYSTEM_DEF(weather)
PROCESSING_SUBSYSTEM_DEF(weather)
name = "Weather"
flags = SS_BACKGROUND
wait = 10
runlevels = RUNLEVEL_GAME
var/list/processing = list()
var/list/eligible_zlevels = list()
var/list/next_hit_by_zlevel = list() //Used by barometers to know when the next storm is coming
/datum/controller/subsystem/weather/fire()
// process active weather
for(var/V in processing)
var/datum/weather/W = V
if(W.aesthetic || W.stage != MAIN_STAGE)
continue
for(var/i in GLOB.mob_living_list)
var/mob/living/L = i
if(W.can_weather_act(L))
W.weather_act(L)
/datum/controller/subsystem/processing/weather/fire()
. = ..() //Active weather is handled by . = ..() processing subsystem base fire().
// start random weather on relevant levels
for(var/z in eligible_zlevels)
@@ -34,7 +25,7 @@ SUBSYSTEM_DEF(weather)
addtimer(CALLBACK(src, .proc/make_eligible, z, possible_weather), randTime + initial(W.weather_duration_upper), TIMER_UNIQUE) //Around 5-10 minutes between weathers
next_hit_by_zlevel["[z]"] = world.time + randTime + initial(W.telegraph_duration)
/datum/controller/subsystem/weather/Initialize(start_timeofday)
/datum/controller/subsystem/processing/weather/Initialize(start_timeofday)
for(var/V in subtypesof(/datum/weather))
var/datum/weather/W = V
var/probability = initial(W.probability)
@@ -47,7 +38,7 @@ SUBSYSTEM_DEF(weather)
eligible_zlevels["[z]"][W] = probability
return ..()
/datum/controller/subsystem/weather/proc/run_weather(datum/weather/weather_datum_type, z_levels)
/datum/controller/subsystem/processing/weather/proc/run_weather(datum/weather/weather_datum_type, z_levels)
if (istext(weather_datum_type))
for (var/V in subtypesof(/datum/weather))
var/datum/weather/W = V
@@ -69,11 +60,11 @@ SUBSYSTEM_DEF(weather)
var/datum/weather/W = new weather_datum_type(z_levels)
W.telegraph()
/datum/controller/subsystem/weather/proc/make_eligible(z, possible_weather)
/datum/controller/subsystem/processing/weather/proc/make_eligible(z, possible_weather)
eligible_zlevels[z] = possible_weather
next_hit_by_zlevel["[z]"] = null
/datum/controller/subsystem/weather/proc/get_weather(z, area/active_area)
/datum/controller/subsystem/processing/weather/proc/get_weather(z, area/active_area)
var/datum/weather/A
for(var/V in processing)
var/datum/weather/W = V
+63
View File
@@ -0,0 +1,63 @@
#define PROFILER_FILENAME "profiler.json"
SUBSYSTEM_DEF(profiler)
name = "Profiler"
init_order = INIT_ORDER_PROFILER
runlevels = RUNLEVELS_DEFAULT | RUNLEVEL_LOBBY
wait = 3000
flags = SS_NO_TICK_CHECK
var/fetch_cost = 0
var/write_cost = 0
/datum/controller/subsystem/profiler/stat_entry(msg)
msg += "F:[round(fetch_cost,1)]ms"
msg += "|W:[round(write_cost,1)]ms"
..(msg)
/datum/controller/subsystem/profiler/Initialize()
if(CONFIG_GET(flag/auto_profile))
StartProfiling()
else
StopProfiling() //Stop the early start from world/New
return ..()
/datum/controller/subsystem/profiler/fire()
if(CONFIG_GET(flag/auto_profile))
DumpFile()
/datum/controller/subsystem/profiler/Shutdown()
if(CONFIG_GET(flag/auto_profile))
DumpFile()
return ..()
/datum/controller/subsystem/profiler/proc/StartProfiling()
#if DM_BUILD < 1506 || DM_VERSION < 513
stack_trace("Auto profiling unsupported on this byond version")
CONFIG_SET(flag/auto_profile, FALSE)
#else
world.Profile(PROFILE_START)
#endif
/datum/controller/subsystem/profiler/proc/StopProfiling()
#if DM_BUILD >= 1506 && DM_VERSION >= 513
world.Profile(PROFILE_STOP)
#endif
/datum/controller/subsystem/profiler/proc/DumpFile()
#if DM_BUILD < 1506 || DM_VERSION < 513
stack_trace("Auto profiling unsupported on this byond version")
CONFIG_SET(flag/auto_profile, FALSE)
#else
var/timer = TICK_USAGE_REAL
var/current_profile_data = world.Profile(PROFILE_REFRESH,format="json")
fetch_cost = MC_AVERAGE(fetch_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
CHECK_TICK
if(!length(current_profile_data)) //Would be nice to have explicit proc to check this
stack_trace("Warning, profiling stopped manually before dump.")
var/json_file = file("[GLOB.log_directory]/[PROFILER_FILENAME]")
if(fexists(json_file))
fdel(json_file)
timer = TICK_USAGE_REAL
WRITE_FILE(json_file, current_profile_data)
write_cost = MC_AVERAGE(write_cost, TICK_DELTA_TO_MS(TICK_USAGE_REAL - timer))
#endif
+2 -2
View File
@@ -81,8 +81,8 @@ SUBSYSTEM_DEF(server_maint)
co.ehjax_send(data = "roundrestart")
if(server) //if you set a server location in config.txt, it sends you there instead of trying to reconnect to the same world address. -- NeoFite
C << link("byond://[server]")
var/tgsversion = world.TgsVersion()
var/datum/tgs_version/tgsversion = world.TgsVersion()
if(tgsversion)
SSblackbox.record_feedback("text", "server_tools", 1, tgsversion)
SSblackbox.record_feedback("text", "server_tools", 1, tgsversion.raw_parameter)
#undef PING_BUFFER_TIME
+2 -3
View File
@@ -158,8 +158,7 @@ SUBSYSTEM_DEF(ticker)
for(var/client/C in GLOB.clients)
window_flash(C, ignorepref = TRUE) //let them know lobby has opened up.
to_chat(world, "<span class='boldnotice'>Welcome to [station_name()]!</span>")
if(CONFIG_GET(flag/irc_announce_new_game))
world.TgsTargetedChatBroadcast("New round starting on [SSmapping.config.map_name]!", FALSE)
send2chat("New round starting on [SSmapping.config.map_name]!", CONFIG_GET(string/chat_announce_new_game))
current_state = GAME_STATE_PREGAME
//Everyone who wants to be an observer is now spawned
create_observers()
@@ -488,7 +487,7 @@ SUBSYSTEM_DEF(ticker)
if("SCORE")
SSvote.initiate_vote("map","server",hideresults=TRUE,votesystem = MAJORITY_JUDGEMENT_VOTING)
// fallback
SSvote.initiate_vote("map","server",hideresults=TRUE,votesystem = MAJORITY_JUDGEMENT_VOTING)
SSvote.initiate_vote("map","server",hideresults=TRUE)
/datum/controller/subsystem/ticker/proc/HasRoundStarted()
return current_state >= GAME_STATE_PLAYING
+8
View File
@@ -820,3 +820,11 @@
target.layer = old_layer
target.plane = old_plane
current_button.appearance_cache = target.appearance
/proc/get_action_of_type(mob/M, var/action_type)
if(!M.actions || !ispath(action_type, /datum/action))
return
for(var/datum/action/A in M.actions)
if(istype(A, action_type))
return A
return
+24 -15
View File
@@ -25,6 +25,7 @@ GLOBAL_LIST_EMPTY(uplinks)
var/unlock_code
var/failsafe_code
var/datum/ui_state/checkstate
var/compact_mode = FALSE
var/debug = FALSE
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate)
@@ -122,7 +123,7 @@ GLOBAL_LIST_EMPTY(uplinks)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()
@@ -139,29 +140,35 @@ GLOBAL_LIST_EMPTY(uplinks)
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["compact_mode"] = compact_mode
return data
/datum/component/uplink/ui_static_data(mob/user)
var/list/data = list()
data["categories"] = list()
for(var/category in uplink_items)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
if(category == selected_cat)
for(var/item in uplink_items[category])
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
for(var/item in uplink_items[category])
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
continue
if(I.restricted_roles.len)
var/is_inaccessible = TRUE
for(var/R in I.restricted_roles)
if(R == user.mind.assigned_role || debug)
is_inaccessible = FALSE
if(is_inaccessible)
continue
if(I.restricted_roles.len)
var/is_inaccessible = 1
for(var/R in I.restricted_roles)
if(R == user.mind.assigned_role || debug)
is_inaccessible = 0
if(is_inaccessible)
continue
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
"desc" = I.desc,
))
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
"desc" = I.desc,
))
data["categories"] += list(cat)
return data
@@ -189,6 +196,8 @@ GLOBAL_LIST_EMPTY(uplinks)
SStgui.close_uis(src)
if("select")
selected_cat = params["category"]
if("compact_toggle")
compact_mode = !compact_mode
return TRUE
/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
+1 -1
View File
@@ -1,5 +1,5 @@
/datum/element/mob_holder
element_flags = ELEMENT_BESPOKE
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
var/worn_state
var/alt_worn
+1 -1
View File
@@ -1,6 +1,6 @@
/datum/element/wuv //D'awwwww
element_flags = ELEMENT_BESPOKE
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
//the for the me emote proc call when petted.
var/pet_emote
+4 -2
View File
@@ -27,7 +27,8 @@
for(var/line in testmerge)
var/datum/tgs_revision_information/test_merge/tm = line
msg += "Test merge active of PR #[tm.number] commit [tm.commit]"
msg += "Test merge active of PR #[tm.number] commit [tm.pull_request_commit]"
SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.pull_request_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
if(commit && commit != originmastercommit)
msg += "HEAD: [commit]"
@@ -75,7 +76,8 @@
else if(!pc)
msg += "No commit information"
if(world.TgsAvailable())
msg += "Server tools version: [world.TgsVersion()]"
var/datum/tgs_version/version = world.TgsVersion()
msg += "Server tools version: [version.raw_parameter]"
// Game mode odds
msg += "<br><b>Current Informational Settings:</b>"
+2 -3
View File
@@ -144,9 +144,8 @@ GLOBAL_LIST_EMPTY(family_heirlooms)
/datum/quirk/nearsighted/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
H.put_in_hands(glasses)
H.equip_to_slot(glasses, SLOT_GLASSES)
H.regenerate_icons() //this is to remove the inhand icon, which persists even if it's not in their hands
if(!H.equip_to_slot_if_possible(glasses, SLOT_GLASSES))
H.put_in_hands(glasses)
/datum/quirk/nyctophobia
name = "Nyctophobia"
+9 -1
View File
@@ -61,7 +61,7 @@
if(A.z in impacted_z_levels)
impacted_areas |= A
weather_duration = rand(weather_duration_lower, weather_duration_upper)
START_PROCESSING(SSweather, src)
START_PROCESSING(SSweather, src) //The reason this doesn't start and stop at main stage is because processing list is also used to see active running weathers (for example, you wouldn't want two ash storms starting at once.)
update_areas()
for(var/M in GLOB.player_list)
var/turf/mob_turf = get_turf(M)
@@ -107,6 +107,14 @@
STOP_PROCESSING(SSweather, src)
update_areas()
/datum/weather/process()
if(aesthetic || (stage != MAIN_STAGE))
return
for(var/i in GLOB.mob_living_list)
var/mob/living/L = i
if(can_weather_act(L))
weather_act(L)
/datum/weather/proc/can_weather_act(mob/living/L) //Can this weather impact a mob?
var/turf/mob_turf = get_turf(L)
if(mob_turf && !(mob_turf.z in impacted_z_levels))
+9 -15
View File
@@ -2,13 +2,11 @@
#define FONT_SIZE "5pt"
#define FONT_COLOR "#09f"
#define FONT_STYLE "Arial Black"
#define MAX_TIMER 9000
#define PRESET_SHORT 1200
#define PRESET_MEDIUM 1800
#define PRESET_LONG 3000
#define MAX_TIMER 15 MINUTES
#define PRESET_SHORT 2 MINUTES
#define PRESET_MEDIUM 3 MINUTES
#define PRESET_LONG 5 MINUTES
///////////////////////////////////////////////////////////////////////////////////////////////
// Brig Door control displays.
@@ -25,7 +23,7 @@
desc = "A remote control for a door."
req_access = list(ACCESS_SECURITY)
density = FALSE
var/id = null // id of linked machinery/lockers
var/id // id of linked machinery/lockers
var/activation_time = 0
var/timer_duration = 0
@@ -43,8 +41,6 @@
Radio = new/obj/item/radio(src)
Radio.listening = 0
/obj/machinery/door_timer/Initialize()
. = ..()
if(id != null)
for(var/obj/machinery/door/window/brigdoor/M in urange(20, src))
if (M.id == id)
@@ -71,7 +67,7 @@
return
if(timing)
if(world.realtime - activation_time >= timer_duration)
if(REALTIMEOFDAY - activation_time >= timer_duration)
timer_end() // open doors, reset timer, clear status screen
update_icon()
@@ -80,14 +76,13 @@
..()
update_icon()
// open/closedoor checks if door_timer has power, if so it checks if the
// linked door is open/closed (by density) then opens it/closes it.
/obj/machinery/door_timer/proc/timer_start()
if(stat & (NOPOWER|BROKEN))
return 0
activation_time = world.realtime
activation_time = REALTIMEOFDAY
timing = TRUE
for(var/obj/machinery/door/window/brigdoor/door in targets)
@@ -104,7 +99,6 @@
C.update_icon()
return 1
/obj/machinery/door_timer/proc/timer_end(forced = FALSE)
if(stat & (NOPOWER|BROKEN))
@@ -136,7 +130,7 @@
/obj/machinery/door_timer/proc/time_left(seconds = FALSE)
. = max(0,timer_duration - (activation_time ? world.realtime - activation_time : 0))
. = max(0,timer_duration - (activation_time ? REALTIMEOFDAY - activation_time : 0))
if(seconds)
. /= 10
@@ -240,7 +234,7 @@
preset_time = PRESET_LONG
. = set_timer(preset_time)
if(timing)
activation_time = world.realtime
activation_time = REALTIMEOFDAY
else
. = FALSE
+1 -1
View File
@@ -345,7 +345,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list(
A = get_turf(A)
var/can_make_pipe = (isturf(A) || is_type_in_typecache(A, make_pipe_whitelist))
. = FALSE
. = TRUE
if((mode & DESTROY_MODE) && istype(A, /obj/item/pipe) || istype(A, /obj/structure/disposalconstruct) || istype(A, /obj/structure/c_transit_tube) || istype(A, /obj/structure/c_transit_tube_pod) || istype(A, /obj/item/pipe_meter))
to_chat(user, "<span class='notice'>You start destroying a pipe...</span>")
@@ -38,6 +38,7 @@
/obj/item/geiger_counter/Destroy()
STOP_PROCESSING(SSobj, src)
QDEL_NULL(soundloop)
return ..()
/obj/item/geiger_counter/process()
@@ -51,6 +51,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
new/datum/stack_recipe("closet", /obj/structure/closet, 2, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
null, \
new/datum/stack_recipe("canister", /obj/machinery/portable_atmospherics/canister, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("trash bin", /obj/structure/closet/crate/bin, 10, time = 15, one_per_turf = TRUE, on_floor = TRUE),\
null, \
new/datum/stack_recipe("floor tile", /obj/item/stack/tile/plasteel, 1, 4, 20), \
new/datum/stack_recipe("metal rod", /obj/item/stack/rods, 1, 2, 60), \
@@ -733,7 +734,6 @@ GLOBAL_LIST_INIT(plastic_recipes, list(
new /datum/stack_recipe("opaque plastic flaps", /obj/structure/plasticflaps/opaque, 5, one_per_turf = TRUE, on_floor = TRUE, time = 40), \
new /datum/stack_recipe("water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/empty), \
new /datum/stack_recipe("large water bottle", /obj/item/reagent_containers/glass/beaker/waterbottle/large/empty,3), \
new /datum/stack_recipe("large trash cart", /obj/structure/closet/crate/bin,50),\
new /datum/stack_recipe("wet floor sign", /obj/item/caution, 2)))
/obj/item/stack/sheet/plastic
+3 -1
View File
@@ -399,10 +399,12 @@
/obj/item/storage/bag/bio/ComponentInitialize()
. = ..()
var/datum/component/storage/STR = GetComponent(/datum/component/storage)
STR.max_w_class = WEIGHT_CLASS_NORMAL //Allows you to pick up Lungs, Liver, and Stomach
STR.max_combined_w_class = 200
STR.max_items = 25
STR.insert_preposition = "in"
STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube))
STR.can_hold = typecacheof(list(/obj/item/slime_extract, /obj/item/reagent_containers/syringe, /obj/item/reagent_containers/glass/beaker, /obj/item/reagent_containers/glass/bottle, /obj/item/reagent_containers/blood, /obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/food/snacks/deadmouse, /obj/item/reagent_containers/food/snacks/monkeycube, /obj/item/organ, /obj/item/reagent_containers/food/snacks/meat/slab, /obj/item/bodypart))
STR.cant_hold = typecacheof(list(/obj/item/organ/brain, /obj/item/organ/liver/cybernetic, /obj/item/organ/heart/cybernetic, /obj/item/organ/lungs/cybernetic, /obj/item/organ/tongue/cybernetic, /obj/item/organ/ears/cybernetic, /obj/item/organ/eyes/robotic, /obj/item/organ/cyberimp))
/obj/item/storage/bag/bio/holding
name = "bio bag of holding"
+1 -1
View File
@@ -138,7 +138,7 @@
/obj/item/hand_tele/pre_attack(atom/target, mob/user, params)
if(try_dispel_portal(target, user))
return FALSE
return TRUE
return ..()
/obj/item/hand_tele/proc/try_dispel_portal(atom/target, mob/user)
@@ -2,38 +2,21 @@
name = "\proper captain's locker"
req_access = list(ACCESS_CAPTAIN)
icon_state = "cap"
/obj/structure/closet/secure_closet/captains/PopulateContents()
/obj/structure/closet/secure_closet/captains/PopulateContents() //Excess clothing and such can be found in the Captain's Wardrobe. You can also find this file in code/modules/vending/wardrobes.
..()
new /obj/item/clothing/suit/hooded/wintercoat/captain(src)
new /obj/item/storage/backpack/captain(src)
new /obj/item/storage/backpack/satchel/cap(src)
new /obj/item/storage/backpack/duffelbag/captain(src)
new /obj/item/clothing/neck/cloak/cap(src)
new /obj/item/clothing/neck/petcollar(src)
new /obj/item/clothing/neck/petcollar(src) //I considered removing the pet stuff too but eh, who knows. We might get Renault back. Plus I guess you could use that collar for... other means. Aren't you supposed to be guarding the disk?
new /obj/item/pet_carrier(src)
new /obj/item/clothing/shoes/sneakers/brown(src)
new /obj/item/clothing/under/rank/captain(src)
new /obj/item/clothing/under/rank/captain/skirt(src)
new /obj/item/clothing/suit/armor/vest/capcarapace(src)
new /obj/item/clothing/head/caphat(src)
new /obj/item/clothing/under/captainparade(src)
new /obj/item/clothing/suit/armor/vest/capcarapace/alt(src)
new /obj/item/clothing/head/caphat/parade(src)
new /obj/item/clothing/head/caphat/beret(src)
new /obj/item/clothing/suit/captunic(src)
new /obj/item/clothing/under/rank/captain/femformal(src) //citadel edit
new /obj/item/clothing/head/crown/fancy(src)
new /obj/item/cartridge/captain(src)
new /obj/item/storage/box/silver_ids(src)
new /obj/item/radio/headset/heads/captain/alt(src)
new /obj/item/radio/headset/heads/captain(src)
new /obj/item/clothing/glasses/sunglasses/gar/supergar(src)
new /obj/item/clothing/gloves/color/captain(src)
new /obj/item/storage/belt/sabre(src)
new /obj/item/gun/energy/e_gun(src)
new /obj/item/door_remote/captain(src)
new /obj/item/storage/photo_album/Captain(src)
new /obj/item/clothing/head/caphat/beret/white(src)
/obj/structure/closet/secure_closet/hop
name = "\proper head of personnel's locker"
@@ -47,6 +47,12 @@
if(manifest)
tear_manifest(user)
/obj/structure/closet/crate/tool_interact(obj/item/W, mob/user)
if(W.tool_behaviour == TOOL_WIRECUTTER && manifest)
tear_manifest(user)
return TRUE
return ..()
/obj/structure/closet/crate/open(mob/living/user)
. = ..()
if(. && manifest)
@@ -4,8 +4,8 @@
icon_state = "largebins"
open_sound = 'sound/effects/bin_open.ogg'
close_sound = 'sound/effects/bin_close.ogg'
material_drop = /obj/item/stack/sheet/plastic
material_drop_amount = 40
material_drop = /obj/item/stack/sheet/metal
material_drop_amount = 10
anchored = TRUE
horizontal = FALSE
delivery_icon = null
+5 -1
View File
@@ -10,6 +10,10 @@ GLOBAL_LIST(topic_status_cache)
/world/New()
enable_debugger()
#if DM_VERSION >= 513 && DM_BUILD >= 1506
world.Profile(PROFILE_START)
#endif
log_world("World loaded at [TIME_STAMP("hh:mm:ss", FALSE)]!")
SetupExternalRSC()
@@ -18,7 +22,7 @@ GLOBAL_LIST(topic_status_cache)
make_datum_references_lists() //initialises global lists for referencing frequently used datums (so that we only ever do it once)
TgsNew()
TgsNew(minimum_required_security_level = TGS_SECURITY_TRUSTED)
GLOB.revdata = new
+2 -2
View File
@@ -1058,7 +1058,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
set name = "Start Line Profiling"
set desc = "Starts tracking line by line profiling for code lines that support it"
PROFILE_START
LINE_PROFILE_START
message_admins("<span class='adminnotice'>[key_name_admin(src)] started line by line profiling.</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Start Line Profiling")
@@ -1069,7 +1069,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention)
set name = "Stops Line Profiling"
set desc = "Stops tracking line by line profiling for code lines that support it"
PROFILE_STOP
LINE_PROFILE_STOP
message_admins("<span class='adminnotice'>[key_name_admin(src)] stopped line by line profiling.</span>")
SSblackbox.record_feedback("tally", "admin_verb", 1, "Stop Line Profiling")
@@ -68,8 +68,8 @@
/obj/item/clockwork/replica_fabricator/pre_attack(atom/target, mob/living/user, params)
if(!target || !user || !is_servant_of_ratvar(user) || istype(target, /obj/item/storage))
return TRUE
return fabricate(target, user)
return ..()
return !fabricate(target, user)
//A note here; return values are for if we CAN BE PUT ON A TABLE, not IF WE ARE SUCCESSFUL, unless no_table_check is TRUE
/obj/item/clockwork/replica_fabricator/proc/fabricate(atom/target, mob/living/user, silent, no_table_check)
+1 -1
View File
@@ -116,7 +116,7 @@ This file contains the cult dagger and rune list code
if(user.blood_volume)
user.apply_damage(initial(rune_to_scribe.scribe_damage), BRUTE, pick(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM))
var/scribe_mod = initial(rune_to_scribe.scribe_delay)
if(istype(get_turf(user), /turf/open/floor/engine/cult))
if(istype(get_turf(user), /turf/open/floor/engine/cult) && !(ispath(rune_to_scribe, /obj/effect/rune/narsie)))
scribe_mod *= 0.5
if(!do_after(user, scribe_mod, target = get_turf(user)))
for(var/V in shields)
@@ -381,6 +381,7 @@
playsound(src, 'sound/machines/nuke/confirm_beep.ogg', 50, FALSE)
set_active()
update_ui_mode()
. = TRUE
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
if("anchor")
@@ -251,6 +251,7 @@
/obj/machinery/atmospherics/components/unary/cryo_cell/close_machine(mob/living/carbon/user)
if((isnull(user) || istype(user)) && state_open && !panel_open)
..(user)
reagent_transfer = 0
return occupant
/obj/machinery/atmospherics/components/unary/cryo_cell/container_resist(mob/living/user)
@@ -466,11 +466,13 @@
// MELEE GANG
/obj/item/claymore/ctf
slot_flags = SLOT_BACK
slot_flags = ITEM_SLOT_BACK
armour_penetration = 100
total_mass = 1
/obj/item/claymore/ctf/pre_attack(atom/target, mob/user, params)
if(!is_ctf_target(target))
return FALSE
return TRUE
return ..()
/obj/item/claymore/ctf/dropped()
+1
View File
@@ -105,6 +105,7 @@ datum/bounty/reagent/complex_drink/New()
/datum/reagent/consumable/ethanol/hearty_punch,\
/datum/reagent/consumable/ethanol/manhattan_proj,\
/datum/reagent/consumable/ethanol/narsour,\
/datum/reagent/consumable/ethanol/cogchamp,\
/datum/reagent/consumable/ethanol/neurotoxin,\
/datum/reagent/consumable/ethanol/patron,\
/datum/reagent/consumable/ethanol/quadruple_sec,\
+1 -1
View File
@@ -6,7 +6,7 @@
k_elasticity = 0
unit_name = "crate"
export_types = list(/obj/structure/closet/crate)
exclude_types = list(/obj/structure/closet/crate/large, /obj/structure/closet/crate/wooden)
exclude_types = list(/obj/structure/closet/crate/large, /obj/structure/closet/crate/wooden, /obj/structure/closet/crate/bin)
/datum/export/large/crate/total_printout(datum/export_report/ex, notes = TRUE) // That's why a goddamn metal crate costs that much.
. = ..()
+1 -1
View File
@@ -272,7 +272,7 @@
/datum/outfit/wizard
name = "Blue Wizard"
uniform = /obj/item/clothing/under/color/lightpurple
uniform = /obj/item/clothing/under/color/lightpurple/trackless
suit = /obj/item/clothing/suit/wizrobe
shoes = /obj/item/clothing/shoes/sandal/magic
ears = /obj/item/radio/headset
+2 -4
View File
@@ -325,17 +325,16 @@
body_parts_covered = CHEST|ARMS
/obj/item/clothing/suit/ran
name = "Shikigami costume"
name = "shikigami costume"
desc = "A costume that looks like a certain shikigami, is super fluffy."
icon_state = "ran_suit"
item_state = "ran_suit"
body_parts_covered = CHEST|GROIN|LEGS
flags_inv = HIDEJUMPSUIT|HIDETAUR
heat_protection = CHEST|GROIN|LEGS //fluffy tails!
//2061
/obj/item/clothing/head/ran
name = "Shikigami hat"
name = "shikigami hat"
desc = "A hat that looks like it keeps any fluffy ears contained super warm, has little charms over it."
icon_state = "ran_hat"
item_state = "ran_hat"
@@ -880,7 +879,6 @@
blood_overlay_type = "armor"
body_parts_covered = CHEST
resistance_flags = NONE
mutantrace_variation = NONE
armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 5, "bio" = 0, "rad" = 0, "fire" = -5, "acid" = -15) //nylon sucks against acid
/obj/item/clothing/suit/assu_suit
+8 -1
View File
@@ -41,6 +41,10 @@
item_color = "black"
resistance_flags = NONE
/obj/item/clothing/under/color/black/trackless
desc = "A black jumpsuit that has its sensors removed."
has_sensor = NO_SENSORS
/obj/item/clothing/under/skirt/color/black
name = "black jumpskirt"
icon_state = "black_skirt"
@@ -189,13 +193,16 @@
item_state = "b_suit"
item_color = "teal_skirt"
/obj/item/clothing/under/color/lightpurple
name = "purple jumpsuit"
icon_state = "lightpurple"
item_state = "p_suit"
item_color = "lightpurple"
/obj/item/clothing/under/color/lightpurple/trackless
desc = "A magically colored jumpsuit. No sensors are attached!"
has_sensor = NO_SENSORS
/obj/item/clothing/under/skirt/color/lightpurple
name = "lightpurple jumpskirt"
icon_state = "lightpurple_skirt"
@@ -19,6 +19,13 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
log_world("The bug with recursion runtimes has been fixed. Please remove the snowflake check from world/Error in [__FILE__]:[__LINE__]")
return //this will never happen.
else if(copytext(E.name,1,18) == "Out of resources!")//18 == length() of that string + 1
log_world("BYOND out of memory. Restarting")
log_game("BYOND out of memory. Restarting")
TgsEndProcess()
Reboot(reason = 1)
return ..()
if (islist(stack_trace_storage))
for (var/line in splittext(E.desc, "\n"))
if (text2ascii(line) != 32)
+68 -28
View File
@@ -107,9 +107,9 @@
smash(hit_atom, throwingdatum?.thrower, TRUE)
/obj/item/reagent_containers/food/drinks/proc/smash(atom/target, mob/thrower, ranged = FALSE)
if(!isGlass)
if(!isGlass && !istype(src, /obj/item/reagent_containers/food/drinks/bottle)) //I don't like this but I also don't want to rework drink container hierarchy
return
if(QDELING(src) || !target) //Invalid loc
if(QDELING(src) || (ranged && !target))
return
if(bartender_check(target) && ranged)
return
@@ -126,12 +126,69 @@
B.transform = M
B.pixel_x = rand(-12, 12)
B.pixel_y = rand(-12, 12)
if(prob(33))
new/obj/item/shard(drop_location())
playsound(src, "shatter", 70, 1)
if(isGlass)
playsound(src, "shatter", 70, 1)
if(prob(33))
new/obj/item/shard(drop_location())
else
B.force = 0
B.throwforce = 0
B.desc = "A carton with the bottom half burst open. Might give you a papercut."
transfer_fingerprints_to(B)
qdel(src)
/obj/item/reagent_containers/food/drinks/MouseDrop(atom/over, atom/src_location, atom/over_location, src_control, over_control, params)
var/mob/user = usr
. = ..()
if (!istype(src_location) || !istype(over_location))
return
if (!user || user.incapacitated() || !user.Adjacent(src))
return
if (!(locate(/obj/structure/table) in src_location) || !(locate(/obj/structure/table) in over_location))
return
//Are we an expert slider?
var/datum/action/innate/D = get_action_of_type(user, /datum/action/innate/drink_fling)
if(!D?.active)
if (!src_location.Adjacent(over_location)) // Regular users can only do short slides.
return
if (prob(10))
user.visible_message("<span class='warning'>\The [user] tries to slide \the [src] down the table, but fails miserably.</span>", "<span class='warning'>You <b>fail</b> to slide \the [src] down the table!</span>")
smash(over_location, user, FALSE)
return
user.visible_message("<span class='notice'>\The [user] slides \the [src] down the table.</span>", "<span class='notice'>You slide \the [src] down the table!</span>")
forceMove(over_location)
return
var/distance = MANHATTAN_DISTANCE(over_location, src)
if (distance >= 8 || distance == 0) // More than a full screen to go, or trying to slide to the same tile
return
// Geometrically checking if we're on a straight line.
var/datum/vector/V = atoms2vector(src, over_location)
var/datum/vector/V_norm = V.duplicate()
V_norm.normalize()
if (!V_norm.is_integer())
return // Only a cardinal vector (north, south, east, west) can pass this test
// Checks if there's tables on the path.
var/turf/dest = get_translated_turf(V)
var/turf/temp_turf = src_location
do
temp_turf = temp_turf.get_translated_turf(V_norm)
if (!locate(/obj/structure/table) in temp_turf)
var/datum/vector/V2 = atoms2vector(src, temp_turf)
vector_translate(V2, 0.1 SECONDS)
user.visible_message("<span class='warning'>\The [user] slides \the [src] down the table... and straight into the ground!</span>", "<span class='warning'>You slide \the [src] down the table, and straight into the ground!</span>")
smash(over_location, user, FALSE)
return
while (temp_turf != dest)
vector_translate(V, 0.1 SECONDS)
user.visible_message("<span class='notice'>\The [user] expertly slides \the [src] down the table.</span>", "<span class='notice'>You slide \the [src] down the table. What a pro.</span>")
return
////////////////////////////////////////////////////////////////////////////////
/// Drinks. END
////////////////////////////////////////////////////////////////////////////////
@@ -223,19 +280,24 @@
spillable = TRUE
/obj/item/reagent_containers/food/drinks/mug/on_reagent_change(changetype)
cut_overlays()
if(reagents.total_volume)
icon_state = "tea"
var/mutable_appearance/MA = mutable_appearance(icon,"mugoverlay")
MA.color = mix_color_from_reagents(reagents.reagent_list)
add_overlay(MA)
else
icon_state = "tea_empty"
/obj/item/reagent_containers/food/drinks/mug/tea
name = "Duke Purple tea"
icon_state = "tea"
desc = "An insult to Duke Purple is an insult to the Space Queen! Any proper gentleman will fight you, if you sully this tea."
list_reagents = list(/datum/reagent/consumable/tea = 30)
/obj/item/reagent_containers/food/drinks/mug/coco
name = "Dutch hot coco"
desc = "Made in Space South America."
icon_state = "coco"
list_reagents = list(/datum/reagent/consumable/hot_coco = 30, /datum/reagent/consumable/sugar = 5)
foodtype = SUGAR
resistance_flags = FREEZE_PROOF
@@ -289,28 +351,6 @@
icon_state = "juicebox"
volume = 15 //I figure if you have to craft these it should at least be slightly better than something you can get for free from a watercooler
/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/smash(atom/target, mob/thrower, ranged = FALSE)
if(bartender_check(target) && ranged)
return
var/obj/item/broken_bottle/B = new (loc)
B.icon_state = icon_state
var/icon/I = new('icons/obj/drinks.dmi', src.icon_state)
I.Blend(B.broken_outline, ICON_OVERLAY, rand(5), 1)
I.SwapColor(rgb(255, 0, 220, 255), rgb(0, 0, 0, 0))
B.icon = I
B.name = "broken [name]"
B.force = 0
B.throwforce = 0
B.desc = "A carton with the bottom half burst open. Might give you a papercut."
if(ranged)
var/matrix/M = matrix(B.transform)
M.Turn(rand(-170, 170))
B.transform = M
B.pixel_x = rand(-12, 12)
B.pixel_y = rand(-12, 12)
transfer_fingerprints_to(B)
qdel(src)
/obj/item/reagent_containers/food/drinks/sillycup/smallcarton/on_reagent_change(changetype)
if (reagents.reagent_list.len)
switch(reagents.get_master_reagent_id())
@@ -16,40 +16,6 @@
isGlass = TRUE
foodtype = ALCOHOL
/obj/item/reagent_containers/food/drinks/bottle/smash(mob/living/target, mob/thrower, ranged = FALSE)
//Creates a shattering noise and replaces the bottle with a broken_bottle
if(bartender_check(target) && ranged)
return
var/obj/item/broken_bottle/B = new (loc)
if(!ranged)
thrower.put_in_hands(B)
else
var/matrix/M = matrix(B.transform)
M.Turn(rand(-170, 170))
B.transform = M
B.pixel_x = rand(-12, 12)
B.pixel_y = rand(-12, 12)
B.icon_state = icon_state
var/icon/I = new('icons/obj/drinks.dmi', src.icon_state)
I.Blend(B.broken_outline, ICON_OVERLAY, rand(5), 1)
I.SwapColor(rgb(255, 0, 220, 255), rgb(0, 0, 0, 0))
B.icon = I
if(isGlass)
if(prob(33))
new/obj/item/shard(drop_location())
playsound(src, "shatter", 70, 1)
else
B.force = 0
B.throwforce = 0
B.desc = "A carton with the bottom half burst open. Might give you a papercut."
B.name = "broken [name]"
transfer_fingerprints_to(B)
qdel(src)
/obj/item/reagent_containers/food/drinks/bottle/attack(mob/living/target, mob/living/user)
if(!target)
@@ -109,7 +75,7 @@
//Keeping this here for now, I'll ask if I should keep it here.
/obj/item/broken_bottle
name = "broken bottle"
desc = "A bottle with a sharp broken bottom."
desc = "A shattered glass container with sharp edges."
icon = 'icons/obj/drinks.dmi'
icon_state = "broken_bottle"
force = 9
@@ -96,6 +96,16 @@
tastes = list("egg" = 4, "salt" = 1, "pepper" = 1)
foodtype = MEAT | FRIED | BREAKFAST
/obj/item/reagent_containers/food/snacks/baconegg
name = "bacon and eggs"
desc = "A fried egg with a side of bacon. Delicious!"
icon_state = "baconegg"
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1)
bitesize = 1
filling_color = "#FFFFF0"
tastes = list("egg" = 2, "bacon" = 2, "salt" = 1, "pepper" = 1)
foodtype = MEAT | FRIED | BREAKFAST
/obj/item/reagent_containers/food/snacks/boiledegg
name = "boiled egg"
desc = "A hard boiled egg."
@@ -353,6 +353,13 @@
tastes = list("muffin" = 3, "spookiness" = 1)
foodtype = GRAIN | FRUIT | SUGAR | BREAKFAST
/obj/item/reagent_containers/food/snacks/muffin/poppy
name = "poppy muffin"
icon_state = "poppymuffin"
desc = "A classic lemon poppy seed muffin. Do not consume prior to drug testing."
tastes = list("muffin" = 3, "lemon" = 1, "seeds" = 1)
foodtype = GRAIN | SUGAR | BREAKFAST
/obj/item/reagent_containers/food/snacks/chawanmushi
name = "chawanmushi"
desc = "A legendary egg custard that makes friends out of enemies. Probably too hot for a cat to eat."
@@ -585,6 +585,14 @@
mix_message = "The mixture develops a sinister glow."
mix_sound = 'sound/effects/singlebeat.ogg'
/datum/chemical_reaction/cogchamp
name = "CogChamp"
id = /datum/reagent/consumable/ethanol/cogchamp
results = list(/datum/reagent/consumable/ethanol/cogchamp = 3)
required_reagents = list(/datum/reagent/consumable/ethanol/cognac = 1, /datum/reagent/fuel = 1, /datum/reagent/consumable/ethanol/screwdrivercocktail = 1)
mix_message = "You hear faint sounds of gears turning as it mixes."
mix_sound = 'sound/effects/clockcult_gateway_closing.ogg'
/datum/chemical_reaction/quadruplesec
name = "Quadruple Sec"
id = /datum/reagent/consumable/ethanol/quadruple_sec
@@ -13,6 +13,15 @@
result = /obj/item/reagent_containers/food/snacks/friedegg
subcategory = CAT_EGG
/datum/crafting_recipe/food/baconegg
name = "Bacon and Eggs"
reqs = list(
/obj/item/reagent_containers/food/snacks/friedegg = 1,
/obj/item/reagent_containers/food/snacks/meat/bacon = 1
)
result = /obj/item/reagent_containers/food/snacks/baconegg
subcategory = CAT_EGG
/datum/crafting_recipe/food/omelette
name = "Omelette"
reqs = list(
@@ -377,6 +377,17 @@ datum/crafting_recipe/food/donut/meat
result = /obj/item/reagent_containers/food/snacks/muffin/booberry
subcategory = CAT_PASTRY
/datum/crafting_recipe/food/poppymuffin
name = "Poppy muffin"
reqs = list(
/datum/reagent/consumable/milk = 5,
/obj/item/reagent_containers/food/snacks/pastrybase = 1,
/obj/item/reagent_containers/food/snacks/grown/citrus/lemon = 1,
/obj/item/seeds/poppy = 1
)
result = /obj/item/reagent_containers/food/snacks/muffin/poppy
subcategory = CAT_PASTRY
/datum/crafting_recipe/food/chawanmushi
name = "Chawanmushi"
reqs = list(
+4
View File
@@ -28,3 +28,7 @@
backpack_contents = list(/obj/item/storage/box/beanbag=1,/obj/item/book/granter/action/drink_fling=1)
shoes = /obj/item/clothing/shoes/laceup
/datum/job/bartender/after_spawn(mob/living/H, mob/M, latejoin = FALSE)
. = ..()
var/datum/action/innate/drink_fling/D = new
D.Grant(H)
+1 -1
View File
@@ -46,7 +46,7 @@
/obj/item/resonator/pre_attack(atom/target, mob/user, params)
if(check_allowed_items(target, 1))
CreateResonance(target, user)
return TRUE
return ..()
//resonance field, crushes rock, damages mobs
/obj/effect/temp_visual/resonance
+1 -1
View File
@@ -93,7 +93,7 @@
req_access = list()
circuit = /obj/item/circuitboard/computer/mining_shuttle/common
shuttleId = "mining_common"
possible_destinations = "whiteship_home;lavaland_common_away;landing_zone_dock;mining_public"
possible_destinations = "lavaland_common_away;commonmining_home"
/**********************Mining car (Crate like thing, not the rail car)**************************/
@@ -16,7 +16,7 @@
from doing this unless you absolutely know what you are doing, and have defined a
conversion in savefile.dm
*/
/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female,var/roundstart = FALSE)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
/proc/init_sprite_accessory_subtypes(prototype, list/L, list/male, list/female, roundstart = FALSE, skip_prototype = TRUE)//Roundstart argument builds a specific list for roundstart parts where some parts may be locked
if(!istype(L))
L = list()
if(!istype(male))
@@ -25,7 +25,7 @@
female = list()
for(var/path in typesof(prototype))
if(path == prototype)
if(path == prototype && skip_prototype)
continue
if(roundstart)
var/datum/sprite_accessory/P = path
+3
View File
@@ -581,6 +581,9 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
if(cultslurring)
cultslurring = max(cultslurring-1, 0)
if(clockcultslurring)
clockcultslurring = max(clockcultslurring-1, 0)
if(silent)
silent = max(silent-1, 0)
@@ -89,6 +89,7 @@
var/stuttering = 0
var/slurring = 0
var/cultslurring = 0
var/clockcultslurring = 0
var/derpspeech = 0
var/list/implants = null
+3
View File
@@ -354,6 +354,9 @@ GLOBAL_LIST_INIT(department_radio_keys, list(
if(cultslurring)
message = cultslur(message)
if(clockcultslurring)
message = CLOCK_CULT_SLUR(message)
message = capitalize(message)
@@ -1,3 +1,4 @@
#define PAI_EMP_SILENCE_DURATION 3 MINUTES
/mob/living/silicon/pai/blob_act(obj/structure/blob/B)
return FALSE
@@ -8,7 +9,7 @@
return
take_holo_damage(50/severity)
Knockdown(400/severity)
silent = max((3 MINUTES)/severity, silent)
silent = max(silent, (PAI_EMP_SILENCE_DURATION) / SSmobs.wait / severity)
if(holoform)
fold_in(force = TRUE)
emitter_next_use = world.time + emitter_emp_cd
@@ -19,7 +19,7 @@
/mob/living/simple_animal/pet/dog/ComponentInitialize()
. = ..()
AddElement(/datum/element/wuv, "yaps_happily!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "growls!", EMOTE_AUDIBLE)
AddElement(/datum/element/wuv, "yaps happily!", EMOTE_AUDIBLE, /datum/mood_event/pet_animal, "growls!", EMOTE_AUDIBLE)
AddElement(/datum/element/mob_holder, held_icon)
//Corgis and pugs are now under one dog subtype
+8 -7
View File
@@ -147,6 +147,10 @@
. += newletter
return sanitize(.)
//Ratvarian Slurring!
#define CLOCK_CULT_SLUR(phrase) sanitize(text2ratvar(phrase))
///Adds stuttering to the message passed in
/proc/stutter(phrase)
phrase = html_decode(phrase)
@@ -420,13 +424,10 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
return TRUE
/mob/proc/getImplant(type)
if (!istype(src,/mob/living))
return
var/mob/living/L = src
for (var/I in L.implants)
if (istype(I,type))
return I
return null
return
/mob/living/getImplant(type)
return locate(type) in implants
/proc/canGhostWrite(var/mob/A, var/obj/target, var/desc="", var/allow_all=FALSE)
if(allow_all & TRUE)
+1 -1
View File
@@ -1,6 +1,6 @@
/datum/outfit/ninja
name = "Space Ninja"
uniform = /obj/item/clothing/under/color/black
uniform = /obj/item/clothing/under/color/black/trackless
suit = /obj/item/clothing/suit/space/space_ninja
glasses = /obj/item/clothing/glasses/night
mask = /obj/item/clothing/mask/gas/space_ninja
+5 -5
View File
@@ -107,7 +107,7 @@
var/icon_update_needed = FALSE
var/obj/machinery/computer/apc_control/remote_control = null
var/mob/living/carbon/hijacker
var/hijackerlast = FALSE
var/hijackerlast = TRUE
/obj/machinery/power/apc/unlocked
locked = FALSE
@@ -399,14 +399,14 @@
if (hijacker)
var/obj/item/implant/hijack/H = hijacker.getImplant(/obj/item/implant/hijack)
hijackerreturn = H && !H.stealthmode
if(last_update_state == update_state && last_update_overlay == update_overlay && hijackerreturn ? hijackerlast : !hijackerlast)
if(last_update_state == update_state && last_update_overlay == update_overlay && hijackerreturn == hijackerlast)
return 0
if(last_update_state != update_state)
results += 1
if(last_update_overlay != update_overlay || hijackerreturn ? !hijackerlast : hijackerlast)
if(last_update_overlay != update_overlay || hijackerreturn != hijackerlast)
results += 2
if (hijackerreturn ? !hijackerlast : hijackerlast)
hijackerlast = hijackerreturn ? TRUE : FALSE
if (hijackerreturn != hijackerlast)
hijackerlast = hijackerreturn
return results
// Used in process so it doesn't update the icon too much
@@ -15,15 +15,15 @@
. = ..()
/obj/item/ammo_casing/energy/gravity/repulse
projectile_type = /obj/item/projectile/gravityrepulse
projectile_type = /obj/item/projectile/gravity/repulse
select_name = "repulse"
/obj/item/ammo_casing/energy/gravity/attract
projectile_type = /obj/item/projectile/gravityattract
projectile_type = /obj/item/projectile/gravity/attract
select_name = "attract"
/obj/item/ammo_casing/energy/gravity/chaos
projectile_type = /obj/item/projectile/gravitychaos
projectile_type = /obj/item/projectile/gravity/chaos
select_name = "chaos"
+1 -1
View File
@@ -150,7 +150,7 @@
if(istype(v, /obj/item/ammo_casing/energy)) //already set
ammo_type[v] = isnull(user_can_select)? TRUE : user_can_select
else
C = new v //if you put non energycasing/type stuff in here you deserve the runtime
C = new v(src) //if you put non energycasing/type stuff in here you deserve the runtime
ammo_type[i] = C
ammo_type[C] = isnull(user_can_select)? TRUE : user_can_select
set_firemode_index(initial(current_firemode_index))
+13 -3
View File
@@ -69,13 +69,10 @@
if(gun)
qdel(gun)
/obj/item/firing_pin/magic
name = "magic crystal shard"
desc = "A small enchanted shard which allows magical weapons to fire."
// Test pin, works only near firing range.
/obj/item/firing_pin/test_range
name = "test-range firing pin"
@@ -229,3 +226,16 @@
if(gun)
gun.pin = null
return ..()
//Station Locked
/obj/item/firing_pin/away
name = "station locked pin"
desc = "A firing pin that only will fire when off the station."
/obj/item/firing_pin/away/pin_auth(mob/living/user)
var/area/station_area = get_area(src)
if(!station_area || is_station_level(station_area.z))
to_chat(user, "<span class='warning'>The pin beeps, refusing to fire.</span>")
return FALSE
return TRUE
@@ -61,7 +61,7 @@
/obj/item/projectile/bullet/pellet/shotgun_buckshot
name = "buckshot pellet"
damage = 10
damage = 12.5
/obj/item/projectile/bullet/pellet/shotgun_rubbershot
name = "rubbershot pellet"
@@ -1,90 +1,65 @@
/obj/item/projectile/gravityrepulse
/obj/item/projectile/gravity
name = "gravity bolt"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = TRUE
var/power = 4
var/list/thrown = list() //normally we wouldn't need this but incase on_hit is called multiple times, yeah.. this is a good idea.
/obj/item/projectile/gravity/Initialize(mapload)
. = ..()
var/obj/item/ammo_casing/energy/gravity/G = loc
if(istype(G))
power = min(G.gun.power, 15)
/obj/item/projectile/gravity/on_hit()
. = ..()
var/turf/T = get_turf(src)
var/list/tothrow = list()
var/list/cachedrange = range(T, power)
for(var/mob/M in cachedrange)
tothrow += M
for(var/obj/O in cachedrange)
tothrow += O
var/safety = 50
for(var/i in tothrow)
if(!safety)
break
var/atom/movable/AM = i
if((AM == src) || (AM == firer) || (AM.move_resist > MOVE_FORCE_EXTREMELY_STRONG) || AM.anchored || thrown[AM])
continue
thrown[AM] = TRUE
safety--
var/target = get_target(T, AM)
if(!target)
return FALSE
AM.throw_at(target, power + 1, 1)
for(var/turf/F in range(T,power))
new /obj/effect/temp_visual/gravpush(F)
/obj/item/projectile/gravity/proc/get_target(turf/origin, atom/movable/AM)
return origin
/obj/item/projectile/gravity/repulse
name = "repulsion bolt"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = 1
color = "#33CCFF"
var/turf/T
var/power = 4
var/list/thrown_items = list()
/obj/item/projectile/gravityrepulse/Initialize()
. = ..()
var/obj/item/ammo_casing/energy/gravity/repulse/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
/obj/item/projectile/gravity/repulse/get_target(turf/origin, atom/movable/AM)
return get_turf_in_angle(Get_Angle(origin, AM), origin, power)
/obj/item/projectile/gravityrepulse/on_hit()
. = ..()
T = get_turf(src)
for(var/atom/movable/A in range(T, power))
if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
continue
var/throwtarget = get_edge_target_turf(src, get_dir(src, get_step_away(A, src)))
A.throw_at(throwtarget,power+1,1)
thrown_items[A] = A
for(var/turf/F in range(T,power))
new /obj/effect/temp_visual/gravpush(F)
/obj/item/projectile/gravityattract
/obj/item/projectile/gravity/attract
name = "attraction bolt"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = 1
color = "#FF6600"
var/turf/T
var/power = 4
var/list/thrown_items = list()
/obj/item/projectile/gravityattract/Initialize()
. = ..()
var/obj/item/ammo_casing/energy/gravity/attract/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
/obj/item/projectile/gravity/attract/get_target(turf/origin, atom/movable/AM)
return origin
/obj/item/projectile/gravityattract/on_hit()
. = ..()
T = get_turf(src)
for(var/atom/movable/A in range(T, power))
if(A == src || (firer && A == src.firer) || A.anchored || thrown_items[A])
continue
A.throw_at(T, power+1, 1)
thrown_items[A] = A
for(var/turf/F in range(T,power))
new /obj/effect/temp_visual/gravpush(F)
/obj/item/projectile/gravitychaos
/obj/item/projectile/gravity/chaos
name = "gravitational blast"
icon = 'icons/effects/effects.dmi'
icon_state = "chronofield"
hitsound = 'sound/weapons/wave.ogg'
damage = 0
damage_type = BRUTE
nodamage = 1
color = "#101010"
var/turf/T
var/power = 4
var/list/thrown_items = list()
/obj/item/projectile/gravitychaos/Initialize()
. = ..()
var/obj/item/ammo_casing/energy/gravity/chaos/C = loc
if(istype(C)) //Hard-coded maximum power so servers can't be crashed by trying to throw the entire Z level's items
power = min(C.gun.power, 15)
/obj/item/projectile/gravitychaos/on_hit()
. = ..()
T = get_turf(src)
for(var/atom/movable/A in range(T, power))
if(A == src|| (firer && A == src.firer) || A.anchored || thrown_items[A])
continue
A.throw_at(get_edge_target_turf(A, pick(GLOB.cardinals)), power+1, 1)
thrown_items[A] = A
for(var/turf/Z in range(T,power))
new /obj/effect/temp_visual/gravpush(Z)
/obj/item/projectile/gravity/chaos/get_target(turf/origin, atom/movable/AM)
return get_turf_in_angle(rand(0, 359), origin, power)
@@ -1513,6 +1513,23 @@ All effects don't start immediately, but rather get worse over time; the rate is
M.stuttering = min(M.stuttering + 3, 3)
..()
/datum/reagent/consumable/ethanol/cogchamp
name = "CogChamp"
description = "Now you can fill yourself with the power of Ratvar!"
color = rgb(255, 201, 49)
boozepwr = 10
quality = DRINK_FANTASTIC
taste_description = "a brass taste with a hint of oil"
glass_icon_state = "cogchamp"
glass_name = "CogChamp"
glass_desc = "Not even Ratvar's Four Generals could withstand this! Qevax Jryy!"
value = 8.13
/datum/reagent/consumable/ethanol/cogchamp/on_mob_life(mob/living/carbon/M)
M.clockcultslurring = min(M.clockcultslurring + 3, 3)
M.stuttering = min(M.stuttering + 3, 3)
..()
/datum/reagent/consumable/ethanol/triple_sec
name = "Triple Sec"
description = "A sweet and vibrant orange liqueur."
@@ -383,7 +383,7 @@
name = "Hot Chocolate"
description = "Made with love! And coco beans."
nutriment_factor = 3 * REAGENTS_METABOLISM
color = "#403010" // rgb: 64, 48, 16
color = "#660000" // rgb: 221, 202, 134
taste_description = "creamy chocolate"
glass_icon_state = "chocolateglass"
glass_name = "glass of chocolate"
+6 -5
View File
@@ -105,11 +105,11 @@
/obj/item/reagent_containers/proc/bartender_check(atom/target)
. = FALSE
var/turf/T = get_turf(src)
if(!T || target.CanPass(src, T) || !thrownby || !thrownby.actions)
if(!T || !target.CanPass(src, T) || !thrownby || !thrownby.actions)
return
for(var/datum/action/innate/drink_fling/D in thrownby.actions)
if(D.active)
return TRUE
var/datum/action/innate/D = get_action_of_type(thrownby, /datum/action/innate/drink_fling)
if(D?.active)
return TRUE
/obj/item/reagent_containers/proc/ForceResetRotation()
transform = initial(transform)
@@ -131,13 +131,14 @@
if(thrownby)
log_combat(thrownby, M, "splashed", R)
reagents.reaction(target, TOUCH)
else if(bartender_check(target) && thrown)
visible_message("<span class='notice'>[src] lands onto the [target.name] without spilling a single drop.</span>")
transform = initial(transform)
addtimer(CALLBACK(src, .proc/ForceResetRotation), 1)
return
else
if(isturf(target) && reagents.reagent_list.len && thrownby)
log_combat(thrownby, target, "splashed (thrown) [english_list(reagents.reagent_list)]", "in [AREACOORD(target)]")
@@ -423,9 +423,9 @@
else
unload_hypo(vial,user)
/obj/item/hypospray/mkii/AltClick(mob/living/user)
/obj/item/hypospray/mkii/CtrlClick(mob/living/user)
. = ..()
if(user.canUseTopic(src, FALSE))
if(user.canUseTopic(src, FALSE) && user.get_active_held_item(src))
switch(mode)
if(HYPO_SPRAY)
mode = HYPO_INJECT
@@ -437,7 +437,7 @@
/obj/item/hypospray/mkii/examine(mob/user)
. = ..()
. += "<span class='notice'><b>Alt-Click</b> it to toggle its mode from spraying to injecting and vice versa.</span>"
. += "<span class='notice'><b>Ctrl-Click</b> it to toggle its mode from spraying to injecting and vice versa.</span>"
#undef HYPO_SPRAY
#undef HYPO_INJECT
@@ -172,6 +172,7 @@
/obj/item/reagent_containers/spray/drying_agent
name = "drying agent spray"
desc = "A spray bottle for drying agent."
icon_state = "cleaner_drying"
volume = 100
list_reagents = list(/datum/reagent/drying_agent = 100)
amount_per_transfer_from_this = 2
@@ -289,6 +289,16 @@
////////////Janitor Designs//////////////
/////////////////////////////////////////
/datum/design/mop
name = "Mop"
desc = "Just your everyday standard mop."
id = "mop"
build_type = PROTOLATHE
materials = list(MAT_METAL = 1200, MAT_GLASS = 100)
build_path = /obj/item/mop
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
/datum/design/advmop
name = "Advanced Mop"
desc = "An upgraded mop with a large internal capacity for holding water or other cleaning chemicals."
@@ -225,6 +225,16 @@
category = list("Firing Pins")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
/datum/design/pin_away
name = "Station Locked Pin"
desc = "This is a security firing pin which only authorizes users who are off station."
id = "pin_away"
build_type = PROTOLATHE
materials = list(MAT_METAL = 1500, MAT_GLASS = 2000)
build_path = /obj/item/firing_pin/away
category = list("Firing Pins")
departmental_flags = DEPARTMENTAL_FLAG_SECURITY
//////////////
//Guns////////
//////////////
+3 -4
View File
@@ -52,13 +52,12 @@ Nothing else in the console has ID requirements.
research_control = FALSE
/proc/CallMaterialName(ID)
if(GLOB.chemical_reagents_list[ID])
var/datum/reagent/reagent = GLOB.chemical_reagents_list[ID]
return reagent.name
if (ID[1] == "$" && GLOB.materials_list[ID])
var/datum/material/material = GLOB.materials_list[ID]
return material.name
else if(GLOB.chemical_reagents_list[ID])
var/datum/reagent/reagent = GLOB.chemical_reagents_list[ID]
return reagent.name
return "ERROR: Report This"
/obj/machinery/computer/rdconsole/proc/SyncRDevices() //Makes sure it is properly sync'ed up with the devices attached to it (if any).
+1 -1
View File
@@ -22,7 +22,7 @@ If you create T5+ please take a pass at gene_modder.dm [L40]. Max_values MUST fi
if(works_from_distance)
user.Beam(T, icon_state = "rped_upgrade", time = 5)
T.exchange_parts(user, src)
return FALSE
return TRUE
return ..()
/obj/item/storage/part_replacer/afterattack(obj/machinery/T, mob/living/user, adjacent, params)
+2 -2
View File
@@ -602,7 +602,7 @@
display_name = "Basic Tools"
description = "Basic mechanical, electronic, surgical and botanical tools."
prereq_ids = list("base")
design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet")
design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet", "mop")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
export_price = 5000
@@ -666,7 +666,7 @@
display_name = "Weapon Development Technology"
description = "Our researchers have found new to weaponize just about everything now."
prereq_ids = list("engineering")
design_ids = list("pin_testing", "tele_shield", "lasercarbine")
design_ids = list("pin_testing", "tele_shield", "lasercarbine", "pin_away")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 7500)
export_price = 5000
@@ -5,6 +5,7 @@
force = 3
throwforce = 3
icon = 'icons/mob/human_parts.dmi'
w_class = WEIGHT_CLASS_SMALL
icon_state = ""
layer = BELOW_MOB_LAYER //so it isn't hidden behind objects when on the floor
var/mob/living/carbon/owner = null
@@ -24,8 +24,8 @@
/datum/surgery_step/dissection
name = "dissection"
implements = list(/obj/item/scalpel/augment = 75, /obj/item/scalpel/advanced = 60, /obj/item/scalpel = 45, /obj/item/kitchen/knife = 20, /obj/item/shard = 10)// special tools not only cut down time but also improve probability, doesn't use TOOL_SCALPEL because different scalpels have different probs
time = 125
implements = list(/obj/item/scalpel/alien = 100, /obj/item/scalpel/advanced = 99, /obj/item/scalpel = 90, /obj/item/kitchen/knife = 45, /obj/item/shard = 25)// special tools not only cut down time but also improve probability, doesn't use TOOL_SCALPEL because different scalpels have different probs
time = 100
silicons_obey_prob = TRUE
repeatable = TRUE
@@ -110,4 +110,4 @@
requires_tech = TRUE
replaced_by = null
#undef BASE_HUMAN_REWARD
#undef BASE_HUMAN_REWARD
+56 -36
View File
@@ -1,47 +1,60 @@
/world/TgsNew(datum/tgs_event_handler/event_handler)
var/tgs_version = world.params[TGS_VERSION_PARAMETER]
if(!tgs_version)
/world/TgsNew(datum/tgs_event_handler/event_handler, minimum_required_security_level = TGS_SECURITY_ULTRASAFE)
var/current_api = TGS_READ_GLOBAL(tgs)
if(current_api)
TGS_ERROR_LOG("TgsNew(): TGS API datum already set ([current_api])! Was TgsNew() called more than once?")
return
var/path = SelectTgsApi(tgs_version)
if(!path)
TGS_ERROR_LOG("Found unsupported API version: [tgs_version]. If this is a valid version please report this, backporting is done on demand.")
#ifdef TGS_V3_API
minimum_required_security_level = TGS_SECURITY_TRUSTED
#endif
var/raw_parameter = world.params[TGS_VERSION_PARAMETER]
if(!raw_parameter)
return
TGS_INFO_LOG("Activating API for version [tgs_version]")
var/datum/tgs_api/new_api = new path
var/datum/tgs_version/version = new(raw_parameter)
if(!version.Valid(FALSE))
TGS_ERROR_LOG("Failed to validate TGS version parameter: [raw_parameter]!")
return
var/result = new_api.OnWorldNew(event_handler ? event_handler : new /datum/tgs_event_handler/tgs_default)
if(result && result != TGS_UNIMPLEMENTED)
TGS_WRITE_GLOBAL(tgs, new_api)
else
var/api_datum
switch(version.suite)
if(3)
#ifndef TGS_V3_API
TGS_ERROR_LOG("Detected V3 API but TGS_V3_API isn't defined!")
#else
switch(version.major)
if(2)
api_datum = /datum/tgs_api/v3210
#endif
if(4)
switch(version.major)
if(0)
api_datum = /datum/tgs_api/v4
var/datum/tgs_version/max_api_version = TgsMaximumAPIVersion();
if(version.suite != null && version.major != null && version.minor != null && version.patch != null && version.deprefixed_parameter > max_api_version.deprefixed_parameter)
TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
api_datum = /datum/tgs_api/latest
if(!api_datum)
TGS_ERROR_LOG("Found unsupported API version: [raw_parameter]. If this is a valid version please report this, backporting is done on demand.")
return
TGS_INFO_LOG("Activating API for version [version.deprefixed_parameter]")
var/datum/tgs_api/new_api = new api_datum(version)
TGS_WRITE_GLOBAL(tgs, new_api)
var/result = new_api.OnWorldNew(event_handler, minimum_required_security_level)
if(!result || result == TGS_UNIMPLEMENTED)
TGS_WRITE_GLOBAL(tgs, null)
TGS_ERROR_LOG("Failed to activate API!")
/world/proc/SelectTgsApi(tgs_version)
//remove the old 3.0 header
tgs_version = replacetext(tgs_version, "/tg/station 13 Server v", "")
var/list/version_bits = splittext(tgs_version, ".")
var/super = text2num(version_bits[1])
var/major = text2num(version_bits[2])
var/minor = text2num(version_bits[3])
var/patch = text2num(version_bits[4])
switch(super)
if(3)
switch(major)
if(2)
return /datum/tgs_api/v3210
if(super != null && major != null && minor != null && patch != null && tgs_version > TgsMaximumAPIVersion())
TGS_ERROR_LOG("Detected unknown API version! Defaulting to latest. Update the DMAPI to fix this problem.")
return /datum/tgs_api/latest
/world/TgsMaximumAPIVersion()
return "4.0.0.0"
return new /datum/tgs_version("4.0.x.x")
/world/TgsMinimumAPIVersion()
return "3.2.0.0"
return new /datum/tgs_version("3.2.0.0")
/world/TgsInitializationComplete()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
@@ -71,7 +84,9 @@
return TGS_READ_GLOBAL(tgs) != null
/world/TgsVersion()
return world.params[TGS_VERSION_PARAMETER]
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
return api.version
/world/TgsInstanceName()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
@@ -116,6 +131,11 @@
if(api)
api.ChatPrivateMessage(message, user)
/world/TgsSecurityLevel()
var/datum/tgs_api/api = TGS_READ_GLOBAL(tgs)
if(api)
api.SecurityLevel()
/*
The MIT License
+9 -1
View File
@@ -1,9 +1,14 @@
TGS_DEFINE_AND_SET_GLOBAL(tgs, null)
/datum/tgs_api
var/datum/tgs_version/version
/datum/tgs_api/New(datum/tgs_version/version)
. = ..()
src.version = version
/datum/tgs_api/latest
parent_type = /datum/tgs_api/v3210
parent_type = /datum/tgs_api/v4
TGS_PROTECT_DATUM(/datum/tgs_api)
@@ -46,6 +51,9 @@ TGS_PROTECT_DATUM(/datum/tgs_api)
/datum/tgs_api/proc/ChatPrivateMessage(message, admin_only)
return TGS_UNIMPLEMENTED
/datum/tgs_api/proc/SecurityLevel()
return TGS_UNIMPLEMENTED
/*
The MIT License
@@ -1,30 +0,0 @@
/datum/tgs_event_handler/tgs_default/HandleEvent(event_code)
//TODO
return
/*
The MIT License
Copyright (c) 2017 Jordan Brown
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+22
View File
@@ -0,0 +1,22 @@
/datum/tgs_version/New(raw_parameter)
src.raw_parameter = raw_parameter
deprefixed_parameter = replacetext(raw_parameter, "/tg/station 13 Server v", "")
var/list/version_bits = splittext(deprefixed_parameter, ".")
suite = text2num(version_bits[1])
if(version_bits.len > 1)
major = text2num(version_bits[2])
if(version_bits.len > 2)
minor = text2num(version_bits[3])
if(version_bits.len == 4)
patch = text2num(version_bits[4])
/datum/tgs_version/proc/Valid(allow_wildcards = FALSE)
if(suite == null)
return FALSE
if(allow_wildcards)
return TRUE
return !Wildcard()
/datum/tgs_version/Wildcard()
return major == null || minor == null || patch == null
+5 -1
View File
@@ -1,6 +1,10 @@
#include "core\_definitions.dm"
#include "core\core.dm"
#include "core\datum.dm"
#include "core\default_event_handler.dm"
#include "core\tgs_version.dm"
#ifdef TGS_V3_API
#include "v3210\api.dm"
#include "v3210\commands.dm"
#endif
#include "v4\api.dm"
#include "v4\commands.dm"
+7 -2
View File
@@ -56,8 +56,9 @@
/datum/tgs_api/v3210/proc/file2list(filename)
return splittext(trim_left(trim_right(file2text(filename))), "\n")
/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler) //don't use event handling in this version
/datum/tgs_api/v3210/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level) //don't use event handling in this version
. = FALSE
comms_key = world.params[SERVICE_WORLD_PARAM]
instance_name = world.params[SERVICE_INSTANCE_PARAM]
if(!instance_name)
@@ -170,6 +171,7 @@
var/datum/tgs_revision_information/ri = new
ri.commit = commit
ri.origin_commit = originmastercommit
return ri
/datum/tgs_api/v3210/EndProcess()
sleep(world.tick_lag) //flush the buffers
@@ -187,9 +189,12 @@
/datum/tgs_api/v3210/ChatTargetedBroadcast(message, admin_only)
ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message]")
/datum/tgs_api/v3210/ChatPrivateMessage(message, admin_only)
/datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user)
return TGS_UNIMPLEMENTED
/datum/tgs_api/v3210/SecurityLevel()
return TGS_SECURITY_TRUSTED
#undef REBOOT_MODE_NORMAL
#undef REBOOT_MODE_HARD
#undef REBOOT_MODE_SHUTDOWN
+342
View File
@@ -0,0 +1,342 @@
#define TGS4_PARAM_INFO_JSON "tgs_json"
#define TGS4_INTEROP_ACCESS_IDENTIFIER "tgs_tok"
#define TGS4_RESPONSE_SUCCESS "tgs_succ"
#define TGS4_TOPIC_CHANGE_PORT "tgs_port"
#define TGS4_TOPIC_CHANGE_REBOOT_MODE "tgs_rmode"
#define TGS4_TOPIC_CHAT_COMMAND "tgs_chat_comm"
#define TGS4_TOPIC_EVENT "tgs_event"
#define TGS4_TOPIC_INTEROP_RESPONSE "tgs_interop"
#define TGS4_COMM_NEW_PORT "tgs_new_port"
#define TGS4_COMM_VALIDATE "tgs_validate"
#define TGS4_COMM_SERVER_PRIMED "tgs_prime"
#define TGS4_COMM_WORLD_REBOOT "tgs_reboot"
#define TGS4_COMM_END_PROCESS "tgs_kill"
#define TGS4_COMM_CHAT "tgs_chat_send"
#define TGS4_PARAMETER_COMMAND "tgs_com"
#define TGS4_PARAMETER_DATA "tgs_data"
#define TGS4_PORT_CRITFAIL_MESSAGE " Must exit to let watchdog reboot..."
#define EXPORT_TIMEOUT_DS 200
/datum/tgs_api/v4
var/access_identifier
var/instance_name
var/json_path
var/chat_channels_json_path
var/chat_commands_json_path
var/server_commands_json_path
var/reboot_mode = TGS_REBOOT_MODE_NORMAL
var/security_level
var/requesting_new_port = FALSE
var/list/intercepted_message_queue
var/list/custom_commands
var/list/cached_test_merges
var/datum/tgs_revision_information/cached_revision
var/datum/tgs_event_handler/event_handler
var/export_lock = FALSE
var/list/last_interop_response
/datum/tgs_api/v4/ApiVersion()
return "4.0.0.0"
/datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler, minimum_required_security_level)
json_path = world.params[TGS4_PARAM_INFO_JSON]
if(!json_path)
TGS_ERROR_LOG("Missing [TGS4_PARAM_INFO_JSON] world parameter!")
return
var/json_file = file2text(json_path)
if(!json_file)
TGS_ERROR_LOG("Missing specified json file: [json_path]")
return
var/cached_json = json_decode(json_file)
if(!cached_json)
TGS_ERROR_LOG("Failed to decode info json: [json_file]")
return
access_identifier = cached_json["accessIdentifier"]
server_commands_json_path = cached_json["serverCommandsJson"]
if(cached_json["apiValidateOnly"])
TGS_INFO_LOG("Validating API and exiting...")
Export(TGS4_COMM_VALIDATE, list(TGS4_PARAMETER_DATA = "[minimum_required_security_level]"))
del(world)
security_level = cached_json["securityLevel"]
chat_channels_json_path = cached_json["chatChannelsJson"]
chat_commands_json_path = cached_json["chatCommandsJson"]
src.event_handler = event_handler
instance_name = cached_json["instanceName"]
ListCustomCommands()
var/list/revisionData = cached_json["revision"]
if(revisionData)
cached_revision = new
cached_revision.commit = revisionData["commitSha"]
cached_revision.origin_commit = revisionData["originCommitSha"]
cached_test_merges = list()
var/list/json = cached_json["testMerges"]
for(var/entry in json)
var/datum/tgs_revision_information/test_merge/tm = new
tm.time_merged = text2num(entry["timeMerged"])
var/list/revInfo = entry["revision"]
if(revInfo)
tm.commit = revInfo["commitSha"]
tm.origin_commit = revInfo["originCommitSha"]
tm.title = entry["titleAtMerge"]
tm.body = entry["bodyAtMerge"]
tm.url = entry["url"]
tm.author = entry["author"]
tm.number = entry["number"]
tm.pull_request_commit = entry["pullRequestRevision"]
tm.comment = entry["comment"]
cached_test_merges += tm
return TRUE
/datum/tgs_api/v4/OnInitializationComplete()
Export(TGS4_COMM_SERVER_PRIMED)
var/tgs4_secret_sleep_offline_sauce = 24051994
var/old_sleep_offline = world.sleep_offline
world.sleep_offline = tgs4_secret_sleep_offline_sauce
sleep(1)
if(world.sleep_offline == tgs4_secret_sleep_offline_sauce) //if not someone changed it
world.sleep_offline = old_sleep_offline
/datum/tgs_api/v4/OnTopic(T)
var/list/params = params2list(T)
var/their_sCK = params[TGS4_INTEROP_ACCESS_IDENTIFIER]
if(!their_sCK)
return FALSE //continue world/Topic
if(their_sCK != access_identifier)
return "Invalid comms key!";
var/command = params[TGS4_PARAMETER_COMMAND]
if(!command)
return "No command!"
. = TGS4_RESPONSE_SUCCESS
switch(command)
if(TGS4_TOPIC_CHAT_COMMAND)
var/result = HandleCustomCommand(params[TGS4_PARAMETER_DATA])
if(result == null)
result = "Error running chat command!"
return result
if(TGS4_TOPIC_EVENT)
intercepted_message_queue = list()
var/list/event_notification = json_decode(params[TGS4_PARAMETER_DATA])
var/list/event_parameters = event_notification["Parameters"]
var/list/event_call = list(event_notification["Type"])
if(event_parameters)
event_call += event_parameters
if(event_handler != null)
event_handler.HandleEvent(arglist(event_call))
. = json_encode(intercepted_message_queue)
intercepted_message_queue = null
return
if(TGS4_TOPIC_INTEROP_RESPONSE)
last_interop_response = json_decode(params[TGS4_PARAMETER_DATA])
return
if(TGS4_TOPIC_CHANGE_PORT)
var/new_port = text2num(params[TGS4_PARAMETER_DATA])
if (!(new_port > 0))
return "Invalid port: [new_port]"
//the topic still completes, miraculously
//I honestly didn't believe byond could do it
if(event_handler != null)
event_handler.HandleEvent(TGS_EVENT_PORT_SWAP, new_port)
if(!world.OpenPort(new_port))
return "Port change failed!"
return
if(TGS4_TOPIC_CHANGE_REBOOT_MODE)
var/new_reboot_mode = text2num(params[TGS4_PARAMETER_DATA])
if(event_handler != null)
event_handler.HandleEvent(TGS_EVENT_REBOOT_MODE_CHANGE, reboot_mode, new_reboot_mode)
reboot_mode = new_reboot_mode
return
return "Unknown command: [command]"
/datum/tgs_api/v4/proc/Export(command, list/data, override_requesting_new_port = FALSE)
if(!data)
data = list()
data[TGS4_PARAMETER_COMMAND] = command
var/json = json_encode(data)
while(requesting_new_port && !override_requesting_new_port)
sleep(1)
//we need some port open at this point to facilitate return communication
if(!world.port)
requesting_new_port = TRUE
if(!world.OpenPort(0)) //open any port
TGS_ERROR_LOG("Unable to open random port to retrieve new port![TGS4_PORT_CRITFAIL_MESSAGE]")
del(world)
//request a new port
export_lock = FALSE
var/list/new_port_json = Export(TGS4_COMM_NEW_PORT, list(TGS4_PARAMETER_DATA = "[world.port]"), TRUE) //stringify this on purpose
if(!new_port_json)
TGS_ERROR_LOG("No new port response from server![TGS4_PORT_CRITFAIL_MESSAGE]")
del(world)
var/new_port = new_port_json[TGS4_PARAMETER_DATA]
if(!isnum(new_port) || new_port <= 0)
TGS_ERROR_LOG("Malformed new port json ([json_encode(new_port_json)])![TGS4_PORT_CRITFAIL_MESSAGE]")
del(world)
if(new_port != world.port && !world.OpenPort(new_port))
TGS_ERROR_LOG("Unable to open port [new_port]![TGS4_PORT_CRITFAIL_MESSAGE]")
del(world)
requesting_new_port = FALSE
while(export_lock)
sleep(1)
export_lock = TRUE
last_interop_response = null
fdel(server_commands_json_path)
text2file(json, server_commands_json_path)
for(var/I = 0; I < EXPORT_TIMEOUT_DS && !last_interop_response; ++I)
sleep(1)
if(!last_interop_response)
TGS_ERROR_LOG("Failed to get export result for: [json]")
else
. = last_interop_response
export_lock = FALSE
/datum/tgs_api/v4/OnReboot()
var/list/result = Export(TGS4_COMM_WORLD_REBOOT)
if(!result)
return
//okay so the standard TGS4 proceedure is: right before rebooting change the port to whatever was sent to us in the above json's data parameter
var/port = result[TGS4_PARAMETER_DATA]
if(!isnum(port))
return //this is valid, server may just want use to reboot
if(port == 0)
//to byond 0 means any port and "none" means close vOv
port = "none"
if(!world.OpenPort(port))
TGS_ERROR_LOG("Unable to set port to [port]!")
/datum/tgs_api/v4/InstanceName()
return instance_name
/datum/tgs_api/v4/TestMerges()
return cached_test_merges
/datum/tgs_api/v4/EndProcess()
Export(TGS4_COMM_END_PROCESS)
/datum/tgs_api/v4/Revision()
return cached_revision
/datum/tgs_api/v4/ChatBroadcast(message, list/channels)
var/list/ids
if(length(channels))
ids = list()
for(var/I in channels)
var/datum/tgs_chat_channel/channel = I
ids += channel.id
message = list("message" = message, "channelIds" = ids)
if(intercepted_message_queue)
intercepted_message_queue += list(message)
else
Export(TGS4_COMM_CHAT, message)
/datum/tgs_api/v4/ChatTargetedBroadcast(message, admin_only)
var/list/channels = list()
for(var/I in ChatChannelInfo())
var/datum/tgs_chat_channel/channel = I
if (!channel.is_private_channel && ((channel.is_admin_channel && admin_only) || (!channel.is_admin_channel && !admin_only)))
channels += channel.id
message = list("message" = message, "channelIds" = channels)
if(intercepted_message_queue)
intercepted_message_queue += list(message)
else
Export(TGS4_COMM_CHAT, message)
/datum/tgs_api/v4/ChatPrivateMessage(message, datum/tgs_chat_user/user)
message = list("message" = message, "channelIds" = list(user.channel.id))
if(intercepted_message_queue)
intercepted_message_queue += list(message)
else
Export(TGS4_COMM_CHAT, message)
/datum/tgs_api/v4/ChatChannelInfo()
. = list()
//no caching cause tgs may change this
var/list/json = json_decode(file2text(chat_channels_json_path))
for(var/I in json)
. += DecodeChannel(I)
/datum/tgs_api/v4/proc/DecodeChannel(channel_json)
var/datum/tgs_chat_channel/channel = new
channel.id = channel_json["id"]
channel.friendly_name = channel_json["friendlyName"]
channel.connection_name = channel_json["connectionName"]
channel.is_admin_channel = channel_json["isAdminChannel"]
channel.is_private_channel = channel_json["isPrivateChannel"]
channel.custom_tag = channel_json["tag"]
return channel
/datum/tgs_api/v4/SecurityLevel()
return security_level
/*
The MIT License
Copyright (c) 2017 Jordan Brown
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+69
View File
@@ -0,0 +1,69 @@
/datum/tgs_api/v4/proc/ListCustomCommands()
var/results = list()
custom_commands = list()
for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command)
var/datum/tgs_chat_command/stc = new I
var/command_name = stc.name
if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\""))
TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!")
continue
if(results[command_name])
var/datum/other = custom_commands[command_name]
TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!")
continue
results += list(list("name" = command_name, "help_text" = stc.help_text, "admin_only" = stc.admin_only))
custom_commands[command_name] = stc
var/commands_file = chat_commands_json_path
if(!commands_file)
return
text2file(json_encode(results), commands_file)
/datum/tgs_api/v4/proc/HandleCustomCommand(command_json)
var/list/data = json_decode(command_json)
var/command = data["command"]
var/user = data["user"]
var/params = data["params"]
var/datum/tgs_chat_user/u = new
u.id = user["id"]
u.friendly_name = user["friendlyName"]
u.mention = user["mention"]
u.channel = DecodeChannel(user["channel"])
var/datum/tgs_chat_command/sc = custom_commands[command]
if(sc)
var/result = sc.Run(u, params)
if(result == null)
result = ""
return result
return "Unknown command: [command]!"
/*
The MIT License
Copyright (c) 2017 Jordan Brown
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+14 -11
View File
@@ -4,22 +4,25 @@
product_slogans = "Try our new nougat bar!;Twice the calories for half the price!"
product_ads = "The healthiest!;Award-winning chocolate bars!;Mmm! So good!;Oh my god it's so juicy!;Have a snack.;Snacks are good for you!;Have some more Getmore!;Best quality snacks straight from mars.;We love chocolate!;Try our new jerky!"
icon_state = "snack"
products = list(/obj/item/reagent_containers/food/snacks/candy = 6,
/obj/item/reagent_containers/food/drinks/dry_ramen = 6,
/obj/item/reagent_containers/food/snacks/chips = 6,
/obj/item/reagent_containers/food/snacks/sosjerky = 6,
/obj/item/reagent_containers/food/snacks/no_raisin = 6,
/obj/item/reagent_containers/food/snacks/spacetwinkie = 6,
/obj/item/reagent_containers/food/snacks/cheesiehonkers = 6)
products = list(/obj/item/reagent_containers/food/snacks/candy = 5,
/obj/item/reagent_containers/food/snacks/chocolatebar = 5,
/obj/item/reagent_containers/food/drinks/dry_ramen = 5,
/obj/item/reagent_containers/food/snacks/chips = 5,
/obj/item/reagent_containers/food/snacks/sosjerky = 5,
/obj/item/reagent_containers/food/snacks/no_raisin = 5,
/obj/item/reagent_containers/food/snacks/spacetwinkie = 5,
/obj/item/reagent_containers/food/snacks/cheesiehonkers = 5,
/obj/item/reagent_containers/food/snacks/cornchips = 5)
contraband = list(
/obj/item/reagent_containers/food/snacks/cracker = 12,
/obj/item/reagent_containers/food/snacks/beans = 2,
/obj/item/reagent_containers/food/snacks/honeybar = 6,
/obj/item/reagent_containers/food/snacks/syndicake = 6)
/obj/item/reagent_containers/food/snacks/cracker = 10,
/obj/item/reagent_containers/food/snacks/honeybar = 5,
/obj/item/reagent_containers/food/snacks/syndicake = 5,
/obj/item/reagent_containers/food/snacks/beans = 2)
premium = list(
/obj/item/reagent_containers/food/snacks/lollipop = 2,
/obj/item/reagent_containers/food/snacks/spiderlollipop = 2,
/obj/item/reagent_containers/food/snacks/chococoin = 1,
/obj/item/storage/box/marshmallow = 1,
/obj/item/storage/box/donkpockets = 2)
refill_canister = /obj/item/vending_refill/snack
+32
View File
@@ -399,3 +399,35 @@
/obj/item/vending_refill/wardrobe/viro_wardrobe
machine_name = "ViroDrobe"
/obj/machinery/vending/wardrobe/cap_wardrobe
name = "Captain's Wardrobe"
desc = "The latest and greatest in Nanotrasen fashion for your great leader."
icon_state = "capsdrobe"
icon_deny = "capsdrobe-deny"
product_ads = "Only the greatest for a commander such as ours."
req_access = list(ACCESS_CAPTAIN)
vend_reply = "A wonderful day to you, great leader."
products = list(/obj/item/clothing/suit/hooded/wintercoat/captain = 1,
/obj/item/storage/backpack/captain = 1,
/obj/item/storage/backpack/satchel/cap = 1,
/obj/item/storage/backpack/duffelbag/captain = 1,
/obj/item/clothing/neck/cloak/cap = 1,
/obj/item/clothing/shoes/sneakers/brown = 2,
/obj/item/clothing/under/rank/captain = 1,
/obj/item/clothing/under/rank/captain/skirt = 1,
/obj/item/clothing/head/caphat = 1,
/obj/item/clothing/head/caphat/parade = 1,
/obj/item/clothing/head/caphat/beret = 1,
/obj/item/clothing/head/caphat/beret/white = 1,
/obj/item/clothing/under/captainparade = 1,
/obj/item/clothing/suit/captunic = 1,
/obj/item/clothing/under/rank/captain/femformal = 1,
/obj/item/clothing/glasses/sunglasses/gar/supergar = 1,
/obj/item/clothing/gloves/color/captain = 1)
refill_canister = /obj/item/vending_refill/wardrobe/cap_wardrobe
/obj/item/vending_refill/wardrobe/cap_wardrobe
machine_name = "Captain's Wardrobe"
icon_state = "refill_caps"
+10 -2
View File
@@ -448,8 +448,14 @@ MINUTE_CLICK_LIMIT 400
##How long to wait between messaging admins about occurences of a unique error
#ERROR_MSG_DELAY 50
## Send a message to IRC when starting a new game
#IRC_ANNOUNCE_NEW_GAME
## Chat Announce Options
## Various messages to be sent to game chats
## Uncommenting these will enable them, by default they will be broadcast to Game chat channels on TGS3 or non-admin channels on TGS4
## If using TGS4, the string option can be set as a chat channel tag to limit the message to channels of that tag type (case-sensitive)
## i.e. CHAT_ANNOUNCE_NEW_GAME chat_channel_tag
## Send a message with the station name starting a new game
#CHAT_ANNOUNCE_NEW_GAME
## Allow admin hrefs that don't use the new token system, will eventually be removed
DEBUG_ADMIN_HREFS
@@ -500,3 +506,5 @@ FAIL2TOPIC_MAX_FAILS 5
## Firewall rule name used on physical server
FAIL2TOPIC_RULE_NAME _dd_fail2topic
## Enable automatic profiling - Byond 513.1506 and newer only.
#AUTO_PROFILE

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