diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index fb33f2fafb..38a16bdba9 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -53,7 +53,8 @@ Mostly for chomp exclusive stuff, otherwise if you need to modify a base file fo
* For multi-line removals: Use a block comment (/\* xxx \*/) to comment out the existing code block (do not modify whitespace more than necessary) and at the start, it should contain /\* CHOMP Removal - "Reason"
* If it is something like a bugfix that Polaris or Vorestation would want (the codebase we use), you may want to consider coding it there as well. They may want any general gameplay bugfixes, and things that are obviously intended to work one way, but do not. They do not have any of our fluff species (vulp, akula, fenn, etc) so do not make PRs related to that, or any vore content to them.
* Change whitespace as little as possible. Do not randomly add/remove whitespace.
-* Any new files should have "_ch" at the end. For example, "life_ch.dm". Just make them in the same location as the file they are related to.
+* Any new files should preferrably go into the modular_chomp folder following the file structure of where it would be placed normally. The old method was to have "_ch" at the end. For example, "life_ch.dm".
+* Do not make changes to base icon files. New icon files should go into modular_chomp and code should be changed to point to the new file.
* Map changes must be in tgm format. See the [Mapmerge2 Readme] for details, or use [StrongDMM] which can automatically save maps as tgm.
The `attempt_ch()` proc has been added for your convienence. It allows a many-line change to become a single-line change in the existing Polaris files, preserving mergeability and allowing better code separation while preventing your new code from causing runtimes that stop the original code from running. If you are wanting to inject new procedures into an existing proc, called `update_atoms()` for example, you would create `update_atoms_ch()` in a nearby `_ch.dm` file, and then call to it from a single line in the original `update_atoms()` with `attempt_ch()`.
diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm
index 1ae4237a0d..06fb819b07 100644
--- a/code/__defines/dcs/signals.dm
+++ b/code/__defines/dcs/signals.dm
@@ -400,10 +400,6 @@
///called when removing a given item from a mob, from mob/living/carbon/remove_embedded_object(mob/living/carbon/target, /obj/item)
#define COMSIG_CARBON_EMBED_REMOVAL "item_embed_remove_safe"
-// /mob/living/simple_animal/hostile signals
-#define COMSIG_HOSTILE_ATTACKINGTARGET "hostile_attackingtarget"
- #define COMPONENT_HOSTILE_NO_ATTACK (1<<0)
-
// /obj signals
///from base of obj/deconstruct(): (disassembled)
diff --git a/code/controllers/subsystems/chat.dm b/code/controllers/subsystems/chat.dm
index aece61d39e..92a1be2757 100644
--- a/code/controllers/subsystems/chat.dm
+++ b/code/controllers/subsystems/chat.dm
@@ -52,7 +52,7 @@ SUBSYSTEM_DEF(chat)
for(var/I in target)
var/client/C = CLIENT_FROM_VAR(I) //Grab us a client if possible
- if(!C)
+ if(!C || !C.chatOutput)
continue // No client? No care.
else if(C.chatOutput.broken)
DIRECT_OUTPUT(C, original_message)
@@ -65,7 +65,7 @@ SUBSYSTEM_DEF(chat)
else
var/client/C = CLIENT_FROM_VAR(target) //Grab us a client if possible
- if(!C)
+ if(!C || !C.chatOutput)
return // No client? No care.
else if(C.chatOutput.broken)
DIRECT_OUTPUT(C, original_message)
diff --git a/code/datums/supplypacks/science_vr.dm b/code/datums/supplypacks/science_vr.dm
index 1d9922e7d8..2e9f756aa2 100644
--- a/code/datums/supplypacks/science_vr.dm
+++ b/code/datums/supplypacks/science_vr.dm
@@ -29,7 +29,7 @@
containertype = /obj/structure/largecrate/animal/weretiger
containername = "Weretiger crate"
access = access_xenobiology
-/*
+
/datum/supply_pack/sci/otie
name = "VARMAcorp adoptable reject (Dangerous!)"
cost = 100
@@ -43,4 +43,3 @@
containertype = /obj/structure/largecrate/animal/otie/phoron
containername = "VARMAcorp adaptive beta subject (Experimental)"
access = access_xenobiology
-*/ //VORESTATION AI TEMPORARY REMOVAL. Oties commented out cuz broke.
diff --git a/code/datums/supplypacks/security_vr.dm b/code/datums/supplypacks/security_vr.dm
index a00556e528..bbb28e08fb 100644
--- a/code/datums/supplypacks/security_vr.dm
+++ b/code/datums/supplypacks/security_vr.dm
@@ -1,4 +1,4 @@
-/*/datum/supply_pack/security/guardbeast //VORESTATION AI TEMPORARY REMOVAL
+/datum/supply_pack/security/guardbeast
name = "VARMAcorp autoNOMous security solution"
cost = 150
containertype = /obj/structure/largecrate/animal/guardbeast
@@ -17,7 +17,6 @@
access_security,
access_xenobiology)
one_access = TRUE
-*/
/datum/supply_pack/randomised/security/armor
access = access_armory
diff --git a/code/game/area/areas.dm b/code/game/area/areas.dm
index 31d1d07f2b..e8f9e2fe13 100644
--- a/code/game/area/areas.dm
+++ b/code/game/area/areas.dm
@@ -465,7 +465,7 @@ var/list/mob/living/forced_ambiance_list = new
/area/proc/prison_break(break_lights = TRUE, open_doors = TRUE, open_blast_doors = FALSE) //CHOMP Edit set blast doors to FALSE
var/obj/machinery/power/apc/theAPC = get_apc()
- if(theAPC.operating)
+ if(theAPC && theAPC.operating)
if(break_lights)
for(var/obj/machinery/power/apc/temp_apc in src)
temp_apc.overload_lighting(70)
diff --git a/code/game/dna/dna2_helpers.dm b/code/game/dna/dna2_helpers.dm
index 931b298091..d5cdc700c7 100644
--- a/code/game/dna/dna2_helpers.dm
+++ b/code/game/dna/dna2_helpers.dm
@@ -22,7 +22,7 @@
// Give Random Bad Mutation to M
/proc/randmutb(var/mob/living/M)
- if(!M) return
+ if(!M || !(M.dna)) return
M.dna.check_integrity()
//var/block = pick(GLASSESBLOCK,COUGHBLOCK,FAKEBLOCK,NERVOUSBLOCK,CLUMSYBLOCK,TWITCHBLOCK,HEADACHEBLOCK,BLINDBLOCK,DEAFBLOCK,HALLUCINATIONBLOCK) // Most of these are disabled anyway.
var/block = pick(FAKEBLOCK,CLUMSYBLOCK,BLINDBLOCK,DEAFBLOCK)
@@ -30,7 +30,7 @@
// Give Random Good Mutation to M
/proc/randmutg(var/mob/living/M)
- if(!M) return
+ if(!M || !(M.dna)) return
M.dna.check_integrity()
//var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,NOBREATHBLOCK,REMOTEVIEWBLOCK,REGENERATEBLOCK,INCREASERUNBLOCK,REMOTETALKBLOCK,MORPHBLOCK,BLENDBLOCK,NOPRINTSBLOCK,SHOCKIMMUNITYBLOCK,SMALLSIZEBLOCK) // Much like above, most of these blocks are disabled in code.
var/block = pick(HULKBLOCK,XRAYBLOCK,FIREBLOCK,TELEBLOCK,REGENERATEBLOCK,REMOTETALKBLOCK)
@@ -38,13 +38,13 @@
// Random Appearance Mutation
/proc/randmuti(var/mob/living/M)
- if(!M) return
+ if(!M || !(M.dna)) return
M.dna.check_integrity()
M.dna.SetUIValue(rand(1,DNA_UI_LENGTH),rand(1,4095))
// Scramble UI or SE.
/proc/scramble(var/UI, var/mob/M, var/prob)
- if(!M) return
+ if(!M || !(M.dna)) return
M.dna.check_integrity()
if(UI)
for(var/i = 1, i <= DNA_UI_LENGTH-1, i++)
diff --git a/code/game/jobs/job/captain_vr.dm b/code/game/jobs/job/captain_vr.dm
index 15a5342deb..62b0eb93ef 100644
--- a/code/game/jobs/job/captain_vr.dm
+++ b/code/game/jobs/job/captain_vr.dm
@@ -32,13 +32,13 @@
access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue,
access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer,
access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station,
- access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway)
+ access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway, access_entertainment)
minimal_access = list(access_security, access_sec_doors, access_brig, access_forensics_lockers,
access_medical, access_engine, access_change_ids, access_ai_upload, access_eva, access_heads,
access_all_personal_lockers, access_maint_tunnels, access_bar, access_janitor, access_construction, access_morgue,
access_crematorium, access_kitchen, access_cargo, access_cargo_bot, access_mailsorting, access_qm, access_hydroponics, access_lawyer,
access_chapel_office, access_library, access_research, access_mining, access_heads_vault, access_mining_station,
- access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway)
+ access_hop, access_RC_announce, access_clown, access_tomfoolery, access_mime, access_keycard_auth, access_gateway, access_entertainment)
/datum/alt_title/deputy_director
title = "Deputy Director"
diff --git a/code/game/jobs/job_controller.dm b/code/game/jobs/job_controller.dm
index 95784c156b..63f8a702e0 100644
--- a/code/game/jobs/job_controller.dm
+++ b/code/game/jobs/job_controller.dm
@@ -393,7 +393,7 @@ var/global/datum/controller/occupations/job_master
//Equip custom gear loadout.
var/list/custom_equip_slots = list()
var/list/custom_equip_leftovers = list()
- if(H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON))
+ if(H.client && H.client.prefs && H.client.prefs.gear && H.client.prefs.gear.len && !(job.mob_type & JOB_SILICON))
for(var/thing in H.client.prefs.gear)
var/datum/gear/G = gear_datums[thing]
if(!G) //Not a real gear datum (maybe removed, as this is loaded from their savefile)
diff --git a/code/game/machinery/Sleeper.dm b/code/game/machinery/Sleeper.dm
index b785a42639..18765fcf9b 100644
--- a/code/game/machinery/Sleeper.dm
+++ b/code/game/machinery/Sleeper.dm
@@ -423,6 +423,8 @@
/obj/machinery/sleeper/relaymove(var/mob/user)
..()
+ if(user.incapacitated())
+ return
go_out()
/obj/machinery/sleeper/emp_act(var/severity)
diff --git a/code/game/machinery/deployable_vr.dm b/code/game/machinery/deployable_vr.dm
index bbb835b763..c939fea3f4 100644
--- a/code/game/machinery/deployable_vr.dm
+++ b/code/game/machinery/deployable_vr.dm
@@ -72,7 +72,7 @@
/obj/structure/barricade/cutout/attackby(var/obj/I, var/mob/user)
if(is_type_in_list(I, painters))
var/choice = tgui_input_list(user, "What would you like to paint the cutout as?", "Cutout Painting", cutout_types)
- if(!choice || !Adjacent(user, src) || I != user.get_active_hand())
+ if(!choice || !Adjacent(user) || I != user.get_active_hand())
return TRUE
if(do_after(user, 10 SECONDS, src))
var/picked_type = cutout_types[choice]
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index d2adbad8da..703d174bee 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -100,7 +100,7 @@
// Also recharge their internal battery.
if(H.isSynthetic() && H.nutrition < 500) //VOREStation Edit
- H.nutrition = min(H.nutrition+10, 500) //VOREStation Edit
+ H.nutrition = min(H.nutrition+(10*(1-H.species.synthetic_food_coeff)), 500) //VOREStation Edit
cell.use(7000/450*10)
// And clear up radiation
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index 505ff94947..dc0a70f711 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -92,8 +92,8 @@
return
-/obj/machinery/teleport/station/attack_ai()
- attack_hand()
+/obj/machinery/teleport/station/attack_ai(mob/user)
+ attack_hand(user)
/obj/machinery/computer/teleporter/attack_ai(mob/user)
teleport_control.tgui_interact(user)
diff --git a/code/game/machinery/virtual_reality/vr_console.dm b/code/game/machinery/virtual_reality/vr_console.dm
index 9d165cc7e3..81df6d934b 100644
--- a/code/game/machinery/virtual_reality/vr_console.dm
+++ b/code/game/machinery/virtual_reality/vr_console.dm
@@ -2,9 +2,9 @@
name = "virtual reality sleeper"
desc = "A fancy bed with built-in sensory I/O ports and connectors to interface users' minds with their bodies in virtual reality."
icon = 'icons/obj/Cryogenic2.dmi'
- icon_state = "syndipod_0"
+ icon_state = "body_scanner_0"
- var/base_state = "syndipod_"
+ var/base_state = "body_scanner_"
density = TRUE
anchored = TRUE
@@ -88,9 +88,9 @@
-/obj/machinery/sleeper/relaymove(var/mob/user)
+/obj/machinery/vr_sleeper/relaymove(var/mob/user)
..()
- if(usr.incapacitated())
+ if(user.incapacitated())
return
go_out()
@@ -245,9 +245,14 @@
if(occupant.species.name != "Promethean" && occupant.species.name != "Human" && mirror_first_occupant)
avatar.shapeshifter_change_shape(occupant.species.name)
avatar.forceMove(get_turf(S)) // Put the mob on the landmark, instead of inside it
- avatar.Sleeping(1)
+
occupant.enter_vr(avatar)
+ //Yes, I am using a aheal just so your markings transfer over, I could not get .prefs.copy_to working. This is very stupid, and I can't be assed to rewrite this. Too bad!
+ avatar.revive()
+ avatar.revive()
+ avatar.verbs += /mob/living/carbon/human/proc/exit_vr //ahealing removes the prommie verbs and the VR verbs, giving it back
+ avatar.Sleeping(1)
// Prompt for username after they've enterred the body.
var/newname = sanitize(tgui_input_text(avatar, "You are entering virtual reality. Your username is currently [src.name]. Would you like to change it to something else?", "Name change", null, MAX_NAME_LEN), MAX_NAME_LEN)
diff --git a/code/game/objects/banners.dm b/code/game/objects/banners.dm
index 0e4aaef6d8..d491e41119 100644
--- a/code/game/objects/banners.dm
+++ b/code/game/objects/banners.dm
@@ -31,6 +31,12 @@
desc = "A banner with the symbol of the Solar Confederate Government."
catalogue_data = list(/datum/category_item/catalogue/information/organization/solgov)
+/obj/item/weapon/banner/altevian
+ name = "\improper Altevian Hegemony Banner"
+ icon_state = "banner-altevian"
+ desc = "A banner that flies for the pride of the hegemony."
+ //catalogue_data = list(/datum/category_item/catalogue/information/organization/altevian_hegemony) // TODO?
+
//VOREStation Removal //CHOMP re-addition. Seriously? You commented this out for your lore? What's wrong with JUST NOT SPAWNING IT or something.
/obj/item/weapon/banner/virgov
name = "\improper VirGov banner"
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 2ed58f88ee..645168b041 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -106,7 +106,7 @@
var/drop_sound = "generic_drop"
var/tip_timer // reference to timer id for a tooltip we might open soon
-
+
var/no_random_knockdown = FALSE //stops item from being able to randomly knock people down in combat
/obj/item/Initialize(mapload) //CHOMPedit I stg I'm going to overwrite these many uncommented edits.
@@ -222,9 +222,12 @@
/obj/item/attack_hand(mob/living/user as mob)
if (!user) return
- if(anchored)
- to_chat(user, span("notice", "\The [src] won't budge, you can't pick it up!"))
- return
+ if(anchored) // Start CHOMPStation Edit
+ if(hascall(src, "attack_self"))
+ return src.attack_self(user)
+ else
+ to_chat ("This is anchored and you can't lift it.")
+ return // End CHOMPStation Edit
if (hasorgans(user))
var/mob/living/carbon/human/H = user
var/obj/item/organ/external/temp = H.organs_by_name["r_hand"]
diff --git a/code/game/objects/items/devices/defib.dm b/code/game/objects/items/devices/defib.dm
index f672cbcf1b..75fddde40e 100644
--- a/code/game/objects/items/devices/defib.dm
+++ b/code/game/objects/items/devices/defib.dm
@@ -308,8 +308,8 @@
return bad_vital_organ
//this needs to be last since if any of the 'other conditions are met their messages take precedence
- if(!H.client && !H.teleop)
- return "buzzes, \"Resuscitation failed - Mental interface error. Further attempts may be successful.\""
+ //if(!H.client && !H.teleop)
+ // return "buzzes, \"Resuscitation failed - Mental interface error. Further attempts may be successful.\""// CHOMPEdit, removing this check to allow revival through bad internet connections.
return null
diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm
index 6ae95bcf8a..54cc46d802 100644
--- a/code/game/objects/items/devices/lightreplacer.dm
+++ b/code/game/objects/items/devices/lightreplacer.dm
@@ -53,6 +53,7 @@
var/emagged = 0
var/failmsg = ""
var/charge = 0
+ var/selected_color = LIGHT_COLOR_INCANDESCENT_TUBE //Default color!
// Eating used bulbs gives us bulb shards
var/bulb_shards = 0
@@ -141,6 +142,10 @@
return
*/
to_chat(usr, "It has [uses] lights remaining.")
+ var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null
+ if(new_color)
+ selected_color = new_color
+ to_chat(usr, "The light color has been changed.")
/obj/item/device/lightreplacer/update_icon()
icon_state = "lightreplacer[emagged]"
@@ -184,19 +189,13 @@
to_chat(U, "\The [src] has fabricated a new bulb from the broken bulbs it has stored. It now has [uses] uses.")
playsound(src, 'sound/machines/ding.ogg', 50, 1)
target.status = LIGHT_EMPTY
+ target.installed_light = null //Remove the light!
target.update()
var/obj/item/weapon/light/L2 = new target.light_type()
-
- target.status = L2.status
- target.switchcount = L2.switchcount
- target.rigged = emagged
- target.brightness_range = L2.brightness_range
- target.brightness_power = L2.brightness_power
- target.brightness_color = L2.brightness_color
- target.on = target.has_power()
+ L2.brightness_color = selected_color
+ target.insert_bulb(L2) //Call the insertion proc.
target.update()
- qdel(L2)
if(target.on && target.rigged)
target.explode()
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index 9676919792..1cb0b442d9 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -20,7 +20,7 @@
to_chat(user, "\The [src] cannot be applied to [M]!")
return 1
- if (!M.IsAdvancedToolUser())
+ if (!user.IsAdvancedToolUser())
to_chat(user, "You don't have the dexterity to do this!")
return 1
diff --git a/code/game/objects/items/toys/toys_vr.dm b/code/game/objects/items/toys/toys_vr.dm
index 386d73aaa9..d114b6e4ab 100644
--- a/code/game/objects/items/toys/toys_vr.dm
+++ b/code/game/objects/items/toys/toys_vr.dm
@@ -1084,4 +1084,27 @@
T = get_turf(src)
new /obj/effect/decal/cleanable/confetti(T)
else
- to_chat(user, "The [src] is already spent!")
\ No newline at end of file
+ to_chat(user, "The [src] is already spent!")
+
+/*
+ * Snow Globes
+ */
+/obj/item/weapon/toy/snowglobe
+ name = "snowglobe"
+ icon = 'icons/obj/snowglobe_vr.dmi'
+
+/obj/item/weapon/toy/snowglobe/snowvillage
+ desc = "Depicts a small, quaint village buried in snow."
+ icon_state = "smolsnowvillage"
+
+/obj/item/weapon/toy/snowglobe/tether
+ desc = "Depicts a massive space elevator reaching to the sky."
+ icon_state = "smoltether"
+
+/obj/item/weapon/toy/snowglobe/stellardelight
+ desc = "Depicts an interstellar spacecraft."
+ icon_state = "smolstellardelight"
+
+/obj/item/weapon/toy/snowglobe/rascalspass
+ desc = "Depicts a nanotrasen facility on a temperate world."
+ icon_state = "smolrascalspass"
diff --git a/code/game/objects/items/trash_vr.dm b/code/game/objects/items/trash_vr.dm
index bb18141f4f..1f6cf5d3a8 100644
--- a/code/game/objects/items/trash_vr.dm
+++ b/code/game/objects/items/trash_vr.dm
@@ -44,4 +44,19 @@
/obj/item/trash/ratjuice
name = "\improper Space-Safe Meal package"
icon = 'icons/obj/trash_vr.dmi'
- icon_state = "altevian_juice-trash"
\ No newline at end of file
+ icon_state = "altevian_juice-trash"
+
+/obj/item/trash/ratfruitcake
+ name = "\improper Flavor Unit package"
+ icon = 'icons/obj/trash_vr.dmi'
+ icon_state = "altevian_fruitcake-trash"
+
+/obj/item/trash/ratpackburger
+ name = "\improper Prepackaged Meal Tray"
+ icon = 'icons/obj/trash_vr.dmi'
+ icon_state = "altevian_pack_burger-trash"
+
+/obj/item/trash/ratpackcheese
+ name = "\improper Prepackaged Meal Tray"
+ icon = 'icons/obj/trash_vr.dmi'
+ icon_state = "altevian_pack_cheese-trash"
\ No newline at end of file
diff --git a/code/game/objects/items/weapons/storage/backpack_vr.dm b/code/game/objects/items/weapons/storage/backpack_vr.dm
index 5588969224..1a872e99ac 100644
--- a/code/game/objects/items/weapons/storage/backpack_vr.dm
+++ b/code/game/objects/items/weapons/storage/backpack_vr.dm
@@ -45,8 +45,9 @@
return
var/datum/sprite_accessory/tail/taur/TT = H.tail_style
- item_state = "[icon_base]_[TT.icon_sprite_tag]" //icon_sprite_tag is something like "deer"
- return 1
+ if(istype(TT))
+ item_state = "[icon_base]_[TT.icon_sprite_tag]" //icon_sprite_tag is something like "deer"
+ return 1
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 40ab9cb45e..db7644476b 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -100,7 +100,7 @@
/obj/item/weapon/melee/baton/examine(mob/user)
. = ..()
- if(Adjacent(user, src))
+ if(Adjacent(user))
if(bcell)
. += "The baton is [round(bcell.percent())]% charged."
if(!bcell)
diff --git a/code/game/objects/micro_structures.dm b/code/game/objects/micro_structures.dm
index 15bf9d13ae..8b10f31fa5 100644
--- a/code/game/objects/micro_structures.dm
+++ b/code/game/objects/micro_structures.dm
@@ -10,6 +10,13 @@
var/magic = FALSE //For events and stuff, if true, this tunnel will show up in the list regardless of whether it's in valid range, of if you're in a tunnel with this var, all tunnels of the same faction will show up redardless of range
micro_target = TRUE
+ var/static/non_micro_types = list(
+ /mob/living/simple_mob/vore/alienanimals/catslug,
+ /mob/living/simple_mob/vore/hostile/morph,
+ /mob/living/simple_mob/protean_blob,
+ /mob/living/simple_mob/slime
+ )
+
/obj/structure/micro_tunnel/Initialize()
. = ..()
if(name == initial(name))
@@ -47,11 +54,19 @@
if(8)
pixel_x = -32
-/obj/structure/micro_tunnel/attack_hand(mob/user)
+/obj/structure/micro_tunnel/attack_hand(mob/living/user)
if(!isliving(user))
return ..()
if(user.loc == src)
- var/choice = tgui_alert(user,"It's dark and gloomy in here. What would you like to do?","Tunnel",list("Exit", "Move", "Cancel"))
+ var/list/our_options = list("Exit", "Move")
+
+ if(is_type_in_list(user, non_micro_types))
+ if(src.contents.len > 1)
+ our_options |= "Eat"
+
+ our_options |= "Cancel"
+
+ var/choice = tgui_alert(user,"It's dark and gloomy in here. What would you like to do?","Tunnel",our_options)
switch(choice)
if("Exit")
if(user.loc != src)
@@ -114,6 +129,28 @@
var/obj/structure/micro_tunnel/da_oddawun = choice
da_oddawun.tunnel_notify(user)
return
+ if("Eat")
+ var/list/our_targets = list()
+ for(var/mob/living/L in src.contents)
+ if(L == user)
+ continue
+ our_targets |= L
+ if(!our_targets.len)
+ to_chat(user, "There is no one in here except for you!")
+ return
+ var/mob/our_choice
+ if(our_targets.len == 1)
+ our_choice = pick(our_targets)
+ else
+ our_choice = tgui_input_list(user, "Who would you like to eat?", "Pick a target to eat", our_targets)
+ if(user.loc != src)
+ to_chat(user, "You are no longer inside \the [src], and so cannot eat \the [our_choice].")
+ return
+ if(our_choice.loc != src)
+ to_chat(user, "\The [our_choice] is no longer inside \the [src], and so cannot be eaten.")
+ return
+ user.feed_grabbed_to_self(user,our_choice)
+ return
if("Cancel")
return
@@ -157,8 +194,6 @@
user.visible_message("\The [user] pulls \the [grabbed] out of \the [src]! ! !")
return
- if(tgui_alert(user,"Do you want to go into the tunnel?","Enter Tunnel",list("Yes", "No")) != "Yes")
- return
user.visible_message("\The [user] begins climbing into \the [src]!")
if(!do_after(user, 10 SECONDS, exclusive = TRUE))
to_chat(user, "You didn't go into \the [src]!")
@@ -170,6 +205,10 @@
if(user.mob_size <= MOB_TINY || user.get_effective_size(TRUE) <= micro_accepted_scale)
return TRUE
+ if(is_type_in_list(user, non_micro_types))
+ if(tgui_alert(user, "Would you like to enter the tunnel, or reach inside it?", "Enter or reach", list("Enter","Reach")) == "Enter")
+ return TRUE
+
return FALSE
/obj/structure/micro_tunnel/attack_generic(mob/user, damage, attack_verb)
diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm
index afa263a8ac..51c32a1f00 100644
--- a/code/game/objects/structures/artstuff.dm
+++ b/code/game/objects/structures/artstuff.dm
@@ -106,7 +106,7 @@
if(choice == "No")
return
var/basecolor = input(user, "Select a base color for the canvas:", "Base Color", canvas_color) as null|color
- if(basecolor && Adjacent(user, src) && Adjacent(user, I))
+ if(basecolor && Adjacent(user) && user.get_active_hand() == I)
canvas_color = basecolor
reset_grid()
user.visible_message("[user] smears paint on [src], covering the entire thing in paint.", "You smear paint on [src], changing the color of the entire thing.", runemessage = "smears paint")
diff --git a/code/game/objects/structures/crates_lockers/largecrate_vr.dm b/code/game/objects/structures/crates_lockers/largecrate_vr.dm
index b70833ccea..3b35be2479 100644
--- a/code/game/objects/structures/crates_lockers/largecrate_vr.dm
+++ b/code/game/objects/structures/crates_lockers/largecrate_vr.dm
@@ -56,7 +56,7 @@
/mob/living/simple_mob/otie/zorgoia, //CHOMPstation edit
/mob/living/simple_mob/vore/rabbit,
/mob/living/simple_mob/vore/weretiger;0.5,
-// /mob/living/simple_mob/otie;0.5
+ /mob/living/simple_mob/otie;0.5
))
return ..()
@@ -72,11 +72,12 @@
/mob/living/simple_mob/animal/space/alien/drone,
/mob/living/simple_mob/animal/space/alien/sentinel,
/mob/living/simple_mob/animal/space/alien/queen,
-// /mob/living/simple_mob/otie/feral,
-// /mob/living/simple_mob/otie/red,
+ /mob/living/simple_mob/otie/feral,
+ /mob/living/simple_mob/otie/feral/chubby,
+ /mob/living/simple_mob/otie/red,
/mob/living/simple_mob/vore/aggressive/corrupthound))
return ..()
-/*
+
/obj/structure/largecrate/animal/guardbeast
name = "VARMAcorp autoNOMous security solution"
desc = "The VARMAcorp bioengineering division flagship product on trained optimal snowflake guard dogs."
@@ -84,6 +85,11 @@
icon_state = "sotiecrate"
starts_with = list(/mob/living/simple_mob/otie/security)
+/obj/structure/largecrate/animal/otie/guardbeast/Initialize()
+ starts_with = list(pick(/mob/living/simple_mob/otie/security,
+ /mob/living/simple_mob/otie/security/chubby))
+ return ..()
+
/obj/structure/largecrate/animal/guardmutant
name = "VARMAcorp autoNOMous security solution for hostile environments."
desc = "The VARMAcorp bioengineering division flagship product on trained optimal snowflake guard dogs. This one can survive hostile atmosphere."
@@ -91,6 +97,12 @@
icon_state = "sotiecrate"
starts_with = list(/mob/living/simple_mob/otie/security/phoron)
+/obj/structure/largecrate/animal/otie/guardmutant/Initialize()
+ starts_with = list(pick(/mob/living/simple_mob/otie/security/phoron;2,
+ /mob/living/simple_mob/otie/security/phoron/red;0.5,
+ /mob/living/simple_mob/otie/security/phoron/red/chubby;0.5))
+ return ..()
+
/obj/structure/largecrate/animal/otie
name = "VARMAcorp adoptable reject (Dangerous!)"
desc = "A warning on the side says the creature inside was returned to the supplier after injuring or devouring several unlucky members of the previous adoption family. It was given a second chance with the next customer. Godspeed and good luck with your new pet!"
@@ -99,6 +111,11 @@
starts_with = list(/mob/living/simple_mob/otie/cotie)
var/taped = 1
+/obj/structure/largecrate/animal/otie/Initialize()
+ starts_with = list(pick(/mob/living/simple_mob/otie/cotie,
+ /mob/living/simple_mob/otie/cotie/chubby))
+ return ..()
+
/obj/structure/largecrate/animal/otie/phoron
name = "VARMAcorp adaptive beta subject (Experimental)"
desc = "VARMAcorp experimental hostile environment adaptive breeding development kit. WARNING, DO NOT RELEASE IN WILD!"
@@ -106,7 +123,8 @@
/obj/structure/largecrate/animal/otie/phoron/Initialize()
starts_with = list(pick(/mob/living/simple_mob/otie/cotie/phoron;2,
- /mob/living/simple_mob/otie/red/friendly;0.5))
+ /mob/living/simple_mob/otie/red/friendly;0.5,
+ /mob/living/simple_mob/otie/red/chubby;0.5))
return ..()
/obj/structure/largecrate/animal/otie/attack_hand(mob/living/carbon/human/M as mob)//I just couldn't decide between the icons lmao
@@ -115,7 +133,6 @@
icon_state = "otiecrate"
taped = 0
..()
-*/ //VORESTATION AI REMOVAL, Oties are still fucking broken.
/obj/structure/largecrate/animal/catgirl
name = "Catgirl Crate"
diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm
index cbbeab71ad..c7f6745c43 100644
--- a/code/modules/admin/admin_verb_lists_vr.dm
+++ b/code/modules/admin/admin_verb_lists_vr.dm
@@ -8,7 +8,7 @@ var/list/admin_verbs_default = list(
/client/proc/cmd_mod_say, //VOREStation Add,
/client/proc/cmd_event_say, //VOREStation Add,
/client/proc/cmd_mentor_ticket_panel,
- /client/proc/cmd_mentor_say
+ /client/proc/cmd_mentor_say,
// /client/proc/hide_verbs, //hides all our adminverbs, //VOREStation Remove,
// /client/proc/hide_most_verbs, //hides all our hideable adminverbs, //VOREStation Remove,
// /client/proc/debug_variables, //allows us to -see- the variables of any instance in the game. +VAREDIT needed to modify, //VOREStation Remove,
@@ -18,6 +18,8 @@ var/list/admin_verbs_default = list(
// /client/proc/cmd_mod_say,
// /client/proc/deadchat //toggles deadchat on/off,
// /client/proc/toggle_ahelp_sound,
+ /client/proc/toggle_admin_global_looc,
+ /client/proc/toggle_admin_deadchat
)
var/list/admin_verbs_admin = list(
diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm
index d9aacdcaca..485cc88e22 100644
--- a/code/modules/admin/verbs/buildmode.dm
+++ b/code/modules/admin/verbs/buildmode.dm
@@ -364,7 +364,7 @@
if(ispath(holder.buildmode.objholder,/turf))
var/turf/T = get_turf(object)
T.ChangeTurf(holder.buildmode.objholder)
- else
+ else if(ispath(holder.buildmode.objholder))
var/obj/A = new holder.buildmode.objholder (get_turf(object))
A.set_dir(holder.builddir.dir)
else if(pa.Find("right"))
diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm
index 2427b2441d..9fa878ad72 100644
--- a/code/modules/admin/verbs/pray.dm
+++ b/code/modules/admin/verbs/pray.dm
@@ -16,7 +16,7 @@
to_chat(usr, " You cannot pray (muted).")
return
- var/image/cross = image('icons/obj/storage.dmi',"bible")
+ var/icon/cross = icon('icons/obj/storage.dmi',"bible")
msg = "\icon[cross][bicon(cross)] PRAY: [key_name(src, 1)] (?) (PP) (VV) (SM) ([admin_jump_link(src, src)]) (CA) (SC) (SMITE): [msg]"
for(var/client/C in GLOB.admins)
diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm
index 960d3a6b99..ceba8367ff 100644
--- a/code/modules/admin/verbs/randomverbs.dm
+++ b/code/modules/admin/verbs/randomverbs.dm
@@ -530,8 +530,9 @@ Traitors and the like can also be revived with the previous role mostly intact.
if(equipment)
if(charjob)
job_master.EquipRank(new_character, charjob, 1, announce)
- new_character.mind.assigned_role = charjob
- new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob)
+ if(new_character.mind)
+ new_character.mind.assigned_role = charjob
+ new_character.mind.role_alt_title = job_master.GetPlayerAltTitle(new_character, charjob)
equip_custom_items(new_character) //CHOMPEdit readded to enable custom_item.txt
//If desired, add records.
diff --git a/code/modules/admin/verbs/smite_vr.dm b/code/modules/admin/verbs/smite_vr.dm
index 750a8c0bea..5ac60fcfc1 100644
--- a/code/modules/admin/verbs/smite_vr.dm
+++ b/code/modules/admin/verbs/smite_vr.dm
@@ -146,6 +146,7 @@ var/redspace_abduction_z
redspace_abduction_z = -1
to_chat(user,"This is the first use of the verb this shift, it will take a minute to configure the abduction z-level. It will be z[world.maxz+1].")
var/z = ++world.maxz
+ world.max_z_changed()
for(var/x = 1 to world.maxx)
for(var/y = 1 to world.maxy)
var/turf/T = locate(x,y,z)
@@ -232,7 +233,7 @@ var/redspace_abduction_z
to_chat(target, "Autosaving your progress, please wait...")
target << 'sound/effects/ding.ogg'
-
+
var/static/list/bad_tips = list(
"Did you know that black shoes protect you from electrocution while hacking?",
"Did you know that airlocks always have a wire that disables ID checks?",
diff --git a/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm b/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm
index 6986f5e23c..210a41239f 100644
--- a/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm
+++ b/code/modules/ai/ai_holder_subtypes/slime_xenobio_ai.dm
@@ -123,6 +123,12 @@
/datum/ai_holder/simple_mob/xenobio_slime/handle_special_tactic()
evolve_and_reproduce()
+/datum/ai_holder/simple_mob/xenobio_slime/handle_stance_tactical()
+ if(!istype(holder) || QDELETED(holder))
+ qdel(src)
+ return
+ ..()
+
// Hit the correct verbs to keep the slime species going.
/datum/ai_holder/simple_mob/xenobio_slime/proc/evolve_and_reproduce()
var/mob/living/simple_mob/slime/xenobio/my_slime = holder
diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm
index cff2875e7d..98e1c2d51d 100644
--- a/code/modules/client/client procs.dm
+++ b/code/modules/client/client procs.dm
@@ -208,6 +208,8 @@
prefs.last_id = computer_id //these are gonna be used for banning
prefs.client = src // Only relevant if we reloaded it from the global list, otherwise prefs/New sets it
+ hook_vr("client_new",list(src)) //VOREStation Code. For now this only loads vore prefs, so better put before mob.Login() call but after normal prefs are loaded.
+
. = ..() //calls mob.Login()
prefs.sanitize_preferences()
if(prefs)
@@ -252,8 +254,6 @@
if(config.aggressive_changelog)
src.changes()
- hook_vr("client_new",list(src)) //VOREStation Code
-
if(config.paranoia_logging)
var/alert = FALSE //VOREStation Edit start.
if(isnum(player_age) && player_age == 0)
diff --git a/code/modules/client/preference_setup/general/02_language.dm b/code/modules/client/preference_setup/general/02_language.dm
index 25d8c2c10e..9ceb693bd2 100644
--- a/code/modules/client/preference_setup/general/02_language.dm
+++ b/code/modules/client/preference_setup/general/02_language.dm
@@ -9,7 +9,8 @@
/datum/category_item/player_setup_item/general/language/load_character(var/savefile/S)
S["language"] >> pref.alternate_languages
S["extra_languages"] >> pref.extra_languages
- testing("LANGSANI: Loaded from [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]")
+ if(islist(pref.alternate_languages)) // Because aparently it may not be?
+ testing("LANGSANI: Loaded from [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]")
S["language_prefixes"] >> pref.language_prefixes
//CHOMPEdit Begin
S["species"] >> pref.species
@@ -19,7 +20,8 @@
/datum/category_item/player_setup_item/general/language/save_character(var/savefile/S)
S["language"] << pref.alternate_languages
S["extra_languages"] << pref.extra_languages
- testing("LANGSANI: Saved to [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]")
+ if(islist(pref.alternate_languages)) // Because aparently it may not be?
+ testing("LANGSANI: Loaded from [pref.client]'s character [pref.real_name || "-name not yet loaded-"] savefile: [english_list(pref.alternate_languages || list())]")
S["language_prefixes"] << pref.language_prefixes
S["language_custom_keys"] << pref.language_custom_keys
diff --git a/code/modules/client/preference_setup/global/setting_datums.dm b/code/modules/client/preference_setup/global/setting_datums.dm
index 11ef5d994b..f7ca6bf963 100644
--- a/code/modules/client/preference_setup/global/setting_datums.dm
+++ b/code/modules/client/preference_setup/global/setting_datums.dm
@@ -422,3 +422,9 @@ var/list/_client_preferences_by_type
key = "CHAT_RLOOC"
enabled_description = "Show"
disabled_description = "Hide"
+
+/datum/client_preference/holder/show_staff_dsay
+ description ="Staff Deadchat"
+ key = "CHAT_ADSAY"
+ enabled_description = "Show"
+ disabled_description = "Hide"
diff --git a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm
index c41474a68e..3ddc8c5388 100644
--- a/code/modules/client/preference_setup/loadout/loadout_head_vr.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_head_vr.dm
@@ -49,6 +49,14 @@
display_name = "pink tiger pelt"
path = /obj/item/clothing/head/pelt/tigerpeltpink
+/datum/gear/head/magic_hat
+ display_name = "wizard hat, colorable"
+ path = /obj/item/clothing/head/wizard/fake/realistic/colorable
+
+/datum/gear/head/magic_hat/New()
+ ..()
+ gear_tweaks += gear_tweak_free_color_choice
+
/*
Talon hats
*/
diff --git a/code/modules/client/preference_setup/loadout/loadout_suit.dm b/code/modules/client/preference_setup/loadout/loadout_suit.dm
index 3041af9e90..14cc60208d 100644
--- a/code/modules/client/preference_setup/loadout/loadout_suit.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_suit.dm
@@ -194,6 +194,11 @@
path = /obj/item/clothing/suit/storage/apron/overalls
cost = 1
+/datum/gear/suit/altevian_apron
+ display_name = "crafters pride apron"
+ path = /obj/item/clothing/suit/storage/apron/altevian
+ cost = 1
+
/datum/gear/suit/cyberpunk
display_name = "cyberpunk jacket"
path = /obj/item/clothing/suit/cyberpunk
diff --git a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm
index 6272e40204..9e4a87cb6d 100644
--- a/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_xeno_vr.dm
@@ -94,5 +94,14 @@
/datum/gear/suit/taur/white_dress
display_name = "white wedding dress (Wolf/Horse-taur)"
- path = /obj/item/clothing/suit/taur_dress/white
+ path = /obj/item/clothing/suit/taur/dress
sort_category = "Xenowear"
+
+/datum/gear/suit/taur/colorable_skirt
+ display_name = "colorable skirt (Wolf/Horse-taur)"
+ path = /obj/item/clothing/suit/taur/skirt
+ sort_category = "Xenowear"
+
+/datum/gear/suit/taur/colorable_skirt/New()
+ ..()
+ gear_tweaks += gear_tweak_free_color_choice
\ No newline at end of file
diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm
index 6df855d1eb..0e83638e88 100644
--- a/code/modules/client/preferences_toggle_procs.dm
+++ b/code/modules/client/preferences_toggle_procs.dm
@@ -512,7 +512,7 @@ CHOMPRemove. Bundled voice sounds into emote/whisper/subtle. Going this extra le
to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] receive debug logs.")
SScharacter_setup.queue_preferences_save(prefs)
- feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ feedback_add_details("admin_verb","TADebugLogs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//Mods
/client/proc/toggle_attack_logs()
@@ -527,4 +527,33 @@ CHOMPRemove. Bundled voice sounds into emote/whisper/subtle. Going this extra le
to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] receive attack logs.")
SScharacter_setup.queue_preferences_save(prefs)
- feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+ feedback_add_details("admin_verb","TAAttackLogs") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+//General
+/client/proc/toggle_admin_global_looc()
+ set name = "Toggle Admin Global LOOC Visibility"
+ set category = "Preferences"
+ set desc = "Toggles seeing LOOC messages outside your actual LOOC range."
+
+ var/pref_path = /datum/client_preference/holder/show_rlooc
+
+ if(holder)
+ toggle_preference(pref_path)
+ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear global LOOC.")
+ SScharacter_setup.queue_preferences_save(prefs)
+
+ feedback_add_details("admin_verb","TAGlobalLOOC") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+
+/client/proc/toggle_admin_deadchat()
+ set name = "Toggle Admin Living Deadchat"
+ set category = "Preferences"
+ set desc = "Toggles seeing deadchat while not observing."
+
+ var/pref_path = /datum/client_preference/holder/show_staff_dsay
+
+ if(holder)
+ toggle_preference(pref_path)
+ to_chat(src,"You will [ (is_preference_enabled(pref_path)) ? "now" : "no longer"] hear deadchat while not observing.")
+ SScharacter_setup.queue_preferences_save(prefs)
+
+ feedback_add_details("admin_verb","TADeadchat") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm
index 8b0b72f8bb..df898e15f6 100644
--- a/code/modules/client/preferences_vr.dm
+++ b/code/modules/client/preferences_vr.dm
@@ -100,7 +100,7 @@
else
to_chat(src, "You are now catchable.")
prefs.capture_crystal = 1
- if(L)
+ if(L && istype(L))
L.capture_crystal = prefs.capture_crystal
SScharacter_setup.queue_preferences_save(prefs)
diff --git a/code/modules/clothing/suits/jobs.dm b/code/modules/clothing/suits/jobs.dm
index d2ba4b26d9..1341af1b69 100644
--- a/code/modules/clothing/suits/jobs.dm
+++ b/code/modules/clothing/suits/jobs.dm
@@ -19,6 +19,12 @@
icon_state = "apron_white"
item_state_slots = list(slot_r_hand_str = "apronchef", slot_l_hand_str = "apronchef")
+/obj/item/clothing/suit/storage/apron/altevian
+ name = "Multi-purpose Crafters' Pride"
+ desc = "An apron designed by the space faring species that can hold an array of tools and other trinkets. It's made with a high-quality material."
+ icon_state = "apron_altevian"
+ item_state_slots = list(slot_r_hand_str = null, slot_l_hand_str = null)
+
//Captain
/obj/item/clothing/suit/captunic
name = "site manager's parade tunic"
diff --git a/code/modules/clothing/suits/miscellaneous_vr.dm b/code/modules/clothing/suits/miscellaneous_vr.dm
index 94fb746d94..9a10952789 100644
--- a/code/modules/clothing/suits/miscellaneous_vr.dm
+++ b/code/modules/clothing/suits/miscellaneous_vr.dm
@@ -74,17 +74,23 @@
desc = "Knightly armor for a mount who doesn't need any rider. This one is marked to the house of Mason."
icon_state = "Mason_barding"
-/obj/item/clothing/suit/taur_dress
+/obj/item/clothing/suit/taur
icon = 'icons/mob/taursuits_horse_vr.dmi'
body_parts_covered = UPPER_TORSO|LOWER_TORSO
pixel_x = -16
-/obj/item/clothing/suit/taur_dress/white
+/obj/item/clothing/suit/taur/dress
name = "white wedding dress"
desc = "A fancy white dress with a blue underdress."
icon_state = "whitedress1"
flags_inv = HIDESHOES
+/obj/item/clothing/suit/taur/skirt
+ name = "taur skirt"
+ desc = "A skirt with a corset, fit for those with four legs."
+ icon_state = "skirt_colorable"
+ flags_inv = HIDESHOES
+
/obj/item/clothing/suit/storage/det_trench/alt
name = "sleek modern coat"
desc = "A sleek overcoat made of neo-laminated fabric. Has a reasonably sized pocket on the inside."
diff --git a/code/modules/clothing/suits/wiz_robe.dm b/code/modules/clothing/suits/wiz_robe.dm
index 30cb1d6b55..054f9dd110 100644
--- a/code/modules/clothing/suits/wiz_robe.dm
+++ b/code/modules/clothing/suits/wiz_robe.dm
@@ -18,6 +18,16 @@
desc = "It has WIZZARD written across it in sequins. Comes with a cool beard."
icon_state = "wizard-fake"
body_parts_covered = HEAD|FACE
+ siemens_coefficient = 1
+
+/obj/item/clothing/head/wizard/fake/realistic
+ desc = "A cool-looking 'magic' hat."
+ icon_state = "wizard"
+ body_parts_covered = HEAD
+
+/obj/item/clothing/head/wizard/fake/realistic/colorable
+ desc = "A cool-looking 'magic' hat."
+ icon_state = "wizard-white"
/obj/item/clothing/head/wizard/marisa
name = "Witch Hat"
diff --git a/code/modules/economy/vending_machines_vr.dm b/code/modules/economy/vending_machines_vr.dm
index dbe287f9df..668208216b 100644
--- a/code/modules/economy/vending_machines_vr.dm
+++ b/code/modules/economy/vending_machines_vr.dm
@@ -3548,8 +3548,14 @@
product_ads = "Perfectly edible!;Squeaky clean foods!;Cheesed to meet you!;Made for spacers, by spacers, of spacers!"
products = list(/obj/item/weapon/reagent_containers/food/snacks/ratprotein = 15,
/obj/item/weapon/reagent_containers/food/snacks/ratveggies = 15,
- /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 15)
+ /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 15,
+ /obj/item/weapon/reagent_containers/food/snacks/ratfruitcake = 15,
+ /obj/item/weapon/reagent_containers/food/snacks/ratpackburger = 8,
+ /obj/item/weapon/reagent_containers/food/snacks/ratpackcheese = 8)
prices = list(/obj/item/weapon/reagent_containers/food/snacks/ratprotein = 8,
/obj/item/weapon/reagent_containers/food/snacks/ratveggies = 8,
- /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 8)
+ /obj/item/weapon/reagent_containers/food/snacks/ratliquid = 8,
+ /obj/item/weapon/reagent_containers/food/snacks/ratfruitcake = 8,
+ /obj/item/weapon/reagent_containers/food/snacks/ratpackburger = 10,
+ /obj/item/weapon/reagent_containers/food/snacks/ratpackcheese = 10)
diff --git a/code/modules/events/prison_break.dm b/code/modules/events/prison_break.dm
index f2675a0ede..d5eb151d3f 100644
--- a/code/modules/events/prison_break.dm
+++ b/code/modules/events/prison_break.dm
@@ -64,7 +64,7 @@
var/obj/machinery/power/apc/theAPC = null
for(var/area/A in areas)
theAPC = A.get_apc()
- if(theAPC.operating) //If the apc's off, it's a little hard to overload the lights.
+ if(theAPC && theAPC.operating) //If the apc's off, it's a little hard to overload the lights.
for(var/obj/machinery/light/L in A)
L.flicker(10)
diff --git a/code/modules/events/spontaneous_appendicitis.dm b/code/modules/events/spontaneous_appendicitis.dm
index ddc392174e..241ed83db0 100644
--- a/code/modules/events/spontaneous_appendicitis.dm
+++ b/code/modules/events/spontaneous_appendicitis.dm
@@ -1,4 +1,7 @@
/datum/event/spontaneous_appendicitis/start()
+ if(prob(50))
+ kill()
+ return
for(var/mob/living/carbon/human/H in shuffle(living_mob_list))
if(H.client && H.appendicitis())
break
diff --git a/code/modules/events/viral_infection.dm b/code/modules/events/viral_infection.dm
index 9be0589077..5ee99ed5db 100644
--- a/code/modules/events/viral_infection.dm
+++ b/code/modules/events/viral_infection.dm
@@ -4,6 +4,9 @@ var/global/list/event_viruses = list() // so that event viruses are kept around
var/list/viruses = list()
/datum/event/viral_infection/setup()
+ if(prob(50))
+ kill()
+ return
announceWhen = rand(0, 3000)
endWhen = announceWhen + 1
diff --git a/code/modules/food/food/drinks/bottle.dm b/code/modules/food/food/drinks/bottle.dm
index df16e4960b..9dcd17c256 100644
--- a/code/modules/food/food/drinks/bottle.dm
+++ b/code/modules/food/food/drinks/bottle.dm
@@ -12,6 +12,7 @@
var/obj/item/weapon/reagent_containers/glass/rag/rag = null
var/rag_underlay = "rag"
+ var/violent_throw = FALSE
/obj/item/weapon/reagent_containers/food/drinks/bottle/on_reagent_change() return // To suppress price updating. Bottles have their own price tags.
@@ -29,18 +30,26 @@
return ..()
//when thrown on impact, bottles smash and spill their contents
+/obj/item/weapon/reagent_containers/food/drinks/bottle/throw_at(atom/target, range, speed, mob/thrower, spin = TRUE, datum/callback/callback)
+ . = ..()
+ if(istype(thrower) && thrower.a_intent == I_HURT)
+ violent_throw = TRUE
+ throw_source = get_turf(thrower)
+
/obj/item/weapon/reagent_containers/food/drinks/bottle/throw_impact(atom/hit_atom, var/speed)
..()
- var/mob/M = thrower
- if(isGlass && istype(M) && M.a_intent == I_HURT)
+ if(isGlass && violent_throw)
var/throw_dist = get_dist(throw_source, loc)
- if(speed >= throw_speed && smash_check(throw_dist)) //not as reliable as smashing directly
+ if(smash_check(throw_dist)) //not as reliable as smashing directly
if(reagents)
hit_atom.visible_message("The contents of \the [src] splash all over [hit_atom]!")
reagents.splash(hit_atom, reagents.total_volume)
src.smash(loc, hit_atom)
+ violent_throw = FALSE
+ throw_source = null
+
/obj/item/weapon/reagent_containers/food/drinks/bottle/proc/smash_check(var/distance)
if(!isGlass || !smash_duration)
return 0
diff --git a/code/modules/food/food/snacks.dm b/code/modules/food/food/snacks.dm
index 11d7a172dc..ad34247ece 100644
--- a/code/modules/food/food/snacks.dm
+++ b/code/modules/food/food/snacks.dm
@@ -34,6 +34,8 @@
var/package_trash
/// Packaged meals switch to this state when opened, if set
var/package_open_state
+ /// Packaged meals that have opening animation
+ var/package_opening_state
/// If this is canned. If true, it will print a message and ask you to open it
var/canned = FALSE
@@ -269,6 +271,8 @@
user.put_in_hands(T)
if(package_open_state)
icon_state = package_open_state
+ if(package_opening_state)
+ flick(package_opening_state, src)
/obj/item/weapon/reagent_containers/food/snacks/proc/uncan(mob/user)
canned = FALSE
@@ -6897,7 +6901,7 @@
nutriment_desc = list("apple" = 1, "sweetness" = 1)
bitesize = 2
-/obj/item/weapon/reagent_containers/food/snacks/appleberry/Initialize()
+/obj/item/weapon/reagent_containers/food/snacks/canned/appleberry/Initialize()
. = ..()
reagents.add_reagent("milk", 8)
reagents.add_reagent("sugar", 5)
diff --git a/code/modules/food/food/snacks_vr.dm b/code/modules/food/food/snacks_vr.dm
index 23935f6207..3ba39dc3ad 100644
--- a/code/modules/food/food/snacks_vr.dm
+++ b/code/modules/food/food/snacks_vr.dm
@@ -826,3 +826,38 @@
/obj/item/weapon/reagent_containers/food/snacks/ratsteak/Initialize()
. = ..()
reagents.add_reagent("protein", 3)
+
+/obj/item/weapon/reagent_containers/food/snacks/ratfruitcake
+ name = "Premade Fruit Block"
+ desc = "A block of processed material that is infused with a mix of fruits and matter of such."
+ icon = 'icons/obj/food_vr.dmi'
+ icon_state = "altevian_fruitcake"
+ package_open_state = "altevian_fruitcake-open"
+ package = TRUE
+ trash = /obj/item/trash/ratfruitcake
+ nutriment_amt = 2
+ nutriment_desc = list("fruitiness" = 4)
+
+/obj/item/weapon/reagent_containers/food/snacks/ratpackburger
+ name = "Altevian Prepackaged Meal - Burger"
+ desc = "A unique twist on what most know as MREs. This seems to be made with using bluespace tech and other methods of preserving an items freshness that it's like someone just ordered this from a restaurant just minutes ago. This one seems to be of burger and fries!"
+ icon = 'icons/obj/food_vr.dmi'
+ icon_state = "altevian_pack_burger"
+ package_open_state = "altevian_pack_burger-open"
+ package_opening_state = "altevian_pack_burger-opening"
+ package = TRUE
+ trash = /obj/item/trash/ratpackburger
+ nutriment_amt = 2
+ nutriment_desc = list("fresh buns" = 2, "burger patty" = 4, "pickles" = 1)
+
+/obj/item/weapon/reagent_containers/food/snacks/ratpackcheese
+ name = "Generations Novelty Packaged Wedge"
+ desc = "Using the popular method of packaging that altevians use, they seemed to also use it for other methods. This one appears to have no real markings on it, save for its different coloring, and an image of the altevian emblem."
+ icon = 'icons/obj/food_vr.dmi'
+ icon_state = "altevian_pack_cheese"
+ package_open_state = "altevian_pack_cheese-open"
+ package_opening_state = "altevian_pack_cheese-opening"
+ package = TRUE
+ trash = /obj/item/trash/ratpackcheese
+ nutriment_amt = 2
+ nutriment_desc = list("gourmand cheese" = 4)
diff --git a/code/modules/games/cards.dm b/code/modules/games/cards.dm
index 52ad80641a..c3f1a6dc3e 100644
--- a/code/modules/games/cards.dm
+++ b/code/modules/games/cards.dm
@@ -400,11 +400,13 @@
/obj/item/weapon/hand/update_icon(var/direction = 0)
- if(!cards.len)
+ var/cardNumber = cards.len
+
+ if(!cardNumber)
qdel(src)
return
- else if(cards.len > 1)
- name = "hand of cards"
+ else if(cardNumber > 1)
+ name = "hand of cards ([cardNumber])"
desc = "Some playing cards."
else
name = "a playing card"
@@ -413,7 +415,7 @@
cut_overlays()
- if(cards.len == 1)
+ if(cardNumber == 1)
var/datum/playingcard/P = cards[1]
var/image/I = new(src.icon, (concealed ? "[P.back_icon]" : "[P.card_icon]") )
I.pixel_x += (-5+rand(10))
@@ -421,7 +423,7 @@
add_overlay(I)
return
- var/offset = FLOOR(20/cards.len, 1)
+ var/offset = FLOOR(20/cardNumber, 1)
var/matrix/M = matrix()
if(direction)
@@ -453,6 +455,7 @@
add_overlay(I)
i++
+
/obj/item/weapon/hand/dropped(mob/user as mob)
if(locate(/obj/structure/table, loc))
src.update_icon(user.dir)
diff --git a/code/modules/mob/language/station_vr.dm b/code/modules/mob/language/station_vr.dm
index 7be2976d69..77186ccb00 100644
--- a/code/modules/mob/language/station_vr.dm
+++ b/code/modules/mob/language/station_vr.dm
@@ -15,7 +15,7 @@
speech_verb = "chirps"
colour = "birdsongc"
key = "G"
- syllables = list ("cheep", "peep", "tweet")
+ syllables = list ("chee", "pee", "twee", "hoo", "ee", "oo", "ch", "ts", "sch", "twe", "too", "pha", "ewe", "shee", "shoo", "p", "tw", "aw", "caw", "c")
/datum/language/sergal
name = LANGUAGE_SAGARU
diff --git a/code/modules/mob/living/bot/bot.dm b/code/modules/mob/living/bot/bot.dm
index bec833476f..1d36c892cd 100644
--- a/code/modules/mob/living/bot/bot.dm
+++ b/code/modules/mob/living/bot/bot.dm
@@ -297,7 +297,8 @@
return
/mob/living/bot/proc/handleFrustrated(var/targ)
- obstacle = targ ? target_path[1] : patrol_path[1]
+ if((targ && LAZYLEN(target_path)) || LAZYLEN(patrol_path))
+ obstacle = targ ? target_path[1] : patrol_path[1]
target_path = list()
patrol_path = list()
return
@@ -577,4 +578,4 @@
feeding = FALSE
can_be_drop_pred = FALSE
- return ..()
\ No newline at end of file
+ return ..()
diff --git a/code/modules/mob/living/carbon/human/life_vr.dm b/code/modules/mob/living/carbon/human/life_vr.dm
index fc4f613418..8e52e3daaf 100644
--- a/code/modules/mob/living/carbon/human/life_vr.dm
+++ b/code/modules/mob/living/carbon/human/life_vr.dm
@@ -78,3 +78,8 @@
// Moving around increases germ_level faster
if(germ_level < GERM_LEVEL_MOVE_CAP && prob(8))
germ_level++
+
+
+/mob/living/carbon
+ var/synth_cosmetic_pain = FALSE
+
diff --git a/code/modules/mob/living/carbon/human/npcs.dm b/code/modules/mob/living/carbon/human/npcs.dm
index 23f7bd722a..3aa2c1bd92 100644
--- a/code/modules/mob/living/carbon/human/npcs.dm
+++ b/code/modules/mob/living/carbon/human/npcs.dm
@@ -12,3 +12,4 @@
real_name = name
w_uniform = new /obj/item/clothing/under/punpun(src)
regenerate_icons()
+ can_be_drop_prey = TRUE //CHOMP Add
diff --git a/code/modules/mob/living/carbon/human/species/outsider/vox.dm b/code/modules/mob/living/carbon/human/species/outsider/vox.dm
index da0e0e0b9b..5093c4e821 100644
--- a/code/modules/mob/living/carbon/human/species/outsider/vox.dm
+++ b/code/modules/mob/living/carbon/human/species/outsider/vox.dm
@@ -46,6 +46,7 @@
breath_type = "nitrogen" //CHOMPedit
poison_type = "oxygen"
+ ideal_air_type = /datum/gas_mixture/belly_air/vox
siemens_coefficient = 0.2
flags = NO_SCAN | NO_DEFIB
@@ -110,6 +111,3 @@
H.internal = locate(/obj/item/weapon/tank) in H.contents
if(istype(H.internal,/obj/item/weapon/tank) && H.internals)
H.internals.icon_state = "internal1"
-
-/datum/species/vox/get_perfect_belly_air_type()
- return /datum/gas_mixture/belly_air/vox
diff --git a/code/modules/mob/living/carbon/human/species/species_getters_vr.dm b/code/modules/mob/living/carbon/human/species/species_getters_vr.dm
index 8bccade358..140923062c 100644
--- a/code/modules/mob/living/carbon/human/species/species_getters_vr.dm
+++ b/code/modules/mob/living/carbon/human/species/species_getters_vr.dm
@@ -7,4 +7,7 @@
return wing_animation
/datum/species/proc/get_perfect_belly_air_type(var/mob/living/carbon/human/H)
- return /datum/gas_mixture/belly_air //Default
\ No newline at end of file
+ if(ideal_air_type)
+ return ideal_air_type //Whatever we want
+ else
+ return /datum/gas_mixture/belly_air //Default
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/species/species_vr.dm b/code/modules/mob/living/carbon/human/species/species_vr.dm
index ef4e8252bd..77d15d000a 100644
--- a/code/modules/mob/living/carbon/human/species/species_vr.dm
+++ b/code/modules/mob/living/carbon/human/species/species_vr.dm
@@ -30,6 +30,8 @@
var/list/copy_vars = list("base_species", "icobase", "deform", "tail", "tail_animation", "icobase_tail", "color_mult", "primitive_form", "appearance_flags", "flesh_color", "base_color", "blood_mask", "damage_mask", "damage_overlays", "move_trail", "has_floating_eyes")
var/trait_points = 0
+ var/ideal_air_type = null // Set to something else if you breathe something else from default composition. Used for inbelly air.
+
var/micro_size_mod = 0 // How different is our size for interactions that involve us being small?
var/macro_size_mod = 0 // How different is our size for interactions that involve us being big?
var/digestion_nutrition_modifier = 1
diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm
index 5ff11b6c9c..d54928b455 100644
--- a/code/modules/mob/living/carbon/human/species/station/station.dm
+++ b/code/modules/mob/living/carbon/human/species/station/station.dm
@@ -426,6 +426,7 @@
hazard_low_pressure = 220 // Dangerously low pressure.
safe_pressure = 400
poison_type = "nitrogen" // technically it's a partial pressure thing but IDK if we can emulate that
+ ideal_air_type = /datum/gas_mixture/belly_air/zaddat
genders = list(FEMALE, PLURAL) //females are polyp-producing, infertile females and males are nigh-identical
@@ -501,9 +502,6 @@
if(!(K in covered))
H.apply_damage(light_amount/4, BURN, K, 0, 0, "Abnormal growths")
-/datum/species/zaddat/get_perfect_belly_air_type()
- return /datum/gas_mixture/belly_air/zaddat
-
/datum/species/diona
name = SPECIES_DIONA
name_plural = "Dionaea"
diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
index 1a7f06be8f..c4c55fa63e 100644
--- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
+++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm
@@ -1153,3 +1153,16 @@
"You are dragged below the water and feel yourself slipping directly into \the [src]'s [vore_selected]!")
to_chat(src, "You successfully drag \the [target] into the water, slipping them into your [vore_selected].")
target.forceMove(src.vore_selected)
+
+
+/mob/living/carbon/human/proc/toggle_pain_module()
+ set name = "Toggle pain simulation."
+ set desc = "Turn on your pain simulation for that organic experience! Or turn it off for repairs, or if it's too much."
+ set category = "Abilities"
+
+ if(synth_cosmetic_pain)
+ to_chat(src, " You turn off your pain simulators.")
+ else
+ to_chat(src, " You turn on your pain simulators ")
+
+ synth_cosmetic_pain = !synth_cosmetic_pain
diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm
index 35a56f9b42..25bb2c5aaf 100644
--- a/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm
+++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/negative.dm
@@ -136,12 +136,12 @@
/datum/trait/negative/breathes/phoron
name = "Phoron Breather"
desc = "You breathe phoron instead of oxygen (which is poisonous to you), much like a Vox."
- var_changes = list("breath_type" = "phoron", "poison_type" = "oxygen")
+ var_changes = list("breath_type" = "phoron", "poison_type" = "oxygen", "ideal_air_type" = /datum/gas_mixture/belly_air/vox)
/datum/trait/negative/breathes/nitrogen
name = "Nitrogen Breather"
desc = "You breathe nitrogen instead of oxygen (which is poisonous to you). Incidentally, phoron isn't poisonous to breathe to you."
- var_changes = list("breath_type" = "nitrogen", "poison_type" = "oxygen")
+ var_changes = list("breath_type" = "nitrogen", "poison_type" = "oxygen", "ideal_air_type" = /datum/gas_mixture/belly_air/nitrogen_breather)
/datum/trait/negative/monolingual
name = "Monolingual"
diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm
index 9732a96922..0f79dd7576 100644
--- a/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm
+++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/neutral.dm
@@ -166,11 +166,12 @@
/datum/trait/neutral/synth_chemfurnace
name = "Biofuel Processor"
- desc = "You are able to gain energy through consuming and processing normal food. Energy-dense foods such as protein bars and survival food will yield the best results."
+ desc = "You are able to gain energy through consuming and processing normal food, at the cost of significantly slower recharging via cyborg chargers. Energy-dense foods such as protein bars and survival food will yield the best results."
cost = 0
custom_only = FALSE
can_take = SYNTHETICS
var_changes = list("organic_food_coeff" = 0.75, "synthetic_food_coeff" = 1) //CHOMPEdit: Increase values
+ excludes = list(/datum/trait/neutral/biofuel_value_down)
/datum/trait/neutral/glowing_eyes
name = "Glowing Eyes"
@@ -608,9 +609,21 @@
/datum/trait/neutral/biofuel_value_down
name = "Discount Biofuel processor"
- desc = "You are able to gain energy through consuming and processing normal food. Unfortunately, it is half as effective as premium models."
+ desc = "You are able to gain energy through consuming and processing normal food. Unfortunately, it is half as effective as premium models. On the plus side, you still recharge from charging stations fairly efficiently."
cost = 0
custom_only = FALSE
can_take = SYNTHETICS
var_changes = list("organic_food_coeff" = 0, "synthetic_food_coeff" = 0.3, digestion_efficiency = 0.5)
excludes = list(/datum/trait/neutral/synth_chemfurnace)
+
+/datum/trait/neutral/synth_cosmetic_pain
+ name = "Pain simulation"
+ desc = "You have added modules in your synthetic shell that simulates the sensation of pain. You are able to turn this on and off for repairs as needed or convenience at will."
+ cost = 0
+ custom_only = FALSE
+ can_take = SYNTHETICS
+
+
+/datum/trait/neutral/synth_cosmetic_pain/apply(var/datum/species/S,var/mob/living/carbon/human/H)
+ ..(S,H)
+ H.verbs |= /mob/living/carbon/human/proc/toggle_pain_module
diff --git a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm
index bea432f1ac..b61f3923e4 100644
--- a/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm
+++ b/code/modules/mob/living/carbon/human/species/station/traits_vr/positive.dm
@@ -215,3 +215,11 @@
custom_only = FALSE
varchange_type = TRAIT_VARCHANGE_MORE_BETTER
*/
+
+/datum/trait/positive/trauma_tolerance //CHOMPEdit renamed because we already have pain_tolerance pathname for halloss damage resistance.
+ name = "Grit"
+ desc = "You can keep going a little longer, a little harder when you get hurt, Injuries only inflict 85% as much pain, and slowdown from pain is 85% as effective."
+ cost = 2
+ var_changes = list("trauma_mod" = 0.85)
+ excludes = list(/datum/trait/negative/neural_hypersensitivity)
+ can_take = ORGANICS
diff --git a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm
index 55ba92b58c..ec054ab520 100644
--- a/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm
+++ b/code/modules/mob/living/carbon/human/species/virtual_reality/avatar.dm
@@ -81,7 +81,7 @@
// Move the mind
avatar.Sleeping(1)
src.mind.transfer_to(avatar)
- to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.\nYou can modify your appearance by using various \"Change \[X\]\" verbs in the Abilities tab.")
+ to_chat(avatar, "You have enterred Virtual Reality!\nAll normal gameplay rules still apply.\nWounds you suffer here won't persist when you leave VR, but some of the pain will.\nYou can leave VR at any time by using the \"Exit Virtual Reality\" verb in the Abilities tab, or by ghosting.") //No more prommie VR thing, so removed tidbit about changing appearance
to_chat(avatar, " You black out for a moment, and wake to find yourself in a new body in virtual reality.") // So this is what VR feels like?
// exit_vr is called on the vr mob, and puts the mind back into the original mob
diff --git a/code/modules/mob/living/carbon/lick_wounds.dm b/code/modules/mob/living/carbon/lick_wounds.dm
index 1bc94d0d98..8ec651d05b 100644
--- a/code/modules/mob/living/carbon/lick_wounds.dm
+++ b/code/modules/mob/living/carbon/lick_wounds.dm
@@ -1,12 +1,11 @@
-/mob/living/carbon/human/proc/lick_wounds(var/mob/living/carbon/M as mob in range(1)) // Allows the user to lick themselves. Given how rarely this trait is used, I don't see an issue with a slight buff.
+/mob/living/carbon/human/proc/lick_wounds(var/mob/living/carbon/M as mob in view(1)) // Allows the user to lick themselves. Given how rarely this trait is used, I don't see an issue with a slight buff.
set name = "Lick Wounds"
set category = "Abilities"
set desc = "Disinfect and heal small wounds with your saliva."
- //CHOMPEdit Start - No longer usable while incapacitated
- if(src.incapacitated())
+ if(stat || paralysis || weakened || stunned)
+ to_chat(src, "You can't do that in your current state.")
return
- //CHOMPEdit End
if(nutrition < 50)
to_chat(src, "You need more energy to produce antiseptic enzymes. Eat something and try again.")
diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
index 12914a0426..f6f37fa210 100644
--- a/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
+++ b/code/modules/mob/living/silicon/robot/dogborg/dog_modules_vr.dm
@@ -409,24 +409,33 @@
var/datum/matter_synth/glass = null
/obj/item/device/lightreplacer/dogborg/attack_self(mob/user)//Recharger refill is so last season. Now we recycle without magic!
- if(uses >= max_uses)
- to_chat(user, "[src.name] is full.")
+
+ var/choice = tgui_alert(user, "Do you wish to check the reserves or change the color?", "Selection List", list("Reserves", "Color"))
+ if(choice == "Color")
+ var/new_color = input(usr, "Choose a color to set the light to! (Default is [LIGHT_COLOR_INCANDESCENT_TUBE])", "", selected_color) as color|null
+ if(new_color)
+ selected_color = new_color
+ to_chat(user, "The light color has been changed.")
return
- if(uses < max_uses && cooldown == 0)
- if(glass.energy < 125)
- to_chat(user, "Insufficient material reserves.")
- return
- to_chat(user, "It has [uses] lights remaining. Attempting to fabricate a replacement. Please stand still.")
- cooldown = 1
- if(do_after(user, 50))
- glass.use_charge(125)
- add_uses(1)
- cooldown = 0
- else
- cooldown = 0
else
- to_chat(user, "It has [uses] lights remaining.")
- return
+ if(uses >= max_uses)
+ to_chat(user, "[src.name] is full.")
+ return
+ if(uses < max_uses && cooldown == 0)
+ if(glass.energy < 125)
+ to_chat(user, "Insufficient material reserves.")
+ return
+ to_chat(user, "It has [uses] lights remaining. Attempting to fabricate a replacement. Please stand still.")
+ cooldown = 1
+ if(do_after(user, 50))
+ glass.use_charge(125)
+ add_uses(1)
+ cooldown = 0
+ else
+ cooldown = 0
+ else
+ to_chat(user, "It has [uses] lights remaining.")
+ return
//Pounce stuff for K-9
/obj/item/weapon/dogborg/pounce
diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm
index 78593a85ac..128bffbc09 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules/station_vr.dm
@@ -345,6 +345,19 @@
var/obj/item/device/dogborg/sleeper/B = new /obj/item/device/dogborg/sleeper(src) //So they can nom people and heal them
B.water = water
src.modules += B
+
+ //CHOMPEdit Start - Give back the ATK/ABP since we don't have the surgeryhound
+ var/obj/item/stack/medical/advanced/ointment/O = new /obj/item/stack/medical/advanced/ointment(src)
+ var/obj/item/stack/medical/advanced/bruise_pack/P = new /obj/item/stack/medical/advanced/bruise_pack(src)
+ O.uses_charge = 1
+ O.charge_costs = list(1000)
+ O.synths = list(medicine)
+ P.uses_charge = 1
+ P.charge_costs = list(1000)
+ P.synths = list(medicine)
+ src.modules += O
+ src.modules += P
+ //CHOMPEdit End
R.icon = 'icons/mob/widerobot_vr.dmi'
diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm
index c11e9e183a..a8ac7e6cac 100644
--- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm
+++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm
@@ -54,6 +54,7 @@
var/obj/item/device/radio/headset/mob_radio //Adminbus headset for simplemob shenanigans.
does_spin = FALSE
can_be_drop_pred = TRUE // Mobs are pred by default.
+ can_be_drop_prey = TRUE //CHOMP Add This also counts for spontaneous prey for telenoms and phase noms.
var/damage_threshold = 0 //For some mobs, they have a damage threshold required to deal damage to them.
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm
index 13b88dd2e2..8bae5e7852 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/spookyghost.dm
@@ -208,7 +208,8 @@
/mob/living/simple_mob/vore/alienanimals/spooky_ghost/apply_melee_effects(var/atom/A)
var/mob/living/L = A
- L.hallucination += rand(1,50)
+ if(L && istype(L))
+ L.hallucination += rand(1,50)
/mob/living/simple_mob/vore/alienanimals/spooky_ghost/Life()
. = ..()
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm
index fb34c4d73f..6b37550c82 100644
--- a/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/giant_spider/nurse.dm
@@ -141,7 +141,8 @@
if(large_cocoon)
C.icon_state = pick("cocoon_large1","cocoon_large2","cocoon_large3")
- ai_holder.remove_target()
+ if(ai_holder)
+ ai_holder.remove_target()
return TRUE
diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm
new file mode 100644
index 0000000000..8772a061d7
--- /dev/null
+++ b/code/modules/mob/living/simple_mob/subtypes/animal/sif/grafadreka.dm
@@ -0,0 +1,633 @@
+/datum/modifier/sifsap_salve
+ name = "Sifsap Salve"
+ desc = "Your wounds have been salved with Sivian sap."
+ mob_overlay_state = "cyan_sparkles"
+ stacks = MODIFIER_STACK_FORBID
+ on_created_text = "The glowing sap seethes and bubbles in your wounds, tingling and stinging."
+ on_expired_text = "The last of the sap in your wounds fizzles away."
+
+/datum/modifier/sifsap_salve/tick()
+
+ if(holder.stat == DEAD || holder.isSynthetic())
+ expire()
+
+ if(istype(holder, /mob/living/simple_mob/animal/sif))
+
+ var/mob/living/simple_mob/animal/sif/critter = holder
+ if(critter.health >= (critter.getMaxHealth() * critter.sap_heal_threshold))
+ return
+
+ if(holder.resting)
+ if(istype(holder.loc, /obj/structure/animal_den))
+ holder.adjustBruteLoss(-3)
+ holder.adjustFireLoss(-3)
+ holder.adjustToxLoss(-2)
+ else
+ holder.adjustBruteLoss(-2)
+ holder.adjustFireLoss(-2)
+ holder.adjustToxLoss(-1)
+ else
+ holder.adjustBruteLoss(-1)
+ holder.adjustFireLoss(-1)
+
+/obj/item/projectile/drake_spit
+ name = "drake spit"
+ icon_state = "ice_1"
+ damage = 0
+ embed_chance = 0
+ damage_type = BRUTE
+ muzzle_type = null
+ hud_state = "monkey"
+ combustion = FALSE
+ stun = 3
+ weaken = 3
+ eyeblur = 5
+ fire_sound = 'sound/effects/splat.ogg'
+
+/obj/item/projectile/drake_spit/weak
+ stun = 0
+ weaken = 0
+ eyeblur = 2
+
+/datum/category_item/catalogue/fauna/grafadreka
+ name = "Sivian Fauna - Grafadreka"
+ desc = {"Classification: S tesca pabulator
+
+The reclusive grafadreka (Icelandic, lit. 'digging dragon'), also known as the snow drake, is a large reptillian pack predator similar in size and morphology to old Earth hyenas. They commonly dig shallow dens in dirt, snow or foliage, sometimes using them for concealment prior to an ambush. Biological cousins to the elusive kururak, they have heavy, low-slung bodies and powerful jaws suited to hunting land prey rather than fishing. Colonization and subsequent expansion have displaced many populations from their tundral territories into colder areas; as a result, their diet of Sivian prey animals has pivoted to a diet of giant spider meat.
+
+Grafadrekas are capable of exerting bite pressures in excess of 900 PSI, which allows them to crack bones or carapace when scavenging for food. While they share the hypercarnivorous metabolism of their cousins, they have developed a symbiotic relationship with the bacteria responsible for the bioluminescence of Sivian trees. This assists with digesting plant matter, and gives their pelts a distinctive and eerie glow.
+
+They have been observed to occasionally attack and kill colonists, generally when conditions are too poor to hunt their usual prey. Despite this, and despite their disposition being generally skittish and avoidant of colonists, some Sivian communities hold that they have been observed to guide or protect lost travellers.
+
+Field studies suggest analytical abilities on par with some species of cepholapods, but their symbiotic physiology rapidly fails in captivity, making laboratory testing difficult. Their inability to make use of tools or form wider social groups beyond a handful of individuals has been hypothesised to prevent the expression of more complex social behaviors."}
+ value = CATALOGUER_REWARD_HARD
+
+/decl/mob_organ_names/grafadreka
+ hit_zones = list(
+ "head",
+ "chest",
+ "left foreleg",
+ "right foreleg",
+ "left hind leg",
+ "right hind leg",
+ "face spines",
+ "body spines",
+ "tail spines",
+ "tail"
+ )
+
+/decl/emote/audible/drake_howl
+ key = "dhowl"
+ emote_message_3p = "lifts USER_THEIR head up and gives an eerie howl."
+ emote_sound = 'sound/effects/drakehowl_close.ogg'
+ broadcast_sound ='sound/effects/drakehowl_far.ogg'
+ emote_cooldown = 20 SECONDS
+ broadcast_distance = 90
+
+/decl/emote/audible/drake_howl/broadcast_emote_to(var/send_sound, var/mob/target, var/direction)
+ if((. = ..()))
+ to_chat(target, SPAN_NOTICE("You hear an eerie howl from somewhere to the [dir2text(direction)]."))
+
+/mob/living/simple_mob/animal/sif/grafadreka/get_available_emotes()
+ if(!is_baby)
+ return global._default_mob_emotes | /decl/emote/audible/drake_howl
+ return global._default_mob_emotes
+
+// Overriding this to handle sitting.
+/mob/living/simple_mob/animal/sif/grafadreka/lay_down()
+ . = ..()
+ if(!resting && sitting)
+ sitting = FALSE
+ update_icon()
+
+/mob/living/simple_mob/animal/sif/grafadreka/verb/sit_down()
+ set name = "Sit Down"
+ set category = "IC"
+
+ if(sitting)
+ resting = FALSE
+ sitting = FALSE
+ else
+ resting = TRUE
+ sitting = TRUE
+
+ to_chat(src, SPAN_NOTICE("You are now [sitting ? "sitting" : "getting up"]."))
+ update_canmove()
+ update_icon()
+
+/mob/living/simple_mob/animal/sif/grafadreka
+ name = "grafadreka"
+ desc = "A large, sleek snow drake with heavy claws, powerful jaws and many pale spines along its body."
+ player_msg = "You are a large Sivian pack predator in symbiosis with the local bioluminescent bacteria. You can eat glowing \
+ tree fruit to fuel your ranged spitting attack and poisonous bite (on harm intent), as well as healing saliva \
+ (on help intent).
There are humans moving through your territory; whether you help them get home safely, or treat them as a snack, \
+ is up to you."
+ color = "#608894"
+ icon = 'icons/mob/drake_adult.dmi'
+ catalogue_data = list(/datum/category_item/catalogue/fauna/grafadreka)
+ icon_state = "doggo"
+ icon_living = "doggo"
+ icon_dead = "doggo_lying"
+ icon_rest = "doggo_lying"
+ projectileverb = "spits"
+ friendly = list("headbutts", "grooms", "play-bites", "rubs against")
+ bitesize = 10 // chomp
+ gender = NEUTER
+
+ has_langs = list("Drake")
+
+ see_in_dark = 8 // on par with Taj
+
+ tt_desc = "S tesca pabulator"
+ faction = "grafadreka"
+
+ mob_size = MOB_LARGE
+ mob_bump_flag = SIMPLE_ANIMAL
+ mob_swap_flags = SIMPLE_ANIMAL
+ mob_push_flags = SIMPLE_ANIMAL
+
+ maxHealth = 150
+ health = 150
+ movement_cooldown = 2
+ base_attack_cooldown = 1 SECOND
+
+ organ_names = /decl/mob_organ_names/grafadreka
+ say_list_type = /datum/say_list/grafadreka
+ ai_holder_type = /datum/ai_holder/simple_mob/intentional/grafadreka
+
+ scavenger = TRUE
+ burrower = TRUE
+
+ projectilesound = 'sound/effects/splat.ogg'
+ projectiletype = /obj/item/projectile/drake_spit
+
+ // Claw attacks.
+ attack_sharp = TRUE
+ melee_damage_lower = 8
+ melee_damage_upper = 18
+ attack_armor_pen = 15
+
+ attack_sound = 'sound/weapons/slice.ogg'
+
+ tame_items = list(
+ /obj/item/reagent_containers/food/snacks/siffruit = 20,
+ /obj/item/reagent_containers/food/snacks/grown/sif/sifpod = 10,
+ /obj/item/reagent_containers/food/snacks/xenomeat/spidermeat = 20,
+ /obj/item/reagent_containers/food/snacks/meat = 10
+ )
+
+ // Attack strings for swapping.
+ attacktext = null
+ var/static/list/claw_attacktext = list("slashed", "clawed", "swiped", "gouged")
+ var/static/list/bite_attacktext = list("savaged", "bitten", "mauled")
+
+ // Bite attacks.
+ var/bite_melee_damage_lower = 30
+ var/bite_melee_damage_upper = 40
+ var/bite_attack_armor_pen = 60
+ var/const/bite_attack_sound = 'sound/weapons/bite.ogg'
+
+ // Used to avoid setting vars every single attack.
+ var/attacking_with_claws = TRUE
+
+ // Set during initialize and used to generate overlays.
+ var/tmp/current_icon_state // used to track our 'actual' icon state due to overlay nonsense in update_icon
+ var/tmp/fur_colour
+ var/tmp/claw_colour
+ var/tmp/glow_colour
+ var/tmp/base_colour
+ var/tmp/eye_colour
+
+ var/offset_compiled_icon = -16
+ var/is_baby = FALSE
+ var/sitting = FALSE
+ var/next_spit = 0
+ var/spit_cooldown = 8 SECONDS
+ var/next_leader_check = 0
+ var/charisma = 0 // A score used to determine pack leader.
+ var/stored_sap = 0
+ var/max_stored_sap = 60
+ var/attacked_by_neutral = FALSE
+
+ var/list/original_armor
+
+var/global/list/wounds_being_tended_by_drakes = list()
+/mob/living/simple_mob/animal/sif/grafadreka/proc/can_tend_wounds(var/mob/living/friend)
+
+ // We can't heal robots.
+ if(friend.isSynthetic())
+ return FALSE
+
+ // Check if someone else is looking after them already.
+ if(global.wounds_being_tended_by_drakes["\ref[friend]"] > world.time)
+ return FALSE
+
+ // Humans need to have a bleeding external organ to qualify.
+ if(ishuman(friend))
+ var/mob/living/carbon/human/H = friend
+ for(var/obj/item/organ/external/E in H.bad_external_organs)
+ if(E.status & ORGAN_BLEEDING)
+ return TRUE
+ return FALSE
+
+ // Sif animals need to be able to regenerate past their current HP value.
+ if(istype(friend, /mob/living/simple_mob/animal/sif))
+ var/mob/living/simple_mob/animal/sif/critter = friend
+ return critter.health < (critter.getMaxHealth() * critter.sap_heal_threshold)
+
+ // Other animals just need to be injured.
+ return (friend.health < friend.maxHealth)
+
+/mob/living/simple_mob/animal/sif/grafadreka/Initialize()
+
+ charisma = rand(5, 15)
+ stored_sap = rand(20, 30)
+ nutrition = rand(400,500)
+
+ if(gender == NEUTER)
+ gender = pick(MALE, FEMALE)
+ attacktext = claw_attacktext.Copy()
+
+ setup_colours()
+ create_reagents(50)
+
+ . = ..()
+
+ original_armor = armor
+ update_icon()
+
+/mob/living/simple_mob/animal/sif/grafadreka/examine(var/mob/living/user)
+ . = ..()
+ if(istype(user, /mob/living/simple_mob/animal/sif/grafadreka) || isobserver(user))
+ var/datum/gender/G = gender_datums[get_visible_gender()]
+ if(stored_sap >= 20)
+ . += SPAN_NOTICE("[G.His] sap reserves are high.")
+ else if(stored_sap >= 10)
+ . += SPAN_WARNING("[G.His] sap reserves are running low.")
+ else
+ . += SPAN_DANGER("[G.His] sap reserves are depleted.")
+
+/mob/living/simple_mob/animal/sif/grafadreka/can_projectile_attack(var/atom/A)
+ if(a_intent != I_HURT || world.time < next_spit)
+ return FALSE
+ if(!has_sap(2))
+ to_chat(src, SPAN_WARNING("You have no sap to spit!"))
+ return FALSE
+ return ..()
+
+// Checking this in the proc itself as AI doesn't seem to care about ranged attack cooldowns.
+/mob/living/simple_mob/animal/sif/grafadreka/shoot_target(atom/A)
+ if(world.time < next_spit || !has_sap(2))
+ return FALSE
+ . = ..()
+ if(.)
+ next_spit = world.time + spit_cooldown
+ setMoveCooldown(1 SECOND)
+ spend_sap(2)
+
+/mob/living/simple_mob/animal/sif/grafadreka/get_dietary_food_modifier(var/datum/reagent/nutriment/food)
+ if(food.allergen_type & ALLERGEN_MEAT)
+ return ..()
+ return 0.25 // Quarter nutrition from non-meat.
+
+/mob/living/simple_mob/animal/sif/grafadreka/handle_reagent_transfer(var/datum/reagents/holder, var/amount = 1, var/chem_type = CHEM_BLOOD, var/multiplier = 1, var/copy = 0)
+ return holder.trans_to_holder(reagents, amount, multiplier, copy)
+
+/mob/living/simple_mob/animal/sif/grafadreka/Life()
+ . = ..()
+
+ if(stat == CONSCIOUS)
+
+ // Don't make clientless drakes lose nutrition or they'll all go feral.
+ if(!resting && client)
+ remove_nutrition(0.3)
+
+ // Very slowly regenerate enough sap to defend ourselves. spit is 2 sap,
+ // spit cooldown is 8s, life is 2s, so this is one free spit per 12 seconds.
+ if(stored_sap < 10)
+ add_sap(0.35)
+
+ // Process food and sap chems.
+ if(reagents?.total_volume)
+ for(var/datum/reagent/chem in reagents.reagent_list)
+ var/removed = clamp(chem.ingest_met, REM, chem.volume)
+ chem.affect_animal(src, removed)
+ reagents.remove_reagent(chem.id, removed)
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/has_sap(var/amt)
+ return stored_sap >= amt
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/add_sap(var/amt)
+ stored_sap = clamp(round(stored_sap + amt, 0.01), 0, max_stored_sap)
+ update_icon()
+ return TRUE
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/spend_sap(var/amt)
+ if(has_sap(amt))
+ stored_sap = clamp(round(stored_sap - amt, 0.01), 0, max_stored_sap)
+ update_icon()
+ return TRUE
+ return FALSE
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/setup_colours()
+
+ var/static/list/fur_colours = list(COLOR_SILVER, COLOR_WHITE, COLOR_GREEN_GRAY, COLOR_PALE_RED_GRAY, COLOR_BLUE_GRAY)
+ var/static/list/claw_colours = list(COLOR_GRAY, COLOR_SILVER, COLOR_WHITE, COLOR_GRAY15, COLOR_GRAY20, COLOR_GRAY40, COLOR_GRAY80)
+ var/static/list/glow_colours = list(COLOR_BLUE_LIGHT, COLOR_LIGHT_CYAN, COLOR_CYAN, COLOR_CYAN_BLUE)
+ var/static/list/base_colours = list("#608894", "#436974", "#7fa3ae")
+ var/static/list/eye_colours = list(COLOR_WHITE, COLOR_SILVER)
+
+ if(!glow_colour)
+ glow_colour = pick(glow_colours)
+ if(!fur_colour)
+ fur_colour = pick(fur_colours)
+ if(!claw_colour)
+ claw_colour = pick(claw_colours)
+ if(!base_colour)
+ base_colour = pick(base_colours)
+ if(!eye_colour)
+ eye_colour = pick(eye_colours)
+
+/mob/living/simple_mob/animal/sif/grafadreka/movement_delay(oldloc, direct)
+ . = ..()
+ if(istype(loc, /turf/space))
+ return
+ var/health_slowdown_threshold = round(maxHealth * 0.65)
+ if(health < health_slowdown_threshold)
+ . += round(5 * (1-(health / health_slowdown_threshold)), 0.1)
+ var/nut_slowdown_threshold = round(max_nutrition * 0.65)
+ if(nutrition < nut_slowdown_threshold)
+ . += round(5 * (1-(nutrition / nut_slowdown_threshold)), 0.1)
+
+/mob/living/simple_mob/animal/sif/grafadreka/update_icon()
+
+ . = ..()
+
+ if(sitting && stat == CONSCIOUS)
+ icon_state = "[initial(icon_state)]_sitting"
+
+ var/list/add_images = list()
+ var/image/I = image(icon, "[icon_state]")
+ I.color = base_colour
+ add_images += I
+
+ I = image(icon, "[icon_state]-fur")
+ I.color = fur_colour
+ add_images += I
+
+ I = image(icon, "[icon_state]-claws")
+ I.color = claw_colour
+ add_images += I
+
+ if(stat == CONSCIOUS && !sleeping)
+ I = image(icon, "[icon_state]-eye_overlay")
+ I.color = eye_colour
+ add_images += I
+
+ if(stat != DEAD)
+ var/glow = add_glow()
+ if(glow)
+ add_images += glow
+
+ for(var/image/adding in add_images)
+ adding.appearance_flags |= (RESET_COLOR|PIXEL_SCALE|KEEP_APART)
+ if(offset_compiled_icon)
+ adding.pixel_x = offset_compiled_icon // Offset here so that things like modifiers, runechat text, etc. are centered
+ add_overlay(adding)
+
+ // We do this last so the default mob icon_state can be used for the overlays.
+ current_icon_state = icon_state
+ icon_state = "blank"
+ color = COLOR_WHITE // Due to KEEP_TOGETHER etc. overlays ignore RESET_COLOR.
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/add_glow()
+ var/image/I = image(icon, "[icon_state]-glow")
+ I.color = glow_colour
+ I.plane = PLANE_LIGHTING_ABOVE
+ I.alpha = 35 + round(220 * clamp(stored_sap/max_stored_sap, 0, 1))
+ return I
+
+/mob/living/simple_mob/animal/sif/grafadreka/get_eye_color()
+ return eye_colour
+
+/mob/living/simple_mob/animal/sif/grafadreka/do_tame(var/obj/O, var/mob/user)
+ . = ..()
+ attacked_by_neutral = FALSE
+
+/mob/living/simple_mob/animal/sif/grafadreka/handle_special()
+ ..()
+ if(client || world.time >= next_leader_check)
+ next_leader_check = world.time + (60 SECONDS)
+ check_leader_status()
+
+/mob/living/simple_mob/animal/sif/grafadreka/do_help_interaction(atom/A)
+
+ if(isliving(A))
+
+ var/mob/living/friend = A
+ if(friend.stat == DEAD)
+ if(friend == src)
+ to_chat(src, SPAN_WARNING("\The [friend] is dead; tending their wounds is pointless."))
+ else
+ return ..()
+ return TRUE
+
+ if(!can_tend_wounds(friend))
+ if(friend == src)
+ if(health == maxHealth)
+ to_chat(src, SPAN_WARNING("You are unwounded."))
+ else
+ to_chat(src, SPAN_WARNING("You cannot tend any of your wounds."))
+ else
+ if(friend.health == friend.maxHealth)
+ return ..()
+ to_chat(src, SPAN_WARNING("You cannot tend any of \the [friend]'s wounds."))
+ return TRUE
+
+ if(friend.has_modifier_of_type(/datum/modifier/sifsap_salve))
+ if(friend == src)
+ to_chat(src, SPAN_WARNING("You have already cleaned your wounds."))
+ else
+ return ..()
+ return TRUE
+
+ if(!has_sap(10))
+ if(friend == src)
+ to_chat(src, SPAN_WARNING("You don't have enough sap to clean your wounds."))
+ else
+ return ..()
+ return TRUE
+
+ if(friend == src)
+ visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over their wounds."))
+ else
+ visible_message(SPAN_NOTICE("\The [src] begins to drool a blue-glowing liquid, which they start slathering over \the [friend]'s wounds."))
+
+ playsound(src, 'sound/effects/ointment.ogg', 25)
+
+ var/friend_ref = "\ref[friend]"
+ global.wounds_being_tended_by_drakes[friend_ref] = world.time + (8 SECONDS)
+ set_AI_busy(TRUE)
+
+ if(!do_after(src, 8 SECONDS, friend) || QDELETED(friend) || friend.has_modifier_of_type(/datum/modifier/sifsap_salve) || incapacitated() || !spend_sap(10))
+ global.wounds_being_tended_by_drakes -= friend_ref
+ set_AI_busy(FALSE)
+ return TRUE
+
+ global.wounds_being_tended_by_drakes -= friend_ref
+ set_AI_busy(FALSE)
+
+ if(friend == src)
+ visible_message(SPAN_NOTICE("\The [src] finishes licking at their wounds."))
+ else
+ visible_message(SPAN_NOTICE("\The [src] finishes licking at \the [friend]'s wounds."))
+ playsound(src, 'sound/effects/ointment.ogg', 25)
+
+ // Sivian animals get a heal buff from the modifier, others just
+ // get it to stop friendly drakes constantly licking their wounds.
+ friend.add_modifier(/datum/modifier/sifsap_salve, 60 SECONDS)
+ // Human wounds are closed, but they get sifsap via open wounds.
+ if(ishuman(friend))
+ var/mob/living/carbon/human/H = friend
+ for(var/obj/item/organ/external/E in H.organs)
+ if(E.status & ORGAN_BLEEDING)
+ E.organ_clamp()
+ H.bloodstr.add_reagent("sifsap", rand(1,2))
+ for(var/datum/wound/W in E.wounds)
+ W.salve()
+ W.disinfect()
+
+ // Everyone else is just poisoned.
+ else if(!istype(friend, /mob/living/simple_mob/animal/sif))
+ friend.adjustToxLoss(rand(10,20))
+ return TRUE
+
+ return ..()
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/get_pack_leader()
+ var/pack = FALSE
+ var/mob/living/simple_mob/animal/sif/grafadreka/leader
+ if(!is_baby)
+ leader = src
+ for(var/mob/living/simple_mob/animal/sif/grafadreka/follower in hearers(7, loc))
+ if(follower == src || follower.is_baby || follower.stat == DEAD || follower.faction != faction)
+ continue
+ pack = TRUE
+ if(!leader || follower.charisma > leader.charisma)
+ leader = follower
+ if(pack)
+ return leader
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/check_leader_status()
+ var/mob/living/simple_mob/animal/sif/grafadreka/leader = get_pack_leader()
+ if(src == leader)
+ add_modifier(/datum/modifier/ace, 60 SECONDS)
+ else
+ remove_modifiers_of_type(/datum/modifier/ace)
+
+/mob/living/simple_mob/animal/sif/grafadreka/Stat()
+ . = ..()
+ if(statpanel("Status"))
+ stat("Nutrition:", "[nutrition]/[max_nutrition]")
+ stat("Stored sap:", "[stored_sap]/[max_stored_sap]")
+
+/mob/living/simple_mob/animal/sif/grafadreka/proc/can_bite(var/mob/living/M)
+ return istype(M) && (M.lying || M.confused || M.incapacitated())
+
+/mob/living/simple_mob/animal/sif/grafadreka/apply_bonus_melee_damage(atom/A, damage_amount)
+ // Melee attack on incapacitated or prone enemies bites instead of slashing
+ var/last_attack_was_claws = attacking_with_claws
+ attacking_with_claws = !can_bite(A)
+
+ if(last_attack_was_claws != attacking_with_claws)
+ if(attacking_with_claws) // Use claws.
+ attack_armor_pen = initial(attack_armor_pen)
+ attack_sound = initial(attack_sound)
+ attacktext = claw_attacktext.Copy()
+ else // Use ur teef
+ damage_amount = max(damage_amount, rand(bite_melee_damage_lower, bite_melee_damage_upper))
+ attack_armor_pen = bite_attack_armor_pen
+ attack_sound = bite_attack_sound
+ attacktext = bite_attacktext.Copy()
+ . = ..()
+
+// Eating sifsap makes bites toxic and changes our glow intensity.
+/mob/living/simple_mob/animal/sif/grafadreka/apply_attack(atom/A, damage_to_do)
+ var/tox_damage = 0
+ if(!attacking_with_claws && isliving(A) && has_sap(5))
+ tox_damage = rand(5,15)
+ . = ..()
+ if(. && tox_damage && spend_sap(5))
+ var/mob/living/M = A
+ M.adjustToxLoss(tox_damage)
+
+/mob/living/simple_mob/animal/sif/grafadreka/verb/rally_pack()
+ set name = "Rally Pack"
+ set desc = "Tries to command your fellow pack members to follow you."
+ set category = "Abilities"
+
+ if(!has_modifier_of_type(/datum/modifier/ace))
+ to_chat(src, SPAN_WARNING("You aren't the pack leader! Sit down!"))
+ return
+
+ audible_message("\The [src] barks loudly and rattles its neck spines.")
+ for(var/mob/living/simple_mob/animal/sif/grafadreka/drake in hearers(world.view * 3, src))
+ if(drake == src || drake.faction != faction)
+ continue
+ if(drake.client)
+ to_chat(drake, SPAN_NOTICE("The pack leader wishes for you to follow them."))
+ else if(drake.ai_holder)
+ drake.ai_holder.set_follow(src)
+
+/mob/living/simple_mob/animal/sif/grafadreka/has_appetite()
+ return reagents && abs(reagents.total_volume - reagents.maximum_volume) >= 10
+
+/mob/living/simple_mob/animal/sif/grafadreka/Login()
+ . = ..()
+ charisma = (client && !is_baby) ? INFINITY : 0
+
+/mob/living/simple_mob/animal/sif/grafadreka/Logout()
+ . = ..()
+ if(!client)
+ charisma = rand(5, 15)
+
+/datum/say_list/grafadreka
+ speak = list("Chff!","Skhh.", "Rrrss...")
+ emote_see = list("scratches its ears","grooms its spines", "sways its tail", "claws at the ground")
+ emote_hear = list("hisses", "rattles", "rasps", "barks")
+
+/obj/structure/animal_den/ghost_join/grafadreka
+ name = "drake den"
+ critter = /mob/living/simple_mob/animal/sif/grafadreka
+
+/obj/structure/animal_den/ghost_join/grafadreka_hatchling
+ name = "drake hatchling den"
+ critter = /mob/living/simple_mob/animal/sif/grafadreka/hatchling
+
+// Subtypes!
+/mob/living/simple_mob/animal/sif/grafadreka/rainbow/setup_colours()
+ glow_colour = get_random_colour(TRUE)
+ fur_colour = get_random_colour(TRUE)
+ claw_colour = get_random_colour(TRUE)
+ base_colour = get_random_colour(TRUE)
+ eye_colour = get_random_colour(TRUE)
+ ..()
+
+/mob/living/simple_mob/animal/sif/grafadreka/hatchling
+ name = "grafadreka hatchling"
+ icon = 'icons/mob/drake_baby.dmi'
+ mob_size = MOB_SMALL
+ desc = "An immature snow drake, not long out of the shell."
+ is_baby = TRUE
+ offset_compiled_icon = null
+
+ melee_damage_lower = 3
+ melee_damage_upper = 5
+ attack_armor_pen = 2
+ bite_melee_damage_lower = 5
+ bite_melee_damage_upper = 10
+ bite_attack_armor_pen = 16
+
+ projectiletype = /obj/item/projectile/drake_spit/weak
+ maxHealth = 60
+ health = 60
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm b/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm
index 4509634757..07231caf07 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/bigdragon.dm
@@ -5,7 +5,7 @@ Scour its code if you dare.
Here's a summary, however.
-This is a 128x64px mob with sprites drawn by Przyjaciel (thanks mate) and some codersprites.
+This is a 128x92px mob with sprites drawn by Przyjaciel (thanks mate) and some codersprites.
The bigdragon is an 800 health hostile boss mob with three special attacks.
The first (disarm intent) is a charge attack that activates when the target is >5 tiles away and requires line of sight.
@@ -62,6 +62,7 @@ I think I covered everything.
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
old_x = -48
old_y = 0
+ vis_height = 92
melee_damage_lower = 35
melee_damage_upper = 25
melee_miss_chance = 0
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm b/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm
index 41098bc65c..e814e3e93a 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/greatwolf.dm
@@ -14,6 +14,7 @@
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
old_x = -48
old_y = 0
+ vis_height = 92
melee_damage_lower = 20
melee_damage_upper = 15
friendly = list("nudges", "sniffs on", "rumbles softly at", "slobberlicks")
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm
index 2b4350fd2e..5427b978c9 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander.dm
@@ -18,6 +18,7 @@
default_pixel_x = -48
pixel_x = -48
pixel_y = 0
+ vis_height = 92
response_help = "pats"
response_disarm = "shoves"
response_harm = "bops"
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm
index 4f7e9e6bed..d8fb0f212f 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/leopardmander_ch.dm
@@ -12,6 +12,7 @@
meat_type = /obj/item/weapon/reagent_containers/food/snacks/meat
old_x = -48
old_y = 0
+ vis_height = 92
melee_damage_lower = 10
melee_damage_upper = 25
friendly = list("nudges", "sniffs on", "rumbles softly at", "slobberlicks")
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index c36a48f13f..0a4e270ed6 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -408,7 +408,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT)
return // Can't talk in deadchat if you can't see it.
for(var/mob/M in player_list)
- if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights)) && M.is_preference_enabled(/datum/client_preference/show_dsay))
+ if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights && M.is_preference_enabled(/datum/client_preference/holder/show_staff_dsay))) && M.is_preference_enabled(/datum/client_preference/show_dsay))
var/follow
var/lname
if(M.forbid_seeing_deadchat && !M.client.holder)
@@ -438,7 +438,7 @@ var/list/intents = list(I_HELP,I_DISARM,I_GRAB,I_HURT)
/proc/say_dead_object(var/message, var/obj/subject = null)
for(var/mob/M in player_list)
- if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights)) && M.is_preference_enabled(/datum/client_preference/show_dsay))
+ if(M.client && ((!istype(M, /mob/new_player) && M.stat == DEAD) || (M.client.holder && M.client.holder.rights && M.is_preference_enabled(/datum/client_preference/holder/show_staff_dsay))) && M.is_preference_enabled(/datum/client_preference/show_dsay))
var/follow
var/lname = "Game Master"
if(M.forbid_seeing_deadchat && !M.client.holder)
diff --git a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm
index 4f267fe7de..cd63d811ef 100644
--- a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm
+++ b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm
@@ -1185,6 +1185,13 @@
color_blend_mode = ICON_MULTIPLY
lower_layer_dirs = list(SOUTH, WEST)
+/datum/sprite_accessory/tail/shark_finless
+ name = "shark tail, finless (colorable)"
+ desc = ""
+ icon_state = "sharktail_finless"
+ do_colouration = 1
+ color_blend_mode = ICON_MULTIPLY
+
/datum/sprite_accessory/tail/tentacle
name = "Tentacle, colorable (vwag)"
desc = ""
diff --git a/code/modules/organs/internal/brain.dm b/code/modules/organs/internal/brain.dm
index 11ce2fbb45..60498402ef 100644
--- a/code/modules/organs/internal/brain.dm
+++ b/code/modules/organs/internal/brain.dm
@@ -237,7 +237,7 @@ GLOBAL_LIST_BOILERPLATE(all_brain_organs, /obj/item/organ/internal/brain)
return 0
for(var/modifier_type in R.genetic_modifiers) //Can't be revived. Probably won't happen...?
- if(istype(modifier_type, /datum/modifier/no_clone))
+ if(ispath(modifier_type, /datum/modifier/no_clone))
return 0
var/mob/living/carbon/human/H = new /mob/living/carbon/human(get_turf(src), R.dna.species)
diff --git a/code/modules/organs/organ.dm b/code/modules/organs/organ.dm
index c91fa50481..718234893e 100644
--- a/code/modules/organs/organ.dm
+++ b/code/modules/organs/organ.dm
@@ -368,6 +368,9 @@ var/list/organ_cache = list()
robotize()
/obj/item/organ/emp_act(severity)
+ for(var/obj/O as anything in src.contents)
+ O.emp_act(severity)
+
if(!(robotic >= ORGAN_ASSISTED))
return
for(var/i = 1; i <= robotic; i++)
diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm
index 63dc8c6666..912889c5f6 100644
--- a/code/modules/organs/organ_external.dm
+++ b/code/modules/organs/organ_external.dm
@@ -115,6 +115,9 @@
return ..()
/obj/item/organ/external/emp_act(severity)
+ for(var/obj/O as anything in src.contents)
+ O.emp_act(severity)
+
if(!(robotic >= ORGAN_ROBOT))
return
var/burn_damage = 0
@@ -595,8 +598,6 @@ This function completely restores a damaged organ to perfect condition.
return 1
else
last_dam = brute_dam + burn_dam
- if (number_wounds != 0)
- return 1
if(germ_level)
return 1
return 0
@@ -733,8 +734,8 @@ Note that amputating the affected organ does in fact remove the infection from t
return
for(var/datum/wound/W in wounds)
- // wounds used to be able to disappear after 10 minutes at the earliest, for now just remove them as soon as there is no damage
- if(W.damage <= 0)
+ // wounds can disappear after 10 minutes at the earliest
+ if(W.damage <= 0 && W.created + 10 MINUTES <= world.time)
wounds -= W
continue
// let the GC handle the deletion of the wound
diff --git a/code/modules/organs/pain.dm b/code/modules/organs/pain.dm
index 6cf236bc19..b65b3fc2fb 100644
--- a/code/modules/organs/pain.dm
+++ b/code/modules/organs/pain.dm
@@ -9,7 +9,7 @@
// power decides how much painkillers will stop the message
// force means it ignores anti-spam timer
/mob/living/carbon/proc/custom_pain(message, power, force)
- if(!message || stat || !can_feel_pain() || chem_effects[CE_PAINKILLER] > power)
+ if((!message || stat || !can_feel_pain() || chem_effects[CE_PAINKILLER] > power) && !synth_cosmetic_pain)
return 0
message = "[message]"
if(power >= 50)
@@ -25,7 +25,7 @@
if(stat)
return
- if(!can_feel_pain())
+ if(!can_feel_pain() && !synth_cosmetic_pain)
return
if(world.time < next_pain_time)
@@ -33,7 +33,7 @@
var/maxdam = 0
var/obj/item/organ/external/damaged_organ = null
for(var/obj/item/organ/external/E in organs)
- if(!E.organ_can_feel_pain()) continue
+ if(!E.organ_can_feel_pain() && !synth_cosmetic_pain) continue
var/dam = E.get_damage()
// make the choice of the organ depend on damage,
// but also sometimes use one of the less damaged ones
diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm
index 3fd25a83e4..d4c8a8bf59 100644
--- a/code/modules/power/apc.dm
+++ b/code/modules/power/apc.dm
@@ -143,7 +143,7 @@ GLOBAL_LIST_EMPTY(apcs)
var/updating_icon = 0
var/global/list/status_overlays_environ
var/alarms_hidden = FALSE //If power alarms from this APC are visible on consoles
-
+
var/nightshift_lights = FALSE
var/nightshift_setting = NIGHTSHIFT_AUTO
var/last_nightshift_switch = 0
@@ -198,7 +198,7 @@ GLOBAL_LIST_EMPTY(apcs)
if(!pixel_x && !pixel_y)
offset_apc()
-
+
if(building)
area = get_area(src)
area.apc = src
@@ -1360,6 +1360,7 @@ GLOBAL_LIST_EMPTY(apcs)
for(var/obj/machinery/light/L in area)
L.nightshift_mode(new_state)
+ L.update() //For some reason it gets hung up on updating the overlay for the light fixture somewhere down the line. This fixes it.
CHECK_TICK
#undef APC_UPDATE_ICON_COOLDOWN
diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm
index 52e412b0ee..e1b41b602e 100644
--- a/code/modules/power/lighting.dm
+++ b/code/modules/power/lighting.dm
@@ -215,6 +215,7 @@ var/global/list/light_type_cache = list()
idle_power_usage = 2
active_power_usage = 10
power_channel = LIGHT //Lights are calc'd via area so they dont need to be in the machine list
+ var/obj/item/weapon/light/installed_light //What light is currently in the socket! Updated in new()
var/on = 0 // 1 if on, 0 if off
var/brightness_range
var/brightness_power
@@ -344,9 +345,10 @@ var/global/list/light_type_cache = list()
construct.transfer_fingerprints_to(src)
set_dir(construct.dir)
else
+ installed_light = new light_type(src)
if(start_with_cell && !no_emergency)
cell = new/obj/item/weapon/cell/emergency_light(src)
- var/obj/item/weapon/light/L = get_light_type_instance(light_type)
+ var/obj/item/weapon/light/L = get_light_type_instance(light_type) //This is fine, but old code.
update_from_bulb(L)
if(prob(L.broken_chance))
broken(1)
@@ -439,7 +441,7 @@ var/global/list/light_type_cache = list()
return
current_alert = null
- var/obj/item/weapon/light/L = get_light_type_instance(light_type)
+ var/obj/item/weapon/light/L = installed_light //This ensures any special bulbs will stay special!
if(L)
update_from_bulb(L)
@@ -464,11 +466,12 @@ var/global/list/light_type_cache = list()
var/correct_range = nightshift_enabled ? brightness_range_ns : brightness_range
var/correct_power = nightshift_enabled ? brightness_power_ns : brightness_power
var/correct_color = nightshift_enabled ? brightness_color_ns : brightness_color
+ var/correct_overlay = nightshift_enabled ? brightness_color_ns : brightness_color //Gives lights the correct overlay if NS is enabled.
if(current_alert) //Oh no, we're on fire! Or the atmos is bad! Let's change the color
correct_range = brightness_range
correct_power = brightness_power
correct_color = brightness_color
- if(light_range != correct_range || light_power != correct_power || light_color != correct_color)
+ if(light_range != correct_range || light_power != correct_power || light_color != correct_color || overlay_color != correct_overlay)
if(!auto_flicker)
switchcount++
if(rigged)
@@ -487,6 +490,7 @@ var/global/list/light_type_cache = list()
else
update_use_power(USE_POWER_ACTIVE)
set_light(correct_range, correct_power, correct_color)
+ overlay_color = correct_overlay
if(cell?.charge < cell?.maxcharge)
START_PROCESSING(SSobj, src)
else if(has_emergency_power(LIGHT_EMERGENCY_POWER_USE) && !turned_off())
@@ -571,6 +575,7 @@ var/global/list/light_type_cache = list()
brightness_range = L.brightness_range
brightness_power = L.brightness_power
brightness_color = L.brightness_color
+ overlay_color = L.brightness_color
brightness_range_ns = L.nightshift_range
brightness_power_ns = L.nightshift_power
@@ -580,7 +585,8 @@ var/global/list/light_type_cache = list()
/obj/machinery/light/proc/insert_bulb(obj/item/weapon/light/L)
update_from_bulb(L)
- qdel(L)
+ installed_light = L
+ L.loc = src //Move it into the socket!
on = powered()
update()
@@ -593,16 +599,17 @@ var/global/list/light_type_cache = list()
explode()
/obj/machinery/light/proc/remove_bulb()
- . = new light_type(src.loc, src)
+ //. = new light_type(src.loc, src)
switchcount = 0
+ installed_light = null
status = LIGHT_EMPTY
update()
/obj/machinery/light/attackby(obj/item/W, mob/user)
//Light replacer code
- if(istype(W, /obj/item/device/lightreplacer))
+ if(istype(W, /obj/item/device/lightreplacer)) //These will never be modified, so it's fine to use old code.
var/obj/item/device/lightreplacer/LR = W
if(isliving(user))
var/mob/living/U = user
@@ -619,7 +626,9 @@ var/global/list/light_type_cache = list()
return
to_chat(user, "You insert [W].")
+ user.drop_item()
insert_bulb(W)
+ update() //Like other places, this is done later down the line but this is essential to updating the overlay when nightmode is involved. Again, I have no idea WHY.
src.add_fingerprint(user)
// attempt to break the light
@@ -724,6 +733,7 @@ var/global/list/light_type_cache = list()
if(cell.charge > 300) //it's meant to handle 120 W, ya doofus
visible_message("[src] short-circuits from too powerful of a power cell!")
status = LIGHT_BURNED
+ installed_light.status = status
return FALSE
cell.use(pwr)
set_light(brightness_range * bulb_emergency_brightness_mul, max(bulb_emergency_pow_min, bulb_emergency_pow_mul * (cell.charge / cell.maxcharge)), bulb_emergency_colour)
@@ -805,8 +815,11 @@ var/global/list/light_type_cache = list()
else
to_chat(user, "You remove the light [get_fitting_name()].")
- // create a light tube/bulb item and put it in the user's hand
- user.put_in_active_hand(remove_bulb()) //puts it in our active hand
+ //Let's actually put the real bulb in their hand.
+ installed_light.status = status //Update the bulb they're being given. If it's broken, the bulb should be as well!
+ user.put_in_active_hand(installed_light) //puts it in our active hand
+ installed_light.update_icon()
+ remove_bulb()
/obj/machinery/light/flamp/attack_hand(mob/user)
if(lamp_shade)
@@ -845,13 +858,17 @@ var/global/list/light_type_cache = list()
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
- status = LIGHT_BROKEN
+ status = LIGHT_BROKEN //This occasionally runtimes when it occurs midround after build mode spawns a broken light. No idea why.
+ installed_light.status = status
+ installed_light.update_icon()
update()
/obj/machinery/light/proc/fix()
if(status == LIGHT_OK)
return
status = LIGHT_OK
+ if(installed_light)
+ installed_light.status = LIGHT_OK
on = 1
update()
diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm
index e4d3254fda..9ccf421b57 100644
--- a/code/modules/power/singularity/particle_accelerator/particle_control.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm
@@ -184,8 +184,8 @@
/obj/machinery/particle_accelerator/control_box/proc/toggle_power()
active = !active
investigate_log("turned [active?"ON":"OFF"] by [usr ? usr.key : "outside forces"]","singulo")
- message_admins("PA Control Computer turned [active ?"ON":"OFF"] by [key_name(usr, usr.client)](?) in ([x],[y],[z] - JMP)",0,1)
- log_game("PACCEL([x],[y],[z]) [key_name(usr)] turned [active?"ON":"OFF"].")
+ message_admins("PA Control Computer turned [active ?"ON":"OFF"] by [usr ? key_name(usr, usr.client) : "outside forces"](?) in ([x],[y],[z] - JMP)",0,1)
+ log_game("PACCEL([x],[y],[z]) [usr ? key_name(usr, usr.client) : "outside forces"] turned [active?"ON":"OFF"].")
if(active)
update_use_power(USE_POWER_ACTIVE)
for(var/obj/structure/particle_accelerator/part in connected_parts)
diff --git a/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm b/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm
index 3b95676eee..22dd450169 100644
--- a/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm
+++ b/code/modules/projectiles/guns/energy/kinetic_accelerator_vr.dm
@@ -382,13 +382,6 @@
. = TRUE
if(src in KA.modkits) // Sanity check to prevent installing the same modkit twice thanks to occasional click/lag delays.
return FALSE
- // if(minebot_upgrade)
- // if(minebot_exclusive && !istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
- // to_chat(user, "The modkit you're trying to install is only rated for minebot use.")
- // return FALSE
- // else if(istype(KA.loc, /mob/living/simple_animal/hostile/mining_drone))
- // to_chat(user, "The modkit you're trying to install is not rated for minebot use.")
- // return FALSE
if(denied_type)
var/number_of_denied = 0
for(var/A in KA.get_modkits())
diff --git a/code/modules/rogueminer_vr/controller.dm b/code/modules/rogueminer_vr/controller.dm
index 4c6c710120..efff235f2e 100644
--- a/code/modules/rogueminer_vr/controller.dm
+++ b/code/modules/rogueminer_vr/controller.dm
@@ -197,6 +197,6 @@ var/datum/controller/rogue/rm_controller
rm_controller.dbg("RMC(pnz): Cleaning up oldest zone.")
spawn(0) //Detatch it so we can return the new zone for now.
var/datum/rogue/zonemaster/ZM_oldest = get_oldest_zone()
- ZM_oldest.clean_zone()
+ if(ZM_oldest) ZM_oldest.clean_zone()
return ZM_target
\ No newline at end of file
diff --git a/code/modules/tgui/states/notcontained.dm b/code/modules/tgui/states/notcontained.dm
index 01811d427f..56b789e3c0 100644
--- a/code/modules/tgui/states/notcontained.dm
+++ b/code/modules/tgui/states/notcontained.dm
@@ -21,6 +21,3 @@ GLOBAL_DATUM_INIT(tgui_notcontained_state, /datum/tgui_state/notcontained_state,
/mob/living/silicon/notcontained_can_use_tgui_topic(src_object)
return default_can_use_tgui_topic(src_object) // Silicons use default bevhavior.
-
-/mob/living/simple_animal/drone/notcontained_can_use_tgui_topic(src_object)
- return default_can_use_tgui_topic(src_object) // Drones use default bevhavior.
diff --git a/code/modules/vchat/html/vchat.html b/code/modules/vchat/html/vchat.html
index a3612d78e4..5b7c7b4f97 100644
--- a/code/modules/vchat/html/vchat.html
+++ b/code/modules/vchat/html/vchat.html
@@ -11,9 +11,9 @@
-
+
-
+
, or missing
. Bailing hydration and performing ' + - 'full client-side render.' - ); - } - } - // either not server-rendered, or hydration failed. - // create an empty node and replace it - oldVnode = emptyNodeAt(oldVnode); - } - - // replacing existing element - var oldElm = oldVnode.elm; - var parentElm = nodeOps.parentNode(oldElm); - - // create new node - createElm( - vnode, - insertedVnodeQueue, - // extremely rare edge case: do not insert if old element is in a - // leaving transition. Only happens when combining transition + - // keep-alive + HOCs. (#4590) - oldElm._leaveCb ? null : parentElm, - nodeOps.nextSibling(oldElm) - ); - - // update parent placeholder node element, recursively - if (isDef(vnode.parent)) { - var ancestor = vnode.parent; - var patchable = isPatchable(vnode); - while (ancestor) { - for (var i = 0; i < cbs.destroy.length; ++i) { - cbs.destroy[i](ancestor); - } - ancestor.elm = vnode.elm; - if (patchable) { - for (var i$1 = 0; i$1 < cbs.create.length; ++i$1) { - cbs.create[i$1](emptyNode, ancestor); - } - // #6513 - // invoke insert hooks that may have been merged by create hooks. - // e.g. for directives that uses the "inserted" hook. - var insert = ancestor.data.hook.insert; - if (insert.merged) { - // start at index 1 to avoid re-invoking component mounted hook - for (var i$2 = 1; i$2 < insert.fns.length; i$2++) { - insert.fns[i$2](); - } - } - } else { - registerRef(ancestor); - } - ancestor = ancestor.parent; - } - } - - // destroy old node - if (isDef(parentElm)) { - removeVnodes([oldVnode], 0, 0); - } else if (isDef(oldVnode.tag)) { - invokeDestroyHook(oldVnode); - } - } - } - - invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch); - return vnode.elm - } - } - - /* */ - - var directives = { - create: updateDirectives, - update: updateDirectives, - destroy: function unbindDirectives (vnode) { - updateDirectives(vnode, emptyNode); - } - }; - - function updateDirectives (oldVnode, vnode) { - if (oldVnode.data.directives || vnode.data.directives) { - _update(oldVnode, vnode); - } - } - - function _update (oldVnode, vnode) { - var isCreate = oldVnode === emptyNode; - var isDestroy = vnode === emptyNode; - var oldDirs = normalizeDirectives$1(oldVnode.data.directives, oldVnode.context); - var newDirs = normalizeDirectives$1(vnode.data.directives, vnode.context); - - var dirsWithInsert = []; - var dirsWithPostpatch = []; - - var key, oldDir, dir; - for (key in newDirs) { - oldDir = oldDirs[key]; - dir = newDirs[key]; - if (!oldDir) { - // new directive, bind - callHook$1(dir, 'bind', vnode, oldVnode); - if (dir.def && dir.def.inserted) { - dirsWithInsert.push(dir); - } - } else { - // existing directive, update - dir.oldValue = oldDir.value; - dir.oldArg = oldDir.arg; - callHook$1(dir, 'update', vnode, oldVnode); - if (dir.def && dir.def.componentUpdated) { - dirsWithPostpatch.push(dir); - } - } - } - - if (dirsWithInsert.length) { - var callInsert = function () { - for (var i = 0; i < dirsWithInsert.length; i++) { - callHook$1(dirsWithInsert[i], 'inserted', vnode, oldVnode); - } - }; - if (isCreate) { - mergeVNodeHook(vnode, 'insert', callInsert); - } else { - callInsert(); - } - } - - if (dirsWithPostpatch.length) { - mergeVNodeHook(vnode, 'postpatch', function () { - for (var i = 0; i < dirsWithPostpatch.length; i++) { - callHook$1(dirsWithPostpatch[i], 'componentUpdated', vnode, oldVnode); - } - }); - } - - if (!isCreate) { - for (key in oldDirs) { - if (!newDirs[key]) { - // no longer present, unbind - callHook$1(oldDirs[key], 'unbind', oldVnode, oldVnode, isDestroy); - } - } - } - } - - var emptyModifiers = Object.create(null); - - function normalizeDirectives$1 ( - dirs, - vm - ) { - var res = Object.create(null); - if (!dirs) { - // $flow-disable-line - return res - } - var i, dir; - for (i = 0; i < dirs.length; i++) { - dir = dirs[i]; - if (!dir.modifiers) { - // $flow-disable-line - dir.modifiers = emptyModifiers; - } - res[getRawDirName(dir)] = dir; - dir.def = resolveAsset(vm.$options, 'directives', dir.name, true); - } - // $flow-disable-line - return res - } - - function getRawDirName (dir) { - return dir.rawName || ((dir.name) + "." + (Object.keys(dir.modifiers || {}).join('.'))) - } - - function callHook$1 (dir, hook, vnode, oldVnode, isDestroy) { - var fn = dir.def && dir.def[hook]; - if (fn) { - try { - fn(vnode.elm, dir, vnode, oldVnode, isDestroy); - } catch (e) { - handleError(e, vnode.context, ("directive " + (dir.name) + " " + hook + " hook")); - } - } - } - - var baseModules = [ - ref, - directives - ]; - - /* */ - - function updateAttrs (oldVnode, vnode) { - var opts = vnode.componentOptions; - if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) { - return - } - if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) { - return - } - var key, cur, old; - var elm = vnode.elm; - var oldAttrs = oldVnode.data.attrs || {}; - var attrs = vnode.data.attrs || {}; - // clone observed objects, as the user probably wants to mutate it - if (isDef(attrs.__ob__)) { - attrs = vnode.data.attrs = extend({}, attrs); - } - - for (key in attrs) { - cur = attrs[key]; - old = oldAttrs[key]; - if (old !== cur) { - setAttr(elm, key, cur); - } - } - // #4391: in IE9, setting type can reset value for input[type=radio] - // #6666: IE/Edge forces progress value down to 1 before setting a max - /* istanbul ignore if */ - if ((isIE || isEdge) && attrs.value !== oldAttrs.value) { - setAttr(elm, 'value', attrs.value); - } - for (key in oldAttrs) { - if (isUndef(attrs[key])) { - if (isXlink(key)) { - elm.removeAttributeNS(xlinkNS, getXlinkProp(key)); - } else if (!isEnumeratedAttr(key)) { - elm.removeAttribute(key); - } - } - } - } - - function setAttr (el, key, value) { - if (el.tagName.indexOf('-') > -1) { - baseSetAttr(el, key, value); - } else if (isBooleanAttr(key)) { - // set attribute for blank value - // e.g. - if (isFalsyAttrValue(value)) { - el.removeAttribute(key); - } else { - // technically allowfullscreen is a boolean attribute for