diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index 3bee841881..c54fbe93cc 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -145,6 +145,7 @@ What is the naming convention for planes or layers?
#define LAYER_HUD_ITEM 3 //Things sitting on HUD items (largely irrelevant because PLANE_PLAYER_HUD_ITEMS)
#define LAYER_HUD_ABOVE 4 //Things that reside above items (highlights)
#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 PLANE_ADMIN3 99 //Purely for shenanigans (above HUD)
diff --git a/code/__defines/misc.dm b/code/__defines/misc.dm
index 7f9128a64f..e59d3765dc 100644
--- a/code/__defines/misc.dm
+++ b/code/__defines/misc.dm
@@ -351,3 +351,8 @@ var/global/list/##LIST_NAME = list();\
#define RAD_LEVEL_MODERATE 10
#define RAD_LEVEL_HIGH 25
#define RAD_LEVEL_VERY_HIGH 50
+
+//https://secure.byond.com/docs/ref/info.html#/atom/var/mouse_opacity
+#define MOUSE_OPACITY_TRANSPARENT 0
+#define MOUSE_OPACITY_ICON 1
+#define MOUSE_OPACITY_OPAQUE 2
diff --git a/code/_helpers/_lists.dm b/code/_helpers/_lists.dm
index 5e322e1928..1eb8ac5456 100644
--- a/code/_helpers/_lists.dm
+++ b/code/_helpers/_lists.dm
@@ -758,3 +758,8 @@ proc/dd_sortedTextList(list/incoming)
. |= i
#define listequal(A, B) (A.len == B.len && !length(A^B))
+
+/proc/popleft(list/L)
+ if(L.len)
+ . = L[1]
+ L.Cut(1,2)
diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm
new file mode 100644
index 0000000000..e83e5d732c
--- /dev/null
+++ b/code/_onclick/hud/radial.dm
@@ -0,0 +1,313 @@
+#define NEXT_PAGE_ID "__next__"
+#define DEFAULT_CHECK_DELAY 20
+
+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
+ var/datum/radial_menu/parent
+
+/obj/screen/radial/slice
+ icon_state = "radial_slice"
+ var/choice
+ var/next_page = FALSE
+ var/tooltips = FALSE
+
+/obj/screen/radial/slice/MouseEntered(location, control, params)
+ . = ..()
+ icon_state = "radial_slice_focus"
+ if(tooltips)
+ openToolTip(usr, src, params, title = name)
+
+/obj/screen/radial/slice/MouseExited(location, control, params)
+ . = ..()
+ icon_state = "radial_slice"
+ if(tooltips)
+ closeToolTip(usr)
+
+/obj/screen/radial/slice/Click(location, control, params)
+ if(usr.client == parent.current_user)
+ if(next_page)
+ parent.next_page()
+ else
+ parent.element_chosen(choice,usr)
+
+/obj/screen/radial/center
+ name = "Close Menu"
+ icon_state = "radial_center"
+
+/obj/screen/radial/center/MouseEntered(location, control, params)
+ . = ..()
+ icon_state = "radial_center_focus"
+
+/obj/screen/radial/center/MouseExited(location, control, params)
+ . = ..()
+ icon_state = "radial_center"
+
+/obj/screen/radial/center/Click(location, control, params)
+ if(usr.client == parent.current_user)
+ 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
+ var/list/page_data = list() //list of choices per page
+
+
+ var/selected_choice
+ var/list/obj/screen/elements = list()
+ var/obj/screen/radial/center/close_button
+ var/client/current_user
+ var/atom/anchor
+ var/image/menu_holder
+ var/finished = FALSE
+ var/datum/callback/custom_check_callback
+ var/next_check = 0
+ var/check_delay = DEFAULT_CHECK_DELAY
+
+ var/radius = 32
+ var/starting_angle = 0
+ var/ending_angle = 360
+ var/zone = 360
+ var/min_angle = 45 //Defaults are setup for this value, if you want to make the menu more dense these will need changes.
+ var/max_elements
+ var/pages = 1
+ var/current_page = 1
+
+ var/hudfix_method = TRUE //TRUE to change anchor to user, FALSE to shift by py_shift
+ var/py_shift = 0
+ var/entry_animation = TRUE
+
+//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
+ if(!istype(AM) || !AM.screen_loc)
+ return
+ if(AM in user.client.screen)
+ if(hudfix_method)
+ anchor = user
+ 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.
+
+//Sets defaults
+//These assume 45 deg min_angle
+/datum/radial_menu/proc/restrict_to_dir(dir)
+ switch(dir)
+ if(NORTH)
+ starting_angle = 270
+ ending_angle = 135
+ if(SOUTH)
+ starting_angle = 90
+ ending_angle = 315
+ if(EAST)
+ starting_angle = 0
+ ending_angle = 225
+ if(WEST)
+ starting_angle = 180
+ ending_angle = 45
+
+/datum/radial_menu/proc/setup_menu(use_tooltips)
+ if(ending_angle > starting_angle)
+ zone = ending_angle - starting_angle
+ else
+ zone = 360 - starting_angle + ending_angle
+
+ max_elements = round(zone / min_angle)
+ var/paged = max_elements < choices.len
+ if(elements.len < max_elements)
+ var/elements_to_add = max_elements - elements.len
+ 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
+ elements += new_element
+
+ var/page = 1
+ page_data = list(null)
+ var/list/current = list()
+ var/list/choices_left = choices.Copy()
+ while(choices_left.len)
+ if(current.len == max_elements)
+ page_data[page] = current
+ page++
+ page_data.len++
+ current = list()
+ if(paged && current.len == max_elements - 1)
+ current += NEXT_PAGE_ID
+ continue
+ else
+ current += popleft(choices_left)
+ if(paged && current.len < max_elements)
+ current += NEXT_PAGE_ID
+
+ page_data[page] = current
+ pages = page
+ current_page = 1
+ update_screen_objects(anim = entry_animation)
+
+/datum/radial_menu/proc/update_screen_objects(anim = 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/angle = WRAP(starting_angle + (i - 1) * angle_per_element,0,360)
+ if(i > page_choices.len)
+ HideElement(E)
+ else
+ SetElement(E,page_choices[i],angle,anim = anim,anim_order = i)
+
+/datum/radial_menu/proc/HideElement(obj/screen/radial/slice/E)
+ E.cut_overlays()
+ E.alpha = 0
+ E.name = "None"
+ E.maptext = null
+ E.mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ E.choice = null
+ E.next_page = FALSE
+
+/datum/radial_menu/proc/SetElement(obj/screen/radial/slice/E,choice_id,angle,anim,anim_order)
+ //Position
+ var/py = round(cos(angle) * radius) + py_shift
+ var/px = round(sin(angle) * radius)
+ if(anim)
+ var/timing = anim_order * 0.5
+ var/matrix/starting = matrix()
+ starting.Scale(0.1,0.1)
+ E.transform = starting
+ var/matrix/TM = matrix()
+ animate(E,pixel_x = px,pixel_y = py, transform = TM, time = timing)
+ else
+ E.pixel_y = py
+ E.pixel_x = px
+
+ //Visuals
+ E.alpha = 255
+ E.mouse_opacity = MOUSE_OPACITY_ICON
+ E.cut_overlays()
+ if(choice_id == NEXT_PAGE_ID)
+ E.name = "Next Page"
+ E.next_page = TRUE
+ E.add_overlay("radial_next")
+ else
+ if(istext(choices_values[choice_id]))
+ E.name = choices_values[choice_id]
+ else
+ var/atom/movable/AM = choices_values[choice_id] //Movables only
+ E.name = AM.name
+ E.choice = choice_id
+ E.maptext = null
+ E.next_page = FALSE
+ if(choices_icons[choice_id])
+ E.add_overlay(choices_icons[choice_id])
+
+/datum/radial_menu/New()
+ close_button = new
+ close_button.parent = src
+
+/datum/radial_menu/proc/Reset()
+ choices.Cut()
+ choices_icons.Cut()
+ choices_values.Cut()
+ current_page = 1
+
+/datum/radial_menu/proc/element_chosen(choice_id,mob/user)
+ selected_choice = choices_values[choice_id]
+
+/datum/radial_menu/proc/get_next_id()
+ return "c_[choices.len]"
+
+/datum/radial_menu/proc/set_choices(list/new_choices, use_tooltips)
+ if(choices.len)
+ Reset()
+ for(var/E in new_choices)
+ var/id = get_next_id()
+ choices += id
+ choices_values[id] = E
+ if(new_choices[E])
+ var/I = extract_image(new_choices[E])
+ if(I)
+ choices_icons[id] = I
+ setup_menu(use_tooltips)
+
+
+/datum/radial_menu/proc/extract_image(E)
+ var/mutable_appearance/MA = new /mutable_appearance(E)
+ if(MA)
+ MA.layer = LAYER_HUD_ABOVE
+ MA.appearance_flags |= RESET_TRANSFORM
+ return MA
+
+
+/datum/radial_menu/proc/next_page()
+ if(pages > 1)
+ current_page = WRAP(current_page + 1,1,pages+1)
+ update_screen_objects()
+
+/datum/radial_menu/proc/show_to(mob/M)
+ 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.vis_contents += elements + close_button
+ current_user.images += menu_holder
+
+/datum/radial_menu/proc/hide()
+ if(current_user)
+ current_user.images -= menu_holder
+
+/datum/radial_menu/proc/wait(atom/user, atom/anchor, require_near = FALSE)
+ while (current_user && !finished && !selected_choice)
+ if(require_near && !in_range(anchor, user))
+ return
+ if(custom_check_callback && next_check < world.time)
+ if(!custom_check_callback.Invoke())
+ return
+ else
+ next_check = world.time + check_delay
+ stoplag(1)
+
+/datum/radial_menu/Destroy()
+ Reset()
+ hide()
+ QDEL_NULL(custom_check_callback)
+ . = ..()
+
+/*
+ Presents radial menu to user anchored to anchor (or user if the anchor is currently in users screen)
+ 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)
+ if(!user || !anchor || !length(choices))
+ return
+ if(!uniqueid)
+ uniqueid = "defmenu_[REF(user)]_[REF(anchor)]"
+
+ if(GLOB.radial_menus[uniqueid])
+ return
+
+ var/datum/radial_menu/menu = new
+ GLOB.radial_menus[uniqueid] = menu
+ if(radius)
+ menu.radius = radius
+ if(istype(custom_check))
+ menu.custom_check_callback = custom_check
+ menu.anchor = anchor
+ 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.wait(user, anchor, require_near)
+ var/answer = menu.selected_choice
+ QDEL_NULL(menu)
+ GLOB.radial_menus -= uniqueid
+ return answer
\ No newline at end of file
diff --git a/code/_onclick/hud/radial_persistent.dm b/code/_onclick/hud/radial_persistent.dm
new file mode 100644
index 0000000000..feaf17c1b2
--- /dev/null
+++ b/code/_onclick/hud/radial_persistent.dm
@@ -0,0 +1,75 @@
+/*
+ A derivative of radial menu which persists onscreen until closed and invokes a callback each time an element is clicked
+*/
+
+/obj/screen/radial/persistent/center
+ name = "Close Menu"
+ icon_state = "radial_center"
+
+/obj/screen/radial/persistent/center/Click(location, control, params)
+ if(usr.client == parent.current_user)
+ parent.element_chosen(null,usr)
+
+/obj/screen/radial/persistent/center/MouseEntered(location, control, params)
+ . = ..()
+ icon_state = "radial_center_focus"
+
+/obj/screen/radial/persistent/center/MouseExited(location, control, params)
+ . = ..()
+ icon_state = "radial_center"
+
+
+
+/datum/radial_menu/persistent
+ var/uniqueid
+ var/datum/callback/select_proc_callback
+
+/datum/radial_menu/persistent/New()
+ close_button = new /obj/screen/radial/persistent/center
+ close_button.parent = src
+
+
+/datum/radial_menu/persistent/element_chosen(choice_id,mob/user)
+ select_proc_callback.Invoke(choices_values[choice_id])
+
+
+/datum/radial_menu/persistent/proc/change_choices(list/newchoices, tooltips)
+ if(!newchoices.len)
+ return
+ Reset()
+ set_choices(newchoices,tooltips)
+
+/datum/radial_menu/persistent/Destroy()
+ QDEL_NULL(select_proc_callback)
+ GLOB.radial_menus -= uniqueid
+ Reset()
+ hide()
+ . = ..()
+
+/*
+ Creates a persistent radial menu and shows it to the user, anchored to anchor (or user if the anchor is currently in users screen).
+ 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
+ Select_proc is the proc to be called each time an element on the menu is clicked, and should accept the chosen element as its final argument
+ Clicking the center button will return a choice of null
+*/
+/proc/show_radial_menu_persistent(mob/user, atom/anchor, list/choices, datum/callback/select_proc, uniqueid, radius, tooltips = FALSE)
+ if(!user || !anchor || !length(choices) || !select_proc)
+ return
+ if(!uniqueid)
+ uniqueid = "defmenu_[REF(user)]_[REF(anchor)]"
+
+ if(GLOB.radial_menus[uniqueid])
+ return
+
+ var/datum/radial_menu/persistent/menu = new
+ menu.uniqueid = uniqueid
+ GLOB.radial_menus[uniqueid] = menu
+ if(radius)
+ menu.radius = radius
+ menu.select_proc_callback = select_proc
+ menu.anchor = anchor
+ 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)
+ return menu
diff --git a/code/controllers/Processes/supply.dm b/code/controllers/Processes/supply.dm
index f71cd8fd1d..640b99121f 100644
--- a/code/controllers/Processes/supply.dm
+++ b/code/controllers/Processes/supply.dm
@@ -226,7 +226,7 @@ var/datum/controller/supply/supply_controller = new()
A.req_access = list(SP.access)
else if(islist(SP.access))
var/list/L = SP.access // access var is a plain var, we need a list
- A.req_access = L.Copy()
+ A.req_one_access = L.Copy() //VOREStation Edit: Lets make sense
else
log_debug("Supply pack with invalid access restriction [SP.access] encountered!")
diff --git a/code/controllers/subsystems/timer.dm b/code/controllers/subsystems/timer.dm
index b743e8ff9e..3dcc64db28 100644
--- a/code/controllers/subsystems/timer.dm
+++ b/code/controllers/subsystems/timer.dm
@@ -1,12 +1,11 @@
-#define BUCKET_LEN (world.fps*1*60) //how many ticks should we keep in the bucket. (1 minutes worth)
-#define BUCKET_POS(timer) ((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag) % BUCKET_LEN)||BUCKET_LEN)
+#define BUCKET_LEN (round(10*(60/world.tick_lag), 1)) //how many ticks should we keep in the bucket. (1 minutes worth)
+#define BUCKET_POS(timer) (((round((timer.timeToRun - SStimer.head_offset) / world.tick_lag)+1) % BUCKET_LEN)||BUCKET_LEN)
#define TIMER_MAX (world.time + TICKS2DS(min(BUCKET_LEN-(SStimer.practical_offset-DS2TICKS(world.time - SStimer.head_offset))-1, BUCKET_LEN-1)))
#define TIMER_ID_MAX (2**24) //max float with integer precision
SUBSYSTEM_DEF(timer)
name = "Timer"
wait = 1 //SS_TICKER subsystem, so wait is in ticks
- priority = FIRE_PRIORITY_TIMERS //VOREStation Emergency Edit
init_order = INIT_ORDER_TIMER
flags = SS_TICKER|SS_NO_INIT
@@ -95,8 +94,8 @@ SUBSYSTEM_DEF(timer)
if(ctime_timer.flags & TIMER_LOOP)
ctime_timer.spent = 0
- clienttime_timers.Insert(ctime_timer, 1)
- cut_start_index++
+ ctime_timer.timeToRun = REALTIMEOFDAY + ctime_timer.wait
+ BINARY_INSERT(ctime_timer, clienttime_timers, datum/timedevent, timeToRun)
else
qdel(ctime_timer)
@@ -270,7 +269,7 @@ SUBSYSTEM_DEF(timer)
var/new_bucket_count
var/i = 1
for (i in 1 to length(alltimers))
- var/datum/timedevent/timer = alltimers[1]
+ var/datum/timedevent/timer = alltimers[i]
if (!timer)
continue
diff --git a/code/datums/supplypacks/contraband.dm b/code/datums/supplypacks/contraband.dm
index 11ec2f8862..d0caadf74f 100644
--- a/code/datums/supplypacks/contraband.dm
+++ b/code/datums/supplypacks/contraband.dm
@@ -84,7 +84,8 @@
/obj/item/weapon/storage/box/syndie_kit/chameleon,
/obj/item/device/encryptionkey/syndicate,
/obj/item/weapon/card/id/syndicate,
- /obj/item/clothing/mask/gas/voice
+ /obj/item/clothing/mask/gas/voice,
+ /obj/item/weapon/makeover
),
list( //the professional,
/obj/item/weapon/gun/energy/ionrifle/pistol,
diff --git a/code/datums/supplypacks/engineering_vr.dm b/code/datums/supplypacks/engineering_vr.dm
index e334268426..273d3f6558 100644
--- a/code/datums/supplypacks/engineering_vr.dm
+++ b/code/datums/supplypacks/engineering_vr.dm
@@ -4,6 +4,7 @@
cost = 30
containertype = /obj/structure/closet/crate/large
containername = "thermal regulator crate"
+ access = access_atmospherics
/datum/supply_pack/eng/radsuit
contains = list(
diff --git a/code/datums/supplypacks/misc_vr.dm b/code/datums/supplypacks/misc_vr.dm
index 973840f5b2..f85d7b9723 100644
--- a/code/datums/supplypacks/misc_vr.dm
+++ b/code/datums/supplypacks/misc_vr.dm
@@ -39,7 +39,10 @@
cost = 150
containertype = /obj/structure/closet/crate/secure/gear
containername = "eva hardsuit crate"
- access = access_mining
+ access = list(access_mining,
+ access_eva,
+ access_explorer,
+ access_pilot)
/datum/supply_pack/misc/mining_rig
name = "industrial hardsuit (empty)"
@@ -49,4 +52,5 @@
cost = 150
containertype = /obj/structure/closet/crate/secure/gear
containername = "industrial hardsuit crate"
- access = access_mining
\ No newline at end of file
+ access = list(access_mining,
+ access_eva)
\ No newline at end of file
diff --git a/code/datums/supplypacks/security.dm b/code/datums/supplypacks/security.dm
index 40698def5d..4e4c8c1311 100644
--- a/code/datums/supplypacks/security.dm
+++ b/code/datums/supplypacks/security.dm
@@ -31,7 +31,6 @@
cost = 40
containertype = /obj/structure/closet/crate/secure/gear
containername = "Armor crate"
- access_armory //VOREStation Add - Armor is for the armory.
/datum/supply_pack/security/riot_gear
name = "Gear - Riot"
diff --git a/code/datums/supplypacks/security_vr.dm b/code/datums/supplypacks/security_vr.dm
index 3538a311d5..12f2fa33b3 100644
--- a/code/datums/supplypacks/security_vr.dm
+++ b/code/datums/supplypacks/security_vr.dm
@@ -17,6 +17,9 @@
access_xenobiology)
*/
+/datum/supply_pack/randomised/security/armor
+ access = access_armory
+
/datum/supply_pack/security/biosuit
contains = list(
/obj/item/clothing/head/bio_hood/security = 3,
diff --git a/code/datums/supplypacks/voidsuits_vr.dm b/code/datums/supplypacks/voidsuits_vr.dm
index ccf296a10a..8fcfd8ae50 100644
--- a/code/datums/supplypacks/voidsuits_vr.dm
+++ b/code/datums/supplypacks/voidsuits_vr.dm
@@ -62,7 +62,6 @@
)
/datum/supply_pack/voidsuits/supply
- name = "Mining voidsuits"
contains = list(
/obj/item/clothing/suit/space/void/mining = 3,
/obj/item/clothing/head/helmet/space/void/mining = 3,
diff --git a/code/datums/uplink/stealth_items.dm b/code/datums/uplink/stealth_items.dm
index 57d379d3fc..0a17e659a6 100644
--- a/code/datums/uplink/stealth_items.dm
+++ b/code/datums/uplink/stealth_items.dm
@@ -33,3 +33,8 @@
name = "Voice Changer"
item_cost = 15
path = /obj/item/clothing/mask/gas/voice
+
+/datum/uplink_item/item/stealth_items/makeover
+ name = "Makeover Kit"
+ item_cost = 5
+ path = /obj/item/weapon/makeover
\ No newline at end of file
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 104fa066b6..7154b9f47d 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -16,7 +16,8 @@
var/moved_recently = 0
var/mob/pulledby = null
var/item_state = null // Used to specify the item state for the on-mob overlays.
- var/icon_scale = 1 // Used to scale icons up or down in update_transform().
+ var/icon_scale_x = 1 // Used to scale icons up or down horizonally in update_transform().
+ var/icon_scale_y = 1 // Used to scale icons up or down vertically in update_transform().
var/icon_rotation = 0 // Used to rotate icons in update_transform()
var/old_x = 0
var/old_y = 0
@@ -476,13 +477,18 @@
/atom/movable/proc/update_transform()
var/matrix/M = matrix()
- M.Scale(icon_scale)
+ M.Scale(icon_scale_x, icon_scale_y)
M.Turn(icon_rotation)
src.transform = M
// Use this to set the object's scale.
-/atom/movable/proc/adjust_scale(new_scale)
- icon_scale = new_scale
+/atom/movable/proc/adjust_scale(new_scale_x, new_scale_y)
+ if(isnull(new_scale_y))
+ new_scale_y = new_scale_x
+ if(new_scale_x != 0)
+ icon_scale_x = new_scale_x
+ if(new_scale_y != 0)
+ icon_scale_y = new_scale_y
update_transform()
/atom/movable/proc/adjust_rotation(new_rotation)
diff --git a/code/game/objects/items/bells.dm b/code/game/objects/items/bells.dm
new file mode 100644
index 0000000000..43046d22b7
--- /dev/null
+++ b/code/game/objects/items/bells.dm
@@ -0,0 +1,90 @@
+/obj/item/weapon/deskbell
+ name = "desk bell"
+ desc = "An annoying bell. Ring for service."
+ icon = 'icons/obj/items.dmi'
+ icon_state = "deskbell"
+ force = 2
+ throwforce = 2
+ w_class = 2.0
+ var/broken
+ attack_verb = list("annoyed")
+ var/static/radial_examine = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_examine")
+ var/static/radial_use = image(icon = 'icons/mob/radial.dmi', icon_state = "radial_use")
+
+/obj/item/weapon/deskbell/examine(mob/user)
+ ..()
+ if(broken)
+ to_chat(user,"It looks damaged, the ringer is stuck firmly inside.")
+
+/obj/item/weapon/deskbell/attack(mob/target as mob, mob/living/user as mob)
+ if(!broken)
+ playsound(user.loc, 'sound/effects/deskbell.ogg', 50, 1)
+ ..()
+
+/obj/item/weapon/deskbell/attack_hand(mob/user)
+
+ //This defines the radials and what call we're assiging to them.
+ var/list/options = list()
+ options["examine"] = radial_examine
+ if(!broken)
+ options["use"] = radial_use
+
+
+ // Just an example, if the bell had no options, due to conditionals, nothing would happen here.
+ if(length(options) < 1)
+ return
+
+ // Right, if there's only one available radial...
+ // For example, say, the bell's broken so you can only examine, it just does that (doesn't show radial)..
+ var/list/choice = list()
+ if(length(options) == 1)
+ for(var/key in options)
+ choice = key
+ else
+ // If we have other options, it will show the radial menu for the player to decide.
+ choice = show_radial_menu(user, src, options, require_near = !issilicon(user))
+
+ // Once the player has decided their option, choose the behaviour that will happen under said option.
+ switch(choice)
+ if("examine")
+ examine(user)
+
+ if("use")
+ if(check_ability(user))
+ ring(user)
+ add_fingerprint(user)
+
+/obj/item/weapon/deskbell/proc/ring(mob/user)
+ if(user.a_intent == "harm")
+ playsound(user.loc, 'sound/effects/deskbell_rude.ogg', 50, 1)
+ to_chat(user,"You hammer [src] rudely!")
+ if (prob(2))
+ break_bell(user)
+ else
+ playsound(user.loc, 'sound/effects/deskbell.ogg', 50, 1)
+ to_chat(user,"You gracefully ring [src].")
+
+/obj/item/weapon/deskbell/proc/check_ability(mob/user)
+ if (ishuman(user))
+ var/mob/living/carbon/human/H = user
+ var/obj/item/organ/external/temp = H.organs_by_name["r_hand"]
+ if (H.hand)
+ temp = H.organs_by_name["l_hand"]
+ if(temp && !temp.is_usable())
+ to_chat(H,"You try to move your [temp.name], but cannot!")
+ return 0
+ return 1
+ else
+ to_chat(user,"You are not able to ring [src].")
+ return 0
+
+/obj/item/weapon/deskbell/attackby(obj/item/i, mob/user, params)
+ if(!istype(i))
+ return
+ if(!broken)
+ ring(user)
+
+
+/obj/item/weapon/deskbell/proc/break_bell(mob/user)
+ to_chat(user,"The ringing abruptly stops as [src]'s ringer gets jammed inside!")
+ broken = 1
diff --git a/code/game/objects/items/weapons/cosmetics.dm b/code/game/objects/items/weapons/cosmetics.dm
index 3d142b223a..4533f41537 100644
--- a/code/game/objects/items/weapons/cosmetics.dm
+++ b/code/game/objects/items/weapons/cosmetics.dm
@@ -87,4 +87,26 @@
text = "guy"
if(FEMALE)
text = "lady"
- user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].")
\ No newline at end of file
+ user.visible_message("[user] uses [src] to comb their hair with incredible style and sophistication. What a [text].")
+
+/obj/item/weapon/makeover
+ name = "makeover kit"
+ desc = "A tiny case containing a mirror and some contact lenses."
+ w_class = ITEMSIZE_TINY
+ icon = 'icons/obj/items.dmi'
+ icon_state = "trinketbox"
+ var/list/ui_users = list()
+
+/obj/item/weapon/makeover/attack_self(mob/living/carbon/user as mob)
+ if(ishuman(user))
+ to_chat(user, "You flip open \the [src] and begin to adjust your appearance.")
+ var/datum/nano_module/appearance_changer/AC = ui_users[user]
+ if(!AC)
+ AC = new(src, user)
+ AC.name = "SalonPro Porta-Makeover Deluxe™"
+ ui_users[user] = AC
+ AC.ui_interact(user)
+ var/mob/living/carbon/human/H = user
+ var/obj/item/organ/internal/eyes/E = H.internal_organs_by_name[O_EYES]
+ if(istype(E))
+ E.change_eye_color()
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/wallets.dm b/code/game/objects/items/weapons/storage/wallets.dm
index ce1227014c..95db86460e 100644
--- a/code/game/objects/items/weapons/storage/wallets.dm
+++ b/code/game/objects/items/weapons/storage/wallets.dm
@@ -35,7 +35,8 @@
/obj/item/weapon/tool/screwdriver,
/obj/item/weapon/stamp,
/obj/item/clothing/accessory/permit,
- /obj/item/clothing/accessory/badge
+ /obj/item/clothing/accessory/badge,
+ /obj/item/weapon/makeover
)
cant_hold = list(/obj/item/weapon/tool/screwdriver/power)
slot_flags = SLOT_ID
diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm
index 0f9f85582f..8f3a50bbed 100644
--- a/code/game/objects/random/misc.dm
+++ b/code/game/objects/random/misc.dm
@@ -519,4 +519,13 @@
/obj/effect/decal/cleanable/ash,
/obj/item/weapon/cigbutt,
/obj/item/weapon/cigbutt/cigarbutt,
- /obj/effect/decal/remains/mouse)
\ No newline at end of file
+ /obj/effect/decal/remains/mouse)
+
+/obj/random/janusmodule
+ name = "random janus circuit"
+ desc = "A random (possibly broken) Janus module."
+ icon = 'icons/obj/abductor.dmi'
+ icon_state = "circuit_damaged"
+
+/obj/random/janusmodule/item_to_spawn()
+ return pick(subtypesof(/obj/item/weapon/circuitboard/mecha/imperion))
diff --git a/code/game/objects/structures/flora/trees.dm b/code/game/objects/structures/flora/trees.dm
index 6db66fc01a..c02d813ffc 100644
--- a/code/game/objects/structures/flora/trees.dm
+++ b/code/game/objects/structures/flora/trees.dm
@@ -14,11 +14,27 @@
var/product_amount = 10 // How much of a stack you get, if the above is defined.
var/is_stump = FALSE // If true, suspends damage tracking and most other effects.
var/indestructable = FALSE // If true, the tree cannot die.
+ var/randomize_size = FALSE // If true, the tree will choose a random scale in the X and Y directions to stretch.
/obj/structure/flora/tree/Initialize()
icon_state = choose_icon_state()
+
+ if(randomize_size)
+ icon_scale_x = rand(90, 125) / 100
+ icon_scale_y = rand(90, 125) / 100
+
+ if(prob(50))
+ icon_scale_x *= -1
+ update_transform()
+
return ..()
+/obj/structure/flora/tree/update_transform()
+ var/matrix/M = matrix()
+ M.Scale(icon_scale_x, icon_scale_y)
+ M.Translate(0, 16*(icon_scale_y-1))
+ animate(src, transform = M, time = 10)
+
// Override this for special icons.
/obj/structure/flora/tree/proc/choose_icon_state()
return icon_state
@@ -57,8 +73,11 @@
/obj/structure/flora/tree/proc/hit_animation()
var/init_px = pixel_x
var/shake_dir = pick(-1, 1)
- animate(src, transform=turn(matrix(), shake_animation_degrees * shake_dir), pixel_x=init_px + 2*shake_dir, time=1)
- animate(transform=null, pixel_x=init_px, time=6, easing=ELASTIC_EASING)
+ var/matrix/M = matrix()
+ M.Scale(icon_scale_x, icon_scale_y)
+ M.Translate(0, 16*(icon_scale_y-1))
+ animate(src, transform=turn(M, shake_animation_degrees * shake_dir), pixel_x=init_px + 2*shake_dir, time=1)
+ animate(transform=M, pixel_x=init_px, time=6, easing=ELASTIC_EASING)
// Used when the tree gets hurt.
/obj/structure/flora/tree/proc/adjust_health(var/amount, var/damage_wood = FALSE)
@@ -282,13 +301,19 @@
base_state = "tree_sif"
product = /obj/item/stack/material/log/sif
catalogue_data = list(/datum/category_item/catalogue/flora/sif_tree)
+ randomize_size = TRUE
+ var/light_shift = 0
+
+/obj/structure/flora/tree/sif/choose_icon_state()
+ light_shift = rand(0, 5)
+ return "[base_state][light_shift]"
/obj/structure/flora/tree/sif/Initialize()
+ . = ..()
update_icon()
- return ..()
/obj/structure/flora/tree/sif/update_icon()
- set_light(5, 1, "#33ccff")
- var/image/glow = image(icon = 'icons/obj/flora/deadtrees.dmi', icon_state = "[base_state]_glow")
+ set_light(5 - light_shift, 1, "#33ccff") // 5 variants, missing bulbs. 5th has no bulbs, so no glow.
+ var/image/glow = image(icon = icon, icon_state = "[base_state][light_shift]_glow")
glow.plane = PLANE_LIGHTING_ABOVE
overlays = list(glow)
diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm
index c9731a0568..c94003e9fb 100644
--- a/code/game/objects/structures/loot_piles.dm
+++ b/code/game/objects/structures/loot_piles.dm
@@ -781,7 +781,8 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh
// Todo: Better loot.
/obj/structure/loot_pile/mecha/gygax/dark/adv
icon_state = "darkgygax_adv-broken"
- icon_scale = 1.5
+ icon_scale_x = 1.5
+ icon_scale_y = 1.5
pixel_y = 8
/obj/structure/loot_pile/mecha/gygax/medgax
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index cda61be1b3..6f35aff05f 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -383,6 +383,16 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
if(!pref.species_preview || !(pref.species_preview in all_species))
return TOPIC_NOACTION
+ var/datum/species/setting_species
+
+ if(all_species[href_list["set_species"]])
+ setting_species = all_species[href_list["set_species"]]
+ else
+ return TOPIC_NOACTION
+
+ if(((!(setting_species.spawn_flags & SPECIES_CAN_JOIN)) || (!is_alien_whitelisted(preference_mob(),setting_species))) && !check_rights(R_ADMIN, 0))
+ return TOPIC_NOACTION
+
var/prev_species = pref.species
pref.species = href_list["set_species"]
if(prev_species != pref.species)
diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm
index dee085dc2b..a4c55b27ae 100644
--- a/code/modules/events/event_container.dm
+++ b/code/modules/events/event_container.dm
@@ -22,6 +22,9 @@ var/global/list/severity_to_string = list(EVENT_LEVEL_MUNDANE = "Mundane", EVENT
var/last_world_time = 0
/datum/event_container/process()
+ if(!round_start_time)
+ return //don't do events if the round hasn't even started yet
+
if(!next_event_time)
set_event_delay()
diff --git a/code/modules/materials/material_recipes.dm b/code/modules/materials/material_recipes.dm
index 721b54826e..f45adba787 100644
--- a/code/modules/materials/material_recipes.dm
+++ b/code/modules/materials/material_recipes.dm
@@ -14,6 +14,7 @@
recipes += new/datum/stack_recipe("[display_name] grave marker", /obj/item/weapon/material/gravemarker, 5, time = 50, supplied_material = "[name]")
recipes += new/datum/stack_recipe("[display_name] ring", /obj/item/clothing/gloves/ring/material, 1, on_floor = 1, supplied_material = "[name]")
recipes += new/datum/stack_recipe("[display_name] bracelet", /obj/item/clothing/accessory/bracelet/material, 1, on_floor = 1, supplied_material = "[name]")
+ recipes += new/datum/stack_recipe("[display_name] deskbell", /obj/item/weapon/deskbell, 1, on_floor = 1, supplied_material = "[name]")
if(integrity>=50)
recipes += new/datum/stack_recipe("[display_name] door", /obj/structure/simple_door, 10, one_per_turf = 1, on_floor = 1, supplied_material = "[name]")
diff --git a/code/modules/materials/material_sheets.dm b/code/modules/materials/material_sheets.dm
index 1035c70eba..1fbecaec7e 100644
--- a/code/modules/materials/material_sheets.dm
+++ b/code/modules/materials/material_sheets.dm
@@ -275,6 +275,8 @@
return
/obj/item/stack/material/supermatter/attack_hand(mob/user)
+ . = ..()
+
update_mass()
radiation_repository.radiate(src, 5 + amount)
var/mob/living/M = user
diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm
index 08580c7f97..bdfab8f92b 100644
--- a/code/modules/mining/drilling/drill.dm
+++ b/code/modules/mining/drilling/drill.dm
@@ -318,13 +318,13 @@
name = "mining drill brace"
desc = "A machinery brace for an industrial drill. It looks easily two feet thick."
icon_state = "mining_brace"
+ circuit = /obj/item/weapon/circuitboard/miningdrillbrace
var/obj/machinery/mining/drill/connected
/obj/machinery/mining/brace/New()
..()
component_parts = list()
- component_parts += new /obj/item/weapon/circuitboard/miningdrillbrace(src)
/obj/machinery/mining/brace/attackby(obj/item/weapon/W as obj, mob/user as mob)
if(connected && connected.active)
diff --git a/code/modules/mob/_modifiers/modifiers.dm b/code/modules/mob/_modifiers/modifiers.dm
index 6e738094a1..665ae80617 100644
--- a/code/modules/mob/_modifiers/modifiers.dm
+++ b/code/modules/mob/_modifiers/modifiers.dm
@@ -42,7 +42,8 @@
var/accuracy // Positive numbers makes hitting things with guns easier, negatives make it harder.
var/accuracy_dispersion // Positive numbers make gun firing cover a wider tile range, and therefore more inaccurate. Negatives help negate dispersion penalties.
var/metabolism_percent // Adjusts the mob's metabolic rate, which affects reagent processing. Won't affect mobs without reagent processing.
- var/icon_scale_percent // Makes the holder's icon get scaled up or down.
+ var/icon_scale_x_percent // Makes the holder's icon get scaled wider or thinner.
+ var/icon_scale_y_percent // Makes the holder's icon get scaled taller or shorter.
var/attack_speed_percent // Makes the holder's 'attack speed' (click delay) shorter or longer.
var/pain_immunity // Makes the holder not care about pain while this is on. Only really useful to human mobs.
var/pulse_modifier // Modifier for pulse, will be rounded on application, then added to the normal 'pulse' multiplier which ranges between 0 and 5 normally. Only applied if they're living.
@@ -73,7 +74,7 @@
holder.modifiers.Remove(src)
if(mob_overlay_state) // We do this after removing ourselves from the list so that the overlay won't remain.
holder.update_modifier_visuals()
- if(icon_scale_percent) // Correct the scaling.
+ if(icon_scale_x_percent || icon_scale_y_percent) // Correct the scaling.
holder.update_transform()
if(client_color)
holder.update_client_color()
@@ -140,7 +141,7 @@
mod.on_applied()
if(mod.mob_overlay_state)
update_modifier_visuals()
- if(mod.icon_scale_percent)
+ if(mod.icon_scale_x_percent || mod.icon_scale_y_percent)
update_transform()
if(mod.client_color)
update_client_color()
@@ -232,8 +233,11 @@
effects += "Your metabolism is [metabolism_percent > 1.0 ? "faster" : "slower"], \
causing reagents in your body to process, and hunger to occur [multipler_to_percentage(metabolism_percent, TRUE)] [metabolism_percent > 1.0 ? "faster" : "slower"]."
- if(!isnull(icon_scale_percent))
- effects += "Your appearance is [multipler_to_percentage(icon_scale_percent, TRUE)] [icon_scale_percent > 1 ? "larger" : "smaller"]."
+ if(!isnull(icon_scale_x_percent))
+ effects += "Your appearance is [multipler_to_percentage(icon_scale_x_percent, TRUE)] [icon_scale_x_percent > 1 ? "wider" : "thinner"]."
+
+ if(!isnull(icon_scale_y_percent))
+ effects += "Your appearance is [multipler_to_percentage(icon_scale_y_percent, TRUE)] [icon_scale_y_percent > 1 ? "taller" : "shorter"]."
if(!isnull(attack_speed_percent))
effects += "The delay between attacking is [multipler_to_percentage(attack_speed_percent, TRUE)] [disable_duration_percent > 1.0 ? "longer" : "shorter"]."
diff --git a/code/modules/mob/_modifiers/modifiers_misc.dm b/code/modules/mob/_modifiers/modifiers_misc.dm
index 7d1ab792ff..e81cf0aaa8 100644
--- a/code/modules/mob/_modifiers/modifiers_misc.dm
+++ b/code/modules/mob/_modifiers/modifiers_misc.dm
@@ -56,7 +56,8 @@ the artifact triggers the rage.
outgoing_melee_damage_percent = 1.5 // 50% more damage from melee.
max_health_percent = 1.5 // More health as a buffer, however the holder might fall into crit after this expires if they're mortally wounded.
disable_duration_percent = 0.25 // Disables only last 25% as long.
- icon_scale_percent = 1.2 // Look scarier.
+ icon_scale_x_percent = 1.2 // Look scarier.
+ icon_scale_y_percent = 1.2
pain_immunity = TRUE // Avoid falling over from shock (at least until it expires).
// The less good stuff.
diff --git a/code/modules/mob/_modifiers/traits.dm b/code/modules/mob/_modifiers/traits.dm
index d0b0abfd89..200f8eabfb 100644
--- a/code/modules/mob/_modifiers/traits.dm
+++ b/code/modules/mob/_modifiers/traits.dm
@@ -64,25 +64,29 @@
name = "Larger"
desc = "Your body is larger than average."
- icon_scale_percent = 1.1
+ icon_scale_x_percent = 1.1
+ icon_scale_y_percent = 1.1
/datum/modifier/trait/large
name = "Large"
desc = "Your body is a bit larger than average."
- icon_scale_percent = 1.05
+ icon_scale_x_percent = 1.05
+ icon_scale_y_percent = 1.05
/datum/modifier/trait/small
name = "Small"
desc = "Your body is a bit smaller than average."
- icon_scale_percent = 0.95
+ icon_scale_x_percent = 0.95
+ icon_scale_y_percent = 0.95
/datum/modifier/trait/smaller
name = "Smaller"
desc = "Your body is smaller than average."
- icon_scale_percent = 0.9
+ icon_scale_x_percent = 0.9
+ icon_scale_y_percent = 0.9
/datum/modifier/trait/colorblind_protanopia
name = "Protanopia"
diff --git a/code/modules/mob/_modifiers/unholy.dm b/code/modules/mob/_modifiers/unholy.dm
index 0eadabc83a..0b6f69a21b 100644
--- a/code/modules/mob/_modifiers/unholy.dm
+++ b/code/modules/mob/_modifiers/unholy.dm
@@ -14,7 +14,8 @@
disable_duration_percent = 0.25 // Disables only last 25% as long.
incoming_damage_percent = 0.5 // 50% incoming damage.
- icon_scale_percent = 1.2 // Become a bigger target.
+ icon_scale_x_percent = 1.2 // Become a bigger target.
+ icon_scale_y_percent = 1.2
pain_immunity = TRUE
slowdown = 2
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 90c8f40ec0..0cf20396d8 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -1139,8 +1139,8 @@
if(species.default_language)
add_language(species.default_language)
- //if(species.icon_scale != 1) //VOREStation Removal
- // update_transform() //VOREStation Removal
+ //if(species.icon_scale_x != 1 || species.icon_scale_y != 1) //VOREStation Removal
+ // update_transform() //VOREStation Removal
if(example) //VOREStation Edit begin
if(!(example == src))
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index 95f909950c..c2bb8cafe4 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -32,7 +32,8 @@
var/tail_animation // If set, the icon to obtain tail animation states from.
var/tail_hair
- var/icon_scale = 1 // Makes the icon larger/smaller.
+ var/icon_scale_x = 1 // Makes the icon wider/thinner.
+ var/icon_scale_y = 1 // Makes the icon taller/shorter.
var/race_key = 0 // Used for mob icon cache string.
var/icon/icon_template // Used for mob icon generation for non-32x32 species.
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 7a96239092..bb861986b8 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -124,15 +124,20 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
/mob/living/carbon/human/update_transform()
/* VOREStation Edit START - TODO - Consider switching to icon_scale
// First, get the correct size.
- var/desired_scale = icon_scale
+ var/desired_scale_x = icon_scale_x
+ var/desired_scale_y = icon_scale_y
- desired_scale *= species.icon_scale
+ desired_scale_x *= species.icon_scale_x
+ desired_scale_y *= species.icon_scale_y
for(var/datum/modifier/M in modifiers)
- if(!isnull(M.icon_scale_percent))
- desired_scale *= M.icon_scale_percent
+ if(!isnull(M.icon_scale_x_percent))
+ desired_scale_x *= M.icon_scale_x_percent
+ if(!isnull(M.icon_scale_y_percent))
+ desired_scale_y *= M.icon_scale_y_percent
*/
- var/desired_scale = size_multiplier
+ var/desired_scale_x = size_multiplier
+ var/desired_scale_y = size_multiplier
//VOREStation Edit End
// Regular stuff again.
@@ -145,12 +150,12 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
if(lying && !species.prone_icon) //Only rotate them if we're not drawing a specific icon for being prone.
M.Turn(90)
- M.Scale(desired_scale)
+ M.Scale(desired_scale_x, desired_scale_y)
M.Translate(1,-6)
layer = MOB_LAYER -0.01 // Fix for a byond bug where turf entry order no longer matters
else
- M.Scale(desired_scale)
- M.Translate(0, 16*(desired_scale-1))
+ M.Scale(desired_scale_x, desired_scale_y)
+ M.Translate(0, 16*(desired_scale_y-1))
layer = MOB_LAYER // Fix for a byond bug where turf entry order no longer matters
animate(src, transform = M, time = anim_time)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 4020f13d14..d5295289fa 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1174,19 +1174,20 @@ default behaviour is:
/mob/living/update_transform()
// First, get the correct size.
- var/desired_scale = size_multiplier //VOREStation edit
+ var/desired_scale_x = size_multiplier //VOREStation edit
+ var/desired_scale_y = size_multiplier //VOREStation edit
for(var/datum/modifier/M in modifiers)
- if(!isnull(M.icon_scale_percent))
- desired_scale *= M.icon_scale_percent
+ if(!isnull(M.icon_scale_x_percent))
+ desired_scale_x *= M.icon_scale_x_percent
+ if(!isnull(M.icon_scale_y_percent))
+ desired_scale_y *= M.icon_scale_y_percent
// Now for the regular stuff.
var/matrix/M = matrix()
- M.Scale(desired_scale)
- M.Translate(0, 16*(desired_scale-1))
- src.transform = M
+ M.Scale(desired_scale_x, desired_scale_y)
+ M.Translate(0, 16*(desired_scale_y-1))
//animate(src, transform = M, time = 10) //VOREStation edit
-
// This handles setting the client's color variable, which makes everything look a specific color.
// This proc is here so it can be called without needing to check if the client exists, or if the client relogs.
/mob/living/update_client_color()
diff --git a/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm b/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm
index d722027ad3..255eef5318 100644
--- a/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm
+++ b/code/modules/mob/living/silicon/robot/subtypes/gravekeeper.dm
@@ -7,6 +7,7 @@
braintype = "Drone"
idcard_type = /obj/item/weapon/card/id
icon_selected = FALSE
+ can_be_antagged = FALSE
/mob/living/silicon/robot/gravekeeper/init()
aiCamera = new/obj/item/device/camera/siliconcam/robot_camera(src)
@@ -23,4 +24,4 @@
laws = new /datum/ai_laws/gravekeeper()
- playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0)
\ No newline at end of file
+ playsound(loc, 'sound/mecha/nominalsyndi.ogg', 75, 0)
diff --git a/code/modules/mob/living/simple_animal/slime/subtypes.dm b/code/modules/mob/living/simple_animal/slime/subtypes.dm
new file mode 100644
index 0000000000..18b43dc33c
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/slime/subtypes.dm
@@ -0,0 +1,748 @@
+// Tier 1
+
+/mob/living/simple_animal/slime/purple
+ desc = "This slime is rather toxic to handle, as it is poisonous."
+ color = "#CC23FF"
+ slime_color = "purple"
+ coretype = /obj/item/slime_extract/purple
+ reagent_injected = "toxin"
+
+ description_info = "This slime spreads a toxin when it attacks. A biosuit or other thick armor can protect from the toxic attack."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/dark_purple,
+ /mob/living/simple_animal/slime/dark_blue,
+ /mob/living/simple_animal/slime/green,
+ /mob/living/simple_animal/slime
+ )
+
+
+/mob/living/simple_animal/slime/orange
+ desc = "This slime is known to be flammable and can ignite enemies."
+ color = "#FFA723"
+ slime_color = "orange"
+ coretype = /obj/item/slime_extract/orange
+
+ description_info = "Attacks from this slime can ignite you. A firesuit can protect from the burning attacks of this slime."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/dark_purple,
+ /mob/living/simple_animal/slime/yellow,
+ /mob/living/simple_animal/slime/red,
+ /mob/living/simple_animal/slime
+ )
+
+/mob/living/simple_animal/slime/orange/post_attack(mob/living/L, intent)
+ if(intent != I_HELP)
+ L.adjust_fire_stacks(1)
+ if(prob(25))
+ L.IgniteMob()
+ ..()
+
+/mob/living/simple_animal/slime/blue
+ desc = "This slime produces 'cryotoxin' and uses it against their foes. Very deadly to other slimes."
+ color = "#19FFFF"
+ slime_color = "blue"
+ coretype = /obj/item/slime_extract/blue
+ reagent_injected = "cryotoxin"
+
+ description_info = "Attacks from this slime can chill you. A biosuit or other thick armor can protect from the chilling attack."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/dark_blue,
+ /mob/living/simple_animal/slime/silver,
+ /mob/living/simple_animal/slime/pink,
+ /mob/living/simple_animal/slime
+ )
+
+
+/mob/living/simple_animal/slime/metal
+ desc = "This slime is a lot more resilient than the others, due to having a metamorphic metallic and sloped surface."
+ color = "#5F5F5F"
+ slime_color = "metal"
+ shiny = 1
+ coretype = /obj/item/slime_extract/metal
+
+ description_info = "This slime is a lot more durable and tough to damage than the others."
+
+ resistance = 10 // Sloped armor is strong.
+ maxHealth = 250
+ maxHealth_adult = 350
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/silver,
+ /mob/living/simple_animal/slime/yellow,
+ /mob/living/simple_animal/slime/gold,
+ /mob/living/simple_animal/slime
+ )
+
+// Tier 2
+
+/mob/living/simple_animal/slime/yellow
+ desc = "This slime is very conductive, and is known to use electricity as a means of defense moreso than usual for slimes."
+ color = "#FFF423"
+ slime_color = "yellow"
+ coretype = /obj/item/slime_extract/yellow
+
+ ranged = 1
+ shoot_range = 3
+ firing_lines = 1
+ projectiletype = /obj/item/projectile/beam/lightning/slime
+ projectilesound = 'sound/weapons/gauss_shoot.ogg' // Closest thing to a 'thunderstrike' sound we have.
+ glows = TRUE
+
+ description_info = "This slime will fire lightning attacks at enemies if they are at range, and generate electricity \
+ for their stun attack faster than usual. Insulative or reflective armor can protect from the lightning."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/bluespace,
+ /mob/living/simple_animal/slime/bluespace,
+ /mob/living/simple_animal/slime/metal,
+ /mob/living/simple_animal/slime/orange
+ )
+
+/mob/living/simple_animal/slime/yellow/handle_regular_status_updates()
+ if(stat == CONSCIOUS)
+ if(prob(25))
+ power_charge = between(0, power_charge + 1, 10)
+ ..()
+
+/obj/item/projectile/beam/lightning/slime
+ power = 15
+
+/mob/living/simple_animal/slime/yellow/ClosestDistance() // Needed or else they won't eat monkeys outside of melee range.
+ if(target_mob && ishuman(target_mob))
+ var/mob/living/carbon/human/H = target_mob
+ if(istype(H.species, /datum/species/monkey))
+ return 1
+ return ..()
+
+
+/mob/living/simple_animal/slime/dark_purple
+ desc = "This slime produces ever-coveted phoron. Risky to handle but very much worth it."
+ color = "#660088"
+ slime_color = "dark purple"
+ coretype = /obj/item/slime_extract/dark_purple
+ reagent_injected = "phoron"
+
+ description_info = "This slime applies phoron to enemies it attacks. A biosuit or other thick armor can protect from the toxic attack. \
+ If hit with a burning attack, it will erupt in flames."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/purple,
+ /mob/living/simple_animal/slime/orange,
+ /mob/living/simple_animal/slime/ruby,
+ /mob/living/simple_animal/slime/ruby
+ )
+
+/mob/living/simple_animal/slime/dark_purple/proc/ignite()
+ visible_message("\The [src] erupts in an inferno!")
+ for(var/turf/simulated/target_turf in view(2, src))
+ target_turf.assume_gas("phoron", 30, 1500+T0C)
+ spawn(0)
+ target_turf.hotspot_expose(1500+T0C, 400)
+ qdel(src)
+
+/mob/living/simple_animal/slime/dark_purple/ex_act(severity)
+ log_and_message_admins("[src] ignited due to a chain reaction with an explosion.")
+ ignite()
+
+/mob/living/simple_animal/slime/dark_purple/fire_act(datum/gas_mixture/air, temperature, volume)
+ log_and_message_admins("[src] ignited due to exposure to fire.")
+ ignite()
+
+/mob/living/simple_animal/slime/dark_purple/bullet_act(var/obj/item/projectile/P, var/def_zone)
+ if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security.
+ log_and_message_admins("[src] ignited due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].")
+ ignite()
+ else
+ ..()
+
+/mob/living/simple_animal/slime/dark_purple/attackby(var/obj/item/weapon/W, var/mob/user)
+ if(istype(W) && W.force && W.damtype == BURN)
+ log_and_message_admins("[src] ignited due to being hit with a burning weapon ([W]) by [key_name(user)].")
+ ignite()
+ else
+ ..()
+
+
+
+
+/mob/living/simple_animal/slime/dark_blue
+ desc = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It tends to kill other slimes rather quickly."
+ color = "#2398FF"
+ glows = TRUE
+ slime_color = "dark blue"
+ coretype = /obj/item/slime_extract/dark_blue
+
+ description_info = "This slime is immune to the cold, however water will still kill it. A winter coat or other cold-resistant clothing can protect from the chilling aura."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/purple,
+ /mob/living/simple_animal/slime/blue,
+ /mob/living/simple_animal/slime/cerulean,
+ /mob/living/simple_animal/slime/cerulean
+ )
+
+ minbodytemp = 0
+ cold_damage_per_tick = 0
+
+/mob/living/simple_animal/slime/dark_blue/Life()
+ if(stat != DEAD)
+ cold_aura()
+ ..()
+
+/mob/living/simple_animal/slime/dark_blue/proc/cold_aura()
+ for(var/mob/living/L in view(2, src))
+ var/protection = L.get_cold_protection()
+
+ if(protection < 1)
+ var/cold_factor = abs(protection - 1)
+ var/delta = -20
+ delta *= cold_factor
+ L.bodytemperature = max(50, L.bodytemperature + delta)
+ var/turf/T = get_turf(src)
+ var/datum/gas_mixture/env = T.return_air()
+ if(env)
+ env.add_thermal_energy(-10 * 1000)
+
+/mob/living/simple_animal/slime/dark_blue/get_cold_protection()
+ return 1 // This slime is immune to cold.
+
+// Surface variant
+/mob/living/simple_animal/slime/dark_blue/feral
+ name = "feral slime"
+ desc = "The result of slimes escaping containment from some xenobiology lab. The slime makes other entities near it feel much colder, \
+ and it is more resilient to the cold. These qualities have made this color of slime able to thrive on a harsh, cold world and is able to rival \
+ the ferocity of other apex predators in this region of Sif. As such, it is a very invasive species."
+ description_info = "This slime makes other entities near it feel much colder, and is more resilient to the cold. It also has learned advanced combat tactics from \
+ having to endure the harsh world outside its lab. Note that processing this large slime will give six cores."
+ icon_scale_x = 2
+ icon_scale_y = 2
+ optimal_combat = TRUE // Gotta be sharp to survive out there.
+ rabid = TRUE
+ rainbow_core_candidate = FALSE
+ cores = 6
+ maxHealth = 150
+ maxHealth_adult = 250
+ type_on_death = /mob/living/simple_animal/slime/dark_blue // Otherwise infinite slimes might occur.
+ pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help.
+
+/mob/living/simple_animal/slime/dark_blue/feral/New()
+ ..()
+ make_adult()
+
+/mob/living/simple_animal/slime/silver
+ desc = "This slime is shiny, and can deflect lasers or other energy weapons directed at it."
+ color = "#AAAAAA"
+ slime_color = "silver"
+ coretype = /obj/item/slime_extract/silver
+ shiny = TRUE
+
+ description_info = "Tasers, including the slime version, are ineffective against this slime. The slimebation still works."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/metal,
+ /mob/living/simple_animal/slime/blue,
+ /mob/living/simple_animal/slime/amber,
+ /mob/living/simple_animal/slime/amber
+ )
+
+/mob/living/simple_animal/slime/silver/bullet_act(var/obj/item/projectile/P, var/def_zone)
+ if(istype(P,/obj/item/projectile/beam) || istype(P, /obj/item/projectile/energy))
+ visible_message("\The [src] reflects \the [P]!")
+
+ // Find a turf near or on the original location to bounce to
+ var/new_x = P.starting.x + pick(0, 0, 0, -1, 1, -2, 2)
+ var/new_y = P.starting.y + pick(0, 0, 0, -1, 1, -2, 2)
+ var/turf/curloc = get_turf(src)
+
+ // redirect the projectile
+ P.redirect(new_x, new_y, curloc, src)
+ return PROJECTILE_CONTINUE // complete projectile permutation
+ else
+ ..()
+
+
+// Tier 3
+
+/mob/living/simple_animal/slime/bluespace
+ desc = "Trapping this slime in a cell is generally futile, as it can teleport at will."
+ color = null
+ slime_color = "bluespace"
+ icon_state_override = "bluespace"
+ coretype = /obj/item/slime_extract/bluespace
+
+ description_info = "This slime will teleport to attack something if it is within a range of seven tiles. The teleport has a cooldown of five seconds."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/bluespace,
+ /mob/living/simple_animal/slime/bluespace,
+ /mob/living/simple_animal/slime/yellow,
+ /mob/living/simple_animal/slime/yellow
+ )
+
+ spattack_prob = 100
+ spattack_min_range = 3
+ spattack_max_range = 7
+ var/last_tele = null // Uses world.time
+ var/tele_cooldown = 5 SECONDS
+
+/mob/living/simple_animal/slime/bluespace/ClosestDistance() // Needed or the SA AI won't ever try to teleport.
+ if(world.time > last_tele + tele_cooldown)
+ return spattack_max_range - 1
+ return ..()
+
+/mob/living/simple_animal/slime/bluespace/SpecialAtkTarget()
+ // Teleport attack.
+ if(!target_mob)
+ to_chat(src, "There's nothing to teleport to.")
+ return FALSE
+
+ if(world.time < last_tele + tele_cooldown)
+ to_chat(src, "You can't teleport right now, wait a few seconds.")
+ return FALSE
+
+ var/list/nearby_things = range(1, target_mob)
+ var/list/valid_turfs = list()
+
+ // All this work to just go to a non-dense tile.
+ for(var/turf/potential_turf in nearby_things)
+ var/valid_turf = TRUE
+ if(potential_turf.density)
+ continue
+ for(var/atom/movable/AM in potential_turf)
+ if(AM.density)
+ valid_turf = FALSE
+ if(valid_turf)
+ valid_turfs.Add(potential_turf)
+
+
+
+ var/turf/T = get_turf(src)
+ var/turf/target_turf = pick(valid_turfs)
+
+ if(!target_turf)
+ to_chat(src, "There wasn't an unoccupied spot to teleport to.")
+ return FALSE
+
+ var/datum/effect/effect/system/spark_spread/s1 = new /datum/effect/effect/system/spark_spread
+ s1.set_up(5, 1, T)
+ var/datum/effect/effect/system/spark_spread/s2 = new /datum/effect/effect/system/spark_spread
+ s2.set_up(5, 1, target_turf)
+
+
+ T.visible_message("\The [src] vanishes!")
+ s1.start()
+
+ forceMove(target_turf)
+ playsound(target_turf, 'sound/effects/phasein.ogg', 50, 1)
+ to_chat(src, "You teleport to \the [target_turf].")
+
+ target_turf.visible_message("\The [src] appears!")
+ s2.start()
+
+ last_tele = world.time
+
+ if(Adjacent(target_mob))
+ PunchTarget()
+ return TRUE
+
+/mob/living/simple_animal/slime/ruby
+ desc = "This slime has great physical strength."
+ color = "#FF3333"
+ slime_color = "ruby"
+ shiny = TRUE
+ glows = TRUE
+ coretype = /obj/item/slime_extract/ruby
+
+ description_info = "This slime is unnaturally stronger, allowing it to hit much harder, take less damage, and be stunned for less time. \
+ Their glomp attacks also send the victim flying."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/dark_purple,
+ /mob/living/simple_animal/slime/dark_purple,
+ /mob/living/simple_animal/slime/ruby,
+ /mob/living/simple_animal/slime/ruby
+ )
+
+/mob/living/simple_animal/slime/ruby/New()
+ ..()
+ add_modifier(/datum/modifier/slime_strength, null, src) // Slime is always swole.
+
+/mob/living/simple_animal/slime/ruby/DoPunch(var/mob/living/L)
+ ..() // Do regular attacks.
+
+ if(istype(L))
+ if(a_intent == I_HURT)
+ visible_message("\The [src] sends \the [L] flying with the impact!")
+ playsound(src, "punch", 50, 1)
+ L.Weaken(1)
+ var/throwdir = get_dir(src, L)
+ L.throw_at(get_edge_target_turf(L, throwdir), 3, 1, src)
+
+
+/mob/living/simple_animal/slime/amber
+ desc = "This slime seems to be an expert in the culinary arts, as they create their own food to share with others. \
+ They would probably be very important to other slimes, if the other colors didn't try to kill them."
+ color = "#FFBB00"
+ slime_color = "amber"
+ shiny = TRUE
+ glows = TRUE
+ coretype = /obj/item/slime_extract/amber
+
+ description_info = "This slime feeds nearby entities passively while it is alive. This can cause uncontrollable \
+ slime growth and reproduction if not kept in check. The amber slime cannot feed itself, but can be fed by other amber slimes."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/silver,
+ /mob/living/simple_animal/slime/silver,
+ /mob/living/simple_animal/slime/amber,
+ /mob/living/simple_animal/slime/amber
+ )
+
+/mob/living/simple_animal/slime/amber/Life()
+ if(stat != DEAD)
+ feed_aura()
+ ..()
+
+/mob/living/simple_animal/slime/amber/proc/feed_aura()
+ for(var/mob/living/L in view(2, src))
+ if(L == src) // Don't feed themselves, or it is impossible to stop infinite slimes without killing all of the ambers.
+ continue
+ if(isslime(L))
+ var/mob/living/simple_animal/slime/S = L
+ S.adjust_nutrition(rand(15, 25))
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(H.isSynthetic())
+ continue
+ H.nutrition = between(0, H.nutrition + rand(15, 25), 600)
+
+
+
+/mob/living/simple_animal/slime/cerulean
+ desc = "This slime is generally superior in a wide range of attributes, compared to the common slime. The jack of all trades, but master of none."
+ color = "#4F7EAA"
+ slime_color = "cerulean"
+ coretype = /obj/item/slime_extract/cerulean
+
+ // Less than the specialized slimes, but higher than the rest.
+ maxHealth = 200
+ maxHealth_adult = 250
+
+ melee_damage_lower = 10
+ melee_damage_upper = 30
+
+ move_to_delay = 3
+
+
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/dark_blue,
+ /mob/living/simple_animal/slime/dark_blue,
+ /mob/living/simple_animal/slime/cerulean,
+ /mob/living/simple_animal/slime/cerulean
+ )
+
+// Tier 4
+
+/mob/living/simple_animal/slime/red
+ desc = "This slime is full of energy, and very aggressive. 'The red ones go faster.' seems to apply here."
+ color = "#FF3333"
+ slime_color = "red"
+ coretype = /obj/item/slime_extract/red
+ move_to_delay = 3 // The red ones go faster.
+
+ description_info = "This slime is faster than the others. Attempting to discipline this slime will always cause it to go berserk."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/red,
+ /mob/living/simple_animal/slime/oil,
+ /mob/living/simple_animal/slime/oil,
+ /mob/living/simple_animal/slime/orange
+ )
+
+
+/mob/living/simple_animal/slime/red/adjust_discipline(amount)
+ if(amount > 0)
+ if(!rabid)
+ enrage() // How dare you try to control the red slime.
+ say("Grrr...!")
+
+/mob/living/simple_animal/slime/red/enrage()
+ ..()
+ add_modifier(/datum/modifier/berserk, 30 SECONDS)
+
+
+/mob/living/simple_animal/slime/green
+ desc = "This slime is radioactive."
+ color = "#14FF20"
+ slime_color = "green"
+ coretype = /obj/item/slime_extract/green
+ glows = TRUE
+ reagent_injected = "radium"
+ var/rads = 25
+
+ description_info = "This slime will irradiate anything nearby passively, and will inject radium on attack. \
+ A radsuit or other thick and radiation-hardened armor can protect from this. It will only radiate while alive."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/purple,
+ /mob/living/simple_animal/slime/green,
+ /mob/living/simple_animal/slime/emerald,
+ /mob/living/simple_animal/slime/emerald
+ )
+
+/mob/living/simple_animal/slime/green/Life()
+ if(stat != DEAD)
+ irradiate()
+ ..()
+
+/mob/living/simple_animal/slime/green/proc/irradiate()
+ radiation_repository.radiate(src, rads)
+
+
+/mob/living/simple_animal/slime/pink
+ desc = "This slime has regenerative properties."
+ color = "#FF0080"
+ slime_color = "pink"
+ coretype = /obj/item/slime_extract/pink
+ glows = TRUE
+
+ description_info = "This slime will passively heal nearby entities within two tiles, including itself. It will only do this while alive."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/blue,
+ /mob/living/simple_animal/slime/light_pink,
+ /mob/living/simple_animal/slime/light_pink,
+ /mob/living/simple_animal/slime/pink
+ )
+
+/mob/living/simple_animal/slime/pink/Life()
+ if(stat != DEAD)
+ heal_aura()
+ ..()
+
+/mob/living/simple_animal/slime/pink/proc/heal_aura()
+ for(var/mob/living/L in view(src, 2))
+ if(L.stat == DEAD || L == target_mob)
+ continue
+ L.add_modifier(/datum/modifier/slime_heal, 5 SECONDS, src)
+
+/datum/modifier/slime_heal
+ name = "slime mending"
+ desc = "You feel somewhat gooy."
+ mob_overlay_state = "pink_sparkles"
+
+ on_created_text = "Twinkling spores of goo surround you. It makes you feel healthier."
+ on_expired_text = "The spores of goo have faded, although you feel much healthier than before."
+ stacks = MODIFIER_STACK_EXTEND
+
+/datum/modifier/slime_heal/tick()
+ if(holder.stat == DEAD) // Required or else simple animals become immortal.
+ expire()
+
+ if(ishuman(holder)) // Robolimbs need this code sadly.
+ var/mob/living/carbon/human/H = holder
+ for(var/obj/item/organ/external/E in H.organs)
+ var/obj/item/organ/external/O = E
+ O.heal_damage(2, 2, 0, 1)
+ else
+ holder.adjustBruteLoss(-2)
+ holder.adjustFireLoss(-2)
+
+ holder.adjustToxLoss(-2)
+ holder.adjustOxyLoss(-2)
+ holder.adjustCloneLoss(-1)
+
+
+
+/mob/living/simple_animal/slime/gold
+ desc = "This slime absorbs energy, and cannot be stunned by normal means."
+ color = "#EEAA00"
+ shiny = TRUE
+ slime_color = "gold"
+ coretype = /obj/item/slime_extract/gold
+ description_info = "This slime is immune to the slimebaton and taser, and will actually charge the slime, however it will still discipline the slime."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/metal,
+ /mob/living/simple_animal/slime/gold,
+ /mob/living/simple_animal/slime/sapphire,
+ /mob/living/simple_animal/slime/sapphire
+ )
+
+/mob/living/simple_animal/slime/gold/Weaken(amount)
+ power_charge = between(0, power_charge + amount, 10)
+ return
+
+/mob/living/simple_animal/slime/gold/Stun(amount)
+ power_charge = between(0, power_charge + amount, 10)
+ return
+
+/mob/living/simple_animal/slime/gold/get_description_interaction() // So it doesn't say to use a baton on them.
+ return list()
+
+
+// Tier 5
+
+/mob/living/simple_animal/slime/oil
+ desc = "This slime is explosive and volatile. Smoking near it is probably a bad idea."
+ color = "#333333"
+ slime_color = "oil"
+ shiny = TRUE
+ coretype = /obj/item/slime_extract/oil
+
+ description_info = "If this slime suffers damage from a fire or heat based source, or if it is caught inside \
+ an explosion, it will explode. Rabid oil slimes will charge at enemies, then suicide-bomb themselves. \
+ Bomb suits can protect from the explosion."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/oil,
+ /mob/living/simple_animal/slime/oil,
+ /mob/living/simple_animal/slime/red,
+ /mob/living/simple_animal/slime/red
+ )
+
+/mob/living/simple_animal/slime/oil/proc/explode()
+ if(stat != DEAD)
+ // explosion(src.loc, 1, 2, 4)
+ explosion(src.loc, 0, 2, 4) // A bit weaker since the suicide charger tended to gib the poor sod being targeted.
+ if(src) // Delete ourselves if the explosion didn't do it.
+ qdel(src)
+
+/mob/living/simple_animal/slime/oil/post_attack(var/mob/living/L, var/intent = I_HURT)
+ if(!rabid)
+ return ..()
+ if(intent == I_HURT || intent == I_GRAB)
+ say(pick("Sacrifice...!", "Sssss...", "Boom...!"))
+ sleep(2 SECOND)
+ log_and_message_admins("[src] has suicide-bombed themselves while trying to kill \the [L].")
+ explode()
+
+/mob/living/simple_animal/slime/oil/ex_act(severity)
+ log_and_message_admins("[src] exploded due to a chain reaction with another explosion.")
+ explode()
+
+/mob/living/simple_animal/slime/oil/fire_act(datum/gas_mixture/air, temperature, volume)
+ log_and_message_admins("[src] exploded due to exposure to fire.")
+ explode()
+
+/mob/living/simple_animal/slime/oil/bullet_act(var/obj/item/projectile/P, var/def_zone)
+ if(P.damage_type && P.damage_type == BURN && P.damage) // Most bullets won't trigger the explosion, as a mercy towards Security.
+ log_and_message_admins("[src] exploded due to bring hit by a burning projectile[P.firer ? " by [key_name(P.firer)]" : ""].")
+ explode()
+ else
+ ..()
+
+/mob/living/simple_animal/slime/oil/attackby(var/obj/item/weapon/W, var/mob/user)
+ if(istype(W) && W.force && W.damtype == BURN)
+ log_and_message_admins("[src] exploded due to being hit with a burning weapon ([W]) by [key_name(user)].")
+ explode()
+ else
+ ..()
+
+
+/mob/living/simple_animal/slime/sapphire
+ desc = "This slime seems a bit brighter than the rest, both figuratively and literally."
+ color = "#2398FF"
+ slime_color = "sapphire"
+ shiny = TRUE
+ glows = TRUE
+ coretype = /obj/item/slime_extract/sapphire
+
+ optimal_combat = TRUE // Lift combat AI restrictions to look smarter.
+ run_at_them = FALSE // Use fancy A* pathing.
+ astar_adjacent_proc = /turf/proc/TurfsWithAccess // Normal slimes don't care about cardinals (because BYOND) so smart slimes shouldn't as well.
+ move_to_delay = 3 // A* chasing is slightly slower in terms of movement speed than regular pathing so reducing this hopefully makes up for that.
+
+ description_info = "This slime uses more robust tactics when fighting and won't hold back, so it is dangerous to be alone \
+ with one if hostile, and especially dangerous if they outnumber you."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/sapphire,
+ /mob/living/simple_animal/slime/sapphire,
+ /mob/living/simple_animal/slime/gold,
+ /mob/living/simple_animal/slime/gold
+ )
+
+/mob/living/simple_animal/slime/emerald
+ desc = "This slime is faster than usual, even more so than the red slimes."
+ color = "#22FF22"
+ shiny = TRUE
+ glows = TRUE
+ slime_color = "emerald"
+ coretype = /obj/item/slime_extract/emerald
+
+ description_info = "This slime will make everything around it, and itself, faster for a few seconds, if close by."
+ move_to_delay = 2
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/green,
+ /mob/living/simple_animal/slime/green,
+ /mob/living/simple_animal/slime/emerald,
+ /mob/living/simple_animal/slime/emerald
+ )
+
+/mob/living/simple_animal/slime/emerald/Life()
+ if(stat != DEAD)
+ zoom_aura()
+ ..()
+
+/mob/living/simple_animal/slime/emerald/proc/zoom_aura()
+ for(var/mob/living/L in view(src, 2))
+ if(L.stat == DEAD || L == target_mob)
+ continue
+ L.add_modifier(/datum/modifier/technomancer/haste, 5 SECONDS, src)
+
+/mob/living/simple_animal/slime/light_pink
+ desc = "This slime seems a lot more peaceful than the others."
+ color = "#FF8888"
+ slime_color = "light pink"
+ coretype = /obj/item/slime_extract/light_pink
+
+ description_info = "This slime is effectively always disciplined initially."
+ obedience = 5
+ discipline = 5
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/pink,
+ /mob/living/simple_animal/slime/pink,
+ /mob/living/simple_animal/slime/light_pink,
+ /mob/living/simple_animal/slime/light_pink
+ )
+
+// Special
+/mob/living/simple_animal/slime/rainbow
+ desc = "This slime changes colors constantly."
+ color = null // Only slime subtype that uses a different icon_state.
+ slime_color = "rainbow"
+ coretype = /obj/item/slime_extract/rainbow
+ icon_state_override = "rainbow"
+ unity = TRUE
+
+ description_info = "This slime is considered to be the same color as all other slime colors at the same time for the purposes of \
+ other slimes being friendly to them, and therefore will never be harmed by another slime. \
+ Attacking this slime will provoke the wrath of all slimes within range."
+
+ slime_mutation = list(
+ /mob/living/simple_animal/slime/rainbow,
+ /mob/living/simple_animal/slime/rainbow,
+ /mob/living/simple_animal/slime/rainbow,
+ /mob/living/simple_animal/slime/rainbow
+ )
+
+/mob/living/simple_animal/slime/rainbow/New()
+ unify()
+ ..()
+
+// The RD's pet slime.
+/mob/living/simple_animal/slime/rainbow/kendrick
+ name = "Kendrick"
+ desc = "The Research Director's pet slime. It shifts colors constantly."
+ rainbow_core_candidate = FALSE
+
+/mob/living/simple_animal/slime/rainbow/kendrick/New()
+ pacify()
+ ..()
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm
index b5b4489f57..6ea44df66d 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/borer/borer.dm
@@ -1,237 +1,261 @@
-// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system.
-// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would
-// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite.
-
-/mob/living/simple_mob/animal/borer
- name = "cortical borer"
- desc = "A small, quivering sluglike creature."
- icon_state = "brainslug"
- item_state = "brainslug"
- icon_living = "brainslug"
- icon_dead = "brainslug_dead"
-
- response_help = "pokes"
- response_disarm = "prods"
- response_harm = "stomps on"
- attacktext = list("nipped")
- friendly = list("prods")
-
- status_flags = CANPUSH
- pass_flags = PASSTABLE
- movement_cooldown = 5
-
- universal_understand = TRUE
- can_be_antagged = TRUE
-
- holder_type = /obj/item/weapon/holder/borer
- ai_holder_type = null // This is player-controlled, always.
-
- var/chemicals = 10 // A resource used for reproduction and powers.
- var/mob/living/carbon/human/host = null // The humanoid host for the brain worm.
- var/true_name = null // String used when speaking among other worms.
- var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth.
- var/controlling = FALSE // Used in human death ceck.
- var/docile = FALSE // Sugar can stop borers from acting.
- var/has_reproduced = FALSE
- var/roundstart = FALSE // If true, spawning won't try to pull a ghost.
- var/used_dominate // world.time when the dominate power was last used.
-
-
-/mob/living/simple_mob/animal/borer/roundstart
- roundstart = TRUE
-
-/mob/living/simple_mob/animal/borer/Login()
- ..()
- if(mind)
- borers.add_antagonist(mind)
-
-/mob/living/simple_mob/animal/borer/Initialize()
- add_language("Cortical Link")
-
- verbs += /mob/living/proc/ventcrawl
- verbs += /mob/living/proc/hide
-
- true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]"
-
- if(!roundstart)
- request_player()
-
- return ..()
-
-/mob/living/simple_mob/animal/borer/handle_special()
- if(host && !stat && !host.stat)
- // Handle docility.
- if(host.reagents.has_reagent("sugar") && !docile)
- var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility."
- var/target = controlling ? host : src
- to_chat(target, span("warning", message))
- docile = TRUE
-
- else if(docile)
- var/message = "You shake off your lethargy as the sugar leaves your host's blood."
- var/target = controlling ? host : src
- to_chat(target, span("notice", message))
- docile = FALSE
-
- // Chem regen.
- if(chemicals < 250)
- chemicals++
-
- // Control stuff.
- if(controlling)
- if(docile)
- to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host..."))
- host.release_control()
- return
-
- if(prob(5))
- host.adjustBrainLoss(0.1)
-
- if(prob(host.brainloss/20))
- host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]")
-
-/mob/living/simple_mob/animal/borer/Stat()
- ..()
- if(client.statpanel == "Status")
- statpanel("Status")
- if(emergency_shuttle)
- var/eta_status = emergency_shuttle.get_status_panel_eta()
- if(eta_status)
- stat(null, eta_status)
- stat("Chemicals", chemicals)
-
-/mob/living/simple_mob/animal/borer/proc/detatch()
- if(!host || !controlling)
- return
-
- if(istype(host, /mob/living/carbon/human))
- var/mob/living/carbon/human/H = host
- var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
- if(head)
- head.implants -= src
-
- controlling = FALSE
-
- host.remove_language("Cortical Link")
- host.verbs -= /mob/living/carbon/proc/release_control
- host.verbs -= /mob/living/carbon/proc/punish_host
- host.verbs -= /mob/living/carbon/proc/spawn_larvae
-
- if(host_brain)
- // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed.
- // computer_id and IP are not updated magically on their own in offline mobs -walter0o
-
- // This shit need to die in a phoron fire.
-
- // host -> self
- var/h2s_id = host.computer_id
- var/h2s_ip= host.lastKnownIP
- host.computer_id = null
- host.lastKnownIP = null
-
- src.ckey = host.ckey
-
- if(!src.computer_id)
- src.computer_id = h2s_id
-
- if(!host_brain.lastKnownIP)
- src.lastKnownIP = h2s_ip
-
- // brain -> host
- var/b2h_id = host_brain.computer_id
- var/b2h_ip= host_brain.lastKnownIP
- host_brain.computer_id = null
- host_brain.lastKnownIP = null
-
- host.ckey = host_brain.ckey
-
- if(!host.computer_id)
- host.computer_id = b2h_id
-
- if(!host.lastKnownIP)
- host.lastKnownIP = b2h_ip
-
- qdel(host_brain)
-
-
-/mob/living/simple_mob/animal/borer/proc/leave_host()
- if(!host)
- return
-
- if(host.mind)
- borers.remove_antagonist(host.mind)
-
- forceMove(get_turf(host))
-
- reset_view(null)
- machine = null
-
- host.reset_view(null)
- host.machine = null
- host = null
-
-/mob/living/simple_mob/animal/borer/proc/request_player()
- var/datum/ghost_query/Q = new /datum/ghost_query/borer()
- var/list/winner = Q.query() // This will sleep the proc for awhile.
- if(winner.len)
- var/mob/observer/dead/D = winner[1]
- transfer_personality(D)
-
-/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate)
- if(!candidate || !candidate.mind)
- return
-
- src.mind = candidate.mind
- candidate.mind.current = src
- ckey = candidate.ckey
-
- if(mind)
- mind.assigned_role = "Cortical Borer"
- mind.special_role = "Cortical Borer"
-
- to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \
- into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \
- your host and your eventual spawn safe and warm."))
- to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.")
-
-/mob/living/simple_mob/animal/borer/cannot_use_vents()
- return
-
-// This is awful but its literally say code.
-/mob/living/simple_mob/animal/borer/say(message)
- message = sanitize(message)
- message = capitalize(message)
-
- if(!message)
- return
-
- if(stat >= DEAD)
- return say_dead(message)
- else if(stat)
- return
-
- if(client && client.prefs.muted & MUTE_IC)
- to_chat(src, span("danger", "You cannot speak in IC (muted)."))
- return
-
- if(copytext(message, 1, 2) == "*")
- return emote(copytext(message, 2))
-
- var/datum/language/L = parse_language(message)
- if(L && L.flags & HIVEMIND)
- L.broadcast(src,trim(copytext(message,3)), src.true_name)
- return
-
- if(!host)
- //TODO: have this pick a random mob within 3 tiles to speak for the borer.
- to_chat(src, span("warning", "You have no host to speak to."))
- return //No host, no audible speech.
-
- to_chat(src, "You drop words into [host]'s mind: \"[message]\"")
- to_chat(host, "Your own thoughts speak: \"[message]\"")
-
- for(var/mob/M in player_list)
- if(istype(M, /mob/new_player))
- continue
- else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
- to_chat(M, "[src.true_name] whispers to [host], \"[message]\"")
+// Borers are probably still going to be buggy as fuck, this is just bringing their mob defines up to the new system.
+// IMO they're a relic of several ages we're long past, their code and their design showing this plainly, but removing them would
+// make certain people Unhappy so here we are. They need a complete redesign but thats beyond the scope of the rewrite.
+
+/mob/living/simple_mob/animal/borer
+ name = "cortical borer"
+ desc = "A small, quivering sluglike creature."
+ icon_state = "brainslug"
+ item_state = "brainslug"
+ icon_living = "brainslug"
+ icon_dead = "brainslug_dead"
+
+ response_help = "pokes"
+ response_disarm = "prods"
+ response_harm = "stomps on"
+ attacktext = list("nipped")
+ friendly = list("prods")
+
+ status_flags = CANPUSH
+ pass_flags = PASSTABLE
+ movement_cooldown = 5
+
+ universal_understand = TRUE
+ can_be_antagged = TRUE
+
+ holder_type = /obj/item/weapon/holder/borer
+ ai_holder_type = null // This is player-controlled, always.
+
+ var/chemicals = 10 // A resource used for reproduction and powers.
+ var/max_chemicals = 250 // Max of said resource.
+ var/mob/living/carbon/human/host = null // The humanoid host for the brain worm.
+ var/true_name = null // String used when speaking among other worms.
+ var/mob/living/captive_brain/host_brain // Used for swapping control of the body back and forth.
+ var/controlling = FALSE // Used in human death ceck.
+ var/docile = FALSE // Sugar can stop borers from acting.
+ var/has_reproduced = FALSE
+ var/roundstart = FALSE // If true, spawning won't try to pull a ghost.
+ var/used_dominate // world.time when the dominate power was last used.
+
+
+/mob/living/simple_mob/animal/borer/roundstart
+ roundstart = TRUE
+
+/mob/living/simple_mob/animal/borer/Login()
+ ..()
+ if(mind)
+ borers.add_antagonist(mind)
+
+/mob/living/simple_mob/animal/borer/Initialize()
+ add_language("Cortical Link")
+
+ verbs += /mob/living/proc/ventcrawl
+ verbs += /mob/living/proc/hide
+
+ true_name = "[pick("Primary","Secondary","Tertiary","Quaternary")] [rand(1000,9999)]"
+
+ if(!roundstart)
+ request_player()
+
+ return ..()
+
+/mob/living/simple_mob/animal/borer/handle_special()
+ if(host && !stat && !host.stat)
+ // Handle docility.
+ if(host.reagents.has_reagent("sugar") && !docile)
+ var/message = "You feel the soporific flow of sugar in your host's blood, lulling you into docility."
+ var/target = controlling ? host : src
+ to_chat(target, span("warning", message))
+ docile = TRUE
+
+ else if(docile)
+ var/message = "You shake off your lethargy as the sugar leaves your host's blood."
+ var/target = controlling ? host : src
+ to_chat(target, span("notice", message))
+ docile = FALSE
+
+ // Chem regen.
+ if(chemicals < max_chemicals)
+ chemicals++
+
+ // Control stuff.
+ if(controlling)
+ if(docile)
+ to_chat(host, span("warning", "You are feeling far too docile to continue controlling your host..."))
+ host.release_control()
+ return
+
+ if(prob(5))
+ host.adjustBrainLoss(0.1)
+
+ if(prob(host.brainloss/20))
+ host.say("*[pick(list("blink","blink_r","choke","aflap","drool","twitch","twitch_v","gasp"))]")
+
+/mob/living/simple_mob/animal/borer/Stat()
+ ..()
+ if(client.statpanel == "Status")
+ statpanel("Status")
+ if(emergency_shuttle)
+ var/eta_status = emergency_shuttle.get_status_panel_eta()
+ if(eta_status)
+ stat(null, eta_status)
+ stat("Chemicals", chemicals)
+
+/mob/living/simple_mob/animal/borer/proc/detatch()
+ if(!host || !controlling)
+ return
+
+ if(istype(host, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = host
+ var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
+ if(head)
+ head.implants -= src
+
+ controlling = FALSE
+
+ host.remove_language("Cortical Link")
+ host.verbs -= /mob/living/carbon/proc/release_control
+ host.verbs -= /mob/living/carbon/proc/punish_host
+ host.verbs -= /mob/living/carbon/proc/spawn_larvae
+
+ if(host_brain)
+ // these are here so bans and multikey warnings are not triggered on the wrong people when ckey is changed.
+ // computer_id and IP are not updated magically on their own in offline mobs -walter0o
+
+ // This shit need to die in a phoron fire.
+
+ // host -> self
+ var/h2s_id = host.computer_id
+ var/h2s_ip= host.lastKnownIP
+ host.computer_id = null
+ host.lastKnownIP = null
+
+ src.ckey = host.ckey
+
+ if(!src.computer_id)
+ src.computer_id = h2s_id
+
+ if(!host_brain.lastKnownIP)
+ src.lastKnownIP = h2s_ip
+
+ // brain -> host
+ var/b2h_id = host_brain.computer_id
+ var/b2h_ip= host_brain.lastKnownIP
+ host_brain.computer_id = null
+ host_brain.lastKnownIP = null
+
+ host.ckey = host_brain.ckey
+
+ if(!host.computer_id)
+ host.computer_id = b2h_id
+
+ if(!host.lastKnownIP)
+ host.lastKnownIP = b2h_ip
+
+ qdel(host_brain)
+
+
+/mob/living/simple_mob/animal/borer/proc/leave_host()
+ if(!host)
+ return
+
+ if(host.mind)
+ borers.remove_antagonist(host.mind)
+
+ forceMove(get_turf(host))
+
+ reset_view(null)
+ machine = null
+
+ if(istype(host, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = host
+ var/obj/item/organ/external/head = H.get_organ(BP_HEAD)
+ if(head)
+ head.implants -= src
+
+ host.reset_view(null)
+ host.machine = null
+ host = null
+
+/mob/living/simple_mob/animal/borer/proc/request_player()
+ var/datum/ghost_query/Q = new /datum/ghost_query/borer()
+ var/list/winner = Q.query() // This will sleep the proc for awhile.
+ if(winner.len)
+ var/mob/observer/dead/D = winner[1]
+ transfer_personality(D)
+
+/mob/living/simple_mob/animal/borer/proc/transfer_personality(mob/candidate)
+ if(!candidate || !candidate.mind)
+ return
+
+ src.mind = candidate.mind
+ candidate.mind.current = src
+ ckey = candidate.ckey
+
+ if(mind)
+ mind.assigned_role = "Cortical Borer"
+ mind.special_role = "Cortical Borer"
+
+ to_chat(src, span("notice", "You are a cortical borer! You are a brain slug that worms its way \
+ into the head of its victim. Use stealth, persuasion and your powers of mind control to keep you, \
+ your host and your eventual spawn safe and warm."))
+ to_chat(src, "You can speak to your victim with say, to other borers with say :x, and use your Abilities tab to access powers.")
+
+/mob/living/simple_mob/animal/borer/cannot_use_vents()
+ return
+
+// This is awful but its literally say code.
+/mob/living/simple_mob/animal/borer/say(message)
+ message = sanitize(message)
+ message = capitalize(message)
+
+ if(!message)
+ return
+
+ if(stat >= DEAD)
+ return say_dead(message)
+ else if(stat)
+ return
+
+ if(client && client.prefs.muted & MUTE_IC)
+ to_chat(src, span("danger", "You cannot speak in IC (muted)."))
+ return
+
+ if(copytext(message, 1, 2) == "*")
+ return emote(copytext(message, 2))
+
+ var/datum/language/L = parse_language(message)
+ if(L && L.flags & HIVEMIND)
+ L.broadcast(src,trim(copytext(message,3)), src.true_name)
+ return
+
+ if(!host)
+ if(chemicals >= 30)
+ to_chat(src, span("alien", "..You emit a psionic pulse with an encoded message.."))
+ var/list/nearby_mobs = list()
+ for(var/mob/living/LM in view(src, 1 + round(6 * (chemicals / max_chemicals))))
+ if(LM == src)
+ continue
+ if(!LM.stat)
+ nearby_mobs += LM
+ var/mob/living/speaker
+ if(nearby_mobs.len)
+ speaker = input("Choose a target speaker.") as null|anything in nearby_mobs
+ if(speaker)
+ log_admin("[src.ckey]/([src]) tried to force [speaker] to say: [message]")
+ message_admins("[src.ckey]/([src]) tried to force [speaker] to say: [message]")
+ speaker.say("[message]")
+ return
+ to_chat(src, span("alien", "..But nothing heard it.."))
+ else
+ to_chat(src, span("warning", "You have no host to speak to."))
+ return //No host, no audible speech.
+
+ to_chat(src, "You drop words into [host]'s mind: \"[message]\"")
+ to_chat(host, "Your own thoughts speak: \"[message]\"")
+
+ for(var/mob/M in player_list)
+ if(istype(M, /mob/new_player))
+ continue
+ else if(M.stat == DEAD && M.is_preference_enabled(/datum/client_preference/ghost_ears))
+ to_chat(M, "[src.true_name] whispers to [host], \"[message]\"")
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm
index 5e77a57a3c..4042dd30b4 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/pets/bird.dm
@@ -57,7 +57,8 @@
icon_state = "commonblackbird"
icon_dead = "commonblackbird-dead"
tt_desc = "E Turdus merula"
- icon_scale = 0.5
+ icon_scale_x = 0.5
+ icon_scale_y = 0.5
/mob/living/simple_mob/animal/passive/bird/azure_tit
name = "azure tit"
@@ -65,7 +66,8 @@
icon_state = "azuretit"
icon_dead = "azuretit-dead"
tt_desc = "E Cyanistes cyanus"
- icon_scale = 0.5
+ icon_scale_x = 0.5
+ icon_scale_y = 0.5
/mob/living/simple_mob/animal/passive/bird/european_robin
name = "european robin"
@@ -73,7 +75,8 @@
icon_state = "europeanrobin"
icon_dead = "europeanrobin-dead"
tt_desc = "E Erithacus rubecula"
- icon_scale = 0.5
+ icon_scale_x = 0.5
+ icon_scale_y = 0.5
/mob/living/simple_mob/animal/passive/bird/goldcrest
name = "goldcrest"
@@ -82,7 +85,8 @@
icon_state = "goldcrest"
icon_dead = "goldcrest-dead"
tt_desc = "E Regulus regulus"
- icon_scale = 0.5
+ icon_scale_x = 0.5
+ icon_scale_y = 0.5
/mob/living/simple_mob/animal/passive/bird/ringneck_dove
name = "ringneck dove"
@@ -90,4 +94,5 @@
icon_state = "ringneckdove"
icon_dead = "ringneckdove-dead"
tt_desc = "E Streptopelia risoria" // This is actually disputed IRL but since we can't tell the future it'll stay the same for 500+ years.
- icon_scale = 0.5
+ icon_scale_x = 0.5
+ icon_scale_y = 0.5
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm
index ca8288e421..32eccb00af 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/hooligan_crab.dm
@@ -32,7 +32,8 @@
icon_state = "sif_crab"
icon_living = "sif_crab"
icon_dead = "sif_crab_dead"
- icon_scale = 1.5
+ icon_scale_x = 1.5
+ icon_scale_y = 1.5
faction = "crabs"
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm
index 881615138e..bb6a25604a 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/shantak.dm
@@ -67,7 +67,8 @@
name = "big shantak"
desc = "A piglike creature with a bright iridiscent mane that sparkles as though lit by an inner light. \
This one seems bigger than the others, and has a commanding presence."
- icon_scale = 1.5
+ icon_scale_x = 1.5
+ icon_scale_y = 1.5
maxHealth = 125
player_msg = "You have the ability to command other shantaks to follow you."
diff --git a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm
index 026c803e4b..36253dce84 100644
--- a/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/humanoid/mercs/mercs_vr.dm
@@ -1,3 +1,9 @@
/datum/say_list/merc/unknown_ind
speak = list("One day the'll fix that damn engine..","Next time, We're hidding on the tropical beach planet.","Wish I had better equipment...","I knew I should have been a line chef...","Fuckin' helmet keeps fogging up.","Hate this blocky ass ship.")
- say_got_target = list("Looks like trouble!","Contact!","We've got company!","Perimeter Breached!!")
\ No newline at end of file
+ say_got_target = list("Looks like trouble!","Contact!","We've got company!","Perimeter Breached!!")
+
+/mob/living/simple_mob/humanoid/merc/melee/sword/space
+ name = "mercenary commando"
+
+/mob/living/simple_mob/humanoid/merc/ranged/space
+ name = "mercenary commando"
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm
index 5e4d877751..bf2e3eb18a 100644
--- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/ranged_damage.dm
@@ -105,7 +105,8 @@
name = "siege engine hivebot"
desc = "A large robot capable of delivering long range bombardment."
projectiletype = /obj/item/projectile/arc/test
- icon_scale = 2
+ icon_scale_x = 2
+ icon_scale_y = 2
icon_state = "red"
icon_living = "red"
diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm
index 007f190c04..7f4e10f05d 100644
--- a/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/hivebot/tank.dm
@@ -39,7 +39,8 @@
desc = "A large robot."
maxHealth = 10 LASERS_TO_KILL // 300 health
health = 10 LASERS_TO_KILL
- icon_scale = 2
+ icon_scale_x = 2
+ icon_scale_y = 2
player_msg = "You have a very large amount of health."
@@ -49,7 +50,8 @@
desc = "A robot clad in heavy armor."
maxHealth = 5 LASERS_TO_KILL // 150 health.
health = 5 LASERS_TO_KILL
- icon_scale = 1.5
+ icon_scale_x = 1.5
+ icon_scale_y = 1.5
player_msg = "You are heavily armored."
// Note that armor effectively makes lasers do about 9 damage instead of 30,
// so it has an effective health of ~16.6 LASERS_TO_KILL if regular lasers are used.
diff --git a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm
index 2f9420bf64..39708365df 100644
--- a/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/mechanical/mecha/adv_dark_gygax.dm
@@ -77,7 +77,8 @@
catalogue_data = list(/datum/category_item/catalogue/technology/adv_dark_gygax)
icon_state = "darkgygax_adv"
wreckage = /obj/structure/loot_pile/mecha/gygax/dark/adv
- icon_scale = 1.5
+ icon_scale_x = 1.5
+ icon_scale_y = 1.5
movement_shake_radius = 14
maxHealth = 450
diff --git a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm
index 31dfa4d34a..8a56f08ddd 100644
--- a/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/occult/constructs/juggernaut.dm
@@ -106,7 +106,8 @@
friendly = list("pokes") //Anything nice the Behemoth would do would still Kill the Human. Leave it at poke.
attack_sound = 'sound/weapons/heavysmash.ogg'
resistance = 10
- icon_scale = 2
+ icon_scale_x = 2
+ icon_scale_y = 2
var/energy = 0
var/max_energy = 1000
armor = list(
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm
index 91aa0b0a79..2fc06815ca 100644
--- a/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/feral/feral.dm
@@ -1,94 +1,96 @@
-// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists.
-
-/mob/living/simple_mob/slime/feral
- name = "feral slime"
- desc = "The result of slimes escaping containment from some xenobiology lab. \
- Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \
- creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species."
- description_info = "Note that processing this large slime will give six cores."
-
- cores = 6 // Xenobio will love getting their hands on these.
-
- icon_state = "slime adult"
- icon_living = "slime adult"
- icon_dead = "slime adult dead"
- glow_range = 5
- glow_intensity = 4
- icon_scale = 2 // Twice as big as the xenobio variant.
- pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help.
- default_pixel_y = -10 // To prevent resetting above var.
-
- maxHealth = 300
- movement_cooldown = 10
- melee_attack_delay = 0.5 SECONDS
-
- ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank
-
-
-// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't).
-/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount)
- taunt(user, TRUE)
-
-
-// ***********
-// *Dark Blue*
-// ***********
-
-// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration.
-// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs.
-
-/mob/living/simple_mob/slime/feral/dark_blue
- name = "dark blue feral slime"
- color = "#2398FF"
- glow_toggle = TRUE
- slime_color = "dark blue"
- coretype = /obj/item/slime_extract/dark_blue
- cold_resist = 1 // Complete immunity.
- minbodytemp = 0
- cold_damage_per_tick = 0
-
- projectiletype = /obj/item/projectile/icicle
- base_attack_cooldown = 2 SECONDS
- ranged_attack_delay = 1 SECOND
-
- player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
\
- You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
\
- Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer."
-
-/obj/item/projectile/icicle
- name = "icicle"
- icon_state = "ice_2"
- damage = 40
- damage_type = BRUTE
- check_armour = "melee"
- armor_penetration = 30
- speed = 2
- icon_scale = 2 // It hits like a truck.
- sharp = TRUE
-
-/obj/item/projectile/icicle/on_impact(atom/A)
- playsound(get_turf(A), "shatter", 70, 1)
- return ..()
-
-/obj/item/projectile/icicle/get_structure_damage()
- return damage / 2 // They're really deadly against mobs, but less effective against solid things.
-
-/mob/living/simple_mob/slime/feral/dark_blue/handle_special()
- if(stat != DEAD)
- cold_aura()
- ..()
-
-/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura()
- for(var/mob/living/L in view(3, src))
- if(L == src)
- continue
- chill(L)
-
-/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L)
- L.inflict_cold_damage(10)
- if(L.get_cold_protection() < 1)
- L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src)
-
- if(L.has_AI()) // Other AIs should react to hostile auras.
- L.ai_holder.react_to_attack(src)
+// These slimes lack certain xenobio features but get more combat-oriented goodies. Generally these are more oriented towards Explorers than Xenobiologists.
+
+/mob/living/simple_mob/slime/feral
+ name = "feral slime"
+ desc = "The result of slimes escaping containment from some xenobiology lab. \
+ Having the means to successfully escape their lab, as well as having to survive on a harsh, cold world has made these \
+ creatures rival the ferocity of other apex predators in this region of Sif. It is considered to be a very invasive species."
+ description_info = "Note that processing this large slime will give six cores."
+
+ cores = 6 // Xenobio will love getting their hands on these.
+
+ icon_state = "slime adult"
+ icon_living = "slime adult"
+ icon_dead = "slime adult dead"
+ glow_range = 5
+ glow_intensity = 4
+ icon_scale_x = 2 // Twice as big as the xenobio variant.
+ icon_scale_y = 2
+ pixel_y = -10 // Since the base sprite isn't centered properly, the pixel auto-adjustment needs some help.
+ default_pixel_y = -10 // To prevent resetting above var.
+
+ maxHealth = 300
+ movement_cooldown = 10
+ melee_attack_delay = 0.5 SECONDS
+
+ ai_holder_type = /datum/ai_holder/simple_mob/ranged/pointblank
+
+
+// Slimebatoning/xenotasing it just makes it mad at you (which can be good if you're heavily armored and your friends aren't).
+/mob/living/simple_mob/slime/feral/slimebatoned(mob/living/user, amount)
+ taunt(user, TRUE)
+
+
+// ***********
+// *Dark Blue*
+// ***********
+
+// Dark Blue feral slimes can fire a strong icicle projectile every few seconds. The icicle hits hard and has some armor penetration.
+// They also have a similar aura as their xenobio counterparts, which inflicts cold damage. It also chills non-resistant mobs.
+
+/mob/living/simple_mob/slime/feral/dark_blue
+ name = "dark blue feral slime"
+ color = "#2398FF"
+ glow_toggle = TRUE
+ slime_color = "dark blue"
+ coretype = /obj/item/slime_extract/dark_blue
+ cold_resist = 1 // Complete immunity.
+ minbodytemp = 0
+ cold_damage_per_tick = 0
+
+ projectiletype = /obj/item/projectile/icicle
+ base_attack_cooldown = 2 SECONDS
+ ranged_attack_delay = 1 SECOND
+
+ player_msg = "You can fire an icicle projectile every two seconds. It hits hard, and armor has a hard time resisting it.
\
+ You are also immune to the cold, and you cause enemies around you to suffer periodic harm from the cold, if unprotected.
\
+ Unprotected enemies are also Chilled, making them slower and less evasive, and disabling effects last longer."
+
+/obj/item/projectile/icicle
+ name = "icicle"
+ icon_state = "ice_2"
+ damage = 40
+ damage_type = BRUTE
+ check_armour = "melee"
+ armor_penetration = 30
+ speed = 2
+ icon_scale_x = 2 // It hits like a truck.
+ icon_scale_y = 2
+ sharp = TRUE
+
+/obj/item/projectile/icicle/on_impact(atom/A)
+ playsound(get_turf(A), "shatter", 70, 1)
+ return ..()
+
+/obj/item/projectile/icicle/get_structure_damage()
+ return damage / 2 // They're really deadly against mobs, but less effective against solid things.
+
+/mob/living/simple_mob/slime/feral/dark_blue/handle_special()
+ if(stat != DEAD)
+ cold_aura()
+ ..()
+
+/mob/living/simple_mob/slime/feral/dark_blue/proc/cold_aura()
+ for(var/mob/living/L in view(3, src))
+ if(L == src)
+ continue
+ chill(L)
+
+/mob/living/simple_mob/slime/feral/dark_blue/proc/chill(mob/living/L)
+ L.inflict_cold_damage(10)
+ if(L.get_cold_protection() < 1)
+ L.add_modifier(/datum/modifier/chilled, 5 SECONDS, src)
+
+ if(L.has_AI()) // Other AIs should react to hostile auras.
+ L.ai_holder.react_to_attack(src)
diff --git a/code/modules/modular_computers/computers/subtypes/dev_console.dm b/code/modules/modular_computers/computers/subtypes/dev_console.dm
index 4a8981d41b..d7f977f33b 100644
--- a/code/modules/modular_computers/computers/subtypes/dev_console.dm
+++ b/code/modules/modular_computers/computers/subtypes/dev_console.dm
@@ -1,7 +1,7 @@
/obj/item/modular_computer/console
name = "console"
desc = "A stationary computer."
- icon = 'icons/obj/modular_console.dmi'
+ icon = 'icons/obj/modular_console_vr.dmi' //VOREStation Edit
icon_state = "console"
icon_state_unpowered = "console"
icon_state_screensaver = "standby"
diff --git a/code/modules/modular_computers/computers/subtypes/dev_laptop.dm b/code/modules/modular_computers/computers/subtypes/dev_laptop.dm
index 851008b23d..3a9cab6bc2 100644
--- a/code/modules/modular_computers/computers/subtypes/dev_laptop.dm
+++ b/code/modules/modular_computers/computers/subtypes/dev_laptop.dm
@@ -28,8 +28,6 @@
//VOREStation Addition Begin
var/supported = FALSE
for(var/obj/structure/table/S in loc)
- if(istype(S, /obj/structure/table/bench) || istype(S, /obj/structure/table/rack))
- continue
supported = TRUE
if(!supported)
to_chat(usr, "You will need a better supporting surface before opening \the [src]!")
diff --git a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm
index 3152878fc2..905df24590 100644
--- a/code/modules/modular_computers/computers/subtypes/preset_laptop.dm
+++ b/code/modules/modular_computers/computers/subtypes/preset_laptop.dm
@@ -31,6 +31,11 @@
battery_module = new/obj/item/weapon/computer_hardware/battery_module/advanced(src)
battery_module.charge_to_full()
+/obj/item/modular_computer/laptop/preset/custom_loadout/elite
+ icon_state_unpowered = "adv-laptop-open"
+ icon_state = "adv-laptop-open"
+ icon_state_closed = "adv-laptop-closed"
+
/obj/item/modular_computer/laptop/preset/custom_loadout/elite/install_default_hardware()
..()
processor_unit = new/obj/item/weapon/computer_hardware/processor_unit(src)
@@ -41,3 +46,23 @@
card_slot = new/obj/item/weapon/computer_hardware/card_slot(src)
battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(src)
battery_module.charge_to_full()
+
+//VOREStation Add Start
+/obj/item/modular_computer/laptop/preset/custom_loadout/rugged
+ name = "rugged laptop computer"
+ desc = "A rugged portable computer."
+ icon = 'icons/obj/modular_laptop_vr.dmi'
+ max_damage = 300
+ broken_damage = 200
+
+/obj/item/modular_computer/laptop/preset/custom_loadout/rugged/install_default_hardware()
+ ..()
+ processor_unit = new/obj/item/weapon/computer_hardware/processor_unit/small(src)
+ tesla_link = new/obj/item/weapon/computer_hardware/tesla_link(src)
+ hard_drive = new/obj/item/weapon/computer_hardware/hard_drive/advanced(src)
+ network_card = new/obj/item/weapon/computer_hardware/network_card/advanced(src)
+ nano_printer = new/obj/item/weapon/computer_hardware/nano_printer(src)
+ card_slot = new/obj/item/weapon/computer_hardware/card_slot(src)
+ battery_module = new/obj/item/weapon/computer_hardware/battery_module/super(src)
+ battery_module.charge_to_full()
+//VOREStation Add End
\ No newline at end of file
diff --git a/code/modules/projectiles/projectile/arc.dm b/code/modules/projectiles/projectile/arc.dm
index 97069db1fb..0c4c9f4caa 100644
--- a/code/modules/projectiles/projectile/arc.dm
+++ b/code/modules/projectiles/projectile/arc.dm
@@ -162,7 +162,8 @@
/obj/item/projectile/arc/radioactive
name = "radiation blast"
icon_state = "green_pellet"
- icon_scale = 2
+ icon_scale_x = 2
+ icon_scale_y = 2
var/rad_power = 50
/obj/item/projectile/arc/radioactive/on_impact(turf/T)
diff --git a/code/modules/research/designs/precursor.dm b/code/modules/research/designs/precursor.dm
index 88575e5667..eaf7b3a923 100644
--- a/code/modules/research/designs/precursor.dm
+++ b/code/modules/research/designs/precursor.dm
@@ -58,6 +58,15 @@
build_path = /obj/item/weapon/weldingtool/experimental/hybrid
sort_string = "PATCW"
+/datum/design/item/precursor/janusmodule
+ name = "Blackbox Circuit Datamass"
+ desc = "A design that seems to be in a constantly shifting superposition."
+ id = "janus_module"
+ materials = list(MAT_DURASTEEL = 3000, MAT_MORPHIUM = 2000, MAT_METALHYDROGEN = 6000, MAT_URANIUM = 6000, MAT_VERDANTIUM = 1500)
+ req_tech = list(TECH_MATERIAL = 7, TECH_BLUESPACE = 5, TECH_MAGNET = 6, TECH_PHORON = 3, TECH_ARCANE = 1, TECH_PRECURSOR = 2)
+ build_path = /obj/random/janusmodule
+ sort_string = "PAJAA"
+
/datum/design/item/anomaly/AssembleDesignName()
..()
name = "Anomalous prototype ([item_name])"
@@ -67,4 +76,4 @@
if(build_path)
var/obj/item/I = build_path
desc = initial(I.desc)
- ..()
\ No newline at end of file
+ ..()
diff --git a/html/changelogs/Mechoid - Borers.yml b/html/changelogs/Mechoid - Borers.yml
new file mode 100644
index 0000000000..f92c619d86
--- /dev/null
+++ b/html/changelogs/Mechoid - Borers.yml
@@ -0,0 +1,37 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Mechoid
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - tweak: "Borers can speak through nearby mobs if they have a sufficient build-up of chemicals."
+ - tweak: "Borers have a max chemical volume."
diff --git a/html/changelogs/Novacat - Fucktimers.yml b/html/changelogs/Novacat - Fucktimers.yml
new file mode 100644
index 0000000000..4be0cb5dfc
--- /dev/null
+++ b/html/changelogs/Novacat - Fucktimers.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: Novacat
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - bugfix: "Attempts to fix the timer SS to be moore robust"
diff --git a/html/changelogs/mistyLuminescence - makeover.yml b/html/changelogs/mistyLuminescence - makeover.yml
new file mode 100644
index 0000000000..e55f71a2ec
--- /dev/null
+++ b/html/changelogs/mistyLuminescence - makeover.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: mistyLuminescence
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - rscadd: "Adds the makeover kit to the traitor uplink for 5 TC, also available in the Infiltrator bundle null crate in Cargo. Makeover kits can be used to change the user's hair style and color, and eye color."
diff --git a/html/changelogs/mistyLuminescence - randomevents.yml b/html/changelogs/mistyLuminescence - randomevents.yml
new file mode 100644
index 0000000000..e5659f8a4e
--- /dev/null
+++ b/html/changelogs/mistyLuminescence - randomevents.yml
@@ -0,0 +1,36 @@
+################################
+# Example Changelog File
+#
+# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
+#
+# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
+# When it is, any changes listed below will disappear.
+#
+# Valid Prefixes:
+# bugfix
+# wip (For works in progress)
+# tweak
+# soundadd
+# sounddel
+# rscadd (general adding of nice things)
+# rscdel (general deleting of nice things)
+# imageadd
+# imagedel
+# maptweak
+# spellcheck (typo fixes)
+# experiment
+#################################
+
+# Your name.
+author: mistyLuminescence
+
+# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
+delete-after: True
+
+# Any changes you've made. See valid prefix list above.
+# INDENT WITH TWO SPACES. NOT TABS. SPACES.
+# SCREW THIS UP AND IT WON'T WORK.
+# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
+# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
+changes:
+ - bugfix: Random event timers now wait until the round starts to begin counting down.
\ No newline at end of file
diff --git a/icons/mob/human_races/cyberlimbs/morpheus/morpheus_alt2.dmi b/icons/mob/human_races/cyberlimbs/morpheus/morpheus_alt2.dmi
index 6bee009356..26f2ccb4e7 100644
Binary files a/icons/mob/human_races/cyberlimbs/morpheus/morpheus_alt2.dmi and b/icons/mob/human_races/cyberlimbs/morpheus/morpheus_alt2.dmi differ
diff --git a/icons/mob/radial.dmi b/icons/mob/radial.dmi
new file mode 100644
index 0000000000..cfdd0e549a
Binary files /dev/null and b/icons/mob/radial.dmi differ
diff --git a/icons/obj/flora/deadtrees.dmi b/icons/obj/flora/deadtrees.dmi
index 7a0b164619..2a3655b0d2 100644
Binary files a/icons/obj/flora/deadtrees.dmi and b/icons/obj/flora/deadtrees.dmi differ
diff --git a/icons/obj/items.dmi b/icons/obj/items.dmi
index d2d986c205..e3de287a99 100644
Binary files a/icons/obj/items.dmi and b/icons/obj/items.dmi differ
diff --git a/icons/obj/modular_console_vr.dmi b/icons/obj/modular_console_vr.dmi
new file mode 100644
index 0000000000..125711410d
Binary files /dev/null and b/icons/obj/modular_console_vr.dmi differ
diff --git a/icons/obj/modular_laptop.dmi b/icons/obj/modular_laptop.dmi
index af1883055e..8f060dd500 100644
Binary files a/icons/obj/modular_laptop.dmi and b/icons/obj/modular_laptop.dmi differ
diff --git a/icons/obj/modular_laptop_vr.dmi b/icons/obj/modular_laptop_vr.dmi
new file mode 100644
index 0000000000..6dc2af2d01
Binary files /dev/null and b/icons/obj/modular_laptop_vr.dmi differ
diff --git a/maps/submaps/shelters/shelter_3.dmm b/maps/submaps/shelters/shelter_3.dmm
index 42321a3d26..c288aa9702 100644
--- a/maps/submaps/shelters/shelter_3.dmm
+++ b/maps/submaps/shelters/shelter_3.dmm
@@ -52,7 +52,7 @@
/obj/item/weapon/storage/pill_bottle/antitox,
/obj/item/weapon/storage/firstaid/adv,
/obj/item/weapon/storage/firstaid/regular,
-/obj/item/modular_computer/laptop/preset/custom_loadout/elite,
+/obj/item/modular_computer/laptop/preset/custom_loadout/rugged,
/obj/item/weapon/storage/box/survival/comp{
starts_with = list(/obj/item/weapon/tool/prybar/red,/obj/item/clothing/glasses/goggles,/obj/item/weapon/reagent_containers/hypospray/autoinjector,/obj/item/stack/medical/bruise_pack,/obj/item/device/flashlight/glowstick,/obj/item/weapon/reagent_containers/food/snacks/candy/proteinbar,/obj/item/clothing/mask/breath,/obj/item/weapon/tank/emergency/oxygen/engi)
},
diff --git a/maps/tether/tether-06-station2.dmm b/maps/tether/tether-06-station2.dmm
index 8e5d0ca44e..fa40d5e3b7 100644
--- a/maps/tether/tether-06-station2.dmm
+++ b/maps/tether/tether-06-station2.dmm
@@ -3958,6 +3958,9 @@
/obj/item/device/radio/intercom/department/medbay{
pixel_y = -24
},
+/obj/effect/landmark/start{
+ name = "Paramedic"
+ },
/turf/simulated/floor/tiled/dark,
/area/medical/medbay_emt_bay)
"fP" = (
@@ -18063,6 +18066,9 @@
/obj/effect/floor_decal/corner/paleblue/border{
dir = 6
},
+/obj/effect/landmark/start{
+ name = "Paramedic"
+ },
/turf/simulated/floor/tiled/dark,
/area/medical/medbay_emt_bay)
"Bx" = (
@@ -18278,7 +18284,7 @@
dir = 8
},
/obj/effect/landmark/start{
- name = "Pathfinder"
+ name = "Paramedic"
},
/turf/simulated/floor/tiled/dark,
/area/medical/medbay_emt_bay)
@@ -18460,7 +18466,7 @@
dir = 4
},
/obj/effect/landmark/start{
- name = "Pathfinder"
+ name = "Paramedic"
},
/turf/simulated/floor/tiled/dark,
/area/medical/medbay_emt_bay)
diff --git a/maps/tether/tether-10-colony.dmm b/maps/tether/tether-10-colony.dmm
index b0704731b3..9ced842fdb 100644
--- a/maps/tether/tether-10-colony.dmm
+++ b/maps/tether/tether-10-colony.dmm
@@ -778,21 +778,11 @@
})
"bj" = (
/obj/structure/table/rack,
-/obj/item/clothing/gloves/swat{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 80, "energy" = 80, "bomb" = 80, "bio" = 80, "rad" = 80)
- },
-/obj/item/clothing/gloves/swat{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 80, "energy" = 80, "bomb" = 80, "bio" = 80, "rad" = 80)
- },
-/obj/item/clothing/gloves/swat{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 80, "energy" = 80, "bomb" = 80, "bio" = 80, "rad" = 80)
- },
-/obj/item/clothing/gloves/swat{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 80, "energy" = 80, "bomb" = 80, "bio" = 80, "rad" = 80)
- },
-/obj/item/clothing/gloves/swat{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 80, "energy" = 80, "bomb" = 80, "bio" = 80, "rad" = 80)
- },
+/obj/item/clothing/gloves/swat,
+/obj/item/clothing/gloves/swat,
+/obj/item/clothing/gloves/swat,
+/obj/item/clothing/gloves/swat,
+/obj/item/clothing/gloves/swat,
/obj/item/clothing/shoes/boots/swat,
/obj/item/clothing/shoes/boots/swat,
/obj/item/clothing/shoes/boots/swat,
@@ -806,47 +796,36 @@
/obj/item/clothing/suit/armor/swat,
/obj/item/clothing/suit/armor/swat,
/obj/item/clothing/mask/gas/commando{
- armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 75, "rad" = 0);
name = "Commando Mask"
},
/obj/item/clothing/mask/gas/commando{
- armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 75, "rad" = 0);
name = "Commando Mask"
},
/obj/item/clothing/mask/gas/commando{
- armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 75, "rad" = 0);
name = "Commando Mask"
},
/obj/item/clothing/mask/gas/commando{
- armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 75, "rad" = 0);
name = "Commando Mask"
},
/obj/item/clothing/mask/gas/commando{
- armor = list("melee" = 50, "bullet" = 50, "laser" = 50, "energy" = 50, "bomb" = 50, "bio" = 75, "rad" = 0);
name = "Commando Mask"
},
/obj/item/clothing/head/helmet/space/deathsquad{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 60, "energy" = 70, "bomb" = 50, "bio" = 100, "rad" = 60);
name = "swat helmet"
},
/obj/item/clothing/head/helmet/space/deathsquad{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 60, "energy" = 70, "bomb" = 50, "bio" = 100, "rad" = 60);
name = "swat helmet"
},
/obj/item/clothing/head/helmet/space/deathsquad{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 60, "energy" = 70, "bomb" = 50, "bio" = 100, "rad" = 60);
name = "swat helmet"
},
/obj/item/clothing/head/helmet/space/deathsquad{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 60, "energy" = 70, "bomb" = 50, "bio" = 100, "rad" = 60);
name = "swat helmet"
},
/obj/item/clothing/head/helmet/space/deathsquad{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 60, "energy" = 70, "bomb" = 50, "bio" = 100, "rad" = 60);
name = "swat helmet"
},
/obj/item/clothing/head/helmet/space/deathsquad{
- armor = list("melee" = 80, "bullet" = 80, "laser" = 60, "energy" = 70, "bomb" = 50, "bio" = 100, "rad" = 60);
name = "swat helmet"
},
/turf/unsimulated/floor{
@@ -10902,7 +10881,6 @@
"tO" = (
/obj/structure/table/steel,
/obj/item/clothing/shoes/boots/jackboots{
- armor = list("melee" = 69, "bullet" = 69, "laser" = 69, "energy" = 69, "bomb" = 69, "bio" = 69, "rad" = 69);
desc = "This pair of Jackboots look worn and freshly used. They have several claw markings inside and you can read the initials D and M at the bottom";
name = "Dhaeleena's Jackboots"
},
diff --git a/sound/effects/deskbell.ogg b/sound/effects/deskbell.ogg
new file mode 100644
index 0000000000..ef40794cf4
Binary files /dev/null and b/sound/effects/deskbell.ogg differ
diff --git a/sound/effects/deskbell_rude.ogg b/sound/effects/deskbell_rude.ogg
new file mode 100644
index 0000000000..1698a15fb7
Binary files /dev/null and b/sound/effects/deskbell_rude.ogg differ
diff --git a/vorestation.dme b/vorestation.dme
index 63c326d73f..96a687412d 100644
--- a/vorestation.dme
+++ b/vorestation.dme
@@ -146,6 +146,8 @@
#include "code\_onclick\hud\human.dm"
#include "code\_onclick\hud\movable_screen_objects.dm"
#include "code\_onclick\hud\other_mobs.dm"
+#include "code\_onclick\hud\radial.dm"
+#include "code\_onclick\hud\radial_persistent.dm"
#include "code\_onclick\hud\robot.dm"
#include "code\_onclick\hud\robot_vr.dm"
#include "code\_onclick\hud\screen_objects.dm"
@@ -947,6 +949,7 @@
#include "code\game\objects\effects\temporary_visuals\projectiles\tracer.dm"
#include "code\game\objects\items\antag_spawners.dm"
#include "code\game\objects\items\apc_frame.dm"
+#include "code\game\objects\items\bells.dm"
#include "code\game\objects\items\blueprints.dm"
#include "code\game\objects\items\bodybag.dm"
#include "code\game\objects\items\contraband.dm"
@@ -2392,8 +2395,8 @@
#include "code\modules\mob\living\simple_mob\subtypes\animal\pets\fox_vr.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\pets\parrot.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\diyaab.dm"
-#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\fluffy_vr.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\duck.dm"
+#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\fluffy_vr.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\frostfly.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\glitterfly.dm"
#include "code\modules\mob\living\simple_mob\subtypes\animal\sif\hooligan_crab.dm"