diff --git a/_maps/RandomRuins/LavaRuins/lavaland_surface_random_ripley.dmm b/_maps/RandomRuins/LavaRuins/lavaland_surface_random_ripley.dmm
new file mode 100644
index 0000000000..f7dff903f5
--- /dev/null
+++ b/_maps/RandomRuins/LavaRuins/lavaland_surface_random_ripley.dmm
@@ -0,0 +1,84 @@
+//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE
+"a" = (
+/turf/template_noop,
+/area/lavaland/surface/outdoors)
+"b" = (
+/turf/closed/mineral/volcanic,
+/area/lavaland/surface/outdoors)
+"c" = (
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/lavaland/surface/outdoors)
+"d" = (
+/obj/mecha/working/ripley/mining,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/lavaland/surface/outdoors)
+"e" = (
+/obj/item/clothing/shoes/workboots/mining,
+/obj/item/clothing/under/rank/miner/lavaland,
+/obj/effect/decal/remains/human,
+/turf/open/floor/plating/asteroid/basalt/lava_land_surface,
+/area/lavaland/surface/outdoors)
+
+(1,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+"}
+(2,1,1) = {"
+a
+a
+b
+b
+b
+a
+a
+"}
+(3,1,1) = {"
+a
+b
+c
+b
+c
+b
+a
+"}
+(4,1,1) = {"
+a
+b
+c
+d
+e
+b
+a
+"}
+(5,1,1) = {"
+a
+b
+c
+c
+b
+b
+a
+"}
+(6,1,1) = {"
+a
+b
+b
+b
+b
+a
+a
+"}
+(7,1,1) = {"
+a
+a
+a
+a
+a
+a
+a
+"}
diff --git a/code/controllers/subsystem/mapping.dm b/code/controllers/subsystem/mapping.dm
index 64282c8762..0cc5851d24 100644
--- a/code/controllers/subsystem/mapping.dm
+++ b/code/controllers/subsystem/mapping.dm
@@ -43,7 +43,7 @@ SUBSYSTEM_DEF(mapping)
loading_ruins = TRUE
var/mining_type = config.minetype
if (mining_type == "lavaland")
- seedRuins(list(5), global.config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates)
+ seedRuins(list(ZLEVEL_LAVALAND), global.config.lavaland_budget, /area/lavaland/surface/outdoors/unexplored, lava_ruins_templates)
spawn_rivers()
// deep space ruins
diff --git a/code/datums/ruins/lavaland.dm b/code/datums/ruins/lavaland.dm
index 011363f001..4f6be75f4f 100644
--- a/code/datums/ruins/lavaland.dm
+++ b/code/datums/ruins/lavaland.dm
@@ -183,3 +183,11 @@
suffix = "lavaland_surface_swarmer_crash.dmm"
allow_duplicates = FALSE
cost = 20
+
+/datum/map_template/ruin/lavaland/miningripley
+ name = "Ripley"
+ id = "ripley"
+ description = "A heavily-damaged mining ripley, property of a very unfortunate miner. You might have to do a bit of work to fix this thing up."
+ suffix = "lavaland_surface_random_ripley.dmm"
+ allow_duplicates = FALSE
+ cost = 5
diff --git a/code/game/machinery/vending.dm b/code/game/machinery/vending.dm
index 3fe719c9f2..bffa2a997b 100644
--- a/code/game/machinery/vending.dm
+++ b/code/game/machinery/vending.dm
@@ -53,6 +53,7 @@
var/extended_inventory = 0 //can we access the hidden inventory?
var/scan_id = 1
var/obj/item/weapon/coin/coin
+ var/obj/item/stack/spacecash/bill
var/dish_quants = list() //used by the snack machine's custom compartment to count dishes.
@@ -118,10 +119,9 @@
..()
/obj/machinery/vending/Destroy()
- qdel(wires)
- wires = null
- qdel(coin)
- coin = null
+ QDEL_NULL(wires)
+ QDEL_NULL(coin)
+ QDEL_NULL(bill)
return ..()
/obj/machinery/vending/snack/Destroy()
@@ -311,6 +311,12 @@
attack_hand(user)
return
else if(istype(W, /obj/item/weapon/coin))
+ if(coin)
+ to_chat(user, "[src] already has [coin] inserted")
+ return
+ if(bill)
+ to_chat(user, "[src] already has [bill] inserted")
+ return
if(!premium.len)
to_chat(user, "[src] doesn't have a coin slot.")
return
@@ -320,6 +326,21 @@
coin = W
to_chat(user, "You insert [W] into [src].")
return
+ else if(istype(W, /obj/item/stack/spacecash))
+ if(coin)
+ to_chat(user, "[src] already has [coin] inserted")
+ return
+ if(bill)
+ to_chat(user, "[src] already has [bill] inserted")
+ return
+ var/obj/item/stack/S = W
+ if(!premium.len)
+ to_chat(user, "[src] doesn't have a bill slot.")
+ return
+ S.use(1)
+ bill = new S.type(src,1)
+ to_chat(user, "You insert [W] into [src].")
+ return
else if(istype(W, refill_canister) && refill_canister != null)
if(stat & (BROKEN|NOPOWER))
to_chat(user, "It does nothing.")
@@ -386,9 +407,9 @@
var/list/display_records = product_records
if(extended_inventory)
display_records = product_records + hidden_records
- if(coin)
+ if(coin || bill)
display_records = product_records + coin_records
- if(coin && extended_inventory)
+ if((coin || bill) && extended_inventory)
display_records = product_records + hidden_records + coin_records
dat += "
"
for (var/datum/data/vending_product/R in display_records)
@@ -403,11 +424,11 @@
dat += "
"
dat += ""
if(premium.len > 0)
- dat += "Coin slot: "
- if (coin)
- dat += "[coin] Remove"
+ dat += "Change Return: "
+ if (coin || bill)
+ dat += "[(coin ? coin : "")][(bill ? bill : "")] Remove"
else
- dat += "No coin Remove"
+ dat += "No money Remove"
if(istype(src, /obj/machinery/vending/snack))
dat += "Chef's Food Selection
"
dat += ""
@@ -443,16 +464,24 @@
return
if(href_list["remove_coin"])
- if(!coin)
- to_chat(usr, "There is no coin in this machine.")
+ if(!(coin || bill))
+ to_chat(usr, "There is no money in this machine.")
return
-
- coin.loc = loc
- if(!usr.get_active_held_item())
- usr.put_in_hands(coin)
- to_chat(usr, "You remove [coin] from [src].")
- coin = null
-
+ if(coin)
+ if(!usr.get_active_held_item())
+ usr.put_in_hands(coin)
+ else
+ coin.forceMove(get_turf(src))
+ to_chat(usr, "You remove [coin] from [src].")
+ coin = null
+ if(bill)
+ if(!usr.get_active_held_item())
+ usr.put_in_hands(bill)
+ else
+ bill.forceMove(get_turf(src))
+ to_chat(usr, "You remove [bill] from [src].")
+ bill = null
+
usr.set_machine(src)
@@ -494,26 +523,25 @@
vend_ready = 1
return
else if(R in coin_records)
- if(!coin)
- to_chat(usr, "You need to insert a coin to get this item!")
+ if(!(coin || bill))
+ to_chat(usr, "You need to insert money to get this item!")
vend_ready = 1
return
- if(coin.string_attached)
+ if(coin && coin.string_attached)
if(prob(50))
if(usr.put_in_hands(coin))
to_chat(usr, "You successfully pull [coin] out before [src] could swallow it.")
coin = null
else
to_chat(usr, "You couldn't pull [coin] out because your hands are full!")
- qdel(coin)
- coin = null
+ QDEL_NULL(coin)
else
to_chat(usr, "You weren't able to pull [coin] out fast enough, the machine ate it, string and all!")
- qdel(coin)
- coin = null
+ QDEL_NULL(coin)
else
- qdel(coin)
- coin = null
+ QDEL_NULL(coin)
+ QDEL_NULL(bill)
+
else if (!(R in product_records))
vend_ready = 1
message_admins("Vending machine exploit attempted by [key_name(usr, usr.client)]!")
diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm
index 3cd142fcdc..8a66a88f33 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -56,7 +56,7 @@
var/datum/gas_mixture/cabin_air
var/obj/machinery/atmospherics/components/unary/portables_connector/connected_port = null
- var/obj/item/device/radio/radio = null
+ var/obj/item/device/radio/mech/radio
var/list/trackers = list()
var/max_temperature = 25000
@@ -117,6 +117,7 @@
hud_possible = list (DIAG_STAT_HUD, DIAG_BATT_HUD, DIAG_MECH_HUD, DIAG_TRACK_HUD)
+/obj/item/device/radio/mech //this has to go somewhere
/obj/mecha/Initialize()
. = ..()
@@ -155,11 +156,9 @@
AI = M //AIs are loaded into the mech computer itself. When the mech dies, so does the AI. They can be recovered with an AI card from the wreck.
else
M.forceMove(loc)
-
- if(prob(30))
- explosion(get_turf(loc), 0, 0, 1, 3)
-
if(wreckage)
+ if(prob(30))
+ explosion(get_turf(src), 0, 0, 1, 3)
var/obj/structure/mecha_wreckage/WR = new wreckage(loc, AI)
for(var/obj/item/mecha_parts/mecha_equipment/E in equipment)
if(E.salvageable && prob(30))
diff --git a/code/game/mecha/mecha.dm.rej b/code/game/mecha/mecha.dm.rej
new file mode 100644
index 0000000000..1f2aa905dc
--- /dev/null
+++ b/code/game/mecha/mecha.dm.rej
@@ -0,0 +1,10 @@
+diff a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm (rejected hunks)
+@@ -146,7 +146,7 @@
+ /obj/mecha/get_cell()
+ return cell
+
+-/obj/mecha/deconstruct(disassembled = TRUE)
++/obj/mecha/Destroy()
+ go_out()
+ var/mob/living/silicon/ai/AI
+ for(var/mob/M in src) //Let's just be ultra sure
diff --git a/code/game/mecha/working/ripley.dm b/code/game/mecha/working/ripley.dm
index a752811fe7..e23cea0397 100644
--- a/code/game/mecha/working/ripley.dm
+++ b/code/game/mecha/working/ripley.dm
@@ -91,32 +91,34 @@
/obj/mecha/working/ripley/mining
desc = "An old, dusty mining Ripley."
name = "\improper APLU \"Miner\""
+ obj_integrity = 75 //Low starting health
-/obj/mecha/working/ripley/mining/New()
- ..()
- //Attach drill
- if(prob(25)) //Possible diamond drill... Feeling lucky?
- var/obj/item/mecha_parts/mecha_equipment/drill/diamonddrill/D = new /obj/item/mecha_parts/mecha_equipment/drill/diamonddrill
- D.attach(src)
- else
- var/obj/item/mecha_parts/mecha_equipment/drill/D = new /obj/item/mecha_parts/mecha_equipment/drill
- D.attach(src)
+/obj/mecha/working/ripley/mining/Initialize()
+ . = ..()
+ if(cell)
+ cell.charge = Floor(cell.charge * 0.25) //Starts at very low charge
+ if(prob(70)) //Maybe add a drill
+ if(prob(15)) //Possible diamond drill... Feeling lucky?
+ var/obj/item/mecha_parts/mecha_equipment/drill/diamonddrill/D = new
+ D.attach(src)
+ else
+ var/obj/item/mecha_parts/mecha_equipment/drill/D = new
+ D.attach(src)
- //Add possible plasma cutter
- if(prob(25))
- var/obj/item/mecha_parts/mecha_equipment/M = new /obj/item/mecha_parts/mecha_equipment/weapon/energy/plasma
+ else //Add possible plasma cutter if no drill
+ var/obj/item/mecha_parts/mecha_equipment/M = new
M.attach(src)
//Add ore box to cargo
cargo.Add(new /obj/structure/ore_box(src))
//Attach hydraulic clamp
- var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new /obj/item/mecha_parts/mecha_equipment/hydraulic_clamp
+ var/obj/item/mecha_parts/mecha_equipment/hydraulic_clamp/HC = new
HC.attach(src)
for(var/obj/item/mecha_parts/mecha_tracking/B in trackers)//Deletes the beacon so it can't be found easily
qdel(B)
- var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new /obj/item/mecha_parts/mecha_equipment/mining_scanner
+ var/obj/item/mecha_parts/mecha_equipment/mining_scanner/scanner = new
scanner.attach(src)
/obj/mecha/working/ripley/Exit(atom/movable/O)
diff --git a/code/game/objects/effects/effect_system/effects_foam.dm b/code/game/objects/effects/effect_system/effects_foam.dm
index 9ca086fad2..0e1c88bb79 100644
--- a/code/game/objects/effects/effect_system/effects_foam.dm
+++ b/code/game/objects/effects/effect_system/effects_foam.dm
@@ -220,7 +220,7 @@
/obj/structure/foamedmetal/attack_hand(mob/user)
user.changeNext_move(CLICK_CD_MELEE)
user.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
- to_chat(user, "You hit the metal foam but bounce off it!")
+ to_chat(user, "You hit [src] but bounce off it!")
playsound(src.loc, 'sound/weapons/tap.ogg', 100, 1)
/obj/structure/foamedmetal/CanPass(atom/movable/mover, turf/target, height=1.5)
diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm
index e4ec02e746..622f878d4a 100644
--- a/code/game/objects/items/stacks/sheets/glass.dm
+++ b/code/game/objects/items/stacks/sheets/glass.dm
@@ -197,6 +197,8 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \
var/obj/item/bodypart/O = H.get_bodypart(picked_def_zone)
if(!istype(O))
return
+ if(O.status == BODYPART_ROBOTIC)
+ return
var/feetCover = (H.wear_suit && H.wear_suit.body_parts_covered & FEET) || (H.w_uniform && H.w_uniform.body_parts_covered & FEET)
if(H.shoes || feetCover || H.movement_type & FLYING || H.buckled)
return
diff --git a/code/modules/admin/verbs/adminhelp.dm b/code/modules/admin/verbs/adminhelp.dm
index 632890e654..ce9f5a62f4 100644
--- a/code/modules/admin/verbs/adminhelp.dm
+++ b/code/modules/admin/verbs/adminhelp.dm
@@ -196,9 +196,6 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
else
MessageNoRecipient(parsed_message)
- //show it to the person adminhelping too
- to_chat(C, "PM to-Admins: [name]")
-
//send it to irc if nobody is on and tell us how many were on
var/admin_number_present = send2irc_adminless_only(initiator_ckey, "Ticket #[id]: [name]")
log_admin_private("Ticket #[id]: [key_name(initiator)]: [name] - heard by [admin_number_present] non-AFK admins who have +BAN.")
@@ -265,6 +262,9 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
window_flash(X, ignorepref = TRUE)
to_chat(X, chat_msg)
+ //show it to the person adminhelping too
+ to_chat(initiator, "PM to-Admins: [name]")
+
//Reopen a closed ticket
/datum/admin_help/proc/Reopen()
if(state == AHELP_ACTIVE)
diff --git a/code/modules/cargo/exports/large_objects.dm b/code/modules/cargo/exports/large_objects.dm
index d52a3cbf52..bfc8fb00b6 100644
--- a/code/modules/cargo/exports/large_objects.dm
+++ b/code/modules/cargo/exports/large_objects.dm
@@ -103,3 +103,79 @@
cost = 325
unit_name = "security barrier"
export_types = list(/obj/item/weapon/grenade/barrier, /obj/structure/barricade/security)
+
+//Mecha
+/datum/export/large/mech
+ export_types = list(/obj/mecha)
+ var/sellable
+
+/datum/export/large/mech/applies_to(obj/O)
+ if(!..())
+ return FALSE
+
+ var/obj/mecha/ME = O
+ ME.wreckage = null // So the mech doesn't blow up in the cargo shuttle
+ if(sellable)
+ return TRUE
+
+/datum/export/large/mech/sellable
+ export_types = list()
+ sellable = TRUE
+
+/datum/export/large/mech/sellable/ripley
+ cost = 7500 //boards cost 2500 and takes another 1566 worth of materials (glass, metal, plaseel) to build + significant labor
+ unit_name = "APLU \"Ripley\""
+ export_types = list(/obj/mecha/working/ripley)
+ exclude_types = list(/obj/mecha/working/ripley/firefighter)
+
+/datum/export/large/mech/sellable/firefighter
+ cost = 9000 //same as a ripley but takes 10 more plasteel and 5 less metal
+ unit_name = "APLU \"Firefighter\""
+ export_types = list(/obj/mecha/working/ripley/firefighter)
+
+/datum/export/large/mech/sellable/odysseus
+ cost = 6000 // 1540 of material + 2000 price boards + labor
+ unit_name = "odysseus"
+ export_types = list(/obj/mecha/medical/odysseus)
+
+/datum/export/large/mech/sellable/gygax
+ cost = 25000 // The material is worth 22631 alone. Not as big of a premium as one would expect, since R&D would have provided upgrades by then.
+ unit_name = "gygax"
+ export_types = list(/obj/mecha/combat/gygax)
+ exclude_types = list(/obj/mecha/combat/gygax/dark)
+
+/datum/export/large/mech/sellable/honkmech
+ cost = 80000 // The bananium alone is worth around 64887 credits
+ unit_name = "H.O.N.K"
+ message = "- HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONK HONKHONKHONKHONK"
+ export_types = list(/obj/mecha/combat/honker)
+
+/datum/export/large/mech/sellable/durand
+ cost = 12000 // 7586 worth of material. That's less than a gygax. Players will be disappointed by the durand's comparative lack of worth but oh well. Still a large premium because this requires significant cooperation between R&D, robotics, and cargo.
+ unit_name = "durand"
+ export_types = list(/obj/mecha/combat/durand)
+
+/datum/export/large/mech/sellable/phazon
+ cost = 50000 // 15767 material + anomaly core. Fuck it, if you're willing to try selling one of these you should get BIG FUCKING MONEY
+ unit_name = "phazon"
+ export_types = list(/obj/mecha/combat/phazon)
+
+/datum/export/large/mech/sellable/syndiegygax
+ cost = 50000 // You somehow stole a nuke op's gygax and sold it to nanotrasen. Go you.
+ unit_name = "captured syndicate gygax"
+ export_types = list(/obj/mecha/combat/gygax/dark)
+
+/datum/export/large/mech/sellable/syndiegygax/syndie
+ cost = 25000 // You somehow stole a nuke op's gygax and sold it back to the syndicate. Why would you do this?
+ unit_name = "gygax"
+ emagged = TRUE
+
+/datum/export/large/mech/sellable/mauler
+ cost = 87500 // Whoa, momma.
+ unit_name = "captured mauler"
+ export_types = list(/obj/mecha/combat/marauder/mauler)
+
+/datum/export/large/mech/sellable/mauler/syndie
+ cost = 43750 // Just like the mauler is worth 1.75x the telecrystals compared to the gygax, the price reflects this
+ unit_name = "mauler"
+ emagged = TRUE
diff --git a/code/modules/cargo/exports/tools.dm b/code/modules/cargo/exports/tools.dm
index 8dcf68a9f0..c0643d9c4d 100644
--- a/code/modules/cargo/exports/tools.dm
+++ b/code/modules/cargo/exports/tools.dm
@@ -99,6 +99,7 @@
cost = 5
unit_name = "radio"
export_types = list(/obj/item/device/radio)
+ exclude_types = list(/obj/item/device/radio/mech)
// High-tech tools.
diff --git a/code/modules/client/verbs/etips.dm b/code/modules/client/verbs/etips.dm
index 9dc1662add..326f693b08 100644
--- a/code/modules/client/verbs/etips.dm
+++ b/code/modules/client/verbs/etips.dm
@@ -1,7 +1,7 @@
/client/verb/toggle_tips()
set name = "Toggle examine tooltips"
set desc = "Toggles examine hover-over tooltips"
- set category = "OOC"
+ set category = "Preferences"
prefs.enable_tips = !prefs.enable_tips
prefs.save_preferences()
@@ -10,7 +10,7 @@
/client/verb/change_tip_delay()
set name = "Set examine tooltip delay"
set desc = "Sets the delay in milliseconds before examine tooltips appear"
- set category = "OOC"
+ set category = "Preferences"
var/indelay = stripped_input(usr, "Enter the tooltip delay in milliseconds (default: 500)", "Enter tooltip delay", "", 10)
indelay = text2num(indelay)
diff --git a/code/modules/client/verbs/ooc.dm b/code/modules/client/verbs/ooc.dm
index 93c5e50da8..fc4d8dfab8 100644
--- a/code/modules/client/verbs/ooc.dm
+++ b/code/modules/client/verbs/ooc.dm
@@ -134,6 +134,89 @@ GLOBAL_VAR_INIT(normal_ooc_colour, OOC_COLOR)
else
to_chat(src, "There are no admin notices at the moment.")
+/client/verb/fix_chat()
+ set name = "Fix chat"
+ set category = "OOC"
+ if (!chatOutput || !istype(chatOutput))
+ var/action = alert(src, "Invalid Chat Output data found!\nRecreate data?", "Wot?", "Recreate Chat Output data", "Cancel")
+ if (action != "Recreate Chat Output data")
+ return
+ chatOutput = new /datum/chatOutput(src)
+ chatOutput.start()
+ action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
+ if (action == "Fixed")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum")
+ else
+ chatOutput.load()
+ action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
+ if (action == "Yes")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by re-creating the chatOutput datum and forcing a load()")
+ else
+ action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
+ if (action == "Switch to old chat")
+ winset(src, "output", "is-visible=true;is-disabled=false")
+ winset(src, "browseroutput", "is-visible=false")
+ log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after recreating the chatOutput and forcing a load()")
+
+ else if (chatOutput.loaded)
+ var/action = alert(src, "ChatOutput seems to be loaded\nDo you want me to force a reload, wiping the chat log or just refresh the chat window because it broke/went away?", "Hmmm", "Force Reload", "Refresh", "Cancel")
+ switch (action)
+ if ("Force Reload")
+ chatOutput.loaded = FALSE
+ chatOutput.start() //this is likely to fail since it asks , but we should try it anyways so we know.
+ action = alert(src, "Goon chat reloading, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
+ if (action == "Fixed")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a start()")
+ else
+ chatOutput.load()
+ action = alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
+ if (action == "Yes")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()")
+ else
+ action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
+ if (action == "Switch to old chat")
+ winset(src, "output", "is-visible=true;is-disabled=false")
+ winset(src, "browseroutput", "is-visible=false")
+ log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a start() and forcing a load()")
+
+ if ("Refresh")
+ chatOutput.showChat()
+ action = alert(src, "Goon chat refreshing, wait a bit and tell me if it's fixed", "", "Fixed", "Nope, force a reload")
+ if (action == "Fixed")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a show()")
+ else
+ chatOutput.loaded = FALSE
+ chatOutput.load()
+ action = alert(src, "How about now? (give it a moment)", "", "Yes", "No")
+ if (action == "Yes")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by forcing a load()")
+ else
+ action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
+ if (action == "Switch to old chat")
+ winset(src, "output", "is-visible=true;is-disabled=false")
+ winset(src, "browseroutput", "is-visible=false")
+ log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window forcing a show() and forcing a load()")
+ return
+
+ else
+ chatOutput.start()
+ var/action = alert(src, "Manually loading Chat, wait a bit and tell me if it's fixed", "", "Fixed", "Nope")
+ if (action == "Fixed")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start()")
+ else
+ chatOutput.load()
+ alert(src, "How about now? (give it a moment (it may also try to load twice))", "", "Yes", "No")
+ if (action == "Yes")
+ log_game("GOONCHAT: [key_name(src)] Had to fix their goonchat by manually calling start() and forcing a load()")
+ else
+ action = alert(src, "Welp, I'm all out of ideas. Try closing byond and reconnecting.\nWe could also disable fancy chat and re-enable oldchat", "", "Thanks anyways", "Switch to old chat")
+ if (action == "Switch to old chat")
+ winset(src, "output", list2params(list("on-show" = "", "is-disabled" = "false", "is-visible" = "true")))
+ winset(src, "browseroutput", "is-disabled=true;is-visible=false")
+ log_game("GOONCHAT: [key_name(src)] Failed to fix their goonchat window after manually calling start() and forcing a load()")
+
+
+
/client/verb/motd()
set name = "MOTD"
set category = "OOC"
diff --git a/code/modules/goonchat/browserOutput.dm b/code/modules/goonchat/browserOutput.dm
index 7ab7950f2c..191da98ea1 100644
--- a/code/modules/goonchat/browserOutput.dm
+++ b/code/modules/goonchat/browserOutput.dm
@@ -90,8 +90,7 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
testing("Chat loaded for [owner.ckey]")
loaded = TRUE
- winset(owner, "output", "is-disabled=true;is-visible=false")
- winset(owner, "browseroutput", "is-disabled=false;is-visible=true")
+ showChat()
for(var/message in messageQueue)
@@ -99,9 +98,16 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
messageQueue = null
sendClientData()
-
+
+ //do not convert to to_chat()
+ owner << {"If you can see this, update byond."}
+
pingLoop()
+/datum/chatOutput/proc/showChat()
+ winset(owner, "output", "is-visible=false")
+ winset(owner, "browseroutput", "is-disabled=false;is-visible=true")
+
/datum/chatOutput/proc/pingLoop()
set waitfor = FALSE
@@ -237,24 +243,24 @@ GLOBAL_DATUM_INIT(iconCache, /savefile, new("data/iconCache.sav")) //Cache of ic
message = replacetext(message, "\proper", "")
message = replacetext(message, "\n", "
")
message = replacetext(message, "\t", "[GLOB.TAB][GLOB.TAB]")
-
+
for(var/I in targets)
//Grab us a client if possible
var/client/C = grab_client(I)
-
+
if (!C)
continue
-
+
+ //Send it to the old style output window.
+ C << original_message
+
if(!C.chatOutput || C.chatOutput.broken) // A player who hasn't updated his skin file.
- C << original_message
- return TRUE
+ continue
if(!C.chatOutput.loaded)
- //Client sucks at loading things, put their messages in a queue
+ //Client still loading, put their messages in a queue
C.chatOutput.messageQueue += message
- //But also send it to their output window since that shows until goonchat loads
- C << original_message
- return
+ continue
// url_encode it TWICE, this way any UTF-8 characters are able to be decoded by the Javascript.
C << output(url_encode(url_encode(message)), "browseroutput:output")
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index 9675ac25b1..6336c118f4 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -7,23 +7,25 @@ The Hierophant spawns in its arena, which makes fighting it challenging but not
The text this boss speaks is ROT4, use ROT22 to decode
-The Hierophant's attacks are as follows, and INTENSIFY at a random chance based on Hierophant's health;
-- Creates a cardinal or diagonal blast(Cross Blast) under its target, exploding after a short time.
- INTENSITY EFFECT: Creates one of the cross blast types under itself instead of under the target.
- INTENSITY EFFECT: The created Cross Blast fires in all directions if below half health.
-- If no chasers exist, creates a chaser that will seek its target, leaving a trail of blasts.
- INTENSITY EFFECT: Creates a second, slower chaser.
-- Creates an expanding AoE burst.
-- INTENSE ATTACKS:
+The Hierophant's attacks are as follows;
+- These attacks happen at a random, increasing chance:
If target is at least 2 tiles away; Blinks to the target after a very brief delay, damaging everything near the start and end points.
As above, but does so multiple times if below half health.
- Rapidly creates Cross Blasts under a target.
- If chasers are off cooldown, creates four high-speed chasers.
-- IF TARGET WAS STRUCK IN MELEE: Creates a 3x3 square of blasts under the target.
+ Rapidly creates cardinal and diagonal Cross Blasts under a target.
+ If chasers are off cooldown, creates 4 chasers.
-- IF TARGET IS OUTSIDE THE ARENA: Creates an arena around the target for 10 seconds, blinking to it if Hierophant is not in the arena.
+- IF TARGET IS OUTSIDE THE ARENA: Creates an arena around the target for 10 seconds, blinking to the target if not in the created arena.
The arena has a 20 second cooldown, giving people a small window to get the fuck out.
+- If no chasers exist, creates a chaser that will seek its target, leaving a trail of blasts.
+ Is more likely to create a second, slower, chaser if hurt.
+- If the target is at least 2 tiles away, may Blink to the target after a very brief delay, damaging everything near the start and end points.
+- Creates a cardinal or diagonal blast(Cross Blast) under its target, exploding after a short time.
+ If below half health, the created Cross Blast may fire in all directions.
+- Creates an expanding AoE burst.
+
+- IF TARGET WAS STRUCK IN MELEE: Creates a 3x3 square of blasts under the target.
+
Cross Blasts and the AoE burst gain additional range as Hierophant loses health, while Chasers gain additional speed.
When Hierophant dies, it stops trying to murder you and shrinks into a small form, which, while much weaker, is still quite effective.
@@ -178,17 +180,18 @@ Difficulty: Hard
/mob/living/simple_animal/hostile/megafauna/hierophant/OpenFire()
calculate_rage()
- var/target_is_slow = FALSE
+ if(blinking)
+ return
+
+ var/target_slowness = 0
if(isliving(target))
var/mob/living/L = target
if(!blinking && L.stat == DEAD && get_dist(src, L) > 2)
blink(L)
return
- if(L.movement_delay() > 1.5)
- target_is_slow = TRUE
- chaser_speed = max(1, (3 - anger_modifier * 0.04) + target_is_slow * 0.5)
- if(blinking)
- return
+ target_slowness += L.movement_delay()
+ target_slowness = max(target_slowness, 1)
+ chaser_speed = max(1, (3 - anger_modifier * 0.04) + ((target_slowness - 1) * 0.5))
arena_trap(target)
ranged_cooldown = world.time + max(5, ranged_cooldown_time - anger_modifier * 0.75) //scale cooldown lower with high anger.
@@ -210,18 +213,19 @@ Difficulty: Hard
var/blink_counter = 1 + round(anger_modifier * 0.08)
switch(pick(possibilities))
if("blink_spam") //blink either once or multiple times.
- if(health < maxHealth * 0.5 && !target_is_slow && blink_counter > 1)
+ if(health < maxHealth * 0.5 && blink_counter > 1)
visible_message("\"Mx ampp rsx iwgeti.\"")
var/oldcolor = color
animate(src, color = "#660099", time = 6)
- while(health && target && blink_counter)
+ sleep(6)
+ while(health && !QDELETED(target) && blink_counter)
if(loc == target.loc || loc == target) //we're on the same tile as them after about a second we can stop now
break
blink_counter--
blinking = FALSE
blink(target)
blinking = TRUE
- sleep(5)
+ sleep(4 + target_slowness)
animate(src, color = oldcolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
sleep(8)
@@ -233,15 +237,14 @@ Difficulty: Hard
blinking = TRUE
var/oldcolor = color
animate(src, color = "#660099", time = 6)
- while(health && target && cross_counter)
+ sleep(6)
+ while(health && !QDELETED(target) && cross_counter)
cross_counter--
- var/delay = 7
if(prob(60))
INVOKE_ASYNC(src, .proc/cardinal_blasts, target)
else
INVOKE_ASYNC(src, .proc/diagonal_blasts, target)
- delay = 5 //this one isn't so mean, so do the next one faster(if there is one)
- sleep(delay)
+ sleep(6 + target_slowness)
animate(src, color = oldcolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
sleep(8)
@@ -250,19 +253,22 @@ Difficulty: Hard
visible_message("\"Mx gerrsx lmhi.\"")
blinking = TRUE
var/oldcolor = color
- animate(src, color = "#660099", time = 10)
+ animate(src, color = "#660099", time = 6)
+ sleep(6)
var/list/targets = ListTargets()
var/list/cardinal_copy = GLOB.cardinal.Copy()
while(health && targets.len && cardinal_copy.len)
var/mob/living/pickedtarget = pick(targets)
- if(targets.len > 4)
+ if(targets.len >= cardinal_copy.len)
pickedtarget = pick_n_take(targets)
if(!istype(pickedtarget) || pickedtarget.stat == DEAD)
pickedtarget = target
+ if(QDELETED(pickedtarget) || (istype(pickedtarget) && pickedtarget.stat == DEAD))
+ break //main target is dead and we're out of living targets, cancel out
var/obj/effect/temp_visual/hierophant/chaser/C = new /obj/effect/temp_visual/hierophant/chaser(loc, src, pickedtarget, chaser_speed, FALSE)
C.moving = 3
C.moving_dir = pick_n_take(cardinal_copy)
- sleep(10)
+ sleep(8 + target_slowness)
chaser_cooldown = world.time + initial(chaser_cooldown)
animate(src, color = oldcolor, time = 8)
addtimer(CALLBACK(src, /atom/proc/update_atom_colour), 8)
@@ -270,31 +276,24 @@ Difficulty: Hard
blinking = FALSE
return
- if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2)
- blink(target)
-
- else if(prob(70 - anger_modifier)) //a cross blast of some type
- if(prob(anger_modifier)) //at us?
- if(prob(anger_modifier * 2) && health < maxHealth * 0.5) //we're super angry do it at all dirs
- INVOKE_ASYNC(src, .proc/alldir_blasts, src)
- else if(prob(60))
- INVOKE_ASYNC(src, .proc/cardinal_blasts, src)
- else
- INVOKE_ASYNC(src, .proc/diagonal_blasts, src)
- else //at them?
- if(prob(anger_modifier * 2) && health < maxHealth * 0.5 && !target_is_slow) //we're super angry do it at all dirs
- INVOKE_ASYNC(src, .proc/alldir_blasts, target)
- else if(prob(60))
- INVOKE_ASYNC(src, .proc/cardinal_blasts, target)
- else
- INVOKE_ASYNC(src, .proc/diagonal_blasts, target)
- else if(chaser_cooldown < world.time) //if chasers are off cooldown, fire some!
+ if(chaser_cooldown < world.time) //if chasers are off cooldown, fire some!
var/obj/effect/temp_visual/hierophant/chaser/C = new /obj/effect/temp_visual/hierophant/chaser(loc, src, target, chaser_speed, FALSE)
chaser_cooldown = world.time + initial(chaser_cooldown)
if((prob(anger_modifier) || target.Adjacent(src)) && target != src)
- var/obj/effect/temp_visual/hierophant/chaser/OC = new /obj/effect/temp_visual/hierophant/chaser(loc, src, target, max(1.5, 5 - anger_modifier * 0.07), FALSE)
+ var/obj/effect/temp_visual/hierophant/chaser/OC = new /obj/effect/temp_visual/hierophant/chaser(loc, src, target, chaser_speed * 1.5, FALSE)
OC.moving = 4
OC.moving_dir = pick(GLOB.cardinal - C.moving_dir)
+
+ else if(prob(10 + (anger_modifier * 0.5)) && get_dist(src, target) > 2)
+ blink(target)
+
+ else if(prob(70 - anger_modifier)) //a cross blast of some type
+ if(prob(anger_modifier * (2 / target_slowness)) && health < maxHealth * 0.5) //we're super angry do it at all dirs
+ INVOKE_ASYNC(src, .proc/alldir_blasts, target)
+ else if(prob(60))
+ INVOKE_ASYNC(src, .proc/cardinal_blasts, target)
+ else
+ INVOKE_ASYNC(src, .proc/diagonal_blasts, target)
else //just release a burst of power
INVOKE_ASYNC(src, .proc/burst, get_turf(src))
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index 643b032cda..00ce2bb3b8 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -37,7 +37,7 @@
name = "disabler"
desc = "A self-defense weapon that exhausts organic targets, weakening them until they collapse."
icon_state = "disabler"
- item_state = "disabler"
+ item_state = null
origin_tech = "combat=3"
ammo_type = list(/obj/item/ammo_casing/energy/disabler)
ammo_x_offset = 3
diff --git a/code/modules/shuttle/supply.dm b/code/modules/shuttle/supply.dm
index 60b37236c1..bc8648b873 100644
--- a/code/modules/shuttle/supply.dm
+++ b/code/modules/shuttle/supply.dm
@@ -109,7 +109,7 @@ GLOBAL_LIST_INIT(blacklisted_cargo_types, typecacheof(list(
var/sold_atoms = ""
for(var/atom/movable/AM in areaInstance)
- if(AM.anchored)
+ if(AM.anchored && !istype(AM, /obj/mecha))
continue
sold_atoms += export_item_and_contents(AM, contraband, emagged, dry_run = FALSE)
diff --git a/html/changelogs/AutoChangeLog-pr-1649.yml b/html/changelogs/AutoChangeLog-pr-1649.yml
new file mode 100644
index 0000000000..26d5e8ea6e
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1649.yml
@@ -0,0 +1,4 @@
+author: "Joan"
+delete-after: True
+changes:
+ - tweak: "The Hierophant is a little less chaotic and murdery on average."
diff --git a/html/changelogs/AutoChangeLog-pr-1664.yml b/html/changelogs/AutoChangeLog-pr-1664.yml
new file mode 100644
index 0000000000..70700ac5c3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1664.yml
@@ -0,0 +1,4 @@
+author: "CitadelStationBot"
+delete-after: True
+changes:
+ - rscadd: "Thanks to the invention of \"Bill slots\", vending machines can now accept space cash."
diff --git a/html/changelogs/AutoChangeLog-pr-1695.yml b/html/changelogs/AutoChangeLog-pr-1695.yml
new file mode 100644
index 0000000000..d66f67f7b6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1695.yml
@@ -0,0 +1,4 @@
+author: "Xhuis"
+delete-after: True
+changes:
+ - bugfix: "Disablers now have an in-hand sprite."
diff --git a/html/changelogs/AutoChangeLog-pr-1772.yml b/html/changelogs/AutoChangeLog-pr-1772.yml
new file mode 100644
index 0000000000..8ff81219e5
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1772.yml
@@ -0,0 +1,4 @@
+author: "RandomMarine"
+delete-after: True
+changes:
+ - rscadd: "Cargo can now export mechs!"
diff --git a/html/changelogs/AutoChangeLog-pr-1778.yml b/html/changelogs/AutoChangeLog-pr-1778.yml
new file mode 100644
index 0000000000..744e80fb95
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-1778.yml
@@ -0,0 +1,4 @@
+author: "Kor"
+delete-after: True
+changes:
+ - rscadd: "Glass shards will no longer hurt people with robotic legs."
diff --git a/tgstation.dme.rej b/tgstation.dme.rej
new file mode 100644
index 0000000000..f8575768ad
--- /dev/null
+++ b/tgstation.dme.rej
@@ -0,0 +1,9 @@
+diff a/tgstation.dme b/tgstation.dme (rejected hunks)
+@@ -1383,6 +1383,7 @@
+ #include "code\modules\hydroponics\grown\misc.dm"
+ #include "code\modules\hydroponics\grown\mushrooms.dm"
+ #include "code\modules\hydroponics\grown\nettle.dm"
++#include "code\modules\hydroponics\grown\onion.dm"
+ #include "code\modules\hydroponics\grown\potato.dm"
+ #include "code\modules\hydroponics\grown\pumpkin.dm"
+ #include "code\modules\hydroponics\grown\random.dm"