Merge remote-tracking branch 'refs/remotes/Citadel-Station-13/master' into are-those-horns-are-are-you-just-retarded

This commit is contained in:
Poojawa
2019-09-13 12:56:57 -05:00
70 changed files with 4105 additions and 123 deletions
+57
View File
@@ -423,6 +423,25 @@
if(GLOB.master_mode == "secret")
dat += "<A href='?src=[REF(src)];[HrefToken()];f_secret=1'>(Force Secret Mode)</A><br>"
if(GLOB.master_mode == "dynamic")
if(SSticker.current_state <= GAME_STATE_PREGAME)
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart=1'>(Force Roundstart Rulesets)</A><br>"
if (GLOB.dynamic_forced_roundstart_ruleset.len > 0)
for(var/datum/dynamic_ruleset/roundstart/rule in GLOB.dynamic_forced_roundstart_ruleset)
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_remove=\ref[rule]'>-> [rule.name] <-</A><br>"}
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_clear=1'>(Clear Rulesets)</A><br>"
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_options=1'>(Dynamic mode options)</A><br>"
else if (SSticker.IsRoundInProgress())
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin=1'>(Force Next Latejoin Ruleset)</A><br>"
if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
if (mode.forced_latejoin_rule)
dat += {"<A href='?src=[REF(src)];[HrefToken()];f_dynamic_latejoin_clear=1'>-> [mode.forced_latejoin_rule.name] <-</A><br>"}
dat += "<A href='?src=[REF(src)];[HrefToken()];f_dynamic_midround=1'>(Execute Midround Ruleset!)</A><br>"
dat += "<hr/>"
if(SSticker.IsRoundInProgress())
dat += "<a href='?src=[REF(src)];[HrefToken()];gamemode_panel=1'>(Game Mode Panel)</a><BR>"
dat += {"
<BR>
<A href='?src=[REF(src)];[HrefToken()];create_object=1'>Create Object</A><br>
@@ -839,6 +858,44 @@
browser.set_content(dat.Join())
browser.open()
/datum/admins/proc/dynamic_mode_options(mob/user)
var/dat = {"
<center><B><h2>Dynamic Mode Options</h2></B></center><hr>
<br/>
<h3>Common options</h3>
<i>All these options can be changed midround.</i> <br/>
<br/>
<b>Force extended:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_force_extended=1'> <b>[GLOB.dynamic_forced_extended ? "ON" : "OFF"]</a></b>.
<br/>This will force the round to be extended. No rulesets will be drafted. <br/>
<br/>
<b>No stacking:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_no_stacking=1'> <b>[GLOB.dynamic_no_stacking ? "ON" : "OFF"]</b></a>.
<br/>Unless the threat goes above [GLOB.dynamic_stacking_limit], only one "round-ender" ruleset will be drafted. <br/>
<br/>
<b>Classic secret mode:</b> - Option is <a href='?src=[REF(src)];[HrefToken()];f_dynamic_classic_secret=1'> <b>[GLOB.dynamic_classic_secret ? "ON" : "OFF"]</b></a>.
<br/>Only one roundstart ruleset will be drafted. Only traitors and minor roles will latespawn. <br/>
<br/>
<br/>
<b>Forced threat level:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_forced_threat=1'><b>[GLOB.dynamic_forced_threat_level]</b></a>.
<br/>The value threat is set to if it is higher than -1.<br/>
<br/>
<b>High population limit:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_high_pop_limit=1'><b>[GLOB.dynamic_high_pop_limit]</b></a>.
<br/>The threshold at which "high population override" will be in effect. <br/>
<br/>
<b>Stacking threeshold:</b> Current value : <a href='?src=[REF(src)];[HrefToken()];f_dynamic_stacking_limit=1'><b>[GLOB.dynamic_stacking_limit]</b></a>.
<br/>The threshold at which "round-ender" rulesets will stack. A value higher than 100 ensure this never happens. <br/>
<h3>Advanced parameters</h3>
Curve centre: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_centre=1'>-> [GLOB.dynamic_curve_centre] <-</A><br>
Curve width: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_width=1'>-> [GLOB.dynamic_curve_width] <-</A><br>
Latejoin injection delay:<br>
Minimum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_latejoin_min=1'>-> [GLOB.dynamic_latejoin_delay_min / 60 / 10] <-</A> Minutes<br>
Maximum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_latejoin_max=1'>-> [GLOB.dynamic_latejoin_delay_max / 60 / 10] <-</A> Minutes<br>
Midround injection delay:<br>
Minimum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_midround_min=1'>-> [GLOB.dynamic_midround_delay_min / 60 / 10] <-</A> Minutes<br>
Maximum: <A href='?src=[REF(src)];[HrefToken()];f_dynamic_roundstart_midround_max=1'>-> [GLOB.dynamic_midround_delay_max / 60 / 10] <-</A> Minutes<br>
"}
user << browse(dat, "window=dyn_mode_options;size=900x650")
/datum/admins/proc/create_or_modify_area()
set category = "Debug"
set name = "Create or modify area"
+290
View File
@@ -291,6 +291,11 @@
else if(href_list["editrights"])
edit_rights_topic(href_list)
else if(href_list["gamemode_panel"])
if(!check_rights(R_ADMIN))
return
SSticker.mode.admin_panel()
else if(href_list["call_shuttle"])
if(!check_rights(R_ADMIN))
return
@@ -1342,6 +1347,291 @@
else if(href_list["f_secret"])
return HandleFSecret()
else if(href_list["f_dynamic_roundstart"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode.", null, null, null, null)
var/roundstart_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/roundstart))
var/datum/dynamic_ruleset/roundstart/newrule = new rule()
roundstart_rules[newrule.name] = newrule
var/added_rule = input(usr,"What ruleset do you want to force? This will bypass threat level and population restrictions.", "Rigging Roundstart", null) as null|anything in roundstart_rules
if (added_rule)
GLOB.dynamic_forced_roundstart_ruleset += roundstart_rules[added_rule]
log_admin("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.")
message_admins("[key_name(usr)] set [added_rule] to be a forced roundstart ruleset.", 1)
Game()
else if(href_list["f_dynamic_roundstart_clear"])
if(!check_rights(R_ADMIN))
return
GLOB.dynamic_forced_roundstart_ruleset = list()
Game()
log_admin("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.")
message_admins("[key_name(usr)] cleared the rigged roundstart rulesets. The mode will pick them as normal.", 1)
else if(href_list["f_dynamic_roundstart_remove"])
if(!check_rights(R_ADMIN))
return
var/datum/dynamic_ruleset/roundstart/rule = locate(href_list["f_dynamic_roundstart_remove"])
GLOB.dynamic_forced_roundstart_ruleset -= rule
Game()
log_admin("[key_name(usr)] removed [rule] from the forced roundstart rulesets.")
message_admins("[key_name(usr)] removed [rule] from the forced roundstart rulesets.", 1)
else if(href_list["f_dynamic_latejoin"])
if(!check_rights(R_ADMIN))
return
if(!SSticker || !SSticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/latejoin_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/latejoin))
var/datum/dynamic_ruleset/latejoin/newrule = new rule()
latejoin_rules[newrule.name] = newrule
var/added_rule = input(usr,"What ruleset do you want to force upon the next latejoiner? This will bypass threat level and population restrictions.", "Rigging Latejoin", null) as null|anything in latejoin_rules
if (added_rule)
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.forced_latejoin_rule = latejoin_rules[added_rule]
log_admin("[key_name(usr)] set [added_rule] to proc on the next latejoin.")
message_admins("[key_name(usr)] set [added_rule] to proc on the next latejoin.", 1)
Game()
else if(href_list["f_dynamic_latejoin_clear"])
if(!check_rights(R_ADMIN))
return
if (SSticker && SSticker.mode && istype(SSticker.mode,/datum/game_mode/dynamic))
var/datum/game_mode/dynamic/mode = SSticker.mode
mode.forced_latejoin_rule = null
Game()
log_admin("[key_name(usr)] cleared the forced latejoin ruleset.")
message_admins("[key_name(usr)] cleared the forced latejoin ruleset.", 1)
else if(href_list["f_dynamic_midround"])
if(!check_rights(R_ADMIN))
return
if(!SSticker || !SSticker.mode)
return alert(usr, "The game must start first.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/midround_rules = list()
for (var/rule in subtypesof(/datum/dynamic_ruleset/midround))
var/datum/dynamic_ruleset/midround/newrule = new rule()
midround_rules[newrule.name] = rule
var/added_rule = input(usr,"What ruleset do you want to force right now? This will bypass threat level and population restrictions.", "Execute Ruleset", null) as null|anything in midround_rules
if (added_rule)
var/datum/game_mode/dynamic/mode = SSticker.mode
log_admin("[key_name(usr)] executed the [added_rule] ruleset.")
message_admins("[key_name(usr)] executed the [added_rule] ruleset.", 1)
mode.picking_specific_rule(midround_rules[added_rule],1)
else if (href_list["f_dynamic_options"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_centre"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_centre = input(usr,"Change the centre of the dynamic mode threat curve. A negative value will give a more peaceful round ; a positive value, a round with higher threat. Any number between -5 and +5 is allowed.", "Change curve centre", null) as num
if (new_centre < -5 || new_centre > 5)
return alert(usr, "Only values between -5 and +5 are allowed.", null, null, null, null)
log_admin("[key_name(usr)] changed the distribution curve center to [new_centre].")
message_admins("[key_name(usr)] changed the distribution curve center to [new_centre]", 1)
GLOB.dynamic_curve_centre = new_centre
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_width"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_width = input(usr,"Change the width of the dynamic mode threat curve. A higher value will favour extreme rounds ; a lower value, a round closer to the average. Any Number between 0.5 and 4 are allowed.", "Change curve width", null) as num
if (new_width < 0.5 || new_width > 4)
return alert(usr, "Only values between 0.5 and +2.5 are allowed.", null, null, null, null)
log_admin("[key_name(usr)] changed the distribution curve width to [new_width].")
message_admins("[key_name(usr)] changed the distribution curve width to [new_width]", 1)
GLOB.dynamic_curve_width = new_width
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_latejoin_min"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_min = input(usr,"Change the minimum delay of latejoin injection in minutes.", "Change latejoin injection delay minimum", null) as num
if(new_min <= 0)
return alert(usr, "The minimum can't be zero or lower.", null, null, null, null)
if((new_min MINUTES) > GLOB.dynamic_latejoin_delay_max)
return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null)
log_admin("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes.")
message_admins("[key_name(usr)] changed the latejoin injection minimum delay to [new_min] minutes", 1)
GLOB.dynamic_latejoin_delay_min = (new_min MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_latejoin_max"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_max = input(usr,"Change the maximum delay of latejoin injection in minutes.", "Change latejoin injection delay maximum", null) as num
if(new_max <= 0)
return alert(usr, "The maximum can't be zero or lower.", null, null, null, null)
if((new_max MINUTES) < GLOB.dynamic_latejoin_delay_min)
return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null)
log_admin("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes.")
message_admins("[key_name(usr)] changed the latejoin injection maximum delay to [new_max] minutes", 1)
GLOB.dynamic_latejoin_delay_max = (new_max MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_midround_min"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_min = input(usr,"Change the minimum delay of midround injection in minutes.", "Change midround injection delay minimum", null) as num
if(new_min <= 0)
return alert(usr, "The minimum can't be zero or lower.", null, null, null, null)
if((new_min MINUTES) > GLOB.dynamic_midround_delay_max)
return alert(usr, "The minimum must be lower than the maximum.", null, null, null, null)
log_admin("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes.")
message_admins("[key_name(usr)] changed the midround injection minimum delay to [new_min] minutes", 1)
GLOB.dynamic_midround_delay_min = (new_min MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_roundstart_midround_max"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_max = input(usr,"Change the maximum delay of midround injection in minutes.", "Change midround injection delay maximum", null) as num
if(new_max <= 0)
return alert(usr, "The maximum can't be zero or lower.", null, null, null, null)
if((new_max MINUTES) > GLOB.dynamic_midround_delay_max)
return alert(usr, "The maximum must be higher than the minimum.", null, null, null, null)
log_admin("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes.")
message_admins("[key_name(usr)] changed the midround injection maximum delay to [new_max] minutes", 1)
GLOB.dynamic_midround_delay_max = (new_max MINUTES)
dynamic_mode_options(usr)
else if(href_list["f_dynamic_force_extended"])
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_forced_extended = !GLOB.dynamic_forced_extended
log_admin("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].")
message_admins("[key_name(usr)] set 'forced_extended' to [GLOB.dynamic_forced_extended].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_no_stacking"])
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_no_stacking = !GLOB.dynamic_no_stacking
log_admin("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].")
message_admins("[key_name(usr)] set 'no_stacking' to [GLOB.dynamic_no_stacking].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_classic_secret"])
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_classic_secret = !GLOB.dynamic_classic_secret
log_admin("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].")
message_admins("[key_name(usr)] set 'classic_secret' to [GLOB.dynamic_classic_secret].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_stacking_limit"])
if(!check_rights(R_ADMIN))
return
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
GLOB.dynamic_stacking_limit = input(usr,"Change the threat limit at which round-endings rulesets will start to stack.", "Change stacking limit", null) as num
log_admin("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].")
message_admins("[key_name(usr)] set 'stacking_limit' to [GLOB.dynamic_stacking_limit].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_high_pop_limit"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_value = input(usr, "Enter the high-pop override threshold for dynamic mode.", "High pop override") as num
if (new_value < 0)
return alert(usr, "Only positive values allowed!", null, null, null, null)
GLOB.dynamic_high_pop_limit = new_value
log_admin("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].")
message_admins("[key_name(usr)] set 'high_pop_limit' to [GLOB.dynamic_high_pop_limit].")
dynamic_mode_options(usr)
else if(href_list["f_dynamic_forced_threat"])
if(!check_rights(R_ADMIN))
return
if(SSticker && SSticker.mode)
return alert(usr, "The game has already started.", null, null, null, null)
if(GLOB.master_mode != "dynamic")
return alert(usr, "The game mode has to be dynamic mode!", null, null, null, null)
var/new_value = input(usr, "Enter the forced threat level for dynamic mode.", "Forced threat level") as num
if (new_value > 100)
return alert(usr, "The value must be be under 100.", null, null, null, null)
GLOB.dynamic_forced_threat_level = new_value
log_admin("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].")
message_admins("[key_name(usr)] set 'forced_threat_level' to [GLOB.dynamic_forced_threat_level].")
dynamic_mode_options(usr)
else if(href_list["c_mode2"])
if(!check_rights(R_ADMIN|R_SERVER))
+5
View File
@@ -75,3 +75,8 @@
var/datum/player_details/player_details //these persist between logins/logouts during the same round.
var/list/char_render_holders //Should only be a key-value list of north/south/east/west = obj/screen.
var/client_keysend_amount = 0
var/next_keysend_reset = 0
var/next_keysend_trip_reset = 0
var/keysend_tripped = FALSE
+3 -3
View File
@@ -700,13 +700,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<h2>Clothing & Equipment</h2>"
dat += "<b>Underwear:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=underwear;task=input'>[underwear]</a>"
if(UNDIE_COLORABLE(GLOB.underwear_list[underwear]))
dat += "<b>Underwear Color:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=undie_color;task=input'>[undie_color]</a>"
dat += "<b>Underwear Color:</b> <span style='border:1px solid #161616; background-color: #[undie_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=undie_color;task=input'>Change</a><BR>"
dat += "<b>Undershirt:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=undershirt;task=input'>[undershirt]</a>"
if(UNDIE_COLORABLE(GLOB.undershirt_list[undershirt]))
dat += "<b>Undershirt Color:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=shirt_color;task=input'>[shirt_color]</a>"
dat += "<b>Undershirt Color:</b> <span style='border:1px solid #161616; background-color: #[shirt_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=shirt_color;task=input'>Change</a><BR>"
dat += "<b>Socks:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=socks;task=input'>[socks]</a>"
if(UNDIE_COLORABLE(GLOB.socks_list[socks]))
dat += "<b>Socks Color:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=socks_color;task=input'>[socks_color]</a>"
dat += "<b>Socks Color:</b> <span style='border:1px solid #161616; background-color: #[socks_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=socks_color;task=input'>Change</a><BR>"
dat += "<b>Backpack:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=bag;task=input'>[backbag]</a>"
dat += "<b>Jumpsuit:</b><BR><a href ='?_src_=prefs;preference=suit;task=input'>[jumpsuit_style]</a><BR>"
dat += "<b>Uplink Location:</b><a style='display:block;width:100px' href ='?_src_=prefs;preference=uplink_loc;task=input'>[uplink_spawn_loc]</a>"
+3 -3
View File
@@ -408,11 +408,11 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
hair_style = sanitize_inlist(hair_style, GLOB.hair_styles_female_list)
facial_hair_style = sanitize_inlist(facial_hair_style, GLOB.facial_hair_styles_female_list)
underwear = sanitize_inlist(underwear, GLOB.underwear_list)
undie_color = sanitize_hexcolor(undie_color, 6, 1, initial(undie_color))
undie_color = sanitize_hexcolor(undie_color, 3, FALSE, initial(undie_color))
undershirt = sanitize_inlist(undershirt, GLOB.undershirt_list)
shirt_color = sanitize_hexcolor(shirt_color, 6, 1, initial(shirt_color))
shirt_color = sanitize_hexcolor(shirt_color, 6, FALSE, initial(shirt_color))
socks = sanitize_inlist(socks, GLOB.socks_list)
socks_color = sanitize_hexcolor(socks_color, 6, 1, initial(socks_color))
socks_color = sanitize_hexcolor(socks_color, 6, FALSE, initial(socks_color))
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
hair_color = sanitize_hexcolor(hair_color, 3, 0)
facial_hair_color = sanitize_hexcolor(facial_hair_color, 3, 0)
+24 -6
View File
@@ -23,30 +23,48 @@
allowed = list(/obj/item/disk, /obj/item/stamp, /obj/item/reagent_containers/food/drinks/flask, /obj/item/melee, /obj/item/storage/lockbox/medal, /obj/item/assembly/flash/handheld, /obj/item/storage/box/matches, /obj/item/lighter, /obj/item/clothing/mask/cigarette, /obj/item/storage/fancy/cigarettes, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
//Chaplain
/obj/item/clothing/suit/nun
/obj/item/clothing/suit/chaplain
name = "chaplain suit"
desc = "A piece of clothing adorned by the gods of Coding. Should never exist in this mortal realm."
allowed = list(/obj/item/storage/book/bible, HOLY_WEAPONS, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
/obj/item/clothing/suit/chaplain/nun
name = "nun robe"
desc = "Maximum piety in this star system."
icon_state = "nun"
item_state = "nun"
body_parts_covered = CHEST|GROIN|LEGS|ARMS|HANDS
flags_inv = HIDESHOES|HIDEJUMPSUIT
allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
/obj/item/clothing/suit/studentuni
/obj/item/clothing/suit/chaplain/studentuni
name = "student robe"
desc = "The uniform of a bygone institute of learning."
icon_state = "studentuni"
item_state = "studentuni"
body_parts_covered = ARMS|CHEST
allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
/obj/item/clothing/suit/witchhunter
/obj/item/clothing/suit/chaplain/witchhunter
name = "witchunter garb"
desc = "This worn outfit saw much use back in the day."
icon_state = "witchhunter"
item_state = "witchhunter"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
/obj/item/clothing/suit/chaplain/pharaoh
name = "pharaoh tunic"
desc = "Lavish space tomb not included."
icon_state = "pharaoh"
icon_state = "pharaoh"
body_parts_covered = CHEST|GROIN
/obj/item/clothing/suit/chaplain/holidaypriest
name = "holiday priest"
desc = "This is a nice holiday, my son."
icon_state = "holidaypriest"
item_state = "w_suit"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
flags_inv = HIDEJUMPSUIT
//Chef
/obj/item/clothing/suit/toggle/chef
@@ -167,16 +167,6 @@
icon_state = "griffin_wings"
item_state = "griffin_wings"
/obj/item/clothing/suit/holidaypriest
name = "holiday priest"
desc = "This is a nice holiday, my son."
icon_state = "holidaypriest"
item_state = "w_suit"
body_parts_covered = CHEST|GROIN|LEGS|ARMS
flags_inv = HIDEJUMPSUIT
allowed = list(/obj/item/storage/book/bible, /obj/item/nullrod, /obj/item/reagent_containers/food/drinks/bottle/holywater, /obj/item/storage/fancy/candle_box, /obj/item/candle, /obj/item/tank/internals/emergency_oxygen, /obj/item/tank/internals/plasmaman)
/obj/item/clothing/suit/cardborg
name = "cardborg suit"
desc = "An ordinary cardboard box with holes cut in the sides."
@@ -468,13 +458,6 @@
flags_inv = HIDEGLOVES|HIDESHOES|HIDEJUMPSUIT
allowed = list(/obj/item/clothing/mask/facehugger/toy)
/obj/item/clothing/suit/pharaoh
name = "pharaoh tunic"
desc = "Lavish space tomb not included."
icon_state = "pharaoh"
icon_state = "pharaoh"
body_parts_covered = CHEST|GROIN
// WINTER COATS
+34 -2
View File
@@ -1,4 +1,4 @@
/obj/item/clothing/accessory //Ties moved to neck slot items, but as there are still things like medals and armbands, this accessory system is being kept as-is
/obj/item/clothing/accessory //Ties moved to neck slot items, but as there are still things like medals, pokadots, and armbands, this accessory system is being kept as-is
name = "Accessory"
desc = "Something has gone wrong!"
icon = 'icons/obj/clothing/accessories.dmi'
@@ -368,7 +368,7 @@
/obj/item/clothing/accessory/kevlar
name = "kevlar sheets"
desc = "Long thin sheets of kevlar to help resist bullets and some physical attacks.."
desc = "Long thin sheets of kevlar to help resist bullets and some physical attacks."
icon_state = "padding"
item_color = "nothing"
armor = list("melee" = 10, "bullet" = 20, "laser" = 0, "energy" = 0, "bomb" = 10, "bio" = 0, "rad" = 0, "fire" = 0, "acid" = 25)
@@ -379,3 +379,35 @@
icon_state = "plastics"
item_color = "nothing"
armor = list("melee" = 0, "bullet" = 0, "laser" = 20, "energy" = 10, "bomb" = 0, "bio" = 30, "rad" = 0, "fire" = 0, "acid" = -40)
/////////////////////
//Pokadots On Pants//
/////////////////////
/obj/item/clothing/accessory/attrocious_pokadots
name = "atrocious pokadots"
desc = "They look like something out of a thrift store. Attaches to clothing not to be worn by itself."
icon_state = "attrocious_pokadots"
item_color = "attrocious_pokadots"
attack_verb = list("horrifed", "eye bleeded")
/obj/item/clothing/accessory/black_white_pokadots
name = "checkered pokadots"
desc = "You can play a game of chess on these! Attaches to clothing not to be worn by itself."
icon_state = "black_white_pokadots"
item_color = "black_white_pokadots"
attack_verb = list("check", "mate")
/obj/item/clothing/accessory/nt_pokadots
name = "blue and white pokadots"
desc = "To show your pride in your workplace, in the most annoying possable way. Attaches to clothing not to be worn by itself."
icon_state = "nt_pokadots"
item_color = "nt_pokadots"
attack_verb = list("eye bleeded", "annoyed")
/obj/item/clothing/accessory/syndi_pokadots
name = "black and red pokadots"
desc = "King me. Attaches to clothing not to be worn by itself." //checkers!
icon_state = "syndi_pokadots"
item_color = "syndi_pokadots"
attack_verb = list("jumped", "taken")
@@ -23,7 +23,7 @@
result = /obj/item/reagent_containers/food/snacks/donut
subcategory = CAT_PASTRY
/datum/crafting_recipe/food/donut
/datum/crafting_recipe/food/donut/semen
time = 15
name = "Semen donut"
reqs = list(
+40 -1
View File
@@ -4,7 +4,42 @@
set instant = TRUE
set hidden = TRUE
client_keysend_amount += 1
var/cache = client_keysend_amount
if(keysend_tripped && next_keysend_trip_reset <= world.time)
keysend_tripped = FALSE
if(next_keysend_reset <= world.time)
client_keysend_amount = 0
next_keysend_reset = world.time + (1 SECONDS)
//The "tripped" system is to confirm that flooding is still happening after one spike
//not entirely sure how byond commands interact in relation to lag
//don't want to kick people if a lag spike results in a huge flood of commands being sent
if(cache >= MAX_KEYPRESS_AUTOKICK)
if(!keysend_tripped)
keysend_tripped = TRUE
next_keysend_trip_reset = world.time + (2 SECONDS)
else
log_admin("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.")
message_admins("Client [ckey] was just autokicked for flooding keysends; likely abuse but potentially lagspike.")
QDEL_IN(src, 1)
return
///Check if the key is short enough to even be a real key
if(LAZYLEN(_key) > MAX_KEYPRESS_COMMANDLENGTH)
to_chat(src, "<span class='userdanger'>Invalid KeyDown detected! You have been disconnected from the server automatically.</span>")
log_admin("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.")
message_admins("Client [ckey] just attempted to send an invalid keypress. Keymessage was over [MAX_KEYPRESS_COMMANDLENGTH] characters, autokicking due to likely abuse.")
QDEL_IN(src, 1)
return
//offset by 1 because the buffer address is 0 indexed because the math was simpler
keys_held[current_key_address + 1] = _key
//the time a key was pressed isn't actually used anywhere (as of 2019-9-10) but this allows easier access usage/checking
keys_held[_key] = world.time
current_key_address = ((current_key_address + 1) % HELD_KEY_BUFFER_LENGTH)
var/movement = SSinput.movement_keys[_key]
if(!(next_move_dir_sub & movement) && !keys_held["Ctrl"])
next_move_dir_add |= movement
@@ -35,7 +70,11 @@
set instant = TRUE
set hidden = TRUE
keys_held -= _key
//Can't just do a remove because it would alter the length of the rolling buffer, instead search for the key then null it out if it exists
for(var/i in 1 to HELD_KEY_BUFFER_LENGTH)
if(keys_held[i] == _key)
keys_held[i] = null
break
var/movement = SSinput.movement_keys[_key]
if(!(next_move_dir_add & movement))
next_move_dir_sub |= movement
+15 -5
View File
@@ -1,9 +1,14 @@
/client
var/list/keys_held = list() // A list of any keys held currently
// These next two vars are to apply movement for keypresses and releases made while move delayed.
// Because discarding that input makes the game less responsive.
var/next_move_dir_add // On next move, add this dir to the move that would otherwise be done
var/next_move_dir_sub // On next move, subtract this dir from the move that would otherwise be done
/// A rolling buffer of any keys held currently
var/list/keys_held = list()
///used to keep track of the current rolling buffer position
var/current_key_address = 0
/// These next two vars are to apply movement for keypresses and releases made while move delayed.
/// Because discarding that input makes the game less responsive.
/// On next move, add this dir to the move that would otherwise be done
var/next_move_dir_add
/// On next move, subtract this dir from the move that would otherwise be done
var/next_move_dir_sub
// Set a client's focus to an object and override these procs on that object to let it handle keypresses
@@ -31,6 +36,11 @@
/client/proc/set_macros()
set waitfor = FALSE
//Reset and populate the rolling buffer
keys_held.Cut()
for(var/i in 1 to HELD_KEY_BUFFER_LENGTH)
keys_held += null
erase_all_macros()
var/list/macro_sets = SSinput.macro_sets
+233
View File
@@ -0,0 +1,233 @@
# `/datum/oracle_ui`
This datum is a replacement for tgui which does not use any Node.js dependencies, and works entirely through raw HTML, JS and CSS. It's designed to be reasonably easy to port something from tgui to oracle_ui.
### How to create a UI
For this example, we're going to port the disposals bin from tgui to oracle_ui.
#### Step 1
In order to create a UI, you will first need to create an instance of `/datum/oracle_ui` or one of its subclasses, in this case `/datum/oracle_ui/themed/nano`.
You need to pass in `src`, the width of the window, the height of the window, and the template to render from. You can optionally set some flags to disallow window resizing and whether to automatically refresh the UI.
`code/modules/recycling/disposal-unit.dm`
```dm
/obj/machinery/disposal/bin/Initialize(mapload, obj/structure/disposalconstruct/make_from)
. = ..()
ui = new /datum/oracle_ui/themed/nano(src, 330, 190, "disposal_bin")
ui.auto_refresh = TRUE
ui.can_resize = FALSE
```
#### Step 2
You will now need to make a template in `html/oracle_ui/content/{template_name}`.
Values defined as `@{value}` will get replaced at runtime by oracle_ui.
`html/oracle_ui/content/disposal_bin/index.html`
```html
<div class='display'>
<section>
<span class='label'>State:</span>
<div class='content' id="full_pressure">@{full_pressure}</div>
</section>
<section>
<span class='label'>Pressure:</span>
<div class='content'>
<div class='progressBar' id='per'>
<div class='progressFill' style="width: @{per}"></div>
<div class='progressLabel'>@{per}</div>
</div>
</div>
</section>
<section>
<span class='label'>Handle:</span>
<div class='content' id="flush">@{flush}</div>
</section>
<section>
<span class='label'>Eject:</span>
<div class='content' id="contents">@{contents}</div>
</section>
<section>
<span class='label'>Compressor:</span>
<div class='content' id="pressure_charging">@{pressure_charging}</div>
</section>
</div>
```
#### Step 3
Now you need to implement the methods that provide data to oracle_ui. `oui_data` can be adapted from the `ui_data` proc that tgui uses.
The `act` proc generates a hyperlink that will result in `oui_act` getting called on your object when clicked. The `class` argument defines a css class to be added to the hyperlink, and disabled determines whether the hyperlink will be disabled or not.
Calling `soft_update_fields` will result in the UI being updated on all clients, which is useful when the object changes state.
`code/modules/recycling/disposal-unit.dm`
```dm
/obj/machinery/disposal/bin/oui_data(mob/user)
var/list/data = list()
data["flush"] = flush ? ui.act("Disengage", user, "handle-0", class="active") : ui.act("Engage", user, "handle-1")
data["full_pressure"] = full_pressure ? "Ready" : (pressure_charging ? "Pressurizing" : "Off")
data["pressure_charging"] = pressure_charging ? ui.act("Turn Off", user, "pump-0", class="active", disabled=full_pressure) : ui.act("Turn On", user, "pump-1", disabled=full_pressure)
var/per = full_pressure ? 100 : Clamp(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 99)
data["per"] = "[round(per, 1)]%"
data["contents"] = ui.act("Eject Contents", user, "eject", disabled=contents.len < 1)
data["isai"] = isAI(user)
return data
/obj/machinery/disposal/bin/oui_act(mob/user, action, list/params)
if(..())
return
switch(action)
if("handle-0")
flush = FALSE
update_icon()
. = TRUE
if("handle-1")
if(!panel_open)
flush = TRUE
update_icon()
. = TRUE
if("pump-0")
if(pressure_charging)
pressure_charging = FALSE
update_icon()
. = TRUE
if("pump-1")
if(!pressure_charging)
pressure_charging = TRUE
update_icon()
. = TRUE
if("eject")
eject()
. = TRUE
ui.soft_update_fields()
```
#### Step 4
You now need to hook in and ensure oracle_ui is invoked upon clicking. `render` should be used to open the UI for a user, typically on click.
`code/modules/recycling/disposal-unit.dm`
```dm
/obj/machinery/disposal/bin/ui_interact(mob/user, state)
if(stat & BROKEN)
return
if(user.loc == src)
to_chat(user, "<span class='warning'>You cannot reach the controls from inside!</span>")
return
ui.render(user)
```
#### Done
![gif](https://user-images.githubusercontent.com/202160/37561879-1bb9179e-2a52-11e8-902c-80e6e6df7204.gif)
You should have a functional UI at this point. Some additional odds and ends can be discovered throughout `code/modules/recycling/disposal-unit.dm`. For a full diff of the changes made to it, refer to [the original pull request on GitHub](https://github.com/OracleStation/OracleStation/pull/702/files#diff-4b6c20ec7d37222630e7524d9577e230).
### API Reference
#### `/datum/oracle_ui`
The main datum which handles the UI.
##### `get_content(mob/target)`
Returns the HTML that should be displayed for a specified target mob. Calls `oui_getcontent` on the datasource to get the return value. *This proc is not used in the themed subclass.*
##### `can_view(mob/target)`
Returns whether the specified target mob can view the UI. Calls `oui_canview` on the datasource to get the return value.
##### `test_viewer(mob/target, updating)`
Tests whether the client is valid and can view the UI. If updating is TRUE, checks to see if they still have the UI window open.
##### `render(mob/target, updating = FALSE)`
Opens the UI for a target mob, sending HTML. If updating is TRUE, will only do it to clients which still have the window open.
##### `render_all()`
Does the above, but for all viewers and with updating set to TRUE.
##### `close(mob/target)`
Closes the UI for the specified target mob.
##### `close_all()`
Does the above, but for all viewers.
##### `check_view(mob/target)`
Checks if the specified target mob can view the UI, and if they can't closes their UI
##### `check_view_all()`
Does the above, but for all viewers.
##### `call_js(mob/target, js_func, list/parameters = list())`
Invokes `js_func` in the UI of the specified target mob with the specified parameters.
##### `call_js_all(js_func, list/parameters = list()))`
Does the above, but for all viewers.
##### `steal_focus(mob/target)`
Causes the UI to steal focus for the specified target mob.
##### `steal_focus_all()`
Does the above, but for all viewers.
##### `flash(mob/target, times = -1)`
Causes the UI to flash for the specified target mob the specified number of times, the default keeps the element flashing until focused.
##### `flash_all()`
Does the above, but for all viewers.
##### `href(mob/user, action, list/parameters = list())`
Generates a href for the specified user which will invoke `oui_act` on the datasource with the specified action and parameters.
#### `/datum/oracle_ui/themed`
A subclass which supports templating and theming.
##### `get_file(path)`
Loads a file from disk and returns the contents. Caches files loaded from disk for you.
##### `get_content_file(filename)`
Loads a file from the current content folder and returns the contents.
##### `get_themed_file(filename)`
Loads a file from the current theme folder and returns the contents.
##### `process_template(template, variables)`
Processes a template and populates it with the provided variables.
##### `get_inner_content(mob/target)`
Returns the templated content to be inserted into the main template for the specified target mob.
##### `soft_update_fields()`
For all viewers, updates the fields in the template via the `updateFields` javaScript function.
##### `soft_update_all()`
For all viewers, updates the content body in the template via the `replaceContent` javaScript function.
##### `change_page(var/newpage)`
Changes the template to use to draw the page and forces an update to all viewers
##### `act(label, mob/user, action, list/parameters = list(), class = "", disabled = FALSE`
Returns a fully formatted hyperlink for the specified user. `label` will be the hyperlink label, `action` and `parameters` are what will be passed to `oui_act`, `class` is any CSS classes to apply to the hyperlink and `disabled` will disable the hyperlink.
#### `/datum`
Functions built into all objects to support oracle_ui. There are default implementations for most major superclasses.
##### `oui_canview(mob/user)`
Returns whether the specified user view the UI at this time.
##### `oui_getcontent(mob/user)`
Returns the raw HTML to be sent to the specified user. *This proc is not used in the themed subclass of oracle_ui.*
##### `oui_data(mob/user)`
Returns templating data for the specified user. *This proc is only used in the themed subclass of oracle_ui.*
##### `oui_data_debug(mob/user)`
Returns the above, but JSON-encoded and escaped, for copy pasting into the web IDE. *This proc is only used for debugging purposes.*
##### `oui_act(mob/user, action, list/params)`
Called when a hyperlink is clicked in the UI.
+8
View File
@@ -0,0 +1,8 @@
/datum/asset/simple/oui_theme_nano
assets = list(
// JavaScript
"sui-nano-common.js" = 'html/oracle_ui/themes/nano/sui-nano-common.js',
"sui-nano-jquery.min.js" = 'html/oracle_ui/themes/nano/sui-nano-jquery.min.js',
// Stylesheets
"sui-nano-common.css" = 'html/oracle_ui/themes/nano/sui-nano-common.css',
)
+44
View File
@@ -0,0 +1,44 @@
/datum/proc/oui_canview(mob/user)
return TRUE
/datum/proc/oui_getcontent(mob/user)
return "Default Implementation"
/datum/proc/oui_canuse(mob/user)
if(isobserver(user) && !user.has_unlimited_silicon_privilege)
return FALSE
return oui_canview(user)
/datum/proc/oui_data(mob/user)
return list()
/datum/proc/oui_data_debug(mob/user)
return html_encode(json_encode(oui_data(user)))
/datum/proc/oui_act(mob/user, action, list/params)
// No Implementation
/atom/oui_canview(mob/user)
if(isobserver(user))
return TRUE
if(user.incapacitated())
return FALSE
if(isturf(src.loc) && Adjacent(user))
return TRUE
return FALSE
/obj/item/oui_canview(mob/user)
if(src.loc == user)
return src in user.held_items
return ..()
/obj/machinery/oui_canview(mob/user)
if(user.has_unlimited_silicon_privilege)
return TRUE
if(!can_interact())
return FALSE
if(iscyborg(user))
return can_see(user, src, 7)
if(isAI(user))
return GLOB.cameranet.checkTurfVis(get_turf_pixel(src))
return ..()
+134
View File
@@ -0,0 +1,134 @@
/datum/oracle_ui
var/width = 512
var/height = 512
var/can_close = TRUE
var/can_minimize = FALSE
var/can_resize = TRUE
var/titlebar = TRUE
var/window_id = null
var/viewers[0]
var/auto_check_view = TRUE
var/auto_refresh = FALSE
var/atom/datasource = null
var/datum/asset/assets = null
/datum/oracle_ui/New(atom/n_datasource, n_width = 512, n_height = 512, n_assets = null)
datasource = n_datasource
window_id = REF(src)
width = n_width
height = n_height
/datum/oracle_ui/Destroy()
close_all()
if(src.datum_flags & DF_ISPROCESSING)
STOP_PROCESSING(SSobj, src)
return ..()
/datum/oracle_ui/process()
if(auto_check_view)
check_view_all()
if(auto_refresh)
render_all()
/datum/oracle_ui/proc/get_content(mob/target)
return call(datasource, "oui_getcontent")(target)
/datum/oracle_ui/proc/can_view(mob/target)
return call(datasource, "oui_canview")(target)
/datum/oracle_ui/proc/test_viewer(mob/target, updating)
//If the target is null or does not have a client, remove from viewers and return
if(!target | !target.client | !can_view(target))
viewers -= target
if(viewers.len < 1 && (src.datum_flags & DF_ISPROCESSING))
STOP_PROCESSING(SSobj, src) //No more viewers, stop polling
close(target)
return FALSE
//If this is an update, and they have closed the window, remove from viewers and return
if(updating && winget(target, window_id, "is-visible") != "true")
viewers -= target
if(viewers.len < 1 && (src.datum_flags & DF_ISPROCESSING))
STOP_PROCESSING(SSobj, src) //No more viewers, stop polling
return FALSE
return TRUE
/datum/oracle_ui/proc/render(mob/target, updating = FALSE)
set waitfor = FALSE //Makes this an async call
if(!can_view(target))
return
//Check to see if they have the window open still if updating
if(updating && !test_viewer(target, updating))
return
//Send assets
if(!updating && assets)
assets.send(target)
//Add them to the viewers if they aren't there already
viewers |= target
if(!(src.datum_flags & DF_ISPROCESSING) && (auto_refresh | auto_check_view))
START_PROCESSING(SSobj, src) //Start processing to poll for viewability
//Send the content
if(updating)
target << output(get_content(target), "[window_id].browser")
else
target << browse(get_content(target), "window=[window_id];size=[width]x[height];can_close=[can_close];can_minimize=[can_minimize];can_resize=[can_resize];titlebar=[titlebar];focus=false;")
steal_focus(target)
/datum/oracle_ui/proc/render_all()
for(var/viewer in viewers)
render(viewer, TRUE)
/datum/oracle_ui/proc/close(mob/target)
if(target && target.client)
target << browse(null, "window=[window_id]")
/datum/oracle_ui/proc/close_all()
for(var/viewer in viewers)
close(viewer)
viewers = list()
/datum/oracle_ui/proc/check_view_all()
for(var/viewer in viewers)
check_view(viewer)
/datum/oracle_ui/proc/check_view(mob/target)
set waitfor = FALSE //Makes this an async call
if(!test_viewer(target, TRUE))
close(target)
/datum/oracle_ui/proc/call_js(mob/target, js_func, list/parameters = list())
set waitfor = FALSE //Makes this an async call
if(!test_viewer(target, TRUE))
return
target << output(list2params(parameters),"[window_id].browser:[js_func]")
/datum/oracle_ui/proc/call_js_all(js_func, list/parameters = list())
for(var/viewer in viewers)
call_js(viewer, js_func, parameters)
/datum/oracle_ui/proc/steal_focus(mob/target)
set waitfor = FALSE //Makes this an async call
winset(target, "[window_id]","focus=true")
/datum/oracle_ui/proc/steal_focus_all()
for(var/viewer in viewers)
steal_focus(viewer)
/datum/oracle_ui/proc/flash(mob/target, times = -1)
set waitfor = FALSE //Makes this an async call
winset(target, "[window_id]","flash=[times]")
/datum/oracle_ui/proc/flash_all(times = -1)
for(var/viewer in viewers)
flash(viewer, times)
/datum/oracle_ui/proc/href(mob/user, action, list/parameters = list())
var/params_string = replacetext(list2params(parameters),"&",";")
return "?src=[REF(src)];sui_action=[action];sui_user=[REF(user)];[params_string]"
/datum/oracle_ui/Topic(href, parameters)
var/action = parameters["sui_action"]
var/mob/current_user = locate(parameters["sui_user"])
if(!call(datasource, "oui_canuse")(current_user))
return
if(datasource)
call(datasource, "oui_act")(current_user, action, parameters);
+82
View File
@@ -0,0 +1,82 @@
/datum/oracle_ui/themed
var/theme = ""
var/content_root = ""
var/current_page = "index.html"
var/root_template = ""
/datum/oracle_ui/themed/New(atom/n_datasource, n_width = 512, n_height = 512, n_content_root = "")
root_template = get_themed_file("index.html")
content_root = n_content_root
return ..(n_datasource, n_width, n_height, get_asset_datum(/datum/asset/simple/oui_theme_nano))
/datum/oracle_ui/themed/process()
if(auto_check_view)
check_view_all()
if(auto_refresh)
soft_update_fields()
GLOBAL_LIST_EMPTY(oui_template_variables)
GLOBAL_LIST_EMPTY(oui_file_cache)
/datum/oracle_ui/themed/proc/get_file(path)
if(GLOB.oui_file_cache[path])
return GLOB.oui_file_cache[path]
else if(fexists(path))
var/data = file2text(path)
GLOB.oui_file_cache[path] = data
return data
else
var/errormsg = "MISSING PATH '[path]'"
#ifndef UNIT_TESTS
log_world(errormsg) //Because Travis absolutely hates these procs
#endif
return errormsg
/datum/oracle_ui/themed/proc/get_content_file(filename)
return get_file("./modular_citadel/html/oracle_ui/content/[content_root]/[filename]")
/datum/oracle_ui/themed/proc/get_themed_file(filename)
return get_file("./modular_citadel/html/oracle_ui/themes/[theme]/[filename]")
/datum/oracle_ui/themed/proc/process_template(template, variables)
var/regex/pattern = regex("\\@\\{(\\w+)\\}","gi")
GLOB.oui_template_variables = variables
var/replaced = pattern.Replace(template, /proc/oui_process_template_replace)
GLOB.oui_template_variables = null
return replaced
/proc/oui_process_template_replace(match, group1)
var/value = GLOB.oui_template_variables[group1]
return "[value]"
/datum/oracle_ui/themed/proc/get_inner_content(mob/target)
var/list/data = call(datasource, "oui_data")(target)
return process_template(get_content_file(current_page), data)
/datum/oracle_ui/themed/get_content(mob/target)
var/list/template_data = list("title" = datasource.name, "body" = get_inner_content(target))
return process_template(root_template, template_data)
/datum/oracle_ui/themed/proc/soft_update_fields()
for(var/viewer in viewers)
var/json = json_encode(call(datasource, "oui_data")(viewer))
call_js(viewer, "updateFields", list(json))
/datum/oracle_ui/themed/proc/soft_update_all()
for(var/viewer in viewers)
call_js(viewer, "replaceContent", list(get_inner_content(viewer)))
/datum/oracle_ui/themed/proc/change_page(newpage)
if(newpage == current_page)
return
current_page = newpage
render_all()
/datum/oracle_ui/themed/proc/act(label, mob/user, action, list/parameters = list(), class = "", disabled = FALSE)
if(disabled)
return "<a class=\"disabled\">[label]</a>"
else
return "<a class=\"[class]\" href=\"" + href(user, action, parameters) + "\">[label]</a>"
/datum/oracle_ui/themed/nano
theme = "nano"
+37 -19
View File
@@ -31,6 +31,7 @@
var/spam_flag = 0
var/contact_poison // Reagent ID to transfer on contact
var/contact_poison_volume = 0
var/datum/oracle_ui/ui = null
/obj/item/paper/pickup(user)
@@ -40,16 +41,40 @@
if(!istype(G) || G.transfer_prints)
H.reagents.add_reagent(contact_poison,contact_poison_volume)
contact_poison = null
ui.check_view_all()
..()
/obj/item/paper/dropped(mob/user)
ui.check_view(user)
return ..()
/obj/item/paper/Initialize()
. = ..()
pixel_y = rand(-8, 8)
pixel_x = rand(-9, 9)
ui = new /datum/oracle_ui(src, 420, 600, get_asset_datum(/datum/asset/spritesheet/simple/paper))
ui.can_resize = FALSE
update_icon()
updateinfolinks()
/obj/item/paper/oui_getcontent(mob/target)
if(!target.is_literate())
return "<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[stars(info)]<HR>[stamps]</BODY></HTML>"
else if(istype(target.get_active_held_item(), /obj/item/pen) | istype(target.get_active_held_item(), /obj/item/toy/crayon))
return "<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[info_links]<HR>[stamps]</BODY><div align='right'style='position:fixed;bottom:0;font-style:bold;'><A href='?src=[REF(src)];help=1'>\[?\]</A></div></HTML>"
else
return "<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[info]<HR>[stamps]</BODY></HTML>"
/obj/item/paper/oui_canview(mob/target)
if(check_rights_for(target.client, R_FUN)) //Allows admins to view faxes
return TRUE
if(isAI(target))
var/mob/living/silicon/ai/ai = target
return get_dist(src, ai.current) < 2
if(iscyborg(target))
return get_dist(src, target) < 2
return ..()
/obj/item/paper/update_icon()
@@ -65,20 +90,13 @@
/obj/item/paper/examine(mob/user)
..()
to_chat(user, "<span class='notice'>Alt-click to fold it.</span>")
var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/simple/paper)
assets.send(user)
if(in_range(user, src) || isobserver(user))
if(user.is_literate())
user << browse("<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[info]<HR>[stamps]</BODY></HTML>", "window=[name]")
onclose(user, "[name]")
else
user << browse("<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[stars(info)]<HR>[stamps]</BODY></HTML>", "window=[name]")
onclose(user, "[name]")
if(oui_canview(user))
ui.render(user)
else
to_chat(user, "<span class='warning'>You're too far away to read it!</span>")
/obj/item/paper/proc/show_content(mob/user)
user.examinate(src)
/obj/item/paper/verb/rename()
set name = "Rename paper"
@@ -98,7 +116,7 @@
if((loc == usr && usr.stat == CONSCIOUS))
name = "paper[(n_name ? text("- '[n_name]'") : null)]"
add_fingerprint(usr)
ui.render_all()
/obj/item/paper/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] scratches a grid on [user.p_their()] wrist with the paper! It looks like [user.p_theyre()] trying to commit sudoku...</span>")
@@ -108,7 +126,7 @@
spam_flag = FALSE
/obj/item/paper/attack_self(mob/user)
user.examinate(src)
show_content(user)
if(rigged && (SSevents.holidays && SSevents.holidays[APRIL_FOOLS]))
if(!spam_flag)
spam_flag = TRUE
@@ -123,11 +141,9 @@
else //cyborg or AI not seeing through a camera
dist = get_dist(src, user)
if(dist < 2)
usr << browse("<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[info]<HR>[stamps]</BODY></HTML>", "window=[name]")
onclose(usr, "[name]")
show_content(user)
else
usr << browse("<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[stars(info)]<HR>[stamps]</BODY></HTML>", "window=[name]")
onclose(usr, "[name]")
to_chat(user, "<span class='notice'>You can't quite see it.</span>")
/obj/item/paper/proc/addtofield(id, text, links = 0)
@@ -173,6 +189,7 @@
for(var/i in 1 to min(fields, 15))
addtofield(i, "<font face=\"[PEN_FONT]\"><A href='?src=[REF(src)];write=[i]'>write</A></font>", 1)
info_links = info_links + "<font face=\"[PEN_FONT]\"><A href='?src=[REF(src)];write=end'>write</A></font>"
ui.render_all()
/obj/item/paper/proc/clearpaper()
@@ -274,7 +291,7 @@
else
info += t // Oh, he wants to edit to the end of the file, let him.
updateinfolinks()
usr << browse("<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[info_links]<HR>[stamps]</BODY><div align='right'style='position:fixed;bottom:0;font-style:bold;'><A href='?src=[REF(src)];help=1'>\[?\]</A></div></HTML>", "window=[name]") // Update the window
show_content(usr)
update_icon()
@@ -289,7 +306,7 @@
if(istype(P, /obj/item/pen) || istype(P, /obj/item/toy/crayon))
if(user.is_literate())
user << browse("<HTML><HEAD><TITLE>[name]</TITLE></HEAD><BODY>[info_links]<HR>[stamps]</BODY><div align='right'style='position:fixed;bottom:0;font-style:bold;'><A href='?src=[REF(src)];help=1'>\[?\]</A></div></HTML>", "window=[name]")
show_content(user)
return
else
to_chat(user, "<span class='notice'>You don't know how to read or write.</span>")
@@ -312,6 +329,7 @@
add_overlay(stampoverlay)
to_chat(user, "<span class='notice'>You stamp the paper with your rubber stamp.</span>")
ui.render_all()
if(P.is_hot())
if(HAS_TRAIT(user, TRAIT_CLUMSY) && prob(10))
@@ -207,6 +207,7 @@
name = "combat shotgun"
desc = "A semi automatic shotgun with tactical furniture and a six-shell capacity underneath."
icon_state = "cshotgun"
fire_delay = 3
mag_type = /obj/item/ammo_box/magazine/internal/shot/com
w_class = WEIGHT_CLASS_HUGE
unique_reskin = list("Tatical" = "cshotgun",
@@ -5,7 +5,7 @@
/obj/item/projectile/bullet/shotgun_beanbag
name = "beanbag slug"
damage = 5
stamina = 80
stamina = 70
/obj/item/projectile/bullet/incendiary/shotgun
name = "incendiary slug"
@@ -61,12 +61,12 @@
/obj/item/projectile/bullet/pellet/shotgun_buckshot
name = "buckshot pellet"
damage = 12.5
damage = 10
/obj/item/projectile/bullet/pellet/shotgun_rubbershot
name = "rubbershot pellet"
damage = 3
stamina = 25
damage = 2
stamina = 15
/obj/item/projectile/bullet/pellet/Range()
..()
@@ -233,6 +233,12 @@
else
reagents.remove_reagent(id, amount)
. = TRUE
else if (amount == -1) // -1 means custom amount
useramount = input("Enter the Amount you want to transfer:", name, useramount) as num|null
if (useramount > 0)
end_fermi_reaction()
reagents.trans_id_to(beaker, id, useramount)
. = TRUE
if("toggleMode")
mode = !mode
@@ -379,7 +385,7 @@
reagents.trans_to(P, vol_part)
. = TRUE
//END CITADEL ADDITIONS
if("analyze")
if("analyzeBeak")
var/datum/reagent/R = GLOB.chemical_reagents_list[params["id"]]
if(R)
var/state = "Unknown"
@@ -395,7 +401,38 @@
fermianalyze = TRUE
var/datum/chemical_reaction/Rcr = get_chemical_reaction(R.id)
var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2
analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = initial(R.purity), "inverseRatioF" = initial(R.InverseChemVal), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache)
var/datum/reagent/targetReagent = beaker.reagents.has_reagent("[R.id]")
if(!targetReagent)
CRASH("Tried to find a reagent that doesn't exist in the chem_master!")
analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = targetReagent.purity, "inverseRatioF" = initial(R.InverseChemVal), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache)
else
fermianalyze = FALSE
analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold))
screen = "analyze"
return
if("analyzeBuff")
var/datum/reagent/R = GLOB.chemical_reagents_list[params["id"]]
if(R)
var/state = "Unknown"
if(initial(R.reagent_state) == 1)
state = "Solid"
else if(initial(R.reagent_state) == 2)
state = "Liquid"
else if(initial(R.reagent_state) == 3)
state = "Gas"
var/const/P = 3 //The number of seconds between life ticks
var/T = initial(R.metabolization_rate) * (60 / P)
if(istype(R, /datum/reagent/fermi))
fermianalyze = TRUE
var/datum/chemical_reaction/Rcr = get_chemical_reaction(R.id)
var/pHpeakCache = (Rcr.OptimalpHMin + Rcr.OptimalpHMax)/2
var/datum/reagent/targetReagent = reagents.has_reagent("[R.id]")
if(!targetReagent)
CRASH("Tried to find a reagent that doesn't exist in the chem_master!")
analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold), "purityF" = targetReagent.purity, "inverseRatioF" = initial(R.InverseChemVal), "purityE" = initial(Rcr.PurityMin), "minTemp" = initial(Rcr.OptimalTempMin), "maxTemp" = initial(Rcr.OptimalTempMax), "eTemp" = initial(Rcr.ExplodeTemp), "pHpeak" = pHpeakCache)
else
fermianalyze = FALSE
analyzeVars = list("name" = initial(R.name), "state" = state, "color" = initial(R.color), "description" = initial(R.description), "metaRate" = T, "overD" = initial(R.overdose_threshold), "addicD" = initial(R.addiction_threshold))
+35 -15
View File
@@ -264,6 +264,13 @@
name = "disposal unit"
desc = "A pneumatic waste disposal unit."
icon_state = "disposal"
var/datum/oracle_ui/themed/nano/ui
/obj/machinery/disposal/bin/Initialize(mapload, obj/structure/disposalconstruct/make_from)
. = ..()
ui = new /datum/oracle_ui/themed/nano(src, 330, 190, "disposal_bin")
ui.auto_refresh = TRUE
ui.can_resize = FALSE
// attack by item places it in to disposal
/obj/machinery/disposal/bin/attackby(obj/item/I, mob/user, params)
@@ -275,32 +282,43 @@
STR.remove_from_storage(O,src)
T.update_icon()
update_icon()
ui.soft_update_fields()
else
ui.soft_update_fields()
return ..()
// handle machine interaction
/obj/machinery/disposal/bin/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
/obj/machinery/disposal/bin/ui_interact(mob/user, state)
if(stat & BROKEN)
return
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "disposal_unit", name, 300, 200, master_ui, state)
ui.open()
if(user.loc == src)
to_chat(user, "<span class='warning'>You cannot reach the controls from inside!</span>")
return
ui.render(user)
/obj/machinery/disposal/bin/ui_data(mob/user)
/obj/machinery/disposal/bin/oui_canview(mob/user)
if(user.loc == src)
return FALSE
if(stat & BROKEN)
return FALSE
if(Adjacent(user))
return TRUE
return ..()
/obj/machinery/disposal/bin/oui_data(mob/user)
var/list/data = list()
data["flush"] = flush
data["full_pressure"] = full_pressure
data["pressure_charging"] = pressure_charging
data["panel_open"] = panel_open
var/per = CLAMP(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 100)
data["per"] = round(per, 1)
data["flush"] = flush ? ui.act("Disengage", user, "handle-0", class="active") : ui.act("Engage", user, "handle-1")
data["full_pressure"] = full_pressure ? "Ready" : (pressure_charging ? "Pressurizing" : "Off")
data["pressure_charging"] = pressure_charging ? ui.act("Turn Off", user, "pump-0", class="active", disabled=full_pressure) : ui.act("Turn On", user, "pump-1", disabled=full_pressure)
var/per = full_pressure ? 100 : CLAMP(100* air_contents.return_pressure() / (SEND_PRESSURE), 0, 99)
data["per"] = "[round(per, 1)]%"
data["contents"] = ui.act("Eject Contents", user, "eject", disabled=contents.len < 1)
data["isai"] = isAI(user)
return data
/obj/machinery/disposal/bin/ui_act(action, params)
/obj/machinery/disposal/bin/oui_act(mob/user, action, list/params)
if(..())
return
@@ -327,6 +345,7 @@
if("eject")
eject()
. = TRUE
ui.soft_update_fields()
/obj/machinery/disposal/bin/hitby(atom/movable/AM)
@@ -346,6 +365,7 @@
full_pressure = FALSE
pressure_charging = TRUE
update_icon()
ui.soft_update_fields()
/obj/machinery/disposal/bin/update_icon()
cut_overlays()
@@ -389,7 +409,7 @@
do_flush()
flush_count = 0
updateDialog()
ui.soft_update_fields()
if(flush && air_contents.return_pressure() >= SEND_PRESSURE) // flush can happen even without power
do_flush()
+2 -2
View File
@@ -54,7 +54,7 @@
/obj/item/clothing/head/ushanka = 1,
/obj/item/clothing/suit/imperium_monk = 1,
/obj/item/clothing/mask/gas/cyborg = 1,
/obj/item/clothing/suit/holidaypriest = 1,
/obj/item/clothing/suit/chaplain/holidaypriest = 1,
/obj/item/clothing/head/wizard/marisa/fake = 1,
/obj/item/clothing/suit/wizrobe/marisa/fake = 1,
/obj/item/clothing/under/sundress = 1,
@@ -122,7 +122,7 @@
/obj/item/clothing/shoes/roman = 1,
/obj/item/shield/riot/roman/fake = 1,
/obj/item/skub = 1,
/obj/item/clothing/under/lobster = 1, // CIT CHANGES
/obj/item/clothing/under/lobster = 1, // CIT CHANGES
/obj/item/clothing/head/lobsterhat = 1,
/obj/item/clothing/head/drfreezehat = 1,
/obj/item/clothing/suit/dracula = 1,
+7 -3
View File
@@ -99,7 +99,9 @@
/obj/item/clothing/suit/jacket/letterman_red = 3,
/obj/item/clothing/ears/headphones = 10,
/obj/item/clothing/suit/apron/purple_bartender = 4,
/obj/item/clothing/under/rank/bartender/purple = 4)
/obj/item/clothing/under/rank/bartender/purple = 4,
/obj/item/clothing/accessory/attrocious_pokadots = 8,
/obj/item/clothing/accessory/black_white_pokadots = 8)
contraband = list(/obj/item/clothing/under/syndicate/tacticool = 3,
/obj/item/clothing/under/syndicate/tacticool/skirt = 3,
/obj/item/clothing/mask/balaclava = 3,
@@ -109,7 +111,8 @@
/obj/item/clothing/suit/jacket/letterman_syndie = 5,
/obj/item/clothing/under/jabroni = 2,
/obj/item/clothing/suit/vapeshirt = 2,
/obj/item/clothing/under/geisha = 4)
/obj/item/clothing/under/geisha = 4,
/obj/item/clothing/accessory/syndi_pokadots = 4)
premium = list(/obj/item/clothing/under/suit_jacket/checkered = 4,
/obj/item/clothing/head/mailman = 2,
/obj/item/clothing/under/rank/mailman = 2,
@@ -117,7 +120,8 @@
/obj/item/clothing/suit/jacket/leather/overcoat = 4,
/obj/item/clothing/under/pants/mustangjeans = 3,
/obj/item/clothing/neck/necklace/dope = 5,
/obj/item/clothing/suit/jacket/letterman_nanotrasen = 5)
/obj/item/clothing/suit/jacket/letterman_nanotrasen = 5,
/obj/item/clothing/accessory/nt_pokadots = 5)
refill_canister = /obj/item/vending_refill/clothing
/obj/item/vending_refill/clothing
+3 -3
View File
@@ -317,10 +317,10 @@
/obj/item/clothing/under/rank/chaplain = 2,
/obj/item/clothing/under/rank/chaplain/skirt = 2,
/obj/item/clothing/shoes/sneakers/black = 2,
/obj/item/clothing/suit/nun = 2,
/obj/item/clothing/suit/chaplain/nun = 2,
/obj/item/clothing/head/nun_hood = 2,
/obj/item/clothing/suit/holidaypriest = 2,
/obj/item/clothing/suit/pharaoh = 2,
/obj/item/clothing/suit/chaplain/holidaypriest = 2,
/obj/item/clothing/suit/chaplain/pharaoh = 2,
/obj/item/clothing/head/nemes = 1,
/obj/item/clothing/head/pharaoh = 1,
/obj/item/storage/fancy/candle_box = 3)