Add new surgery tgui (#64579)

* Move element to component, start UI, move assets into their own directory

* Complete UI

* Stop when another surgery is started

* Set your real zone since I forgot you actually need to start the surgery too

* Bring this back since I was just removing it as part of a cleanup for asset cache, but I can't prove it's not used anymore

* Remove unnecessary constructor I was using for something else

* Fix signal override
This commit is contained in:
Mothblocks
2022-02-06 22:04:06 +02:00
committed by GitHub
parent 0911b616fb
commit 068f5df349
54 changed files with 1293 additions and 744 deletions
@@ -0,0 +1,6 @@
/// From /datum/surgery/New(): (datum/surgery/surgery, surgery_location (body zone), obj/item/bodypart/targeted_limb)
#define COMSIG_MOB_SURGERY_STARTED "mob_surgery_started"
/// From /datum/surgery_step/success(): (datum/surgery_step/step, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results)
#define COMSIG_MOB_SURGERY_STEP_SUCCESS "mob_surgery_step_success"
@@ -132,5 +132,6 @@
#define COMSIG_MOB_CTRL_CLICKED "mob_ctrl_clicked"
///From base of mob/update_movespeed():area
#define COMSIG_MOB_MOVESPEED_UPDATED "mob_update_movespeed"
/// From /datum/surgery_step/success(): (datum/surgey_step/step, mob/living/target, target_zone, obj/item/tool, datum/surgery/surgery, default_display_results)
#define COMSIG_MOB_SURGERY_STEP_SUCCESS "mob_surgery_step_success"
/// From /atom/movable/screen/zone_sel/proc/set_selected_zone.
/// Fires when the user has changed their selected body target.
#define COMSIG_MOB_SELECTED_ZONE_SET "mob_set_selected_zone"
+5
View File
@@ -1035,3 +1035,8 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
. = "gigantic"
else
. = ""
/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts
/proc/sanitize_css_class_name(name)
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
return replacetext(name, regex, "")
+1
View File
@@ -573,6 +573,7 @@
if(choice != hud.mymob.zone_selected)
hud.mymob.zone_selected = choice
update_appearance()
SEND_SIGNAL(user, COMSIG_MOB_SELECTED_ZONE_SET, choice)
return TRUE
+338
View File
@@ -0,0 +1,338 @@
/// Allows an item to be used to initiate surgeries.
/datum/component/surgery_initiator
/// The currently selected target that the user is proposing a surgery on
var/datum/weakref/surgery_target_ref
/// The last user, as a weakref
var/datum/weakref/last_user_ref
/datum/component/surgery_initiator/Initialize()
. = ..()
if(!isitem(parent))
return COMPONENT_INCOMPATIBLE
/datum/component/surgery_initiator/Destroy(force, silent)
last_user_ref = null
surgery_target_ref = null
return ..()
/datum/component/surgery_initiator/RegisterWithParent()
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/initiate_surgery_moment)
/datum/component/surgery_initiator/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_ITEM_ATTACK)
unregister_signals()
/datum/component/surgery_initiator/proc/unregister_signals()
var/mob/living/last_user = last_user_ref?.resolve()
if (!isnull(last_user_ref))
UnregisterSignal(last_user, COMSIG_MOB_SELECTED_ZONE_SET)
var/mob/living/surgery_target = surgery_target_ref?.resolve()
if (!isnull(surgery_target_ref))
UnregisterSignal(surgery_target, COMSIG_MOB_SURGERY_STARTED)
/// Does the surgery initiation.
/datum/component/surgery_initiator/proc/initiate_surgery_moment(datum/source, atom/target, mob/user)
SIGNAL_HANDLER
if(!isliving(target))
return
INVOKE_ASYNC(src, .proc/do_initiate_surgery_moment, target, user)
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/component/surgery_initiator/proc/do_initiate_surgery_moment(mob/living/target, mob/user)
var/datum/surgery/current_surgery
for(var/i_one in target.surgeries)
var/datum/surgery/surgeryloop = i_one
if(surgeryloop.location == user.zone_selected)
current_surgery = surgeryloop
break
if (!isnull(current_surgery) && !current_surgery.step_in_progress)
attempt_cancel_surgery(current_surgery, target, user)
return
var/list/available_surgeries = get_available_surgeries(user, target)
if(!length(available_surgeries))
if (target.body_position == LYING_DOWN)
target.balloon_alert(user, "no surgeries available!")
else
target.balloon_alert(user, "make them lie down!")
return
unregister_signals()
last_user_ref = WEAKREF(user)
surgery_target_ref = WEAKREF(target)
RegisterSignal(user, COMSIG_MOB_SELECTED_ZONE_SET, .proc/on_set_selected_zone)
RegisterSignal(target, COMSIG_MOB_SURGERY_STARTED, .proc/on_mob_surgery_started)
ui_interact(user)
/datum/component/surgery_initiator/proc/get_available_surgeries(mob/user, mob/living/target)
var/list/available_surgeries = list()
var/mob/living/carbon/carbon_target
var/obj/item/bodypart/affecting
if (iscarbon(target))
carbon_target = target
affecting = carbon_target.get_bodypart(check_zone(user.zone_selected))
for(var/datum/surgery/surgery as anything in GLOB.surgeries_list)
if(!surgery.possible_locs.Find(user.zone_selected))
continue
if(affecting)
if(!surgery.requires_bodypart)
continue
if(surgery.requires_bodypart_type && affecting.status != surgery.requires_bodypart_type)
continue
if(surgery.requires_real_bodypart && affecting.is_pseudopart)
continue
else if(carbon_target && surgery.requires_bodypart) //mob with no limb in surgery zone when we need a limb
continue
if(surgery.lying_required && target.body_position != LYING_DOWN)
continue
if(!surgery.can_start(user, target))
continue
for(var/path in surgery.target_mobtypes)
if(istype(target, path))
available_surgeries += surgery
break
return available_surgeries
/// Does the surgery de-initiation.
/datum/component/surgery_initiator/proc/attempt_cancel_surgery(datum/surgery/the_surgery, mob/living/patient, mob/user)
var/selected_zone = user.zone_selected
if(the_surgery.status == 1)
patient.surgeries -= the_surgery
REMOVE_TRAIT(patient, TRAIT_ALLOWED_HONORBOUND_ATTACK, type)
user.visible_message(
span_notice("[user] removes [parent] from [patient]'s [parse_zone(selected_zone)]."),
span_notice("You remove [parent] from [patient]'s [parse_zone(selected_zone)]."),
)
patient.balloon_alert(user, "stopped work on [parse_zone(selected_zone)]")
qdel(the_surgery)
return
if(!the_surgery.can_cancel)
return
var/required_tool_type = TOOL_CAUTERY
var/obj/item/close_tool = user.get_inactive_held_item()
var/is_robotic = the_surgery.requires_bodypart_type == BODYPART_ROBOTIC
if(is_robotic)
required_tool_type = TOOL_SCREWDRIVER
if(iscyborg(user))
close_tool = locate(/obj/item/cautery) in user.held_items
if(!close_tool)
patient.balloon_alert(user, "need a cautery in an inactive slot to stop the surgery!")
return
else if(!close_tool || close_tool.tool_behaviour != required_tool_type)
patient.balloon_alert(user, "need a [is_robotic ? "screwdriver": "cautery"] in your inactive hand to stop the surgery!")
return
if(the_surgery.operated_bodypart)
the_surgery.operated_bodypart.generic_bleedstacks -= 5
patient.surgeries -= the_surgery
REMOVE_TRAIT(patient, TRAIT_ALLOWED_HONORBOUND_ATTACK, ELEMENT_TRAIT(type))
user.visible_message(
span_notice("[user] closes [patient]'s [parse_zone(selected_zone)] with [close_tool] and removes [parent]."),
span_notice("You close [patient]'s [parse_zone(selected_zone)] with [close_tool] and remove [parent]."),
)
patient.balloon_alert(user, "closed up [parse_zone(selected_zone)]")
qdel(the_surgery)
/datum/component/surgery_initiator/proc/on_mob_surgery_started(mob/source, datum/surgery/surgery, surgery_location)
SIGNAL_HANDLER
var/mob/living/last_user = last_user_ref.resolve()
if (surgery_location != last_user.zone_selected)
return
if (!isnull(last_user))
source.balloon_alert(last_user, "someone else started a surgery!")
ui_close()
/datum/component/surgery_initiator/proc/on_set_selected_zone(mob/source, new_zone)
ui_interact(source)
/datum/component/surgery_initiator/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if (!ui)
ui = new(user, src, "SurgeryInitiator")
ui.open()
/datum/component/surgery_initiator/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if (.)
return .
var/mob/user = usr
var/mob/living/surgery_target = surgery_target_ref.resolve()
if (isnull(surgery_target))
return TRUE
switch (action)
if ("change_zone")
var/zone = params["new_zone"]
if (!(zone in list(
BODY_ZONE_HEAD,
BODY_ZONE_CHEST,
BODY_ZONE_L_ARM,
BODY_ZONE_R_ARM,
BODY_ZONE_L_LEG,
BODY_ZONE_R_LEG,
BODY_ZONE_PRECISE_EYES,
BODY_ZONE_PRECISE_MOUTH,
BODY_ZONE_PRECISE_GROIN,
)))
return TRUE
var/atom/movable/screen/zone_sel/zone_selector = user.hud_used?.zone_select
zone_selector?.set_selected_zone(zone, user)
return TRUE
if ("start_surgery")
for (var/datum/surgery/surgery as anything in get_available_surgeries(user, surgery_target))
if (surgery.name == params["surgery_name"])
try_choose_surgery(user, surgery_target, surgery)
return TRUE
/datum/component/surgery_initiator/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/simple/body_zones),
)
/datum/component/surgery_initiator/ui_data(mob/user)
var/mob/living/surgery_target = surgery_target_ref.resolve()
var/list/surgeries = list()
if (!isnull(surgery_target))
for (var/datum/surgery/surgery as anything in get_available_surgeries(user, surgery_target))
var/list/surgery_info = list(
"name" = surgery.name,
)
if (surgery_needs_exposure(surgery, surgery_target))
surgery_info["blocked"] = TRUE
surgeries += list(surgery_info)
return list(
"selected_zone" = user.zone_selected,
"target_name" = surgery_target?.name,
"surgeries" = surgeries,
)
/datum/component/surgery_initiator/ui_close(mob/user)
unregister_signals()
surgery_target_ref = null
return ..()
/datum/component/surgery_initiator/ui_status(mob/user, datum/ui_state/state)
var/obj/item/item_parent = parent
if (user != item_parent.loc)
return UI_CLOSE
var/mob/living/surgery_target = surgery_target_ref?.resolve()
if (isnull(surgery_target))
return UI_CLOSE
if (!can_start_surgery(user, surgery_target))
return UI_CLOSE
return ..()
/datum/component/surgery_initiator/proc/can_start_surgery(mob/user, mob/living/target)
if (!user.Adjacent(target))
return FALSE
// The item was moved somewhere else
if (!(parent in user))
return FALSE
// While we were choosing, another surgery was started at the same location
for (var/datum/surgery/surgery in target.surgeries)
if (surgery.location == user.zone_selected)
return FALSE
return TRUE
/datum/component/surgery_initiator/proc/try_choose_surgery(mob/user, mob/living/target, datum/surgery/surgery)
if (!can_start_surgery(user, target))
// This could have a more detailed message, but the UI closes when this is true anyway, so
// if it ever comes up, it'll be because of lag.
target.balloon_alert(user, "can't start the surgery!")
return
var/obj/item/bodypart/affecting_limb
var/selected_zone = user.zone_selected
if (iscarbon(target))
var/mob/living/carbon/carbon_target = target
affecting_limb = carbon_target.get_bodypart(check_zone(selected_zone))
if (surgery.requires_bodypart == isnull(affecting_limb))
if (surgery.requires_bodypart)
target.balloon_alert(user, "patient has no [parse_zone(selected_zone)]!")
else
target.balloon_alert(user, "patient has \a [parse_zone(selected_zone)]!")
return
if (!isnull(affecting_limb) && surgery.requires_bodypart_type && affecting_limb.status != surgery.requires_bodypart_type)
target.balloon_alert(user, "not the right type of limb!")
return
if (surgery.lying_required && target.body_position != LYING_DOWN)
target.balloon_alert(user, "patient is not lying down!")
return
if (!surgery.can_start(user, target))
target.balloon_alert(user, "can't start the surgery!")
return
if (surgery_needs_exposure(surgery, target))
target.balloon_alert(user, "expose [target.p_their()] [parse_zone(selected_zone)]!")
return
ui_close()
var/datum/surgery/procedure = new surgery.type(target, selected_zone, affecting_limb)
ADD_TRAIT(target, TRAIT_ALLOWED_HONORBOUND_ATTACK, type)
target.balloon_alert(user, "starting \"[lowertext(procedure.name)]\"")
user.visible_message(
span_notice("[user] drapes [parent] over [target]'s [parse_zone(selected_zone)] to prepare for surgery."),
span_notice("You drape [parent] over [target]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name]."),
)
log_combat(user, target, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
/datum/component/surgery_initiator/proc/surgery_needs_exposure(datum/surgery/surgery, mob/living/target)
var/mob/living/user = last_user_ref?.resolve()
if (isnull(user))
return FALSE
return !surgery.ignore_clothes && !get_location_accessible(target, user.zone_selected)
-145
View File
@@ -1,145 +0,0 @@
/// Allows an item to be used to initiate surgeries.
/datum/element/surgery_initiator
element_flags = ELEMENT_DETACH
/datum/element/surgery_initiator/Attach(datum/target)
. = ..()
if(!isitem(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_ITEM_ATTACK, .proc/initiate_surgery_moment)
/datum/element/surgery_initiator/Detach(datum/source)
UnregisterSignal(source, COMSIG_ITEM_ATTACK)
return ..()
/// Does the surgery initiation.
/datum/element/surgery_initiator/proc/initiate_surgery_moment(datum/source, atom/target, mob/user)
SIGNAL_HANDLER
if(!isliving(target))
return
INVOKE_ASYNC(src, .proc/do_initiate_surgery_moment, source, target, user)
return COMPONENT_CANCEL_ATTACK_CHAIN
/datum/element/surgery_initiator/proc/do_initiate_surgery_moment(datum/source, atom/target, mob/user)
var/mob/living/livingtarget = target
var/mob/living/carbon/carbontarget
var/obj/item/bodypart/affecting
var/selected_zone = user.zone_selected
if(iscarbon(livingtarget))
carbontarget = livingtarget
affecting = carbontarget.get_bodypart(check_zone(selected_zone))
var/datum/surgery/current_surgery
for(var/i_one in livingtarget.surgeries)
var/datum/surgery/surgeryloop = i_one
if(surgeryloop.location == selected_zone)
current_surgery = surgeryloop
if(!current_surgery)
var/list/all_surgeries = GLOB.surgeries_list.Copy()
var/list/available_surgeries = list()
for(var/i_two in all_surgeries)
var/datum/surgery/surgeryloop_two = i_two
if(!surgeryloop_two.possible_locs.Find(selected_zone))
continue
if(affecting)
if(!surgeryloop_two.requires_bodypart)
continue
if(surgeryloop_two.requires_bodypart_type && affecting.status != surgeryloop_two.requires_bodypart_type)
continue
if(surgeryloop_two.requires_real_bodypart && affecting.is_pseudopart)
continue
else if(carbontarget && surgeryloop_two.requires_bodypart) //mob with no limb in surgery zone when we need a limb
continue
if(surgeryloop_two.lying_required && livingtarget.body_position != LYING_DOWN)
continue
if(!surgeryloop_two.can_start(user, livingtarget))
continue
for(var/path in surgeryloop_two.target_mobtypes)
if(istype(livingtarget, path))
available_surgeries[surgeryloop_two.name] = surgeryloop_two
break
if(!length(available_surgeries))
return
var/pick_your_surgery = tgui_input_list(user, "Which procedure?", "Surgery", sort_list(available_surgeries))
if(isnull(pick_your_surgery))
return
if(user?.Adjacent(livingtarget) && (source in user))
var/datum/surgery/surgeryinstance_notonmob = available_surgeries[pick_your_surgery]
for(var/i_three in livingtarget.surgeries)
var/datum/surgery/surgeryloop_three = i_three
if(surgeryloop_three.location == selected_zone)
return //during the input() another surgery was started at the same location.
//we check that the surgery is still doable after the input() wait.
if(carbontarget)
affecting = carbontarget.get_bodypart(check_zone(selected_zone))
if(affecting)
if(!surgeryinstance_notonmob.requires_bodypart)
return
if(surgeryinstance_notonmob.requires_bodypart_type && affecting.status != surgeryinstance_notonmob.requires_bodypart_type)
return
else if(carbontarget && surgeryinstance_notonmob.requires_bodypart)
return
if(surgeryinstance_notonmob.lying_required && livingtarget.body_position != LYING_DOWN)
return
if(!surgeryinstance_notonmob.can_start(user, livingtarget))
return
if(surgeryinstance_notonmob.ignore_clothes || get_location_accessible(livingtarget, selected_zone))
var/datum/surgery/procedure = new surgeryinstance_notonmob.type(livingtarget, selected_zone, affecting)
ADD_TRAIT(livingtarget, TRAIT_ALLOWED_HONORBOUND_ATTACK, ELEMENT_TRAIT(type))
user.visible_message(span_notice("[user] drapes [source] over [livingtarget]'s [parse_zone(selected_zone)] to prepare for surgery."), \
span_notice("You drape [source] over [livingtarget]'s [parse_zone(selected_zone)] to prepare for \an [procedure.name]."))
log_combat(user, livingtarget, "operated on", null, "(OPERATION TYPE: [procedure.name]) (TARGET AREA: [selected_zone])")
else
to_chat(user, span_warning("You need to expose [livingtarget]'s [parse_zone(selected_zone)] first!"))
else if(!current_surgery.step_in_progress)
attempt_cancel_surgery(current_surgery, source, livingtarget, user)
/// Does the surgery de-initiation.
/datum/element/surgery_initiator/proc/attempt_cancel_surgery(datum/surgery/the_surgery, obj/item/the_item, mob/living/the_patient, mob/user)
var/selected_zone = user.zone_selected
if(the_surgery.status == 1)
the_patient.surgeries -= the_surgery
REMOVE_TRAIT(the_patient, TRAIT_ALLOWED_HONORBOUND_ATTACK, ELEMENT_TRAIT(type))
user.visible_message(span_notice("[user] removes [the_item] from [the_patient]'s [parse_zone(selected_zone)]."), \
span_notice("You remove [the_item] from [the_patient]'s [parse_zone(selected_zone)]."))
qdel(the_surgery)
return
if(!the_surgery.can_cancel)
return
var/required_tool_type = TOOL_CAUTERY
var/obj/item/close_tool = user.get_inactive_held_item()
var/is_robotic = the_surgery.requires_bodypart_type == BODYPART_ROBOTIC
if(is_robotic)
required_tool_type = TOOL_SCREWDRIVER
if(iscyborg(user))
close_tool = locate(/obj/item/cautery) in user.held_items
if(!close_tool)
to_chat(user, span_warning("You need to equip a cautery in an inactive slot to stop [the_patient]'s surgery!"))
return
else if(!close_tool || close_tool.tool_behaviour != required_tool_type)
to_chat(user, span_warning("You need to hold a [is_robotic ? "screwdriver" : "cautery"] in your inactive hand to stop [the_patient]'s surgery!"))
return
if(the_surgery.operated_bodypart)
the_surgery.operated_bodypart.generic_bleedstacks -= 5
the_patient.surgeries -= the_surgery
REMOVE_TRAIT(the_patient, TRAIT_ALLOWED_HONORBOUND_ATTACK, ELEMENT_TRAIT(type))
user.visible_message(span_notice("[user] closes [the_patient]'s [parse_zone(selected_zone)] with [close_tool] and removes [the_item]."), \
span_notice("You close [the_patient]'s [parse_zone(selected_zone)] with [close_tool] and remove [the_item]."))
qdel(the_surgery)
+1 -1
View File
@@ -33,7 +33,7 @@ LINEN BINS
/obj/item/bedsheet/Initialize(mapload)
. = ..()
AddElement(/datum/element/surgery_initiator)
AddComponent(/datum/component/surgery_initiator)
AddElement(/datum/element/bed_tuckable, 0, 0, 0)
if(bedsheet_type == BEDSHEET_DOUBLE)
stack_amount *= 2
-1
View File
@@ -79,7 +79,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
for (var/asset_name in assets)
.[asset_name] = SSassets.transport.get_asset_url(asset_name, assets[asset_name])
// For registering or sending multiple others at once
/datum/asset/group
_abstract = /datum/asset/group
@@ -1,590 +0,0 @@
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
"tgui.bundle.js" = file("tgui/public/tgui.bundle.js"),
"tgui.bundle.css" = file("tgui/public/tgui.bundle.css"),
)
/datum/asset/simple/tgui_panel
keep_local_name = TRUE
assets = list(
"tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"),
"tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"),
)
/datum/asset/simple/headers
assets = list(
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
"alarm_red.gif" = 'icons/program_icons/alarm_red.gif',
"batt_5.gif" = 'icons/program_icons/batt_5.gif',
"batt_20.gif" = 'icons/program_icons/batt_20.gif',
"batt_40.gif" = 'icons/program_icons/batt_40.gif',
"batt_60.gif" = 'icons/program_icons/batt_60.gif',
"batt_80.gif" = 'icons/program_icons/batt_80.gif',
"batt_100.gif" = 'icons/program_icons/batt_100.gif',
"charging.gif" = 'icons/program_icons/charging.gif',
"downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif',
"downloader_running.gif" = 'icons/program_icons/downloader_running.gif',
"ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif',
"ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif',
"power_norm.gif" = 'icons/program_icons/power_norm.gif',
"power_warn.gif" = 'icons/program_icons/power_warn.gif',
"sig_high.gif" = 'icons/program_icons/sig_high.gif',
"sig_low.gif" = 'icons/program_icons/sig_low.gif',
"sig_lan.gif" = 'icons/program_icons/sig_lan.gif',
"sig_none.gif" = 'icons/program_icons/sig_none.gif',
"smmon_0.gif" = 'icons/program_icons/smmon_0.gif',
"smmon_1.gif" = 'icons/program_icons/smmon_1.gif',
"smmon_2.gif" = 'icons/program_icons/smmon_2.gif',
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
"borg_mon.gif" = 'icons/program_icons/borg_mon.gif',
"robotact.gif" = 'icons/program_icons/robotact.gif'
)
/datum/asset/simple/radar_assets
assets = list(
"ntosradarbackground.png" = 'icons/ui_icons/tgui/ntosradar_background.png',
"ntosradarpointer.png" = 'icons/ui_icons/tgui/ntosradar_pointer.png',
"ntosradarpointerS.png" = 'icons/ui_icons/tgui/ntosradar_pointer_S.png'
)
/datum/asset/simple/circuit_assets
assets = list(
"grid_background.png" = 'icons/ui_icons/tgui/grid_background.png'
)
/datum/asset/spritesheet/simple/pda
name = "pda"
assets = list(
"atmos" = 'icons/pda_icons/pda_atmos.png',
"back" = 'icons/pda_icons/pda_back.png',
"bell" = 'icons/pda_icons/pda_bell.png',
"blank" = 'icons/pda_icons/pda_blank.png',
"boom" = 'icons/pda_icons/pda_boom.png',
"bucket" = 'icons/pda_icons/pda_bucket.png',
"medbot" = 'icons/pda_icons/pda_medbot.png',
"floorbot" = 'icons/pda_icons/pda_floorbot.png',
"cleanbot" = 'icons/pda_icons/pda_cleanbot.png',
"crate" = 'icons/pda_icons/pda_crate.png',
"cuffs" = 'icons/pda_icons/pda_cuffs.png',
"eject" = 'icons/pda_icons/pda_eject.png',
"flashlight" = 'icons/pda_icons/pda_flashlight.png',
"honk" = 'icons/pda_icons/pda_honk.png',
"mail" = 'icons/pda_icons/pda_mail.png',
"medical" = 'icons/pda_icons/pda_medical.png',
"menu" = 'icons/pda_icons/pda_menu.png',
"mule" = 'icons/pda_icons/pda_mule.png',
"notes" = 'icons/pda_icons/pda_notes.png',
"power" = 'icons/pda_icons/pda_power.png',
"rdoor" = 'icons/pda_icons/pda_rdoor.png',
"reagent" = 'icons/pda_icons/pda_reagent.png',
"refresh" = 'icons/pda_icons/pda_refresh.png',
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
"skills" = 'icons/pda_icons/pda_skills.png',
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png',
"droneblacklist" = 'icons/pda_icons/pda_droneblacklist.png',
)
/datum/asset/spritesheet/simple/paper
name = "paper"
assets = list(
"stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png',
"stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png',
"stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png',
"stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png',
"stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png',
"stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png',
"stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png',
"stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png',
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png',
"stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png',
"stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
"stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
"stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png'
)
/datum/asset/simple/irv
assets = list(
"jquery-ui.custom-core-widgit-mouse-sortable.min.js" = 'html/jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js',
)
/datum/asset/group/irv
children = list(
/datum/asset/simple/jquery,
/datum/asset/simple/irv
)
/datum/asset/simple/jquery
legacy = TRUE
assets = list(
"jquery.min.js" = 'html/jquery/jquery.min.js',
)
/datum/asset/simple/namespaced/fontawesome
assets = list(
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
"fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
"fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css',
)
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')
/datum/asset/simple/namespaced/tgfont
assets = list(
"tgfont.eot" = file("tgui/packages/tgfont/static/tgfont.eot"),
"tgfont.woff2" = file("tgui/packages/tgfont/static/tgfont.woff2"),
)
parents = list(
"tgfont.css" = file("tgui/packages/tgfont/static/tgfont.css"),
)
/datum/asset/spritesheet/chat
name = "chat"
/datum/asset/spritesheet/chat/create_spritesheets()
InsertAll("emoji", EMOJI_SET)
// pre-loading all lanugage icons also helps to avoid meta
InsertAll("language", 'icons/misc/language.dmi')
// catch languages which are pulling icons from another file
for(var/path in typesof(/datum/language))
var/datum/language/L = path
var/icon = initial(L.icon)
if (icon != 'icons/misc/language.dmi')
var/icon_state = initial(L.icon_state)
Insert("language-[icon_state]", icon, icon_state=icon_state)
/datum/asset/simple/lobby
assets = list(
"playeroptions.css" = 'html/browser/playeroptions.css'
)
/datum/asset/simple/namespaced/common
assets = list("padlock.png" = 'icons/ui_icons/common/padlock.png')
parents = list("common.css" = 'html/browser/common.css')
/datum/asset/simple/permissions
assets = list(
"search.js" = 'html/admin/search.js',
"panels.css" = 'html/admin/panels.css'
)
/datum/asset/group/permissions
children = list(
/datum/asset/simple/permissions,
/datum/asset/simple/namespaced/common
)
/datum/asset/simple/notes
assets = list(
"high_button.png" = 'icons/ui_icons/notes/high_button.png',
"medium_button.png" = 'icons/ui_icons/notes/medium_button.png',
"minor_button.png" = 'icons/ui_icons/notes/minor_button.png',
"none_button.png" = 'icons/ui_icons/notes/none_button.png',
)
/datum/asset/simple/arcade
assets = list(
"boss1.gif" = 'icons/ui_icons/arcade/boss1.gif',
"boss2.gif" = 'icons/ui_icons/arcade/boss2.gif',
"boss3.gif" = 'icons/ui_icons/arcade/boss3.gif',
"boss4.gif" = 'icons/ui_icons/arcade/boss4.gif',
"boss5.gif" = 'icons/ui_icons/arcade/boss5.gif',
"boss6.gif" = 'icons/ui_icons/arcade/boss6.gif',
)
/datum/asset/spritesheet/simple/achievements
name ="achievements"
/datum/asset/spritesheet/simple/achievements/create_spritesheets()
InsertAll("", ACHIEVEMENTS_SET)
/datum/asset/spritesheet/simple/pills
name = "pills"
assets = list(
"pill1" = 'icons/ui_icons/pills/pill1.png',
"pill2" = 'icons/ui_icons/pills/pill2.png',
"pill3" = 'icons/ui_icons/pills/pill3.png',
"pill4" = 'icons/ui_icons/pills/pill4.png',
"pill5" = 'icons/ui_icons/pills/pill5.png',
"pill6" = 'icons/ui_icons/pills/pill6.png',
"pill7" = 'icons/ui_icons/pills/pill7.png',
"pill8" = 'icons/ui_icons/pills/pill8.png',
"pill9" = 'icons/ui_icons/pills/pill9.png',
"pill10" = 'icons/ui_icons/pills/pill10.png',
"pill11" = 'icons/ui_icons/pills/pill11.png',
"pill12" = 'icons/ui_icons/pills/pill12.png',
"pill13" = 'icons/ui_icons/pills/pill13.png',
"pill14" = 'icons/ui_icons/pills/pill14.png',
"pill15" = 'icons/ui_icons/pills/pill15.png',
"pill16" = 'icons/ui_icons/pills/pill16.png',
"pill17" = 'icons/ui_icons/pills/pill17.png',
"pill18" = 'icons/ui_icons/pills/pill18.png',
"pill19" = 'icons/ui_icons/pills/pill19.png',
"pill20" = 'icons/ui_icons/pills/pill20.png',
"pill21" = 'icons/ui_icons/pills/pill21.png',
"pill22" = 'icons/ui_icons/pills/pill22.png',
)
/datum/asset/spritesheet/simple/patches
name = "patches"
assets = list(
"bandaid" = 'icons/ui_icons/patches/bandaid.png',
"bandaid_brute" = 'icons/ui_icons/patches/bandaid_brute.png',
"bandaid_burn" = 'icons/ui_icons/patches/bandaid_burn.png',
"bandaid_both" = 'icons/ui_icons/patches/bandaid_both.png'
)
/datum/asset/spritesheet/simple/condiments
name = "condiments"
assets = list(
CONDIMASTER_STYLE_FALLBACK = 'icons/ui_icons/condiments/emptycondiment.png',
"enzyme" = 'icons/ui_icons/condiments/enzyme.png',
"flour" = 'icons/ui_icons/condiments/flour.png',
"mayonnaise" = 'icons/ui_icons/condiments/mayonnaise.png',
"milk" = 'icons/ui_icons/condiments/milk.png',
"blackpepper" = 'icons/ui_icons/condiments/peppermillsmall.png',
"rice" = 'icons/ui_icons/condiments/rice.png',
"sodiumchloride" = 'icons/ui_icons/condiments/saltshakersmall.png',
"soymilk" = 'icons/ui_icons/condiments/soymilk.png',
"soysauce" = 'icons/ui_icons/condiments/soysauce.png',
"sugar" = 'icons/ui_icons/condiments/sugar.png',
"ketchup" = 'icons/ui_icons/condiments/ketchup.png',
"capsaicin" = 'icons/ui_icons/condiments/hotsauce.png',
"frostoil" = 'icons/ui_icons/condiments/coldsauce.png',
"bbqsauce" = 'icons/ui_icons/condiments/bbqsauce.png',
"cornoil" = 'icons/ui_icons/condiments/oliveoil.png',
)
//this exists purely to avoid meta by pre-loading all language icons.
/datum/asset/language/register()
for(var/path in typesof(/datum/language))
set waitfor = FALSE
var/datum/language/L = new path ()
L.get_icon()
/datum/asset/spritesheet/pipes
name = "pipes"
/datum/asset/spritesheet/pipes/create_spritesheets()
for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi'))
InsertAll("", each, GLOB.alldirs)
/datum/asset/spritesheet/supplypods
name = "supplypods"
/datum/asset/spritesheet/supplypods/create_spritesheets()
for (var/style in 1 to length(GLOB.podstyles))
if (style == STYLE_SEETHROUGH)
Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi' , "seethrough-icon"))
continue
var/base = GLOB.podstyles[style][POD_BASE]
if (!base)
Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi', "invisible-icon"))
continue
var/icon/podIcon = icon('icons/obj/supplypods.dmi', base)
var/door = GLOB.podstyles[style][POD_DOOR]
if (door)
door = "[base]_door"
podIcon.Blend(icon('icons/obj/supplypods.dmi', door), ICON_OVERLAY)
var/shape = GLOB.podstyles[style][POD_SHAPE]
if (shape == POD_SHAPE_NORML)
var/decal = GLOB.podstyles[style][POD_DECAL]
if (decal)
podIcon.Blend(icon('icons/obj/supplypods.dmi', decal), ICON_OVERLAY)
var/glow = GLOB.podstyles[style][POD_GLOW]
if (glow)
glow = "pod_glow_[glow]"
podIcon.Blend(icon('icons/obj/supplypods.dmi', glow), ICON_OVERLAY)
Insert("pod_asset[style]", podIcon)
// Representative icons for each research design
/datum/asset/spritesheet/research_designs
name = "design"
/datum/asset/spritesheet/research_designs/create_spritesheets()
for (var/path in subtypesof(/datum/design))
var/datum/design/D = path
var/icon_file
var/icon_state
var/icon/I
if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest
icon_file = initial(D.research_icon)
icon_state = initial(D.research_icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
I = icon(icon_file, icon_state, SOUTH)
else
// construct the icon and slap it into the resource cache
var/atom/item = initial(D.build_path)
if (!ispath(item, /atom))
// biogenerator outputs to beakers by default
if (initial(D.build_type) & BIOGENERATOR)
item = /obj/item/reagent_containers/glass/beaker/large
else
continue // shouldn't happen, but just in case
// circuit boards become their resulting machines or computers
if (ispath(item, /obj/item/circuitboard))
var/obj/item/circuitboard/C = item
var/machine = initial(C.build_path)
if (machine)
item = machine
// Check for GAGS support where necessary
var/greyscale_config = initial(item.greyscale_config)
var/greyscale_colors = initial(item.greyscale_colors)
if (greyscale_config && greyscale_colors)
icon_file = SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)
else
icon_file = initial(item.icon)
icon_state = initial(item.icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
I = icon(icon_file, icon_state, SOUTH)
// computers (and snowflakes) get their screen and keyboard sprites
if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control))
var/obj/machinery/computer/C = item
var/screen = initial(C.icon_screen)
var/keyboard = initial(C.icon_keyboard)
var/all_states = icon_states(icon_file)
if (screen && (screen in all_states))
I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY)
if (keyboard && (keyboard in all_states))
I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY)
Insert(initial(D.id), I)
/datum/asset/spritesheet/vending
name = "vending"
/datum/asset/spritesheet/vending/create_spritesheets()
for (var/k in GLOB.vending_products)
var/atom/item = k
if (!ispath(item, /atom))
continue
var/icon_file
if (initial(item.greyscale_colors) && initial(item.greyscale_config))
icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors))
else
icon_file = initial(item.icon)
var/icon_state = initial(item.icon_state)
var/icon/I
var/icon_states_list = icon_states(icon_file)
if(icon_state in icon_states_list)
I = icon(icon_file, icon_state, SOUTH)
var/c = initial(item.color)
if (!isnull(c) && c != "#FFFFFF")
I.Blend(c, ICON_MULTIPLY)
else
var/icon_states_string
for (var/an_icon_state in icon_states_list)
if (!icon_states_string)
icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
else
icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
I = icon('icons/turf/floors.dmi', "", SOUTH)
var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
Insert(imgid, I)
/datum/asset/simple/genetics
assets = list(
"dna_discovered.gif" = 'icons/ui_icons/dna/dna_discovered.gif',
"dna_undiscovered.gif" = 'icons/ui_icons/dna/dna_undiscovered.gif',
"dna_extra.gif" = 'icons/ui_icons/dna/dna_extra.gif'
)
/datum/asset/simple/orbit
assets = list(
"ghost.png" = 'icons/ui_icons/orbit/ghost.png'
)
/datum/asset/simple/vv
assets = list(
"view_variables.css" = 'html/admin/view_variables.css'
)
/datum/asset/spritesheet/sheetmaterials
name = "sheetmaterials"
/datum/asset/spritesheet/sheetmaterials/create_spritesheets()
InsertAll("", 'icons/obj/stack_objects.dmi')
// Special case to handle Bluespace Crystals
Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
/datum/asset/spritesheet/mafia
name = "mafia"
/datum/asset/spritesheet/mafia/create_spritesheets()
InsertAll("", 'icons/obj/mafia.dmi')
/datum/asset/simple/portraits
assets = list()
/datum/asset/simple/portraits/New()
if(!length(SSpersistent_paintings.paintings))
return
for(var/datum/painting/portrait as anything in SSpersistent_paintings.paintings)
var/png = "data/paintings/images/[portrait.md5].png"
if(fexists(png))
var/asset_name = "paintings_[portrait.md5]"
assets[asset_name] = png
..() //this is where it registers all these assets we added to the list
/datum/asset/simple/safe
assets = list(
"safe_dial.png" = 'icons/ui_icons/safe/safe_dial.png'
)
/datum/asset/simple/contracts
assets = list(
"bluespace.png" = 'icons/ui_icons/contracts/bluespace.png',
"destruction.png" = 'icons/ui_icons/contracts/destruction.png',
"healing.png" = 'icons/ui_icons/contracts/healing.png',
"robeless.png" = 'icons/ui_icons/contracts/robeless.png',
)
/datum/asset/spritesheet/fish
name = "fish"
/datum/asset/spritesheet/fish/create_spritesheets()
for (var/path in subtypesof(/datum/aquarium_behaviour/fish))
var/datum/aquarium_behaviour/fish/fish_type = path
var/fish_icon = initial(fish_type.icon)
var/fish_icon_state = initial(fish_type.icon_state)
var/id = sanitize_css_class_name("[fish_icon][fish_icon_state]")
if(sprites[id]) //no dupes
continue
Insert(id, fish_icon, fish_icon_state)
/datum/asset/simple/adventure
assets = list(
"default" = 'icons/ui_icons/adventure/default.png',
"grue" = 'icons/ui_icons/adventure/grue.png',
"signal_lost" ='icons/ui_icons/adventure/signal_lost.png',
"trade" = 'icons/ui_icons/adventure/trade.png',
)
/datum/asset/simple/inventory
assets = list(
"inventory-glasses.png" = 'icons/ui_icons/inventory/glasses.png',
"inventory-head.png" = 'icons/ui_icons/inventory/head.png',
"inventory-neck.png" = 'icons/ui_icons/inventory/neck.png',
"inventory-mask.png" = 'icons/ui_icons/inventory/mask.png',
"inventory-ears.png" = 'icons/ui_icons/inventory/ears.png',
"inventory-uniform.png" = 'icons/ui_icons/inventory/uniform.png',
"inventory-suit.png" = 'icons/ui_icons/inventory/suit.png',
"inventory-gloves.png" = 'icons/ui_icons/inventory/gloves.png',
"inventory-hand_l.png" = 'icons/ui_icons/inventory/hand_l.png',
"inventory-hand_r.png" = 'icons/ui_icons/inventory/hand_r.png',
"inventory-shoes.png" = 'icons/ui_icons/inventory/shoes.png',
"inventory-suit_storage.png" = 'icons/ui_icons/inventory/suit_storage.png',
"inventory-id.png" = 'icons/ui_icons/inventory/id.png',
"inventory-belt.png" = 'icons/ui_icons/inventory/belt.png',
"inventory-back.png" = 'icons/ui_icons/inventory/back.png',
"inventory-pocket.png" = 'icons/ui_icons/inventory/pocket.png',
"inventory-collar.png" = 'icons/ui_icons/inventory/collar.png',
)
/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts
/proc/sanitize_css_class_name(name)
var/static/regex/regex = new(@"[^a-zA-Z0-9]","g")
return replacetext(name, regex, "")
/datum/asset/simple/tutorial_advisors
assets = list(
"chem_help_advisor.gif" = 'icons/ui_icons/advisors/chem_help_advisor.gif',
)
/datum/asset/spritesheet/moods
name = "moods"
var/iconinserted = 1
/datum/asset/spritesheet/moods/create_spritesheets()
for(var/i in 1 to 9)
var/target_to_insert = "mood"+"[iconinserted]"
Insert(target_to_insert, 'icons/hud/screen_gen.dmi', target_to_insert)
iconinserted++
/datum/asset/spritesheet/moods/ModifyInserted(icon/pre_asset)
var/blended_color
switch(iconinserted)
if(1)
blended_color = "#f15d36"
if(2 to 3)
blended_color = "#f38943"
if(4)
blended_color = "#dfa65b"
if(5)
blended_color = "#4b96c4"
if(6)
blended_color = "#86d656"
else
blended_color = "#2eeb9a"
pre_asset.Blend(blended_color, ICON_MULTIPLY)
return pre_asset
/// Sends information needed for uplinks
/datum/asset/json/uplink
name = "uplink"
/datum/asset/json/uplink/generate()
var/list/data = list()
var/list/categories = list()
var/list/items = list()
for(var/datum/uplink_category/category as anything in subtypesof(/datum/uplink_category))
categories += category
categories = sortTim(categories, .proc/cmp_uplink_category_desc)
var/list/new_categories = list()
for(var/datum/uplink_category/category as anything in categories)
new_categories += initial(category.name)
categories = new_categories
for(var/datum/uplink_item/item_path as anything in subtypesof(/datum/uplink_item))
var/datum/uplink_item/item = new item_path()
if(item.item) {
items += list(list(
"id" = item_path,
"name" = item.name,
"cost" = item.cost,
"desc" = item.desc,
"category" = item.category? initial(item.category.name) : null,
"purchasable_from" = item.purchasable_from,
"restricted" = item.restricted,
"limited_stock" = item.limited_stock,
"restricted_roles" = item.restricted_roles,
"restricted_species" = item.restricted_species,
"progression_minimum" = item.progression_minimum,
))
}
SStraitor.uplink_items += item
SStraitor.uplink_items_by_type[item_path] = item
data["items"] = items
data["categories"] = categories
return data
@@ -0,0 +1,5 @@
/datum/asset/spritesheet/simple/achievements
name ="achievements"
/datum/asset/spritesheet/simple/achievements/create_spritesheets()
InsertAll("", ACHIEVEMENTS_SET)
@@ -0,0 +1,7 @@
/datum/asset/simple/adventure
assets = list(
"default" = 'icons/ui_icons/adventure/default.png',
"grue" = 'icons/ui_icons/adventure/grue.png',
"signal_lost" ='icons/ui_icons/adventure/signal_lost.png',
"trade" = 'icons/ui_icons/adventure/trade.png',
)
@@ -0,0 +1,9 @@
/datum/asset/simple/arcade
assets = list(
"boss1.gif" = 'icons/ui_icons/arcade/boss1.gif',
"boss2.gif" = 'icons/ui_icons/arcade/boss2.gif',
"boss3.gif" = 'icons/ui_icons/arcade/boss3.gif',
"boss4.gif" = 'icons/ui_icons/arcade/boss4.gif',
"boss5.gif" = 'icons/ui_icons/arcade/boss5.gif',
"boss6.gif" = 'icons/ui_icons/arcade/boss6.gif',
)
@@ -0,0 +1,23 @@
/// Spritesheet for body zones. Necessary if your tgui uses BodyZoneSelector
// This is a simple sheet instead of a spritesheet because spritesheets don't support
// -ms-interpolation-mode when resized, since you need `transform: scale`.
// Also spritesheets have some weird fudge on the edges of them because of an IE bug I can't track down.
/datum/asset/simple/body_zones
/datum/asset/simple/body_zones/register()
assets["body_zones.base.png"] = icon('icons/hud/screen_midnight.dmi', "zone_sel")
add_limb(BODY_ZONE_HEAD)
add_limb(BODY_ZONE_CHEST)
add_limb(BODY_ZONE_L_ARM)
add_limb(BODY_ZONE_R_ARM)
add_limb(BODY_ZONE_L_LEG)
add_limb(BODY_ZONE_R_LEG)
add_limb(BODY_ZONE_PRECISE_EYES)
add_limb(BODY_ZONE_PRECISE_MOUTH)
add_limb(BODY_ZONE_PRECISE_GROIN)
return ..()
/datum/asset/simple/body_zones/proc/add_limb(limb)
assets[SANITIZE_FILENAME("body_zones.[limb].png")] = icon('icons/hud/screen_gen.dmi', limb)
+14
View File
@@ -0,0 +1,14 @@
/datum/asset/spritesheet/chat
name = "chat"
/datum/asset/spritesheet/chat/create_spritesheets()
InsertAll("emoji", EMOJI_SET)
// pre-loading all lanugage icons also helps to avoid meta
InsertAll("language", 'icons/misc/language.dmi')
// catch languages which are pulling icons from another file
for(var/path in typesof(/datum/language))
var/datum/language/L = path
var/icon = initial(L.icon)
if (icon != 'icons/misc/language.dmi')
var/icon_state = initial(L.icon_state)
Insert("language-[icon_state]", icon, icon_state=icon_state)
@@ -0,0 +1,4 @@
/datum/asset/simple/circuit_assets
assets = list(
"grid_background.png" = 'icons/ui_icons/tgui/grid_background.png'
)
@@ -0,0 +1,3 @@
/datum/asset/simple/namespaced/common
assets = list("padlock.png" = 'icons/ui_icons/common/padlock.png')
parents = list("common.css" = 'html/browser/common.css')
@@ -0,0 +1,20 @@
/datum/asset/spritesheet/simple/condiments
name = "condiments"
assets = list(
CONDIMASTER_STYLE_FALLBACK = 'icons/ui_icons/condiments/emptycondiment.png',
"enzyme" = 'icons/ui_icons/condiments/enzyme.png',
"flour" = 'icons/ui_icons/condiments/flour.png',
"mayonnaise" = 'icons/ui_icons/condiments/mayonnaise.png',
"milk" = 'icons/ui_icons/condiments/milk.png',
"blackpepper" = 'icons/ui_icons/condiments/peppermillsmall.png',
"rice" = 'icons/ui_icons/condiments/rice.png',
"sodiumchloride" = 'icons/ui_icons/condiments/saltshakersmall.png',
"soymilk" = 'icons/ui_icons/condiments/soymilk.png',
"soysauce" = 'icons/ui_icons/condiments/soysauce.png',
"sugar" = 'icons/ui_icons/condiments/sugar.png',
"ketchup" = 'icons/ui_icons/condiments/ketchup.png',
"capsaicin" = 'icons/ui_icons/condiments/hotsauce.png',
"frostoil" = 'icons/ui_icons/condiments/coldsauce.png',
"bbqsauce" = 'icons/ui_icons/condiments/bbqsauce.png',
"cornoil" = 'icons/ui_icons/condiments/oliveoil.png',
)
@@ -0,0 +1,7 @@
/datum/asset/simple/contracts
assets = list(
"bluespace.png" = 'icons/ui_icons/contracts/bluespace.png',
"destruction.png" = 'icons/ui_icons/contracts/destruction.png',
"healing.png" = 'icons/ui_icons/contracts/healing.png',
"robeless.png" = 'icons/ui_icons/contracts/robeless.png',
)
+12
View File
@@ -0,0 +1,12 @@
/datum/asset/spritesheet/fish
name = "fish"
/datum/asset/spritesheet/fish/create_spritesheets()
for (var/path in subtypesof(/datum/aquarium_behaviour/fish))
var/datum/aquarium_behaviour/fish/fish_type = path
var/fish_icon = initial(fish_type.icon)
var/fish_icon_state = initial(fish_type.icon_state)
var/id = sanitize_css_class_name("[fish_icon][fish_icon_state]")
if(sprites[id]) //no dupes
continue
Insert(id, fish_icon, fish_icon_state)
@@ -0,0 +1,9 @@
/datum/asset/simple/namespaced/fontawesome
assets = list(
"fa-regular-400.eot" = 'html/font-awesome/webfonts/fa-regular-400.eot',
"fa-regular-400.woff" = 'html/font-awesome/webfonts/fa-regular-400.woff',
"fa-solid-900.eot" = 'html/font-awesome/webfonts/fa-solid-900.eot',
"fa-solid-900.woff" = 'html/font-awesome/webfonts/fa-solid-900.woff',
"v4shim.css" = 'html/font-awesome/css/v4-shims.min.css',
)
parents = list("font-awesome.css" = 'html/font-awesome/css/all.min.css')
@@ -0,0 +1,6 @@
/datum/asset/simple/genetics
assets = list(
"dna_discovered.gif" = 'icons/ui_icons/dna/dna_discovered.gif',
"dna_undiscovered.gif" = 'icons/ui_icons/dna/dna_undiscovered.gif',
"dna_extra.gif" = 'icons/ui_icons/dna/dna_extra.gif'
)
@@ -0,0 +1,31 @@
/datum/asset/simple/headers
assets = list(
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
"alarm_red.gif" = 'icons/program_icons/alarm_red.gif',
"batt_5.gif" = 'icons/program_icons/batt_5.gif',
"batt_20.gif" = 'icons/program_icons/batt_20.gif',
"batt_40.gif" = 'icons/program_icons/batt_40.gif',
"batt_60.gif" = 'icons/program_icons/batt_60.gif',
"batt_80.gif" = 'icons/program_icons/batt_80.gif',
"batt_100.gif" = 'icons/program_icons/batt_100.gif',
"charging.gif" = 'icons/program_icons/charging.gif',
"downloader_finished.gif" = 'icons/program_icons/downloader_finished.gif',
"downloader_running.gif" = 'icons/program_icons/downloader_running.gif',
"ntnrc_idle.gif" = 'icons/program_icons/ntnrc_idle.gif',
"ntnrc_new.gif" = 'icons/program_icons/ntnrc_new.gif',
"power_norm.gif" = 'icons/program_icons/power_norm.gif',
"power_warn.gif" = 'icons/program_icons/power_warn.gif',
"sig_high.gif" = 'icons/program_icons/sig_high.gif',
"sig_low.gif" = 'icons/program_icons/sig_low.gif',
"sig_lan.gif" = 'icons/program_icons/sig_lan.gif',
"sig_none.gif" = 'icons/program_icons/sig_none.gif',
"smmon_0.gif" = 'icons/program_icons/smmon_0.gif',
"smmon_1.gif" = 'icons/program_icons/smmon_1.gif',
"smmon_2.gif" = 'icons/program_icons/smmon_2.gif',
"smmon_3.gif" = 'icons/program_icons/smmon_3.gif',
"smmon_4.gif" = 'icons/program_icons/smmon_4.gif',
"smmon_5.gif" = 'icons/program_icons/smmon_5.gif',
"smmon_6.gif" = 'icons/program_icons/smmon_6.gif',
"borg_mon.gif" = 'icons/program_icons/borg_mon.gif',
"robotact.gif" = 'icons/program_icons/robotact.gif'
)
@@ -0,0 +1,20 @@
/datum/asset/simple/inventory
assets = list(
"inventory-glasses.png" = 'icons/ui_icons/inventory/glasses.png',
"inventory-head.png" = 'icons/ui_icons/inventory/head.png',
"inventory-neck.png" = 'icons/ui_icons/inventory/neck.png',
"inventory-mask.png" = 'icons/ui_icons/inventory/mask.png',
"inventory-ears.png" = 'icons/ui_icons/inventory/ears.png',
"inventory-uniform.png" = 'icons/ui_icons/inventory/uniform.png',
"inventory-suit.png" = 'icons/ui_icons/inventory/suit.png',
"inventory-gloves.png" = 'icons/ui_icons/inventory/gloves.png',
"inventory-hand_l.png" = 'icons/ui_icons/inventory/hand_l.png',
"inventory-hand_r.png" = 'icons/ui_icons/inventory/hand_r.png',
"inventory-shoes.png" = 'icons/ui_icons/inventory/shoes.png',
"inventory-suit_storage.png" = 'icons/ui_icons/inventory/suit_storage.png',
"inventory-id.png" = 'icons/ui_icons/inventory/id.png',
"inventory-belt.png" = 'icons/ui_icons/inventory/belt.png',
"inventory-back.png" = 'icons/ui_icons/inventory/back.png',
"inventory-pocket.png" = 'icons/ui_icons/inventory/pocket.png',
"inventory-collar.png" = 'icons/ui_icons/inventory/collar.png',
)
+10
View File
@@ -0,0 +1,10 @@
/datum/asset/simple/irv
assets = list(
"jquery-ui.custom-core-widgit-mouse-sortable.min.js" = 'html/jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js',
)
/datum/asset/group/irv
children = list(
/datum/asset/simple/jquery,
/datum/asset/simple/irv
)
@@ -0,0 +1,5 @@
/datum/asset/simple/jquery
legacy = TRUE
assets = list(
"jquery.min.js" = 'html/jquery/jquery.min.js',
)
@@ -0,0 +1,9 @@
//this exists purely to avoid meta by pre-loading all language icons.
/datum/asset/language
/datum/asset/language/register()
set waitfor = FALSE
for(var/path in typesof(/datum/language))
var/datum/language/language = new path()
language.get_icon()
+4
View File
@@ -0,0 +1,4 @@
/datum/asset/simple/lobby
assets = list(
"playeroptions.css" = 'html/browser/playeroptions.css'
)
+5
View File
@@ -0,0 +1,5 @@
/datum/asset/spritesheet/mafia
name = "mafia"
/datum/asset/spritesheet/mafia/create_spritesheets()
InsertAll("", 'icons/obj/mafia.dmi')
+27
View File
@@ -0,0 +1,27 @@
/datum/asset/spritesheet/moods
name = "moods"
var/iconinserted = 1
/datum/asset/spritesheet/moods/create_spritesheets()
for(var/i in 1 to 9)
var/target_to_insert = "mood"+"[iconinserted]"
Insert(target_to_insert, 'icons/hud/screen_gen.dmi', target_to_insert)
iconinserted++
/datum/asset/spritesheet/moods/ModifyInserted(icon/pre_asset)
var/blended_color
switch(iconinserted)
if(1)
blended_color = "#f15d36"
if(2 to 3)
blended_color = "#f38943"
if(4)
blended_color = "#dfa65b"
if(5)
blended_color = "#4b96c4"
if(6)
blended_color = "#86d656"
else
blended_color = "#2eeb9a"
pre_asset.Blend(blended_color, ICON_MULTIPLY)
return pre_asset
+7
View File
@@ -0,0 +1,7 @@
/datum/asset/simple/notes
assets = list(
"high_button.png" = 'icons/ui_icons/notes/high_button.png',
"medium_button.png" = 'icons/ui_icons/notes/medium_button.png',
"minor_button.png" = 'icons/ui_icons/notes/minor_button.png',
"none_button.png" = 'icons/ui_icons/notes/none_button.png',
)
+4
View File
@@ -0,0 +1,4 @@
/datum/asset/simple/orbit
assets = list(
"ghost.png" = 'icons/ui_icons/orbit/ghost.png'
)
+19
View File
@@ -0,0 +1,19 @@
/datum/asset/spritesheet/simple/paper
name = "paper"
assets = list(
"stamp-clown" = 'icons/stamp_icons/large_stamp-clown.png',
"stamp-deny" = 'icons/stamp_icons/large_stamp-deny.png',
"stamp-ok" = 'icons/stamp_icons/large_stamp-ok.png',
"stamp-hop" = 'icons/stamp_icons/large_stamp-hop.png',
"stamp-cmo" = 'icons/stamp_icons/large_stamp-cmo.png',
"stamp-ce" = 'icons/stamp_icons/large_stamp-ce.png',
"stamp-hos" = 'icons/stamp_icons/large_stamp-hos.png',
"stamp-rd" = 'icons/stamp_icons/large_stamp-rd.png',
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png',
"stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png',
"stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
"stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
"stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png'
)
@@ -0,0 +1,8 @@
/datum/asset/spritesheet/simple/patches
name = "patches"
assets = list(
"bandaid" = 'icons/ui_icons/patches/bandaid.png',
"bandaid_brute" = 'icons/ui_icons/patches/bandaid_brute.png',
"bandaid_burn" = 'icons/ui_icons/patches/bandaid_burn.png',
"bandaid_both" = 'icons/ui_icons/patches/bandaid_both.png'
)
+34
View File
@@ -0,0 +1,34 @@
/datum/asset/spritesheet/simple/pda
name = "pda"
assets = list(
"atmos" = 'icons/pda_icons/pda_atmos.png',
"back" = 'icons/pda_icons/pda_back.png',
"bell" = 'icons/pda_icons/pda_bell.png',
"blank" = 'icons/pda_icons/pda_blank.png',
"boom" = 'icons/pda_icons/pda_boom.png',
"bucket" = 'icons/pda_icons/pda_bucket.png',
"medbot" = 'icons/pda_icons/pda_medbot.png',
"floorbot" = 'icons/pda_icons/pda_floorbot.png',
"cleanbot" = 'icons/pda_icons/pda_cleanbot.png',
"crate" = 'icons/pda_icons/pda_crate.png',
"cuffs" = 'icons/pda_icons/pda_cuffs.png',
"eject" = 'icons/pda_icons/pda_eject.png',
"flashlight" = 'icons/pda_icons/pda_flashlight.png',
"honk" = 'icons/pda_icons/pda_honk.png',
"mail" = 'icons/pda_icons/pda_mail.png',
"medical" = 'icons/pda_icons/pda_medical.png',
"menu" = 'icons/pda_icons/pda_menu.png',
"mule" = 'icons/pda_icons/pda_mule.png',
"notes" = 'icons/pda_icons/pda_notes.png',
"power" = 'icons/pda_icons/pda_power.png',
"rdoor" = 'icons/pda_icons/pda_rdoor.png',
"reagent" = 'icons/pda_icons/pda_reagent.png',
"refresh" = 'icons/pda_icons/pda_refresh.png',
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
"skills" = 'icons/pda_icons/pda_skills.png',
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png',
"droneblacklist" = 'icons/pda_icons/pda_droneblacklist.png',
)
@@ -0,0 +1,11 @@
/datum/asset/simple/permissions
assets = list(
"search.js" = 'html/admin/search.js',
"panels.css" = 'html/admin/panels.css'
)
/datum/asset/group/permissions
children = list(
/datum/asset/simple/permissions,
/datum/asset/simple/namespaced/common
)
+26
View File
@@ -0,0 +1,26 @@
/datum/asset/spritesheet/simple/pills
name = "pills"
assets = list(
"pill1" = 'icons/ui_icons/pills/pill1.png',
"pill2" = 'icons/ui_icons/pills/pill2.png',
"pill3" = 'icons/ui_icons/pills/pill3.png',
"pill4" = 'icons/ui_icons/pills/pill4.png',
"pill5" = 'icons/ui_icons/pills/pill5.png',
"pill6" = 'icons/ui_icons/pills/pill6.png',
"pill7" = 'icons/ui_icons/pills/pill7.png',
"pill8" = 'icons/ui_icons/pills/pill8.png',
"pill9" = 'icons/ui_icons/pills/pill9.png',
"pill10" = 'icons/ui_icons/pills/pill10.png',
"pill11" = 'icons/ui_icons/pills/pill11.png',
"pill12" = 'icons/ui_icons/pills/pill12.png',
"pill13" = 'icons/ui_icons/pills/pill13.png',
"pill14" = 'icons/ui_icons/pills/pill14.png',
"pill15" = 'icons/ui_icons/pills/pill15.png',
"pill16" = 'icons/ui_icons/pills/pill16.png',
"pill17" = 'icons/ui_icons/pills/pill17.png',
"pill18" = 'icons/ui_icons/pills/pill18.png',
"pill19" = 'icons/ui_icons/pills/pill19.png',
"pill20" = 'icons/ui_icons/pills/pill20.png',
"pill21" = 'icons/ui_icons/pills/pill21.png',
"pill22" = 'icons/ui_icons/pills/pill22.png',
)
+6
View File
@@ -0,0 +1,6 @@
/datum/asset/spritesheet/pipes
name = "pipes"
/datum/asset/spritesheet/pipes/create_spritesheets()
for (var/each in list('icons/obj/atmospherics/pipes/pipe_item.dmi', 'icons/obj/atmospherics/pipes/disposal.dmi', 'icons/obj/atmospherics/pipes/transit_tube.dmi', 'icons/obj/plumbing/fluid_ducts.dmi'))
InsertAll("", each, GLOB.alldirs)
@@ -0,0 +1,12 @@
/datum/asset/simple/portraits
assets = list()
/datum/asset/simple/portraits/New()
if(!length(SSpersistent_paintings.paintings))
return
for(var/datum/painting/portrait as anything in SSpersistent_paintings.paintings)
var/png = "data/paintings/images/[portrait.md5].png"
if(fexists(png))
var/asset_name = "paintings_[portrait.md5]"
assets[asset_name] = png
..() //this is where it registers all these assets we added to the list
+6
View File
@@ -0,0 +1,6 @@
/datum/asset/simple/radar_assets
assets = list(
"ntosradarbackground.png" = 'icons/ui_icons/tgui/ntosradar_background.png',
"ntosradarpointer.png" = 'icons/ui_icons/tgui/ntosradar_pointer.png',
"ntosradarpointerS.png" = 'icons/ui_icons/tgui/ntosradar_pointer_S.png'
)
@@ -0,0 +1,63 @@
// Representative icons for each research design
/datum/asset/spritesheet/research_designs
name = "design"
/datum/asset/spritesheet/research_designs/create_spritesheets()
for (var/path in subtypesof(/datum/design))
var/datum/design/D = path
var/icon_file
var/icon_state
var/icon/I
if(initial(D.research_icon) && initial(D.research_icon_state)) //If the design has an icon replacement skip the rest
icon_file = initial(D.research_icon)
icon_state = initial(D.research_icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
I = icon(icon_file, icon_state, SOUTH)
else
// construct the icon and slap it into the resource cache
var/atom/item = initial(D.build_path)
if (!ispath(item, /atom))
// biogenerator outputs to beakers by default
if (initial(D.build_type) & BIOGENERATOR)
item = /obj/item/reagent_containers/glass/beaker/large
else
continue // shouldn't happen, but just in case
// circuit boards become their resulting machines or computers
if (ispath(item, /obj/item/circuitboard))
var/obj/item/circuitboard/C = item
var/machine = initial(C.build_path)
if (machine)
item = machine
// Check for GAGS support where necessary
var/greyscale_config = initial(item.greyscale_config)
var/greyscale_colors = initial(item.greyscale_colors)
if (greyscale_config && greyscale_colors)
icon_file = SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)
else
icon_file = initial(item.icon)
icon_state = initial(item.icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
I = icon(icon_file, icon_state, SOUTH)
// computers (and snowflakes) get their screen and keyboard sprites
if (ispath(item, /obj/machinery/computer) || ispath(item, /obj/machinery/power/solar_control))
var/obj/machinery/computer/C = item
var/screen = initial(C.icon_screen)
var/keyboard = initial(C.icon_keyboard)
var/all_states = icon_states(icon_file)
if (screen && (screen in all_states))
I.Blend(icon(icon_file, screen, SOUTH), ICON_OVERLAY)
if (keyboard && (keyboard in all_states))
I.Blend(icon(icon_file, keyboard, SOUTH), ICON_OVERLAY)
Insert(initial(D.id), I)
+4
View File
@@ -0,0 +1,4 @@
/datum/asset/simple/safe
assets = list(
"safe_dial.png" = 'icons/ui_icons/safe/safe_dial.png'
)
@@ -0,0 +1,8 @@
/datum/asset/spritesheet/sheetmaterials
name = "sheetmaterials"
/datum/asset/spritesheet/sheetmaterials/create_spritesheets()
InsertAll("", 'icons/obj/stack_objects.dmi')
// Special case to handle Bluespace Crystals
Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
@@ -0,0 +1,27 @@
/datum/asset/spritesheet/supplypods
name = "supplypods"
/datum/asset/spritesheet/supplypods/create_spritesheets()
for (var/style in 1 to length(GLOB.podstyles))
if (style == STYLE_SEETHROUGH)
Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi' , "seethrough-icon"))
continue
var/base = GLOB.podstyles[style][POD_BASE]
if (!base)
Insert("pod_asset[style]", icon('icons/obj/supplypods.dmi', "invisible-icon"))
continue
var/icon/podIcon = icon('icons/obj/supplypods.dmi', base)
var/door = GLOB.podstyles[style][POD_DOOR]
if (door)
door = "[base]_door"
podIcon.Blend(icon('icons/obj/supplypods.dmi', door), ICON_OVERLAY)
var/shape = GLOB.podstyles[style][POD_SHAPE]
if (shape == POD_SHAPE_NORML)
var/decal = GLOB.podstyles[style][POD_DECAL]
if (decal)
podIcon.Blend(icon('icons/obj/supplypods.dmi', decal), ICON_OVERLAY)
var/glow = GLOB.podstyles[style][POD_GLOW]
if (glow)
glow = "pod_glow_[glow]"
podIcon.Blend(icon('icons/obj/supplypods.dmi', glow), ICON_OVERLAY)
Insert("pod_asset[style]", podIcon)
@@ -0,0 +1,8 @@
/datum/asset/simple/namespaced/tgfont
assets = list(
"tgfont.eot" = file("tgui/packages/tgfont/static/tgfont.eot"),
"tgfont.woff2" = file("tgui/packages/tgfont/static/tgfont.woff2"),
)
parents = list(
"tgfont.css" = file("tgui/packages/tgfont/static/tgfont.css"),
)
+13
View File
@@ -0,0 +1,13 @@
/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
"tgui.bundle.js" = file("tgui/public/tgui.bundle.js"),
"tgui.bundle.css" = file("tgui/public/tgui.bundle.css"),
)
/datum/asset/simple/tgui_panel
keep_local_name = TRUE
assets = list(
"tgui-panel.bundle.js" = file("tgui/public/tgui-panel.bundle.js"),
"tgui-panel.bundle.css" = file("tgui/public/tgui-panel.bundle.css"),
)
@@ -0,0 +1,4 @@
/datum/asset/simple/tutorial_advisors
assets = list(
"chem_help_advisor.gif" = 'icons/ui_icons/advisors/chem_help_advisor.gif',
)
+40
View File
@@ -0,0 +1,40 @@
/// Sends information needed for uplinks
/datum/asset/json/uplink
name = "uplink"
/datum/asset/json/uplink/generate()
var/list/data = list()
var/list/categories = list()
var/list/items = list()
for(var/datum/uplink_category/category as anything in subtypesof(/datum/uplink_category))
categories += category
categories = sortTim(categories, .proc/cmp_uplink_category_desc)
var/list/new_categories = list()
for(var/datum/uplink_category/category as anything in categories)
new_categories += initial(category.name)
categories = new_categories
for(var/datum/uplink_item/item_path as anything in subtypesof(/datum/uplink_item))
var/datum/uplink_item/item = new item_path()
if(item.item) {
items += list(list(
"id" = item_path,
"name" = item.name,
"cost" = item.cost,
"desc" = item.desc,
"category" = item.category? initial(item.category.name) : null,
"purchasable_from" = item.purchasable_from,
"restricted" = item.restricted,
"limited_stock" = item.limited_stock,
"restricted_roles" = item.restricted_roles,
"restricted_species" = item.restricted_species,
"progression_minimum" = item.progression_minimum,
))
}
SStraitor.uplink_items += item
SStraitor.uplink_items_by_type[item_path] = item
data["items"] = items
data["categories"] = categories
return data
@@ -0,0 +1,36 @@
/datum/asset/spritesheet/vending
name = "vending"
/datum/asset/spritesheet/vending/create_spritesheets()
for (var/k in GLOB.vending_products)
var/atom/item = k
if (!ispath(item, /atom))
continue
var/icon_file
if (initial(item.greyscale_colors) && initial(item.greyscale_config))
icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors))
else
icon_file = initial(item.icon)
var/icon_state = initial(item.icon_state)
var/icon/I
var/icon_states_list = icon_states(icon_file)
if(icon_state in icon_states_list)
I = icon(icon_file, icon_state, SOUTH)
var/c = initial(item.color)
if (!isnull(c) && c != "#FFFFFF")
I.Blend(c, ICON_MULTIPLY)
else
var/icon_states_string
for (var/an_icon_state in icon_states_list)
if (!icon_states_string)
icon_states_string = "[json_encode(an_icon_state)](\ref[an_icon_state])"
else
icon_states_string += ", [json_encode(an_icon_state)](\ref[an_icon_state])"
stack_trace("[item] does not have a valid icon state, icon=[icon_file], icon_state=[json_encode(icon_state)](\ref[icon_state]), icon_states=[icon_states_string]")
I = icon('icons/turf/floors.dmi', "", SOUTH)
var/imgid = replacetext(replacetext("[item]", "/obj/item/", ""), "/", "-")
Insert(imgid, I)
+4
View File
@@ -0,0 +1,4 @@
/datum/asset/simple/vv
assets = list(
"view_variables.css" = 'html/admin/view_variables.css'
)
+4 -2
View File
@@ -22,9 +22,9 @@
var/requires_tech = FALSE //handles techweb-oriented surgeries, previously restricted to the /advanced subtype (You still need to add designs)
var/replaced_by //type; doesn't show up if this type exists. Set to /datum/surgery if you want to hide a "base" surgery (useful for typing parents IE healing.dm just make sure to null it out again)
/// Organ being directly manipulated, used for checking if the organ is still in the body after surgery has begun
var/organ_to_manipulate
var/organ_to_manipulate
/datum/surgery/New(surgery_target, surgery_location, surgery_bodypart)
/datum/surgery/New(atom/surgery_target, surgery_location, surgery_bodypart)
..()
if(!surgery_target)
return
@@ -39,6 +39,8 @@
operated_wound = operated_bodypart.get_wound_type(targetable_wound)
operated_wound.attached_surgery = src
SEND_SIGNAL(surgery_target, COMSIG_MOB_SURGERY_STARTED, src, surgery_location, surgery_bodypart)
/datum/surgery/Destroy()
if(operated_wound)
operated_wound.attached_surgery = null
+1 -1
View File
@@ -230,7 +230,7 @@
/obj/item/surgical_drapes/Initialize(mapload)
. = ..()
AddElement(/datum/element/surgery_initiator)
AddComponent(/datum/component/surgery_initiator)
/obj/item/surgical_processor //allows medical cyborgs to scan and initiate advanced surgeries
+42 -2
View File
@@ -207,6 +207,7 @@
#include "code\__DEFINES\dcs\signals\signals_hydroponic.dm"
#include "code\__DEFINES\dcs\signals\signals_janitor.dm"
#include "code\__DEFINES\dcs\signals\signals_light_eater.dm"
#include "code\__DEFINES\dcs\signals\signals_medical.dm"
#include "code\__DEFINES\dcs\signals\signals_mind.dm"
#include "code\__DEFINES\dcs\signals\signals_mod.dm"
#include "code\__DEFINES\dcs\signals\signals_mood.dm"
@@ -757,6 +758,7 @@
#include "code\datums\components\strong_pull.dm"
#include "code\datums\components\subtype_picker.dm"
#include "code\datums\components\summoning.dm"
#include "code\datums\components\surgery_initiator.dm"
#include "code\datums\components\swabbing.dm"
#include "code\datums\components\swarming.dm"
#include "code\datums\components\tackle.dm"
@@ -942,7 +944,6 @@
#include "code\datums\elements\spooky.dm"
#include "code\datums\elements\squish.dm"
#include "code\datums\elements\strippable.dm"
#include "code\datums\elements\surgery_initiator.dm"
#include "code\datums\elements\swabbable.dm"
#include "code\datums\elements\tenacious.dm"
#include "code\datums\elements\tool_flash.dm"
@@ -2123,7 +2124,46 @@
#include "code\modules\asset_cache\asset_cache_client.dm"
#include "code\modules\asset_cache\asset_cache_item.dm"
#include "code\modules\asset_cache\asset_list.dm"
#include "code\modules\asset_cache\asset_list_items.dm"
#include "code\modules\asset_cache\assets\achievements.dm"
#include "code\modules\asset_cache\assets\adventure.dm"
#include "code\modules\asset_cache\assets\arcade.dm"
#include "code\modules\asset_cache\assets\body_zones.dm"
#include "code\modules\asset_cache\assets\chat.dm"
#include "code\modules\asset_cache\assets\circuits.dm"
#include "code\modules\asset_cache\assets\common.dm"
#include "code\modules\asset_cache\assets\condiments.dm"
#include "code\modules\asset_cache\assets\contracts.dm"
#include "code\modules\asset_cache\assets\fish.dm"
#include "code\modules\asset_cache\assets\fontawesome.dm"
#include "code\modules\asset_cache\assets\genetics.dm"
#include "code\modules\asset_cache\assets\headers.dm"
#include "code\modules\asset_cache\assets\inventory.dm"
#include "code\modules\asset_cache\assets\irv.dm"
#include "code\modules\asset_cache\assets\jquery.dm"
#include "code\modules\asset_cache\assets\language.dm"
#include "code\modules\asset_cache\assets\lobby.dm"
#include "code\modules\asset_cache\assets\mafia.dm"
#include "code\modules\asset_cache\assets\moods.dm"
#include "code\modules\asset_cache\assets\notes.dm"
#include "code\modules\asset_cache\assets\orbit.dm"
#include "code\modules\asset_cache\assets\paper.dm"
#include "code\modules\asset_cache\assets\patches.dm"
#include "code\modules\asset_cache\assets\pda.dm"
#include "code\modules\asset_cache\assets\permissions.dm"
#include "code\modules\asset_cache\assets\pills.dm"
#include "code\modules\asset_cache\assets\pipes.dm"
#include "code\modules\asset_cache\assets\portraits.dm"
#include "code\modules\asset_cache\assets\radar.dm"
#include "code\modules\asset_cache\assets\research_designs.dm"
#include "code\modules\asset_cache\assets\safe.dm"
#include "code\modules\asset_cache\assets\sheetmaterials.dm"
#include "code\modules\asset_cache\assets\supplypods.dm"
#include "code\modules\asset_cache\assets\tgfont.dm"
#include "code\modules\asset_cache\assets\tgui.dm"
#include "code\modules\asset_cache\assets\tutorial_advisors.dm"
#include "code\modules\asset_cache\assets\uplink.dm"
#include "code\modules\asset_cache\assets\vending.dm"
#include "code\modules\asset_cache\assets\vv.dm"
#include "code\modules\asset_cache\transports\asset_transport.dm"
#include "code\modules\asset_cache\transports\webroot_transport.dm"
#include "code\modules\atmospherics\environmental\LINDA_fire.dm"
@@ -0,0 +1,150 @@
import { Component, createRef } from "inferno";
import { resolveAsset } from "../assets";
import { Box } from "./Box";
export enum BodyZone {
Head = "head",
Chest = "chest",
LeftArm = "l_arm",
RightArm = "r_arm",
LeftLeg = "l_leg",
RightLeg = "r_leg",
Eyes = "eyes",
Mouth = "mouth",
Groin = "groin",
}
const bodyZonePixelToZone: (x: number, y: number) => (BodyZone | null)
= (x, y) => {
// TypeScript translation of /atom/movable/screen/zone_sel/proc/get_zone_at
if (y < 1) {
return null;
} else if (y < 10) {
if (x > 10 && x < 15) {
return BodyZone.RightLeg;
} else if (x > 17 && x < 22) {
return BodyZone.LeftLeg;
}
} else if (y < 13) {
if (x > 8 && x < 11) {
return BodyZone.RightArm;
} else if (x > 12 && x < 20) {
return BodyZone.Groin;
} else if (x > 21 && x < 24) {
return BodyZone.LeftArm;
}
} else if (y < 22) {
if (x > 8 && x < 11) {
return BodyZone.RightArm;
} else if (x > 12 && x < 20) {
return BodyZone.Chest;
} else if (x > 21 && x < 24) {
return BodyZone.LeftArm;
}
} else if (y < 30 && (x > 12 && x < 20)) {
if (y > 23 && y < 24 && (x > 15 && x < 17)) {
return BodyZone.Mouth;
} else if (y > 25 && y < 27 && (x > 14 && x < 18)) {
return BodyZone.Eyes;
} else {
return BodyZone.Head;
}
}
return null;
};
type BodyZoneSelectorProps = {
onClick?: (zone: BodyZone) => void,
scale?: number,
selectedZone: BodyZone | null,
}
type BodyZoneSelectorState = {
hoverZone: BodyZone | null,
}
export class BodyZoneSelector
extends Component<BodyZoneSelectorProps, BodyZoneSelectorState>
{
ref = createRef<HTMLDivElement>();
state: BodyZoneSelectorState = {
hoverZone: null,
}
render() {
const { hoverZone } = this.state;
const { scale = 3, selectedZone } = this.props;
return (
<div ref={this.ref} style={{
width: `${32 * scale}px`,
height: `${32 * scale}px`,
position: "relative",
}}>
<Box
as="img"
src={resolveAsset("body_zones.base.png")}
onClick={() => {
const onClick = this.props.onClick;
if (onClick && this.state.hoverZone) {
onClick(this.state.hoverZone);
}
}}
onMouseMove={(event) => {
if (!this.props.onClick) {
return;
}
const rect = this.ref.current?.getBoundingClientRect();
if (!rect) {
return;
}
const x = event.clientX - rect.left;
const y = (32 * scale) - (event.clientY - rect.top);
this.setState({
hoverZone: bodyZonePixelToZone(x / scale, y / scale),
});
}}
style={{
"-ms-interpolation-mode": "nearest-neighbor",
"position": "absolute",
"width": `${32 * scale}px`,
"height": `${32 * scale}px`,
}}
/>
{selectedZone && (
<Box
as="img"
src={resolveAsset(`body_zones.${selectedZone}.png`)}
style={{
"-ms-interpolation-mode": "nearest-neighbor",
"pointer-events": "none",
"position": "absolute",
"width": `${32 * scale}px`,
"height": `${32 * scale}px`,
}}
/>
)}
{hoverZone && (hoverZone !== selectedZone) && (
<Box
as="img"
src={resolveAsset(`body_zones.${hoverZone}.png`)}
style={{
"-ms-interpolation-mode": "nearest-neighbor",
"opacity": 0.5,
"pointer-events": "none",
"position": "absolute",
"width": `${32 * scale}px`,
"height": `${32 * scale}px`,
}}
/>
)}
</div>
);
}
}
@@ -0,0 +1,172 @@
import { sortBy } from "common/collections";
import { KEY_DOWN, KEY_ENTER, KEY_UP } from "common/keycodes";
import { BooleanLike } from "common/react";
import { Component } from "inferno";
import { useBackend } from "../backend";
import { Button, KeyListener, Stack } from "../components";
import { BodyZone, BodyZoneSelector } from "../components/BodyZoneSelector";
import { Window } from "../layouts";
type Surgery = {
name: string,
blocked?: BooleanLike,
};
type SurgeryInitiatorData = {
selected_zone: BodyZone,
surgeries: Surgery[],
target_name: string,
};
const sortSurgeries
= sortBy((surgery: Surgery) => surgery.name);
type SurgeryInitiatorInnerState = {
selectedSurgeryIndex: number,
};
class SurgeryInitiatorInner extends Component<
SurgeryInitiatorData,
SurgeryInitiatorInnerState
> {
state = {
selectedSurgeryIndex: 0,
}
componentDidMount() {
this.updateSelectedSurgeryIndexState();
}
componentDidUpdate(prevProps: SurgeryInitiatorData) {
if (prevProps.selected_zone !== this.props.selected_zone) {
this.updateSelectedSurgeryIndexState();
}
}
updateSelectedSurgeryIndexState() {
this.setState({
selectedSurgeryIndex: this.findSelectedSurgeryAfter(-1) || 0,
});
}
findSelectedSurgeryAfter(after: number): number | undefined {
const foundIndex
= this.props.surgeries.findIndex(
(surgery, index) => index > after && !surgery.blocked
);
return foundIndex === -1 ? undefined : foundIndex;
}
findSelectedSurgeryBefore(before: number): number | undefined {
for (let index = before; index >= 0; index--) {
const surgery = this.props.surgeries[index];
if (!surgery.blocked) {
return index;
}
}
return undefined;
}
render() {
const { act } = useBackend<SurgeryInitiatorData>(this.context);
const { selected_zone, surgeries, target_name } = this.props;
return (
<Window
width={400}
height={350}
title={`Surgery on ${target_name}`}
>
<Window.Content>
<Stack fill height="100%">
<Stack.Item width="30%">
<BodyZoneSelector
onClick={(zone) => act("change_zone", { new_zone: zone })}
selectedZone={selected_zone}
/>
</Stack.Item>
<Stack.Item width="95%">
<Stack vertical height="100%">
{surgeries.map((surgery, index) => (
<Button
onClick={() => {
act("start_surgery", {
surgery_name: surgery.name,
});
}}
disabled={surgery.blocked}
selected={index === this.state.selectedSurgeryIndex}
tooltip={surgery.blocked ? "Their body is covered!" : undefined}
key={surgery.name}
fluid
>
{surgery.name}
</Button>
))}
</Stack>
</Stack.Item>
</Stack>
<KeyListener
onKeyDown={(event) => {
const keyCode = event.code;
const surgery = surgeries[this.state.selectedSurgeryIndex];
switch (keyCode) {
case KEY_DOWN:
this.setState((state) => {
return {
selectedSurgeryIndex:
this.findSelectedSurgeryAfter(
state.selectedSurgeryIndex
) || this.findSelectedSurgeryAfter(-1) || 0,
};
});
break;
case KEY_UP:
this.setState((state) => {
return {
selectedSurgeryIndex:
this.findSelectedSurgeryBefore(
state.selectedSurgeryIndex - 1
)
?? this.findSelectedSurgeryBefore(
this.props.surgeries.length - 1
)
?? 0,
};
});
break;
case KEY_ENTER:
if (surgery) {
act("start_surgery", {
surgery_name: surgery.name,
});
}
break;
}
}}
/>
</Window.Content>
</Window>
);
}
}
export const SurgeryInitiator = (props, context) => {
const { data } = useBackend<SurgeryInitiatorData>(context);
return (
<SurgeryInitiatorInner
selected_zone={data.selected_zone}
surgeries={sortSurgeries(data.surgeries)}
target_name={data.target_name}
/>
);
};