Merge branch 'release' of https://github.com/VOREStation/VOREStation into voreupdate-2

This commit is contained in:
izac112
2024-12-04 15:21:21 +01:00
51 changed files with 1908 additions and 258 deletions
+4
View File
@@ -188,6 +188,10 @@ What is the naming convention for planes or layers?
#define PLANE_PLAYER_HUD_ITEMS 96 //Separate layer with which to apply colorblindness
#define PLANE_PLAYER_HUD_ABOVE 97 //Things above the player hud
#define RADIAL_BACKGROUND_LAYER 0
///1000 is an unimportant number, it's just to normalize copied layers
#define RADIAL_CONTENT_LAYER 1000
#define PLANE_ADMIN3 99 //Purely for shenanigans (above HUD)
+49 -1
View File
@@ -1 +1,49 @@
#define MOB_WATER_LAYER 36
// These are used as the layers for the icons, as well as indexes in a list that holds onto them.
// Technically the layers used are all -100+layer to make them FLOAT_LAYER overlays.
//Human Overlays Indexes/////////
#define MUTATIONS_LAYER 1 //Mutations like fat, and lasereyes
#define SKIN_LAYER 2 //Skin things added by a call on species
#define BLOOD_LAYER 3 //Bloodied hands/feet/anything else
#define BODYPARTS_LAYER 4 //Bodyparts layer
#define MOB_DAM_LAYER 5 //Injury overlay sprites like open wounds
#define SURGERY_LAYER 6 //Overlays for open surgical sites
#define UNDERWEAR_LAYER 7 //Underwear/bras/etc
#define TAIL_LOWER_LAYER 8 //Tail as viewed from the south
#define WING_LOWER_LAYER 9 //Wings as viewed from the south
#define SHOES_LAYER_ALT 10 //Shoe-slot item (when set to be under uniform via verb)
#define UNIFORM_LAYER 11 //Uniform-slot item
#define ID_LAYER 12 //ID-slot item
#define SHOES_LAYER 13 //Shoe-slot item
#define GLOVES_LAYER 14 //Glove-slot item
#define BELT_LAYER 15 //Belt-slot item
#define SUIT_LAYER 16 //Suit-slot item
#define TAIL_UPPER_LAYER 17 //Some species have tails to render (As viewed from the N, E, or W)
#define GLASSES_LAYER 18 //Eye-slot item
#define BELT_LAYER_ALT 19 //Belt-slot item (when set to be above suit via verb)
#define SUIT_STORE_LAYER 20 //Suit storage-slot item
#define BACK_LAYER 21 //Back-slot item
#define HAIR_LAYER 22 //The human's hair
#define HAIR_ACCESSORY_LAYER 23 //Simply move this up a number if things are added.
#define EARS_LAYER 24 //Both ear-slot items (combined image)
#define EYES_LAYER 25 //Mob's eyes (used for glowing eyes)
#define FACEMASK_LAYER 26 //Mask-slot item
#define GLASSES_LAYER_ALT 27 //So some glasses can appear on top of hair and things
#define HEAD_LAYER 28 //Head-slot item
#define HANDCUFF_LAYER 29 //Handcuffs, if the human is handcuffed, in a secret inv slot
#define LEGCUFF_LAYER 30 //Same as handcuffs, for legcuffs
#define L_HAND_LAYER 31 //Left-hand item
#define R_HAND_LAYER 32 //Right-hand item
#define WING_LAYER 33 //Wings or protrusions over the suit.
#define TAIL_UPPER_LAYER_ALT 34 //Modified tail-sprite layer. Tend to be larger.
#define MODIFIER_EFFECTS_LAYER 35 //Effects drawn by modifiers
#define FIRE_LAYER 36 //'Mob on fire' overlay layer
#define MOB_WATER_LAYER 37
#define TARGETED_LAYER 38 //'Aimed at' overlay layer
#define VORE_BELLY_LAYER 39
#define VORE_TAIL_LAYER 40
#define TOTAL_LAYERS 40 // <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list.
//These two are only used for gargoyles currently
#define HUMAN_BODY_LAYERS list(MUTATIONS_LAYER, TAIL_LOWER_LAYER, WING_LOWER_LAYER, BODYPARTS_LAYER, SKIN_LAYER, BLOOD_LAYER, MOB_DAM_LAYER, TAIL_UPPER_LAYER, HAIR_LAYER, HAIR_ACCESSORY_LAYER, EYES_LAYER, WING_LAYER, VORE_BELLY_LAYER, VORE_TAIL_LAYER, TAIL_UPPER_LAYER_ALT)
#define HUMAN_OTHER_LAYERS list(MODIFIER_EFFECTS_LAYER, FIRE_LAYER, MOB_WATER_LAYER, TARGETED_LAYER)
+137 -38
View File
@@ -3,33 +3,52 @@
GLOBAL_LIST_EMPTY(radial_menus)
// Ported from TG
/obj/screen/radial
icon = 'icons/mob/radial.dmi'
layer = LAYER_HUD_ABOVE
plane = PLANE_PLAYER_HUD_ABOVE
vis_flags = VIS_INHERIT_PLANE
var/click_on_hover = FALSE
var/datum/radial_menu/parent
/obj/screen/radial/proc/set_parent(new_value)
if(parent)
UnregisterSignal(parent, COMSIG_PARENT_QDELETING)
parent = new_value
if(parent)
RegisterSignal(parent, COMSIG_PARENT_QDELETING, PROC_REF(handle_parent_del))
/obj/screen/radial/proc/handle_parent_del()
SIGNAL_HANDLER
set_parent(null)
/obj/screen/radial/slice
icon_state = "radial_slice"
var/choice
var/next_page = FALSE
var/tooltips = FALSE
/obj/screen/radial/Destroy()
parent = null
return ..()
/obj/screen/radial/slice/set_parent(new_value)
. = ..()
if(parent)
icon_state = parent.radial_slice_icon
/obj/screen/radial/slice/MouseEntered(location, control, params)
. = ..()
icon_state = "radial_slice_focus"
if(next_page || !parent)
icon_state = "radial_slice_focus"
else
icon_state = "[parent.radial_slice_icon]_focus"
if(tooltips)
openToolTip(usr, src, params, title = name)
if (click_on_hover && !isnull(usr) && !isnull(parent))
Click(location, control, params)
/obj/screen/radial/slice/MouseExited(location, control, params)
. = ..()
icon_state = "radial_slice"
if(next_page || !parent)
icon_state = "radial_slice"
else
icon_state = parent.radial_slice_icon
if(tooltips)
closeToolTip(usr)
@@ -38,7 +57,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
if(next_page)
parent.next_page()
else
parent.element_chosen(choice,usr)
parent.element_chosen(choice, usr, params)
/obj/screen/radial/center
name = "Close Menu"
@@ -57,9 +76,18 @@ GLOBAL_LIST_EMPTY(radial_menus)
parent.finished = TRUE
/datum/radial_menu
var/list/choices = list() //List of choice id's
var/list/choices_icons = list() //choice_id -> icon
var/list/choices_values = list() //choice_id -> choice
/// List of choice IDs
var/list/choices = list()
/// choice_id -> icon
var/list/choices_icons = list()
/// choice_id -> choice
var/list/choices_values = list()
/// choice_id -> /datum/radial_menu_choice
var/list/choice_datums = list()
var/list/page_data = list() //list of choices per page
@@ -87,6 +115,9 @@ GLOBAL_LIST_EMPTY(radial_menus)
var/py_shift = 0
var/entry_animation = TRUE
///A replacement icon state for the generic radial slice bg icon. Doesn't affect the next page nor the center buttons
var/radial_slice_icon
//If we swap to vis_contens inventory these will need a redo
/datum/radial_menu/proc/check_screen_border(mob/user)
var/atom/movable/AM = anchor
@@ -98,6 +129,8 @@ GLOBAL_LIST_EMPTY(radial_menus)
else
py_shift = 32
restrict_to_dir(NORTH) //I was going to parse screen loc here but that's more effort than it's worth.
else if(hudfix_method && AM.loc)
anchor = get_atom_on_turf(anchor)
//Sets defaults
//These assume 45 deg min_angle
@@ -116,7 +149,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
starting_angle = 180
ending_angle = 45
/datum/radial_menu/proc/setup_menu(use_tooltips)
/datum/radial_menu/proc/setup_menu(use_tooltips, set_page = 1, click_on_hover = FALSE)
if(ending_angle > starting_angle)
zone = ending_angle - starting_angle
else
@@ -129,7 +162,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
for(var/i in 1 to elements_to_add) //Create all elements
var/obj/screen/radial/slice/new_element = new /obj/screen/radial/slice
new_element.tooltips = use_tooltips
new_element.parent = src
new_element.set_parent(src)
elements += new_element
var/page = 1
@@ -152,22 +185,31 @@ GLOBAL_LIST_EMPTY(radial_menus)
page_data[page] = current
pages = page
current_page = 1
update_screen_objects(anim = entry_animation)
current_page = clamp(set_page, 1, pages)
update_screen_objects(entry_animation, click_on_hover)
/datum/radial_menu/proc/update_screen_objects(anim = FALSE)
/datum/radial_menu/proc/update_screen_objects(anim = FALSE, click_on_hover = FALSE)
var/list/page_choices = page_data[current_page]
var/angle_per_element = round(zone / page_choices.len)
for(var/i in 1 to elements.len)
var/obj/screen/radial/E = elements[i]
var/obj/screen/radial/element = elements[i]
var/angle = WRAP(starting_angle + (i - 1) * angle_per_element,0,360)
if(i > page_choices.len)
HideElement(E)
HideElement(element)
element.click_on_hover = FALSE
else
SetElement(E,page_choices[i],angle,anim = anim,anim_order = i)
SetElement(element,page_choices[i],angle,anim = anim,anim_order = i)
// Only activate click on hover after the animation plays
if (!click_on_hover)
continue
if (anim)
addtimer(VARSET_CALLBACK(element, click_on_hover, TRUE), i * 0.5)
else
element.click_on_hover = TRUE
/datum/radial_menu/proc/HideElement(obj/screen/radial/slice/E)
E.cut_overlays()
E.vis_contents.Cut()
E.alpha = 0
E.name = "None"
E.maptext = null
@@ -194,13 +236,22 @@ GLOBAL_LIST_EMPTY(radial_menus)
E.alpha = 255
E.mouse_opacity = MOUSE_OPACITY_ICON
E.cut_overlays()
E.vis_contents.Cut()
if(choice_id == NEXT_PAGE_ID)
E.name = "Next Page"
E.next_page = TRUE
E.icon_state = "radial_slice" // Resets the bg icon state to the default for next page buttons.
E.add_overlay("radial_next")
else
if(istext(choices_values[choice_id]))
//This isn't granted to exist, so use the ?. operator for conditionals that use it.
var/datum/radial_menu_choice/choice_datum = choice_datums[choice_id]
if(choice_datum?.name)
E.name = choice_datum.name
else if(istext(choices_values[choice_id]))
E.name = choices_values[choice_id]
else if(ispath(choices_values[choice_id],/atom))
var/atom/A = choices_values[choice_id]
E.name = initial(A.name)
else
var/atom/movable/AM = choices_values[choice_id] //Movables only
E.name = AM.name
@@ -209,15 +260,21 @@ GLOBAL_LIST_EMPTY(radial_menus)
E.next_page = FALSE
if(choices_icons[choice_id])
E.add_overlay(choices_icons[choice_id])
if (choice_datum?.info)
var/obj/effect/abstract/info/info_button = new(E, choice_datum.info)
info_button.plane = PLANE_PLAYER_HUD_ABOVE
info_button.layer = RADIAL_CONTENT_LAYER
E.vis_contents += info_button
/datum/radial_menu/New()
close_button = new
close_button.parent = src
close_button.set_parent(src)
/datum/radial_menu/proc/Reset()
choices.Cut()
choices_icons.Cut()
choices_values.Cut()
choice_datums.Cut()
current_page = 1
/datum/radial_menu/proc/element_chosen(choice_id,mob/user)
@@ -226,7 +283,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
/datum/radial_menu/proc/get_next_id()
return "c_[choices.len]"
/datum/radial_menu/proc/set_choices(list/new_choices, use_tooltips)
/datum/radial_menu/proc/set_choices(list/new_choices, use_tooltips, click_on_hover = FALSE, set_page = 1)
if(choices.len)
Reset()
for(var/E in new_choices)
@@ -237,13 +294,20 @@ GLOBAL_LIST_EMPTY(radial_menus)
var/I = extract_image(new_choices[E])
if(I)
choices_icons[id] = I
setup_menu(use_tooltips)
if (istype(new_choices[E], /datum/radial_menu_choice))
choice_datums[id] = new_choices[E]
setup_menu(use_tooltips, set_page, click_on_hover)
/datum/radial_menu/proc/extract_image(E)
var/mutable_appearance/MA = new /mutable_appearance(E)
/datum/radial_menu/proc/extract_image(to_extract_from)
if (istype(to_extract_from, /datum/radial_menu_choice))
var/datum/radial_menu_choice/choice = to_extract_from
to_extract_from = choice.image
var/mutable_appearance/MA = new /mutable_appearance(to_extract_from)
if(MA)
MA.layer = LAYER_HUD_ABOVE
MA.plane = PLANE_PLAYER_HUD_ABOVE
MA.layer = RADIAL_CONTENT_LAYER
MA.appearance_flags |= RESET_TRANSFORM
return MA
@@ -253,15 +317,16 @@ GLOBAL_LIST_EMPTY(radial_menus)
current_page = WRAP(current_page + 1,1,pages+1)
update_screen_objects()
/datum/radial_menu/proc/show_to(mob/M)
/datum/radial_menu/proc/show_to(mob/M, offset_x = 0, offset_y = 0)
if(current_user)
hide()
if(!M.client || !anchor)
return
current_user = M.client
//Blank
menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing",layer = LAYER_HUD_ABOVE)
menu_holder.appearance_flags |= KEEP_APART
menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing", layer = RADIAL_BACKGROUND_LAYER, pixel_x = offset_x, pixel_y = offset_y)
menu_holder.plane = PLANE_PLAYER_HUD_ABOVE
menu_holder.appearance_flags |= KEEP_APART|RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM
menu_holder.vis_contents += elements + close_button
current_user.images += menu_holder
@@ -283,9 +348,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
/datum/radial_menu/Destroy()
Reset()
hide()
QDEL_LIST_NULL(elements)
QDEL_NULL(close_button)
QDEL_NULL(custom_check_callback)
custom_check_callback = null
. = ..()
/*
@@ -293,30 +356,66 @@ GLOBAL_LIST_EMPTY(radial_menus)
Choices should be a list where list keys are movables or text used for element names and return value
and list values are movables/icons/images used for element icons
*/
/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE)
/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE, radial_slice_icon = "radial_slice", autopick_single_option = TRUE, entry_animation = TRUE, click_on_hover = FALSE, user_space = FALSE)
if(!user || !anchor || !length(choices))
return
if(length(choices)==1 && autopick_single_option)
return choices[1]
if(!uniqueid)
uniqueid = "defmenu_[REF(user)]_[REF(anchor)]"
if(GLOB.radial_menus[uniqueid])
if(!no_repeat_close)
var/datum/radial_menu/menu = GLOB.radial_menus[uniqueid]
menu.finished = TRUE
return
var/datum/radial_menu/menu = new
menu.entry_animation = entry_animation
GLOB.radial_menus[uniqueid] = menu
if(radius)
menu.radius = radius
if(istype(custom_check))
menu.custom_check_callback = custom_check
menu.anchor = anchor
menu.anchor = user_space ? user : anchor
menu.radial_slice_icon = radial_slice_icon
menu.check_screen_border(user) //Do what's needed to make it look good near borders or on hud
menu.set_choices(choices, tooltips)
menu.show_to(user)
menu.set_choices(choices, tooltips, click_on_hover)
var/offset_x = 0
var/offset_y = 0
if (user_space)
var/turf/user_turf = get_turf(user)
var/turf/anchor_turf = get_turf(anchor)
offset_x = (anchor_turf.x - user_turf.x) * ICON_SIZE_X + anchor.pixel_x - user.pixel_x
offset_y = (anchor_turf.y - user_turf.y) * ICON_SIZE_Y + anchor.pixel_y - user.pixel_y
menu.show_to(user, offset_x, offset_y)
menu.wait(user, anchor, require_near)
var/answer = menu.selected_choice
QDEL_NULL(menu)
qdel(menu)
GLOB.radial_menus -= uniqueid
if(require_near && !in_range(anchor, user))
return
if(istype(custom_check))
if(!custom_check.Invoke())
return
return answer
/// Can be provided to choices in radial menus if you want to provide more information
/datum/radial_menu_choice
/// Required -- what to display for this button
var/image
/// If provided, this will be the name the radial slice hud button. This has priority over everything else.
var/name
/// If provided, will display an info button that will put this text in your chat
var/info
/datum/radial_menu_choice/Destroy(force)
. = ..()
QDEL_NULL(image)
#undef NEXT_PAGE_ID
#undef DEFAULT_CHECK_DELAY
+101
View File
@@ -0,0 +1,101 @@
/datum/component/gargoyle
var/energy = 100
var/transformed = FALSE
var/paused = FALSE
var/paused_loc
var/cooldown
var/mob/living/carbon/human/gargoyle //easy reference
var/obj/structure/gargoyle/statue //another easy ref
//Adjustable mod
var/identifier = "statue"
var/adjective = "hardens"
var/material = "stone"
var/tint = "#FFFFFF"
/datum/component/gargoyle/Initialize()
if (!ishuman(parent))
return COMPONENT_INCOMPATIBLE
gargoyle = parent
add_verb(gargoyle,/mob/living/carbon/human/proc/gargoyle_transformation)
add_verb(gargoyle,/mob/living/carbon/human/proc/gargoyle_pause)
add_verb(gargoyle,/mob/living/carbon/human/proc/gargoyle_checkenergy)
START_PROCESSING(SSprocessing, src)
/datum/component/gargoyle/process()
if (QDELETED(gargoyle))
return
if (paused && gargoyle.loc != paused_loc)
unpause()
if (energy > 0)
if (!transformed && !paused)
energy = max(0,energy-0.05)
else if (!transformed && isturf(gargoyle.loc))
gargoyle.gargoyle_transformation()
if (transformed)
if (!statue)
transformed = FALSE
statue.damage(-0.5)
energy = min(energy+0.3, 100)
/datum/component/gargoyle/proc/unpause()
if (!paused || transformed)
paused = FALSE
paused_loc = null
UnregisterSignal(gargoyle, COMSIG_ATOM_ENTERING)
return
if (gargoyle?.loc != paused_loc)
paused = FALSE
paused_loc = null
energy = max(energy - 5, 0)
if (energy == 0)
gargoyle.gargoyle_transformation()
UnregisterSignal(gargoyle, COMSIG_ATOM_ENTERING)
//verbs or action buttons...?
/mob/living/carbon/human/proc/gargoyle_transformation()
set name = "Gargoyle - Petrification"
set category = "Abilities.Gargoyle"
set desc = "Turn yourself into (or back from) being a gargoyle."
if (stat == DEAD)
return
var/datum/component/gargoyle/comp = GetComponent(/datum/component/gargoyle)
if (comp)
if (comp.energy <= 0 && isturf(loc))
to_chat(src, span_danger("You suddenly turn into a [comp.identifier] as you run out of energy!"))
else if (comp.cooldown > world.time)
var/time_to_wait = (comp.cooldown - world.time) / (1 SECONDS)
to_chat(src, span_warning("You can't transform just yet again! Wait for another [round(time_to_wait,0.1)] seconds!"))
return
if (istype(loc, /obj/structure/gargoyle))
qdel(loc)
else if (isturf(loc))
new /obj/structure/gargoyle(loc, src)
/mob/living/carbon/human/proc/gargoyle_pause()
set name = "Gargoyle - Pause"
set category = "Abilities.Gargoyle"
set desc = "Pause your energy while standing still, so you don't use up any more, though you will lose a small amount upon moving again."
if (stat)
return
var/datum/component/gargoyle/comp = GetComponent(/datum/component/gargoyle)
if (comp && !comp.transformed && !comp.paused)
comp.paused = TRUE
comp.paused_loc = loc
comp.RegisterSignal(src, COMSIG_ATOM_ENTERING, /datum/component/gargoyle/proc/unpause)
to_chat(src, span_notice("You start conserving your energy."))
/mob/living/carbon/human/proc/gargoyle_checkenergy()
set name = "Gargoyle - Check Energy"
set category = "Abilities.Gargoyle"
set desc = "Check how much energy you have remaining as a gargoyle."
var/datum/component/gargoyle/comp = GetComponent(/datum/component/gargoyle)
if (comp)
to_chat(src, span_notice("You have [round(comp.energy,0.01)] energy remaining. It is currently [comp.paused ? "stable" : (comp.transformed ? "increasing" : "decreasing")]."))
+10
View File
@@ -298,6 +298,16 @@
active1 = general_record
active2 = medical_record
screen = MED_DATA_RECORD
if("sync_r")
if(active2)
set_temp(client_update_record(src,usr))
if("edit_notes")
// The modal input in tgui is busted for this sadly...
var/new_notes = strip_html_simple(tgui_input_text(usr,"Enter new information here.","Character Preference", html_decode(active2.fields["notes"]), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(usr.Adjacent(src))
if(new_notes != "" || tgui_alert(usr, "Are you sure you want to delete the current record's notes?", "Confirm Delete", list("Delete", "No")) == "Delete")
if(usr.Adjacent(src))
active2.fields["notes"] = new_notes
if("new")
if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
var/datum/data/record/R = new /datum/data/record()
+10
View File
@@ -270,6 +270,16 @@
qdel(active1)
if(active2)
qdel(active2)
if("sync_r")
if(active2)
set_temp(client_update_record(src,usr))
if("edit_notes")
// The modal input in tgui is busted for this sadly...
var/new_notes = strip_html_simple(tgui_input_text(usr,"Enter new information here.","Character Preference", html_decode(active2.fields["notes"]), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(usr.Adjacent(src))
if(new_notes != "" || tgui_alert(usr, "Are you sure you want to delete the current record's notes?", "Confirm Delete", list("Delete", "No")) == "Delete")
if(usr.Adjacent(src))
active2.fields["notes"] = new_notes
if("d_rec")
var/datum/data/record/general_record = locate(params["d_rec"] || "")
if(!data_core.general.Find(general_record))
+10
View File
@@ -214,6 +214,16 @@
for(var/datum/data/record/R in data_core.general)
qdel(R)
set_temp("All employment records deleted.")
if("sync_r")
if(active1)
set_temp(client_update_record(src,active1,usr))
if("edit_notes")
// The modal input in tgui is busted for this sadly...
var/new_notes = strip_html_simple(tgui_input_text(usr,"Enter new information here.","Character Preference", html_decode(active1.fields["notes"]), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(usr.Adjacent(src))
if(new_notes != "" || tgui_alert(usr, "Are you sure you want to delete the current record's notes?", "Confirm Delete", list("Delete", "No")) == "Delete")
if(usr.Adjacent(src))
active1.fields["notes"] = new_notes
if("del_r")
if(PDA_Manifest)
PDA_Manifest.Cut()
+254
View File
@@ -0,0 +1,254 @@
/obj/machinery/petrification
name = "odd interface"
desc = "An odd looking machine with an interface, some buttons and a tiny keyboard on the side."
icon = 'icons/obj/machines/petrification.dmi'
icon_state = "petrification"
idle_power_usage = 100
active_power_usage = 1000
use_power = USE_POWER_IDLE
anchored = TRUE
unacidable = TRUE
dir = EAST
var/material = "stone"
var/identifier = "statue"
var/adjective = "hardens"
var/tint = "#ffffff"
var/able_to_unpetrify = TRUE
var/discard_clothes = TRUE
var/mob/living/carbon/human/target
var/list/remotes = list()
/obj/machinery/petrification/New()
. = ..()
if(!pixel_x && !pixel_y)
pixel_x = (dir & 3) ? 0 : (dir == 4 ? 26 : -26)
pixel_y = (dir & 3) ? (dir == 1 ? 26 : -26) : 0
/obj/machinery/petrification/proc/get_viable_targets()
var/list/targets = list()
//dir is the opposite of whichever direction we want to scan
var/turf/center
center = get_step(src, turn(dir, 180))
if (!center)
return
//square of 3x3 in front of the device
for (var/n = center.x-1; n <= center.x+1; n++)
for (var/m = center.y-1; m <= center.y+1; m++)
var/turf/T = locate(n,m,z)
if (!isturf(T))
continue
for (var/mob/living/carbon/human/H in T)
if (H.stat == DEAD)
continue
var/option = "[H]["[H]" != H.real_name ? " ([H.real_name])" : ""]"
var/r = 1
if (option in targets)
while ("[option] ([r])" in targets)
r += 1
option = "[option] ([r])"
targets[option] = H
return targets
/obj/machinery/petrification/proc/is_valid_target(var/mob/living/carbon/human/H)
if (QDELETED(H) || !istype(H) || !H.client)
return FALSE
var/turf/T = H.loc
if (!isturf(T))
return FALSE
var/turf/center
center = get_step(get_turf(src), turn(dir, 180))
if (!center)
return
if (T.z != z || T.x > center.x + 1 || T.x < center.x - 1 || T.y > center.y + 1 || T.y < center.y - 1)
return FALSE
return TRUE
/obj/machinery/petrification/proc/popup_msg(var/mob/user, var/message, var/notice = TRUE)
if (notice)
message = "A notice pops up on the interface: \"[message]\""
if (target)
to_chat(user, span_notice("[message]"))
/obj/machinery/petrification/proc/petrify(var/mob/user, var/obj/item/petrifier/petrifier = null)
. = FALSE
var/mat = material
var/idt = identifier
var/adj = adjective
var/tnt = tint
var/can_unpetrify = able_to_unpetrify
var/no_clothes = discard_clothes
var/mob/living/carbon/human/statue = target
if (petrifier && istype(petrifier))
mat = petrifier.material
idt = petrifier.identifier
adj = petrifier.adjective
tnt = petrifier.tint
can_unpetrify = petrifier.able_to_unpetrify
no_clothes = petrifier.discard_clothes
statue = petrifier.target
if (QDELETED(statue) || !istype(statue))
popup_msg(user, "Invalid target.")
return
if (statue.stat == DEAD)
popup_msg(user, "The target must be alive.")
return
if (!statue.client)
popup_msg(user, "The target must be capable of conscious thought.")
return
if (!istext(mat) || !istext(idt) || !istext(adj) || !istext(tnt))
popup_msg(user, "Invalid options.")
var/turf/T = statue.loc
if (!istype(T))
popup_msg(user, "They must be visible to the [petrifier ? "device" : "machine"].")
if (!petrifier)
var/turf/center = get_step(get_turf(src), turn(dir, 180))
if (!center)
return
if (T.z != z || T.x > center.x + 1 || T.x < center.x - 1 || T.y > center.y + 1 || T.y < center.y - 1)
popup_msg(user, "They are out of range. They must be standing within a 3x3 square in front of the machine.")
return
else
var/turf/center = get_turf(petrifier)
if (!center)
return
if (T.z != center.z || get_dist(center, T) > 4)
popup_msg(user, "They are out of range. They must be standing within 4 tiles of the device.")
return
var/datum/component/gargoyle/comp = statue.GetComponent(/datum/component/gargoyle)
if (no_clothes)
for(var/obj/item/W in statue)
if(istype(W, /obj/item/implant/backup) || istype(W, /obj/item/nif))
continue
statue.drop_from_inventory(W)
var/obj/structure/gargoyle/G = new(T, statue, idt, mat, adj, tnt, can_unpetrify, no_clothes)
G.was_rayed = TRUE
if (can_unpetrify)
add_verb(statue,/mob/living/carbon/human/proc/gargoyle_transformation)
comp?.cooldown = 0
else
remove_verb(statue,/mob/living/carbon/human/proc/gargoyle_transformation)
remove_verb(statue,/mob/living/carbon/human/proc/gargoyle_pause)
remove_verb(statue,/mob/living/carbon/human/proc/gargoyle_checkenergy)
comp?.cooldown = INFINITY
if (!petrifier)
visible_message(span_notice("A ray of purple light streams out of \the [src], aimed directly at [statue]. Everywhere the light touches on them quickly [adj] into [mat]."))
SStgui.update_uis(src)
return TRUE
/obj/machinery/petrification/attack_hand(var/mob/user as mob)
if(..())
return
user.set_machine(src)
tgui_interact(user)
/obj/machinery/petrification/tgui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "PetrificationInterface", name)
ui.open()
/obj/machinery/petrification/tgui_data(mob/user)
var/list/data = list()
data["material"] = material
data["identifier"] = identifier
data["adjective"] = adjective
data["tint"] = tint
var/list/h = rgb2num(tint)
data["t"] = ((h[1]*0.299)+(h[2]*0.587)+(h[3]*0.114)) > 102 //0.4 luminance
data["target"] = "[target ? target : "None"]"
data["able_to_unpetrify"] = able_to_unpetrify
data["discard_clothes"] = discard_clothes
data["can_remote"] = is_valid_target(target) && istext(material) && istext(identifier) && istext(adjective) && istext(tint)
return data
/obj/machinery/petrification/proc/set_input(var/option, mob/user)
var/list/only_these = list("tint","material","identifier","adjective","able_to_unpetrify","discard_clothes","target")
if (!(option in only_these))
return
switch(option)
if("tint")
var/new_color = input(user, "Choose the color for the [identifier] to be:", "Statue color", tint) as color|null
if (new_color)
tint = new_color
if("material","identifier","adjective")
var/input = tgui_input_text(user, "What should the [option] be?", "Statue [option]", vars[option], MAX_NAME_LEN)
input = sanitizeSafe(input, 25)
if (length(input) <= 0)
return
if (option == "adjective")
if (copytext_char(input, -1) != "s")
switch(copytext_char(input, -2))
if ("ss")
input += "es"
if ("sh")
input += "es"
if ("ch")
input += "es"
else
switch(copytext_char(input, -1))
if("s", "x", "z")
input += "es"
else
input += "s"
vars[option] = input
if("able_to_unpetrify", "discard_clothes")
vars[option] = !vars[option]
if("target")
var/list/targets = get_viable_targets()
if (!length(targets))
popup_msg(user, "No targets within range. Make sure there is a humanoid being within a 3x3 metre square in front of the interface.")
return
var/selected = input(user, "Choose the target.", "Petrification Target") as null|anything in targets
if (selected && ishuman(targets[selected]) && is_valid_target(targets[selected]))
var/confirmation = tgui_alert(targets[selected], "You have been selected as a petrification target. If you press confirm, you will possibly be turned into a statue, and if the option is selected, possibly one that cannot be reverted back from a statue at all.","Petrification Target",list("Confirm", "Cancel"))
if (confirmation != "Confirm")
popup_msg(user, "They declined the request.", FALSE)
return
var/double = tgui_alert(targets[selected], "This is your last warning, are you -certain-?","Petrification Target",list("Confirm", "Cancel"))
if (confirmation == "Confirm" && double == "Confirm")
target = targets[selected]
else
popup_msg(user, "They declined the request.", FALSE)
/obj/machinery/petrification/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return TRUE
if (ui.user)
add_fingerprint(ui.user)
switch(action)
if("set_option")
if (params["option"])
set_input(params["option"], ui.user)
SStgui.update_uis(src)
return TRUE
if("petrify")
petrify(ui.user)
return TRUE
if("remote")
if (is_valid_target(target) && istext(material) && istext(identifier) && istext(adjective) && istext(tint))
var/obj/item/petrifier/PE = remotes[target]
if (!QDELETED(PE))
PE.visible_message(span_warning("\The [PE] disappears!"))
qdel(PE)
var/obj/item/petrifier/P = new(loc, src)
P.material = material
P.identifier = identifier
P.adjective = adjective
P.tint = tint
P.able_to_unpetrify = able_to_unpetrify
P.discard_clothes = discard_clothes
P.target = target
remotes[target] = P
ui.user.put_in_hands(P)
return TRUE
return TRUE
/obj/item/paper/petrification_notes
name = "written notes"
info = "<font face=\"Times New Roman\">" + span_italics("Found this buried in the machine over there after digging through it a bit- I hooked it up to one of our displays so it was a bit more usable- seems to be a spare part, it was right next to another one that actually " + span_bold("was") + " hooked up. Turns things into other materials, probably one of the components that makes that machine work.") + "</font>"
@@ -194,7 +194,7 @@ var/global/list/image/splatter_cache=list()
density = FALSE
anchored = TRUE
icon = 'icons/effects/blood.dmi'
icon_state = "gibbl5"
icon_state = "gib1"
random_icon_states = list("gib1", "gib2", "gib3", "gib5", "gib6")
var/fleshcolor = "#FFFFFF"
+28
View File
@@ -0,0 +1,28 @@
/// An info button that, when clicked, puts some text in the user's chat
/obj/effect/abstract/info
name = "info"
icon = 'icons/effects/effects.dmi'
icon_state = "info"
mouse_opacity = MOUSE_OPACITY_OPAQUE
/// What should the info button display when clicked?
var/info_text
/obj/effect/abstract/info/Initialize(mapload, info_text)
. = ..()
if (!isnull(info_text))
src.info_text = info_text
/obj/effect/abstract/info/Click()
. = ..()
to_chat(usr, info_text)
/obj/effect/abstract/info/MouseEntered(location, control, params)
. = ..()
icon_state = "info_hovered"
/obj/effect/abstract/info/MouseExited()
. = ..()
icon_state = initial(icon_state)
+28
View File
@@ -0,0 +1,28 @@
/obj/item/petrifier
name = "odd button"
desc = "A metal device with a single, purple button on it, and a tiny interface."
icon = 'icons/obj/machines/petrification.dmi'
icon_state = "petrifier"
var/mob/living/carbon/human/target
var/identifier = "statue"
var/material = "stone"
var/adjective = "hardens"
var/tint = "#FFFFFF"
var/discard_clothes = TRUE
var/able_to_unpetrify = TRUE
var/obj/machinery/petrification/linked
/obj/item/petrifier/Initialize(mapload, var/to_link)
. = ..()
linked = to_link
/obj/item/petrifier/attack_self(var/mob/user)
. = ..()
if (!isturf(user.loc) && user.get_ultimate_mob() != target)
to_chat(user, span_warning("The device beeps but does nothing."))
return
if (linked?.petrify(user, src))
visible_message(span_notice("A ray of purple light streams out of \the [src], aimed directly at [target]. Everywhere the light touches on them quickly [adjective] into [material]."))
to_chat(user, span_warning("The device fizzles and crumbles into dust."))
qdel(src)
+293
View File
@@ -0,0 +1,293 @@
/obj/structure/gargoyle
name = "statue"
desc = "A very lifelike carving."
density = TRUE
anchored = TRUE
var/mob/living/carbon/human/gargoyle
var/initial_sleep
var/initial_blind
var/initial_is_shifted
var/initial_lying
var/initial_lying_prev
var/wagging
var/flapping
var/obj_integrity = 100
var/original_int = 100
var/max_integrity = 100
var/stored_examine
var/identifier = "statue"
var/material = "stone"
var/adjective = "hardens"
var/list/tail_lower_dirs = list(SOUTH, EAST, WEST)
var/image/tail_image
var/tail_alt = TAIL_UPPER_LAYER
var/can_revert = TRUE
var/was_rayed = FALSE
/obj/structure/gargoyle/Initialize(mapload, var/mob/living/carbon/human/H, var/ident_ovr, var/mat_ovr, var/adj_ovr, var/tint_ovr, var/revert = TRUE, var/discard_clothes)
. = ..()
if (isspace(loc) || isopenspace(loc))
anchored = FALSE
if (!istype(H) || !isturf(H.loc))
return
var/datum/component/gargoyle/comp = H.GetComponent(/datum/component/gargoyle)
var/tint = "#FFFFFF"
if (comp)
comp.cooldown = world.time + (15 SECONDS)
comp.statue = src
comp.transformed = TRUE
comp.paused = FALSE
identifier = length(comp.identifier) > 0 ? comp.identifier : initial(identifier)
material = length(comp.material) > 0 ? comp.material : initial(material)
tint = length(comp.tint) > 0 ? comp.tint : initial(tint)
adjective = length(comp.adjective) > 0 ? comp.adjective : initial(adjective)
if (copytext_char(adjective, -1) != "s")
adjective += "s"
gargoyle = H
if (H.get_effective_size(TRUE) < 0.5) // "So small! I can step over it!"
density = FALSE
if (ident_ovr)
identifier = ident_ovr
if (mat_ovr)
material = mat_ovr
if (adj_ovr)
adjective = adj_ovr
if (tint_ovr)
tint = tint_ovr
if (H.tail_style?.clip_mask_state)
tail_lower_dirs.Cut()
else if (H.tail_style)
tail_lower_dirs = H.tail_style.lower_layer_dirs.Copy()
tail_alt = H.tail_alt ? TAIL_UPPER_LAYER_ALT : TAIL_UPPER_LAYER
max_integrity = H.getMaxHealth() + 100
obj_integrity = H.health + 100
original_int = obj_integrity
name = "[identifier] of [H.name]"
desc = "A very lifelike [identifier] made of [material]."
stored_examine = H.examine(H)
description_fluff = H.get_description_fluff()
if (H.buckled)
H.buckled.unbuckle_mob(H, TRUE)
//icon = H.icon
//copy_overlays(H)
//calculate our tints
var/list/RGB = rgb2num(tint)
var/colorr = rgb(RGB[1]*0.299, RGB[2]*0.299, RGB[3]*0.299)
var/colorg = rgb(RGB[1]*0.587, RGB[2]*0.587, RGB[3]*0.587)
var/colorb = rgb(RGB[1]*0.114, RGB[2]*0.114, RGB[3]*0.114)
var/tint_color = list(colorr, colorg, colorb, "#000000")
var/list/body_layers = HUMAN_BODY_LAYERS
var/list/other_layers = HUMAN_OTHER_LAYERS
for (var/i = 1; i <= length(H.overlays_standing); i++)
if (i in other_layers)
continue
if (discard_clothes && !(i in body_layers))
continue
if (istype(H.overlays_standing[i], /image) && (i in body_layers))
var/image/old_image = H.overlays_standing[i]
var/image/new_image = image(old_image)
if (i == TAIL_LOWER_LAYER || i == TAIL_UPPER_LAYER || i == TAIL_UPPER_LAYER_ALT)
tail_image = new_image
new_image.color = tint_color
new_image.layer = old_image.layer
add_overlay(new_image)
else
if (!isnull(H.overlays_standing[i]))
add_overlay(H.overlays_standing[i])
initial_sleep = H.sleeping
initial_blind = H.eye_blind
initial_is_shifted = H.is_shifted
transform = H.transform
layer = H.layer
pixel_x = H.pixel_x
pixel_y = H.pixel_y
dir = H.dir
initial_lying = H.lying
initial_lying_prev = H.lying_prev
H.sdisabilities |= MUTE
if (H.appearance_flags & PIXEL_SCALE)
appearance_flags |= PIXEL_SCALE
wagging = H.wagging
H.transforming = TRUE
flapping = H.flapping
H.toggle_tail(FALSE, FALSE)
H.toggle_wing(FALSE, FALSE)
H.visible_message(span_warning("[H]'s skin rapidly [adjective] as they turn to [material]!"), span_warning("Your skin abruptly [adjective] as you turn to [material]!"))
H.forceMove(src)
H.SetBlinded(0)
H.SetSleeping(0)
H.status_flags |= GODMODE
H.updatehealth()
H.canmove = 0
can_revert = revert
START_PROCESSING(SSprocessing, src)
/obj/structure/gargoyle/Destroy()
STOP_PROCESSING(SSprocessing, src)
if (!gargoyle)
return ..()
if (can_revert)
unpetrify(deleting = FALSE) //don't delete if we're already deleting!
else
visible_message(span_warning("The [identifier] loses shape and crumbles into a pile of [material]!"))
. = ..()
/obj/structure/gargoyle/process()
if (!gargoyle)
qdel(src)
if (gargoyle.loc != src)
can_revert = TRUE //something's gone wrong, they escaped, lets not qdel them
unpetrify(deal_damage = FALSE, deleting = TRUE)
/obj/structure/gargoyle/examine_icon()
var/icon/examine_icon = icon(icon=src.icon, icon_state=src.icon_state, dir=SOUTH, frame=1, moving=0)
examine_icon.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
return examine_icon
/obj/structure/gargoyle/get_description_info()
if (gargoyle)
if (isspace(loc) || isopenspace(loc))
return
return "It can be [anchored ? "un" : ""]anchored with a wrench."
/obj/structure/gargoyle/examine(mob/user)
. = ..()
if (gargoyle && stored_examine)
. += "The [identifier] seems to have a bit more to them..."
. += stored_examine
return
/obj/structure/gargoyle/proc/unpetrify(var/deal_damage = TRUE, var/deleting = FALSE)
if (!gargoyle)
return
var/datum/component/gargoyle/comp = gargoyle.GetComponent(/datum/component/gargoyle)
if (comp)
comp.cooldown = world.time + (15 SECONDS)
comp.statue = null
comp.transformed = FALSE
else
if (was_rayed)
remove_verb(gargoyle,/mob/living/carbon/human/proc/gargoyle_transformation)
if (gargoyle.loc == src)
gargoyle.forceMove(loc)
gargoyle.transform = transform
gargoyle.pixel_x = pixel_x
gargoyle.pixel_y = pixel_y
gargoyle.is_shifted = initial_is_shifted
gargoyle.dir = dir
gargoyle.lying = initial_lying
gargoyle.lying_prev = initial_lying_prev
gargoyle.toggle_tail(wagging, FALSE)
gargoyle.toggle_wing(flapping, FALSE)
gargoyle.sdisabilities &= ~MUTE //why is there no ADD_TRAIT etc here that's actually ussssed
gargoyle.status_flags &= ~GODMODE
gargoyle.SetBlinded(initial_blind)
gargoyle.SetSleeping(initial_sleep)
gargoyle.transforming = FALSE
gargoyle.canmove = 1
gargoyle.update_canmove()
var/hurtmessage = ""
if (deal_damage)
if (obj_integrity < original_int)
var/f = (original_int - obj_integrity) / 10
for (var/x in 1 to 10)
gargoyle.adjustBruteLoss(f)
hurtmessage = " " + span_bold("You feel your body take the damage that was dealt while being [material]!")
gargoyle.updatehealth()
alpha = 0
gargoyle.visible_message(span_warning("[gargoyle]'s skin rapidly reverts, returning them to normal!"), span_warning("Your skin reverts, freeing your movement once more![hurtmessage]"))
gargoyle = null
if (deleting)
qdel(src)
/obj/structure/gargoyle/return_air()
return return_air_for_internal_lifeform()
/obj/structure/gargoyle/return_air_for_internal_lifeform(var/mob/living/lifeform)
var/air_type = /datum/gas_mixture/belly_air
if(istype(lifeform))
air_type = lifeform.get_perfect_belly_air_type()
var/air = new air_type(1000)
return air
/obj/structure/gargoyle/proc/damage(var/damage)
if (was_rayed)
return //gargoyle quick regenerates, the others don't, so let's not have them getting too damaged
obj_integrity = min(obj_integrity-damage, max_integrity)
if(obj_integrity <= 0)
qdel(src)
/obj/structure/gargoyle/take_damage(var/damage)
damage(damage)
/obj/structure/gargoyle/attack_generic(var/mob/user, var/damage, var/attack_message = "hits")
user.do_attack_animation(src)
visible_message(span_danger("[user] [attack_message] the [src]!"))
damage(damage)
/obj/structure/gargoyle/attackby(var/obj/item/W as obj, var/mob/living/user as mob)
if(W.is_wrench())
if (isspace(loc) || isopenspace(loc))
to_chat(user, span_warning("You can't anchor that here!"))
anchored = FALSE
return ..()
playsound(src, W.usesound, 50, 1)
if (do_after(user, (2 SECONDS) * W.toolspeed, target = src))
to_chat(user, span_notice("You [anchored ? "un" : ""]anchor the [src]."))
anchored = !anchored
else if(!isrobot(user) && gargoyle && gargoyle.vore_selected && gargoyle.trash_catching)
if(istype(W,/obj/item/grab || /obj/item/holder))
gargoyle.vore_attackby(W, user)
return
if(gargoyle.adminbus_trash || is_type_in_list(W,edible_trash) && W.trash_eatable && !is_type_in_list(W,item_vore_blacklist))
to_chat(user, span_warning("You slip [W] into [gargoyle]'s [lowertext(gargoyle.vore_selected.name)] ."))
user.drop_item()
W.forceMove(gargoyle.vore_selected)
return
else if (!(W.flags & NOBLUDGEON))
user.setClickCooldown(user.get_attack_speed(W))
if(W.damtype == BRUTE || W.damtype == BURN)
user.do_attack_animation(src)
playsound(src, W.hitsound, 50, 1)
damage(W.force)
else
return ..()
/obj/structure/gargoyle/set_dir(var/new_dir)
. = ..()
if(. && tail_image)
cut_overlay(tail_image)
tail_image.layer = BODY_LAYER + ((dir in tail_lower_dirs) ? TAIL_LOWER_LAYER : tail_alt)
add_overlay(tail_image)
/obj/structure/gargoyle/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR)
if(istype(AM,/obj/item) && gargoyle && gargoyle.vore_selected && gargoyle.trash_catching)
var/obj/item/I = AM
if(gargoyle.adminbus_trash || is_type_in_list(I,edible_trash) && I.trash_eatable && !is_type_in_list(I,item_vore_blacklist))
gargoyle.hitby(AM, speed)
return
else if(istype(AM,/mob/living) && gargoyle)
var/mob/living/L = AM
if(gargoyle.throw_vore && L.throw_vore && gargoyle.can_be_drop_pred && L.can_be_drop_prey)
var/drop_prey_temp = FALSE
if(gargoyle.can_be_drop_prey)
drop_prey_temp = TRUE
gargoyle.can_be_drop_prey = FALSE //Making sure the original gargoyle body is not the one getting throwvored instead.
gargoyle.hitby(L, speed)
if(drop_prey_temp)
gargoyle.can_be_drop_prey = TRUE
return
return ..()
@@ -573,6 +573,9 @@
H.l_hand.clean_blood()
H.bloody_hands = 0
H.germ_level = 0
H.hand_blood_color = null
LAZYCLEARLIST(H.blood_DNA)
H.update_bloodied()
else
user.clean_blood()
for(var/mob/V in viewers(src, null))
@@ -0,0 +1,26 @@
/datum/asset/spritesheet/kitchen_recipes
name = "kitchen_recipes"
/datum/asset/spritesheet/kitchen_recipes/create_spritesheets()
for(var/datum/recipe/R as anything in subtypesof(/datum/recipe))
add_atom_icon(R.result, sanitize_css_class_name("[R.type]"))
/datum/asset/spritesheet/kitchen_recipes/proc/add_atom_icon(typepath, id)
var/icon_file
var/icon_state
var/obj/preview_item = typepath
// if(ispath(ingredient_typepath, /datum/reagent))
// var/datum/reagent/reagent = ingredient_typepath
// preview_item = initial(reagent.default_container)
// var/datum/glass_style/style = GLOB.glass_style_singletons[preview_item]?[reagent]
// if(istype(style))
// icon_file = style.icon
// icon_state = style.icon_state
// icon_file ||= initial(preview_item.icon_preview) || initial(preview_item.icon)
// icon_state ||= initial(preview_item.icon_state_preview) || initial(preview_item.icon_state)
icon_file = initial(preview_item.icon)
icon_state = initial(preview_item.icon_state)
Insert("[id]", icon_file, icon_state)
@@ -63,6 +63,9 @@ var/global/list/valid_bloodreagents = list("default","iron","copper","phoron","s
. += link + (trait_prefs[identifier] ? "Enabled" : "Disabled")
if (2) //TRAIT_PREF_TYPE_COLOR
. += " " + color_square(hex = trait_prefs[identifier]) + link + "Change"
if (3) //TRAIT_PREF_TYPE_STRING
var/string = trait_prefs[identifier]
. += link + (length(string) > 0 ? string : "\[Empty\]")
. += "</a></li>"
. += "</ul>"
if (altered)
@@ -101,6 +104,9 @@ var/global/list/valid_bloodreagents = list("default","iron","copper","phoron","s
var/new_color = input(user, "Choose the color for this trait preference:", "Trait Preference", trait_prefs[preference]) as color|null
if (new_color)
trait_prefs[preference] = new_color
if (3) //TRAIT_PREF_TYPE_STRING
var/new_string = instance.apply_sanitization_to_string(preference, tgui_input_text(user, "What should the new value be?", instance.has_preferences[preference][2], trait_prefs[preference], MAX_NAME_LEN))
trait_prefs[preference] = new_string
// Definition of the stuff for Ears
/datum/category_item/player_setup_item/vore/traits
+129
View File
@@ -0,0 +1,129 @@
var/global/client_record_update_lock = FALSE
// Manually updating records from medical console to a player's save.
/proc/get_current_mob_from_record(var/datum/data/record/active)
var/datum/transcore_db/db = SStranscore.db_by_mind_name(active.fields["name"])
if(db)
var/datum/transhuman/mind_record/record = db.backed_up[active.fields["name"]]
if(record.mind_ref)
var/datum/mind/D = record.mind_ref
if(D.current)
var/client/C = D.current.client
if(C && C.ckey != record.ckey)
return null
return D.current
return null
/proc/client_update_record(var/obj/machinery/computer/COM, var/user)
if(!COM || QDELETED(COM))
return "Invalid console"
if(jobban_isbanned(user, "Records") )
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update syncronization denied (OOC: You are banned from editing records)"
var/record_string = ""
var/datum/data/record/active
var/console_path = null
if(istype(COM,/obj/machinery/computer/med_data))
var/obj/machinery/computer/med_data/MCOM = COM
active = MCOM.active2
record_string = "medical"
console_path = /obj/machinery/computer/med_data
if(istype(COM,/obj/machinery/computer/skills))
var/obj/machinery/computer/skills/ECOM = COM
active = ECOM.active1
record_string = "employment"
console_path = /obj/machinery/computer/skills
if(istype(COM,/obj/machinery/computer/secure_data))
var/obj/machinery/computer/secure_data/SCOM = COM
active = SCOM.active2
record_string = "security"
console_path = /obj/machinery/computer/secure_data
if(client_record_update_lock)
to_chat(user,"Update already in progress! Please wait a moment...")
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update already in progress! Please wait a moment..."
client_record_update_lock = TRUE
spawn(60 SECONDS)
client_record_update_lock = FALSE
if(!active || !console_path)
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update syncronization failed (OOC: Record or console destroyed)"
to_chat(user,"Update sent! Please wait for a response...")
message_admins("[user] pushed [record_string] record update to [active.fields["name"]].")
var/mob/M = get_current_mob_from_record(active)
if(!M)
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update syncronization failed (OOC: Client mob does not exist, has no mind record, or is possesssed)"
var/client/C = M.client
if(!C)
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update syncronization failed (OOC: Record's owner is offline)"
var/choice = tgui_alert(M, "Your [record_string] record has been updated from the a records console by [user]. Please review the changes made to your [record_string] record. Accepting these changes will SAVE your CURRENT character slot! If your new [record_string] record has errors, it is recomended to have it corrected IC instead of editing it yourself.", "Record Updated", list("Review Changes","Refuse Update"))
if(choice == "Refuse Update")
message_admins("[active.fields["name"]] refused [record_string] record update from [user] without review.")
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update syncronization failed (OOC: Client refused without review)"
var/datum/preferences/P = C.prefs
var/new_data = strip_html_simple(tgui_input_text(M,"Please review [user]'s changes to your [record_string] record before confirming. Confirming will SAVE your CURRENT character slot! If your new [record_string] record major errors, it is recomended to have it corrected IC instead of editing it yourself.","Character Preference", html_decode(active.fields["notes"]), MAX_RECORD_LENGTH, TRUE, prevent_enter = TRUE), MAX_RECORD_LENGTH)
if(!new_data)
message_admins("[active.fields["name"]] refused [record_string] record update from [user] with review.")
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update syncronization failed (OOC: Client refused with review)"
if(!M || !M.client || !P)
message_admins("[active.fields["name"]]'s [record_string] record could not be updated, client disconnected.")
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] buzzes!"))
playsound(COM, 'sound/machines/deniedbeep.ogg', 50, 0)
return "Update syncronization failed (OOC: Client does not exist)"
// Update records in the consoles, remember this can happen a while after a record is closed on the console... Use cached data.
switch(console_path)
if(/obj/machinery/computer/med_data)
P.med_record = new_data
if(active)
active.fields["notes"] = new_data
if(/obj/machinery/computer/skills)
P.gen_record = new_data
if(active)
active.fields["notes"] = new_data
if(/obj/machinery/computer/secure_data)
P.sec_record = new_data
if(active)
active.fields["notes"] = new_data
// Update player record
P.save_preferences()
P.save_character()
if(M)
to_chat(M,span_notice("Your [record_string] record for [active.fields["name"]] has been updated."))
message_admins("[active.fields["name"]] accepted the [record_string] record update from [user].")
// ding!
if(COM && !QDELETED(COM))
COM.visible_message(span_notice("\The [COM] dings!"))
playsound(COM, 'sound/machines/ding.ogg', 50, 1)
return "Record syncronized."
+7 -2
View File
@@ -83,7 +83,7 @@
var/decl/emote/use_emote = get_emote_by_key(act)
if(!istype(use_emote))
to_chat(src, span_warning("Unknown emote '[act]'. Type <b>say *help</b> for a list of usable emotes."))
to_chat(src, span_warning("Unknown emote '[act]'. Type " + span_bold("say *help") + " for a list of usable emotes. ([act] [message])")) // Add full message in the event you used * instead of ! or something like that
return
if(!use_emote.mob_can_use(src))
@@ -210,6 +210,7 @@
var/turf/T = get_turf(src)
if(!T) return
if(client)
playsound(T, pick(emote_sound), 25, TRUE, falloff = 1 , is_global = TRUE, frequency = ourfreq, ignore_walls = FALSE, preference = /datum/preference/toggle/emote_sounds)
@@ -224,7 +225,11 @@
message = span_emote(span_bold("[src]") + " ([ghost_follow_link(src, M)]) [input]")
if(usr && usr.client && M && !(get_z(usr) == get_z(M)))
message = span_multizsay("[message]")
M.show_message(message, m_type)
// If you are in the same tile, right next to, or being held by a person doing an emote, you should be able to see it while blind
if(m_type != AUDIBLE_MESSAGE && (src.Adjacent(M) || (istype(src.loc, /obj/item/holder) && src.loc.loc == M)))
M.show_message(message)
else
M.show_message(message, m_type)
M.create_chat_message(src, "[runemessage]", FALSE, list("emote"), (m_type == AUDIBLE_MESSAGE))
for(var/obj/O as anything in o_viewers)
@@ -43,6 +43,11 @@
var/combine_first = FALSE // If TRUE, this appliance will do combination cooking before checking recipes
var/food_safety = FALSE //RS ADD - If true, the appliance automatically ejects food instead of burning it
var/static/radial_eject = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_eject")
var/static/radial_power = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_power")
var/static/radial_safety = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_safety")
var/static/radial_output = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_change_output")
/obj/machinery/appliance/Initialize()
. = ..()
@@ -611,8 +616,31 @@
if(..())
return
if(cooking_objs.len)
removal_menu(user)
interact(user)
/obj/machinery/appliance/interact(mob/user)
var/list/options = list(
"power" = radial_power,
"safety" = radial_safety,
)
if(LAZYLEN(cooking_objs))
options["remove"] = radial_eject
if(LAZYLEN(output_options))
options["select_output"] = radial_output
var/choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
switch(choice)
if("power")
toggle_power()
if("safety")
toggle_safety()
if("remove")
removal_menu(user)
if("select_output")
choose_output()
/obj/machinery/appliance/proc/removal_menu(var/mob/user)
if (can_remove_items(user))
@@ -26,11 +26,15 @@
/obj/machinery/appliance/cooker/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
data["on"] = !(stat & POWEROFF)
data["safety"] = food_safety
data["temperature"] = round(temperature - T0C, 0.1)
data["optimalTemp"] = round(optimal_temp - T0C, 0.1)
data["temperatureEnough"] = temperature >= min_temp
data["efficiency"] = round(get_efficiency(), 0.1)
data["containersRemovable"] = can_remove_items(user, show_warning = FALSE)
data["selected_option"] = selected_option
data["show_selected_option"] = LAZYLEN(output_options)
var/list/our_contents = list()
for(var/i in 1 to max_contents)
@@ -56,6 +60,15 @@
return TRUE
switch(action)
if("toggle_power")
attempt_toggle_power(usr)
return TRUE
if("toggle_safety")
toggle_safety()
return TRUE
if("change_output")
choose_output()
return TRUE
if("slot")
var/slot = params["slot"]
var/obj/item/I = ui.user.get_active_hand()
+46 -77
View File
@@ -138,6 +138,7 @@
user.visible_message( \
span_notice("\The [user] has added one of [O] to \the [src]."), \
span_notice("You add one of [O] to \the [src]."))
update_static_data_for_all_viewers()
return
else
// user.remove_from_mob(O) //This just causes problems so far as I can tell. -Pete - Man whoever you are, it's been years. o7
@@ -145,7 +146,7 @@
user.visible_message( \
span_notice("\The [user] has added \the [O] to \the [src]."), \
span_notice("You add \the [O] to \the [src]."))
SStgui.update_uis(src)
update_static_data_for_all_viewers()
return
else if (istype(O,/obj/item/storage/bag/plants)) // There might be a better way about making plant bags dump their contents into a microwave, but it works.
var/obj/item/storage/bag/plants/bag = O
@@ -173,6 +174,7 @@
to_chat(user, "You fill \the [src] from \the [O].")
SStgui.update_uis(src)
update_static_data_for_all_viewers()
return 0
else if(istype(O,/obj/item/reagent_containers/glass) || \
@@ -185,6 +187,8 @@
if (!(R.id in acceptable_reagents))
to_chat(user, span_warning("Your [O] contains components unsuitable for cookery."))
return 1
// gotta let afterattack resolve
addtimer(CALLBACK(src, TYPE_PROC_REF(/datum, update_static_data_for_all_viewers)), 1 SECOND)
return
else if(istype(O,/obj/item/grab))
var/obj/item/grab/G = O
@@ -219,6 +223,11 @@
..()
SStgui.update_uis(src)
/obj/machinery/microwave/tgui_status(mob/user)
if(user == paicard?.pai)
return STATUS_INTERACTIVE
. = ..()
/obj/machinery/microwave/tgui_state(mob/user)
return GLOB.tgui_physical_state
@@ -242,6 +251,20 @@
ui = new(user, src, "Microwave", name)
ui.open()
/obj/machinery/microwave/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/kitchen_recipes)
)
/obj/machinery/microwave/tgui_static_data(mob/user)
var/list/data = ..()
var/datum/recipe/recipe = select_recipe(available_recipes,src)
data["recipe"] = recipe ? sanitize_css_class_name("[recipe.type]") : null
data["recipe_name"] = recipe ? initial(recipe.result:name) : null
return data
/obj/machinery/microwave/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
@@ -250,6 +273,21 @@
data["dirty"] = dirty == 100
data["items"] = get_items_list()
var/list/reagents_data = list()
for(var/datum/reagent/R in reagents.reagent_list)
var/display_name = R.name
if(R.id == "capsaicin")
display_name = "Hotsauce"
if(R.id == "frostoil")
display_name = "Coldsauce"
UNTYPED_LIST_ADD(reagents_data, list(
"name" = display_name,
"amt" = R.volume,
"extra" = "unit[R.volume > 1 ? "s" : ""]",
"color" = R.color,
))
data["reagents"] = reagents_data
return data
/obj/machinery/microwave/proc/get_items_list()
@@ -258,7 +296,8 @@
var/list/items_counts = list()
var/list/items_measures = list()
var/list/items_measures_p = list()
//for(var/obj/O in ((contents - component_parts) - circuit))
var/list/icons = list()
for(var/obj/O in cookingContents())
var/display_name = O.name
if(istype(O,/obj/item/reagent_containers/food/snacks/egg))
@@ -278,33 +317,26 @@
items_measures[display_name] = "fillet of meat"
items_measures_p[display_name] = "fillets of meat"
items_counts[display_name]++
icons[display_name] = list("icon" = O.icon, "icon_state" = O.icon_state)
for(var/O in items_counts)
var/N = items_counts[O]
var/icon = icons[O]
if(!(O in items_measures))
data.Add(list(list(
"name" = capitalize(O),
"amt" = N,
"extra" = "[lowertext(O)][N > 1 ? "s" : ""]",
"icon" = icon,
)))
else
data.Add(list(list(
"name" = capitalize(O),
"amt" = N,
"extra" = N == 1 ? items_measures[O] : items_measures_p[O],
"icon" = icon,
)))
for(var/datum/reagent/R in reagents.reagent_list)
var/display_name = R.name
if(R.id == "capsaicin")
display_name = "Hotsauce"
if(R.id == "frostoil")
display_name = "Coldsauce"
data.Add(list(list(
"name" = display_name,
"amt" = R.volume,
"extra" = "unit[R.volume > 1 ? "s" : ""]"
)))
return data
/obj/machinery/microwave/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
@@ -322,69 +354,6 @@
if("dispose")
dispose()
return TRUE
/*
/obj/machinery/microwave/interact(mob/user as mob) // The microwave Menu
var/dat = ""
if(src.broken > 0)
dat = {"<TT>Bzzzzttttt</TT>"}
else if(src.operating)
dat = {"<TT>Microwaving in progress!<BR>Please wait...!</TT>"}
else if(src.dirty==100)
dat = {"<TT>This microwave is dirty!<BR>Please clean it before use!</TT>"}
else
var/list/items_counts = new
var/list/items_measures = new
var/list/items_measures_p = new
for (var/obj/O in ((contents - component_parts) - circuit))
var/display_name = O.name
if (istype(O,/obj/item/reagent_containers/food/snacks/egg))
items_measures[display_name] = "egg"
items_measures_p[display_name] = "eggs"
if (istype(O,/obj/item/reagent_containers/food/snacks/tofu))
items_measures[display_name] = "tofu chunk"
items_measures_p[display_name] = "tofu chunks"
if (istype(O,/obj/item/reagent_containers/food/snacks/meat)) //any meat
items_measures[display_name] = "slab of meat"
items_measures_p[display_name] = "slabs of meat"
if (istype(O,/obj/item/reagent_containers/food/snacks/donkpocket))
display_name = "Turnovers"
items_measures[display_name] = "turnover"
items_measures_p[display_name] = "turnovers"
if (istype(O,/obj/item/reagent_containers/food/snacks/carpmeat))
items_measures[display_name] = "fillet of meat"
items_measures_p[display_name] = "fillets of meat"
items_counts[display_name]++
for (var/O in items_counts)
var/N = items_counts[O]
if (!(O in items_measures))
dat += span_bold("[capitalize(O)]:") + " [N] [lowertext(O)]\s<BR>"
else
if (N==1)
dat += span_bold("[capitalize(O)]:") + " [N] [items_measures[O]]<BR>"
else
dat += span_bold("[capitalize(O)]:") + " [N] [items_measures_p[O]]<BR>"
for (var/datum/reagent/R in reagents.reagent_list)
var/display_name = R.name
if (R.id == "capsaicin")
display_name = "Hotsauce"
if (R.id == "frostoil")
display_name = "Coldsauce"
dat += span_bold("[display_name]:") + " [R.volume] unit\s<BR>"
if (items_counts.len==0 && reagents.reagent_list.len==0)
dat = span_bold("The microwave is empty") + "<BR>"
else
dat = span_bold("Ingredients:") + "<br>[dat]"
dat += {"<HR><BR>\
<A href='?src=\ref[src];action=cook'>Turn on!<BR>\
<A href='?src=\ref[src];action=dispose'>Eject ingredients!<BR>\
"}
user << browse("<HEAD><TITLE>Microwave Controls</TITLE></HEAD><TT>[dat]</TT>", "window=microwave")
onclose(user, "microwave")
return
*/
/***********************************
* Microwave Menu Handling/Cooking
@@ -8,6 +8,7 @@
#define TRAIT_PREF_TYPE_BOOLEAN 1
#define TRAIT_PREF_TYPE_COLOR 2
#define TRAIT_PREF_TYPE_STRING 3
#define TRAIT_NO_VAREDIT_TARGET 0
#define TRAIT_VAREDIT_TARGET_SPECIES 1
@@ -1177,3 +1177,48 @@ YW CHANGE STOP*/
/datum/trait/neutral/agraviaphobia/apply(var/datum/species/S,var/mob/living/carbon/human/H, var/trait_prefs = null)
..()
H.phobias |= AGRAVIAPHOBIA
/datum/trait/neutral/gargoyle
name = "Gargoyle (Adjustable)"
desc = "You turn into a statue (or similar) at will, but also whenever you run out of energy. Being a statue replenishes your energy slowly."
cost = 0
custom_only = FALSE //slimes, xenochimera, diona, proteans, etc, basically anything but custom doesn't make sense (as much as I wanna play a petrifying slime)
//Nah makes perfect sense, they could just be gene modded, not to mention we can expand this to have the statue and description of it renameable as well as color adjustable, to support general petrification
has_preferences = list("identifier" = list(TRAIT_PREF_TYPE_STRING, "Identifier", TRAIT_NO_VAREDIT_TARGET, "statue"),
"material" = list(TRAIT_PREF_TYPE_STRING, "Material", TRAIT_NO_VAREDIT_TARGET, "stone"),
"tint" = list(TRAIT_PREF_TYPE_COLOR, "Statue color", TRAIT_NO_VAREDIT_TARGET, "#FFFFFF"),
"adjective" = list(TRAIT_PREF_TYPE_STRING, "Adjective", TRAIT_NO_VAREDIT_TARGET, "hardens")/*,
"pickupable" = list(TRAIT_PREF_TYPE_BOOLEAN, "Can be picked up", TRAIT_NO_VAREDIT_TARGET, FALSE)*/)
/datum/trait/neutral/gargoyle/apply(var/datum/species/S,var/mob/living/carbon/human/H, var/list/trait_prefs)
..()
var/datum/component/gargoyle/G = H.LoadComponent(/datum/component/gargoyle)
if (trait_prefs)
G.tint = trait_prefs["tint"]
G.material = lowertext(trait_prefs["material"])
G.identifier = lowertext(trait_prefs["identifier"])
G.adjective = lowertext(trait_prefs["adjective"])
/datum/trait/neutral/gargoyle/apply_sanitization_to_string(var/pref, var/input)
if (has_preferences[pref][1] != TRAIT_PREF_TYPE_STRING || length(input) <= 0)
return
input = sanitizeSafe(input, 25)
if (length(input) <= 0)
return default_value_for_pref(pref)
input = lowertext(input)
if (pref == "adjective")
if (copytext_char(input, -1) != "s")
switch(copytext_char(input, -2))
if ("ss")
input += "es"
if ("sh")
input += "es"
if ("ch")
input += "es"
else
switch(copytext_char(input, -1))
if("s", "x", "z")
input += "es"
else
input += "s"
return input
@@ -75,4 +75,14 @@
return TRUE
if(TRAIT_PREF_TYPE_COLOR) //color
return "#ffffff"
if(TRAIT_PREF_TYPE_STRING) //string
return ""
return
/datum/trait/proc/apply_sanitization_to_string(var/pref, var/input)
if (has_preferences[pref][1] != TRAIT_PREF_TYPE_STRING || length(input) <= 0)
return default_value_for_pref(pref)
input = sanitizeSafe(input, MAX_NAME_LEN)
if (length(input) <= 0)
return default_value_for_pref(pref)
return input
@@ -57,52 +57,6 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
cut_overlay(I)
overlays_standing[cache_index] = null
// These are used as the layers for the icons, as well as indexes in a list that holds onto them.
// Technically the layers used are all -100+layer to make them FLOAT_LAYER overlays.
//Human Overlays Indexes/////////
#define MUTATIONS_LAYER 1 //Mutations like fat, and lasereyes
#define SKIN_LAYER 2 //Skin things added by a call on species
#define BLOOD_LAYER 3 //Bloodied hands/feet/anything else
#define MOB_DAM_LAYER 4 //Injury overlay sprites like open wounds
#define SURGERY_LAYER 5 //Overlays for open surgical sites
#define UNDERWEAR_LAYER 6 //Underwear/bras/etc
#define TAIL_LOWER_LAYER 7 //Tail as viewed from the south
#define WING_LOWER_LAYER 8 //Wings as viewed from the south
#define SHOES_LAYER_ALT 9 //Shoe-slot item (when set to be under uniform via verb)
#define UNIFORM_LAYER 10 //Uniform-slot item
#define ID_LAYER 11 //ID-slot item
#define SHOES_LAYER 12 //Shoe-slot item
#define GLOVES_LAYER 13 //Glove-slot item
#define BELT_LAYER 14 //Belt-slot item
#define SUIT_LAYER 15 //Suit-slot item
#define TAIL_UPPER_LAYER 16 //Some species have tails to render (As viewed from the N, E, or W)
#define GLASSES_LAYER 17 //Eye-slot item
#define BELT_LAYER_ALT 18 //Belt-slot item (when set to be above suit via verb)
#define SUIT_STORE_LAYER 19 //Suit storage-slot item
#define BACK_LAYER 20 //Back-slot item
#define HAIR_LAYER 21 //The human's hair
#define HAIR_ACCESSORY_LAYER 22 //VOREStation edit. Simply move this up a number if things are added.
#define EARS_LAYER 23 //Both ear-slot items (combined image)
#define EYES_LAYER 24 //Mob's eyes (used for glowing eyes)
#define FACEMASK_LAYER 25 //Mask-slot item
#define GLASSES_LAYER_ALT 26 //So some glasses can appear on top of hair and things
#define HEAD_LAYER 27 //Head-slot item
#define HANDCUFF_LAYER 28 //Handcuffs, if the human is handcuffed, in a secret inv slot
#define LEGCUFF_LAYER 29 //Same as handcuffs, for legcuffs
#define L_HAND_LAYER 30 //Left-hand item
#define R_HAND_LAYER 31 //Right-hand item
#define WING_LAYER 32 //Wings or protrusions over the suit.
#define TAIL_UPPER_LAYER_ALT 33 //Modified tail-sprite layer. Tend to be larger.
#define MODIFIER_EFFECTS_LAYER 34 //Effects drawn by modifiers
#define FIRE_LAYER 35 //'Mob on fire' overlay layer
// # define MOB_WATER_LAYER 36 //'Mob submerged' overlay layer // Moved to global defines
#define TARGETED_LAYER 37 //'Aimed at' overlay layer
#define VORE_BELLY_LAYER 38
#define VORE_TAIL_LAYER 39
#define TOTAL_LAYERS 39 //VOREStation edit. <---- KEEP THIS UPDATED, should always equal the highest number here, used to initialize a list.
//////////////////////////////////
/mob/living/carbon/human
var/list/overlays_standing[TOTAL_LAYERS]
var/previous_damage_appearance // store what the body last looked like, so we only have to update it if something changed
@@ -245,6 +199,8 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
if(QDESTROYING(src))
return
remove_layer(BODYPARTS_LAYER)
var/husk_color_mod = rgb(96,88,80)
var/hulk_color_mod = rgb(48,224,40)
@@ -427,6 +383,13 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
//END CACHED ICON GENERATION.
stand_icon.Blend(base_icon,ICON_OVERLAY)
var/image/body = image(stand_icon)
if (body)
body.layer = BODY_LAYER + BODYPARTS_LAYER
overlays_standing[BODYPARTS_LAYER] = body
apply_layer(BODYPARTS_LAYER)
icon = stand_icon
//tail
+2 -2
View File
@@ -376,7 +376,7 @@ var/list/channel_to_radio_key = new
if(M && src) //If we still exist, when the spawn processes
//VOREStation Add - Ghosts don't hear whispers
if(whispering && isobserver(M) && (!M.client?.prefs?.read_preference(/datum/preference/toggle/ghost_see_whisubtle) || \
(!client?.prefs?.read_preference(/datum/preference/toggle/whisubtle_vis) && !M.client?.holder)))
(!(client?.prefs?.read_preference(/datum/preference/toggle/whisubtle_vis) || (isbelly(M.loc) && src == M.loc:owner)) && !M.client?.holder)))
M.show_message(span_game(span_say(span_name(src.name) + " [w_not_heard].")), 2)
return
//VOREStation Add End
@@ -437,7 +437,7 @@ var/list/channel_to_radio_key = new
/mob/living/proc/say_signlang(var/message, var/verb="gestures", var/verb_understood="gestures", var/datum/language/language, var/type = 1)
var/turf/T = get_turf(src)
//We're in something, gesture to people inside the same thing
if(loc != T)
if(loc != T && !istype(loc, /obj/item/holder)) // Partially fixes sign language while being held.
for(var/mob/M in loc)
M.hear_signlang(message, verb, verb_understood, language, src, type)
@@ -291,6 +291,16 @@
if(I_GRAB)
pai_nom(A)
// Allow card inhabited machines to be interacted with
// This has to override ClickOn because of storage depth nonsense with how pAIs are in cards in machines
/mob/living/silicon/pai/ClickOn(var/atom/A, var/params)
if(istype(A, /obj/machinery))
var/obj/machinery/M = A
if(M.paicard == card)
M.attack_ai(src)
return
return ..()
/mob/living/silicon/pai/proc/hug(var/mob/living/silicon/pai/H, var/mob/living/target)
var/t_him = "them"
@@ -177,6 +177,23 @@
/obj/item/material/gravemarker
)
/obj/item/gripper/scene
name = "misc gripper"
desc = "A simple grasping tool that can hold a variety of 'general' objects..."
can_hold = list(
/obj/item/capture_crystal,
/obj/item/clothing,
/obj/item/implanter,
/obj/item/disk/nifsoft/compliance,
/obj/item/handcuffs,
/obj/item/toy,
/obj/item/petrifier,
/obj/item/dice,
/obj/item/casino_platinum_chip,
/obj/item/spacecasinocash
)
/obj/item/gripper/no_use/organ
name = "organ gripper"
icon_state = "gripper-flesh"
@@ -208,6 +208,7 @@ var/global/list/robot_modules = list(
src.modules += new /obj/item/flash/robot(src)
src.modules += new /obj/item/extinguisher(src)
src.modules += new /obj/item/tool/crowbar/cyborg(src)
src.modules += new /obj/item/gripper/scene(src)
/obj/item/robot_module/robot/standard
name = "standard robot module"
@@ -97,7 +97,8 @@
/mob/living/simple_mob/vore/alienanimals/space_ghost/apply_melee_effects(var/atom/A)
var/mob/living/L = A
L.hallucination += 50
if(L.hallucination <= 100)
L.hallucination += rand(1,10)
/mob/living/simple_mob/vore/alienanimals/space_ghost/shoot(atom/A) //We're shooting ghosts at people and need them to have the same faction as their parent, okay?
if(!projectiletype)
@@ -209,7 +210,8 @@
/mob/living/simple_mob/vore/alienanimals/spooky_ghost/apply_melee_effects(var/atom/A)
var/mob/living/L = A
if(L && istype(L))
L.hallucination += rand(1,50)
if(L.hallucination <= 100)
L.hallucination += rand(1,10)
/mob/living/simple_mob/vore/alienanimals/spooky_ghost/Life()
. = ..()
+11
View File
@@ -126,6 +126,17 @@
/atom/proc/drain_power(var/drain_check,var/surge, var/amount = 0)
return -1
// used for petrification machines
/atom/proc/get_ultimate_mob()
var/mob/ultimate_mob
var/atom/to_check = loc
var/n = 0
while (to_check && !isturf(to_check) && n++ < 16)
if (ismob(to_check))
ultimate_mob = to_check
to_check = to_check.loc
return ultimate_mob
// Show a message to all mobs and objects in earshot of this one
// This would be for audible actions by the src mob
// message is the message output to anyone who can hear.
+13 -4
View File
@@ -3,7 +3,6 @@
/mob/verb/whisper(message as text)
set name = "Whisper"
set category = "IC.Subtle"
set hidden = 1
//VOREStation Addition Start
if(forced_psay)
@@ -15,7 +14,6 @@
/mob/verb/say_verb(message as text)
set name = "Say"
set category = "IC.Chat"
set hidden = 1
set instant = TRUE
@@ -33,7 +31,6 @@
/mob/verb/me_verb(message as message)
set name = "Me"
set category = "IC.Chat"
set desc = "Emote to nearby people (and your pred/prey)"
set hidden = 1
@@ -110,7 +107,19 @@
if(speaking.flags & NONVERBAL)
if(sdisabilities & BLIND || blinded)
return FALSE
if(!other || !(other in view(src)))
if(!other)
return FALSE
// Fixes seeing non-verbal languages while being held
if(istype(other.loc, /obj/item/holder))
if(istype(src.loc, /obj/item/holder))
if(!(other.loc in view(src.loc.loc)))
return FALSE
else if(!(other.loc in view(src)))
return FALSE
else if(istype(src.loc, /obj/item/holder))
if((!other) in view(src.loc.loc))
return FALSE
else if((!other) in view(src))
return FALSE
//Language check.
+82
View File
@@ -0,0 +1,82 @@
// Allows the usage of old style chat inputs even with TG Say enabled
/mob/verb/say_verb_old()
set name = "Say Old"
set category = "IC.Chat"
client?.start_thinking()
client?.start_typing()
var/message = tgui_input_text(usr, "Speak to people in sight.\nType your message:", "Say")
client?.stop_thinking()
if(message)
say_verb(message)
/mob/verb/me_verb_old()
set name = "Me Old"
set category = "IC.Chat"
set desc = "Emote to nearby people (and your pred/prey)"
client?.start_thinking()
client?.start_typing()
var/message = tgui_input_text(usr, "Emote to people in sight (and your pred/prey).\nType your message:", "Emote", multiline = TRUE)
client?.stop_thinking()
if(message)
me_verb(message)
/mob/verb/whisper_old()
set name = "Whisper Old"
set category = "IC.Subtle"
var/message = tgui_input_text(usr, "Speak to nearby people.\nType your message:", "Whisper")
if(message)
whisper(message)
/mob/verb/me_verb_subtle_old()
set name = "Subtle Old"
set category = "IC.Subtle"
set desc = "Emote to nearby people (and your pred/prey)"
var/message = tgui_input_text(usr, "Emote to nearby people (and your pred/prey).\nType your message:", "Subtle", multiline = TRUE)
if(message)
me_verb_subtle(message)
/mob/verb/me_verb_subtle_custom_old()
set name = "Subtle (Custom) Old"
set category = "IC.Subtle"
set desc = "Emote to nearby people, with ability to choose which specific portion of people you wish to target."
var/message = tgui_input_text(usr, "Emote to nearby people, with ability to choose which specific portion of people you wish to target.\nType your message:", "Subtle (Custom)", multiline = TRUE)
if(message)
me_verb_subtle_custom(message)
/mob/verb/psay_old()
set name = "Psay Old"
set category = "IC.Subtle"
var/message = tgui_input_text(usr, "Talk to people affected by complete absorbed or dominate predator/prey.\nType your message:", "Psay")
if(message)
psay(message)
/mob/verb/pme_old()
set name = "Pme Old"
set category = "IC.Subtle"
var/message = tgui_input_text(usr, "Emote to people affected by complete absorbed or dominate predator/prey.\nType your message:", "Pme")
if(message)
pme(message)
/mob/living/verb/player_narrate_ch()
set name = "Narrate (Player) Old"
set category = "IC.Chat"
var/message = tgui_input_text(usr, "Narrate an action or event! An alternative to emoting, for when your emote shouldn't start with your name!\nType your message:", "Narrate (Player)")
if(message)
player_narrate(message)
+1 -6
View File
@@ -4,7 +4,6 @@
/mob/verb/me_verb_subtle(message as message) //This would normally go in say.dm
set name = "Subtle"
set category = "IC.Subtle"
set desc = "Emote to nearby people (and your pred/prey)"
set hidden = 1
@@ -24,7 +23,6 @@
/mob/verb/me_verb_subtle_custom(message as message) // Literally same as above but with mode_selection set to true
set name = "Subtle (Custom)"
set category = "IC.Subtle"
set desc = "Emote to nearby people, with ability to choose which specific portion of people you wish to target."
if(forced_psay)
@@ -197,7 +195,7 @@
continue
if(src.client && M && !(get_z(src) == get_z(M)))
message = span_multizsay("[message]")
if(isobserver(M) && (!M.client?.prefs?.read_preference(/datum/preference/toggle/ghost_see_whisubtle) || \
if(isobserver(M) && (!(M.client?.prefs?.read_preference(/datum/preference/toggle/ghost_see_whisubtle) || (isbelly(M.loc) && src == M.loc:owner)) || \
!client?.prefs?.read_preference(/datum/preference/toggle/whisubtle_vis) && !M.client?.holder))
spawn(0)
M.show_message(undisplayed_message, 2)
@@ -254,7 +252,6 @@
///// PSAY /////
/mob/verb/psay(message as text)
set category = "IC.Subtle"
set name = "Psay"
set desc = "Talk to people affected by complete absorbed or dominate predator/prey."
@@ -352,7 +349,6 @@
///// PME /////
/mob/verb/pme(message as message)
set category = "IC.Subtle"
set name = "Pme"
set desc = "Emote to people affected by complete absorbed or dominate predator/prey."
@@ -448,7 +444,6 @@
M.me_verb(message)
/mob/living/verb/player_narrate(message as message)
set category = "IC.Chat"
set name = "Narrate (Player)"
set desc = "Narrate an action or event! An alternative to emoting, for when your emote shouldn't start with your name!"
+1 -8
View File
@@ -209,14 +209,7 @@ var/global/list/ore_reagents = list( //have a number of reageents divisible by R
if(length(holdingitems))
options["grind"] = radial_grind
var/choice
if(length(options) < 1)
return
if(length(options) == 1)
for(var/key in options)
choice = key
else
choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
var/choice = show_radial_menu(user, src, options, require_near = !issilicon(user), autopick_single_option = FALSE)
// post choice verification
if(inuse || (isAI(user) && stat & NOPOWER) || user.incapacitated())
+6
View File
@@ -609,6 +609,12 @@
forceMove(get_turf(src))
log_and_message_admins("[key_name(src)] used the OOC escape button to get out of a microwave.")
else if(istype(loc, /obj/structure/gargoyle) && loc:was_rayed)
var/obj/structure/gargoyle/G = loc
G.can_revert = TRUE
qdel(G)
log_and_message_admins("[key_name(src)] used the OOC escape button to revert back from being petrified.")
//You are in food and for some reason can't resist out
else if(istype(loc, /obj/item/reagent_containers/food))
var/obj/item/reagent_containers/food/F = src.loc
+24 -1
View File
@@ -26,9 +26,32 @@
/obj/item/strangerock/New(loc, var/inside_item_type = 0)
pixel_x = rand(0,16)-8
pixel_y = rand(0,8)-8
var/d100 = rand(1,100)
if(inside_item_type)
new /obj/item/archaeological_find(src, new_item_type = inside_item_type)
switch(d100)
if(51 to 100) //standard spawn logic 50% of the time
new /obj/item/archaeological_find(src, new_item_type = inside_item_type)
if(21 to 50) // 30% chance
new /obj/item/research_sample/common(src)
if(6 to 20) // 15% chance
new /obj/item/research_sample/uncommon(src)
if(1 to 5) // 5% chance
new /obj/item/research_sample/rare(src)
else //if something went wrong, somehow, generate the usual find
new /obj/item/archaeological_find(src, new_item_type = inside_item_type)
else //if this strange rock isn't set to generate a find for whatever reason, create a sample 75% of the time (this shouldn't happen unless the rock is mapped in or adminspawned)
switch(d100)
if(76 to 100)
return
if(21 to 75)
new /obj/item/research_sample/common(src)
if(6 to 20)
new /obj/item/research_sample/uncommon(src)
if(1 to 5)
new /obj/item/research_sample/rare(src)
else //if we somehow glitched
return //do nothing
/obj/item/strangerock/attackby(var/obj/item/I, var/mob/user)
if(istype(I, /obj/item/pickaxe/brush))
Binary file not shown.

Before

Width:  |  Height:  |  Size: 425 KiB

After

Width:  |  Height:  |  Size: 426 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -19,11 +19,13 @@
prob_fall = 40
//guard = 20
mobs_to_pick_from = list(
/mob/living/simple_mob/animal/space/bats = 10,
/mob/living/simple_mob/vore/alienanimals/space_jellyfish = 15,
/mob/living/simple_mob/vore/alienanimals/startreader = 15,
/mob/living/simple_mob/vore/alienanimals/space_ghost = 6,
/mob/living/simple_mob/vore/alienanimals/space_jellyfish = 1,
/mob/living/simple_mob/vore/alienanimals/startreader = 3,
/mob/living/simple_mob/vore/alienanimals/space_ghost = 2,
/mob/living/simple_mob/vore/oregrub = 1,
/mob/living/simple_mob/animal/space/ray = 10,
/mob/living/simple_mob/animal/space/bats = 10,
/mob/living/simple_mob/animal/space/gnat = 15,
/mob/living/simple_mob/animal/space/carp = 3,
/mob/living/simple_mob/animal/space/carp/large = 1,
/mob/living/simple_mob/animal/space/carp/large/huge = 1
@@ -12,6 +12,10 @@ import {
import { Window } from '../layouts';
type Data = {
on: BooleanLike;
safety: BooleanLike;
selected_option: string | null;
show_selected_option: BooleanLike;
temperature: number;
optimalTemp: number;
temperatureEnough: BooleanLike;
@@ -29,6 +33,10 @@ export const CookingAppliance = (props) => {
const { act, data } = useBackend<Data>();
const {
on,
safety,
selected_option,
show_selected_option,
temperature,
optimalTemp,
temperatureEnough,
@@ -40,8 +48,41 @@ export const CookingAppliance = (props) => {
return (
<Window width={600} height={600}>
<Window.Content scrollable>
<Section title="Status">
<Section
title="Status"
buttons={
<Button
selected={on}
icon="power-off"
onClick={() => act('toggle_power')}
>
{on ? 'On' : 'Off'}
</Button>
}
>
<LabeledList>
<LabeledList.Item label="Safety">
<Button
fluid
selected={safety}
icon={safety ? 'shield-alt' : 'exclamation-triangle'}
onClick={() => act('toggle_safety')}
>
{safety ? 'On' : 'Off'}
</Button>
</LabeledList.Item>
{!!show_selected_option && (
<LabeledList.Item label="Selected Output">
<Button
icon="pencil"
fluid
onClick={() => act('change_output')}
tooltip="Change Output"
>
{selected_option || 'Default'}
</Button>
</LabeledList.Item>
)}
<LabeledList.Item label="Temperature">
<ProgressBar
color={temperatureEnough ? 'good' : 'blue'}
@@ -21,6 +21,14 @@ export const GeneralRecordsView = (props) => {
<GeneralRecordsViewGeneral />
</Section>
<Section title="Actions">
<Button
icon="upload"
disabled={!!general!.empty}
color="good"
onClick={() => act('sync_r')}
>
Sync Employment Record
</Button>
<Button.Confirm
icon="trash"
disabled={!!general!.empty}
@@ -43,6 +43,7 @@ export const GeneralRecordsViewGeneral = (props) => {
</LabeledList>
<Section title="Employment/skills summary" preserveWhitespace>
{general.skills || 'No data found.'}
{<Button icon="pen" ml="0.5rem" onClick={() => act('edit_notes')} />}
</Section>
<Section title="Comments/Log">
{general.comments && general.comments.length === 0 ? (
@@ -36,6 +36,14 @@ export const MedicalRecordsView = (props) => {
<MedicalRecordsViewMedical />
</Section>
<Section title="Actions">
<Button
icon="upload"
disabled={!!medical!.empty}
color="good"
onClick={() => act('sync_r')}
>
Sync Medical Record
</Button>
<Button.Confirm
icon="trash"
disabled={!!medical!.empty}
@@ -25,7 +25,20 @@ export const MedicalRecordsViewMedical = (props) => {
<LabeledList.Item key={i} label={field.field}>
<Box preserveWhitespace>
{field.value}
<Button icon="pen" ml="0.5rem" onClick={() => doEdit(field)} />
{!!field.edit &&
(field.edit === 'notes' ? (
<Button
icon="pen"
ml="1rem"
onClick={() => act('edit_notes')}
/>
) : (
<Button
icon="pen"
ml="0.5rem"
onClick={() => doEdit(field)}
/>
))}
</Box>
</LabeledList.Item>
))}
+190 -56
View File
@@ -1,75 +1,209 @@
import { BooleanLike } from 'common/react';
import { DmIcon, Stack, Tooltip } from 'tgui-core/components';
import { classes } from 'tgui-core/react';
import { useBackend } from '../backend';
import { Box, Button, LabeledList, Section } from '../components';
import { Box, Button, Section } from '../components';
import { Window } from '../layouts';
import { AnimatedArrows } from './common/AnimatedArrows';
type Item = {
name: string;
amt: number;
extra: string;
icon: { icon: string; icon_state: string };
};
type Reagent = {
name: string;
amt: number;
extra: string;
color: string;
};
type Data = {
broken: BooleanLike;
operating: BooleanLike;
dirty: BooleanLike;
items: { name: string; amt: number; extra: string }[];
items: Item[];
reagents: Reagent[];
recipe: string | null;
recipe_name: string | null;
};
export const Microwave = (props) => {
const { act, config, data } = useBackend<Data>();
const { config, data } = useBackend<Data>();
const { broken, operating, dirty, items } = data;
let inner;
if (broken) {
inner = (
<Section fill>
<Box color="bad">Bzzzzttttt!!</Box>
</Section>
);
} else if (operating) {
inner = (
<Section fill>
<Box color="good">
Microwaving in progress!
<br />
Please wait...!
</Box>
</Section>
);
} else if (dirty) {
inner = (
<Section fill>
<Box color="bad">
This microwave is dirty!
<br />
Please clean it before use!
</Box>
</Section>
);
} else if (items.length) {
inner = <MicrowaveContents />;
} else {
inner = (
<Section fill>
<Box color="bad">{config.title} is empty.</Box>
</Section>
);
}
return (
<Window width={400} height={500}>
<Window.Content scrollable>
{(broken && (
<Section>
<Box color="bad">Bzzzzttttt!!</Box>
</Section>
)) ||
(operating && (
<Section>
<Box color="good">
Microwaving in progress!
<br />
Please wait...!
</Box>
</Section>
)) ||
(dirty && (
<Section>
<Box color="bad">
This microwave is dirty!
<br />
Please clean it before use!
</Box>
</Section>
)) ||
(items.length && (
<Section
title="Ingredients"
buttons={
<>
<Button icon="radiation" onClick={() => act('cook')}>
Microwave
</Button>
<Button icon="eject" onClick={() => act('dispose')}>
Eject
</Button>
</>
}
>
<LabeledList>
{items.map((item) => (
<LabeledList.Item key={item.name} label={item.name}>
{item.amt} {item.extra}
</LabeledList.Item>
))}
</LabeledList>
</Section>
)) || (
<Section>
<Box color="bad">{config.title} is empty.</Box>
</Section>
)}
</Window.Content>
<Window width={520} height={300}>
<Window.Content scrollable>{inner}</Window.Content>
</Window>
);
};
const MicrowaveContents = (props) => {
const { act, data } = useBackend<Data>();
const { items, reagents, recipe, recipe_name } = data;
return (
<Section
fill
title="Ingredients"
buttons={
<>
<Button icon="radiation" onClick={() => act('cook')}>
Microwave
</Button>
<Button icon="eject" onClick={() => act('dispose')}>
Eject
</Button>
</>
}
>
<Stack fill align="center">
<Stack.Item basis="70%">
<Box>
{items.map((item) => (
<Tooltip
content={item.name + ' - ' + item.amt + ' ' + item.extra}
position="top"
key={item.name}
>
<Box
backgroundColor="black"
height="64px"
width="64px"
position="relative"
m={1}
style={{ border: '1px solid #4444ab', float: 'left' }}
>
<Box position="absolute" top={0} right={0}>
x{item.amt}
</Box>
<DmIcon
icon={item.icon?.icon}
icon_state={item.icon?.icon_state}
width="64px"
height="64px"
/>
</Box>
</Tooltip>
))}
{reagents.map((r) => (
<Tooltip
content={`${r.name} - ${r.amt} ${r.extra}`}
key={r.name}
position="top"
>
<Box
backgroundColor="black"
height="64px"
width="64px"
position="relative"
m={1}
style={{ border: '1px solid #4444ab', float: 'left ' }}
>
<Box position="absolute" top={0} right={0}>
{r.amt}
</Box>
{/* To be clear: This is fucking cursed
We're directly loading the rectangular glass and
manually colorizing a div that's set to be the right shape */}
<Box
position="absolute"
left="24px"
top="26px"
width="16px"
height="20px"
backgroundColor={r.color}
/>
<DmIcon
position="absolute"
width="64px"
height="64px"
icon="icons/pdrink.dmi"
icon_state="square"
/>
</Box>
</Tooltip>
))}
</Box>
</Stack.Item>
<Stack.Item basis="10%">
<AnimatedArrows on inline />
</Stack.Item>
<Stack.Item>
<Tooltip
content={'Predicted Result - ' + (recipe_name || 'Burned Mess')}
position="top"
>
<Box
inline
backgroundColor="black"
height="64px"
width="64px"
position="relative"
style={{ border: '1px solid #4444ab' }}
>
{recipe ? (
<Box
ml="16px"
mt="16px"
className={classes(['kitchen_recipes32x32', recipe])}
/>
) : (
<DmIcon
icon="icons/obj/food.dmi"
icon_state="badrecipe"
width="64px"
height="64px"
/>
)}
</Box>
</Tooltip>
</Stack.Item>
</Stack>
</Section>
);
};
@@ -0,0 +1,148 @@
import { BooleanLike } from 'common/react';
import { useBackend } from 'tgui/backend';
import { Button, LabeledList, Section } from 'tgui/components';
import { Window } from 'tgui/layouts';
type Data = {
material: string;
identifier: string;
adjective: string;
tint: string;
t: BooleanLike;
target: string;
able_to_unpetrify: BooleanLike;
discard_clothes: BooleanLike;
can_remote: BooleanLike;
};
export const PetrificationInterface = (props) => {
const { act, data } = useBackend<Data>();
const {
material,
identifier,
adjective,
tint,
t,
able_to_unpetrify,
discard_clothes,
target,
can_remote,
} = data;
return (
<Window width={425} height={313}>
<Window.Content scrollable>
<Section title="Interface">
<LabeledList>
<LabeledList.Item label="Identifier">
<Button
fluid
tooltip="The identifier for the petrification. ie. 'A statue of (target)'"
tooltipPosition="top"
onClick={() => act('set_option', { option: 'identifier' })}
>
{'Change Identifier: "' + identifier + '"'}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Material">
<Button
fluid
tooltip={
'The material for the petrification. ie. "(name)' +
"'" +
's skin rapidly (adjective) as they turn to (material)!"'
}
tooltipPosition="top"
onClick={() => act('set_option', { option: 'material' })}
>
{'Change Material: "' + material + '"'}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Adjective">
<Button
fluid
tooltip={
'The adjective for the petrification. ie. "(name)' +
"'" +
's skin rapidly (adjective) as they turn to (material)!"'
}
tooltipPosition="top"
onClick={() => act('set_option', { option: 'adjective' })}
>
{'Change Adjective: "' + adjective + '"'}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Color">
<Button
fluid
backgroundColor={tint}
textColor={t ? '#000000' : '#ffffff'}
tooltip="The color of the statue. Pure white is direct greyscale."
tooltipPosition="top"
onClick={() => act('set_option', { option: 'tint' })}
>
Change Color
</Button>
</LabeledList.Item>
<LabeledList.Item label="Can Unpetrify">
<Button
fluid
selected={able_to_unpetrify}
tooltip="Whether or not the statue can be unpetrified. If yes, they will get a verb letting them turn back- if not, even if they're a gargoyle, it will be taken away. OOC Escape is always an option though."
tooltipPosition="top"
onClick={() =>
act('set_option', { option: 'able_to_unpetrify' })
}
>
{able_to_unpetrify ? 'Yes' : 'No'}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Discard Clothes">
<Button
fluid
selected={discard_clothes}
tooltip="Whether the target's clothing falls off before the petrification happens. (Clothes do not change color when petrified, and cannot be removed while being a statue)"
tooltipPosition="top"
onClick={() => act('set_option', { option: 'discard_clothes' })}
>
{discard_clothes ? 'Enabled' : 'Disabled'}
</Button>
</LabeledList.Item>
<LabeledList.Item label="Target">
<Button
fluid
onClick={() => act('set_option', { option: 'target' })}
>
{target}
</Button>
</LabeledList.Item>
</LabeledList>
<br />
<Button
ml={1}
disabled={!can_remote}
tooltip={
'Turn the target to ' +
material +
". This is meant for roleplay/scene purposes. Please don't abuse it."
}
tooltipPosition="top"
onClick={() => act('petrify')}
>
Petrify
</Button>
<Button
mr={1}
disabled={!can_remote}
tooltip="Create a remote that will petrify the target with the given options when the button is pressed. It must be within 4 tiles of the target when pressed to work.. This is meant for roleplay/scene purposes. Please don't abuse it."
tooltipPosition="top"
onClick={() => act('remote')}
>
Create Remote
</Button>
</Section>
</Window.Content>
</Window>
);
};
@@ -35,6 +35,14 @@ export const SecurityRecordsView = (props) => {
<SecurityRecordsViewSecurity />
</Section>
<Section title="Actions">
<Button
icon="upload"
disabled={!!security!.empty}
color="good"
onClick={() => act('sync_r')}
>
Sync Security Record
</Button>
<Button.Confirm
icon="trash"
disabled={!!security!.empty}
@@ -24,12 +24,21 @@ export const SecurityRecordsViewSecurity = (props) => {
<LabeledList.Item key={i} label={field.field}>
<Box preserveWhitespace>
{field.value}
<Button
icon="pen"
ml="0.5rem"
mb={'initial'}
onClick={() => doEdit(field)}
/>
{!!field.edit &&
(field.edit === 'notes' ? (
<Button
icon="pen"
ml="1rem"
onClick={() => act('edit_notes')}
/>
) : (
<Button
icon="pen"
ml="0.5rem"
mb={'initial'}
onClick={() => doEdit(field)}
/>
))}
</Box>
</LabeledList.Item>
))}
@@ -0,0 +1,37 @@
import { useEffect, useState } from 'react';
import { Box } from 'tgui/components';
import { BoxProps } from 'tgui/components/Box';
import { Icon } from 'tgui-core/components';
export const AnimatedArrows = (props: { on: boolean } & BoxProps) => {
const { on, ...rest } = props;
const [activeArrow, setActiveArrow] = useState(0);
// Lower to make it animate faster
const SPEED = 200;
useEffect(() => {
const id = setInterval(() => {
setActiveArrow((arrow) => (arrow + 1) % 3);
}, SPEED);
return () => clearInterval(id);
}, []);
return (
<Box {...rest}>
<Icon
color={!on ? 'gray' : activeArrow === 0 ? 'green' : 'white'}
name="chevron-right"
/>
<Icon
color={!on ? 'gray' : activeArrow === 1 ? 'green' : 'white'}
name="chevron-right"
/>
<Icon
color={!on ? 'gray' : activeArrow === 2 ? 'green' : 'white'}
name="chevron-right"
/>
</Box>
);
};
+9
View File
@@ -426,6 +426,8 @@
#include "code\datums\autolathe\tools_vr.dm"
#include "code\datums\autolathe\tools_yw.dm"
#include "code\datums\components\_component.dm"
#include "code\datums\components\connect_mob_behalf.dm"
#include "code\datums\components\gargoyle.dm"
#include "code\datums\components\material_container.dm"
#include "code\datums\components\overlay_lighting.dm"
#include "code\datums\components\recursive_move.dm"
@@ -930,6 +932,7 @@
#include "code\game\machinery\painter_vr.dm"
#include "code\game\machinery\partslathe_vr.dm"
#include "code\game\machinery\pda_multicaster.dm"
#include "code\game\machinery\petrification.dm"
#include "code\game\machinery\pointdefense.dm"
#include "code\game\machinery\portable_turret.dm"
#include "code\game\machinery\portable_turret_vr.dm"
@@ -1172,6 +1175,7 @@
#include "code\game\objects\effects\explosion_particles.dm"
#include "code\game\objects\effects\gibs.dm"
#include "code\game\objects\effects\glowshroom.dm"
#include "code\game\objects\effects\info.dm"
#include "code\game\objects\effects\item_pickup_ghost.dm"
#include "code\game\objects\effects\job_start_landmarks.dm"
#include "code\game\objects\effects\landmarks.dm"
@@ -1248,6 +1252,7 @@
#include "code\game\objects\items\latexballoon.dm"
#include "code\game\objects\items\magazine.dm"
#include "code\game\objects\items\paintkit.dm"
#include "code\game\objects\items\petrifier.dm"
#include "code\game\objects\items\pizza_voucher_vr.dm"
#include "code\game\objects\items\poi_items.dm"
#include "code\game\objects\items\robobag.dm"
@@ -1573,6 +1578,7 @@
#include "code\game\objects\structures\fireaxe.dm"
#include "code\game\objects\structures\fitness.dm"
#include "code\game\objects\structures\fitness_vr.dm"
#include "code\game\objects\structures\gargoyle.dm"
#include "code\game\objects\structures\girders.dm"
#include "code\game\objects\structures\gravemarker.dm"
#include "code\game\objects\structures\grille.dm"
@@ -1935,6 +1941,7 @@
#include "code\modules\asset_cache\assets\fontawesome.dm"
#include "code\modules\asset_cache\assets\icon_ref_map.dm"
#include "code\modules\asset_cache\assets\jquery.dm"
#include "code\modules\asset_cache\assets\kitchen_recipes.dm"
#include "code\modules\asset_cache\assets\preferences.dm"
#include "code\modules\asset_cache\assets\tgfont.dm"
#include "code\modules\asset_cache\assets\tgui.dm"
@@ -2016,6 +2023,7 @@
#include "code\modules\client\preferences_toggle_procs.dm"
#include "code\modules\client\preferences_vr.dm"
#include "code\modules\client\preferences_yw.dm"
#include "code\modules\client\record_updater.dm"
#include "code\modules\client\spam_prevention.dm"
#include "code\modules\client\stored_item.dm"
#include "code\modules\client\ui_style.dm"
@@ -2890,6 +2898,7 @@
#include "code\modules\mob\mob_planes_vr.dm"
#include "code\modules\mob\mob_transformation_simple.dm"
#include "code\modules\mob\say.dm"
#include "code\modules\mob\say_old.dm"
#include "code\modules\mob\say_vr.dm"
#include "code\modules\mob\skillset.dm"
#include "code\modules\mob\theme_lists.dm"