Merge branch 'master' into upstream-2026-06-23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"version": 1,
|
||||
"map_name": "Pubby Station",
|
||||
"map_path": "map_files/PubbyStation",
|
||||
"map_file": "PubbyStation.dmm",
|
||||
"shuttles": {
|
||||
"emergency": "emergency_pubby",
|
||||
"whiteship": "whiteship_pubby",
|
||||
"ferry": "ferry_fancy",
|
||||
"cargo": "cargo_box"
|
||||
},
|
||||
"job_changes": {
|
||||
"Cook": {
|
||||
"additional_cqc_areas": ["/area/station/service/bar/atrium"]
|
||||
},
|
||||
"Chaplain": {
|
||||
"has_monastery": 1
|
||||
},
|
||||
"Prisoner": {
|
||||
"spawn_positions": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@
|
||||
|
||||
/// Whether the menu is in the middle of refreshing the preview
|
||||
var/refreshing = TRUE
|
||||
///BUBBER VAR: Optional display labels for color groups, keyed by color index.
|
||||
var/list/color_labels
|
||||
|
||||
/**
|
||||
* Whether the menu is currently locked down to prevent abuse from players.
|
||||
@@ -102,12 +104,14 @@
|
||||
/datum/greyscale_modify_menu/ui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["greyscale_config"] = "[config.name]"
|
||||
data["full_color_string"] = split_colors?.Join("") //BUBBER ADDITION
|
||||
|
||||
var/list/color_data = list()
|
||||
data["colors"] = color_data
|
||||
for(var/i in 1 to config.expected_colors)
|
||||
color_data += list(list(
|
||||
"index" = i,
|
||||
"label" = color_labels?["[i]"] || color_labels?[i], //BUBBER ADDITION
|
||||
"value" = split_colors[i]
|
||||
))
|
||||
|
||||
|
||||
@@ -407,7 +407,11 @@ GLOBAL_LIST_INIT(all_loadout_categories, init_loadout_categories())
|
||||
formatted_item["reskins"] = get_reskin_options()
|
||||
formatted_item["icon"] = ui_icon
|
||||
formatted_item["icon_state"] = ui_icon_state
|
||||
formatted_item["ckey_whitelist"] = ckeywhitelist // BUBBER EDIT ADDITION: Filter ckey-locked items
|
||||
//BUBBER EDIT ADDITION START - Dynamic Uniforms + ckey lock filter
|
||||
if(initial(item_path.greyscale_component_style_type))
|
||||
add_component_style_preview_to_ui_data(formatted_item)
|
||||
formatted_item["ckey_whitelist"] = ckeywhitelist
|
||||
//BUBBER EDIT ADDITION END
|
||||
|
||||
return formatted_item
|
||||
|
||||
|
||||
@@ -204,6 +204,11 @@
|
||||
|
||||
if(greyscale_colors)
|
||||
vended_item.set_greyscale(colors=greyscale_colors)
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
var/list/component_style_details = greyscale_component_style_vend_details?[REF(user)]
|
||||
if(component_style_details && vended_item.greyscale_component_style_type)
|
||||
vended_item.apply_greyscale_component_style(component_style_details)
|
||||
//BUBBER ADDITION END
|
||||
if(IsReachableBy(user) && user.put_in_hands(vended_item))
|
||||
to_chat(user, span_notice("You take [item_record.name] out of the slot."))
|
||||
vended_item.do_pickup_animation(user, src)
|
||||
|
||||
@@ -44,11 +44,22 @@
|
||||
)
|
||||
|
||||
var/atom/printed = record.product_path
|
||||
//BUBBER ADDITION START - Dynamic uniforms
|
||||
var/component_preview_applied = FALSE
|
||||
if(ispath(printed, /obj/item))
|
||||
var/obj/item/printed_item = printed
|
||||
if(initial(printed_item.greyscale_component_style_type))
|
||||
static_record["image"] = get_component_style_vending_preview_image(printed_item)
|
||||
component_preview_applied = TRUE
|
||||
//BUBBER ADDITION END
|
||||
// If it's not GAGS and has no innate colors we have to care about, we use DMIcon
|
||||
if(ispath(printed, /atom) \
|
||||
//BUBBER EDIT START - dynamic uniform
|
||||
if(!component_preview_applied \
|
||||
&& ispath(printed, /atom) \
|
||||
&& (!initial(printed.greyscale_config) || !initial(printed.greyscale_colors)) \
|
||||
&& !initial(printed.color) \
|
||||
)
|
||||
//BUBBER EDIT END
|
||||
static_record["icon"] = initial(printed.icon)
|
||||
static_record["icon_state"] = initial(printed.icon_state)
|
||||
|
||||
@@ -147,6 +158,12 @@
|
||||
if(!istype(product))
|
||||
return FALSE
|
||||
var/atom/fake_atom = product.product_path
|
||||
//BUBBER ADDITION START - Dynamic uniforms
|
||||
if(ispath(fake_atom, /obj/item))
|
||||
var/obj/item/fake_item = fake_atom
|
||||
if(initial(fake_item.greyscale_component_style_type))
|
||||
return open_component_style_vending_menu(product, params, ui.user)
|
||||
//BUBBER ADDITION END
|
||||
var/config = initial(fake_atom.greyscale_config)
|
||||
if(!config)
|
||||
return FALSE
|
||||
|
||||
@@ -72,6 +72,11 @@ map kilostation
|
||||
webmap_url https://webmap.affectedarc07.co.uk/maps/bubber/kilostation/
|
||||
endmap
|
||||
|
||||
map PubbyStation
|
||||
votable
|
||||
webmap_url https://webmap.affectedarc07.co.uk/maps/fulp/pubbystation/
|
||||
endmap
|
||||
|
||||
map moonstation
|
||||
votable
|
||||
webmap_url https://webmap.affectedarc07.co.uk/maps/bubber/moonstation/
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
author: "Robwo"
|
||||
delete-after: True
|
||||
changes:
|
||||
- map: "Risk of Pubby Station Returns"
|
||||
@@ -0,0 +1,5 @@
|
||||
author: "KateAnbolho, vinylspiders"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "The VOSKHOD Modular Suit, and the systems needed to create it."
|
||||
- rscadd: "Depowered VOSKHOD armor can be sourced from \"questionable\" sources."
|
||||
@@ -0,0 +1,5 @@
|
||||
author: "UvvU"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Added dynamic uniform support"
|
||||
- rscadd: "Added black varients for the sec huds, belt, and gloves"
|
||||
@@ -0,0 +1,5 @@
|
||||
author: "nikothedude"
|
||||
delete-after: True
|
||||
changes:
|
||||
- rscadd: "Re-adds the modular laser weapons to the game, with the rifle being found in a traitor's uplink, and the carbine being crewsided."
|
||||
- balance: "Tweaked the modular weapons to be less powerful overall."
|
||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 509 B |
@@ -266,7 +266,7 @@ SUBSYSTEM_DEF(area_spawn)
|
||||
/// See code/__DEFINES/~skyrat_defines/automapper.dm
|
||||
var/mode = AREA_SPAWN_MODE_OPEN
|
||||
/// Map blacklist, this is used to determine what maps we should not spawn on.
|
||||
var/list/blacklisted_stations = list("Void Raptor", "Ouroboros", "Runtime Station", "Minimal Runtime Station", "MultiZ Debug", "Gateway Test", "Blueshift", "Biodome", "Moon Station", "Box Station", "NorthStar")
|
||||
var/list/blacklisted_stations = list("Void Raptor", "Ouroboros", "Runtime Station", "Minimal Runtime Station", "MultiZ Debug", "Gateway Test", "Blueshift", "Biodome", "Moon Station", "Box Station", "NorthStar", "Pubby Station")
|
||||
/// If failing to find a suitable area is OK, then this should be TRUE or CI will fail.
|
||||
/// Should probably be true if the target_areas are random, such as ruins.
|
||||
var/optional = FALSE
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/datum/outfit/centcom/ert/security
|
||||
l_hand = /obj/item/gun/energy/modular_laser_rifle/carbine
|
||||
l_hand = /obj/item/gun/energy/e_gun/nuclear
|
||||
|
||||
/datum/outfit/centcom/ert/medic
|
||||
r_hand = /obj/item/gun/energy/modular_laser_rifle/carbine
|
||||
r_hand = /obj/item/gun/energy/e_gun/nuclear
|
||||
|
||||
/datum/outfit/centcom/ert/engineer
|
||||
l_hand = /obj/item/gun/energy/modular_laser_rifle/carbine
|
||||
l_hand = /obj/item/gun/energy/e_gun/nuclear
|
||||
|
||||
/datum/outfit/centcom/ert/commander
|
||||
l_hand = /obj/item/gun/energy/modular_laser_rifle
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
/obj/item/gun/energy/modular_laser_rifle
|
||||
name = "\improper Hyeseong modular laser rifle"
|
||||
desc = "A popular energy weapon system that can be reconfigured into many different variants on the fly. \
|
||||
Seen commonly amongst the Marsians who produce the weapon, with many different shapes and sizes to fit \
|
||||
Seen commonly amongst the Martians who produce the weapon, with many different shapes and sizes to fit \
|
||||
the wide variety of modders the planet is home to."
|
||||
base_icon_state = "hyeseong"
|
||||
icon = 'modular_skyrat/modules/modular_weapons/icons/obj/company_and_or_faction_based/saibasan/guns48x.dmi'
|
||||
@@ -22,6 +22,7 @@
|
||||
inhand_icon_state = "hyeseong_kill"
|
||||
worn_icon = 'modular_skyrat/modules/modular_weapons/icons/mob/company_and_or_faction_based/saibasan/guns_worn.dmi'
|
||||
worn_icon_state = "hyeseong_kill"
|
||||
selfcharge = FALSE
|
||||
cell_type = /obj/item/stock_parts/power_store/cell/hyeseong_internal_cell
|
||||
modifystate = FALSE
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/cybersun_big_kill)
|
||||
@@ -29,6 +30,7 @@
|
||||
ammo_x_offset = 0
|
||||
selfcharge = 1
|
||||
charge_delay = 15
|
||||
charge_sections = 3
|
||||
shaded_charge = TRUE
|
||||
slot_flags = ITEM_SLOT_BACK
|
||||
obj_flags = UNIQUE_RENAME
|
||||
@@ -69,7 +71,7 @@
|
||||
/// Keeps track of our soulcatcher component
|
||||
var/datum/component/carrier/soulcatcher/tracked_soulcatcher
|
||||
/// What is this gun's extended examine, we only have to do this because the carbine is a subtype
|
||||
var/expanded_examine_text = "The Hyeseong rifle is the first line of man-portable Marsian weapons platforms \
|
||||
var/expanded_examine_text = "The Hyeseong rifle is the first line of man-portable Martian weapons platforms \
|
||||
from Cybersun Industries. Like her younger sister weapon, the Hoshi carbine, CI used funding aid provided \
|
||||
by TerraGov to develop a portable weapon fueled by a proprietary generator rumored to be fueled by superstable plasma. \
|
||||
A rugged and hefty weapon, the Hyeseong stars in applications anywhere from medium to long ranges, though struggling \
|
||||
@@ -261,12 +263,11 @@
|
||||
inhand_icon_state = "hoshi_kill"
|
||||
worn_icon_state = "hoshi_kill"
|
||||
base_icon_state = "hoshi"
|
||||
charge_sections = 3
|
||||
cell_type = /obj/item/stock_parts/power_store/cell
|
||||
cell_type = /obj/item/stock_parts/power_store/cell/hyeseong_internal_cell
|
||||
ammo_type = list(/obj/item/ammo_casing/energy/cybersun_small_hellfire)
|
||||
slot_flags = ITEM_SLOT_BACK | ITEM_SLOT_BELT
|
||||
SET_BASE_PIXEL(0, 0)
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
weapon_weight = WEAPON_MEDIUM
|
||||
weapon_mode_options = list(
|
||||
/datum/laser_weapon_mode/hellfire,
|
||||
@@ -275,11 +276,11 @@
|
||||
/datum/laser_weapon_mode/shotgun_small,
|
||||
/datum/laser_weapon_mode/trickshot_disabler,
|
||||
)
|
||||
default_selected_mode = "Incinerate"
|
||||
default_selected_mode = "kill"
|
||||
speech_json_file = SHORT_MOD_LASER_SPEECH
|
||||
expanded_examine_text = "The Hoshi carbine is the latest line of man-portable Marsian weapons platforms from \
|
||||
expanded_examine_text = "The Hoshi carbine is the latest line of man-portable Martian weapons platforms from \
|
||||
Cybersun Industries. Like her older sister weapon, the Hyeseong rifle, CI used funding aid provided by TerraGov \
|
||||
to develop a portable weapon fueled by a proprietary generator rumored to be fueled by superstable plasma. A \
|
||||
to develop a portable weapon using the same modular weapon system as the Hyeseong, but unforunately lacking the plasma generator. A \
|
||||
lithe and mobile weapon, the Hoshi stars in close-quarters battle, trickshots, and area-of-effect blasts; though \
|
||||
ineffective at ranged combat. Her onboard machine intelligence, at first devised to support the operator and \
|
||||
manage the internal reactor, was originally shipped with a more energetic personality-- since influenced by 'negligence' \
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
var/datum/component/scope/scope_component
|
||||
|
||||
/datum/laser_weapon_mode/marksman/apply_to_weapon(obj/item/gun/energy/applied_gun)
|
||||
scope_component = applied_gun.AddComponent(/datum/component/scope, 3)
|
||||
scope_component = applied_gun.AddComponent(/datum/component/scope, 2)
|
||||
|
||||
/datum/laser_weapon_mode/marksman/remove_from_weapon(obj/item/gun/energy/applied_gun)
|
||||
QDEL_NULL(scope_component)
|
||||
@@ -110,11 +110,11 @@
|
||||
|
||||
// Hellfire mode for the small laser
|
||||
/datum/laser_weapon_mode/hellfire
|
||||
name = "Incinerate"
|
||||
name = "kill"
|
||||
casing = /obj/item/ammo_casing/energy/cybersun_small_hellfire
|
||||
weapon_icon_state = "kill"
|
||||
charge_sections = 3
|
||||
shot_delay = 0.4 SECONDS
|
||||
shot_delay = 0.5 SECONDS
|
||||
json_speech_string = "incinerate"
|
||||
gun_runetext_color = "#cd4456"
|
||||
|
||||
@@ -131,15 +131,15 @@
|
||||
// And also so the visuals work :3
|
||||
casing = /obj/item/ammo_casing/energy/cybersun_small_blade
|
||||
weapon_icon_state = "blade"
|
||||
charge_sections = 2
|
||||
charge_sections = 3
|
||||
json_speech_string = "blade"
|
||||
gun_runetext_color = "#f8d860"
|
||||
|
||||
/datum/laser_weapon_mode/sword/apply_to_weapon(obj/item/gun/energy/modular_laser_rifle/applied_gun)
|
||||
playsound(src, 'sound/items/unsheath.ogg', 25, TRUE)
|
||||
applied_gun.force = 20
|
||||
applied_gun.force = 15
|
||||
applied_gun.sharpness = SHARP_EDGED
|
||||
applied_gun.exposed_wound_bonus = 15
|
||||
applied_gun.exposed_wound_bonus = 10
|
||||
applied_gun.wound_bonus = 5
|
||||
applied_gun.disabled_for_other_reasons = TRUE
|
||||
applied_gun.attack_verb_continuous = list("slashes", "cuts")
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
range = 6
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
|
||||
light_color = COLOR_PALE_GREEN
|
||||
pass_flags = PASSTABLE | PASSGRILLE // His ass does NOT pass through glass!
|
||||
pass_flags = PASSTABLE // His ass does NOT pass through glass!
|
||||
/// What type of casing should we put inside the bullet to act as shrapnel later
|
||||
var/casing_to_spawn = /obj/item/grenade/c980payload/plasma_grenade
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
exposed_wound_bonus = 55 // Lasers have a wound bonus of 40, this is a bit higher
|
||||
wound_bonus = -50 // However we do not very much against armor
|
||||
range = 2
|
||||
pass_flags = PASSTABLE | PASSGRILLE // His ass does NOT pass through glass!
|
||||
pass_flags = PASSTABLE // His ass does NOT pass through glass!
|
||||
weak_against_armour = TRUE
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
|
||||
light_color = COLOR_PALE_GREEN
|
||||
@@ -124,15 +124,14 @@
|
||||
|
||||
/obj/item/ammo_casing/energy/cybersun_small_hellfire
|
||||
projectile_type = /obj/projectile/beam/cybersun_laser/hellfire
|
||||
e_cost = LASER_SHOTS(10, STANDARD_CELL_CHARGE)
|
||||
e_cost = LASER_SHOTS(7, STANDARD_CELL_CHARGE)
|
||||
select_name = "Incinerate"
|
||||
fire_sound = 'modular_zubbers/sound/weapons/incinerate.ogg'
|
||||
|
||||
/obj/projectile/beam/cybersun_laser/hellfire
|
||||
icon_state = "hellfire"
|
||||
damage = 30
|
||||
damage = 25
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/red_laser
|
||||
speed = 1.6
|
||||
wound_bonus = 0
|
||||
light_color = COLOR_SOFT_RED
|
||||
|
||||
@@ -143,12 +142,15 @@
|
||||
e_cost = LASER_SHOTS(10, STANDARD_CELL_CHARGE)
|
||||
select_name = "Disable"
|
||||
harmful = FALSE
|
||||
fire_sound = 'sound/items/weapons/taser2.ogg'
|
||||
impact_sound = 'sound/items/weapons/sear_disabler.ogg'
|
||||
|
||||
/obj/projectile/beam/cybersun_laser/disable_bounce
|
||||
icon_state = "disable_bounce"
|
||||
damage = 0
|
||||
stamina = 30
|
||||
stamina = 20
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
|
||||
impact_sound = 'sound/items/weapons/sear_disabler.ogg'
|
||||
light_color = COLOR_BRIGHT_BLUE
|
||||
ricochet_auto_aim_angle = 30
|
||||
ricochet_auto_aim_range = 5
|
||||
@@ -172,7 +174,7 @@
|
||||
/obj/projectile/beam/cybersun_laser/flare
|
||||
name = "plasma flare"
|
||||
icon_state = "flare"
|
||||
damage = 30
|
||||
damage = 40
|
||||
speed = 0.5
|
||||
range = 6
|
||||
impact_effect_type = /obj/effect/temp_visual/impact_effect/green_laser
|
||||
@@ -220,7 +222,7 @@
|
||||
|
||||
/obj/item/ammo_casing/energy/cybersun_small_shotgun
|
||||
projectile_type = /obj/projectile/beam/cybersun_laser/granata_shrapnel/shotgun_pellet
|
||||
e_cost = LASER_SHOTS(10, STANDARD_CELL_CHARGE)
|
||||
e_cost = LASER_SHOTS(7, STANDARD_CELL_CHARGE)
|
||||
pellets = 3
|
||||
variance = 20
|
||||
select_name = "Shotgun"
|
||||
|
||||
@@ -45,3 +45,10 @@
|
||||
if(LAZYLEN(the_piece.magazine.stored_ammo))
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/item/crafting_conversion_kit/voskhod_refit
|
||||
name = "\improper Voskhod depowered armor MOD refit kit"
|
||||
desc = "A metallic case of various tubes, sensors and spare materials required to reuse Voskhod's components in the making of a next-generation MODed version."
|
||||
force = 10
|
||||
icon = 'modular_zubbers/icons/misc/refitkit.dmi'
|
||||
icon_state = "refit_kit"
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
flash_protect = FLASH_PROTECTION_WELDER
|
||||
visor_flags_inv = HIDEMASK|HIDEEYES|HIDEFACE|HIDEFACIALHAIR
|
||||
visor_flags = STOPSPRESSUREDAMAGE
|
||||
slowdown = 0
|
||||
slowdown = 1
|
||||
|
||||
/obj/item/clothing/head/helmet/space/hev_suit/nri/captain
|
||||
name = "\improper VOSKHOD-2 powered combat armor helmet"
|
||||
@@ -48,3 +48,5 @@
|
||||
name = "\improper VOSKHOD-IN powered combat armor helmet"
|
||||
desc = "A composite tungsten-plasteel helmet with a lead-lined ballistic nylon inner padding, complete with a deployable airtight polycarbonate visor and respirator system. 'НРИ - Оборонная Коллегия' is imprinted on the back."
|
||||
icon_state = "nri_engineer"
|
||||
|
||||
|
||||
|
||||
@@ -125,6 +125,11 @@
|
||||
|
||||
/obj/item/mod/control/pre_equipped/frontline/surplus
|
||||
theme = /datum/mod_theme/frontline/surplus
|
||||
applied_modules = list(
|
||||
/obj/item/mod/module/storage,
|
||||
/obj/item/mod/module/status_readout/operational,
|
||||
/obj/item/mod/module/flashlight,
|
||||
)
|
||||
|
||||
/datum/supply_pack/imports/surplus_nri_modsuit
|
||||
name = "Surplus Combat MODsuit Crate"
|
||||
@@ -234,159 +239,4 @@
|
||||
to alert anyone nearby that someone has, in fact, died. This specific unit has a clock and operational ID readout."
|
||||
display_time = TRUE
|
||||
death_sound = 'modular_skyrat/modules/novaya_ert/sound/flatline.ogg'
|
||||
|
||||
///Blatant copy of the adrenaline boost module.
|
||||
/obj/item/mod/module/auto_doc
|
||||
name = "MOD automatic paramedical module"
|
||||
desc = "The reverse-engineered and redesigned medical assistance system, previously used by the now decommissioned VOSKHOD combat armor. \
|
||||
The technology it uses is very similar to the one of Spider Clan, yet Innovations and Defense Collegium reject any similarities. \
|
||||
Using a built-in storage of chemical compounds and miniature chemical mixer, it's capable of injecting its user with simple painkillers and coagulants, \
|
||||
assisting them with their restoration, as long as they don't overdose themselves. However, this system heavily relies on some rarely combat-available chemical compounds to prepare its injections, \
|
||||
mainly Cryptobiolin, which appear in the user's bloodstream from time to time, and its trivial damage assesment systems are inadequate for complete restoration purposes."
|
||||
icon_state = "adrenaline_boost"
|
||||
module_type = MODULE_TOGGLE
|
||||
incompatible_modules = list(/obj/item/mod/module/adrenaline_boost, /obj/item/mod/module/auto_doc)
|
||||
cooldown_time = null
|
||||
complexity = 4
|
||||
use_energy_cost = DEFAULT_CHARGE_DRAIN * 20
|
||||
/// What reagent we need to refill?
|
||||
var/reagent_required = /datum/reagent/cryptobiolin
|
||||
/// How much of a reagent we need to refill a single boost.
|
||||
var/reagent_required_amount = 20
|
||||
/// Maximum amount of reagents this module can hold.
|
||||
var/reagent_max_amount = 120
|
||||
/// Health threshold above which the module won't heal.
|
||||
var/health_threshold = 80
|
||||
/// Cooldown betwen each treatment.
|
||||
var/heal_cooldown = 30 SECONDS
|
||||
|
||||
/// Timer for the cooldown.
|
||||
COOLDOWN_DECLARE(heal_timer)
|
||||
|
||||
/// Creates chemical container for chemicals and fills it with chemicals. Chemception.
|
||||
/obj/item/mod/module/auto_doc/Initialize(mapload)
|
||||
. = ..()
|
||||
create_reagents(reagent_max_amount)
|
||||
reagents.add_reagent(reagent_required, reagent_max_amount)
|
||||
|
||||
/obj/item/mod/module/auto_doc/on_activation()
|
||||
. = ..()
|
||||
if(!.)
|
||||
return
|
||||
RegisterSignal(mod.wearer, COMSIG_LIVING_HEALTH_UPDATE, PROC_REF(on_use))
|
||||
drain_power(use_energy_cost)
|
||||
|
||||
/// Heals damage (in fact, injects chems) based on the damage received and certain other variables (a single one), i.e. having more than X amount of health, not having enough needed chemicals or so on.
|
||||
/obj/item/mod/module/auto_doc/on_use()
|
||||
if(!COOLDOWN_FINISHED(src, heal_timer))
|
||||
return FALSE
|
||||
|
||||
if(!check_power(use_energy_cost))
|
||||
balloon_alert(mod.wearer, "not enough charge!")
|
||||
SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED)
|
||||
return FALSE
|
||||
|
||||
if(!(allow_flags & MODULE_ALLOW_PHASEOUT) && istype(mod.wearer.loc, /obj/effect/dummy/phased_mob))
|
||||
to_chat(mod.wearer, span_warning("You cannot activate this right now."))
|
||||
return FALSE
|
||||
|
||||
if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE)
|
||||
return FALSE
|
||||
|
||||
if(!reagents.has_reagent(reagent_required, reagent_required_amount))
|
||||
balloon_alert(mod.wearer, "not enough chems!")
|
||||
SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED)
|
||||
return FALSE
|
||||
|
||||
if(mod.wearer.health > health_threshold)
|
||||
return
|
||||
|
||||
var/new_bruteloss = mod.wearer.get_brute_loss()
|
||||
var/new_fireloss = mod.wearer.get_fire_loss()
|
||||
var/new_toxloss = mod.wearer.get_tox_loss()
|
||||
var/new_stamloss = mod.wearer.get_stamina_loss()
|
||||
playsound(mod.wearer, 'modular_skyrat/modules/hev_suit/sound/hev/hiss.ogg', 100)
|
||||
|
||||
if(new_bruteloss)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/mine_salve, 10)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/sal_acid, 5)
|
||||
to_chat(mod.wearer, span_warning("Brute treatment administered. Overdose risks present on further use, consult your first-aid analyzer."))
|
||||
|
||||
if(new_fireloss)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/mine_salve, 10)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/oxandrolone, 5)
|
||||
to_chat(mod.wearer, span_warning("Burn treatment administered. Overdose risks present on further use, consult your first-aid analyzer."))
|
||||
|
||||
if(new_toxloss)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/mine_salve, 10)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/pen_acid, 5)
|
||||
to_chat(mod.wearer, span_warning("Toxin treatment administered. Overdose risks present on further use, consult your first-aid analyzer."))
|
||||
|
||||
if(new_stamloss)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/mine_salve, 10)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/stimulants, 10)
|
||||
to_chat(mod.wearer, span_warning("Combat stimulants administered. Overdose risks present on further use, consult your first-aid analyzer."))
|
||||
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/coagulant, 5)
|
||||
reagents.remove_reagent(reagent_required, reagent_required_amount)
|
||||
drain_power(use_energy_cost*10)
|
||||
|
||||
///Debuff so it's "balanced", as well as a cooldown.
|
||||
addtimer(CALLBACK(src, PROC_REF(boost_aftereffects), mod.wearer), 45 SECONDS)
|
||||
COOLDOWN_START(src, heal_timer, heal_cooldown)
|
||||
|
||||
/// Refills MODsuit module with the needed chemicals. That's all it does.
|
||||
/obj/item/mod/module/auto_doc/proc/charge_boost(obj/item/attacking_item, mob/user)
|
||||
/// Oh, and it also doesn't work if it's (the chemical container) closed.
|
||||
if(!attacking_item.is_open_container())
|
||||
return FALSE
|
||||
/// And if it's already full (:flushed:)
|
||||
if(reagents.has_reagent(reagent_required, reagent_max_amount))
|
||||
balloon_alert(mod.wearer, "already full!")
|
||||
return FALSE
|
||||
/// And if the reagent's wrong.
|
||||
if(!attacking_item.reagents.trans_to(src, reagent_required_amount, target_id = reagent_required))
|
||||
return FALSE
|
||||
/// And if you got to that point without screwing up then it awards you with being refilled.
|
||||
balloon_alert(mod.wearer, "charge reloaded")
|
||||
return TRUE
|
||||
|
||||
/obj/item/mod/module/auto_doc/on_deactivation(display_message = TRUE, deleting = FALSE)
|
||||
. = ..()
|
||||
UnregisterSignal(mod.wearer, COMSIG_LIVING_HEALTH_UPDATE)
|
||||
|
||||
/obj/item/mod/module/auto_doc/on_install()
|
||||
. = ..()
|
||||
RegisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(on_item_interact))
|
||||
|
||||
/obj/item/mod/module/auto_doc/on_uninstall(deleting)
|
||||
. = ..()
|
||||
UnregisterSignal(mod, COMSIG_ATOM_ATTACKBY)
|
||||
|
||||
/obj/item/mod/module/auto_doc/attackby(obj/item/attacking_item, mob/user, params)
|
||||
if(charge_boost(attacking_item, user))
|
||||
return TRUE
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/module/auto_doc/proc/on_item_interact(datum/source, mob/user, obj/item/thing, params)
|
||||
SIGNAL_HANDLER
|
||||
|
||||
if(charge_boost(thing, user))
|
||||
return COMPONENT_NO_AFTERATTACK
|
||||
|
||||
return NONE
|
||||
|
||||
/// Drawbacks to make this module less self-sufficient and so it feels "balanced" (there's no balance).
|
||||
/obj/item/mod/module/auto_doc/proc/boost_aftereffects(mob/affected_mob)
|
||||
if(!affected_mob)
|
||||
return
|
||||
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/cryptobiolin, 10)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/drug/maint/sludge, 5)
|
||||
to_chat(affected_mob, span_danger("Your head starts slightly spinning, and your chest hurts."))
|
||||
|
||||
/// Not exactly a MODsuit thing but it's needed for the refills huh?
|
||||
/obj/item/reagent_containers/cup/glass/waterbottle/large/cryptobiolin
|
||||
name = "bottle of cryptobiolin"
|
||||
desc = "Nothing screams budget cuts like bottled suit fluid."
|
||||
list_reagents = list(/datum/reagent/cryptobiolin = 100)
|
||||
removable = TRUE
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
/// Tracks selected core components for component-style GAGS loadout items.
|
||||
#define INFO_GREYSCALE_COMPONENT_CORES "greyscale_component_cores"
|
||||
/// Tracks selected optional components for component-style GAGS loadout items.
|
||||
#define INFO_GREYSCALE_COMPONENT_ACCESSORIES "greyscale_component_accessories"
|
||||
|
||||
/// Accessory applies to every option in a core component slot.
|
||||
#define COMPONENT_CORE_ALL "all"
|
||||
/// Accessory applies only to the named core option.
|
||||
#define COMPONENT_CORE_EXACT "exact"
|
||||
/// Accessory applies to the named core option and any option descended from it.
|
||||
#define COMPONENT_CORE_BRANCH "branch"
|
||||
/// Accessory applies to a named group defined on the core component.
|
||||
#define COMPONENT_CORE_GROUP "group"
|
||||
@@ -8,6 +8,47 @@
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/captain.dmi'
|
||||
json_config = 'modular_zubbers/code/datums/greyscale/json_configs/cso.json'
|
||||
|
||||
/datum/greyscale_config/security_uniform
|
||||
name = "Security Uniform"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_uniform
|
||||
greyscale_component_config_states = list("recolorable_security_uniform", "rsecurity")
|
||||
|
||||
/datum/greyscale_config/security_uniform/New()
|
||||
InitializeGreyscaleComponentStyleConfig()
|
||||
|
||||
/datum/greyscale_config/security_uniform/Refresh(loadFromDisk = FALSE)
|
||||
return RefreshGreyscaleComponentStyleConfig(loadFromDisk)
|
||||
|
||||
/datum/greyscale_config/security_uniform/worn
|
||||
name = "Security Uniform (Worn)"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
|
||||
/datum/greyscale_config/security_uniform/worn/digi
|
||||
name = "Security Uniform (Worn, Digi)"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
|
||||
/datum/greyscale_config/security_cap
|
||||
name = "Security Cap"
|
||||
icon_file = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_cap
|
||||
greyscale_component_config_states = list("security_cap")
|
||||
|
||||
/datum/greyscale_config/security_cap/New()
|
||||
InitializeGreyscaleComponentStyleConfig()
|
||||
|
||||
/datum/greyscale_config/security_cap/Refresh(loadFromDisk = FALSE)
|
||||
return RefreshGreyscaleComponentStyleConfig(loadFromDisk)
|
||||
|
||||
/datum/greyscale_config/security_cap/worn
|
||||
name = "Security Cap (Worn)"
|
||||
icon_file = 'modular_zubbers/icons/mob/clothing/head/hats.dmi'
|
||||
greyscale_component_config_state_overrides = list(
|
||||
"security_cap" = "security_capg",
|
||||
"security_cap_accs_front" = "security_capg_accs_backing",
|
||||
"security_cap_accs_logo" = "security_capg_accs_logo",
|
||||
)
|
||||
|
||||
/datum/greyscale_config/gi/worn/digi
|
||||
name = "Gi (Worn, Digi)"
|
||||
icon_file = 'modular_skyrat/master_files/icons/mob/clothing/under/costume_digi.dmi'
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
/datum/market_item/misc/voskhod_refit
|
||||
name = "Voskhod refit kit"
|
||||
desc = "A shipping, holding, medical, security and many other fee-free case of thingamabobs one can use to turn their ancient steel-plated space suit into a less ancient modular space suit."
|
||||
item = /obj/item/crafting_conversion_kit/voskhod_refit
|
||||
price_min = CARGO_CRATE_VALUE * 4
|
||||
price_max = CARGO_CRATE_VALUE * 5.75
|
||||
stock_max = 3
|
||||
availability_prob = 25
|
||||
|
||||
/datum/market_item/misc/autodoc
|
||||
name = "CIN Auto-Doc MODule"
|
||||
desc = "A 'legally' aquired auto-doc MODule, taken from some of the latest CIN equipment. This thing uses Cryptobiolin to heal you, but it's leaky as hell."
|
||||
item = /obj/item/mod/module/auto_doc
|
||||
price_min = CARGO_CRATE_VALUE * 3
|
||||
price_max = CARGO_CRATE_VALUE * 4.5
|
||||
stock_max = 3
|
||||
availability_prob = 20
|
||||
@@ -49,3 +49,13 @@
|
||||
)
|
||||
crate_name = "Battle Rifle Crate"
|
||||
order_flags = ORDER_CONTRABAND
|
||||
|
||||
/datum/supply_pack/imports/depowerarmor
|
||||
name = "Depowered VOSKHOD Armor Set"
|
||||
desc = "We've gotten in some of the PSC's 'finest' sets of armor, these have seen much better days. But hey, we know you guys love this shit!"
|
||||
cost = 3500
|
||||
contains = list(
|
||||
/obj/item/clothing/head/helmet/space/voskhod,
|
||||
/obj/item/clothing/suit/space/voskhod,
|
||||
)
|
||||
order_flags = ORDER_CONTRABAND
|
||||
|
||||
@@ -108,3 +108,7 @@
|
||||
/datum/supply_pack/company_import/psc_surplus/misc/cabin
|
||||
item_type = /obj/item/survivalcapsule/cabin
|
||||
cost = PAYCHECK_COMMAND * 2
|
||||
|
||||
/datum/supply_pack/company_import/psc_surplus/misc/restorationkit
|
||||
item_type = /obj/item/crafting_conversion_kit/voskhod_refit
|
||||
cost = CARGO_CRATE_VALUE * 6
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
/datum/supply_pack/imports/hoshi
|
||||
name = "Hoshi Modular Carbine crate"
|
||||
desc = "This crate includes a licenesed Hoshi Modular Carbine, one of the newest addition to the Terran's armories."
|
||||
cost = CARGO_CRATE_VALUE * 10
|
||||
contains = list(
|
||||
/obj/item/gun/energy/modular_laser_rifle/carbine,
|
||||
)
|
||||
access_view = ACCESS_WEAPONS
|
||||
@@ -21,7 +21,6 @@
|
||||
)
|
||||
//This makes the Security ammo crate use the cool advanced ammo boxes instead of the old ones
|
||||
|
||||
|
||||
/datum/supply_pack/security/secmed_technician
|
||||
name = "Security Medic Kit Crate - Technician"
|
||||
crate_name = "security medic crate"
|
||||
@@ -218,3 +217,13 @@
|
||||
/obj/item/gun/ballistic/shotgun/riot/sol,
|
||||
/obj/item/gun/ballistic/shotgun/riot/sol,
|
||||
)
|
||||
|
||||
/datum/supply_pack/security/armory/hoshis
|
||||
name = "Hoshi Modular Laser Carbine crate"
|
||||
desc = "This crate includes 3 of Terra's newest addition to their armories, produced by... 'Saibasan'. These modular weapons have multiple fire modes that can be switched to, and an onboard AI."
|
||||
cost = CARGO_CRATE_VALUE * 20
|
||||
contains = list(
|
||||
/obj/item/gun/energy/modular_laser_rifle/carbine,
|
||||
/obj/item/gun/energy/modular_laser_rifle/carbine,
|
||||
/obj/item/gun/energy/modular_laser_rifle/carbine,
|
||||
)
|
||||
|
||||
@@ -46,6 +46,75 @@
|
||||
icon_state = "secsoft_v"
|
||||
armor_type = /datum/armor/cosmetic_sec
|
||||
|
||||
/datum/greyscale_component_style/security_cap
|
||||
output_icon_state = "security_cap"
|
||||
fallback_icon_state = "security_cap"
|
||||
default_accessories = list("security_cap_accs_front", "security_cap_accs_logo")
|
||||
core_components = list(
|
||||
list(
|
||||
"name" = "Cap",
|
||||
"key" = "cap",
|
||||
"default" = "security",
|
||||
"options" = list(
|
||||
"security" = list(
|
||||
"name" = "Security cap",
|
||||
"state" = "security_cap",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Main",
|
||||
"default_color" = "#A53429",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
accessories = list(
|
||||
"Backing" = list(
|
||||
"state" = "security_cap_accs_front",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Backing",
|
||||
"default_color" = "#3F6E9E",
|
||||
),
|
||||
"Logo" = list(
|
||||
"state" = "security_cap_accs_logo",
|
||||
"color_id" = 3,
|
||||
"color_label" = "Logo",
|
||||
"default_color" = "#FFFFFF",
|
||||
),
|
||||
)
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable
|
||||
name = "security cap"
|
||||
desc = "It's a robust baseball hat, this one seems to have been custom ordered."
|
||||
icon = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
worn_icon = 'modular_zubbers/icons/mob/clothing/head/hats.dmi'
|
||||
icon_state = "security_cap"
|
||||
post_init_icon_state = "security_cap"
|
||||
soft_type = "security_cap"
|
||||
soft_suffix = ""
|
||||
greyscale_config = /datum/greyscale_config/security_cap
|
||||
greyscale_config_worn = /datum/greyscale_config/security_cap/worn
|
||||
greyscale_colors = "#A53429#3F6E9E#FFFFFF"
|
||||
flags_1 = parent_type::flags_1 | IS_PLAYER_COLORABLE_1
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_cap
|
||||
greyscale_component_icon_file = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
greyscale_component_worn_icon_file = 'modular_zubbers/icons/mob/clothing/head/hats.dmi'
|
||||
greyscale_component_worn_state_overrides = list(
|
||||
"security_cap" = "security_capg",
|
||||
"security_cap_accs_front" = "security_capg_accs_backing",
|
||||
"security_cap_accs_logo" = "security_capg_accs_logo",
|
||||
)
|
||||
greyscale_component_accessories = list("security_cap_accs_front", "security_cap_accs_logo")
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable/Initialize(mapload)
|
||||
initialize_greyscale_component_style()
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable/update_greyscale()
|
||||
. = ..()
|
||||
update_greyscale_component_icons()
|
||||
|
||||
/obj/item/clothing/head/soft/sec/recolorable/flip(mob/user)
|
||||
balloon_alert(user, "can't be flipped!")
|
||||
|
||||
/obj/item/clothing/head/sec/viro/beanie
|
||||
name = "security beanie"
|
||||
desc = "A beanie for security purposes"
|
||||
|
||||
@@ -92,6 +92,196 @@
|
||||
can_adjust = TRUE
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
|
||||
|
||||
/datum/greyscale_component_style/security_uniform
|
||||
output_icon_state = "rsecurity"
|
||||
fallback_icon_state = "security_shirt"
|
||||
default_accessories = list("security_shirt_accs_tie")
|
||||
core_components = list(
|
||||
list(
|
||||
"name" = "Shirt",
|
||||
"key" = "shirt",
|
||||
"default" = "security",
|
||||
"groups" = list(
|
||||
"tie_compatible" = list("security", "viro", "suit"),
|
||||
"arm_trim_compatible" = list("security", "viro", "turtleneck"),
|
||||
),
|
||||
"options" = list(
|
||||
"security" = list(
|
||||
"name" = "Security shirt",
|
||||
"state" = "security_shirt",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
"viro" = list(
|
||||
"name" = "Virosec shirt",
|
||||
"state" = "security_shirt_viro",
|
||||
"parent" = "security",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
"suit" = list(
|
||||
"name" = "Security suit",
|
||||
"state" = "security_shirt_suit",
|
||||
"parent" = "security",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
"turtleneck" = list(
|
||||
"name" = "Security turtleneck",
|
||||
"state" = "security_shirt_turtleneck",
|
||||
"parent" = "security",
|
||||
"color_id" = 1,
|
||||
"color_label" = "Shirt",
|
||||
"default_color" = "#BA3B2E",
|
||||
),
|
||||
),
|
||||
),
|
||||
list(
|
||||
"name" = "Pants",
|
||||
"key" = "pants",
|
||||
"default" = "security",
|
||||
"options" = list(
|
||||
"security" = list(
|
||||
"name" = "Security pants",
|
||||
"state" = "security_pants",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Pants",
|
||||
"default_color" = "#4B4C51",
|
||||
),
|
||||
"skirt" = list(
|
||||
"name" = "Security skirt",
|
||||
"state" = "security_pants_skirt",
|
||||
"parent" = "security",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Pants",
|
||||
"default_color" = "#4B4C51",
|
||||
),
|
||||
"viro" = list(
|
||||
"name" = "Virosec pants",
|
||||
"state" = "security_pants_viro",
|
||||
"parent" = "security",
|
||||
"color_id" = 2,
|
||||
"color_label" = "Pants",
|
||||
"default_color" = "#4B4C51",
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
accessories = list(
|
||||
"Tie" = list(
|
||||
"state" = "security_shirt_accs_tie",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_GROUP = "tie_compatible")),
|
||||
"color_id" = 3,
|
||||
"color_label" = "Tie",
|
||||
"default_color" = "#2D2D33",
|
||||
),
|
||||
"Arm trim one" = list(
|
||||
"state" = "security_shirt_accs_arm_trim_one",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_EXACT = list("security", "viro"))),
|
||||
"color_id" = 4,
|
||||
"color_label" = "Arm trim one",
|
||||
"default_color" = "#39393F",
|
||||
),
|
||||
"Arm trim two" = list(
|
||||
"state" = "security_shirt_accs_arm_trim_two",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_GROUP = "arm_trim_compatible")),
|
||||
"color_id" = 13,
|
||||
"color_label" = "Arm trim two",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Arm trim three" = list(
|
||||
"state" = "security_shirt_accs_arm_trim_three",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_GROUP = "arm_trim_compatible")),
|
||||
"color_id" = 12,
|
||||
"color_label" = "Arm trim three",
|
||||
"default_color" = "#D0D0D0",
|
||||
),
|
||||
"Epaulettes" = list(
|
||||
"state" = "security_shirt_accss_epaulettes",
|
||||
"applies_to" = list("shirt" = list(COMPONENT_CORE_BRANCH = "security")),
|
||||
"color_id" = 11,
|
||||
"color_label" = "Epaulettes",
|
||||
"default_color" = "#39393F",
|
||||
),
|
||||
"Viro shirt trim" = list(
|
||||
"state" = "security_shirt_accs_trim_viro",
|
||||
"applies_to" = list("shirt" = list("viro" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 8,
|
||||
"color_label" = "Viro shirt trim",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Suit inner shirt" = list(
|
||||
"state" = "security_shirt_suit_accs_inner",
|
||||
"applies_to" = list("shirt" = list("suit" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 9,
|
||||
"color_label" = "Suit inner shirt",
|
||||
"default_color" = "#EBEBEB",
|
||||
),
|
||||
"Buckle" = list(
|
||||
"state" = "security_pants_accs_buckle",
|
||||
"applies_to" = list("pants" = COMPONENT_CORE_ALL),
|
||||
"color_id" = 10,
|
||||
"color_label" = "Buckle",
|
||||
"default_color" = "#B5BBCF",
|
||||
),
|
||||
"Skirt outer trim" = list(
|
||||
"state" = "security_pants_skirt_accs_outer_trim",
|
||||
"applies_to" = list("pants" = list("skirt" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 5,
|
||||
"color_label" = "Skirt outer trim",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Skirt inner trim" = list(
|
||||
"state" = "security_pants_skirt_accs_inner_trim",
|
||||
"applies_to" = list("pants" = list("skirt" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 6,
|
||||
"color_label" = "Skirt inner trim",
|
||||
"default_color" = "#46464D",
|
||||
),
|
||||
"Viro pants trim" = list(
|
||||
"state" = "security_pants_viro_accs_trim",
|
||||
"applies_to" = list("pants" = list("viro" = COMPONENT_CORE_EXACT)),
|
||||
"color_id" = 7,
|
||||
"color_label" = "Viro pants trim",
|
||||
"default_color" = "#7D2A25",
|
||||
),
|
||||
)
|
||||
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable
|
||||
name = "security uniform"
|
||||
desc = "A tactical security jumpsuit for officers. This one seems to have a few custom modifications."
|
||||
icon = 'icons/map_icons/clothing/under/_under.dmi'
|
||||
worn_icon = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
worn_icon_digi = 'modular_zubbers/icons/mob/clothing/under/security_digi.dmi'
|
||||
icon_state = "/obj/item/clothing/under/rank/security/officer/recolorable"
|
||||
post_init_icon_state = "recolorable_security_uniform"
|
||||
greyscale_config = /datum/greyscale_config/security_uniform
|
||||
greyscale_config_worn = /datum/greyscale_config/security_uniform/worn
|
||||
greyscale_config_worn_digi = /datum/greyscale_config/security_uniform/worn/digi
|
||||
greyscale_colors = "#BA3B2E#4B4C51#2D2D33#39393F#46464D#46464D#7D2A25#46464D#EBEBEB#B5BBCF#39393F#D0D0D0#46464D"
|
||||
flags_1 = parent_type::flags_1 | IS_PLAYER_COLORABLE_1
|
||||
can_adjust = TRUE
|
||||
supports_variations_flags = CLOTHING_DIGITIGRADE_VARIATION
|
||||
greyscale_component_style_type = /datum/greyscale_component_style/security_uniform
|
||||
greyscale_component_worn_icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
greyscale_component_worn_digi_icon_file = 'modular_zubbers/icons/mob/clothing/under/security_digi.dmi'
|
||||
greyscale_component_digi_fallback_icon_file = 'modular_zubbers/icons/mob/clothing/under/security.dmi'
|
||||
greyscale_component_accessories = list("security_shirt_accs_tie")
|
||||
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable/Initialize(mapload)
|
||||
initialize_greyscale_component_style()
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable/update_greyscale()
|
||||
. = ..()
|
||||
update_greyscale_component_icons()
|
||||
icon = 'icons/obj/clothing/under/security.dmi'
|
||||
icon_state = "rsecurity"
|
||||
inhand_icon_state = "r_suit"
|
||||
|
||||
// Virosec, sprites by axietheaxolotl
|
||||
|
||||
/obj/item/clothing/under/rank/security/viro/officer/
|
||||
|
||||
@@ -0,0 +1,866 @@
|
||||
/datum/greyscale_component_style
|
||||
/// Marker used when loadout storage needs to preserve an intentionally empty accessory selection.
|
||||
var/static/no_accessories_marker = "__component_style_no_accessories"
|
||||
|
||||
/datum/greyscale_component_style
|
||||
/// Icon state inserted into generated icon files.
|
||||
var/output_icon_state
|
||||
/// Fallback component state to use if every selected component is missing.
|
||||
var/fallback_icon_state
|
||||
/// Suffix checked first for digitigrade component states.
|
||||
var/digi_suffix = "_digi"
|
||||
/// Ordered list of core component definitions.
|
||||
var/list/core_components = list()
|
||||
/// Optional component definitions, keyed by display name.
|
||||
var/list/accessories = list()
|
||||
/// Optional accessory component states enabled by default.
|
||||
var/list/default_accessories = list()
|
||||
/// Suffix checked first for adjusted/casual component states.
|
||||
var/adjusted_suffix = "_d"
|
||||
|
||||
/datum/greyscale_component_style/proc/default_core_selection() as /list
|
||||
var/list/selection = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
selection[core_component["key"]] = core_component["default"]
|
||||
return selection
|
||||
|
||||
/datum/greyscale_component_style/proc/sanitize_core_selection(list/selected_cores) as /list
|
||||
var/list/sanitized = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/core_key = core_component["key"]
|
||||
var/selected_state = selected_cores?[core_key] || core_component["default"]
|
||||
if(!core_option_exists(core_component, selected_state))
|
||||
selected_state = core_component["default"]
|
||||
sanitized[core_key] = selected_state
|
||||
return sanitized
|
||||
|
||||
/datum/greyscale_component_style/proc/sanitize_accessories(list/selected_accessories, list/selected_cores) as /list
|
||||
var/list/sanitized = list()
|
||||
if(!islist(selected_accessories) || (no_accessories_marker in selected_accessories))
|
||||
return sanitized
|
||||
for(var/accessory in selected_accessories)
|
||||
if(!accessory_allowed(accessory, selected_cores))
|
||||
continue
|
||||
sanitized += accessory
|
||||
return sanitized
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_exists(list/core_component, option_state)
|
||||
var/list/options = core_component["options"]
|
||||
return islist(options?[option_state])
|
||||
|
||||
/datum/greyscale_component_style/proc/core_component_for_key(core_key) as /list
|
||||
for(var/list/core_component as anything in core_components)
|
||||
if(core_component["key"] == core_key)
|
||||
return core_component
|
||||
return null
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_data(list/core_component, option_id) as /list
|
||||
var/list/options = core_component?["options"]
|
||||
return options?[option_id]
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_state(list/core_component, option_id)
|
||||
var/list/option_data = core_option_data(core_component, option_id)
|
||||
return option_data?["state"]
|
||||
|
||||
/datum/greyscale_component_style/proc/core_group_options(list/core_component, group_id) as /list
|
||||
var/list/groups = core_component?["groups"]
|
||||
var/list/group_options = groups?[group_id]
|
||||
return islist(group_options) ? group_options : null
|
||||
|
||||
/datum/greyscale_component_style/proc/core_option_matches(list/core_component, selected_option, target_option, match_mode)
|
||||
if(match_mode == COMPONENT_CORE_ALL)
|
||||
return TRUE
|
||||
if(match_mode == COMPONENT_CORE_EXACT)
|
||||
return selected_option == target_option
|
||||
if(match_mode != COMPONENT_CORE_BRANCH)
|
||||
return FALSE
|
||||
|
||||
var/current_option = selected_option
|
||||
while(current_option)
|
||||
if(current_option == target_option)
|
||||
return TRUE
|
||||
var/list/option_data = core_option_data(core_component, current_option)
|
||||
current_option = option_data?["parent"]
|
||||
return FALSE
|
||||
|
||||
/datum/greyscale_component_style/proc/core_rule_targets(list/core_component, rule_value, use_groups = FALSE) as /list
|
||||
if(islist(rule_value))
|
||||
return rule_value
|
||||
if(use_groups)
|
||||
return core_group_options(core_component, rule_value) || list()
|
||||
return list(rule_value)
|
||||
|
||||
/datum/greyscale_component_style/proc/core_apply_rule_matches(list/core_component, selected_option, rule_mode, rule_value)
|
||||
var/list/targets = core_rule_targets(core_component, rule_value, rule_mode == COMPONENT_CORE_GROUP)
|
||||
var/match_mode = (rule_mode == COMPONENT_CORE_GROUP) ? COMPONENT_CORE_EXACT : rule_mode
|
||||
for(var/target_option in targets)
|
||||
if(core_option_matches(core_component, selected_option, target_option, match_mode))
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/greyscale_component_style/proc/core_ui_data(list/selected_cores) as /list
|
||||
var/list/core_data = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/core_key = core_component["key"]
|
||||
core_data += list(list(
|
||||
"name" = core_component["name"],
|
||||
"key" = core_key,
|
||||
"options" = option_ui_data(core_component["options"], selected_cores[core_key]),
|
||||
))
|
||||
return core_data
|
||||
|
||||
/datum/greyscale_component_style/proc/option_ui_data(list/options, selected_value) as /list
|
||||
var/list/style_options = list()
|
||||
for(var/option_id in options)
|
||||
var/list/option_data = options[option_id]
|
||||
style_options += list(list(
|
||||
"name" = option_data["name"] || option_id,
|
||||
"value" = option_id,
|
||||
"selected" = (option_id == selected_value),
|
||||
))
|
||||
return style_options
|
||||
|
||||
/datum/greyscale_component_style/proc/accessory_ui_data(list/selected_accessories, list/selected_cores) as /list
|
||||
var/list/style_options = list()
|
||||
for(var/accessory_name in accessories)
|
||||
var/list/accessory_rules = accessories[accessory_name]
|
||||
var/accessory_state = accessory_rules["state"]
|
||||
if(!accessory_allowed(accessory_state, selected_cores))
|
||||
continue
|
||||
style_options += list(list(
|
||||
"name" = accessory_name,
|
||||
"value" = accessory_state,
|
||||
"selected" = (accessory_state in selected_accessories),
|
||||
))
|
||||
return style_options
|
||||
|
||||
/datum/greyscale_component_style/proc/accessory_allowed(accessory_state, list/selected_cores)
|
||||
var/list/accessory_rules = accessory_rules_for_state(accessory_state)
|
||||
if(isnull(accessory_rules))
|
||||
return FALSE
|
||||
|
||||
var/list/apply_rules = accessory_rules["applies_to"]
|
||||
if(!length(apply_rules))
|
||||
return TRUE
|
||||
|
||||
for(var/core_key in apply_rules)
|
||||
var/list/core_component = core_component_for_key(core_key)
|
||||
if(!core_component)
|
||||
return FALSE
|
||||
|
||||
var/selected_option = selected_cores?[core_key]
|
||||
if(!selected_option)
|
||||
return FALSE
|
||||
|
||||
var/apply_rule = apply_rules[core_key]
|
||||
if(apply_rule == COMPONENT_CORE_ALL)
|
||||
continue
|
||||
|
||||
var/matched_core = FALSE
|
||||
if(islist(apply_rule))
|
||||
for(var/rule_key in apply_rule)
|
||||
if(rule_key in list(COMPONENT_CORE_EXACT, COMPONENT_CORE_BRANCH, COMPONENT_CORE_GROUP))
|
||||
if(core_apply_rule_matches(core_component, selected_option, rule_key, apply_rule[rule_key]))
|
||||
matched_core = TRUE
|
||||
break
|
||||
continue
|
||||
|
||||
if(core_apply_rule_matches(core_component, selected_option, apply_rule[rule_key] || COMPONENT_CORE_EXACT, rule_key))
|
||||
matched_core = TRUE
|
||||
break
|
||||
else
|
||||
matched_core = core_option_matches(core_component, selected_option, apply_rule, COMPONENT_CORE_EXACT)
|
||||
|
||||
if(!matched_core)
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/datum/greyscale_component_style/proc/accessory_rules_for_state(accessory_state) as /list
|
||||
for(var/accessory_name in accessories)
|
||||
var/list/accessory_rules = accessories[accessory_name]
|
||||
if(accessory_rules["state"] == accessory_state)
|
||||
return accessory_rules
|
||||
return null
|
||||
|
||||
/datum/greyscale_component_style/proc/active_components(list/selected_cores, list/selected_accessories) as /list
|
||||
var/list/components = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/component_state = core_option_state(core_component, selected_cores?[core_component["key"]])
|
||||
if(component_state)
|
||||
components += component_state
|
||||
if(islist(selected_accessories))
|
||||
for(var/accessory_state in selected_accessories)
|
||||
if(!accessory_state)
|
||||
continue
|
||||
components += accessory_state
|
||||
return components
|
||||
|
||||
/datum/greyscale_component_style/proc/color_id_for_component(component_state)
|
||||
var/list/component_rules = component_rules_for_state(component_state)
|
||||
return component_rules?["color_id"]
|
||||
|
||||
/datum/greyscale_component_style/proc/component_rules_for_state(component_state) as /list
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/list/options = core_component["options"]
|
||||
for(var/option_id in options)
|
||||
var/list/option_data = options[option_id]
|
||||
if(option_data["state"] == component_state)
|
||||
return option_data
|
||||
for(var/accessory_name in accessories)
|
||||
var/list/accessory_rules = accessories[accessory_name]
|
||||
if(accessory_rules["state"] == component_state)
|
||||
return accessory_rules
|
||||
return null
|
||||
|
||||
/datum/greyscale_component_style/proc/active_color_ids(list/selected_cores, list/selected_accessories) as /list
|
||||
var/list/active_color_ids = list()
|
||||
for(var/component in active_components(selected_cores, selected_accessories))
|
||||
var/color_id = color_id_for_component(component)
|
||||
if(!color_id || (color_id in active_color_ids))
|
||||
continue
|
||||
active_color_ids += color_id
|
||||
return active_color_ids
|
||||
|
||||
/datum/greyscale_component_style/proc/active_color_labels(list/selected_cores, list/selected_accessories) as /list
|
||||
var/list/labels = list()
|
||||
for(var/component_state in active_components(selected_cores, selected_accessories))
|
||||
var/list/component_rules = component_rules_for_state(component_state)
|
||||
var/color_id = component_rules?["color_id"]
|
||||
var/color_label = component_rules?["color_label"]
|
||||
if(!color_id || !color_label)
|
||||
continue
|
||||
labels["[color_id]"] = color_label
|
||||
return labels
|
||||
|
||||
/datum/greyscale_component_style/proc/default_color_list(list/fallback_colors, expected_colors = 0) as /list
|
||||
var/list/colors = islist(fallback_colors) ? fallback_colors.Copy() : list()
|
||||
while(length(colors) < expected_colors)
|
||||
colors += null
|
||||
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/list/options = core_component["options"]
|
||||
for(var/option_id in options)
|
||||
apply_component_default_color(colors, options[option_id])
|
||||
for(var/accessory_name in accessories)
|
||||
apply_component_default_color(colors, accessories[accessory_name])
|
||||
|
||||
for(var/i in 1 to length(colors))
|
||||
if(!colors[i])
|
||||
colors[i] = rgb(100, 100, 100)
|
||||
return colors
|
||||
|
||||
/datum/greyscale_component_style/proc/apply_component_default_color(list/colors, list/component_rules)
|
||||
var/color_id = component_rules?["color_id"]
|
||||
var/default_color = component_rules?["default_color"]
|
||||
if(!color_id || !default_color)
|
||||
return
|
||||
while(length(colors) < color_id)
|
||||
colors += null
|
||||
colors[color_id] = default_color
|
||||
|
||||
/datum/greyscale_component_style/proc/greyscale_config_data(list/output_states, list/state_overrides) as /list
|
||||
var/list/layers = greyscale_config_layers(state_overrides)
|
||||
var/list/config_data = list()
|
||||
if(!length(output_states))
|
||||
output_states = list(output_icon_state)
|
||||
for(var/output_state in output_states)
|
||||
if(isnull(output_state))
|
||||
continue
|
||||
config_data[output_state] = layers
|
||||
return config_data
|
||||
|
||||
/datum/greyscale_component_style/proc/greyscale_config_layers(list/state_overrides) as /list
|
||||
var/list/layers = list()
|
||||
var/list/seen_states = list()
|
||||
for(var/component_state in greyscale_config_component_states())
|
||||
if(!component_state || seen_states[component_state])
|
||||
continue
|
||||
seen_states[component_state] = TRUE
|
||||
|
||||
var/list/component_rules = component_rules_for_state(component_state)
|
||||
var/layer_state = islist(state_overrides) ? (state_overrides[component_state] || component_state) : component_state
|
||||
var/list/layer_data = list(
|
||||
"type" = "icon_state",
|
||||
"icon_state" = layer_state,
|
||||
"blend_mode" = "overlay",
|
||||
)
|
||||
var/color_id = component_rules?["color_id"]
|
||||
if(color_id)
|
||||
layer_data["color_ids"] = list(color_id)
|
||||
layers += list(layer_data)
|
||||
return layers
|
||||
|
||||
/datum/greyscale_component_style/proc/greyscale_config_component_states() as /list
|
||||
var/list/component_states = list()
|
||||
for(var/list/core_component as anything in core_components)
|
||||
var/list/options = core_component["options"]
|
||||
for(var/option_id in options)
|
||||
component_states += options[option_id]["state"]
|
||||
for(var/accessory_name in accessories)
|
||||
component_states += accessories[accessory_name]["state"]
|
||||
return component_states
|
||||
|
||||
/datum/greyscale_config
|
||||
/// Optional dynamic component-style definition used to generate this config without a hand-written JSON file.
|
||||
var/greyscale_component_style_type
|
||||
/// Icon states this generated config should expose in its output bundle.
|
||||
var/list/greyscale_component_config_states
|
||||
/// Optional logical component state to real icon state mapping for this config's icon file.
|
||||
var/list/greyscale_component_config_state_overrides
|
||||
|
||||
/datum/greyscale_config/proc/InitializeGreyscaleComponentStyleConfig()
|
||||
if(!icon_file)
|
||||
stack_trace("Greyscale component-style config object [DebugName()] is missing an icon file, make sure `icon_file` has been assigned a value.")
|
||||
string_icon_file = "[icon_file]"
|
||||
if(!name)
|
||||
stack_trace("Greyscale component-style config object [DebugName()] is missing a name, make sure `name` has been assigned a value.")
|
||||
if(!greyscale_component_style_type)
|
||||
stack_trace("Greyscale component-style config object [DebugName()] is missing a component style type.")
|
||||
|
||||
/datum/greyscale_config/proc/RefreshGreyscaleComponentStyleConfig(loadFromDisk = FALSE)
|
||||
if(loadFromDisk)
|
||||
var/changed = FALSE
|
||||
|
||||
icon_file = file(string_icon_file)
|
||||
var/icon_hash = rustg_hash_file("md5", string_icon_file)
|
||||
if(icon_file_hash != icon_hash)
|
||||
icon_file_hash = icon_hash
|
||||
changed = TRUE
|
||||
|
||||
for(var/datum/greyscale_layer/layer as anything in flat_all_layers)
|
||||
if(layer.DiskRefresh())
|
||||
changed = TRUE
|
||||
|
||||
if(!changed)
|
||||
return FALSE
|
||||
|
||||
var/style_type = greyscale_component_style_type
|
||||
var/datum/greyscale_component_style/component_style = new style_type
|
||||
var/list/raw = component_style.greyscale_config_data(greyscale_component_config_states, greyscale_component_config_state_overrides)
|
||||
raw_json_string = json_encode(raw)
|
||||
qdel(component_style)
|
||||
|
||||
ReadIconStateConfiguration(raw)
|
||||
if(!length(icon_states))
|
||||
CRASH("The component-style greyscale configuration [DebugName()] doesn't have any icon states.")
|
||||
|
||||
icon_cache = list()
|
||||
ReadMetadata()
|
||||
|
||||
SEND_SIGNAL(src, COMSIG_GREYSCALE_CONFIG_REFRESHED)
|
||||
return TRUE
|
||||
|
||||
/datum/greyscale_component_style/proc/build_icon(icon_file, list/colors, list/selected_cores, list/selected_accessories, use_digi = FALSE, digi_fallback_icon_file = null, only_dir = null, list/state_overrides = null, use_adjusted = FALSE)
|
||||
var/icon/final_icon = icon('icons/effects/effects.dmi', "nothing")
|
||||
final_icon.Scale(32, 32)
|
||||
if(!icon_file)
|
||||
return final_icon
|
||||
|
||||
var/icon/built_icon
|
||||
for(var/component in active_components(selected_cores, selected_accessories))
|
||||
if(!component)
|
||||
continue
|
||||
var/layer_file = icon_file
|
||||
var/state_to_use = component
|
||||
if(islist(state_overrides))
|
||||
state_to_use = state_overrides[component] || component
|
||||
|
||||
var/list/candidate_states = list()
|
||||
if(use_digi && use_adjusted)
|
||||
candidate_states += "[state_to_use][digi_suffix][adjusted_suffix]"
|
||||
if(use_adjusted)
|
||||
candidate_states += "[state_to_use][adjusted_suffix]"
|
||||
if(use_digi)
|
||||
candidate_states += "[state_to_use][digi_suffix]"
|
||||
candidate_states += state_to_use
|
||||
|
||||
var/list/candidate_files = list(layer_file)
|
||||
if(use_digi && digi_fallback_icon_file)
|
||||
candidate_files += digi_fallback_icon_file
|
||||
|
||||
var/found_component_state = FALSE
|
||||
for(var/candidate_state in candidate_states)
|
||||
for(var/candidate_file in candidate_files)
|
||||
if(!icon_exists(candidate_file, candidate_state))
|
||||
continue
|
||||
layer_file = candidate_file
|
||||
state_to_use = candidate_state
|
||||
found_component_state = TRUE
|
||||
break
|
||||
if(found_component_state)
|
||||
break
|
||||
|
||||
if(!found_component_state)
|
||||
continue
|
||||
|
||||
var/icon/component_icon = isnull(only_dir) ? icon(layer_file, state_to_use) : icon(layer_file, state_to_use, only_dir)
|
||||
var/color_id = color_id_for_component(component)
|
||||
if(color_id && length(colors) >= color_id && colors[color_id])
|
||||
component_icon.Blend(colors[color_id], ICON_MULTIPLY)
|
||||
|
||||
if(isnull(built_icon))
|
||||
built_icon = component_icon
|
||||
else
|
||||
built_icon.Blend(component_icon, ICON_OVERLAY)
|
||||
|
||||
if(isnull(built_icon) && fallback_icon_state)
|
||||
built_icon = isnull(only_dir) ? icon(icon_file, fallback_icon_state) : icon(icon_file, fallback_icon_state, only_dir)
|
||||
|
||||
if(!isnull(built_icon))
|
||||
var/generated_icon_state = use_adjusted ? "[output_icon_state][adjusted_suffix]" : output_icon_state
|
||||
final_icon.Insert(built_icon, generated_icon_state)
|
||||
return final_icon
|
||||
|
||||
/obj/item
|
||||
/// Optional component-style GAGS definition used for layered, selectable components.
|
||||
var/greyscale_component_style_type
|
||||
/// Selected core option ids, keyed by core component id.
|
||||
var/list/greyscale_component_cores
|
||||
/// Selected optional component states.
|
||||
var/list/greyscale_component_accessories
|
||||
/// Object icon file used for component-style GAGS composition.
|
||||
var/greyscale_component_icon_file
|
||||
/// Worn icon file used for component-style GAGS composition.
|
||||
var/greyscale_component_worn_icon_file
|
||||
/// Digitigrade worn icon file used for component-style GAGS composition.
|
||||
var/greyscale_component_worn_digi_icon_file
|
||||
/// Optional non-digi worn icon fallback for missing digitigrade component states.
|
||||
var/greyscale_component_digi_fallback_icon_file
|
||||
/// Whether component-style worn icons should use adjusted/casual component states.
|
||||
var/greyscale_component_use_adjusted = FALSE
|
||||
/// Optional logical component state to worn icon state mapping.
|
||||
var/list/greyscale_component_worn_state_overrides
|
||||
/// Optional logical component state to digitigrade worn icon state mapping.
|
||||
var/list/greyscale_component_worn_digi_state_overrides
|
||||
|
||||
/obj/item/proc/get_greyscale_component_style() as /datum/greyscale_component_style
|
||||
if(!greyscale_component_style_type)
|
||||
return null
|
||||
return new greyscale_component_style_type
|
||||
|
||||
/obj/item/proc/initialize_greyscale_component_style()
|
||||
var/datum/greyscale_component_style/component_style = get_greyscale_component_style()
|
||||
if(!component_style)
|
||||
return
|
||||
greyscale_component_cores = component_style.sanitize_core_selection(greyscale_component_cores)
|
||||
var/list/accessories_to_apply = isnull(greyscale_component_accessories) ? component_style.default_accessories : greyscale_component_accessories
|
||||
greyscale_component_accessories = component_style.sanitize_accessories(accessories_to_apply, greyscale_component_cores)
|
||||
qdel(component_style)
|
||||
|
||||
/obj/item/proc/apply_greyscale_component_style(list/item_details)
|
||||
var/datum/greyscale_component_style/component_style = get_greyscale_component_style()
|
||||
if(!component_style)
|
||||
return
|
||||
greyscale_component_cores = component_style.sanitize_core_selection(item_details?[INFO_GREYSCALE_COMPONENT_CORES] || greyscale_component_cores)
|
||||
var/list/accessories_to_apply = item_details?[INFO_GREYSCALE_COMPONENT_ACCESSORIES]
|
||||
if(isnull(accessories_to_apply))
|
||||
accessories_to_apply = isnull(greyscale_component_accessories) ? component_style.default_accessories : greyscale_component_accessories
|
||||
greyscale_component_accessories = component_style.sanitize_accessories(accessories_to_apply, greyscale_component_cores)
|
||||
qdel(component_style)
|
||||
update_greyscale()
|
||||
|
||||
/obj/item/proc/update_greyscale_component_icons()
|
||||
var/datum/greyscale_component_style/component_style = get_greyscale_component_style()
|
||||
if(!component_style || !greyscale_colors)
|
||||
qdel(component_style)
|
||||
return
|
||||
|
||||
var/list/colors = SSgreyscale.ParseColorString(greyscale_colors)
|
||||
icon_state = component_style.output_icon_state
|
||||
worn_icon_state = greyscale_component_use_adjusted ? "[component_style.output_icon_state][component_style.adjusted_suffix]" : component_style.output_icon_state
|
||||
greyscale_config_worn = null
|
||||
greyscale_config_worn_digi = null
|
||||
if(greyscale_component_icon_file)
|
||||
icon = component_style.build_icon(greyscale_component_icon_file, colors, greyscale_component_cores, greyscale_component_accessories)
|
||||
if(greyscale_component_worn_icon_file)
|
||||
worn_icon = component_style.build_icon(greyscale_component_worn_icon_file, colors, greyscale_component_cores, greyscale_component_accessories, state_overrides = greyscale_component_worn_state_overrides, use_adjusted = greyscale_component_use_adjusted)
|
||||
if(greyscale_component_worn_digi_icon_file)
|
||||
worn_icon_digi = component_style.build_icon(greyscale_component_worn_digi_icon_file, colors, greyscale_component_cores, greyscale_component_accessories, use_digi = TRUE, digi_fallback_icon_file = greyscale_component_digi_fallback_icon_file, state_overrides = greyscale_component_worn_digi_state_overrides || greyscale_component_worn_state_overrides, use_adjusted = greyscale_component_use_adjusted)
|
||||
qdel(component_style)
|
||||
|
||||
var/obj/item/clothing/worn_clothing = src
|
||||
if(!istype(worn_clothing))
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/wearer = worn_clothing.loc
|
||||
if(!istype(wearer))
|
||||
return
|
||||
wearer.update_clothing(worn_clothing.slot_flags)
|
||||
|
||||
/obj/item/clothing/under/adjust_to_normal()
|
||||
. = ..()
|
||||
if(!greyscale_component_style_type)
|
||||
return
|
||||
greyscale_component_use_adjusted = FALSE
|
||||
update_greyscale_component_icons()
|
||||
|
||||
/obj/item/clothing/under/adjust_to_alt()
|
||||
. = ..()
|
||||
if(!greyscale_component_style_type)
|
||||
return
|
||||
greyscale_component_use_adjusted = TRUE
|
||||
update_greyscale_component_icons()
|
||||
|
||||
/datum/loadout_item/proc/uses_greyscale_component_style()
|
||||
return !!initial(item_path.greyscale_component_style_type)
|
||||
|
||||
/datum/loadout_item
|
||||
/// Optional icon file used for component-style color previews instead of the item's worn preview.
|
||||
var/greyscale_component_preview_icon_file
|
||||
/// Optional logical component state to preview icon state mapping.
|
||||
var/list/greyscale_component_preview_state_overrides
|
||||
|
||||
/// Opens a component-style GAGS editing menu when the loadout item supports it.
|
||||
/datum/loadout_item/proc/set_component_style_item_color(datum/preference_middleware/loadout/manager, mob/user)
|
||||
if(manager.menu)
|
||||
return FALSE
|
||||
|
||||
var/list/loadout = manager.get_current_loadout()
|
||||
var/list/allowed_configs = list()
|
||||
if(initial(item_path.greyscale_config_worn))
|
||||
allowed_configs += "[initial(item_path.greyscale_config_worn)]"
|
||||
else if(initial(item_path.greyscale_config))
|
||||
allowed_configs += "[initial(item_path.greyscale_config)]"
|
||||
|
||||
var/list/selected_cores = loadout?[item_path]?[INFO_GREYSCALE_COMPONENT_CORES]
|
||||
if(isnull(selected_cores))
|
||||
selected_cores = initial(item_path.greyscale_component_cores)
|
||||
|
||||
var/list/selected_accessories = loadout?[item_path]?[INFO_GREYSCALE_COMPONENT_ACCESSORIES]
|
||||
if(islist(selected_accessories) && (/datum/greyscale_component_style::no_accessories_marker in selected_accessories))
|
||||
selected_accessories = list()
|
||||
|
||||
var/starting_config = initial(item_path.greyscale_config_worn) || initial(item_path.greyscale_config)
|
||||
var/starting_colors = loadout?[item_path]?[INFO_GREYSCALE] || initial(item_path.greyscale_colors)
|
||||
var/datum/greyscale_config/selected_config = SSgreyscale.configurations["[starting_config]"]
|
||||
if(selected_config && length(SSgreyscale.ParseColorString(starting_colors)) != selected_config.expected_colors)
|
||||
starting_colors = initial(item_path.greyscale_colors)
|
||||
|
||||
var/preview_icon_file = greyscale_component_preview_icon_file || initial(item_path.greyscale_component_worn_icon_file) || initial(item_path.greyscale_component_icon_file)
|
||||
var/list/preview_state_overrides = greyscale_component_preview_state_overrides
|
||||
if(!preview_state_overrides && preview_icon_file == initial(item_path.greyscale_component_worn_icon_file))
|
||||
preview_state_overrides = initial(item_path.greyscale_component_worn_state_overrides)
|
||||
|
||||
var/datum/greyscale_modify_menu/component_style/menu = new(
|
||||
manager,
|
||||
user,
|
||||
allowed_configs,
|
||||
CALLBACK(src, PROC_REF(set_slot_greyscale), manager),
|
||||
starting_icon_state = initial(item_path.icon_state),
|
||||
starting_config = starting_config,
|
||||
starting_colors = starting_colors,
|
||||
component_style_type = initial(item_path.greyscale_component_style_type),
|
||||
selected_cores = selected_cores,
|
||||
selected_accessories = selected_accessories,
|
||||
preview_icon_file = preview_icon_file,
|
||||
preview_digi_fallback_icon_file = initial(item_path.greyscale_component_digi_fallback_icon_file),
|
||||
preview_state_overrides = preview_state_overrides,
|
||||
default_colors = initial(item_path.greyscale_colors),
|
||||
default_cores = initial(item_path.greyscale_component_cores),
|
||||
default_accessories = null,
|
||||
)
|
||||
|
||||
manager.register_greyscale_menu(menu)
|
||||
menu.ui_interact(user)
|
||||
return TRUE
|
||||
|
||||
/// Saves component-style GAGS color and component state.
|
||||
/datum/loadout_item/proc/set_component_style_slot_greyscale(datum/preference_middleware/loadout/manager, datum/greyscale_modify_menu/open_menu)
|
||||
var/datum/greyscale_modify_menu/component_style/component_menu = open_menu
|
||||
if(!istype(component_menu))
|
||||
return FALSE
|
||||
|
||||
var/list/loadout = manager.get_current_loadout()
|
||||
if(!loadout?[item_path])
|
||||
return FALSE
|
||||
|
||||
var/list/colors = component_menu.split_colors
|
||||
if(!colors)
|
||||
return FALSE
|
||||
|
||||
loadout[item_path][INFO_GREYSCALE] = colors.Join("")
|
||||
loadout[item_path][INFO_GREYSCALE_COMPONENT_CORES] = component_menu.selected_cores.Copy()
|
||||
loadout[item_path][INFO_GREYSCALE_COMPONENT_ACCESSORIES] = length(component_menu.selected_accessories) ? component_menu.selected_accessories.Copy() : list(/datum/greyscale_component_style::no_accessories_marker)
|
||||
manager.save_current_loadout(loadout)
|
||||
return TRUE
|
||||
|
||||
/datum/loadout_item/proc/apply_component_style_to_equipped_item(obj/item/equipped_item, list/item_details)
|
||||
if(equipped_item?.greyscale_component_style_type)
|
||||
equipped_item.apply_greyscale_component_style(item_details)
|
||||
|
||||
/proc/get_component_style_item_preview_image(obj/item/item_path)
|
||||
if(item_path == /obj/item/clothing/head/soft/sec/recolorable)
|
||||
var/icon/preview_icon = icon('icons/obj/clothing/head/hats.dmi', "secsoft")
|
||||
return icon2base64(preview_icon)
|
||||
|
||||
var/component_style_type = initial(item_path.greyscale_component_style_type)
|
||||
if(!component_style_type)
|
||||
return null
|
||||
var/datum/greyscale_component_style/component_style = new component_style_type
|
||||
var/list/default_cores = component_style.sanitize_core_selection(initial(item_path.greyscale_component_cores))
|
||||
var/list/default_accessories = component_style.default_accessories
|
||||
default_accessories = component_style.sanitize_accessories(default_accessories, default_cores)
|
||||
var/list/default_colors = component_style.default_color_list(SSgreyscale.ParseColorString(initial(item_path.greyscale_colors)))
|
||||
var/preview_icon_file = initial(item_path.greyscale_component_worn_icon_file) || initial(item_path.greyscale_component_icon_file)
|
||||
var/list/preview_state_overrides
|
||||
if(preview_icon_file == initial(item_path.greyscale_component_worn_icon_file))
|
||||
preview_state_overrides = initial(item_path.greyscale_component_worn_state_overrides)
|
||||
var/icon/south_preview_icon = component_style.build_icon(preview_icon_file, default_colors, default_cores, default_accessories, only_dir = SOUTH, state_overrides = preview_state_overrides)
|
||||
var/icon/single_frame_preview = icon(south_preview_icon, component_style.output_icon_state, SOUTH, 1)
|
||||
qdel(component_style)
|
||||
return icon2base64(single_frame_preview)
|
||||
|
||||
/datum/loadout_item/proc/get_component_style_preview_image()
|
||||
return get_component_style_item_preview_image(item_path)
|
||||
|
||||
/datum/loadout_item/proc/add_component_style_preview_to_ui_data(list/formatted_item)
|
||||
if(!uses_greyscale_component_style())
|
||||
return
|
||||
formatted_item["image"] = get_component_style_preview_image()
|
||||
|
||||
/obj/machinery/vending/proc/get_component_style_vending_preview_image(obj/item/item_path)
|
||||
return get_component_style_item_preview_image(item_path)
|
||||
|
||||
/obj/machinery/vending
|
||||
/// Temporary component-style selections keyed by vending user ref while the normal vend path creates the item.
|
||||
var/list/greyscale_component_style_vend_details
|
||||
|
||||
/obj/machinery/vending/proc/open_component_style_vending_menu(datum/data/vending_product/product, list/params, mob/user)
|
||||
var/obj/item/item_path = product.product_path
|
||||
var/list/allowed_configs = list()
|
||||
if(initial(item_path.greyscale_config_worn))
|
||||
allowed_configs += "[initial(item_path.greyscale_config_worn)]"
|
||||
else if(initial(item_path.greyscale_config))
|
||||
allowed_configs += "[initial(item_path.greyscale_config)]"
|
||||
|
||||
var/preview_icon_file = initial(item_path.greyscale_component_worn_icon_file) || initial(item_path.greyscale_component_icon_file)
|
||||
var/list/preview_state_overrides
|
||||
if(preview_icon_file == initial(item_path.greyscale_component_worn_icon_file))
|
||||
preview_state_overrides = initial(item_path.greyscale_component_worn_state_overrides)
|
||||
|
||||
var/datum/greyscale_modify_menu/component_style/menu = new(
|
||||
src,
|
||||
user,
|
||||
allowed_configs,
|
||||
CALLBACK(src, PROC_REF(_vend_component_style), params, user),
|
||||
starting_icon_state = initial(item_path.icon_state),
|
||||
starting_config = initial(item_path.greyscale_config_worn) || initial(item_path.greyscale_config),
|
||||
starting_colors = initial(item_path.greyscale_colors),
|
||||
component_style_type = initial(item_path.greyscale_component_style_type),
|
||||
selected_cores = initial(item_path.greyscale_component_cores),
|
||||
selected_accessories = initial(item_path.greyscale_component_accessories),
|
||||
preview_icon_file = preview_icon_file,
|
||||
preview_digi_fallback_icon_file = initial(item_path.greyscale_component_digi_fallback_icon_file),
|
||||
preview_state_overrides = preview_state_overrides,
|
||||
default_colors = initial(item_path.greyscale_colors),
|
||||
default_cores = initial(item_path.greyscale_component_cores),
|
||||
default_accessories = initial(item_path.greyscale_component_accessories),
|
||||
)
|
||||
menu.ui_interact(user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/vending/proc/_vend_component_style(list/params, mob/user, datum/greyscale_modify_menu/menu)
|
||||
PRIVATE_PROC(TRUE)
|
||||
|
||||
var/datum/greyscale_modify_menu/component_style/component_menu = menu
|
||||
if(user != menu.user || !istype(component_menu))
|
||||
return
|
||||
|
||||
var/list/component_details = list(
|
||||
INFO_GREYSCALE_COMPONENT_CORES = component_menu.selected_cores.Copy(),
|
||||
INFO_GREYSCALE_COMPONENT_ACCESSORIES = length(component_menu.selected_accessories) ? component_menu.selected_accessories.Copy() : list(/datum/greyscale_component_style::no_accessories_marker),
|
||||
)
|
||||
if(!greyscale_component_style_vend_details)
|
||||
greyscale_component_style_vend_details = list()
|
||||
var/user_ref = REF(user)
|
||||
greyscale_component_style_vend_details[user_ref] = component_details
|
||||
vend(params, user, component_menu.split_colors)
|
||||
greyscale_component_style_vend_details -= user_ref
|
||||
if(!length(greyscale_component_style_vend_details))
|
||||
greyscale_component_style_vend_details = null
|
||||
|
||||
/datum/greyscale_modify_menu/component_style
|
||||
var/datum/greyscale_component_style/component_style
|
||||
var/component_style_type
|
||||
var/list/selected_cores
|
||||
var/list/selected_accessories
|
||||
var/preview_icon_file
|
||||
var/preview_use_digi = FALSE
|
||||
var/preview_digi_fallback_icon_file
|
||||
var/list/preview_state_overrides
|
||||
var/list/default_colors
|
||||
var/list/default_cores
|
||||
var/list/default_accessories
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/New(datum/target, client/user, list/allowed_configs, datum/callback/apply_callback, starting_icon_state = "", starting_config, starting_colors, unlocked = FALSE, component_style_type = null, list/selected_cores = null, list/selected_accessories = null, preview_icon_file = null, preview_use_digi = FALSE, preview_digi_fallback_icon_file = null, list/preview_state_overrides = null, default_colors = null, list/default_cores = null, list/default_accessories = null)
|
||||
src.component_style_type = component_style_type || /datum/greyscale_component_style
|
||||
var/style_type = src.component_style_type
|
||||
src.component_style = new style_type
|
||||
src.selected_cores = component_style.sanitize_core_selection(selected_cores)
|
||||
if(isnull(selected_accessories))
|
||||
selected_accessories = component_style.default_accessories
|
||||
src.selected_accessories = component_style.sanitize_accessories(selected_accessories, src.selected_cores)
|
||||
src.preview_icon_file = preview_icon_file
|
||||
src.preview_use_digi = preview_use_digi
|
||||
src.preview_digi_fallback_icon_file = preview_digi_fallback_icon_file
|
||||
src.preview_state_overrides = preview_state_overrides
|
||||
src.default_colors = component_style.default_color_list(SSgreyscale.ParseColorString(default_colors || starting_colors))
|
||||
src.default_cores = component_style.sanitize_core_selection(default_cores)
|
||||
if(isnull(default_accessories))
|
||||
default_accessories = component_style.default_accessories
|
||||
src.default_accessories = component_style.sanitize_accessories(default_accessories, src.default_cores)
|
||||
sprite_data = list(
|
||||
"icon_states" = list(component_style.output_icon_state),
|
||||
"finished" = null,
|
||||
"steps" = list(),
|
||||
"time_spent" = 0,
|
||||
)
|
||||
update_component_color_labels()
|
||||
return ..()
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/Destroy()
|
||||
QDEL_NULL(component_style)
|
||||
selected_cores = null
|
||||
selected_accessories = null
|
||||
preview_state_overrides = null
|
||||
default_colors = null
|
||||
default_cores = null
|
||||
default_accessories = null
|
||||
return ..()
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/ReadColorsFromString(colorString)
|
||||
if(..())
|
||||
return TRUE
|
||||
normalize_split_colors()
|
||||
return TRUE
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/ui_data(mob/user)
|
||||
normalize_split_colors()
|
||||
var/list/data = list()
|
||||
data["greyscale_config"] = "[config.name]"
|
||||
data["full_color_string"] = split_colors.Join("")
|
||||
|
||||
var/list/active_color_ids = component_style.active_color_ids(selected_cores, selected_accessories)
|
||||
var/list/active_colors = list()
|
||||
for(var/i in 1 to config.expected_colors)
|
||||
if(!(i in active_color_ids))
|
||||
continue
|
||||
active_colors += list(list(
|
||||
"index" = i,
|
||||
"label" = color_labels?["[i]"],
|
||||
"value" = (length(split_colors) >= i) ? split_colors[i] : rgb(100, 100, 100),
|
||||
))
|
||||
data["colors"] = active_colors
|
||||
|
||||
data["generate_full_preview"] = generate_full_preview
|
||||
data["unlocked"] = unlocked
|
||||
data["refreshing"] = refreshing
|
||||
data["monitoring_files"] = !!(config.datum_flags & DF_ISPROCESSING)
|
||||
data["sprites_dir"] = dir2text(sprite_dir)
|
||||
data["icon_state"] = icon_state
|
||||
data["sprites"] = sprite_data || list(
|
||||
"icon_states" = list(component_style.output_icon_state),
|
||||
"finished" = null,
|
||||
"steps" = list(),
|
||||
"time_spent" = 0,
|
||||
)
|
||||
data["hide_full_color_string"] = FALSE
|
||||
data["component_style"] = list(
|
||||
"core_components" = component_style.core_ui_data(selected_cores),
|
||||
"accessories" = component_style.accessory_ui_data(selected_accessories, selected_cores),
|
||||
)
|
||||
return data
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
|
||||
switch(action)
|
||||
if("component_style_set_core")
|
||||
var/core_component = params["component"]
|
||||
var/new_style = params["value"]
|
||||
if(!set_core_component(core_component, new_style))
|
||||
return TRUE
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("component_style_toggle_accessory")
|
||||
var/accessory = params["accessory"]
|
||||
if(!component_style.accessory_allowed(accessory, selected_cores))
|
||||
return TRUE
|
||||
if(accessory in selected_accessories)
|
||||
selected_accessories -= accessory
|
||||
else
|
||||
selected_accessories += accessory
|
||||
update_component_color_labels()
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("random_all_colors")
|
||||
for(var/color_id in component_style.active_color_ids(selected_cores, selected_accessories))
|
||||
randomize_color(color_id)
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("component_style_reset_color")
|
||||
var/color_id = text2num(params["color_index"])
|
||||
if(color_id && length(default_colors) >= color_id)
|
||||
normalize_split_colors()
|
||||
split_colors[color_id] = default_colors[color_id]
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
if("component_style_reset_all")
|
||||
selected_cores = default_cores.Copy()
|
||||
selected_accessories = default_accessories.Copy()
|
||||
split_colors = islist(default_colors) ? default_colors.Copy() : list()
|
||||
normalize_split_colors()
|
||||
update_component_color_labels()
|
||||
queue_refresh()
|
||||
return TRUE
|
||||
|
||||
return ..()
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/set_core_component(core_key, new_style)
|
||||
for(var/list/core_component as anything in component_style.core_components)
|
||||
if(core_component["key"] != core_key)
|
||||
continue
|
||||
if(!component_style.core_option_exists(core_component, new_style))
|
||||
return FALSE
|
||||
selected_cores[core_key] = new_style
|
||||
prune_invalid_accessories()
|
||||
update_component_color_labels()
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/prune_invalid_accessories()
|
||||
selected_accessories = component_style.sanitize_accessories(selected_accessories, selected_cores)
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/update_component_color_labels()
|
||||
color_labels = component_style.active_color_labels(selected_cores, selected_accessories)
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/proc/normalize_split_colors()
|
||||
if(!config)
|
||||
return
|
||||
if(!islist(split_colors))
|
||||
split_colors = list()
|
||||
if(length(split_colors) > config.expected_colors)
|
||||
split_colors.Cut(config.expected_colors + 1)
|
||||
while(length(split_colors) < config.expected_colors)
|
||||
var/next_color_index = length(split_colors) + 1
|
||||
split_colors += (islist(default_colors) && length(default_colors) >= next_color_index) ? default_colors[next_color_index] : rgb(100, 100, 100)
|
||||
|
||||
/datum/greyscale_modify_menu/component_style/refresh_preview()
|
||||
normalize_split_colors()
|
||||
var/list/used_colors = split_colors.Copy(1, config.expected_colors + 1)
|
||||
|
||||
sprite_data = list()
|
||||
sprite_data["icon_states"] = list(component_style.output_icon_state)
|
||||
icon_state = component_style.output_icon_state
|
||||
|
||||
var/time_spent = TICK_USAGE
|
||||
var/icon/preview_icon = component_style.build_icon(preview_icon_file, used_colors, selected_cores, selected_accessories, preview_use_digi, preview_digi_fallback_icon_file, only_dir = sprite_dir, state_overrides = preview_state_overrides)
|
||||
var/image/finished = image(preview_icon, icon_state = icon_state)
|
||||
time_spent = TICK_USAGE - time_spent
|
||||
|
||||
sprite_data["time_spent"] = TICK_DELTA_TO_MS(time_spent)
|
||||
sprite_data["finished"] = icon2html(finished, user, dir = sprite_dir, sourceonly = TRUE)
|
||||
sprite_data["steps"] = list()
|
||||
refreshing = FALSE
|
||||
@@ -76,6 +76,22 @@
|
||||
item_path = /obj/item/clothing/head/soft/sec
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable
|
||||
name = "Recolorable Security Cap"
|
||||
item_path = /obj/item/clothing/head/soft/sec/recolorable
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
greyscale_component_preview_icon_file = 'modular_zubbers/icons/obj/clothing/head/hats.dmi'
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable/set_item_color(datum/preference_middleware/loadout/manager, mob/user)
|
||||
return set_component_style_item_color(manager, user)
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable/set_slot_greyscale(datum/preference_middleware/loadout/manager, datum/greyscale_modify_menu/open_menu)
|
||||
return set_component_style_slot_greyscale(manager, open_menu)
|
||||
|
||||
/datum/loadout_item/head/security_cap/recolorable/on_equip_item(obj/item/equipped_item, list/item_details, mob/living/carbon/human/equipper, datum/outfit/outfit, visuals_only = FALSE)
|
||||
. = ..()
|
||||
apply_component_style_to_equipped_item(equipped_item, item_details)
|
||||
|
||||
/datum/loadout_item/head/security_cap/veteran
|
||||
name = "Veteran's Soft Security Cap"
|
||||
item_path = /obj/item/clothing/head/soft/veteran
|
||||
|
||||
@@ -80,6 +80,21 @@
|
||||
item_path = /obj/item/clothing/under/rank/security/officer/redsec
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable
|
||||
name = "Recolorable Security Uniform"
|
||||
item_path = /obj/item/clothing/under/rank/security/officer/recolorable
|
||||
restricted_roles = list(ALL_JOBS_SEC)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable/set_item_color(datum/preference_middleware/loadout/manager, mob/user)
|
||||
return set_component_style_item_color(manager, user)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable/set_slot_greyscale(datum/preference_middleware/loadout/manager, datum/greyscale_modify_menu/open_menu)
|
||||
return set_component_style_slot_greyscale(manager, open_menu)
|
||||
|
||||
/datum/loadout_item/uniform/security/officer/recolorable/on_equip_item(obj/item/equipped_item, list/item_details, mob/living/carbon/human/equipper, datum/outfit/outfit, visuals_only = FALSE)
|
||||
. = ..()
|
||||
apply_component_style_to_equipped_item(equipped_item, item_details)
|
||||
|
||||
/datum/loadout_item/uniform/security/peacekeeper/armadyne
|
||||
name = "Armadyne Corporate Uniform"
|
||||
item_path =/obj/item/clothing/under/rank/security/peacekeeper/armadyne
|
||||
|
||||
@@ -165,3 +165,73 @@
|
||||
#undef LUSTWISH_GAUNTLET_UNSEAL
|
||||
#undef LUSTWISH_BOOT_SEAL
|
||||
#undef LUSTWISH_BOOT_UNSEAL
|
||||
|
||||
// VOSKHOD MOD, ported from Nova. By Vinylspiders
|
||||
|
||||
/datum/mod_theme/voskhod
|
||||
name = "refitted voskhod"
|
||||
desc = "An upgraded set of aging VOSKHOD armor, brought up to more modern MODular standards."
|
||||
extended_desc = "While Modular Suits are still somewhat of a rare sight within the PSC, many client nations within the CIN itself have needed to refit aging suits of VOSKHOD suits into something much more modern. This package provides said modernized alternative, with MODular suit technology baked in. Often done with a specialized fabircation system, the same effects can be done with somewhat rare aftermarket parts kits, and quite a bit of elbow grease."
|
||||
default_skin = "voskhod"
|
||||
armor_type = /datum/armor/mod_theme_voskhod
|
||||
complexity_max = DEFAULT_MAX_COMPLEXITY //Five of which is occupied by the in-builts, thus it's closer to 10
|
||||
charge_drain = DEFAULT_CHARGE_DRAIN * 1.5
|
||||
allowed_suit_storage = list(
|
||||
/obj/item/flashlight,
|
||||
/obj/item/tank/internals,
|
||||
/obj/item/ammo_box,
|
||||
/obj/item/ammo_casing,
|
||||
/obj/item/restraints/handcuffs,
|
||||
/obj/item/assembly/flash,
|
||||
/obj/item/melee/baton,
|
||||
/obj/item/knife/combat,
|
||||
/obj/item/shield/riot,
|
||||
/obj/item/gun,
|
||||
)
|
||||
variants = list(
|
||||
"voskhod" = list(
|
||||
MOD_ICON_OVERRIDE = 'modular_zubbers/icons/obj/clothing/modsuit/mod_clothing.dmi',
|
||||
MOD_WORN_ICON_OVERRIDE = 'modular_zubbers/icons/mob/clothing/modsuit/mod_clothing.dmi',
|
||||
/obj/item/clothing/head/mod = list(
|
||||
UNSEALED_LAYER = HEAD_LAYER,
|
||||
UNSEALED_CLOTHING = SNUG_FIT,
|
||||
SEALED_CLOTHING = THICKMATERIAL|STOPSPRESSUREDAMAGE,
|
||||
SEALED_INVISIBILITY = HIDEFACIALHAIR|HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDESNOUT,
|
||||
SEALED_COVER = HEADCOVERSMOUTH|HEADCOVERSEYES|PEPPERPROOF,
|
||||
UNSEALED_MESSAGE = HELMET_UNSEAL_MESSAGE,
|
||||
SEALED_MESSAGE = HELMET_SEAL_MESSAGE,
|
||||
),
|
||||
/obj/item/clothing/suit/mod = list(
|
||||
UNSEALED_CLOTHING = THICKMATERIAL,
|
||||
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
|
||||
SEALED_INVISIBILITY = HIDEJUMPSUIT|HIDETAIL,
|
||||
UNSEALED_MESSAGE = CHESTPLATE_UNSEAL_MESSAGE,
|
||||
SEALED_MESSAGE = CHESTPLATE_SEAL_MESSAGE,
|
||||
),
|
||||
/obj/item/clothing/gloves/mod = list(
|
||||
UNSEALED_CLOTHING = THICKMATERIAL,
|
||||
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
|
||||
CAN_OVERSLOT = TRUE,
|
||||
UNSEALED_MESSAGE = GAUNTLET_UNSEAL_MESSAGE,
|
||||
SEALED_MESSAGE = GAUNTLET_SEAL_MESSAGE,
|
||||
),
|
||||
/obj/item/clothing/shoes/mod = list(
|
||||
UNSEALED_CLOTHING = THICKMATERIAL,
|
||||
SEALED_CLOTHING = STOPSPRESSUREDAMAGE,
|
||||
CAN_OVERSLOT = TRUE,
|
||||
UNSEALED_MESSAGE = BOOT_UNSEAL_MESSAGE,
|
||||
SEALED_MESSAGE = BOOT_SEAL_MESSAGE,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
/datum/armor/mod_theme_voskhod
|
||||
melee = 30
|
||||
bullet = 30
|
||||
laser = 30
|
||||
energy = 30
|
||||
bomb = 30
|
||||
bio = 30
|
||||
fire = 80
|
||||
acid = 85
|
||||
wound = 20
|
||||
|
||||
@@ -35,3 +35,18 @@
|
||||
/obj/item/mod/module/hat_stabilizer,
|
||||
)
|
||||
return ..()
|
||||
|
||||
/obj/item/mod/control/pre_equipped/voskhod
|
||||
applied_cell = /obj/item/stock_parts/power_store/cell/high
|
||||
theme = /datum/mod_theme/voskhod
|
||||
applied_modules = list(
|
||||
/obj/item/mod/module/storage,
|
||||
/obj/item/mod/module/flashlight,
|
||||
/obj/item/mod/module/auto_doc,
|
||||
/obj/item/mod/module/status_readout/operational,
|
||||
)
|
||||
default_pins = list(
|
||||
/obj/item/mod/module/flashlight,
|
||||
/obj/item/mod/module/auto_doc,
|
||||
)
|
||||
|
||||
|
||||
@@ -13,6 +13,21 @@
|
||||
icon_state = "security"
|
||||
worn_icon_state = "security"
|
||||
|
||||
/obj/item/storage/belt/security/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_belt, infinite = FALSE)
|
||||
|
||||
/datum/atom_skin/sec_belt
|
||||
abstract_type = /datum/atom_skin/sec_belt
|
||||
|
||||
/datum/atom_skin/sec_belt/standard
|
||||
preview_name = "Standard"
|
||||
|
||||
/datum/atom_skin/sec_belt/black
|
||||
preview_name = "Black"
|
||||
new_icon = 'modular_zubbers/icons/obj/clothing/belts/belts.dmi'
|
||||
new_worn_icon = 'modular_zubbers/icons/mob/clothing/belt.dmi'
|
||||
new_icon_state = "security_black"
|
||||
|
||||
///Enables you to quickdraw weapons from security holsters
|
||||
/datum/storage/security/open_storage(datum/source, mob/user)
|
||||
var/atom/resolve_parent = parent
|
||||
@@ -51,6 +66,21 @@
|
||||
icon_state = "sunhudsec"
|
||||
glass_colour_type = /datum/client_colour/glass_colour/red
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/sunglasses/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_hud_sunglasses, infinite = FALSE)
|
||||
|
||||
/datum/atom_skin/sec_hud_sunglasses
|
||||
abstract_type = /datum/atom_skin/sec_hud_sunglasses
|
||||
|
||||
/datum/atom_skin/sec_hud_sunglasses/standard
|
||||
preview_name = "Standard"
|
||||
|
||||
/datum/atom_skin/sec_hud_sunglasses/sunglasses
|
||||
preview_name = "Sunglasses"
|
||||
new_icon = 'icons/obj/clothing/glasses.dmi'
|
||||
new_worn_icon = 'icons/mob/clothing/eyes.dmi'
|
||||
new_icon_state = "sun"
|
||||
|
||||
/obj/item/clothing/glasses/hud/security/sunglasses/eyepatch
|
||||
icon_state = "hudpatch"
|
||||
base_icon_state = "hudpatch"
|
||||
@@ -203,16 +233,37 @@
|
||||
worn_icon = 'icons/mob/clothing/hands.dmi'
|
||||
icon_state = "sec"
|
||||
|
||||
/obj/item/clothing/gloves/color/black/security/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_gloves, infinite = FALSE)
|
||||
|
||||
/obj/item/clothing/gloves/tackler/security //Can't just overwrite tackler, as there's a ton of subtypes that we'd then need to account for. This is easier. MUCH easier.
|
||||
icon = 'icons/obj/clothing/gloves.dmi'
|
||||
worn_icon = 'icons/mob/clothing/hands.dmi'
|
||||
icon_state = "gorilla"
|
||||
|
||||
/obj/item/clothing/gloves/tackler/security/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_gloves, infinite = FALSE)
|
||||
|
||||
/obj/item/clothing/gloves/tackler/combat
|
||||
icon = 'icons/obj/clothing/gloves.dmi'
|
||||
worn_icon = 'icons/mob/clothing/hands.dmi'
|
||||
icon_state = "gorilla"
|
||||
|
||||
/obj/item/clothing/gloves/tackler/combat/setup_reskins()
|
||||
AddComponent(/datum/component/reskinable_item, /datum/atom_skin/sec_gloves, infinite = FALSE)
|
||||
|
||||
/datum/atom_skin/sec_gloves
|
||||
abstract_type = /datum/atom_skin/sec_gloves
|
||||
|
||||
/datum/atom_skin/sec_gloves/standard
|
||||
preview_name = "Standard"
|
||||
|
||||
/datum/atom_skin/sec_gloves/plain_black
|
||||
preview_name = "Plain Black"
|
||||
new_icon = 'modular_skyrat/master_files/icons/obj/clothing/gloves.dmi'
|
||||
new_worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/hands.dmi'
|
||||
new_icon_state = "combat"
|
||||
|
||||
/obj/item/clothing/gloves/kaza_ruk/sec
|
||||
icon = 'modular_skyrat/master_files/icons/obj/clothing/gloves.dmi'
|
||||
worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/hands.dmi'
|
||||
@@ -646,12 +697,6 @@
|
||||
inhand_icon_state = "security"
|
||||
worn_icon_state = "security"
|
||||
|
||||
/obj/item/storage/belt/security/redsec/Initialize(mapload)
|
||||
. = ..()
|
||||
var/list/reskin_components = GetComponents(/datum/component/reskinable_item)
|
||||
for(var/datum/component/reskinable_item/reskin_component as anything in reskin_components)
|
||||
qdel(reskin_component)
|
||||
|
||||
/obj/item/storage/belt/holster
|
||||
desc = "A rather plain but still cool looking holster that can hold a handgun, and some ammo."
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
cost = 20
|
||||
*/
|
||||
|
||||
/datum/uplink_item/dangerous/Hyeseong
|
||||
name = "Hyeseong Modular Laser Rifle"
|
||||
desc = "One of the newest Cybersun products. This weapon can switch modes on the fly, from a lethal laser to a shotgun to even a plasma grenade. These are rare, and don't come cheap. Luckily for you, it also can slowly recharge itself."
|
||||
item = /obj/item/gun/energy/modular_laser_rifle
|
||||
cost = 10
|
||||
|
||||
/datum/uplink_item/dangerous/stetchkin
|
||||
name = "Stetchkin APS Machine Pistol kit"
|
||||
desc = "A burst-fire weapon dating all the way back to the first Soviet Union, reproduced and found uncommonly among Syndicate agents."
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
/obj/item/clothing/head/security_kepi = 6,
|
||||
/obj/item/clothing/head/beret/sec/peacekeeper/armadyne = 6,
|
||||
/obj/item/clothing/head/soft/sec = 6,
|
||||
/obj/item/clothing/head/soft/sec/recolorable = 6,
|
||||
/obj/item/clothing/head/security_beanie = 6,
|
||||
/obj/item/clothing/head/costume/ushanka/sec = 10,
|
||||
/obj/item/clothing/head/playbunnyears/security = 6,
|
||||
@@ -59,6 +60,7 @@
|
||||
/obj/item/clothing/neck/security_cape = 6,
|
||||
/obj/item/clothing/neck/security_cape/armplate = 6,
|
||||
/obj/item/clothing/under/rank/security/officer = 6,
|
||||
/obj/item/clothing/under/rank/security/officer/recolorable = 6,
|
||||
/obj/item/clothing/under/rank/security/peacekeeper/skirt = 6,
|
||||
/obj/item/clothing/under/rank/security/peacekeeper/miniskirt = 6,
|
||||
/obj/item/clothing/under/rank/security/officer/blueshirt = 6,
|
||||
|
||||
|
After Width: | Height: | Size: 581 B |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 63 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
@@ -0,0 +1,198 @@
|
||||
/obj/item/clothing/head/helmet/space/voskhod
|
||||
name = "\proper Voskhod-P depowered combat helmet"
|
||||
desc = "A composite graphene-plasteel helmet with a ballistic nylon inner padding, complete with a deployable airtight polycarbonate visor and respirator system. <br>\
|
||||
This particular unit's rebreathers have been salvaged off; unable to resynthesize any more breathable air for the user."
|
||||
icon = 'modular_skyrat/master_files/icons/obj/clothing/head/helmet.dmi'
|
||||
worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/head/helmet.dmi'
|
||||
worn_icon_digi = 'modular_skyrat/master_files/icons/mob/clothing/head_muzzled.dmi'
|
||||
worn_icon_teshari = 'modular_skyrat/master_files/icons/mob/clothing/species/teshari/head.dmi'
|
||||
icon_state = "nri_soldier"
|
||||
armor_type = /datum/armor/hev_suit_nri
|
||||
supports_variations_flags = NONE
|
||||
|
||||
/obj/item/clothing/suit/space/voskhod
|
||||
name = "\proper Voskhod-P depowered combat armor"
|
||||
desc = "A hybrid set of space-resistant armor built on a modified mass-produced Nomex-Aerogel flight suit, polyurea coated durathread-lined light plasteel plates hinder mobility as little as possible.\
|
||||
These 'paralyzed', marketable variations of the suit come with most of their main features removed: from the infamous wound-tending systems, to the less appreciated death alarms. It's more effective, lightweight plates have been removed with just armor-grade steels and ceramics, slowing the suits down signifigantly."
|
||||
icon = 'modular_skyrat/master_files/icons/obj/clothing/suits/spacesuit.dmi'
|
||||
worn_icon = 'modular_skyrat/master_files/icons/mob/clothing/suits/spacesuit.dmi'
|
||||
worn_icon_digi = 'modular_skyrat/master_files/icons/mob/clothing/suits/spacesuit_digi.dmi'
|
||||
worn_icon_teshari = 'modular_skyrat/master_files/icons/mob/clothing/species/teshari/suit.dmi'
|
||||
icon_state = "nri_soldier"
|
||||
armor_type = /datum/armor/hev_suit_nri
|
||||
allowed = list(/obj/item/gun, /obj/item/melee/baton, /obj/item/melee/energy/sword/saber, /obj/item/restraints/handcuffs, /obj/item/tank/internals)
|
||||
supports_variations_flags = NONE //It's already huge enough to look like it can work with digis
|
||||
slowdown = 1
|
||||
|
||||
/datum/crafting_recipe/voskhod_to_mod
|
||||
name = "Depowered Voskhod-To-Refurbished Voskhod MOD Conversion"
|
||||
desc = "While this is usually done on a specialised automated workbench, you can tinker with the suit manually for a longer while to achieve the same result."
|
||||
result = /obj/effect/spawner/random/voskhod_refit
|
||||
reqs = list(
|
||||
/obj/item/clothing/suit/space/voskhod = 1,
|
||||
/obj/item/clothing/head/helmet/space/voskhod = 1,
|
||||
/obj/item/crafting_conversion_kit/voskhod_refit = 1,
|
||||
/obj/item/storage/backpack/industrial/cin_surplus = 1,
|
||||
/obj/item/mod/core = 1,
|
||||
/obj/item/stock_parts/power_store/cell/high = 1,
|
||||
/obj/item/stack/sheet/plasteel = 10,
|
||||
/obj/item/stack/cable_coil = 15,
|
||||
/obj/item/assembly/health = 1,
|
||||
)
|
||||
tool_behaviors = list(TOOL_WELDER, TOOL_MULTITOOL)
|
||||
time = 30 SECONDS
|
||||
category = CAT_CLOTHING
|
||||
crafting_flags = parent_type::crafting_flags | CRAFT_SKIP_MATERIALS_PARITY
|
||||
|
||||
/obj/effect/spawner/random/voskhod_refit
|
||||
name = "converted MODskhod spawner"
|
||||
icon = 'modular_zubbers/icons/obj/clothing/modsuit/mod_clothing.dmi'
|
||||
icon_state = "voskhod-chestplate-sealed"
|
||||
spawn_all_loot = TRUE
|
||||
spawn_loot_count = 1
|
||||
loot = list(/obj/item/mod/control/pre_equipped/voskhod)
|
||||
custom_materials = list(/datum/material/alloy/plasteel = SHEET_MATERIAL_AMOUNT * 10, /datum/material/iron = SHEET_MATERIAL_AMOUNT * 1.95, /datum/material/glass = SHEET_MATERIAL_AMOUNT * 1.45)
|
||||
|
||||
/obj/item/mod/module/auto_doc
|
||||
name = "MOD automatic paramedical module"
|
||||
desc = "The reverse-engineered and redesigned medical assistance system, previously used by the now decommissioned Voskhod combat armor. \
|
||||
The technology it uses is very similar to the one of the N-URSEI suites, yet miniaturised and lacking self-synthesis capabilities. \
|
||||
Using a built-in storage of chemical compounds and a miniature chemical mixer, it's capable of injecting its user with a plethora of drugs, \
|
||||
assisting them with their restoration. However, this system heavily relies on some rarely combat-available chemical compounds to prepare its injections, \
|
||||
mainly cryptobiolin , which appear in the user's bloodstream from time to time, and its trivial damage assessment systems are prone to kicking in only when you're moderately wounded."
|
||||
icon_state = "adrenaline_boost"
|
||||
module_type = MODULE_TOGGLE
|
||||
incompatible_modules = list(
|
||||
/obj/item/mod/module/adrenaline_boost,
|
||||
/obj/item/mod/module/auto_doc,
|
||||
/obj/item/mod/module/ash_accretion,
|
||||
)
|
||||
complexity = 4
|
||||
removable = TRUE
|
||||
use_energy_cost = DEFAULT_CHARGE_DRAIN * 20
|
||||
/// Reagent used as 'fuel'
|
||||
var/reagent_required = /datum/reagent/cryptobiolin
|
||||
/// How much of a reagent we need to refill a single boost.
|
||||
var/reagent_required_amount = 20
|
||||
/// Maximum amount of reagents this module can hold.
|
||||
var/reagent_max_amount = 120
|
||||
/// Flat health threshold above which the module won't heal.
|
||||
var/health_threshold = 80
|
||||
/// Cooldown betwen each treatment.
|
||||
var/general_cooldown = 25 SECONDS
|
||||
|
||||
/// Timer for the healing cooldown.
|
||||
COOLDOWN_DECLARE(heal_timer)
|
||||
/// Timer for the stamina damage cooldown.
|
||||
COOLDOWN_DECLARE(stamina_timer)
|
||||
/// Timer for the blood-refilling cooldown.
|
||||
COOLDOWN_DECLARE(blood_timer)
|
||||
|
||||
/obj/item/mod/module/auto_doc/Initialize(mapload)
|
||||
. = ..()
|
||||
create_reagents(reagent_max_amount)
|
||||
|
||||
/obj/item/mod/module/auto_doc/on_active_process(seconds_per_tick)
|
||||
if(!reagents.has_reagent(reagent_required, reagent_required_amount))
|
||||
balloon_alert(mod.wearer, "not enough chems!")
|
||||
deactivate()
|
||||
return FALSE
|
||||
|
||||
var/new_oxyloss = mod.wearer.get_oxy_loss()
|
||||
var/new_bruteloss = mod.wearer.get_brute_loss()
|
||||
var/new_fireloss = mod.wearer.get_fire_loss()
|
||||
var/new_toxloss = mod.wearer.get_tox_loss()
|
||||
|
||||
if(mod.wearer.get_blood_volume() < BLOOD_VOLUME_OKAY && reagents.total_volume >= reagent_required_amount * 0.5 * seconds_per_tick)
|
||||
if(!COOLDOWN_FINISHED(src, blood_timer))
|
||||
return FALSE
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/blood, 25, list("viruses"=null,"blood_DNA"=null,"blood_type"=mod.wearer.dna.blood_type,"resistances"=null,"trace_chem"=null))
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/coagulant, 2.5 * seconds_per_tick)
|
||||
mod.wearer.playsound_local(mod, 'sound/items/hypospray.ogg', 25, TRUE)
|
||||
reagents.remove_reagent(reagent_required, reagent_required_amount * 0.5 * seconds_per_tick)
|
||||
to_chat(mod.wearer, span_warning("Blood infused."))
|
||||
drain_power(use_energy_cost * 10 * seconds_per_tick)
|
||||
addtimer(CALLBACK(src, PROC_REF(heal_aftereffects), mod.wearer), 60 SECONDS, TIMER_STOPPABLE|TIMER_DELETE_ME)
|
||||
COOLDOWN_START(src, blood_timer, general_cooldown)
|
||||
|
||||
if(mod.wearer.health < health_threshold)
|
||||
if(!COOLDOWN_FINISHED(src, heal_timer))
|
||||
return FALSE
|
||||
if(new_oxyloss && reagents.total_volume >= reagent_required_amount * 0.5 * seconds_per_tick)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/salbutamol, 2.5 * seconds_per_tick)
|
||||
mod.wearer.playsound_local(mod, 'sound/items/internals/internals_on.ogg', 25, TRUE)
|
||||
reagents.remove_reagent(reagent_required, reagent_required_amount * 0.5 * seconds_per_tick)
|
||||
to_chat(mod.wearer, span_warning("Blood oxygen saturated."))
|
||||
if(new_bruteloss && reagents.total_volume >= reagent_required_amount * 1 * seconds_per_tick)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/sal_acid, 2.5 * seconds_per_tick)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/mine_salve, 2.5 * seconds_per_tick)
|
||||
mod.wearer.playsound_local(mod, 'sound/effects/spray2.ogg', 25, TRUE)
|
||||
reagents.remove_reagent(reagent_required, reagent_required_amount * 1 * seconds_per_tick)
|
||||
to_chat(mod.wearer, span_warning("Wound treatment administered."))
|
||||
if(new_fireloss && reagents.total_volume >= reagent_required_amount * 1 * seconds_per_tick)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/oxandrolone, 2.5 * seconds_per_tick)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/mine_salve, 2.5 * seconds_per_tick)
|
||||
mod.wearer.playsound_local(mod, 'sound/effects/spray2.ogg', 25, TRUE)
|
||||
reagents.remove_reagent(reagent_required, reagent_required_amount * 1 * seconds_per_tick)
|
||||
to_chat(mod.wearer, span_warning("Ointment applied."))
|
||||
if(new_toxloss && reagents.total_volume >= reagent_required_amount * 0.5 * seconds_per_tick)
|
||||
mod.wearer.reagents.add_reagent(/datum/reagent/medicine/pen_acid, 2.5 * seconds_per_tick)
|
||||
mod.wearer.playsound_local(mod, 'sound/items/hypospray.ogg', 25, TRUE)
|
||||
reagents.remove_reagent(reagent_required, reagent_required_amount * 0.5 * seconds_per_tick)
|
||||
to_chat(mod.wearer, span_warning("Antitoxin administered."))
|
||||
drain_power(use_energy_cost * 15 * seconds_per_tick)
|
||||
addtimer(CALLBACK(src, PROC_REF(heal_aftereffects), mod.wearer), 60 SECONDS)
|
||||
COOLDOWN_START(src, heal_timer, general_cooldown)
|
||||
|
||||
/obj/item/mod/module/auto_doc/emp_act(severity)
|
||||
. = ..()
|
||||
on_emp(src, severity, .)
|
||||
|
||||
/obj/item/mod/module/auto_doc/proc/on_emp(datum/source, severity, protection)
|
||||
SIGNAL_HANDLER
|
||||
if(protection & EMP_PROTECT_SELF)
|
||||
return
|
||||
heal_aftereffects(mod.wearer, TRUE)
|
||||
|
||||
/// Refills the module with needed chemicals, assuming the container isn't closed or the module isn't full.
|
||||
/obj/item/mod/module/auto_doc/proc/charge_boost(obj/item/attacking_item, mob/user)
|
||||
if(!attacking_item.is_open_container())
|
||||
return FALSE
|
||||
if(reagents.has_reagent(reagent_required, reagent_max_amount))
|
||||
balloon_alert(mod.wearer, "already full!")
|
||||
return FALSE
|
||||
if(!attacking_item.reagents.trans_to(src, reagent_required_amount, target_id = reagent_required))
|
||||
return FALSE
|
||||
balloon_alert(mod.wearer, "charge reloaded!")
|
||||
return TRUE
|
||||
|
||||
/obj/item/mod/module/auto_doc/on_install()
|
||||
. = ..()
|
||||
RegisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(try_refill))
|
||||
RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp))
|
||||
|
||||
/obj/item/mod/module/auto_doc/on_uninstall(deleting = FALSE)
|
||||
. = ..()
|
||||
UnregisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION)
|
||||
UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT)
|
||||
|
||||
/obj/item/mod/module/auto_doc/proc/try_refill(source, mob/user, obj/item/attacking_item)
|
||||
SIGNAL_HANDLER
|
||||
if(charge_boost(attacking_item))
|
||||
return COMPONENT_NO_AFTERATTACK
|
||||
return NONE
|
||||
|
||||
/// With a certain chance, triggers a spontaneous injection of cryptobiolin into the user's bloodstream; suit design's rather ancient and prone to mishaps.
|
||||
/obj/item/mod/module/auto_doc/proc/heal_aftereffects(mob/affected_mob, forced)
|
||||
if(!affected_mob)
|
||||
return
|
||||
var/fault_chance = (reagents.maximum_volume/(reagents.total_volume ? reagents.total_volume : 20))*5 // 5% at max cryptobiolin , 20% at low-to-none cryptobiolin
|
||||
if(prob(fault_chance) || forced == TRUE)
|
||||
reagents.trans_to(affected_mob, min(15,reagents.total_volume))
|
||||
balloon_alert(affected_mob, "reagent canister leak!")
|
||||
affected_mob.playsound_local(mod, 'sound/effects/spray3.ogg', 25, TRUE)
|
||||
|
||||
/obj/item/reagent_containers/cup/glass/waterbottle/large/cryptobiolin
|
||||
name = "bottle of 'Medical Reagents'"
|
||||
desc = "Nothing screams 'Budget cuts' like a plastic bottle of autodoc refills."
|
||||
list_reagents = list(/datum/reagent/cryptobiolin = 100)
|
||||
@@ -95,6 +95,18 @@
|
||||
"Close-quarters mode engaged, lethal."
|
||||
],
|
||||
|
||||
"flare": [
|
||||
"Ignite the flames! Now!",
|
||||
"Incendiary, incendiary!",
|
||||
"Fabricating 'Balrog' energy flares; this'll be sick.",
|
||||
"Fabricating 'Balrog' energy flares; can you feel the fire?",
|
||||
"Firestarter, twisted firestarter!",
|
||||
"Apocalyptic meltdown!",
|
||||
"Incendiary weapons are banned in numerous sectors; makes it kind of risqué..",
|
||||
"Dragon breath!"
|
||||
],
|
||||
|
||||
|
||||
"operator": [
|
||||
"Operator",
|
||||
"Handler"
|
||||
|
||||
@@ -79,9 +79,9 @@
|
||||
|
||||
"incinerate": [
|
||||
"Superheating lens, time to cauterize!",
|
||||
"Charging Hellfire capacitators, let's toast em!",
|
||||
"Hellfire capacitators charging. Don't stop 'till I'm empty!",
|
||||
"Hellfire! Bring it on, bastards!",
|
||||
"Charging lethal energy capacitators, let's toast em!",
|
||||
"Lethal capacitators charging. Don't stop 'till I'm empty!",
|
||||
"Lethal! Bring it on, bastards!",
|
||||
"Heh-heh, can you feel the heat?",
|
||||
"Gunslinger!"
|
||||
],
|
||||
|
||||
@@ -9071,6 +9071,7 @@
|
||||
#include "modular_skyrat\modules\xenos_skyrat_redo\code\xeno_types\warrior.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\bloodsucker_defines.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\guardian_defines.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\loadout.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\maps.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\moonstation_defines.dm"
|
||||
#include "modular_zubbers\code\__DEFINES\projectiles.dm"
|
||||
@@ -9581,9 +9582,11 @@
|
||||
#include "modular_zubbers\code\modules\cargo\bounties\blacksmith.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\bounties\prisoner.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\exports\weapons.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\markets\market_items\misc.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\packs\contraband.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\packs\general.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\packs\goodies.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\packs\imports.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\packs\medical.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\packs\minerals.dm"
|
||||
#include "modular_zubbers\code\modules\cargo\packs\security.dm"
|
||||
@@ -9808,6 +9811,7 @@
|
||||
#include "modular_zubbers\code\modules\general_sprite_overrides\turfs\walls.dm"
|
||||
#include "modular_zubbers\code\modules\ghost_wavedefence\wave_def.dm"
|
||||
#include "modular_zubbers\code\modules\gladiator\code\game\objects\items\gladiator_items.dm"
|
||||
#include "modular_zubbers\code\modules\greyscale\component_style.dm"
|
||||
#include "modular_zubbers\code\modules\hydroponics\gene_modder.dm"
|
||||
#include "modular_zubbers\code\modules\hydroponics\plant_genes.dm"
|
||||
#include "modular_zubbers\code\modules\hydroponics\grown\rocks.dm"
|
||||
@@ -10462,4 +10466,5 @@
|
||||
#include "modular_zubbers\modules\taur_mechanics\taur_body.dm"
|
||||
#include "modular_zubbers\modules\taur_mechanics\taur_clothing_offset.dm"
|
||||
#include "modular_zubbers\modules\wood_walls\code\new_floors.dm"
|
||||
#include "modular_zubbers\sound\novaya_ert\code\mod_suit.dm"
|
||||
// END_INCLUDE
|
||||
|
||||
@@ -18,6 +18,7 @@ import { Window } from '../layouts';
|
||||
|
||||
type ColorEntry = {
|
||||
index: number;
|
||||
label?: string; //BUBBER ADDITION - dynamic uniforms
|
||||
value: string;
|
||||
};
|
||||
|
||||
@@ -37,6 +38,11 @@ type SpriteEntry = {
|
||||
type GreyscaleMenuData = {
|
||||
greyscale_config: string;
|
||||
colors: ColorEntry[];
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
full_color_string?: string;
|
||||
hide_full_color_string?: boolean;
|
||||
component_style?: ComponentStyleData;
|
||||
//BUBBER ADDITION END - dynamic uniforms
|
||||
sprites: SpriteData;
|
||||
generate_full_preview: boolean;
|
||||
unlocked: boolean;
|
||||
@@ -46,6 +52,25 @@ type GreyscaleMenuData = {
|
||||
refreshing: boolean;
|
||||
};
|
||||
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
type ComponentStyleOption = {
|
||||
name: string;
|
||||
value: string;
|
||||
selected: boolean;
|
||||
};
|
||||
|
||||
type ComponentStyleCore = {
|
||||
name: string;
|
||||
key: string;
|
||||
options: ComponentStyleOption[];
|
||||
};
|
||||
|
||||
type ComponentStyleData = {
|
||||
core_components: ComponentStyleCore[];
|
||||
accessories: ComponentStyleOption[];
|
||||
};
|
||||
//BUBBER ADDITION END - dynamic uniforms
|
||||
|
||||
enum Direction {
|
||||
North = 'north',
|
||||
NorthEast = 'northeast',
|
||||
@@ -86,6 +111,68 @@ const ConfigDisplay = (props) => {
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
//BUBBER ADDITION START - dynamic uniforms
|
||||
const ComponentStyleDisplay = (props) => {
|
||||
const { act, data } = useBackend<GreyscaleMenuData>();
|
||||
const style = data.component_style;
|
||||
|
||||
if (!style) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Section title="Style">
|
||||
<LabeledList>
|
||||
{style.core_components.map((component) => (
|
||||
<LabeledList.Item key={component.key} label={component.name}>
|
||||
<Flex wrap>
|
||||
{component.options.map((item) => (
|
||||
<Flex.Item
|
||||
key={`${component.key}-${item.value}`}
|
||||
mr={0.5}
|
||||
mb={0.5}
|
||||
>
|
||||
<Button.Checkbox
|
||||
checked={item.selected}
|
||||
onClick={() =>
|
||||
act('component_style_set_core', {
|
||||
component: component.key,
|
||||
value: item.value,
|
||||
})
|
||||
}
|
||||
>
|
||||
{item.name}
|
||||
</Button.Checkbox>
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
{!!style.accessories.length && (
|
||||
<LabeledList.Item label="Accessories">
|
||||
<Flex wrap>
|
||||
{style.accessories.map((item) => (
|
||||
<Flex.Item key={item.value} mr={0.5} mb={0.5}>
|
||||
<Button.Checkbox
|
||||
checked={item.selected}
|
||||
onClick={() =>
|
||||
act('component_style_toggle_accessory', {
|
||||
accessory: item.value,
|
||||
})
|
||||
}
|
||||
>
|
||||
{item.name}
|
||||
</Button.Checkbox>
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
//BUBBER ADDITION END - dynamic uniforms
|
||||
|
||||
const ColorDisplay = (props) => {
|
||||
const { act, data } = useBackend<GreyscaleMenuData>();
|
||||
@@ -93,23 +180,37 @@ const ColorDisplay = (props) => {
|
||||
return (
|
||||
<Section title="Colors">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Full Color String">
|
||||
<Button
|
||||
icon="dice"
|
||||
onClick={() => act('random_all_colors')}
|
||||
tooltip="Randomizes all color groups."
|
||||
/>
|
||||
<Input
|
||||
value={colors.map((item) => item.value).join('')}
|
||||
onBlur={(value) =>
|
||||
act('recolor_from_string', { color_string: value })
|
||||
}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
{/* BUBBER EDIT START - dynamic uniforms */}
|
||||
{!data.hide_full_color_string && (
|
||||
<LabeledList.Item label="Full Color String">
|
||||
<Button
|
||||
icon="dice"
|
||||
onClick={() => act('random_all_colors')}
|
||||
tooltip="Randomizes all color groups."
|
||||
/>
|
||||
{!!data.component_style && (
|
||||
<Button
|
||||
icon="undo"
|
||||
onClick={() => act('component_style_reset_all')}
|
||||
tooltip="Resets colors and style options to their defaults."
|
||||
/>
|
||||
)}
|
||||
<Input
|
||||
value={
|
||||
data.full_color_string ||
|
||||
colors.map((item) => item.value).join('')
|
||||
}
|
||||
onBlur={(value) =>
|
||||
act('recolor_from_string', { color_string: value })
|
||||
}
|
||||
/>
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{/* BUBBER EDIT END - dynamic uniforms */}
|
||||
{colors.map((item) => (
|
||||
<LabeledList.Item
|
||||
key={`colorgroup${item.index}${item.value}`}
|
||||
label={`Color Group ${item.index}`}
|
||||
label={item.label || `Color Group ${item.index}`} //BUBBER EDIT
|
||||
color={item.value}
|
||||
>
|
||||
<ColorBox color={item.value} />{' '}
|
||||
@@ -123,6 +224,19 @@ const ColorDisplay = (props) => {
|
||||
onClick={() => act('random_color', { color_index: item.index })}
|
||||
tooltip="Randomizes the color for this color group."
|
||||
/>
|
||||
{/* BUBBER EDIT START - dynamic uniforms */}
|
||||
{!!data.component_style && (
|
||||
<Button
|
||||
icon="undo"
|
||||
onClick={() =>
|
||||
act('component_style_reset_color', {
|
||||
color_index: item.index,
|
||||
})
|
||||
}
|
||||
tooltip="Resets this color group to its default."
|
||||
/>
|
||||
)}
|
||||
{/* BUBBER EDIT END - dynamic uniforms */}
|
||||
<Input
|
||||
value={item.value}
|
||||
width={7}
|
||||
@@ -284,6 +398,9 @@ export const GreyscaleModifyMenu = (props) => {
|
||||
<Window title="Color Configuration" width={325} height={800}>
|
||||
<Window.Content scrollable>
|
||||
<ConfigDisplay />
|
||||
{/* BUBBER ADDITION START - dynamic uniforms */}
|
||||
<ComponentStyleDisplay />
|
||||
{/* BUBBER ADDITION END - dynamic uniforms */}
|
||||
<ColorDisplay />
|
||||
<IconStatesDisplay />
|
||||
<Flex direction="column">
|
||||
|
||||
@@ -21,6 +21,20 @@ export function ItemIcon(props: Props) {
|
||||
const { item, scale = 3 } = props;
|
||||
const icon_to_use = item.icon;
|
||||
const icon_state_to_use = item.icon_state;
|
||||
//BUBBER EDIT START - dyamic uniforms
|
||||
if (item.image) {
|
||||
return (
|
||||
<img
|
||||
src={`data:image/png;base64,${item.image}`}
|
||||
style={{
|
||||
width: `${32 * scale}px`,
|
||||
height: `${32 * scale}px`,
|
||||
imageRendering: 'pixelated',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
//BUBBER EDIT END - dynamic uniforms
|
||||
|
||||
if (!icon_to_use || !icon_state_to_use) {
|
||||
return (
|
||||
@@ -67,6 +81,7 @@ export function ItemDisplay(props: DisplayProps) {
|
||||
style={{ textTransform: 'capitalize', zIndex: '1' }}
|
||||
tooltip={item.name}
|
||||
tooltipPosition={'bottom'}
|
||||
base64={item.image} //BUBBER EDIT ADDITION: Dynamic uniform icons
|
||||
dmIcon={item.icon}
|
||||
dmIconState={item.icon_state}
|
||||
onClick={() =>
|
||||
|
||||
@@ -36,6 +36,7 @@ export type LoadoutItem = {
|
||||
path: typePath;
|
||||
icon: DmIconFile | null;
|
||||
icon_state: DmIconState | null;
|
||||
image?: string;
|
||||
buttons: LoadoutButton[];
|
||||
reskins: ReskinOption[] | null;
|
||||
information: LoadoutTooltip[];
|
||||
|
||||