Merge remote-tracking branch 'origin/master' into semi-sync
This commit is contained in:
@@ -64,6 +64,8 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
|
||||
var/threat_average_weight = 0
|
||||
/// Last time a threat average sample was taken. Used for weighting the rolling average.
|
||||
var/last_threat_sample_time = 0
|
||||
/// Maximum threat recorded so far, for cross-round chaos adjustment.
|
||||
var/max_threat = 0
|
||||
/// Things that cause a rolling threat adjustment to be displayed at roundend.
|
||||
var/list/threat_tallies = list()
|
||||
/// Running information about the threat. Can store text or datum entries.
|
||||
@@ -145,6 +147,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
|
||||
third_rule_req = list(101, 101, 101, 101, 101, 100, 90, 80, 70, 60)
|
||||
high_pop_second_rule_req = CONFIG_GET(number/dynamic_second_rule_high_pop_requirement)
|
||||
high_pop_third_rule_req = CONFIG_GET(number/dynamic_third_rule_high_pop_requirement)
|
||||
added_threat = CONFIG_GET(number/dynamic_threat_baseline)
|
||||
GLOB.dynamic_high_pop_limit = CONFIG_GET(number/dynamic_high_pop_limit)
|
||||
GLOB.dynamic_latejoin_delay_min = CONFIG_GET(number/dynamic_latejoin_delay_min)*600
|
||||
GLOB.dynamic_latejoin_delay_max = CONFIG_GET(number/dynamic_latejoin_delay_max)*600
|
||||
@@ -415,7 +418,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
|
||||
/datum/game_mode/dynamic/post_setup(report)
|
||||
update_playercounts()
|
||||
if(minor_ruleset_start)
|
||||
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/minor_roundstart),rand(1 MINUTES,5 MINUTES))
|
||||
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/minor_roundstart),rand(1 MINUTES,3 MINUTES))
|
||||
else
|
||||
for(var/datum/dynamic_ruleset/roundstart/rule in executed_rules)
|
||||
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_roundstart_rule, rule), rule.delay)
|
||||
@@ -439,7 +442,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
|
||||
potential_minor_rulesets -= rule
|
||||
update_playercounts()
|
||||
iterations++
|
||||
message_admins("Minor antag roundstart rolls completed, with [num_rulesets_executed] antags or antag teams made.")
|
||||
message_admins("Minor antag roundstart rolls completed, with [iterations] rolls done and [num_rulesets_executed] antags or antag teams made.")
|
||||
log_game("DYNAMIC: Minor antag roundstart made [num_rulesets_executed] antags or antag teams.")
|
||||
|
||||
|
||||
@@ -612,6 +615,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
|
||||
latejoin_rules = remove_from_list(latejoin_rules, rule.type)
|
||||
else if(rule.ruletype == "Midround")
|
||||
midround_rules = remove_from_list(midround_rules, rule.type)
|
||||
message_admins("DYNAMIC: Picked [rule]; executing soon...")
|
||||
addtimer(CALLBACK(src, /datum/game_mode/dynamic/.proc/execute_midround_latejoin_rule, rule), rule.delay)
|
||||
return TRUE
|
||||
|
||||
@@ -663,6 +667,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
|
||||
/datum/game_mode/dynamic/proc/execute_midround_latejoin_rule(sent_rule)
|
||||
var/datum/dynamic_ruleset/rule = sent_rule
|
||||
if (rule.execute())
|
||||
message_admins("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
|
||||
log_game("DYNAMIC: Injected a [rule.ruletype == "latejoin" ? "latejoin" : "midround"] ruleset [rule.name].")
|
||||
log_threat("[rule.ruletype] [rule.name] added [rule.cost]", verbose = TRUE)
|
||||
if(rule.flags & HIGHLANDER_RULESET)
|
||||
@@ -741,6 +746,7 @@ GLOBAL_VAR_INIT(dynamic_forced_storyteller, null)
|
||||
if(!M.voluntary_ghosted)
|
||||
current_players[CURRENT_DEAD_PLAYERS].Add(M) // Players who actually died (and admins who ghosted, would be nice to avoid counting them somehow)
|
||||
threat = storyteller.calculate_threat() + added_threat
|
||||
max_threat = max(max_threat,threat)
|
||||
if(threat_average_weight)
|
||||
var/cur_sample_weight = world.time - last_threat_sample_time
|
||||
threat_average = ((threat_average * threat_average_weight) + (threat * cur_sample_weight)) / (threat_average_weight + cur_sample_weight)
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
if (!M.client) // Are they connected?
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if(M.client.prefs && M.client.prefs.toggles & MIDROUND_ANTAG)
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if(!mode.check_age(M.client, minimum_required_age))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
|
||||
@@ -1,3 +1,46 @@
|
||||
/datum/dynamic_ruleset/minor/proc/trim_list(list/L = list())
|
||||
var/list/trimmed_list = L.Copy()
|
||||
for(var/mob/M in trimmed_list)
|
||||
if (!ishuman(M))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if (HAS_TRAIT(M, TRAIT_NO_MIDROUND_ANTAG))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if (!M.client) // Are they connected?
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if(!mode.check_age(M.client, minimum_required_age))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if(antag_flag_override)
|
||||
if(!(antag_flag_override in M.client.prefs.be_special) || jobban_isbanned(M.ckey, antag_flag_override))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
else
|
||||
if(!(antag_flag in M.client.prefs.be_special) || jobban_isbanned(M.ckey, antag_flag))
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if (M.mind)
|
||||
if ((M.mind.assigned_role in GLOB.exp_specialmap[EXP_TYPE_SPECIAL])) // Are they playing a ghost role?
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if (M.mind.assigned_role in restricted_roles) // Does their job allow it?
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
if ((exclusive_roles.len > 0) && !(M.mind.assigned_role in exclusive_roles)) // Is the rule exclusive to their job?
|
||||
trimmed_list.Remove(M)
|
||||
continue
|
||||
return trimmed_list
|
||||
|
||||
/datum/dynamic_ruleset/minor/trim_candidates()
|
||||
//
|
||||
// All you need to know is that here, the candidates list contains 4 lists itself, indexed with the following defines:
|
||||
// Candidates = list(CURRENT_LIVING_PLAYERS, CURRENT_LIVING_ANTAGS, CURRENT_DEAD_PLAYERS, CURRENT_OBSERVERS)
|
||||
// So for example you can get the list of all current dead players with var/list/dead_players = candidates[CURRENT_DEAD_PLAYERS]
|
||||
// Make sure to properly typecheck the mobs in those lists, as the dead_players list could contain ghosts, or dead players still in their bodies.
|
||||
// We're still gonna trim the obvious (mobs without clients, jobbanned players, etc)
|
||||
candidates = trim_list(mode.current_players[CURRENT_LIVING_PLAYERS])
|
||||
|
||||
//////////////////////////////////////////////
|
||||
// //
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
/datum/dynamic_ruleset/roundstart/traitor
|
||||
name = "Traitors"
|
||||
config_tag = "traitor"
|
||||
persistent = TRUE
|
||||
antag_flag = ROLE_TRAITOR
|
||||
antag_datum = /datum/antagonist/traitor/
|
||||
minimum_required_age = 0
|
||||
@@ -34,15 +33,6 @@
|
||||
M.mind.restricted_roles = restricted_roles
|
||||
return TRUE
|
||||
|
||||
/datum/dynamic_ruleset/roundstart/traitor/rule_process()
|
||||
if (autotraitor_cooldown > 0)
|
||||
autotraitor_cooldown--
|
||||
else
|
||||
autotraitor_cooldown = 450 // 15 minutes
|
||||
message_admins("Checking if we can turn someone into a traitor.")
|
||||
log_game("DYNAMIC: Checking if we can turn someone into a traitor.")
|
||||
mode.picking_specific_rule(/datum/dynamic_ruleset/midround/autotraitor)
|
||||
|
||||
//////////////////////////////////////////
|
||||
// //
|
||||
// BLOOD BROTHERS //
|
||||
|
||||
@@ -15,10 +15,13 @@
|
||||
*/
|
||||
var/flags = 0
|
||||
var/dead_player_weight = 1 // How much dead players matter for threat calculation
|
||||
var/weight = 3 // Weights for randomly picking storyteller. Multiplied by score after voting.
|
||||
var/weight = 0 // Weights for randomly picking storyteller. Multiplied by score after voting.
|
||||
var/min_chaos = -1000 // Won't show up if recent rounds have been below this chaotic on average
|
||||
var/max_chaos = 1000 // Won't show up if recent rounds have been above this chaotic on average
|
||||
var/event_frequency_lower = 6 MINUTES // How rare events will be, at least.
|
||||
var/event_frequency_upper = 20 MINUTES // How rare events will be, at most.
|
||||
var/min_players = -1 // How many players are required for this one to start.
|
||||
var/soft_antag_ratio_cap = 4 // how many players-per-antag there should be
|
||||
var/datum/game_mode/dynamic/mode = null // Cached as soon as it's made, by dynamic.
|
||||
|
||||
/**
|
||||
@@ -101,8 +104,10 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
|
||||
/datum/dynamic_storyteller/proc/should_inject_antag(dry_run = FALSE)
|
||||
if(mode.forced_injection)
|
||||
mode.forced_injection = !dry_run
|
||||
mode.forced_injection = dry_run
|
||||
return TRUE
|
||||
if(mode.current_players[CURRENT_LIVING_PLAYERS].len < (mode.current_players[CURRENT_LIVING_ANTAGS].len * soft_antag_ratio_cap))
|
||||
return FALSE
|
||||
return mode.threat < mode.threat_level
|
||||
|
||||
/datum/dynamic_storyteller/proc/roundstart_draft()
|
||||
@@ -126,11 +131,10 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
|
||||
/datum/dynamic_storyteller/proc/minor_rule_draft()
|
||||
var/list/drafted_rules = list()
|
||||
for (var/datum/dynamic_ruleset/minor/rule in mode.minor_rules)
|
||||
for (var/datum/dynamic_ruleset/rule in mode.minor_rules)
|
||||
if (rule.acceptable(mode.current_players[CURRENT_LIVING_PLAYERS].len, mode.threat_level))
|
||||
rule.candidates = mode.candidates.Copy()
|
||||
rule.trim_candidates()
|
||||
if (rule.ready() && rule.candidates.len > 0)
|
||||
if (rule.ready())
|
||||
var/property_weight = 0
|
||||
for(var/property in property_weights)
|
||||
if(property in rule.property_weights) // just treat it as 0 if it's not in there
|
||||
@@ -213,6 +217,8 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
weight = 1
|
||||
event_frequency_lower = 2 MINUTES
|
||||
event_frequency_upper = 10 MINUTES
|
||||
max_chaos = 50
|
||||
soft_antag_ratio_cap = 1
|
||||
flags = WAROPS_ALWAYS_ALLOWED | FORCE_IF_WON
|
||||
min_players = 30
|
||||
var/refund_cooldown = 0
|
||||
@@ -235,12 +241,13 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
desc = "Modes where the crew must band together. Nukies, xenos, blob. Only one antag threat at once."
|
||||
curve_centre = 2
|
||||
curve_width = 1.5
|
||||
weight = 2
|
||||
weight = 4
|
||||
max_chaos = 75
|
||||
min_players = 20
|
||||
flags = WAROPS_ALWAYS_ALLOWED | USE_PREV_ROUND_WEIGHTS
|
||||
property_weights = list("valid" = 3, "trust" = 5)
|
||||
|
||||
/datum/dynamic_storyteller/chaotic/minor_start_chance()
|
||||
/datum/dynamic_storyteller/team/minor_start_chance()
|
||||
return 0
|
||||
|
||||
/datum/dynamic_storyteller/team/should_inject_antag(dry_run = FALSE)
|
||||
@@ -256,13 +263,15 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
flags = WAROPS_ALWAYS_ALLOWED
|
||||
property_weights = list("valid" = 1, "conversion" = 20)
|
||||
|
||||
/datum/dynamic_storyteller/chaotic/minor_start_chance()
|
||||
/datum/dynamic_storyteller/conversion/minor_start_chance()
|
||||
return 0
|
||||
|
||||
/datum/dynamic_storyteller/random
|
||||
name = "Random"
|
||||
config_tag = "random"
|
||||
weight = 1
|
||||
max_chaos = 60
|
||||
soft_antag_ratio_cap = 1
|
||||
desc = "No weighting at all; every ruleset has the same chance of happening. Cooldowns vary wildly. As random as it gets."
|
||||
|
||||
/datum/dynamic_storyteller/random/on_start()
|
||||
@@ -275,7 +284,7 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
/datum/dynamic_storyteller/random/should_inject_antag()
|
||||
return prob(50)
|
||||
|
||||
/datum/dynamic_storyteller/chaotic/minor_start_chance()
|
||||
/datum/dynamic_storyteller/random/minor_start_chance()
|
||||
return 20
|
||||
|
||||
/datum/dynamic_storyteller/random/roundstart_draft()
|
||||
@@ -331,7 +340,7 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
name = "Story"
|
||||
config_tag = "story"
|
||||
desc = "Antags with options for loadouts and gimmicks. Traitor, wizard, nukies."
|
||||
weight = 2
|
||||
weight = 4
|
||||
curve_width = 2
|
||||
flags = USE_PREV_ROUND_WEIGHTS
|
||||
property_weights = list("story_potential" = 2)
|
||||
@@ -339,6 +348,7 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
/datum/dynamic_storyteller/classic
|
||||
name = "Classic"
|
||||
config_tag = "classic"
|
||||
weight = 8
|
||||
desc = "No special antagonist weights. Good variety, but not like random. Uses your chaos preference to weight."
|
||||
flags = USE_PREF_WEIGHTS | USE_PREV_ROUND_WEIGHTS
|
||||
|
||||
@@ -346,7 +356,7 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
name = "Intrigue"
|
||||
config_tag = "intrigue"
|
||||
desc = "Antags that instill distrust in the crew. Traitors, bloodsuckers."
|
||||
weight = 2
|
||||
weight = 4
|
||||
curve_width = 2
|
||||
dead_player_weight = 2
|
||||
flags = USE_PREV_ROUND_WEIGHTS
|
||||
@@ -358,8 +368,8 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
/datum/dynamic_storyteller/grabbag
|
||||
name = "Grab Bag"
|
||||
config_tag = "grabbag"
|
||||
desc = "Crew antags (e.g. traitor, changeling, bloodsucker, heretic) only, all mixed together."
|
||||
weight = 2
|
||||
desc = "Crew antags (e.g. traitor, changeling, bloodsucker, heretic) only at round start, all mixed together."
|
||||
weight = 4
|
||||
flags = USE_PREF_WEIGHTS | USE_PREV_ROUND_WEIGHTS
|
||||
|
||||
/datum/dynamic_storyteller/grabbag/minor_start_chance()
|
||||
@@ -372,12 +382,14 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
curve_centre = -3
|
||||
curve_width = 0.5
|
||||
flags = NO_ASSASSIN
|
||||
weight = 1
|
||||
min_chaos = 30
|
||||
weight = 3
|
||||
dead_player_weight = 5
|
||||
soft_antag_ratio_cap = 8
|
||||
property_weights = list("extended" = 2, "chaos" = -1, "valid" = -1, "conversion" = -10)
|
||||
|
||||
/datum/dynamic_storyteller/liteextended/minor_start_chance()
|
||||
return 100
|
||||
return 90
|
||||
|
||||
/datum/dynamic_storyteller/no_antag
|
||||
name = "Extended"
|
||||
@@ -385,6 +397,7 @@ Property weights are added to the config weight of the ruleset. They are:
|
||||
desc = "No standard antags."
|
||||
curve_centre = -5
|
||||
curve_width = 0.5
|
||||
min_chaos = 40
|
||||
flags = NO_ASSASSIN | FORCE_IF_WON
|
||||
weight = 1
|
||||
property_weights = list("extended" = 2)
|
||||
|
||||
@@ -46,17 +46,17 @@ GLOBAL_LIST_INIT(meteorsC, list(/obj/effect/meteor/dust)) //for space dust event
|
||||
var/startx
|
||||
switch(startSide)
|
||||
if(NORTH)
|
||||
starty = world.maxy-(TRANSITIONEDGE+1)
|
||||
startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
|
||||
starty = world.maxy-(TRANSITIONEDGE+2)
|
||||
startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2))
|
||||
if(EAST)
|
||||
starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
|
||||
startx = world.maxx-(TRANSITIONEDGE+1)
|
||||
starty = rand((TRANSITIONEDGE+2),world.maxy-(TRANSITIONEDGE+2))
|
||||
startx = world.maxx-(TRANSITIONEDGE+2)
|
||||
if(SOUTH)
|
||||
starty = (TRANSITIONEDGE+1)
|
||||
startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
|
||||
starty = (TRANSITIONEDGE+2)
|
||||
startx = rand((TRANSITIONEDGE+2), world.maxx-(TRANSITIONEDGE+2))
|
||||
if(WEST)
|
||||
starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
|
||||
startx = (TRANSITIONEDGE+1)
|
||||
starty = rand((TRANSITIONEDGE+2), world.maxy-(TRANSITIONEDGE+2))
|
||||
startx = (TRANSITIONEDGE+2)
|
||||
. = locate(startx, starty, Z)
|
||||
|
||||
/proc/spaceDebrisFinishLoc(startSide, Z)
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
/obj/machinery/gear_painter
|
||||
name = "\improper Color Mate"
|
||||
desc = "A machine to give your apparel a fresh new color! Recommended to use with white items for best results."
|
||||
icon = 'icons/obj/vending.dmi'
|
||||
icon_state = "colormate"
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
circuit = /obj/item/circuitboard/machine/colormate
|
||||
var/obj/item/inserted
|
||||
var/activecolor = "#FFFFFF"
|
||||
var/list/color_matrix_last
|
||||
var/matrix_mode = FALSE
|
||||
/// Minimum lightness for normal mode
|
||||
var/minimum_normal_lightness = 50
|
||||
/// Minimum lightness for matrix mode, tested using 4 test colors of full red, green, blue, white.
|
||||
var/minimum_matrix_lightness = 75
|
||||
/// Minimum matrix tests that must pass for something to be considered a valid color (see above)
|
||||
var/minimum_matrix_tests = 2
|
||||
var/list/allowed_types = list(
|
||||
/obj/item/clothing,
|
||||
/obj/item/storage/backpack,
|
||||
/obj/item/storage/belt
|
||||
)
|
||||
|
||||
/obj/machinery/gear_painter/Initialize(mapload)
|
||||
. = ..()
|
||||
color_matrix_last = list(
|
||||
1, 0, 0,
|
||||
0, 1, 0,
|
||||
0, 0, 1,
|
||||
0, 0, 0
|
||||
)
|
||||
|
||||
/obj/machinery/gear_painter/update_icon_state()
|
||||
if(panel_open)
|
||||
icon_state = "colormate_open"
|
||||
else if(!is_operational())
|
||||
icon_state = "colormate_off"
|
||||
else if(inserted)
|
||||
icon_state = "colormate_active"
|
||||
else
|
||||
icon_state = "colormate"
|
||||
|
||||
/obj/machinery/gear_painter/Destroy()
|
||||
inserted.forceMove(drop_location())
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gear_painter/attackby(obj/item/I, mob/living/user)
|
||||
if(inserted)
|
||||
to_chat(user, "<span class='warning'>The machine is already loaded.</span>")
|
||||
return
|
||||
if(default_deconstruction_screwdriver(user, "colormate_open", "colormate", I))
|
||||
return
|
||||
if(default_deconstruction_crowbar(I))
|
||||
return
|
||||
if(default_unfasten_wrench(user, I, 40))
|
||||
return
|
||||
if(user.a_intent == INTENT_HARM)
|
||||
return ..()
|
||||
|
||||
if(is_type_in_list(I, allowed_types) && is_operational())
|
||||
if(!user.transferItemToLoc(I, src))
|
||||
to_chat(user, "<span class='warning'>[I] is stuck to your hand!</span>")
|
||||
return
|
||||
user.visible_message("<span class='notice'>[user] inserts [I] into [src]'s receptable.</span>")
|
||||
|
||||
inserted = I
|
||||
update_icon()
|
||||
else
|
||||
return ..()
|
||||
|
||||
/obj/machinery/gear_painter/AllowDrop()
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/gear_painter/AltClick(mob/user)
|
||||
. = ..()
|
||||
if(!user.CanReach(src))
|
||||
return
|
||||
if(!inserted)
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You remove [inserted] from [src]")
|
||||
inserted.forceMove(drop_location())
|
||||
inserted = null
|
||||
update_icon()
|
||||
updateUsrDialog()
|
||||
|
||||
/obj/machinery/gear_painter/ui_interact(mob/user)
|
||||
if(!is_operational())
|
||||
return
|
||||
user.set_machine(src)
|
||||
var/list/dat = list("<TITLE>Color Mate Control Panel</TITLE><BR>")
|
||||
if(!inserted)
|
||||
dat += "No item inserted."
|
||||
else
|
||||
dat += "Item inserted: [inserted]<HR>"
|
||||
dat += "<a href='?src=[REF(src)];toggle_matrix_mode=1'>Matrix mode: [matrix_mode? "On" : "Off"]</a>"
|
||||
if(!matrix_mode)
|
||||
dat += "<A href='?src=\ref[src];select=1'>Select new color.</A><BR>"
|
||||
dat += "Color: <font color='[activecolor]'>⚫</font>"
|
||||
dat += "<A href='?src=\ref[src];paint=1'>Apply new color.</A><BR><BR>"
|
||||
else
|
||||
// POGGERS
|
||||
#define MATRIX_FIELD(field, default) "<b><label for='[##field]'>[##field]</label></b> <input type='number' step='0.001' name='[field]' value='[default]'>"
|
||||
dat += "<br><form name='matrix paint' action='?src=[REF(src)]'>"
|
||||
dat += "<input type='hidden' name='src' value='[REF(src)]'>"
|
||||
dat += "<input type='hidden' name='matrix_paint' value='1'"
|
||||
dat += "<br><br>"
|
||||
dat += MATRIX_FIELD("rr", color_matrix_last[1])
|
||||
dat += MATRIX_FIELD("rg", color_matrix_last[2])
|
||||
dat += MATRIX_FIELD("rb", color_matrix_last[3])
|
||||
dat += "<br><br>"
|
||||
dat += MATRIX_FIELD("gr", color_matrix_last[4])
|
||||
dat += MATRIX_FIELD("gg", color_matrix_last[5])
|
||||
dat += MATRIX_FIELD("gb", color_matrix_last[6])
|
||||
dat += "<br><br>"
|
||||
dat += MATRIX_FIELD("br", color_matrix_last[7])
|
||||
dat += MATRIX_FIELD("bg", color_matrix_last[8])
|
||||
dat += MATRIX_FIELD("bb", color_matrix_last[9])
|
||||
dat += "<br><br>"
|
||||
dat += MATRIX_FIELD("cr", color_matrix_last[10])
|
||||
dat += MATRIX_FIELD("cg", color_matrix_last[11])
|
||||
dat += MATRIX_FIELD("cb", color_matrix_last[12])
|
||||
dat += "<br><br>"
|
||||
dat += "<input type='submit' value='Matrix Paint'>"
|
||||
dat += "</form><br>"
|
||||
#undef MATRIX_FIELD
|
||||
dat += "<A href='?src=\ref[src];clear=1'>Remove paintjob.</A><BR><BR>"
|
||||
dat += "<A href='?src=\ref[src];eject=1'>Eject item.</A><BR><BR>"
|
||||
|
||||
var/datum/browser/menu = new(user, "colormate","Color Mate Control Panel", 800, 600, src)
|
||||
menu.set_content(dat.Join(""))
|
||||
menu.open()
|
||||
|
||||
/obj/machinery/gear_painter/Topic(href, href_list)
|
||||
if((. = ..()))
|
||||
return
|
||||
|
||||
add_fingerprint(usr)
|
||||
|
||||
if(href_list["close"])
|
||||
usr << browse(null, "window=colormate")
|
||||
return
|
||||
|
||||
if(href_list["select"])
|
||||
var/newcolor = input(usr, "Choose a color.", "", activecolor) as color|null
|
||||
if(newcolor)
|
||||
activecolor = newcolor
|
||||
updateUsrDialog()
|
||||
|
||||
if(href_list["paint"])
|
||||
if(!inserted)
|
||||
return
|
||||
if(!check_valid_color(activecolor, usr))
|
||||
return
|
||||
inserted.add_atom_colour(activecolor, FIXED_COLOUR_PRIORITY)
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
updateUsrDialog()
|
||||
|
||||
if(href_list["toggle_matrix_mode"])
|
||||
matrix_mode = !matrix_mode
|
||||
updateUsrDialog()
|
||||
|
||||
if(href_list["matrix_paint"])
|
||||
if(!inserted)
|
||||
return
|
||||
// assemble matrix
|
||||
var/list/cm = rgb_construct_color_matrix(
|
||||
text2num(href_list["rr"]),
|
||||
text2num(href_list["rg"]),
|
||||
text2num(href_list["rb"]),
|
||||
text2num(href_list["gr"]),
|
||||
text2num(href_list["gg"]),
|
||||
text2num(href_list["gb"]),
|
||||
text2num(href_list["br"]),
|
||||
text2num(href_list["bg"]),
|
||||
text2num(href_list["bb"]),
|
||||
text2num(href_list["cr"]),
|
||||
text2num(href_list["cg"]),
|
||||
text2num(href_list["cb"])
|
||||
)
|
||||
color_matrix_last = cm.Copy()
|
||||
if(!check_valid_color(cm, usr))
|
||||
return
|
||||
inserted.add_atom_colour(cm, FIXED_COLOUR_PRIORITY)
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
updateUsrDialog()
|
||||
|
||||
if(href_list["clear"])
|
||||
if(!inserted)
|
||||
return
|
||||
inserted.remove_atom_colour(FIXED_COLOUR_PRIORITY)
|
||||
playsound(src, 'sound/effects/spray3.ogg', 50, 1)
|
||||
updateUsrDialog()
|
||||
|
||||
if(href_list["eject"])
|
||||
if(!inserted)
|
||||
return
|
||||
inserted.forceMove(drop_location())
|
||||
inserted = null
|
||||
update_icon()
|
||||
updateUsrDialog()
|
||||
|
||||
/obj/machinery/gear_painter/proc/check_valid_color(list/cm, mob/user)
|
||||
if(!islist(cm)) // normal
|
||||
var/list/HSV = ReadHSV(RGBtoHSV(cm))
|
||||
if(HSV[3] < minimum_normal_lightness)
|
||||
to_chat(user, "<span class='warning'>[cm] is far too dark (min lightness [minimum_normal_lightness]!</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
else // matrix
|
||||
// We test using full red, green, blue, and white
|
||||
// A predefined number of them must pass to be considered valid
|
||||
var/passed = 0
|
||||
#define COLORTEST(thestring, thematrix) passed += (ReadHSV(RGBtoHSV(RGBMatrixTransform(thestring, thematrix)))[3] >= minimum_matrix_lightness)
|
||||
COLORTEST("FF0000", cm)
|
||||
COLORTEST("00FF00", cm)
|
||||
COLORTEST("0000FF", cm)
|
||||
COLORTEST("FFFFFF", cm)
|
||||
#undef COLORTEST
|
||||
if(passed < minimum_matrix_tests)
|
||||
to_chat(user, "<span class='warning'>[english_list(color)] is not allowed (pased [passed] out of 4, minimum [minimum_matrix_tests], minimum lightness [minimum_matrix_lightness]).</span>")
|
||||
return FALSE
|
||||
return TRUE
|
||||
@@ -24,19 +24,24 @@
|
||||
var/selected_category
|
||||
var/screen = 1
|
||||
var/list/categories = list(
|
||||
"human",
|
||||
"lizard",
|
||||
"fly",
|
||||
"insect",
|
||||
"plasmaman",
|
||||
"mammal",
|
||||
"xeno",
|
||||
"other"
|
||||
"human" = /datum/species/human,
|
||||
"lizard" = /datum/species/lizard,
|
||||
"mammal" = /datum/species/mammal,
|
||||
"insect" = /datum/species/insect,
|
||||
"fly" = /datum/species/fly,
|
||||
"plasmaman" = /datum/species/plasmaman,
|
||||
"xeno" = /datum/species/xeno,
|
||||
"other" = /datum/species,
|
||||
)
|
||||
var/list/stored_species = list()
|
||||
var/obj/item/disk/data/dna_disk
|
||||
|
||||
/obj/machinery/limbgrower/Initialize()
|
||||
create_reagents(100, OPENCONTAINER)
|
||||
stored_research = new /datum/techweb/specialized/autounlocking/limbgrower
|
||||
for(var/i in categories)
|
||||
var/species = categories[i]
|
||||
stored_species[i] = new species()
|
||||
. = ..()
|
||||
|
||||
/obj/machinery/limbgrower/ui_interact(mob/user)
|
||||
@@ -64,8 +69,8 @@
|
||||
..()
|
||||
|
||||
/obj/machinery/limbgrower/attackby(obj/item/O, mob/user, params)
|
||||
if (busy)
|
||||
to_chat(user, "<span class=\"alert\">The Limb Grower is busy. Please wait for completion of previous operation.</span>")
|
||||
if(busy)
|
||||
to_chat(user, "<span class=\"alert\">\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
return
|
||||
|
||||
if(default_deconstruction_screwdriver(user, "limbgrower_panelopen", "limbgrower_idleoff", O))
|
||||
@@ -78,6 +83,16 @@
|
||||
if(user.a_intent == INTENT_HARM) //so we can hit the machine
|
||||
return ..()
|
||||
|
||||
if(istype(O, /obj/item/disk))
|
||||
if(dna_disk)
|
||||
to_chat(user, "<span class='warning'>\The [src] already has a dna disk, take it out first!</span>")
|
||||
return
|
||||
else
|
||||
O.forceMove(src)
|
||||
dna_disk = O
|
||||
to_chat(user, "<span class='notice'>You insert \the [O] into \the [src].</span>")
|
||||
return
|
||||
|
||||
/obj/machinery/limbgrower/Topic(href, href_list)
|
||||
if(..())
|
||||
return
|
||||
@@ -110,8 +125,24 @@
|
||||
icon_state = "limbgrower_idleon"
|
||||
addtimer(CALLBACK(src, .proc/build_item),32*prod_coeff)
|
||||
|
||||
if(href_list["dna_disk"])
|
||||
var/mob/living/carbon/user = usr
|
||||
if(istype(user))
|
||||
if(!dna_disk)
|
||||
var/obj/item/disk/diskette = user.get_active_held_item()
|
||||
if(istype(diskette))
|
||||
diskette.forceMove(src)
|
||||
dna_disk = diskette
|
||||
to_chat(user, "<span class='notice'>You insert \the [diskette] into \the [src].</span>")
|
||||
else
|
||||
dna_disk.forceMove(src.loc)
|
||||
user.put_in_active_hand(dna_disk)
|
||||
to_chat(user, "<span class='notice'>You remove \the [dna_disk] from \the [src].</span>")
|
||||
dna_disk = null
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You are unable to grasp \the [dna_disk] disk from \the [src].</span>")
|
||||
else
|
||||
to_chat(usr, "<span class=\"alert\">The limb grower is busy. Please wait for completion of previous operation.</span>")
|
||||
to_chat(usr, "<span class=\"alert\">\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
|
||||
updateUsrDialog()
|
||||
return
|
||||
@@ -120,8 +151,10 @@
|
||||
if(reagents.has_reagent(/datum/reagent/medicine/synthflesh, being_built.reagents_list[/datum/reagent/medicine/synthflesh]*prod_coeff)) //sanity check, if this happens we are in big trouble
|
||||
reagents.remove_reagent(/datum/reagent/medicine/synthflesh, being_built.reagents_list[/datum/reagent/medicine/synthflesh]*prod_coeff)
|
||||
var/buildpath = being_built.build_path
|
||||
if(ispath(buildpath, /obj/item/bodypart)) //This feels like spatgheti code, but i need to initilise a limb somehow
|
||||
if(ispath(buildpath, /obj/item/bodypart)) //This feels like spaghetti code, but i need to initiliaze a limb somehow
|
||||
build_limb(buildpath)
|
||||
else if(ispath(buildpath, /obj/item/organ/genital)) //genitals are uhh... customizable
|
||||
build_genital(buildpath)
|
||||
else
|
||||
//Just build whatever it is
|
||||
new buildpath(loc)
|
||||
@@ -135,19 +168,65 @@
|
||||
/obj/machinery/limbgrower/proc/build_limb(buildpath)
|
||||
//i need to create a body part manually using a set icon (otherwise it doesnt appear)
|
||||
var/obj/item/bodypart/limb
|
||||
var/datum/species/selected = stored_species[selected_category]
|
||||
limb = new buildpath(loc)
|
||||
if(selected_category=="human" || selected_category=="lizard") //Species with greyscale parts should be included here
|
||||
limb.icon = 'icons/mob/human_parts_greyscale.dmi'
|
||||
limb.base_bp_icon = DEFAULT_BODYPART_ICON_ORGANIC
|
||||
limb.color_src = MUTCOLORS
|
||||
else
|
||||
limb.icon = 'icons/mob/human_parts.dmi'
|
||||
// Set this limb up using the specias name and body zone
|
||||
limb.icon_state = "[selected_category]_[limb.body_zone]"
|
||||
limb.name = "\improper synthetic [selected_category] [parse_zone(limb.body_zone)]"
|
||||
limb.desc = "A synthetic [selected_category] limb that will morph on its first use in surgery. This one is for the [parse_zone(limb.body_zone)]."
|
||||
limb.species_id = selected_category
|
||||
limb.base_bp_icon = selected.icon_limbs || DEFAULT_BODYPART_ICON_ORGANIC
|
||||
limb.species_id = selected.limbs_id
|
||||
limb.color_src = (MUTCOLORS in selected.species_traits ? MUTCOLORS : (selected.use_skintones ? SKINTONE : FALSE))
|
||||
limb.should_draw_gender = (selected.sexes && (limb.body_zone in list(BODY_ZONE_HEAD, BODY_ZONE_CHEST)))
|
||||
limb.update_limb(TRUE)
|
||||
limb.update_icon_dropped()
|
||||
limb.name = "\improper synthetic [lowertext(selected.name)] [limb.name]"
|
||||
limb.desc = "A synthetic [selected_category] limb that will morph on its first use in surgery. This one is for the [parse_zone(limb.body_zone)]."
|
||||
for(var/obj/item/bodypart/BP in limb)
|
||||
BP.base_bp_icon = selected.icon_limbs || DEFAULT_BODYPART_ICON_ORGANIC
|
||||
BP.species_id = selected.limbs_id
|
||||
BP.color_src = (MUTCOLORS in selected.species_traits ? MUTCOLORS : (selected.use_skintones ? SKINTONE : FALSE))
|
||||
BP.should_draw_gender = (selected.sexes && (limb.body_zone in list(BODY_ZONE_HEAD, BODY_ZONE_CHEST)))
|
||||
BP.update_limb(TRUE)
|
||||
BP.update_icon_dropped()
|
||||
BP.name = "\improper synthetic [lowertext(selected.name)] [limb.name]"
|
||||
BP.desc = "A synthetic [selected_category] limb that will morph on its first use in surgery. This one is for the [parse_zone(limb.body_zone)]."
|
||||
|
||||
/obj/machinery/limbgrower/proc/build_genital(buildpath)
|
||||
//i needed to create a way to customize gene tools using dna
|
||||
var/list/features = dna_disk?.fields["features"]
|
||||
if(length(features))
|
||||
switch(buildpath)
|
||||
if(/obj/item/organ/genital/penis)
|
||||
var/obj/item/organ/genital/penis/penis = new(loc)
|
||||
if(features["has_cock"])
|
||||
penis.shape = features["cock_shape"]
|
||||
penis.length = features["cock_shape"]
|
||||
penis.diameter_ratio = features["cock_diameter_ratio"]
|
||||
penis.color = sanitize_hexcolor(features["cock_color"], 6)
|
||||
penis.update_icon()
|
||||
if(/obj/item/organ/genital/testicles)
|
||||
var/obj/item/organ/genital/testicles/balls = new(loc)
|
||||
if(features["has_balls"])
|
||||
balls.color = sanitize_hexcolor(features["balls_color"], 6)
|
||||
balls.shape = features["balls_shape"]
|
||||
balls.size = features["balls_size"]
|
||||
balls.fluid_rate = features["balls_cum_rate"]
|
||||
balls.fluid_mult = features["balls_cum_mult"]
|
||||
balls.fluid_efficiency = features["balls_efficiency"]
|
||||
if(/obj/item/organ/genital/vagina)
|
||||
var/obj/item/organ/genital/vagina/vegana = new(loc)
|
||||
if(features["has_vagina"])
|
||||
vegana.color = sanitize_hexcolor(features["vag_color"], 6)
|
||||
vegana.shape = features["vag_shape"]
|
||||
if(/obj/item/organ/genital/breasts)
|
||||
var/obj/item/organ/genital/breasts/boobs = new(loc)
|
||||
if(features["has_breasts"])
|
||||
boobs.color = sanitize_hexcolor(features["breasts_color"], 6)
|
||||
boobs.size = features["breasts_size"]
|
||||
boobs.shape = features["breasts_shape"]
|
||||
if(!features["breasts_producing"])
|
||||
boobs.genital_flags &= ~(GENITAL_FUID_PRODUCTION|CAN_CLIMAX_WITH|CAN_MASTURBATE_WITH)
|
||||
else
|
||||
new buildpath(loc)
|
||||
else
|
||||
new buildpath(loc)
|
||||
|
||||
/obj/machinery/limbgrower/RefreshParts()
|
||||
reagents.maximum_volume = 0
|
||||
@@ -165,7 +244,9 @@
|
||||
. += "<span class='notice'>The status display reads: Storing up to <b>[reagents.maximum_volume]u</b> of synthflesh.<br>Synthflesh consumption at <b>[prod_coeff*100]%</b>.<span>"
|
||||
|
||||
/obj/machinery/limbgrower/proc/main_win(mob/user)
|
||||
var/dat = "<div class='statusDisplay'><h3>Limb Grower Menu:</h3><br>"
|
||||
var/dat = "<div class='statusDisplay'><h3>[src] Menu:</h3><br>"
|
||||
dat += "<A href='?src=[REF(src)];dna_disk=1'>[dna_disk ? "Remove" : "Insert"] cloning data disk</A>"
|
||||
dat += "<hr>"
|
||||
dat += "<A href='?src=[REF(src)];menu=[LIMBGROWER_CHEMICAL_MENU]'>Chemical Storage</A>"
|
||||
dat += materials_printout()
|
||||
dat += "<table style='width:100%' align='center'><tr>"
|
||||
@@ -218,7 +299,7 @@
|
||||
|
||||
/obj/machinery/limbgrower/proc/get_design_cost(datum/design/D)
|
||||
var/dat
|
||||
if(D.reagents_list["synthflesh"])
|
||||
if(D.reagents_list[/datum/reagent/medicine/synthflesh])
|
||||
dat += "[D.reagents_list[/datum/reagent/medicine/synthflesh] * prod_coeff] Synthetic flesh "
|
||||
return dat
|
||||
|
||||
@@ -233,3 +314,47 @@
|
||||
to_chat(user, "<span class='warning'>A warning flashes onto the screen, stating that safety overrides have been deactivated!</span>")
|
||||
obj_flags |= EMAGGED
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/limbgrower/AltClick(mob/living/user)
|
||||
. = ..()
|
||||
if(istype(user) && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
|
||||
if(busy)
|
||||
to_chat(user, "<span class=\"alert\">\The [src] is busy. Please wait for completion of previous operation.</span>")
|
||||
else
|
||||
if(dna_disk)
|
||||
dna_disk.forceMove(src.loc)
|
||||
user.put_in_active_hand(dna_disk)
|
||||
to_chat(user, "<span class='notice'>You remove \the [dna_disk] from \the [src].</span>")
|
||||
dna_disk = null
|
||||
else
|
||||
to_chat(user, "<span class='warning'>\The [src] has doesn't have a disk on it!")
|
||||
|
||||
//Defines some vars that makes limbs appears, TO-DO: define every single species.
|
||||
|
||||
/datum/species/human
|
||||
limbs_id = SPECIES_HUMAN
|
||||
icon_limbs = 'icons/mob/human_parts_greyscale.dmi'
|
||||
|
||||
/datum/species/lizard
|
||||
limbs_id = SPECIES_LIZARD
|
||||
icon_limbs = 'icons/mob/human_parts_greyscale.dmi'
|
||||
|
||||
/datum/species/mammal
|
||||
limbs_id = SPECIES_MAMMAL
|
||||
icon_limbs = 'icons/mob/human_parts_greyscale.dmi'
|
||||
|
||||
/datum/species/insect
|
||||
limbs_id = SPECIES_INSECT
|
||||
icon_limbs = 'icons/mob/human_parts_greyscale.dmi'
|
||||
|
||||
/datum/species/fly
|
||||
limbs_id = SPECIES_FLY
|
||||
icon_limbs = 'icons/mob/human_parts.dmi'
|
||||
|
||||
/datum/species/plasmaman
|
||||
limbs_id = SPECIES_PLASMAMAN
|
||||
icon_limbs = 'icons/mob/human_parts.dmi'
|
||||
|
||||
/datum/species/xeno
|
||||
limbs_id = SPECIES_XENOHYBRID
|
||||
icon_limbs = 'icons/mob/human_parts_greyscale.dmi'
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
return
|
||||
. = ..()
|
||||
|
||||
if(AM.movement_type & FLYING)
|
||||
if(AM.movement_type & FLYING || iseffect(AM))
|
||||
return
|
||||
|
||||
triggermine(AM)
|
||||
|
||||
@@ -243,6 +243,12 @@
|
||||
/obj/item/stack/sheet/glass = 1)
|
||||
def_components = list(/obj/item/stack/ore/bluespace_crystal = /obj/item/stack/ore/bluespace_crystal/artificial)
|
||||
|
||||
/obj/item/circuitboard/machine/colormate
|
||||
name = "Colormate (Machine Board)"
|
||||
build_path = /obj/machinery/gear_painter
|
||||
req_components = list()
|
||||
def_components = list()
|
||||
|
||||
/obj/item/circuitboard/machine/vendor
|
||||
name = "Custom Vendor (Machine Board)"
|
||||
desc = "You can turn the \"brand selection\" dial using a screwdriver."
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
else
|
||||
if(zero_amount())
|
||||
return
|
||||
chosen_circuit = input("What type of circuit would you like to remove?", "Choose a Circuit Type", chosen_circuit) in list("airlock","firelock","fire alarm","air alarm","APC")
|
||||
if(zero_amount())
|
||||
chosen_circuit = input("What type of circuit would you like to remove?", "Choose a Circuit Type", chosen_circuit) as null|anything in list("airlock","firelock","fire alarm","air alarm","APC")
|
||||
if(zero_amount() || !chosen_circuit || !in_range(src,user))
|
||||
return
|
||||
switch(chosen_circuit)
|
||||
if("airlock")
|
||||
|
||||
@@ -853,8 +853,8 @@
|
||||
/obj/item/storage/belt/sabre/twin
|
||||
name = "twin sheath"
|
||||
desc = "Two sheaths. One is capable of holding a katana (or bokken) and the other a wakizashi. You could put two wakizashis in if you really wanted to. Now you can really roleplay as a samurai."
|
||||
icon_state = "twinsheath"
|
||||
item_state = "quiver" //this'll do.
|
||||
icon_state = "2sheath"
|
||||
item_state = "katana" //this'll do.
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
fitting_swords = list(/obj/item/melee/smith/wakizashi, /obj/item/melee/smith/twohand/katana, /obj/item/melee/bokken)
|
||||
starting_sword = null
|
||||
@@ -865,6 +865,42 @@
|
||||
STR.max_items = 2
|
||||
STR.max_w_class = WEIGHT_CLASS_BULKY + WEIGHT_CLASS_NORMAL //katana and waki.
|
||||
|
||||
/obj/item/melee/smith/twohand/katana/on_exit_storage(datum/component/storage/S)
|
||||
var/obj/item/storage/belt/sabre/twin/B = S.parent
|
||||
if(istype(B))
|
||||
playsound(B, 'sound/items/unsheath.ogg', 25, 1)
|
||||
. = ..()
|
||||
|
||||
/obj/item/melee/smith/twohand/katana/on_enter_storage(datum/component/storage/S)
|
||||
var/obj/item/storage/belt/sabre/twin/B = S.parent
|
||||
if(istype(B))
|
||||
playsound(B, 'sound/items/sheath.ogg', 25, 1)
|
||||
. = ..()
|
||||
|
||||
/obj/item/melee/smith/wakizashi/on_exit_storage(datum/component/storage/S)
|
||||
var/obj/item/storage/belt/sabre/twin/B = S.parent
|
||||
if(istype(B))
|
||||
playsound(B, 'sound/items/unsheath.ogg', 25, 1)
|
||||
. = ..()
|
||||
|
||||
/obj/item/melee/smith/wakizashi/on_enter_storage(datum/component/storage/S)
|
||||
var/obj/item/storage/belt/sabre/twin/B = S.parent
|
||||
if(istype(B))
|
||||
playsound(B, 'sound/items/sheath.ogg', 25, 1)
|
||||
. = ..()
|
||||
|
||||
/obj/item/melee/bokken/on_exit_storage(datum/component/storage/S)
|
||||
var/obj/item/storage/belt/sabre/twin/B = S.parent
|
||||
if(istype(B))
|
||||
playsound(B, 'sound/items/unsheath.ogg', 25, 1)
|
||||
. = ..()
|
||||
|
||||
/obj/item/melee/bokken/on_enter_storage(datum/component/storage/S)
|
||||
var/obj/item/storage/belt/sabre/twin/B = S.parent
|
||||
if(istype(B))
|
||||
playsound(B, 'sound/items/sheath.ogg', 25, 1)
|
||||
. = ..()
|
||||
|
||||
/obj/item/storage/belt/plant
|
||||
name = "botanical belt"
|
||||
desc = "A belt used to hold most hydroponics supplies. Suprisingly, not green."
|
||||
|
||||
@@ -17,9 +17,6 @@
|
||||
|
||||
/obj/item/storage/fancy
|
||||
icon = 'icons/obj/food/containers.dmi'
|
||||
icon_state = "donutbox6"
|
||||
name = "donut box"
|
||||
desc = "Mmm. Donuts."
|
||||
resistance_flags = FLAMMABLE
|
||||
var/icon_type = "donut"
|
||||
var/spawn_type = null
|
||||
@@ -59,18 +56,22 @@
|
||||
fancy_open = TRUE
|
||||
update_icon()
|
||||
|
||||
#define DONUT_INBOX_SPRITE_WIDTH 3
|
||||
|
||||
/*
|
||||
* Donut Box
|
||||
*/
|
||||
|
||||
/obj/item/storage/fancy/donut_box
|
||||
icon = 'icons/obj/food/containers.dmi'
|
||||
icon_state = "donutbox6"
|
||||
icon_type = "donut"
|
||||
name = "donut box"
|
||||
desc = "Mmm. Donuts."
|
||||
icon = 'icons/obj/food/donut.dmi'
|
||||
icon_state = "donutbox_inner"
|
||||
icon_type = "donut"
|
||||
spawn_type = /obj/item/reagent_containers/food/snacks/donut
|
||||
fancy_open = TRUE
|
||||
custom_price = PRICE_NORMAL
|
||||
appearance_flags = KEEP_TOGETHER
|
||||
|
||||
/obj/item/storage/fancy/donut_box/ComponentInitialize()
|
||||
. = ..()
|
||||
@@ -78,6 +79,36 @@
|
||||
STR.max_items = 6
|
||||
STR.can_hold = typecacheof(list(/obj/item/reagent_containers/food/snacks/donut))
|
||||
|
||||
/obj/item/storage/fancy/donut_box/PopulateContents()
|
||||
. = ..()
|
||||
update_icon()
|
||||
|
||||
/obj/item/storage/fancy/donut_box/update_icon_state()
|
||||
if(fancy_open)
|
||||
icon_state = "donutbox_inner"
|
||||
else
|
||||
icon_state = "donutbox"
|
||||
|
||||
/obj/item/storage/fancy/donut_box/update_overlays()
|
||||
. = ..()
|
||||
|
||||
if (!fancy_open)
|
||||
return
|
||||
|
||||
var/donuts = 0
|
||||
|
||||
for (var/_donut in contents)
|
||||
var/obj/item/reagent_containers/food/snacks/donut/donut = _donut
|
||||
if (!istype(donut))
|
||||
continue
|
||||
|
||||
. += image(icon = initial(icon), icon_state = donut.in_box_sprite(), pixel_x = donuts * DONUT_INBOX_SPRITE_WIDTH)
|
||||
donuts += 1
|
||||
|
||||
. += image(icon = initial(icon), icon_state = "donutbox_top")
|
||||
|
||||
#undef DONUT_INBOX_SPRITE_WIDTH
|
||||
|
||||
/*
|
||||
* Egg Box
|
||||
*/
|
||||
|
||||
@@ -520,3 +520,9 @@
|
||||
new /obj/item/storage/fancy/cigarettes/cigpack_syndicate(src)
|
||||
new /obj/item/lighter(src)
|
||||
|
||||
/obj/item/storage/box/syndie_kit/scarp
|
||||
|
||||
/obj/item/storage/box/syndie_kit/scarp/PopulateContents()
|
||||
new /obj/item/book/granter/martial/carp(src)
|
||||
new /obj/item/clothing/suit/hooded/carp_costume(src)
|
||||
new /obj/item/staff/bostaff(src)
|
||||
|
||||
@@ -271,6 +271,11 @@
|
||||
icon = "the_lightbulb"
|
||||
desc = "A cafe popular among moths and moffs. Once shut down for a week after the bartender used mothballs to protect her spare uniforms."
|
||||
|
||||
/datum/barsign/goose
|
||||
name = "The Loose Goose"
|
||||
icon = "goose"
|
||||
desc = "Drink till you puke and/or break the laws of reality!"
|
||||
|
||||
/datum/barsign/cybersylph
|
||||
name = "Cyber Sylph's"
|
||||
icon = "cybersylph"
|
||||
|
||||
+7
-9
@@ -96,18 +96,16 @@ GLOBAL_LIST_INIT(freqtospan, list(
|
||||
return "[say_mod(input, message_mode)][spanned ? ", \"[spanned]\"" : ""]"
|
||||
// Citadel edit [spanned ? ", \"[spanned]\"" : ""]"
|
||||
|
||||
#define ENCODE_HTML_EPHASIS(input, char, html, varname) \
|
||||
var/static/regex/##varname = regex("[char]{2}(.+?)[char]{2}", "g");\
|
||||
input = varname.Replace_char(input, "<[html]>$1</[html]>")
|
||||
|
||||
/// Converts specific characters, like +, |, and _ to formatted output.
|
||||
/atom/movable/proc/say_emphasis(input)
|
||||
ENCODE_HTML_EPHASIS(input, "\\|", "i", italics)
|
||||
ENCODE_HTML_EPHASIS(input, "\\+", "b", bold)
|
||||
ENCODE_HTML_EPHASIS(input, "_", "u", underline)
|
||||
var/static/regex/italics = regex(@"\|(\S[\w\W]*?\S)\|", "g")
|
||||
input = italics.Replace_char(input, "<i>$1</i>")
|
||||
var/static/regex/bold = regex(@"\+(\S[\w\W]*?\S)\+", "g")
|
||||
input = bold.Replace_char(input, "<b>$1</b>")
|
||||
var/static/regex/underline = regex(@"_(\S[\w\W]*?\S)_", "g")
|
||||
input = underline.Replace_char(input, "<u>$1</u>")
|
||||
return input
|
||||
|
||||
#undef ENCODE_HTML_EPHASIS
|
||||
|
||||
/// Quirky citadel proc for our custom sayverbs to strip the verb out. Snowflakey as hell, say rewrite 3.0 when?
|
||||
/atom/movable/proc/quoteless_say_quote(input, list/spans = list(speech_span), message_mode)
|
||||
if((input[1] == "!") && (length_char(input) > 1))
|
||||
|
||||
Reference in New Issue
Block a user