Merge branch 'master' into spike-loot-differentces

This commit is contained in:
Trilbyspaceclone
2020-08-12 15:10:37 -04:00
committed by GitHub
760 changed files with 27169 additions and 10617 deletions
+3 -9
View File
@@ -9,8 +9,6 @@
icon_state = "bus"
density = TRUE
circuit = /obj/item/circuitboard/machine/ntnet_relay
ui_x = 400
ui_y = 300
var/datum/ntnet/NTNet = null // This is mostly for backwards reference and to allow varedit modifications from ingame.
var/enabled = 1 // Set to 0 if the relay was turned off
@@ -64,15 +62,12 @@
SSnetworks.station_network.add_log("Quantum relay switched from overload recovery mode to normal operation mode.")
..()
/obj/machinery/ntnet_relay/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/ntnet_relay/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "NtnetRelay", "NTNet Quantum Relay", ui_x, ui_y, master_ui, state)
ui = new(user, src, "NtnetRelay")
ui.open()
/obj/machinery/ntnet_relay/ui_data(mob/user)
var/list/data = list()
data["enabled"] = enabled
@@ -81,7 +76,6 @@
data["dos_crashed"] = dos_failure
return data
/obj/machinery/ntnet_relay/ui_act(action, params)
if(..())
return
+8 -3
View File
@@ -78,10 +78,15 @@
close_machine(target)
ui_interact(user)
/obj/machinery/vr_sleeper/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_contained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/vr_sleeper/ui_state(mob/user)
if(user == occupant)
return GLOB.contained_state
return GLOB.default_state
/obj/machinery/vr_sleeper/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "VrSleeper", "VR Sleeper", 475, 340, master_ui, state)
ui = new(user, src, "VrSleeper", "VR Sleeper")
ui.open()
/obj/machinery/vr_sleeper/ui_act(action, params)
+1 -1
View File
@@ -150,7 +150,7 @@ GLOBAL_LIST(round_end_notifiees)
/datum/tgs_chat_command/wheelofsalt/Run(datum/tgs_chat_user/sender, params)
var/saltresult = "The wheel of salt [pick("clatters","screams","vibrates","clanks","resonates","groans","moans","squeaks","emits a[pick(" god-forsaken"," lewd"," creepy"," generic","n orgasmic"," demonic")] [pick("airhorn","bike horn","trumpet","clown","latex","vore","dog","laughing")] noise")] as it spins violently... And it seems the salt of the day is the "
var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]"
var/saltprimarysubject = "[pick("combat","medical","grab","furry","wall","orgasm","cat","ERP","lizard","dog","latex","vision cone","atmospherics","table","chem","vore","dogborg","Skylar Lineman","Mekhi Anderson","Peppermint","rework","cum","dick","cockvore","Medihound","sleeper","belly sleeper","door wires","flightsuit","coder privilege","Developer abuse","ban reason","github self merge","red panda","beret","male catgirl","powergame","hexacrocin removal","Discord server","Clitadel","Cargonia","Solarian Republic","Main and RP merger","bluespace","salt","chem dispenser theft","Botany","moth","BWOINK","anal vore","stamina","Mason Jakops","mining","noodle","milf","Lavaland","Necropolis","Ashwalker","Chase Redtail","Drew Mint","Pavel Marsk","Joker Amari","Durgit","chaplain","Antag","nanite","Syndicate","Nar-Sie","Ratvar","Cult","maint","Foam-Force","AI","cyborg","ghost","clockwork","cyberpunk","vaporwave","Clown","Leon Beech","Mime","security","research","Megafauna","Bubblegum","Ash Drake","Legion","Colossus","White Shuttle","Changeling","Cowboy","Space Ninja","Poly","Revolutionary","Skyrim","forbidden fruits","xenomorph","blob","Nuclear Operative","crossdressing")]"
var/saltsecondarysubject = "[pick("rework","changes","r34","ban","removal","addition","leak","proposal","fanart","introduction","tabling","ERP","bikeshedding","crossdressing","sprites","semen keg","argument","theft","nerf","screeching","salt","creampie","lewding","murder","kissing","marriage","replacement","fucking","ship","netflix adaptation","dance","remaster","system","voyeur","decoration","pre-order","bukkake","seduction","worship","gangbang","handholding")]"
if(prob(10))
saltresult += "@here for your salt, all day every day"
+12 -2
View File
@@ -2184,8 +2184,18 @@
if(!ishuman(H))
to_chat(usr, "This can only be used on instances of type /mob/living/carbon/human.")
return
var/obj/item/reagent_containers/food/snacks/cookie/cookie = new(H)
//let's keep it simple
//milk to plasmemes and skeletons, meat to lizards, electricity bars to ethereals, cookies to everyone else
var/cookiealt = /obj/item/reagent_containers/food/snacks/cookie
if(isskeleton(H))
cookiealt = /obj/item/reagent_containers/food/condiment/milk
else if(isplasmaman(H))
cookiealt = /obj/item/reagent_containers/food/condiment/milk
else if(isethereal(H))
cookiealt = /obj/item/reagent_containers/food/snacks/energybar
else if(islizard(H))
cookiealt = /obj/item/reagent_containers/food/snacks/meat/slab
var/obj/item/cookie = new cookiealt(H)
if(H.put_in_hands(cookie))
H.update_inv_hands()
else
+1 -2
View File
@@ -371,8 +371,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
return
var/msg = "<font color='red' size='4'><b>- AdminHelp marked as IC issue by [usr?.client?.holder?.fakekey? usr.client.holder.fakekey : "an administrator"]! -</b></font><br>"
msg += "<font color='red'><b>Losing is part of the game!</b></font><br>"
msg += "<font color='red'>It is also possible that your ahelp is unable to be answered properly, due to events occurring in the round.</font>"
msg += "<font color='red'>Your ahelp is unable to be answered properly due to events occurring in the round. Your question probably has an IC answer, which means you should deal with it IC!</font>"
if(initiator)
to_chat(initiator, msg)
@@ -0,0 +1,79 @@
GLOBAL_LIST_INIT(abductor_gear, subtypesof(/datum/abductor_gear))
/datum/abductor_gear
/// Name of the gear
var/name = "Generic Abductor Gear"
/// Description of the gear
var/description = "Generic description."
/// Unique ID of the gear
var/id = "abductor_generic"
/// Credit cost of the gear
var/cost = 1
/// Build path of the gear itself
var/build_path = null
/// Category of the gear
var/category = "Basic Gear"
/datum/abductor_gear/agent_helmet
name = "Agent Helmet"
description = "Abduct with style - spiky style. Prevents digital tracking."
id = "agent_helmet"
build_path = /obj/item/clothing/head/helmet/abductor
/datum/abductor_gear/agent_vest
name = "Agent Vest"
description = "A vest outfitted with advanced stealth technology. It has two modes - combat and stealth."
id = "agent_vest"
build_path = /obj/item/clothing/suit/armor/abductor/vest
/datum/abductor_gear/radio_silencer
name = "Radio Silencer"
description = "A compact device used to shut down communications equipment."
id = "radio_silencer"
build_path = /obj/item/abductor/silencer
/datum/abductor_gear/science_tool
name = "Science Tool"
description = "A dual-mode tool for retrieving specimens and scanning appearances. Scanning can be done through cameras."
id = "science_tool"
build_path = /obj/item/abductor/gizmo
/*
/datum/abductor_gear/advanced_baton
name = "Advanced Baton"
description = "A quad-mode baton used for incapacitation and restraining of specimens."
id = "advanced_baton"
cost = 2
build_path = /obj/item/melee/baton/abductor //does not exist?
*/
/datum/abductor_gear/superlingual_matrix
name = "Superlingual Matrix"
description = "A mysterious structure that allows for instant communication between users. Pretty impressive until you need to eat something."
id = "superlingual_matrix"
build_path = /obj/item/organ/tongue/abductor
category = "Advanced Gear"
/datum/abductor_gear/mental_interface
name = "Mental Interface Device"
description = "A dual-mode tool for directly communicating with sentient brains. It can be used to send a direct message to a target, \
or to send a command to a test subject with a charged gland."
id = "mental_interface"
cost = 2
build_path = /obj/item/abductor/mind_device
category = "Advanced Gear"
/datum/abductor_gear/reagent_synthesizer
name = "Reagent Synthesizer"
description = "Synthesizes a variety of reagents using proto-matter."
id = "reagent_synthesizer"
cost = 2
build_path = /obj/item/abductor_machine_beacon/chem_dispenser
category = "Advanced Gear"
/datum/abductor_gear/shrink_ray
name = "Shrink Ray Blaster"
description = "This is a piece of frightening alien tech that enhances the magnetic pull of atoms in a localized space to temporarily make an object shrink. \
That or it's just space magic. Either way, it shrinks stuff."
id = "shrink_ray"
cost = 2
build_path = /obj/item/gun/energy/shrink_ray
category = "Advanced Gear"
@@ -23,99 +23,105 @@
var/obj/machinery/abductor/pad/pad
var/obj/machinery/computer/camera_advanced/abductor/camera
var/list/datum/icon_snapshot/disguises = list()
/// Currently selected gear category
var/selected_cat
/// Dictates if the compact mode of the interface is on or off
var/compact_mode = FALSE
/// Possible gear to be dispensed
var/list/possible_gear
/obj/machinery/abductor/console/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!HAS_TRAIT(user, TRAIT_ABDUCTOR_TRAINING) && !HAS_TRAIT(user.mind, TRAIT_ABDUCTOR_TRAINING))
to_chat(user, "<span class='warning'>You start mashing alien buttons at random!</span>")
if(do_after(user,100, target = src))
TeleporterSend()
return
user.set_machine(src)
var/dat = ""
dat += "<H3> Abductsoft 3000 </H3>"
/obj/machinery/abductor/console/ui_status(mob/user)
if(!isabductor(user) && !isobserver(user))
return UI_CLOSE
return ..()
/obj/machinery/abductor/console/ui_state(mob/user)
return GLOB.physical_state
/obj/machinery/abductor/console/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "AbductorConsole", name)
ui.open()
/obj/machinery/abductor/console/ui_static_data(mob/user)
var/list/data = list()
data["categories"] = list()
for(var/category in possible_gear)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
for(var/gear in possible_gear[category])
var/datum/abductor_gear/AG = possible_gear[category][gear]
cat["items"] += list(list(
"name" = AG.name,
"cost" = AG.cost,
"desc" = AG.description,
))
data["categories"] += list(cat)
return data
/obj/machinery/abductor/console/ui_data(mob/user)
var/list/data = list()
data["compactMode"] = compact_mode
data["experiment"] = experiment ? TRUE : FALSE
if(experiment)
var/points = experiment.points
var/credits = experiment.credits
dat += "Collected Samples : [points] <br>"
dat += "Gear Credits: [credits] <br>"
dat += "<b>Transfer data in exchange for supplies:</b><br>"
dat += "<a href='?src=[REF(src)];dispense=baton'>Advanced Baton (2 Credits)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=mind_device'>Mental Interface Device (2 Credits)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=chem_dispenser'>Reagent Synthesizer (2 Credits)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=helmet'>Agent Helmet (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=vest'>Agent Vest (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=silencer'>Radio Silencer (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=tool'>Science Tool (1 Credit)</A><br>"
dat += "<a href='?src=[REF(src)];dispense=tongue'>Superlingual Matrix (1 Credit)</a><br>"
else
dat += "<span class='bad'>NO EXPERIMENT MACHINE DETECTED</span> <br>"
data["points"] = experiment.points
data["credits"] = experiment.credits
data["pad"] = pad ? TRUE : FALSE
if(pad)
dat += "<span class='bad'>Emergency Teleporter System.</span>"
dat += "<span class='bad'>Consider using primary observation console first.</span>"
dat += "<a href='?src=[REF(src)];teleporter_send=1'>Activate Teleporter</A><br>"
if(gizmo && gizmo.marked)
dat += "<a href='?src=[REF(src)];teleporter_retrieve=1'>Retrieve Mark</A><br>"
else
dat += "<span class='linkOff'>Retrieve Mark</span><br>"
else
dat += "<span class='bad'>NO TELEPAD DETECTED</span></br>"
data["gizmo"] = gizmo && gizmo.marked ? TRUE : FALSE
data["vest"] = vest ? TRUE : FALSE
if(vest)
dat += "<h4> Agent Vest Mode </h4><br>"
var/mode = vest.mode
if(mode == VEST_STEALTH)
dat += "<a href='?src=[REF(src)];flip_vest=1'>Combat</A>"
dat += "<span class='linkOff'>Stealth</span>"
else
dat += "<span class='linkOff'>Combat</span>"
dat += "<a href='?src=[REF(src)];flip_vest=1'>Stealth</A>"
data["vest_mode"] = vest.mode
data["vest_lock"] = HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT)
return data
dat+="<br>"
dat += "<a href='?src=[REF(src)];select_disguise=1'>Select Agent Vest Disguise</a><br>"
dat += "<a href='?src=[REF(src)];toggle_vest=1'>[HAS_TRAIT_FROM(vest, TRAIT_NODROP, ABDUCTOR_VEST_TRAIT) ? "Unlock" : "Lock"] Vest</a><br>"
else
dat += "<span class='bad'>NO AGENT VEST DETECTED</span>"
var/datum/browser/popup = new(user, "computer", "Abductor Console", 400, 500)
popup.set_content(dat)
popup.open()
/obj/machinery/abductor/console/Topic(href, href_list)
if(..())
/obj/machinery/abductor/console/ui_act(action, list/params)
. = ..()
if(.)
return
usr.set_machine(src)
if(href_list["teleporter_send"])
TeleporterSend()
else if(href_list["teleporter_retrieve"])
TeleporterRetrieve()
else if(href_list["flip_vest"])
FlipVest()
else if(href_list["toggle_vest"])
if(vest)
switch(action)
if("buy")
var/item_name = params["name"]
var/list/buyable_items = list()
for(var/category in possible_gear)
buyable_items += possible_gear[category]
for(var/key in buyable_items)
var/datum/abductor_gear/AG = buyable_items[key]
if(AG.name == item_name)
Dispense(AG.build_path, AG.cost)
return TRUE
if("teleporter_send")
TeleporterSend()
return TRUE
if("teleporter_retrieve")
TeleporterRetrieve()
return TRUE
if("flip_vest")
FlipVest()
return TRUE
if("toggle_vest")
if(!vest)
return
vest.toggle_nodrop()
else if(href_list["select_disguise"])
SelectDisguise()
else if(href_list["dispense"])
switch(href_list["dispense"])
if("baton")
Dispense(/obj/item/abductor/baton,cost=2)
if("helmet")
Dispense(/obj/item/clothing/head/helmet/abductor)
if("silencer")
Dispense(/obj/item/abductor/silencer)
if("tool")
Dispense(/obj/item/abductor/gizmo)
if("vest")
Dispense(/obj/item/clothing/suit/armor/abductor/vest)
if("mind_device")
Dispense(/obj/item/abductor/mind_device,cost=2)
if("chem_dispenser")
Dispense(/obj/item/abductor_machine_beacon/chem_dispenser,cost=2)
if("tongue")
Dispense(/obj/item/organ/tongue/abductor)
updateUsrDialog()
return TRUE
if("select_disguise")
SelectDisguise()
return TRUE
if("select")
selected_cat = params["category"]
return TRUE
if("compact_toggle")
compact_mode = !compact_mode
return TRUE
/obj/machinery/abductor/console/proc/TeleporterRetrieve()
if(pad && gizmo && gizmo.marked)
@@ -156,7 +162,6 @@
pad.teleport_target = location
to_chat(user, "<span class='notice'>Location marked as test subject release point.</span>")
/obj/machinery/abductor/console/Initialize(mapload)
..()
return INITIALIZE_HINT_LATELOAD
@@ -22,38 +22,44 @@
gland_colors[i] = random_color()
amounts[i] = rand(1,5)
/obj/machinery/abductor/gland_dispenser/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!isabductor(user))
/obj/machinery/abductor/gland_dispenser/ui_status(mob/user)
if(!isabductor(user) && !isobserver(user))
return UI_CLOSE
return ..()
/obj/machinery/abductor/gland_dispenser/ui_state(mob/user)
return GLOB.physical_state
/obj/machinery/abductor/gland_dispenser/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "GlandDispenser", name)
ui.open()
/obj/machinery/abductor/gland_dispenser/ui_data(mob/user)
var/list/data = list()
data["glands"] = list()
for(var/gland_number=1,gland_number<=gland_colors.len,gland_number++)
var/list/gland_information = list(
"color" = gland_colors[gland_number],
"amount" = amounts[gland_number],
"id" = gland_number,
)
data["glands"] += list(gland_information)
return data
/obj/machinery/abductor/gland_dispenser/ui_act(action, list/params)
. = ..()
if(.)
return
user.set_machine(src)
var/box_css = {"
<style>
a.box.gland {
float: left;
width: 20px;
height: 20px;
margin: 5px;
border-width: 1px;
border-style: solid;
border-color: rgba(0,0,0,.2);
text-align: center;
}
</style>"}
var/dat = ""
var/item_count = 0
for(var/i=1,i<=gland_colors.len,i++)
item_count++
var/g_color = gland_colors[i]
var/amount = amounts[i]
dat += "<a class='box gland' style='background-color:[g_color]' href='?src=[REF(src)];dispense=[i]'>[amount]</a>"
if(item_count == 4) // Four boxes per line
dat +="</br></br>"
item_count = 0
var/datum/browser/popup = new(user, "glands", "Gland Dispenser", 200, 200)
popup.add_head_content(box_css)
popup.set_content(dat)
popup.open()
return
switch(action)
if("dispense")
var/gland_id = text2num(params["gland_id"])
if(!gland_id)
return
Dispense(gland_id)
return TRUE
/obj/machinery/abductor/gland_dispenser/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/organ/heart/gland))
@@ -65,15 +71,6 @@
else
return ..()
/obj/machinery/abductor/gland_dispenser/Topic(href, href_list)
if(..())
return
usr.set_machine(src)
if(href_list["dispense"])
Dispense(text2num(href_list["dispense"]))
updateUsrDialog()
/obj/machinery/abductor/gland_dispenser/proc/Dispense(count)
if(amounts[count]>0)
amounts[count]--
@@ -15,16 +15,13 @@
var/breakout_time = 450
/obj/machinery/abductor/experiment/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
var/mob/living/L = user
if(user.stat || (isliving(user) && (!(L.mobility_flags & MOBILITY_STAND) || !(L.mobility_flags & MOBILITY_UI))) || !Adjacent(user) || !target.Adjacent(user) || !ishuman(target))
return
if(isabductor(target))
return
close_machine(target)
/obj/machinery/abductor/experiment/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
experimentUI(user)
/obj/machinery/abductor/experiment/open_machine()
if(!state_open && !panel_open)
..()
@@ -46,7 +43,7 @@
/obj/machinery/abductor/experiment/container_resist(mob/living/user)
user.visible_message("<span class='notice'>You see [user] kicking against the door of [src]!</span>", \
"<span class='notice'>You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a metallic creaking from [src].</span>")
"<span class='hear'>You hear a metallic creaking from [src].</span>")
if(do_after(user,(breakout_time), target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open)
return
@@ -54,122 +51,84 @@
"<span class='notice'>You successfully break out of [src]!</span>")
open_machine()
/obj/machinery/abductor/experiment/proc/dissection_icon(mob/living/carbon/human/H)
var/icon/photo = null
var/g = (H.dna.features["body_model"] == FEMALE) ? "f" : "m"
if(H.dna.species.use_skintones)
photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.skin_tone]_[g]")
else
photo = icon("icon" = 'icons/mob/human.dmi', "icon_state" = "[H.dna.species.id]_[g]")
photo.Blend("#[H.dna.features["mcolor"]]", ICON_MULTIPLY)
/obj/machinery/abductor/experiment/ui_status(mob/user)
if(user == occupant)
return UI_CLOSE
return ..()
var/icon/eyes
if(EYECOLOR in H.dna.species.species_traits)
eyes = icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "eyes")
eyes.Blend("#[H.eye_color]", ICON_MULTIPLY)
/obj/machinery/abductor/experiment/ui_state(mob/user)
return GLOB.physical_state
var/datum/sprite_accessory/S
S = GLOB.hair_styles_list[H.hair_style]
if(S && (HAIR in H.dna.species.species_traits))
var/icon/hair = icon("icon" = S.icon, "icon_state" = "[S.icon_state]")
hair.Blend("#[H.hair_color]", ICON_MULTIPLY)
eyes.Blend(hair, ICON_OVERLAY)
/obj/machinery/abductor/experiment/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ProbingConsole", name)
ui.open()
S = GLOB.facial_hair_styles_list[H.facial_hair_style]
if(S && (FACEHAIR in H.dna.species.species_traits))
var/icon/facial = icon("icon" = S.icon, "icon_state" = "[S.icon_state]")
facial.Blend("#[H.facial_hair_color]", ICON_MULTIPLY)
eyes.Blend(facial, ICON_OVERLAY)
if(eyes)
photo.Blend(eyes, ICON_OVERLAY)
var/icon/splat = icon("icon" = 'icons/mob/dam_mob.dmi',"icon_state" = "chest30")
photo.Blend(splat,ICON_OVERLAY)
return photo
/obj/machinery/abductor/experiment/proc/experimentUI(mob/user)
var/dat
dat += "<h3> Experiment </h3>"
if(occupant)
var/obj/item/photo/P = new
P.picture = new
P.picture.picture_image = icon(dissection_icon(occupant), dir = SOUTH)
user << browse_rsc(P.picture.picture_image, "dissection_img")
dat += "<table><tr><td>"
dat += "<img src=dissection_img height=80 width=80>" //Avert your eyes
dat += "</td><td>"
dat += "<a href='?src=[REF(src)];experiment=1'>Probe</a><br>"
dat += "<a href='?src=[REF(src)];experiment=2'>Dissect</a><br>"
dat += "<a href='?src=[REF(src)];experiment=3'>Analyze</a><br>"
dat += "</td></tr></table>"
else
dat += "<span class='linkOff'>Experiment </span>"
if(!occupant)
dat += "<h3>Machine Unoccupied</h3>"
else
dat += "<h3>Subject Status : </h3>"
dat += "[occupant.name] => "
var/mob/living/mob_occupant = occupant
switch(mob_occupant.stat)
if(CONSCIOUS)
dat += "<span class='good'>Conscious</span>"
if(UNCONSCIOUS)
dat += "<span class='average'>Unconscious</span>"
else // DEAD
dat += "<span class='bad'>Deceased</span>"
dat += "<br>"
dat += "[flash]"
dat += "<br>"
dat += "<a href='?src=[REF(src)];refresh=1'>Scan</a>"
dat += "<a href='?src=[REF(src)];[state_open ? "close=1'>Close</a>" : "open=1'>Open</a>"]"
var/datum/browser/popup = new(user, "experiment", "Probing Console", 300, 300)
popup.set_title_image(user.browse_rsc_icon(icon, icon_state))
popup.set_content(dat)
popup.open()
/obj/machinery/abductor/experiment/Topic(href, href_list)
if(..() || usr == occupant)
return
usr.set_machine(src)
if(href_list["refresh"])
updateUsrDialog()
return
if(href_list["open"])
open_machine()
return
if(href_list["close"])
close_machine()
return
/obj/machinery/abductor/experiment/ui_data(mob/user)
var/list/data = list()
data["open"] = state_open
data["feedback"] = flash
data["occupant"] = occupant ? TRUE : FALSE
data["occupant_name"] = null
data["occupant_status"] = null
if(occupant)
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat != DEAD)
if(href_list["experiment"])
flash = Experiment(occupant,href_list["experiment"],usr)
updateUsrDialog()
add_fingerprint(usr)
data["occupant_name"] = mob_occupant.name
data["occupant_status"] = mob_occupant.stat
return data
/obj/machinery/abductor/experiment/proc/Experiment(mob/occupant,type,mob/user)
/obj/machinery/abductor/experiment/ui_act(action, list/params)
. = ..()
if(.)
return
switch(action)
if("door")
if(state_open)
close_machine()
return TRUE
else
open_machine()
return TRUE
if("experiment")
if(!occupant)
return
var/mob/living/mob_occupant = occupant
if(mob_occupant.stat == DEAD)
return
flash = experiment(occupant, params["experiment_type"], usr)
return TRUE
/**
* experiment: Performs selected experiment on occupant mob, resulting in a point reward on success
*
* Arguments:
* * occupant The mob inside the machine
* * type The type of experiment to be performed
* * user The mob starting the experiment
*/
/obj/machinery/abductor/experiment/proc/experiment(mob/occupant, type, mob/user)
LAZYINITLIST(history)
var/mob/living/carbon/human/H = occupant
var/datum/antagonist/abductor/user_abductor = user.mind.has_antag_datum(/datum/antagonist/abductor)
if(!user_abductor)
return "<span class='bad'>Authorization failure. Contact mothership immidiately.</span>"
return "Authorization failure. Contact mothership immediately."
var/point_reward = 0
if(!H)
return "Invalid or missing specimen."
if(H in history)
return "<span class='bad'>Specimen already in database.</span>"
return "Specimen already in database."
if(H.stat == DEAD)
say("Specimen deceased - please provide fresh sample.")
return "<span class='bad'>Specimen deceased.</span>"
return "Specimen deceased."
var/obj/item/organ/heart/gland/GlandTest = locate() in H.internal_organs
if(!GlandTest)
say("Experimental dissection not detected!")
return "<span class='bad'>No glands detected!</span>"
if(H.mind != null && (H.voluntary_ghosted || (H.ckey != null)))
return "No glands detected!"
if(H.mind != null && H.ckey != null)
LAZYINITLIST(abductee_minds)
LAZYADD(history, H)
LAZYADD(abductee_minds, H.mind)
@@ -191,22 +150,27 @@
point_reward++
if(point_reward > 0)
open_machine()
SendBack(H)
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
send_back(H)
playsound(src.loc, 'sound/machines/ding.ogg', 50, TRUE)
points += point_reward
credits += point_reward
return "<span class='good'>Experiment successful! [point_reward] new data-points collected.</span>"
return "Experiment successful! [point_reward] new data-points collected."
else
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 1)
return "<span class='bad'>Experiment failed! No replacement organ detected.</span>"
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, TRUE)
return "Experiment failed! No replacement organ detected."
else
say("Brain activity nonexistent - disposing sample...")
open_machine()
SendBack(H)
return "<span class='bad'>Specimen braindead - disposed.</span>"
send_back(H)
return "Specimen braindead - disposed."
/obj/machinery/abductor/experiment/proc/SendBack(mob/living/carbon/human/H)
/**
* send_back: Sends a mob back to a selected teleport location if safe
*
* Arguments:
* * H The human mob to be sent back
*/
/obj/machinery/abductor/experiment/proc/send_back(mob/living/carbon/human/H)
H.Sleeping(160)
H.uncuff()
if(console && console.pad && console.pad.teleport_target)
@@ -216,7 +180,6 @@
SSjob.SendToLateJoin(H, FALSE)
return
/obj/machinery/abductor/experiment/update_icon_state()
if(state_open)
icon_state = "experiment-open"
@@ -45,11 +45,8 @@
desc = "A solid wall of slightly twitching tendrils with a reflective glow."
damaged_desc = "A wall of twitching tendrils with a reflective glow."
icon_state = "blob_glow"
flags_ricochet = RICOCHET_SHINY
point_return = 8
max_integrity = 100
brute_resist = 1
explosion_block = 2
/obj/structure/blob/shield/reflective/check_projectile_ricochet(obj/item/projectile/P)
return PROJECTILE_RICOCHET_FORCE
@@ -13,10 +13,13 @@
changeling = null
. = ..()
/datum/cellular_emporium/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/cellular_emporium/ui_state(mob/user)
return GLOB.always_state
/datum/cellular_emporium/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "CellularEmporium", name, 900, 480, master_ui, state)
ui = new(user, src, "CellularEmporium", name)
ui.open()
/datum/cellular_emporium/ui_data(mob/user)
@@ -405,3 +405,49 @@
animation_number = initial(animation_number)
sigil_active = FALSE
animate(src, alpha = initial(alpha), time = 10, flags = ANIMATION_END_NOW)
/obj/effect/clockwork/sigil/rite
name = "radiant sigil"
desc = "A glowing sigil glowing with barely-contained power."
clockwork_desc = "A sigil that will allow you to perform certain rites on it, provided you have access to sufficient power and materials."
icon_state = "sigiltransmission" //am big lazy - recolored transmission sigil
sigil_name = "Sigil of Rites"
alpha = 255
var/performing_rite = FALSE
color = "#ffe63a"
light_color = "#ffe63a"
light_range = 1
light_power = 2
/obj/effect/clockwork/sigil/rite/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
. = ..()
if(.)
return
if(!is_servant_of_ratvar(user))
return
if(!GLOB.all_clockwork_rites.len) //Did we already generate the list?
generate_all_rites()
if(performing_rite)
to_chat(user, "<span class='warning'>Someone is already performing a rite here!")
return
var/list/possible_rites = list()
for(var/datum/clockwork_rite/R in GLOB.all_clockwork_rites)
possible_rites[R] = R
var/input_key = input(user, "Choose a rite", "Choosing a rite") as null|anything in possible_rites
if(!input_key)
return
var/datum/clockwork_rite/CR = possible_rites[input_key]
if(!CR)
return
var/choice = alert(user, "What to do with this rite?", "What to do?", "Cast", "Show Info", "Cancel")
switch(choice)
if("Cast")
CR.try_cast(src, user)
if("Show Info")
var/infotext = CR.build_info()
to_chat(user, infotext)
/obj/effect/clockwork/sigil/rite/proc/generate_all_rites() //The first time someone uses a sigil of rites, all the rites are actually generated. No need to have a bunch of random datums laying around all the time.
for(var/V in subtypesof(/datum/clockwork_rite))
var/datum/clockwork_rite/R = new V
GLOB.all_clockwork_rites += R
@@ -0,0 +1,196 @@
//This file is for clock rites, mainly used by the Sigil of Rites in clock_sigils.dm
//The rites themselves are in this file to prevent bloating the other file too much, aswell as for easier access
#define INFINITE -1
//The base clockwork rite. This should never be visible
/datum/clockwork_rite
var/name = "Rite of THE frog" //The name of the rite
var/desc = "This rite is used to summon the legendary frog whose-name-shall-not-be-spoken, ender of many worlds." //What does this rite do? Shown to cultists if they choose 'Show Info' after selecting the rite.
var/list/required_ingredients = list(/obj/item/clockwork) //What does this rite require?
var/power_cost = 0 //How much power does this rite cost.. or does it even add power?
var/requires_human = FALSE //Does the rite require a ../carbon/human on the rune?
var/must_be_servant = TRUE //If the above is true, does the human need to be a servant?
var/target_can_be_invoker = TRUE //Does this rite work if the invoker is also the target?
var/cast_time = 0 //How long does the rite take to cast?
var/limit = INFINITE //How often can this rite be used per round? Set this to INFINITE for unlimited, 0 for disallowed, anything above 0 for a limit
var/times_used = 0 //How often has the rite already been used this shift?
var/rite_cast_sound = 'sound/items/bikehorn.ogg' //The sound played when successfully casting the rite. If it honks, the one adding the rite forgot to set one (or was just lazy).
/datum/clockwork_rite/proc/try_cast(var/obj/effect/clockwork/sigil/rite/R, var/mob/living/invoker) //Performs a ton of checks to see if the invoker can cast the rite
if(!istype(R))
return FALSE
if(!R || !R.loc)
return FALSE
var/turf/T = R.loc
if(!T) //Uh oh something is fucky
return FALSE
if(limit != INFINITE && times_used >= limit) //Is the limit on casts exceeded?
to_chat(invoker, "<span class='brass'>There are no more uses left for this rite!</span>")
return FALSE
var/mob/living/carbon/human/H //This is only used if requires_human is TRUE
if(requires_human) //In case this requires a target
for(var/mob/living/carbon/human/possible_H in T)
if((!must_be_servant || is_servant_of_ratvar(possible_H)) && (target_can_be_invoker || invoker != possible_H))
H = possible_H
break
if(!H)
to_chat(invoker, "<span class='brass'>There is no target for the rite on the sigil!</span>")
return FALSE
if(required_ingredients.len) //In case this requires materials
var/is_missing_materials = FALSE
for(var/I in required_ingredients)
var/obj/item/Material = locate(I) in T
if(!Material)
is_missing_materials = TRUE
break
if(is_missing_materials)
var/still_required_string = ""
for(var/i = 1 to required_ingredients.len)
var/obj/O = required_ingredients[i]
if(i != 1)
still_required_string += ", "
still_required_string += "a [initial(O.name)]"
to_chat(invoker, "<span class='brass'>There are still materials missing for this rite. You require [still_required_string].</span>")
return FALSE
if(power_cost) //If this costs power
if(!get_clockwork_power(power_cost))
to_chat(invoker, "<span class='brass'>There is not enough power for this rite!</span>")
return FALSE
R.performing_rite = TRUE
if(!do_after(invoker, cast_time, target = R))
to_chat(invoker, "<span class='warning'>Your rite is disrupted.</span>")
R.performing_rite = FALSE
return FALSE
. = cast(invoker, T, H)
if(!.)
to_chat(invoker, "<span class='warning'> You fail casting [name]</span>")
post_cast(FALSE)
else
to_chat(invoker, "<span class='warning'>You successfully cast [name]</span>")
post_cast(TRUE)
R.performing_rite = FALSE
return
/datum/clockwork_rite/proc/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target) //Casts the rite and uses up ingredients. Doublechecks some things to prevent bypassing some restrictions via funky timing or badminnery.
if(!T || !invoker)
return FALSE
if(requires_human && !target)
return FALSE
if(power_cost && !get_clockwork_power(power_cost))
return FALSE
adjust_clockwork_power(-power_cost)
if(limit != INFINITE && times_used >= limit)
return FALSE
if(required_ingredients.len)
var/is_missing_materials = FALSE
for(var/I in required_ingredients)
var/obj/item/Material = locate(I) in T
if(!Material)
is_missing_materials = TRUE
break
qdel(Material)
if(is_missing_materials)
return FALSE
playsound(T, rite_cast_sound, 50, 2)
return TRUE
/datum/clockwork_rite/proc/post_cast(var/cast_succeeded)
if(cast_succeeded)
times_used++
return TRUE
/datum/clockwork_rite/proc/build_info() //Constructs the info text of a given rite, based on the vars of the rite
. = ""
. += "<span class='brass'>This is the <b>[name]</b>.\n"
. += "[desc]\n"
. += "It requires: "
if(required_ingredients.len)
var/material_string = ""
for(var/i = 1 to required_ingredients.len)
var/obj/O = required_ingredients[i]
if(i != 1)
material_string += ", "
material_string += "a [initial(O.name)]"
. += "[material_string].\n"
else
. += "</span><span class='inathneq_small'><b>no</b><span class='brass'> materials.\n"
. += "It [power_cost >= 0 ? "costs" : "generates"]<span class='inathneq_small'><b> [power_cost ? "[power_cost]" : "no"] </b><span class='brass'>power.\n"
. += "It requires <span class='inathneq_small'><b>[requires_human ? " a human" : " no"]</b><span class='brass'> target.\n"
if(requires_human)
. += "The target <span class='inathneq_small'><b>[must_be_servant ? "cannot be" : "can be"] </b><span class='brass'> a nonservant.\n"
. += "The target <span class='inathneq_small'><b>[target_can_be_invoker ? "can be" : "cannot be"]</b><span class='brass'> the invoker.\n"
. += "It requires <span class='inathneq_small'><b>[cast_time/10]</b><span class='brass'> seconds to cast.\n"
. += "It has been used <span class='inathneq_small'><b>[times_used]</b><span class='brass'> time[times_used != 1 ? "s" : ""], out of <span class='inathneq_small'><b>[limit != INFINITE ? "[limit]" : "infinite"]</b><span class='brass'> available uses.</span>"
//Adds a organ or cybernetic implant to a servant without the need for surgery. Cannot be used with brains for.. reasons.
/datum/clockwork_rite/advancement
name = "Rite of Advancement"
desc = "This rite is used to augment a servant with organs or cybernetic implants. The organ of choice, aswell as the servant and the required ingredients must be placed on the sigil for this rite to take place."
required_ingredients = list(/obj/item/assembly/prox_sensor, /obj/item/stock_parts/cell)
power_cost = 500
requires_human = TRUE
cast_time = 40
rite_cast_sound = 'sound/magic/blind.ogg'
/datum/clockwork_rite/advancement/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
var/obj/item/organ/O = locate(/obj/item/organ) in T
if(!O)
return FALSE
if(istype(O, /obj/item/organ/brain)) //NOPE
return FALSE
. = ..()
if(!.)
return FALSE
O.Insert(target)
new /obj/effect/temp_visual/ratvar/sigil/transgression(T)
//Heals all wounds (not damage) on the target, causing toxloss proportional to amount of wounds healed. 10 damage per wound.
/datum/clockwork_rite/treat_wounds
name = "Rite of Woundmending"
desc = "This rite is used to heal wounds of the servant on the rune. It causes toxins damage proportional to the amount of wounds healed. This can be lethal if performed on an critically injured target."
required_ingredients = list(/obj/item/stock_parts/cell, /obj/item/healthanalyzer, /obj/item/reagent_containers/food/drinks/bottle/holyoil)
power_cost = 300
requires_human = TRUE
must_be_servant = FALSE
target_can_be_invoker = FALSE
cast_time = 80
rite_cast_sound = 'sound/magic/staff_healing.ogg'
/datum/clockwork_rite/treat_wounds/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
if(!target)
return FALSE
if(!target.all_wounds.len)
to_chat(invoker, "<span class='inathneq_small'>This one does not require mending.</span>")
return FALSE
.= ..()
if(!.)
return FALSE
target.adjustToxLoss(10 * target.all_wounds.len)
QDEL_LIST(target.all_wounds)
to_chat(target, "<span class='warning'>You feel your wounds heal, but are overcome with deep nausea.</span>")
new /obj/effect/temp_visual/ratvar/sigil/vitality(T)
//Summons a brass claw implant on the sigil, which can extend a claw that benefits from repeatedly attacking a single target. Can only be cast a limited amount of times.
/datum/clockwork_rite/summon_claw
name = "Rite of the Claw"
desc = "Summons a special arm implant that, when added to a servant's limb, will allow them to extend and retract a claw at will. Don't leave any implants you want to keep on this rune when casting the rite."
required_ingredients = list(/obj/item/stock_parts/cell, /obj/item/organ/cyberimp, /obj/item/assembly/flash)
power_cost = 1000
cast_time = 60
limit = 4
rite_cast_sound = 'sound/magic/clockwork/fellowship_armory.ogg'
/datum/clockwork_rite/summon_claw/cast(var/mob/living/invoker, var/turf/T, var/mob/living/carbon/human/target)
. = ..()
if(!.)
return FALSE
var/obj/item/organ/cyberimp/arm/clockwork/claw/CL = new /obj/item/organ/cyberimp/arm/clockwork/claw(T)
CL.visible_message("<span class='warning'>[CL] materialises out of thin air!")
new /obj/effect/temp_visual/ratvar/sigil/transmission(T,2)
#undef INFINITE
@@ -0,0 +1,32 @@
//This file is for snowflakey clock augmentations and clock-themed cybernetic implants.
//The base clockie arm implant, which only clock cultist can use unless it is emagged. THIS SHOULD NEVER ACTUALLY EXIST
/obj/item/organ/cyberimp/arm/clockwork
name = "clock-themed arm-mounted implant"
var/clockwork_desc = "According to Ratvar, this really shouldn't exist. Tell Him about this immediately."
syndicate_implant = TRUE
icon_state = "clock_arm_implant"
/obj/item/organ/cyberimp/arm/clockwork/ui_action_click()
if(is_servant_of_ratvar(owner) || (obj_flags & EMAGGED)) //If you somehow manage to steal a clockie's implant AND have an emag AND manage to get it implanted for yourself, good on ya!
return ..()
to_chat(owner, "<span class='warning'>The implant refuses to activate..</span>")
/obj/item/organ/cyberimp/arm/clockwork/examine(mob/user)
if((is_servant_of_ratvar(user) || isobserver(user)) && clockwork_desc)
desc = clockwork_desc
. = ..()
desc = initial(desc)
/obj/item/organ/cyberimp/arm/clockwork/emag_act()
if(obj_flags & EMAGGED)
return
obj_flags |= EMAGGED
to_chat(usr, "<span class='notice'>You emag [src], hoping it'll achieve something..</span>")
//Brass claw implant. Holds the brass claw from brass_claw.dm and can extend / retract it at will.
/obj/item/organ/cyberimp/arm/clockwork/claw
name = "brass claw implant"
desc = "Yikes, the claw attached to this looks pretty darn sharp."
clockwork_desc = "This implant, when added to a servant's arm, allows them to extend and retract a claw at will, though this is mildly painful to do. It will refuse to work for any non-servants."
contents = newlist(/obj/item/clockwork/brass_claw)
@@ -0,0 +1,51 @@
//Brass claw, an armblade-like weapon used by a clock implant. Stealthy if retracted, very obvious if active.
//Bit weaker than an armblade strength-wise but gains combo on consecutive attacks against the same target, which causes bonus damage
/obj/item/clockwork/brass_claw
name = "brass claw"
desc = "A very sharp claw made out of brass."
clockwork_desc = "A incredibly sharp claw made out of brass. It is quite effective at crippling enemies, though very obvious when extended.\nGains combo on consecutive attacks against a target, causing bonus damage."
icon_state = "brass_claw" //Codersprite moment
item_state = "brass_claw"
lefthand_file = 'icons/mob/inhands/antag/clockwork_lefthand.dmi'
righthand_file = 'icons/mob/inhands/antag/clockwork_righthand.dmi'
w_class = WEIGHT_CLASS_HUGE
force = 15 //Doesn't generate vitality like the spear does / has somewhat less damage, but quite good at wounding and gets through armor pretty well. Also gains 2 bonus damage per consecutive attack on the same target
throwforce = 0 //haha yes lets be safe about this
throw_range = 0
throw_speed = 0
armour_penetration = 20
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
sharpness = SHARP_EDGED
wound_bonus = 5
bare_wound_bonus = 15
total_mass = TOTAL_MASS_HAND_REPLACEMENT
var/mob/living/last_attacked
var/combo = 0
var/damage_per_combo = 2
var/maximum_combo_damage = 18 //33 damage on max stacks. Usually the target will already be dead by then but if they somehow aren't, better to have this capped
/obj/item/clockwork/brass_claw/Initialize()
. = ..()
AddComponent(/datum/component/butchering, 60, 80)
/obj/item/clockwork/brass_claw/examine(mob/user)
if(is_servant_of_ratvar(user))
clockwork_desc += "\n<span class='brass'>It has </span><span class='inathneq_small'><b>[combo]</span></b><span class='brass'> combo stacks built up against the current target, causing </span><span class='inathneq_small'><b>[min(maximum_combo_damage, combo * damage_per_combo)]</span></b><span class='brass'> bonus damage.</span>"
. = ..()
clockwork_desc = initial(clockwork_desc)
/obj/item/clockwork/brass_claw/attack(mob/living/target, mob/living/carbon/human/user)
. = ..()
if(QDELETED(target) || target.anti_magic_check(chargecost = 0) || is_servant_of_ratvar(target))
return
if(target != last_attacked) //Loses all combat on switching targets
last_attacked = target
combo = 0
else
if(!iscultist(target)) //Hostile cultists being hit stacks up combo far faster than usual
combo++
else
combo += 3
target.adjustBruteLoss(min(maximum_combo_damage, combo * damage_per_combo))
@@ -12,6 +12,8 @@
attack_verb = list("stabbed", "poked", "slashed")
hitsound = 'sound/weapons/bladeslice.ogg'
w_class = WEIGHT_CLASS_BULKY
block_parry_data = /datum/block_parry_data/ratvarian_spear
item_flags = ITEM_CAN_PARRY
var/bonus_burn = 5
/obj/item/clockwork/weapon/ratvarian_spear/ratvar_act()
@@ -43,7 +45,7 @@
else if(iscultist(target) || isconstruct(target))
to_chat(target, "<span class='userdanger'>Your body flares with agony at [src]'s presence!</span>")
bonus_damage *= 3 //total 30 damage on cultists, 50 with ratvar
GLOB.clockwork_vitality += target.adjustFireLoss(bonus_damage) //adds the damage done to existing vitality
GLOB.clockwork_vitality += max(0, target.adjustFireLoss(bonus_damage)) //adds the damage done to existing vitality
/obj/item/clockwork/weapon/ratvarian_spear/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
var/turf/T = get_turf(hit_atom)
@@ -80,3 +82,15 @@
new /obj/effect/temp_visual/ratvar/spearbreak(T)
action.weapon_reset(RATVARIAN_WEAPON_COOLDOWN)
//A very short, very effective parry that counts on you predicting when the enemy will attack.
/datum/block_parry_data/ratvarian_spear
parry_time_windup = 0 //Very good for predicting
parry_time_active = 3 //Very short
parry_time_spindown = 1
parry_time_perfect = 2
parry_efficiency_perfect = 110 //Very low leeway for counterattacks...
parry_efficiency_considered_successful = 0.8
parry_efficiency_to_counterattack = 1
parry_cooldown = 15 //But also very low cooldown..
parry_failed_stagger_duration = 2 SECONDS //And relatively small penalties for failing.
parry_failed_clickcd_duration = 1 SECONDS
@@ -25,9 +25,6 @@
/datum/clockwork_scripture/ranged_ability/kindle, /datum/clockwork_scripture/ranged_ability/hateful_manacles) //quickbound scripture, accessed by index
var/maximum_quickbound = 5 //how many quickbound scriptures we can have
var/ui_x = 800
var/ui_z = 420
var/obj/structure/destructible/clockwork/trap/linking //If we're linking traps together, which ones we're doing
/obj/item/clockwork/slab/internal //an internal motor for mobs running scripture
@@ -39,6 +36,11 @@
speed_multiplier = 0
no_cost = TRUE
/obj/item/clockwork/slab/debug/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!is_servant_of_ratvar(user))
add_servant_of_ratvar(user)
return ..()
/obj/item/clockwork/slab/traitor
var/spent = FALSE
@@ -57,11 +59,6 @@
to_chat(user, "<span class='userdanger'>[src] falls dark. It appears you weren't worthy.</span>")
return ..()
/obj/item/clockwork/slab/debug/on_attack_hand(mob/living/user, act_intent = user.a_intent, unarmed_attack_flags)
if(!is_servant_of_ratvar(user))
add_servant_of_ratvar(user)
return ..()
/obj/item/clockwork/slab/cyborg //three scriptures, plus a spear and fabricator
clockwork_desc = "A divine link to the Celestial Derelict, allowing for limited recital of scripture."
quickbound = list(/datum/clockwork_scripture/ranged_ability/judicial_marker, /datum/clockwork_scripture/ranged_ability/linked_vanguard, \
@@ -145,14 +142,15 @@
/obj/item/clockwork/slab/examine(mob/user)
. = ..()
if(is_servant_of_ratvar(user) || isobserver(user))
if(LAZYLEN(quickbound))
for(var/i in 1 to quickbound.len)
if(!quickbound[i])
continue
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
. += "Quickbind button: <span class='[get_component_span(initial(quickbind_slot.primary_component))]'>[initial(quickbind_slot.name)]</span>."
. += "Available power: <span class='bold brass'>[DisplayPower(get_clockwork_power())].</span>"
if(!is_servant_of_ratvar(user) || !isobserver(user))
return
if(LAZYLEN(quickbound))
for(var/i in 1 to quickbound.len)
if(!quickbound[i])
continue
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
. += "Quickbind button: <span class='[get_component_span(initial(quickbind_slot.primary_component))]'>[initial(quickbind_slot.name)]</span>."
. += "Available power: <span class='bold brass'>[DisplayPower(get_clockwork_power())].</span>"
//Slab actions; Hierophant, Quickbind
/obj/item/clockwork/slab/ui_action_click(mob/user, action)
@@ -198,12 +196,6 @@
ui_interact(user)
return TRUE
/obj/item/clockwork/slab/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.inventory_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "ClockworkSlab", name, ui_x, ui_z, master_ui, state)
ui.open()
/obj/item/clockwork/slab/proc/recite_scripture(datum/clockwork_scripture/scripture, mob/living/user)
if(!scripture || !user || !user.canUseTopic(src) || (!no_cost && !can_recite_scripture(user)))
return FALSE
@@ -223,27 +215,62 @@
scripture_to_recite.run_scripture()
return TRUE
/*
* Gets text for a certain section. "Default" is used for when you first open Recollection.
* Current sections (make sure to update this if you add one:
* Basics
* Terminology
* Components
* Scripture
* Power
* Conversion
* * what - What section?
*/
/obj/item/clockwork/slab/proc/get_recollection(what) //Now DMDOC compliant!*
. = list()
switch(what) //need someone to rewrite info for this.
if("Default")
.["title"] = "Default"
.["info"] = "Hello servant! Currently these categories dosen't work!"
/*
if("Basics")
.["title"] = "Basics"
.["info"] = "# MARKDOWN WITH HTML?"
if("Terminology")
.["title"] = "Terminology"
.["info"] = "# MARKDOWN WITH HTML?"
if("Components")
.["title"] = "Default"
.["info"] = "# MARKDOWN WITH HTML?"
if("Scripture")
.["title"] = "Default"
.["info"] = "# MARKDOWN WITH HTML?"
if("Power")
.["title"] = "Power"
.["info"] = "# MARKDOWN WITH HTML?"
if("Conversion")
.["title"] = "Conversion"
.["info"] = "# MARKDOWN WITH HTML?"
*/
else
return null //error text handled tgui side. should not cause BSOD
//Gets text for a certain section. "Default" is used for when you first open Recollection.
//Current sections (make sure to update this if you add one:
//- Basics
//- Terminology
//- Components
//- Scripture
//- Power
//- Conversion
/obj/item/clockwork/slab/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "ClockworkSlab", name)
ui.open()
/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
. = list()
.["recollection"] = recollecting
.["power"] = DisplayPower(get_clockwork_power())
.["power_unformatted"] = get_clockwork_power()
// .["rec_text"] = recollection() handled TGUI side
.["HONOR_RATVAR"] = GLOB.ratvar_awakens
.["scripture"] = list()
for(var/s in GLOB.all_scripture)
for(var/s in GLOB.all_scripture) //don't block this, even when ratvar spawns for roundend griff.
var/datum/clockwork_scripture/S = GLOB.all_scripture[s]
if(S.tier == SCRIPTURE_PERIPHERAL) //yes, tiers are the tabs.
if(S.tier == SCRIPTURE_PERIPHERAL) // This tier is skiped because this contains basetype stuff
continue
var/list/data = list()
@@ -253,7 +280,7 @@
data["required"] = "([DisplayPower(S.power_cost)][S.special_power_text ? "+ [replacetext(S.special_power_text, "POWERCOST", "[DisplayPower(S.special_power_cost)]")]" : ""])"
data["required_unformatted"] = S.power_cost
data["type"] = "[S.type]"
data["quickbind"] = S.quickbind //this is if it cant quickbind
data["quickbind"] = S.quickbind //this is if it cant quickbind (bool)
data["fontcolor"] = get_component_color_bright(S.primary_component)
data["important"] = S.important //italic!
@@ -265,10 +292,8 @@
.["rec_binds"] = list()
for(var/i in 1 to maximum_quickbound)
if(GLOB.ratvar_awakens)
return
if(LAZYLEN(quickbound) < i || !quickbound[i])
.["rec_binds"] += list(list())
.["rec_binds"] += list(list()) //a blank json.
else
var/datum/clockwork_scripture/quickbind_slot = quickbound[i]
.["rec_binds"] += list(list(
@@ -280,7 +305,11 @@
/obj/item/clockwork/slab/ui_static_data(mob/user)
. = list()
.["tier_infos"] = list()
.["tier_infos"] = list() //HEY!! WHEN ADDING NEW TIER, ADD IT HERE
.["tier_infos"][SCRIPTURE_PERIPHERAL] = list(
"requirement" = "Breaking the code DM side. Report to coggerbus if this appears!!",
"ready" = FALSE //just in case. Should NOT exist at all
)
.["tier_infos"][SCRIPTURE_DRIVER] = list(
"requirement" = "None, this is already unlocked",
"ready" = TRUE //to bold it on JS side, and to say "These scriptures are permanently unlocked."
@@ -297,10 +326,10 @@
"requirement" = "Unlock powerful equipment and structures by converting five servants or if [DisplayPower(JUDGEMENT_UNLOCK_THRESHOLD)] of power is reached..",
"ready" = SSticker.scripture_states[SCRIPTURE_JUDGEMENT]
)
// .["selected"] = selected_scripture
generate_all_scripture()
.["recollection_categories"] = GLOB.ratvar_awakens ? list() : list(
.["recollection_categories"] = list()
if(GLOB.ratvar_awakens)
return
.["recollection_categories"] = list(
list("name" = "Getting Started", "desc" = "First-time servant? Read this first."),
list("name" = "Basics", "desc" = "A primer on how to play as a servant."),
list("name" = "Terminology", "desc" = "Common acronyms, words, and terms."),
@@ -309,8 +338,9 @@
list("name" = "Power", "desc" = "The power system that certain objects use to function."),
list("name" = "Conversion", "desc" = "Converting the crew, cyborgs, and very walls to your cause.")
)
// .["rec_section"]["title"] //this is here if ever we decided to return these back.
// .["rec_section"]["info"]// wall of info for the thing
.["rec_section"] = get_recollection(recollection_category)
generate_all_scripture()
//needs a new place to live, preferably when clockcult unlocks/downgrades a tier. Smart enough to earlyreturn.
/obj/item/clockwork/slab/ui_act(action, params)
switch(action)
@@ -369,4 +399,4 @@
Q.button_icon_state = quickbind_slot.name
Q.UpdateButtonIcon()
if(isliving(loc))
Q.Grant(loc)
Q.Grant(loc)
@@ -97,7 +97,7 @@
desc = "Charges your slab with divine energy, allowing you to overwhelm a target with Ratvar's light."
invocations = list("Divinity, show them your light!")
whispered = TRUE
channel_time = 10 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time.
channel_time = 15 // I think making kindle channel a third of the time less is a good make up for the fact that it silences people for such a little amount of time.
power_cost = 125
usage_tip = "The light can be used from up to two tiles away. Damage taken will GREATLY REDUCE the stun's duration."
tier = SCRIPTURE_DRIVER
@@ -81,6 +81,25 @@
return /obj/effect/clockwork/sigil/vitality/neutered
return ..()
//Sigil of Rites: Creates a sigil that allows to perform certain rites on it. More information on these can be found in clock_rites.dm, they usually require power, materials and sometimes a target.
/datum/clockwork_scripture/create_object/sigil_of_rites
descname = "Sigil, Access to rites"
name = "Sigil of Rites"
desc = "Places a sigil that, when interacted with, will allow for a variety of rites to be performed on the sigil. These usually require power cells, clockwork power, and some other components."
invocations = list("Engine, allow us..", ".. to be blessed with your rites.")
channel_time = 80
power_cost = 1400
invokers_required = 2
multiple_invokers_used = TRUE
whispered = TRUE
object_path = /obj/effect/clockwork/sigil/rite
creator_message = "<span class='brass'>A sigil of Rites appears beneath you. It will allow you to perform certain rites, given sufficient materials and power.</span>"
usage_tip = "It may be useful to coordinate to acquire needed materials quickly."
tier = SCRIPTURE_SCRIPT
one_per_tile = TRUE
primary_component = HIEROPHANT_ANSIBLE
sort_priority = 4
//Judicial Visor: Creates a judicial visor, which can smite an area.
/datum/clockwork_scripture/create_object/judicial_visor
descname = "Delayed Area Knockdown Glasses"
@@ -96,7 +115,7 @@
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = BELLIGERENT_EYE
sort_priority = 4
sort_priority = 5
quickbind = TRUE
quickbind_desc = "Creates a Judicial Visor, which can smite an area, applying Belligerent and briefly stunning."
@@ -115,7 +134,7 @@
tier = SCRIPTURE_SCRIPT
space_allowed = TRUE
primary_component = VANGUARD_COGWHEEL
sort_priority = 6
sort_priority = 7
quickbind = TRUE
quickbind_desc = "Creates a Ratvarian shield, which can absorb energy from attacks for use in powerful bashes."
@@ -131,7 +150,7 @@
usage_tip = "Throwing the spear at a mob will do massive damage and knock them down, but break the spear. You will need to wait for 30 seconds before resummoning it."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
sort_priority = 7
sort_priority = 8
important = TRUE
quickbind = TRUE
quickbind_desc = "Permanently binds clockwork armor and a Ratvarian spear to you."
@@ -229,7 +248,7 @@
usage_tip = "This is a very effective way to rapidly reinforce a base after an attack."
tier = SCRIPTURE_SCRIPT
primary_component = VANGUARD_COGWHEEL
sort_priority = 8
sort_priority = 9
quickbind = TRUE
quickbind_desc = "Repairs nearby structures and constructs. Servants wearing clockwork armor will also be healed.<br><b>Maximum 10 chants.</b>"
var/heal_attempts = 4
@@ -342,7 +361,7 @@
usage_tip = "Though it requires you to stand still, this scripture can do massive damage."
tier = SCRIPTURE_SCRIPT
primary_component = BELLIGERENT_EYE
sort_priority = 5
sort_priority = 6
quickbind = TRUE
quickbind_desc = "Allows you to fire energy rays at target locations.<br><b>Maximum 5 chants.</b>"
var/static/list/nzcrentr_insults = list("You're not very good at aiming.", "You hunt badly.", "What a waste of energy.", "Almost funny to watch.",
@@ -391,7 +410,7 @@
usage_tip = "It may be useful to end channelling early if the burning becomes too much to handle.."
tier = SCRIPTURE_SCRIPT
primary_component = GEIS_CAPACITOR
sort_priority = 10
sort_priority = 11
quickbind = TRUE
quickbind_desc = "Quickly drains power in an area around the invoker, causing burns proportional to the amount of energy drained.<br><b>Maximum of 20 chants.</b>"
@@ -7,17 +7,13 @@
density = TRUE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
ui_x = 350
ui_y = 442
var/timer_set = 90
var/default_timer_set = 90
var/minimum_timer_set = 90
var/maximum_timer_set = 3600
ui_style = "nanotrasen"
var/numeric_input = ""
var/ui_mode = NUKEUI_AWAIT_DISK
var/timing = FALSE
var/exploding = FALSE
var/exploded = FALSE
@@ -34,7 +30,6 @@
var/interior = ""
var/proper_bomb = TRUE //Please
var/obj/effect/countdown/nuclearbomb/countdown
var/nuclear_cooldown //used to stop global spam.
/obj/machinery/nuclearbomb/Initialize()
. = ..()
@@ -77,15 +72,16 @@
/obj/machinery/nuclearbomb/syndicate/get_cinematic_type(off_station)
var/datum/game_mode/nuclear/NM = SSticker.mode
switch(off_station)
if(FALSE)
if(0)
if(istype(NM) && !NM.nuke_team.syndies_escaped())
return CINEMATIC_ANNIHILATION
else
return CINEMATIC_NUKE_WIN
if(NUKE_MISS_STATION)
if(1)
return CINEMATIC_NUKE_MISS
else
if(2)
return CINEMATIC_NUKE_FAR
return CINEMATIC_NUKE_FAR
/obj/machinery/nuclearbomb/proc/disk_check(obj/item/disk/nuclear/D)
if(D.fake)
@@ -194,7 +190,7 @@
icon_state = "nuclearbomb_exploding"
/obj/machinery/nuclearbomb/update_overlays()
. = ..()
. += ..()
update_icon_interior()
update_icon_lights()
@@ -236,7 +232,7 @@
explode()
else
var/volume = (get_time_left() <= 20 ? 30 : 5)
playsound(loc, 'sound/items/timer.ogg', volume, 0)
playsound(loc, 'sound/items/timer.ogg', volume, FALSE)
/obj/machinery/nuclearbomb/proc/update_ui_mode()
if(exploded)
@@ -261,17 +257,18 @@
ui_mode = NUKEUI_AWAIT_TIMER
/obj/machinery/nuclearbomb/ui_interact(mob/user, ui_key="main", datum/tgui/ui=null, force_open=0, datum/tgui/master_ui=null, datum/ui_state/state=GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/nuclearbomb/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "NuclearBomb", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "NuclearBomb", name)
ui.open()
/obj/machinery/nuclearbomb/ui_data(mob/user)
var/list/data = list()
data["disk_present"] = auth
var/hidden_code = (ui_mode == NUKEUI_AWAIT_CODE && numeric_input != "ERROR")
var/current_code = ""
if(hidden_code)
while(length(current_code) < length(numeric_input))
@@ -388,14 +385,13 @@
if("anchor")
if(auth && yes_code)
playsound(src, 'sound/machines/nuke/general_beep.ogg', 50, FALSE)
set_anchor(usr)
set_anchor()
else
playsound(src, 'sound/machines/nuke/angry_beep.ogg', 50, FALSE)
/obj/machinery/nuclearbomb/proc/set_anchor(mob/user)
if((istype(get_area(src), /area/space) || isinspace()) && !anchored)
to_chat(user, "<span class='warning'>This is not a suitable platform for anchoring [src]!</span>")
/obj/machinery/nuclearbomb/proc/set_anchor()
if(isinspace() && !anchored)
to_chat(usr, "<span class='warning'>There is nothing to anchor to!</span>")
else
anchored = !anchored
@@ -416,9 +412,6 @@
if(safety)
to_chat(usr, "<span class='danger'>The safety is still on.</span>")
return
if(!timing && nuclear_cooldown > world.time)
to_chat(usr, "<span class='danger'>[src]'s timer protocols are currently on cooldown, please stand by.</span>")
return
timing = !timing
if(timing)
previous_level = NUM2SECLEVEL(GLOB.security_level)
@@ -427,12 +420,6 @@
S.switch_mode_to(TRACK_INFILTRATOR)
countdown.start()
set_security_level("delta")
nuclear_cooldown = world.time + 15 SECONDS
if(GLOB.war_declared)
var/area/A = get_area(src)
priority_announce("Alert: Unexpected increase in radiation levels near [A.name] ([src.x],[src.y],[src.z]). Please send an authorized radiation specialist to investigate.", "Sensory Nuclear Indexer Telemetry Calculation Helper")
else
detonation_timer = null
set_security_level(previous_level)
@@ -483,12 +470,19 @@
var/off_station = FALSE
var/turf/bomb_location = get_turf(src)
if(!bomb_location || !is_station_level(bomb_location.z))
off_station = NUKE_MISS_STATION
var/area/A = get_area(bomb_location)
if(bomb_location && is_station_level(bomb_location.z))
if(istype(A, /area/space))
off_station = NUKE_NEAR_MISS
if((bomb_location.x < (128-NUKERANGE)) || (bomb_location.x > (128+NUKERANGE)) || (bomb_location.y < (128-NUKERANGE)) || (bomb_location.y > (128+NUKERANGE)))
off_station = NUKE_NEAR_MISS
else if(bomb_location.onSyndieBase())
off_station = NUKE_SYNDICATE_BASE
else
off_station = NUKE_MISS_STATION
if(!off_station)
if(off_station < 2) //can only launch when nuke is on syndie base or space
SSshuttle.registerHostileEnvironment(src)
SSshuttle.lockdown = TRUE
@@ -502,13 +496,13 @@
INVOKE_ASYNC(GLOBAL_PROC,.proc/KillEveryoneOnZLevel, z)
/obj/machinery/nuclearbomb/proc/get_cinematic_type(off_station)
if(!off_station)
if(off_station < 2)
return CINEMATIC_SELFDESTRUCT
else
return CINEMATIC_SELFDESTRUCT_MISS
/obj/machinery/nuclearbomb/beer
name = "Nanotrasen-brand nuclear fission explosive"
name = "\improper Nanotrasen-brand nuclear fission explosive"
desc = "One of the more successful achievements of the Nanotrasen Corporate Warfare Division, their nuclear fission explosives are renowned for being cheap to produce and devastatingly effective. Signs explain that though this particular device has been decommissioned, every Nanotrasen station is equipped with an equivalent one, just in case. All Captains carefully guard the disk needed to detonate them - at least, the sign says they do. There seems to be a tap on the back."
proper_bomb = FALSE
var/obj/structure/reagent_dispensers/beerkeg/keg
@@ -521,9 +515,9 @@
/obj/machinery/nuclearbomb/beer/examine(mob/user)
. = ..()
if(keg.reagents.total_volume)
. += "<span class='notice'>It has [keg.reagents.total_volume] unit\s left.</span>"
to_chat(user, "<span class='notice'>It has [keg.reagents.total_volume] unit\s left.</span>")
else
. += "<span class='danger'>It's empty.</span>"
to_chat(user, "<span class='danger'>It's empty.</span>")
/obj/machinery/nuclearbomb/beer/attackby(obj/item/W, mob/user, params)
if(W.is_refillable())
@@ -535,6 +529,8 @@
return ..()
/obj/machinery/nuclearbomb/beer/actually_explode()
//Unblock roundend, we're not actually exploding.
SSticker.roundend_check_paused = FALSE
var/turf/bomb_location = get_turf(src)
if(!bomb_location)
disarm()
@@ -583,7 +579,7 @@
This is here to make the tiles around the station mininuke change when it's armed.
*/
/obj/machinery/nuclearbomb/selfdestruct/set_anchor(mob/user)
/obj/machinery/nuclearbomb/selfdestruct/set_anchor()
return
/obj/machinery/nuclearbomb/selfdestruct/set_active()
@@ -641,18 +637,19 @@ This is here to make the tiles around the station mininuke change when it's arme
if(newturf && lastlocation == newturf)
if(last_disk_move < world.time - 5000 && prob((world.time - 5000 - last_disk_move)*0.0001))
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop))
if(istype(loneop) && loneop.occurrences < loneop.max_occurrences)
loneop.weight += 1
if(loneop.weight % 5 == 0)
if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1) //players count now
message_admins("[src] is stationary in [ADMIN_VERBOSEJMP(newturf)]. The weight of Lone Operative is now [loneop.weight].")
log_game("[src] is stationary for too long in [loc_name(newturf)], and has increased the weight of the Lone Operative event to [loneop.weight].")
else
lastlocation = newturf
last_disk_move = world.time
var/datum/round_event_control/operative/loneop = locate(/datum/round_event_control/operative) in SSevents.control
if(istype(loneop) && prob(loneop.weight))
if(istype(loneop) && loneop.occurrences < loneop.max_occurrences && prob(loneop.weight))
loneop.weight = max(loneop.weight - 1, 0)
if(loneop.weight % 5 == 0)
if(loneop.weight % 5 == 0 && SSticker.totalPlayers > 1)
message_admins("[src] is on the move (currently in [ADMIN_VERBOSEJMP(newturf)]). The weight of Lone Operative is now [loneop.weight].")
log_game("[src] being on the move has reduced the weight of the Lone Operative event to [loneop.weight].")
@@ -661,9 +658,19 @@ This is here to make the tiles around the station mininuke change when it's arme
if(!fake)
return
if(isobserver(user) || HAS_TRAIT(user, TRAIT_DISK_VERIFIER) || (user.mind && HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER)))
if(isobserver(user) || HAS_TRAIT(user.mind, TRAIT_DISK_VERIFIER))
. += "<span class='warning'>The serial numbers on [src] are incorrect.</span>"
/*
* You can't accidentally eat the nuke disk, bro
*/
/*
/obj/item/disk/nuclear/on_accidental_consumption(mob/living/carbon/M, mob/living/carbon/user, obj/item/source_item, discover_after = TRUE)
M.visible_message("<span class='warning'>[M] looks like [M.p_theyve()] just bitten into something important.</span>", \
"<span class='warning'>Wait, is this the nuke disk?</span>")
return discover_after
*/
/obj/item/disk/nuclear/attackby(obj/item/I, mob/living/user, params)
if(istype(I, /obj/item/claymore/highlander) && !fake)
var/obj/item/claymore/highlander/H = I
@@ -686,7 +693,7 @@ This is here to make the tiles around the station mininuke change when it's arme
/obj/item/disk/nuclear/suicide_act(mob/user)
user.visible_message("<span class='suicide'>[user] is going delta! It looks like [user.p_theyre()] trying to commit suicide!</span>")
playsound(src, 'sound/machines/alarm.ogg', 50, -1, 1)
playsound(src, 'sound/machines/alarm.ogg', 50, -1, TRUE)
for(var/i in 1 to 100)
addtimer(CALLBACK(user, /atom/proc/add_atom_colour, (i % 2)? "#00FF00" : "#FF0000", ADMIN_COLOUR_PRIORITY), i)
addtimer(CALLBACK(src, .proc/manual_suicide, user), 101)
@@ -694,7 +701,7 @@ This is here to make the tiles around the station mininuke change when it's arme
/obj/item/disk/nuclear/proc/manual_suicide(mob/living/user)
user.remove_atom_colour(ADMIN_COLOUR_PRIORITY)
user.visible_message("<span class='suicide'>[user] was destroyed by the nuclear blast!</span>")
user.visible_message("<span class='suicide'>[user] is destroyed by the nuclear blast!</span>")
user.adjustOxyLoss(200)
user.death(0)
@@ -28,6 +28,7 @@
throwforce = 0
blood_volume = 0
has_field_of_vision = FALSE //we are a spoopy ghost
rad_flags = RAD_NO_CONTAMINATE | RAD_PROTECT_CONTENTS
see_in_dark = 8
lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE
@@ -294,6 +294,11 @@
dat += "[surplus] left.<br>"
return dat
/datum/spellbook_entry/item/timestop_katana
name = "Temporal Katana"
desc = "An oddly-weighted katana, reinforced to allow parrying, with a temporal anomaly magically shoved into it. Successful ripostes prove devastating to those unprepared."
item_path = /obj/item/katana/timestop
/datum/spellbook_entry/item/staffchange
name = "Staff of Change"
desc = "An artefact that spits bolts of coruscating energy which cause the target's very form to reshape itself."
-17
View File
@@ -232,23 +232,6 @@
/obj/item/organ/genital/proc/get_features(mob/living/carbon/human/H)
return
//procs to handle sprite overlays being applied to humans
/mob/living/carbon/human/equip_to_slot(obj/item/I, slot)
. = ..()
if(!. && I && slot && !(slot in GLOB.no_genitals_update_slots)) //the item was successfully equipped, and the chosen slot wasn't merely storage, hands or cuffs.
update_genitals()
/mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE)
var/no_update = FALSE
if(!I || I == l_store || I == r_store || I == s_store || I == handcuffed || I == legcuffed || get_held_index_of_item(I)) //stops storages, cuffs and held items from triggering it.
no_update = TRUE
. = ..()
if(!. || no_update)
return
update_genitals()
/mob/living/carbon/human/proc/update_genitals()
if(QDELETED(src))
return
+4 -7
View File
@@ -4,8 +4,6 @@
icon_state = "infrared"
custom_materials = list(/datum/material/iron=1000, /datum/material/glass=500)
is_position_sensitive = TRUE
var/ui_x = 225
var/ui_y = 110
var/on = FALSE
var/visible = FALSE
var/maxlength = 8
@@ -134,7 +132,7 @@
. = ..()
setDir(t)
/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE)
/obj/item/assembly/infra/throw_at(atom/target, range, speed, mob/thrower, spin=1, diagonals_first = 0, datum/callback/callback, force, gentle = FALSE, quickstart = TRUE)
. = ..()
olddir = dir
@@ -186,11 +184,10 @@
return ..()
return UI_CLOSE
/obj/item/assembly/infra/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/item/assembly/infra/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "InfraredEmitter", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "InfraredEmitter", name)
ui.open()
/obj/item/assembly/infra/ui_data(mob/user)
+3 -6
View File
@@ -4,8 +4,6 @@
icon_state = "prox"
custom_materials = list(/datum/material/iron=800, /datum/material/glass=200)
attachable = TRUE
var/ui_x = 250
var/ui_y = 185
var/scanning = FALSE
var/timing = FALSE
var/time = 10
@@ -113,11 +111,10 @@
return ..()
return UI_CLOSE
/obj/item/assembly/prox_sensor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/item/assembly/prox_sensor/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "ProximitySensor", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "ProximitySensor", name)
ui.open()
/obj/item/assembly/prox_sensor/ui_data(mob/user)
+28 -80
View File
@@ -8,33 +8,46 @@
custom_materials = list(/datum/material/iron=400, /datum/material/glass=120)
wires = WIRE_RECEIVE | WIRE_PULSE | WIRE_RADIO_PULSE | WIRE_RADIO_RECEIVE
attachable = TRUE
var/ui_x = 280
var/ui_y = 132
var/code = DEFAULT_SIGNALER_CODE
var/frequency = FREQ_SIGNALER
var/datum/radio_frequency/radio_connection
var/suicider = null
///Holds the mind that commited suicide.
var/datum/mind/suicider
///Holds a reference string to the mob, decides how much of a gamer you are.
var/suicide_mob
var/hearing_range = 1
/obj/item/assembly/signaler/suicide_act(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user] eats \the [src]! If it is signaled, [user.p_they()] will die!</span>")
playsound(src, 'sound/items/eatfood.ogg', 50, TRUE)
user.transferItemToLoc(src, user, TRUE)
suicider = user
moveToNullspace()
suicider = user.mind
suicide_mob = REF(user)
return MANUAL_SUICIDE
/obj/item/assembly/signaler/proc/manual_suicide(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user]'s \the [src] receives a signal, killing [user.p_them()] instantly!</span>")
/obj/item/assembly/signaler/proc/manual_suicide(datum/mind/suicidee)
var/mob/living/user = suicidee.current
if(!istype(user))
return
if(suicide_mob == REF(user))
user.visible_message("<span class='suicide'>[user]'s [src] receives a signal, killing [user.p_them()] instantly!</span>")
else
user.visible_message("<span class='suicide'>[user]'s [src] receives a signal and [user.p_they()] die[user.p_s()] like a gamer!</span>")
user.adjustOxyLoss(200)//it sends an electrical pulse to their heart, killing them. or something.
user.death(0)
//user.set_suicide(TRUE)
user.suicide_log()
playsound(user, 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
qdel(src)
/obj/item/assembly/signaler/Initialize()
. = ..()
set_frequency(frequency)
/obj/item/assembly/signaler/Destroy()
SSradio.remove_object(src,frequency)
suicider = null
. = ..()
/obj/item/assembly/signaler/activate()
@@ -53,11 +66,10 @@
return ..()
return UI_CLOSE
/obj/item/assembly/signaler/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/item/assembly/signaler/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Signaler", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "Signaler", name)
ui.open()
/obj/item/assembly/signaler/ui_data(mob/user)
@@ -66,12 +78,12 @@
data["code"] = code
data["minFrequency"] = MIN_FREE_FREQ
data["maxFrequency"] = MAX_FREE_FREQ
return data
/obj/item/assembly/signaler/ui_act(action, params)
if(..())
return
switch(action)
if("signal")
INVOKE_ASYNC(src, .proc/signal)
@@ -115,9 +127,6 @@
if(usr)
GLOB.lastsignalers.Add("[time] <B>:</B> [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) <B>:</B> [format_frequency(frequency)]/[code]")
return
/obj/item/assembly/signaler/receive_signal(datum/signal/signal)
. = FALSE
if(!signal)
@@ -128,6 +137,7 @@
return
if(suicider)
manual_suicide(suicider)
return
pulse(TRUE)
audible_message("[icon2html(src, hearers(src))] *beep* *beep* *beep*", null, hearing_range)
for(var/CHM in get_hearers_in_view(hearing_range, src))
@@ -136,7 +146,6 @@
LM.playsound_local(get_turf(src), 'sound/machines/triple_beep.ogg', ASSEMBLY_BEEP_VOLUME, TRUE)
return TRUE
/obj/item/assembly/signaler/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
frequency = new_frequency
@@ -165,69 +174,8 @@
return
return ..(signal)
// Embedded signaller used in anomalies.
/obj/item/assembly/signaler/anomaly
name = "anomaly core"
desc = "The neutralized core of an anomaly. It'd probably be valuable for research."
icon_state = "anomaly core"
item_state = "electronic"
lefthand_file = 'icons/mob/inhands/misc/devices_lefthand.dmi'
righthand_file = 'icons/mob/inhands/misc/devices_righthand.dmi'
resistance_flags = FIRE_PROOF
var/anomaly_type = /obj/effect/anomaly
/obj/item/assembly/signaler/anomaly/receive_signal(datum/signal/signal)
if(!signal)
return FALSE
if(signal.data["code"] != code)
return FALSE
if(suicider)
manual_suicide(suicider)
for(var/obj/effect/anomaly/A in get_turf(src))
A.anomalyNeutralize()
return TRUE
/obj/item/assembly/signaler/anomaly/manual_suicide(mob/living/carbon/user)
user.visible_message("<span class='suicide'>[user]'s [src] is reacting to the radio signal, warping [user.p_their()] body!</span>")
user.suiciding = TRUE
user.suicide_log()
user.gib()
/obj/item/assembly/signaler/anomaly/attackby(obj/item/I, mob/user, params)
if(I.tool_behaviour == TOOL_ANALYZER)
to_chat(user, "<span class='notice'>Analyzing... [src]'s stabilized field is fluctuating along frequency [format_frequency(frequency)], code [code].</span>")
..()
//Anomaly cores
/obj/item/assembly/signaler/anomaly/pyro
name = "\improper pyroclastic anomaly core"
desc = "The neutralized core of a pyroclastic anomaly. It feels warm to the touch. It'd probably be valuable for research."
icon_state = "pyro core"
anomaly_type = /obj/effect/anomaly/pyro
/obj/item/assembly/signaler/anomaly/grav
name = "\improper gravitational anomaly core"
desc = "The neutralized core of a gravitational anomaly. It feels much heavier than it looks. It'd probably be valuable for research."
icon_state = "grav core"
anomaly_type = /obj/effect/anomaly/grav
/obj/item/assembly/signaler/anomaly/flux
name = "\improper flux anomaly core"
desc = "The neutralized core of a flux anomaly. Touching it makes your skin tingle. It'd probably be valuable for research."
icon_state = "flux core"
anomaly_type = /obj/effect/anomaly/flux
/obj/item/assembly/signaler/anomaly/bluespace
name = "\improper bluespace anomaly core"
desc = "The neutralized core of a bluespace anomaly. It keeps phasing in and out of view. It'd probably be valuable for research."
icon_state = "anomaly core"
anomaly_type = /obj/effect/anomaly/bluespace
/obj/item/assembly/signaler/anomaly/vortex
name = "\improper vortex anomaly core"
desc = "The neutralized core of a vortex anomaly. It won't sit still, as if some invisible force is acting on it. It'd probably be valuable for research."
icon_state = "vortex core"
anomaly_type = /obj/effect/anomaly/bhole
/obj/item/assembly/signaler/anomaly/attack_self()
return
/obj/item/assembly/signaler/cyborg
+4 -6
View File
@@ -4,8 +4,7 @@
icon_state = "timer"
custom_materials = list(/datum/material/iron=500, /datum/material/glass=50)
attachable = TRUE
var/ui_x = 275
var/ui_y = 115
var/timing = FALSE
var/time = 5
var/saved_time = 5
@@ -88,11 +87,10 @@
return ..()
return UI_CLOSE
/obj/item/assembly/timer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/item/assembly/timer/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Timer", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "Timer", name)
ui.open()
/obj/item/assembly/timer/ui_data(mob/user)
+7
View File
@@ -94,6 +94,13 @@ Note: If your code uses output() with assets you will need to call asset_flush o
var/list/stacktrace = gib_stack_trace()
log_asset("WARNING: dupe asset added to the asset cache: [asset_name] existing asset md5: [OACI.md5] new asset md5:[ACI.md5]\n[stacktrace.Join("\n")]")
SSassets.cache[asset_name] = ACI
return ACI
/// Returns the url of the asset, currently this is just its name, here to allow further work cdn'ing assets.
/// Can be given an asset as well, this is just a work around for buggy edge cases where two assets may have the same name, doesn't matter now, but it will when the cdn comes.
/proc/get_asset_url(asset_name, asset = null)
var/datum/asset_cache_item/ACI = SSassets.cache[asset_name]
return ACI?.url
//Generated names do not include file extention.
//Used mainly for code that deals with assets in a generic way
@@ -5,6 +5,7 @@
**/
/datum/asset_cache_item
var/name
var/url
var/md5
var/resource
@@ -18,4 +19,5 @@
CRASH("invalid asset sent to asset cache")
debug_world_log("asset cache unexpected success of second fcopy_rsc")
src.name = name
url = name
resource = file
+34 -8
View File
@@ -16,6 +16,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
GLOB.asset_datums[type] = src
register()
/datum/asset/proc/get_url_mappings()
return list()
/datum/asset/proc/register()
return
@@ -30,11 +33,19 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/simple/register()
for(var/asset_name in assets)
register_asset(asset_name, assets[asset_name])
assets[asset_name] = register_asset(asset_name, assets[asset_name])
/datum/asset/simple/send(client)
. = send_asset_list(client, assets)
/datum/asset/simple/get_url_mappings()
. = list()
for (var/asset_name in assets)
var/datum/asset_cache_item/ACI = assets[asset_name]
if (!ACI)
continue
.[asset_name] = ACI.url
// For registering or sending multiple others at once
/datum/asset/group
@@ -50,6 +61,11 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/datum/asset/A = get_asset_datum(type)
. = A.send(C) || .
/datum/asset/group/get_url_mappings()
. = list()
for(var/type in children)
var/datum/asset/A = get_asset_datum(type)
. += A.get_url_mappings()
// spritesheet implementation - coalesces various icons into a single .png file
// and uses CSS to select icons out of that file - saves on transferring some
@@ -70,7 +86,9 @@ GLOBAL_LIST_EMPTY(asset_datums)
if (!name)
CRASH("spritesheet [type] cannot register without a name")
ensure_stripped()
for(var/size_id in sizes)
var/size = sizes[size_id]
register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
var/res_name = "spritesheet_[name].css"
var/fname = "data/spritesheets/[res_name]"
fdel(fname)
@@ -78,10 +96,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
register_asset(res_name, fcopy_rsc(fname))
fdel(fname)
for(var/size_id in sizes)
var/size = sizes[size_id]
register_asset("[name]_[size_id].png", size[SPRSZ_STRIPPED])
/datum/asset/spritesheet/send(client/C)
if (!name)
return
@@ -90,6 +104,15 @@ GLOBAL_LIST_EMPTY(asset_datums)
all += "[name]_[size_id].png"
. = send_asset_list(C, all)
/datum/asset/spritesheet/get_url_mappings()
if (!name)
return
. = list("spritesheet_[name].css" = get_asset_url("spritesheet_[name].css"))
for(var/size_id in sizes)
.["[name]_[size_id].png"] = get_asset_url("[name]_[size_id].png")
/datum/asset/spritesheet/proc/ensure_stripped(sizes_to_strip = sizes)
for(var/size_id in sizes_to_strip)
var/size = sizes[size_id]
@@ -111,7 +134,7 @@ GLOBAL_LIST_EMPTY(asset_datums)
for (var/size_id in sizes)
var/size = sizes[size_id]
var/icon/tiny = size[SPRSZ_ICON]
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[name]_[size_id].png') no-repeat;}"
out += ".[name][size_id]{display:inline-block;width:[tiny.Width()]px;height:[tiny.Height()]px;background:url('[get_asset_url("[name]_[size_id].png")]') no-repeat;}"
for (var/sprite_id in sprites)
var/sprite = sprites[sprite_id]
@@ -162,7 +185,10 @@ GLOBAL_LIST_EMPTY(asset_datums)
Insert("[prefix][prefix2][icon_state_name]", I, icon_state=icon_state_name, dir=direction)
/datum/asset/spritesheet/proc/css_tag()
return {"<link rel="stylesheet" href="spritesheet_[name].css" />"}
return {"<link rel="stylesheet" href="[css_filename()]" />"}
/datum/asset/spritesheet/proc/css_filename()
return get_asset_url("spritesheet_[name].css")
/datum/asset/spritesheet/proc/icon_tag(sprite_name)
var/sprite = sprites[sprite_name]
+23 -8
View File
@@ -6,12 +6,6 @@
"tgui.bundle.css" = 'tgui/packages/tgui/public/tgui.bundle.css',
)
/datum/asset/group/tgui
children = list(
/datum/asset/simple/tgui,
/datum/asset/simple/fontawesome
)
/datum/asset/simple/headers
assets = list(
"alarm_green.gif" = 'icons/program_icons/alarm_green.gif',
@@ -78,6 +72,7 @@
"refresh" = 'icons/pda_icons/pda_refresh.png',
"scanner" = 'icons/pda_icons/pda_scanner.png',
"signaler" = 'icons/pda_icons/pda_signaler.png',
// "skills" = 'icons/pda_icons/pda_skills.png',
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png'
@@ -97,6 +92,10 @@
"stamp-cap" = 'icons/stamp_icons/large_stamp-cap.png',
"stamp-qm" = 'icons/stamp_icons/large_stamp-qm.png',
"stamp-law" = 'icons/stamp_icons/large_stamp-law.png'
// "stamp-chap" = 'icons/stamp_icons/large_stamp-chap.png'
// "stamp-mime" = 'icons/stamp_icons/large_stamp-mime.png',
// "stamp-centcom" = 'icons/stamp_icons/large_stamp-centcom.png',
// "stamp-syndicate" = 'icons/stamp_icons/large_stamp-syndicate.png'
)
@@ -147,7 +146,6 @@
"jquery.min.js" = 'code/modules/goonchat/browserassets/js/jquery.min.js',
)
/datum/asset/simple/goonchat
assets = list(
"json2.min.js" = 'code/modules/goonchat/browserassets/js/json2.min.js',
@@ -375,7 +373,24 @@
"ghost.png" = 'html/ghost.png'
)
/datum/asset/simple/vv
assets = list(
"ghost.png" = 'html/ghost.png'
"view_variables.css" = 'html/admin/view_variables.css'
)
/datum/asset/spritesheet/sheetmaterials
name = "sheetmaterials"
/datum/asset/spritesheet/sheetmaterials/register()
InsertAll("", 'icons/obj/stack_objects.dmi')
// Special case to handle Bluespace Crystals
Insert("polycrystal", 'icons/obj/telescience.dmi', "polycrystal")
..()
/datum/asset/spritesheet/mafia
name = "mafia"
/datum/asset/spritesheet/mafia/register()
InsertAll("", 'icons/obj/mafia.dmi')
..()
@@ -237,11 +237,10 @@
return ..()
return UI_CLOSE
/obj/machinery/airalarm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/airalarm/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "AirAlarm", name, 440, 650, master_ui, state)
ui = new(user, src, "AirAlarm", name)
ui.open()
/obj/machinery/airalarm/ui_data(mob/user)
@@ -26,9 +26,6 @@ Passive gate is similar to the regular pump except:
construction_type = /obj/item/pipe/directional
pipe_state = "passivegate"
ui_x = 335
ui_y = 115
/obj/machinery/atmospherics/components/binary/passive_gate/CtrlClick(mob/user)
if(can_interact(user))
on = !on
@@ -102,11 +99,10 @@ Passive gate is similar to the regular pump except:
))
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/binary/passive_gate/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/binary/passive_gate/ui_data()
@@ -107,11 +107,10 @@
))
radio_connection.post_signal(src, signal, filter = RADIO_ATMOSIA)
/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/binary/pump/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "AtmosPump", name, 335, 115, master_ui, state)
ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/binary/pump/ui_data()
@@ -60,15 +60,14 @@
else if(!opened && our_pressure >= open_pressure)
open()
/obj/machinery/atmospherics/components/binary/relief_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/binary/relief_valve/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "atmos_relief", name, 335, 115, master_ui, state)
ui = new(user, src, "AtmosRelief", name)
ui.open()
/obj/machinery/atmospherics/components/binary/relief_valve/ui_data()
var/data = list()
var/list/data = list()
data["open_pressure"] = round(open_pressure)
data["close_pressure"] = round(close_pressure)
data["max_pressure"] = round(50*ONE_ATMOSPHERE)
@@ -79,11 +78,11 @@
return
switch(action)
if("open_pressure")
var/pressure = params["open_pressure"]
var/pressure = params["pressure"]
if(pressure == "max")
pressure = 50*ONE_ATMOSPHERE
. = TRUE
else if(pressure == "input")
else if(pressure == "input") // The manual expirience.
pressure = input("New output pressure ([close_pressure]-[50*ONE_ATMOSPHERE] kPa):", name, open_pressure) as num|null
if(!isnull(pressure) && !..())
. = TRUE
@@ -94,7 +93,7 @@
open_pressure = clamp(pressure, close_pressure, 50*ONE_ATMOSPHERE)
investigate_log("open pressure was set to [open_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
if("close_pressure")
var/pressure = params["close_pressure"]
var/pressure = params["pressure"]
if(pressure == "max")
pressure = open_pressure
. = TRUE
@@ -92,11 +92,10 @@
))
radio_connection.post_signal(src, signal)
/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/binary/volume_pump/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "AtmosPump", name, 310, 115, master_ui, state)
ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/binary/volume_pump/ui_data()
@@ -133,11 +133,10 @@
set_frequency(frequency)
return ..()
/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/trinary/filter/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "AtmosFilter", name, 475, 185, master_ui, state)
ui = new(user, src, "AtmosFilter", name)
ui.open()
/obj/machinery/atmospherics/components/trinary/filter/ui_data()
@@ -14,9 +14,6 @@
construction_type = /obj/item/pipe/trinary/flippable
pipe_state = "mixer"
ui_x = 370
ui_y = 165
//node 3 is the outlet, nodes 1 & 2 are intakes
/obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user)
@@ -127,11 +124,10 @@
var/datum/pipeline/parent3 = parents[3]
parent3.update = TRUE
/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/trinary/mixer/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "AtmosMixer", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "AtmosMixer", name)
ui.open()
/obj/machinery/atmospherics/components/trinary/mixer/ui_data()
@@ -332,11 +332,13 @@
return
return ..()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.notcontained_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_state(mob/user)
return GLOB.notcontained_state
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Cryo", name, 400, 550, master_ui, state)
ui = new(user, src, "Cryo", name)
ui.open()
/obj/machinery/atmospherics/components/unary/cryo_cell/ui_data()
@@ -20,9 +20,6 @@
pipe_state = "injector"
ui_x = 310
ui_y = 115
/obj/machinery/atmospherics/components/unary/outlet_injector/CtrlClick(mob/user)
if(can_interact(user))
on = !on
@@ -132,7 +129,7 @@
on = !on
if("inject" in signal.data)
spawn inject()
INVOKE_ASYNC(src, .proc/inject)
return
if("set_volume_rate" in signal.data)
@@ -140,22 +137,16 @@
var/datum/gas_mixture/air_contents = airs[1]
volume_rate = clamp(number, 0, air_contents.return_volume())
if("status" in signal.data)
spawn(2)
broadcast_status()
return //do not update_icon
addtimer(CALLBACK(src, .proc/broadcast_status), 2)
spawn(2)
broadcast_status()
update_icon()
if(!("status" in signal.data)) //do not update_icon
update_icon()
/obj/machinery/atmospherics/components/unary/outlet_injector/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/unary/outlet_injector/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "AtmosPump", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "AtmosPump", name)
ui.open()
/obj/machinery/atmospherics/components/unary/outlet_injector/ui_data()
@@ -61,15 +61,14 @@
update_parents()
/obj/machinery/atmospherics/components/unary/relief_valve/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/unary/relief_valve/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "atmos_relief", name, 335, 115, master_ui, state)
ui = new(user, src, "AtmosRelief", name)
ui.open()
/obj/machinery/atmospherics/components/unary/relief_valve/ui_data()
var/data = list()
var/list/data = list()
data["open_pressure"] = round(open_pressure)
data["close_pressure"] = round(close_pressure)
data["max_pressure"] = round(50*ONE_ATMOSPHERE)
@@ -80,7 +79,7 @@
return
switch(action)
if("open_pressure")
var/pressure = params["open_pressure"]
var/pressure = params["pressure"]
if(pressure == "max")
pressure = 50*ONE_ATMOSPHERE
. = TRUE
@@ -95,7 +94,7 @@
open_pressure = clamp(pressure, close_pressure, 50*ONE_ATMOSPHERE)
investigate_log("open pressure was set to [open_pressure] kPa by [key_name(usr)]", INVESTIGATE_ATMOS)
if("close_pressure")
var/pressure = params["close_pressure"]
var/pressure = params["pressure"]
if(pressure == "max")
pressure = open_pressure
. = TRUE
@@ -11,8 +11,6 @@
layer = OBJ_LAYER
plane = GAME_PLANE
circuit = /obj/item/circuitboard/machine/thermomachine
ui_x = 300
ui_y = 230
pipe_flags = PIPING_ONE_PER_TURF
@@ -125,11 +123,10 @@
return ..()
return UI_CLOSE
/obj/machinery/atmospherics/components/unary/thermomachine/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/atmospherics/components/unary/thermomachine/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "ThermoMachine", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "ThermoMachine", name)
ui.open()
/obj/machinery/atmospherics/components/unary/thermomachine/ui_data(mob/user)
@@ -5,8 +5,6 @@
desc = "A canister for the storage of gas."
icon_state = "yellow"
density = TRUE
ui_x = 300
ui_y = 232
var/valve_open = FALSE
var/obj/machinery/atmospherics/components/binary/passive_gate/pump
@@ -319,11 +317,13 @@ obj/machinery/portable_atmospherics/canister/welder_act(mob/living/user, obj/ite
air_update_turf() // Update the environment if needed.
update_icon()
/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/portable_atmospherics/canister/ui_state(mob/user)
return GLOB.physical_state
/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Canister", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "Canister", name)
ui.open()
/obj/machinery/portable_atmospherics/canister/ui_data()
@@ -8,8 +8,6 @@
name = "portable air pump"
icon_state = "psiphon:0"
density = TRUE
ui_x = 300
ui_y = 315
var/on = FALSE
var/direction = PUMP_OUT
@@ -83,11 +81,10 @@
investigate_log("[key_name(user)] started a transfer into [holding].", INVESTIGATE_ATMOS)
/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/portable_atmospherics/pump/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "PortablePump", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "PortablePump", name)
ui.open()
/obj/machinery/portable_atmospherics/pump/ui_data()
@@ -62,11 +62,10 @@
on = !on
update_icon()
/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.physical_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/portable_atmospherics/scrubber/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "PortableScrubber", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "PortableScrubber", name)
ui.open()
/obj/machinery/portable_atmospherics/scrubber/ui_data()
+3 -4
View File
@@ -277,11 +277,10 @@ GLOBAL_LIST_EMPTY(gateway_destinations)
. = ..()
try_to_linkup()
/obj/machinery/computer/gateway_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui, force_open, datum/tgui/master_ui, datum/ui_state/state = GLOB.default_state)
. = ..()
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/gateway_control/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Gateway", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "Gateway", name)
ui.open()
/obj/machinery/computer/gateway_control/ui_data(mob/user)
+29 -56
View File
@@ -1,18 +1,18 @@
#define PRINTER_TIMEOUT 10
/obj/machinery/computer/bounty
name = "Nanotrasen bounty console"
name = "\improper Nanotrasen bounty console"
desc = "Used to check and claim bounties offered by Nanotrasen"
icon_screen = "bounty"
circuit = /obj/item/circuitboard/computer/bounty
light_color = "#E2853D"//orange
var/printer_ready = 0 //cooldown var
var/static/datum/bank_account/cargocash
/obj/machinery/computer/bounty/Initialize()
. = ..()
printer_ready = world.time + PRINTER_TIMEOUT
cargocash = SSeconomy.get_dep_account(ACCOUNT_CAR)
/obj/machinery/computer/bounty/proc/print_paper()
new /obj/item/paper/bounty_printout(loc)
@@ -23,70 +23,43 @@
/obj/item/paper/bounty_printout/Initialize()
. = ..()
info = "<h2>Nanotrasen Cargo Bounties</h2></br>"
update_icon()
for(var/datum/bounty/B in GLOB.bounties_list)
if(B.claimed)
continue
info += {"<h3>[B.name]</h3>
<ul><li>Reward: [B.reward_string()]</li>
<li>Completed: [B.completion_string()]</li></ul>"}
/obj/machinery/computer/bounty/ui_interact(mob/user)
. = ..()
<ul><li>Reward: [B.reward_string()]</li>
<li>Completed: [B.completion_string()]</li></ul>"}
/obj/machinery/computer/bounty/ui_interact(mob/user, datum/tgui/ui)
if(!GLOB.bounties_list.len)
setup_bounties()
var/datum/bank_account/D = SSeconomy.get_dep_account(ACCOUNT_CAR)
var/list/dat = list({"<a href='?src=[REF(src)];refresh=1'>Refresh</a>
<a href='?src=[REF(src)];refresh=1;choice=Print'>Print Paper</a>
<p>Credits: <b>[D.account_balance]</b></p>
<table style="text-align:center;" border="1" cellspacing="0" width="100%">
<tr><th>Name</th><th>Description</th><th>Reward</th><th>Completion</th><th>Status</th></tr>"})
for(var/datum/bounty/B in GLOB.bounties_list)
if(B.claimed)
dat += "<tr style='background-color:#294675;'>"
else if(B.can_claim())
dat += "<tr style='background-color:#4F7529;'>"
else
dat += "<tr style='background-color:#990000;'>"
if(B.high_priority)
dat += {"<td><b>[B.name]</b></td>
<td><b>High Priority:</b> [B.description]</td>
<td><b>[B.reward_string()]</b></td>"}
else
dat += {"<td>[B.name]</td>
<td>[B.description]</td>
<td>[B.reward_string()]</td>"}
dat += "<td>[B.completion_string()]</td>"
if(B.claimed)
dat += "<td>Claimed</td>"
else if(B.can_claim())
dat += "<td><A href='?src=[REF(src)];refresh=1;choice=Claim;d_rec=[REF(B)]'>Claim</a></td>"
else
dat += "<td>Unclaimed</td>"
dat += "</tr>"
dat += "</table>"
dat = dat.Join()
var/datum/browser/popup = new(user, "bounties", "Nanotrasen Bounties", 700, 600)
popup.set_content(dat)
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
popup.open()
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "CargoBountyConsole", name)
ui.open()
/obj/machinery/computer/bounty/Topic(href, href_list)
/obj/machinery/computer/bounty/ui_data(mob/user)
var/list/data = list()
var/list/bountyinfo = list()
for(var/datum/bounty/B in GLOB.bounties_list)
bountyinfo += list(list("name" = B.name, "description" = B.description, "reward_string" = B.reward_string(), "completion_string" = B.completion_string() , "claimed" = B.claimed, "can_claim" = B.can_claim(), "priority" = B.high_priority, "bounty_ref" = REF(B)))
data["stored_cash"] = cargocash.account_balance
data["bountydata"] = bountyinfo
return data
/obj/machinery/computer/bounty/ui_act(action,params)
if(..())
return
switch(href_list["choice"])
switch(action)
if("ClaimBounty")
var/datum/bounty/cashmoney = locate(params["bounty"]) in GLOB.bounties_list
if(cashmoney)
cashmoney.claim()
return TRUE
if("Print")
if(printer_ready < world.time)
printer_ready = world.time + PRINTER_TIMEOUT
print_paper()
if("Claim")
var/datum/bounty/B = locate(href_list["d_rec"])
if(B in GLOB.bounties_list)
B.claim()
if(href_list["refresh"])
playsound(src, "terminal_type", 25, 0)
updateUsrDialog()
return
+154 -63
View File
@@ -11,7 +11,7 @@
/client/proc/centcom_podlauncher() //Creates a verb for admins to open up the ui
set name = "Config/Launch Supplypod"
set desc = "Configure and launch a Centcom supplypod full of whatever your heart desires!"
set desc = "Configure and launch a CentCom supplypod full of whatever your heart desires!"
set category = "Admin"
var/datum/centcom_podlauncher/plaunch = new(usr)//create the datum
plaunch.ui_interact(usr)//datum has a tgui component, here we open the window
@@ -23,7 +23,10 @@
var/turf/oldTurf //Keeps track of where the user was at if they use the "teleport to centcom" button, so they can go back
var/client/holder //client of whoever is using this datum
var/area/bay //What bay we're using to launch shit from.
var/turf/dropoff_turf //If we're reversing, where the reverse pods go
var/picking_dropoff_turf
var/launchClone = FALSE //If true, then we don't actually launch the thing in the bay. Instead we call duplicateObject() and send the result
var/launchRandomItem = FALSE //If true, lauches a single random item instead of everything on a turf.
var/launchChoice = 1 //Determines if we launch all at once (0) , in order (1), or at random(2)
var/explosionChoice = 0 //Determines if there is no explosion (0), custom explosion (1), or just do a maxcap (2)
var/damageChoice = 0 //Determines if we do no damage (0), custom amnt of damage (1), or gib + 5000dmg (2)
@@ -50,20 +53,25 @@
temp_pod = new(locate(/area/centcom/supplypod/podStorage) in GLOB.sortedAreas) //Create a new temp_pod in the podStorage area on centcom (so users are free to look at it and change other variables if needed)
orderedArea = createOrderedArea(bay) //Order all the turfs in the selected bay (top left to bottom right) to a single list. Used for the "ordered" mode (launchChoice = 1)
/datum/centcom_podlauncher/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, \
force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.admin_state)//ui_interact is called when the client verb is called.
/datum/centcom_podlauncher/ui_state(mob/user)
return GLOB.admin_state
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/centcom_podlauncher/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "CentcomPodLauncher", "Config/Launch Supplypod", 700, 700, master_ui, state)
ui = new(user, src, "CentcomPodLauncher")
ui.open()
/datum/centcom_podlauncher/ui_data(mob/user) //Sends info about the pod to the UI.
var/list/data = list() //*****NOTE*****: Many of these comments are similarly described in supplypod.dm. If you change them here, please consider doing so in the supplypod code as well!
var/B = (istype(bay, /area/centcom/supplypod/loading/one)) ? 1 : (istype(bay, /area/centcom/supplypod/loading/two)) ? 2 : (istype(bay, /area/centcom/supplypod/loading/three)) ? 3 : (istype(bay, /area/centcom/supplypod/loading/four)) ? 4 : 0 //top ten THICCEST FUCKING TERNARY CONDITIONALS OF 2036
data["bay"] = B //Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map.
var/B = (istype(bay, /area/centcom/supplypod/loading/one)) ? 1 : (istype(bay, /area/centcom/supplypod/loading/two)) ? 2 : (istype(bay, /area/centcom/supplypod/loading/three)) ? 3 : (istype(bay, /area/centcom/supplypod/loading/four)) ? 4 : 0 //(istype(bay, /area/centcom/supplypod/loading/ert)) ? 5 : 0 //top ten THICCEST FUCKING TERNARY CONDITIONALS OF 2036
data["bay"] = bay //Holds the current bay the user is launching objects from. Bays are specific rooms on the centcom map.
data["bayNumber"] = B //Holds the bay as a number. Useful for comparisons in centcom_podlauncher.ract
data["oldArea"] = (oldTurf ? get_area(oldTurf) : null) //Holds the name of the area that the user was in before using the teleportCentcom action
data["picking_dropoff_turf"] = picking_dropoff_turf //If we're picking or have picked a dropoff turf. Only works when pod is in reverse mode
data["dropoff_turf"] = dropoff_turf //The turf that reverse pods will drop their newly acquired cargo off at
data["launchClone"] = launchClone //Do we launch the actual items in the bay or just launch clones of them?
data["launchRandomItem"] = launchRandomItem //Do we launch a single random item instead of everything on the turf?
data["launchChoice"] = launchChoice //Launch turfs all at once (0), ordered (1), or randomly(1)
data["explosionChoice"] = explosionChoice //An explosion that occurs when landing. Can be no explosion (0), custom explosion (1), or maxcap (2)
data["damageChoice"] = damageChoice //Damage that occurs to any mob under the pod when it lands. Can be no damage (0), custom damage (1), or gib+5000dmg (2)
@@ -72,11 +80,12 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
data["openingDelay"] = temp_pod.openingDelay //How long the pod takes to open after landing
data["departureDelay"] = temp_pod.departureDelay //How long the pod takes to leave after opening (if bluespace=true, it deletes. if reversing=true, it flies back to centcom)
data["styleChoice"] = temp_pod.style //Style is a variable that keeps track of what the pod is supposed to look like. It acts as an index to the POD_STYLES list in cargo.dm defines to get the proper icon/name/desc for the pod.
data["effectShrapnel"] = FALSE //temp_pod.effectShrapnel //If true, creates a cloud of shrapnel of a decided type and magnitude on landing
data["effectStun"] = temp_pod.effectStun //If true, stuns anyone under the pod when it launches until it lands, forcing them to get hit by the pod. Devilish!
data["effectLimb"] = temp_pod.effectLimb //If true, pops off a limb (if applicable) from anyone caught under the pod when it lands
data["effectOrgans"] = temp_pod.effectOrgans //If true, yeets the organs out of any bodies caught under the pod when it lands
data["effectBluespace"] = temp_pod.bluespace //If true, the pod deletes (in a shower of sparks) after landing
data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isnt displayed on the turf where the pod will land
data["effectStealth"] = temp_pod.effectStealth //If true, a target icon isn't displayed on the turf where the pod will land
data["effectQuiet"] = temp_pod.effectQuiet //The female sniper. If true, the pod makes no noise (including related explosions, opening sounds, etc)
data["effectMissile"] = temp_pod.effectMissile //If true, the pod deletes the second it lands. If you give it an explosion, it will act like a missile exploding as it hits the ground
data["effectCircle"] = temp_pod.effectCircle //If true, allows the pod to come in at any angle. Bit of a weird feature but whatever its here
@@ -115,20 +124,41 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
bay = locate(/area/centcom/supplypod/loading/four) in GLOB.sortedAreas
refreshBay()
. = TRUE
if("bay5")
to_chat(usr, "LetterN is lazy and didin't bother porting this new cc area!")
return
// bay = locate(/area/centcom/supplypod/loading/ert) in GLOB.sortedAreas
// refreshBay()
// . = TRUE
if("pickDropoffTurf") //Enters a mode that lets you pick the dropoff location for reverse pods
if (picking_dropoff_turf)
picking_dropoff_turf = FALSE
updateCursor(FALSE, FALSE) //Update the cursor of the user to a cool looking target icon
return
if (launcherActivated)
launcherActivated = FALSE //We don't want to have launch mode enabled while we're picking a turf
picking_dropoff_turf = TRUE
updateCursor(FALSE, TRUE) //Update the cursor of the user to a cool looking target icon
. = TRUE
if("clearDropoffTurf")
picking_dropoff_turf = FALSE
dropoff_turf = null
updateCursor(FALSE, FALSE)
. = TRUE
if("teleportCentcom") //Teleports the user to the centcom supply loading facility.
var/mob/M = holder.mob //We teleport whatever mob the client is attached to at the point of clicking
oldTurf = get_turf(M) //Used for the "teleportBack" action
var/area/A = locate(/area/centcom/supplypod/loading) in GLOB.sortedAreas
var/area/A = locate(bay) in GLOB.sortedAreas
var/list/turfs = list()
for(var/turf/T in A)
turfs.Add(T) //Fill a list with turfs in the area
var/turf/T = safepick(turfs) //Only teleport if the list isn't empty
if(!T) //If the list is empty, error and cancel
if (!length(turfs)) //If the list is empty, error and cancel
to_chat(M, "Nowhere to jump to!")
return
return //Only teleport if the list isn't empty
var/turf/T = pick(turfs)
M.forceMove(T) //Perform the actual teleport
log_admin("[key_name(usr)] jumped to [AREACOORD(A)]")
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(A)]")
log_admin("[key_name(usr)] jumped to [AREACOORD(T)]")
message_admins("[key_name_admin(usr)] jumped to [AREACOORD(T)]")
. = TRUE
if("teleportBack") //After teleporting to centcom, this button allows the user to teleport to the last spot they were at.
var/mob/M = holder.mob
@@ -144,6 +174,9 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if("launchClone") //Toggles the launchClone var. See variable declarations above for what this specifically means
launchClone = !launchClone
. = TRUE
if("launchRandomItem") //Pick random turfs from the supplypod bay at centcom to launch
launchRandomItem = !launchRandomItem
. = TRUE
if("launchOrdered") //Launch turfs (from the orderedArea list) one at a time in order, from the supplypod bay at centcom
if (launchChoice == 1) //launchChoice 1 represents ordered. If we push "ordered" and it already is, then we go to default value
launchChoice = 0
@@ -152,7 +185,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
launchChoice = 1
updateSelector()
. = TRUE
if("launchRandom") //Pick random turfs from the supplypod bay at centcom to launch
if("launchRandomTurf") //Pick random turfs from the supplypod bay at centcom to launch
if (launchChoice == 2)
launchChoice = 0
updateSelector()
@@ -170,11 +203,11 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
var/list/expNames = list("Devastation", "Heavy Damage", "Light Damage", "Flame") //Explosions have a range of different types of damage
var/list/boomInput = list()
for (var/i=1 to expNames.len) //Gather input from the user for the value of each type of damage
boomInput.Add(input("[expNames[i]] Range", "Enter the [expNames[i]] range of the explosion. WARNING: This ignores the bomb cap!", 0) as null|num)
boomInput.Add(input("Enter the [expNames[i]] range of the explosion. WARNING: This ignores the bomb cap!", "[expNames[i]] Range", 0) as null|num)
if (isnull(boomInput[i]))
return
if (!isnum(boomInput[i])) //If the user doesn't input a number, set that specific explosion value to zero
alert(usr, "That wasnt a number! Value set to default (zero) instead.")
alert(usr, "That wasn't a number! Value set to default (zero) instead.")
boomInput = 0
explosionChoice = 1
temp_pod.explosionSize = boomInput
@@ -192,11 +225,11 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
damageChoice = 0
temp_pod.damage = 0
return
var/damageInput = input("How much damage to deal", "Enter the amount of brute damage dealt by getting hit", 0) as null|num
var/damageInput = input("Enter the amount of brute damage dealt by getting hit","How much damage to deal", 0) as null|num
if (isnull(damageInput))
return
if (!isnum(damageInput)) //Sanitize the input for damage to deal.s
alert(usr, "That wasnt a number! Value set to default (zero) instead.")
alert(usr, "That wasn't a number! Value set to default (zero) instead.")
damageInput = 0
damageChoice = 1
temp_pod.damage = damageInput
@@ -226,13 +259,32 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
temp_pod.desc = descInput
temp_pod.adminNamed = TRUE //This variable is checked in the supplypod/setStyle() proc
. = TRUE
/*
if("effectShrapnel") //Creates a cloud of shrapnel on landing
if (temp_pod.effectShrapnel == TRUE) //If already doing custom damage, set back to default (no shrapnel)
temp_pod.effectShrapnel = FALSE
return
var/shrapnelInput = input("Please enter the type of pellet cloud you'd like to create on landing (Can be any projectile!)", "Projectile Typepath", 0) in sortList(subtypesof(/obj/item/projectile), /proc/cmp_typepaths_asc)
if (isnull(shrapnelInput))
return
var/shrapnelMagnitude = input("Enter the magnitude of the pellet cloud. This is usually a value around 1-5. Please note that Ryll-Ryll has asked me to tell you that if you go too crazy with the projectiles you might crash the server. So uh, be gentle!", "Shrapnel Magnitude", 0) as null|num
if (isnull(shrapnelMagnitude))
return
if (!isnum(shrapnelMagnitude))
alert(usr, "That wasn't a number! Value set to 3 instead.")
shrapnelMagnitude = 3
temp_pod.shrapnel_type = shrapnelInput
temp_pod.shrapnel_magnitude = shrapnelMagnitude
temp_pod.effectShrapnel = TRUE
. = TRUE
*/
if("effectStun") //Toggle: Any mob under the pod is stunned (cant move) until the pod lands, hitting them!
temp_pod.effectStun = !temp_pod.effectStun
. = TRUE
if("effectLimb") //Toggle: Anyone carbon mob under the pod loses a limb when it lands
temp_pod.effectLimb = !temp_pod.effectLimb
. = TRUE
if("effectOrgans") //Toggle: Any carbon mob under the pod loses every limb and organ
if("effectOrgans") //Toggle: Anyone carbon mob under the pod loses a limb when it lands
temp_pod.effectOrgans = !temp_pod.effectOrgans
. = TRUE
if("effectBluespace") //Toggle: Deletes the pod after landing
@@ -253,7 +305,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if("effectBurst") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target
effectBurst = !effectBurst
. = TRUE
if("effectAnnounce") //Toggle: Sends a ghost announcement.
if("effectAnnounce") //Toggle: Launch 5 pods (with a very slight delay between) in a 3x3 area centered around the target
effectAnnounce = !effectAnnounce
. = TRUE
if("effectReverse") //Toggle: Don't send any items. Instead, after landing, close (taking any objects inside) and go back to the centcom bay it came from
@@ -272,15 +324,15 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
. = TRUE
////////////////////////////TIMER DELAYS//////////////////
if("fallDuration") //Change the falling animation duration
if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the fall duration has already been changed when we push the "change value" button, then set it to default
if("fallDuration") //Change the time it takes the pod to land, after firing
if (temp_pod.fallDuration != initial(temp_pod.fallDuration)) //If the landing delay has already been changed when we push the "change value" button, then set it to default
temp_pod.fallDuration = initial(temp_pod.fallDuration)
return
var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num
var/timeInput = input("Enter the duration of the pod's falling animation, in seconds", "Delay Time", initial(temp_pod.fallDuration) * 0.1) as null|num
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default
alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.")
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallDuration)*0.1]) instead.")
timeInput = initial(temp_pod.fallDuration)
temp_pod.fallDuration = 10 * timeInput
. = TRUE
@@ -292,7 +344,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input, if it doesnt check out, error and set to default
alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.landingDelay)*0.1]) instead.")
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.landingDelay)*0.1]) instead.")
timeInput = initial(temp_pod.landingDelay)
temp_pod.landingDelay = 10 * timeInput
. = TRUE
@@ -304,7 +356,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput)) //Sanitize input
alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.openingDelay)*0.1]) instead.")
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.openingDelay)*0.1]) instead.")
timeInput = initial(temp_pod.openingDelay)
temp_pod.openingDelay = 10 * timeInput
. = TRUE
@@ -316,13 +368,13 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput))
alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.departureDelay)*0.1]) instead.")
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.departureDelay)*0.1]) instead.")
timeInput = initial(temp_pod.departureDelay)
temp_pod.departureDelay = 10 * timeInput
. = TRUE
////////////////////////////ADMIN SOUNDS//////////////////
if("fallingSound") //Admin sound from a local file that plays when the pod falls
if("fallSound") //Admin sound from a local file that plays when the pod lands
if ((temp_pod.fallingSound) != initial(temp_pod.fallingSound))
temp_pod.fallingSound = initial(temp_pod.fallingSound)
temp_pod.fallingSoundLength = initial(temp_pod.fallingSoundLength)
@@ -334,7 +386,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(timeInput))
return
if (!isnum(timeInput))
alert(usr, "That wasnt a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.")
alert(usr, "That wasn't a number! Value set to default ([initial(temp_pod.fallingSoundLength)*0.1]) instead.")
temp_pod.fallingSound = soundInput
temp_pod.fallingSoundLength = 10 * timeInput
. = TRUE
@@ -369,7 +421,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (temp_pod.soundVolume != initial(temp_pod.soundVolume))
temp_pod.soundVolume = initial(temp_pod.soundVolume)
return
var/soundInput = input(holder, "Please pick a volume. Default is between 1 and 100 with 80 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num
var/soundInput = input(holder, "Please pick a volume. Default is between 1 and 100 with 50 being average, but pick whatever. I'm a notification, not a cop. If you still cant hear your sound, consider turning on the Quiet effect. It will silence all pod sounds except for the custom admin ones set by the previous three buttons.", "Pick Admin Sound Volume") as null|num
if (isnull(soundInput))
return
temp_pod.soundVolume = soundInput
@@ -421,26 +473,36 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
. = TRUE
if("giveLauncher") //Enters the "Launch Mode". When the launcher is activated, temp_pod is cloned, and the result it filled and launched anywhere the user clicks (unless specificTarget is true)
launcherActivated = !launcherActivated
updateCursor(launcherActivated) //Update the cursor of the user to a cool looking target icon
updateCursor(launcherActivated, FALSE) //Update the cursor of the user to a cool looking target icon
. = TRUE
if("clearBay") //Delete all mobs and objs in the selected bay
if(alert(usr, "This will delete all objs and mobs in [bay]. Are you sure?", "Confirmation", "Delete that shit", "No") == "Delete that shit")
clearBay()
refreshBay()
. = TRUE
/datum/centcom_podlauncher/ui_close() //Uses the destroy() proc. When the user closes the UI, we clean up the temp_pod and supplypod_selector variables.
qdel(src)
/datum/centcom_podlauncher/proc/updateCursor(var/launching) //Update the moues of the user
if (holder) //Check to see if we have a client
if (launching) //If the launching param is true, we give the user new mouse icons.
holder.mouse_up_icon = 'icons/effects/supplypod_target.dmi' //Icon for when mouse is released
holder.mouse_down_icon = 'icons/effects/supplypod_down_target.dmi' //Icon for when mouse is pressed
holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released)
holder.click_intercept = src //Create a click_intercept so we know where the user is clicking
else
var/mob/M = holder.mob
holder.mouse_up_icon = null
holder.mouse_down_icon = null
holder.click_intercept = null
if (M)
M.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)()
/datum/centcom_podlauncher/proc/updateCursor(var/launching, var/turf_picking) //Update the mouse of the user
if (!holder) //Can't update the mouse icon if the client doesnt exist!
return
if (launching || turf_picking) //If the launching param is true, we give the user new mouse icons.
if(launching)
holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_target.dmi' //Icon for when mouse is released
holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_down_target.dmi' //Icon for when mouse is pressed
if(turf_picking)
holder.mouse_up_icon = 'icons/effects/mouse_pointers/supplypod_pickturf.dmi' //Icon for when mouse is released
holder.mouse_down_icon = 'icons/effects/mouse_pointers/supplypod_pickturf_down.dmi' //Icon for when mouse is pressed
holder.mouse_pointer_icon = holder.mouse_up_icon //Icon for idle mouse (same as icon for when released)
holder.click_intercept = src //Create a click_intercept so we know where the user is clicking
else
var/mob/M = holder.mob
holder.mouse_up_icon = null
holder.mouse_down_icon = null
holder.click_intercept = null
if (M)
M.update_mouse_pointer() //set the moues icons to null, then call update_moues_pointer() which resets them to the correct values based on what the mob is doing (in a mech, holding a spell, etc)()
/datum/centcom_podlauncher/proc/InterceptClickOn(user,params,atom/target) //Click Intercept so we know where to send pods where the user clicks
var/list/pa = params2list(params)
@@ -461,7 +523,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
else
return //if target is null and we don't have a specific target, cancel
if (effectAnnounce)
deadchat_broadcast("<span class='deadsay'>A special package is being launched at the station!</span>", turf_target = target)
deadchat_broadcast("A special package is being launched at the station!", turf_target = target) //, message_type=DEADCHAT_ANNOUNCEMENT)
var/list/bouttaDie = list()
for (var/mob/living/M in target)
bouttaDie.Add(M)
@@ -479,6 +541,15 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
else
launch(target) //If we couldn't locate an adjacent turf, just launch at the normal target
sleep(rand()*2) //looks cooler than them all appearing at once. Gives the impression of burst fire.
else if (picking_dropoff_turf)
//Clicking on UI elements shouldn't pick a dropoff turf
if(istype(target,/obj/screen))
return FALSE
. = TRUE
if(left_click) //When we left click:
dropoff_turf = get_turf(target)
to_chat(user, "<span class = 'notice'> You've selected [dropoff_turf] at [COORD(dropoff_turf)] as your dropoff location.</span>")
/datum/centcom_podlauncher/proc/refreshBay() //Called whenever the bay is switched, as well as wheneber a pod is launched
orderedArea = createOrderedArea(bay) //Create an ordered list full of turfs form the bay
@@ -489,7 +560,7 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
to_chat(holder.mob, "No /area/centcom/supplypod/loading/one (or /two or /three or /four) in the world! You can make one yourself (then refresh) for now, but yell at a mapper to fix this, today!")
CRASH("No /area/centcom/supplypod/loading/one (or /two or /three or /four) has been mapped into the centcom z-level!")
orderedArea = list()
if (!isemptylist(A.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values
if (length(A.contents)) //Go through the area passed into the proc, and figure out the top left and bottom right corners by calculating max and min values
var/startX = A.contents[1].x //Create the four values (we do it off a.contents[1] so they have some sort of arbitrary initial value. They should be overwritten in a few moments)
var/endX = A.contents[1].x
var/startY = A.contents[1].y
@@ -512,12 +583,12 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
numTurfs = 0 //Counts the number of turfs that can be launched (remember, supplypods either launch all at once or one turf-worth of items at a time)
acceptableTurfs = list()
for (var/turf/T in orderedArea) //Go through the orderedArea list
if (typecache_filter_list_reverse(T.contents, ignored_atoms).len != 0) //if there is something in this turf that isnt in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list
if (typecache_filter_list_reverse(T.contents, ignored_atoms).len != 0) //if there is something in this turf that isn't in the blacklist, we consider this turf "acceptable" and add it to the acceptableTurfs list
acceptableTurfs.Add(T) //Because orderedArea was an ordered linear list, acceptableTurfs will be as well.
numTurfs ++
launchList = list() //Anything in launchList will go into the supplypod when it is launched
if (!isemptylist(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true)
if (length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We dont fill the supplypod if acceptableTurfs is empty, if the pod is going in reverse (effectReverse=true), or if the pod is acitng like a missile (effectMissile=true)
switch(launchChoice)
if(0) //If we are launching all the turfs at once
for (var/turf/T in acceptableTurfs)
@@ -536,22 +607,36 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
if (isnull(A))
return
var/obj/structure/closet/supplypod/centcompod/toLaunch = DuplicateObject(temp_pod) //Duplicate the temp_pod (which we have been varediting or configuring with the UI) and store the result
toLaunch.bay = bay //Bay is currently a nonstatic expression, so it cant go into toLaunch using DuplicateObject
toLaunch.update_icon()//we update_icon() here so that the door doesnt "flicker on" right after it lands
if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those
for (var/atom/movable/O in launchList)
DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod
new /obj/effect/abstract/DPtarget(A, toLaunch) //Create the DPTarget, which will eventually forceMove the temp_pod to it's location
/*
if(dropoff_turf)
toLaunch.reverse_dropoff_turf = dropoff_turf
else
for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList
toLaunch.reverse_dropoff_turf = bay //Bay is currently a nonstatic expression, so it cant go into toLaunch using DuplicateObject
*/
toLaunch.update_icon()//we update_icon() here so that the door doesnt "flicker on" right after it lands
// var/shippingLane = GLOB.areas_by_type[/area/centcom/supplypod/fly_me_to_the_moon]
// toLaunch.forceMove(shippingLane) The shipping lane is temporarily closed due to ratvarian blockades
if (launchClone) //We arent launching the actual items from the bay, rather we are creating clones and launching those
if(launchRandomItem)
var/atom/movable/O = pick_n_take(launchList)
DuplicateObject(O).forceMove(toLaunch) //Duplicate a single atom/movable from launchList and forceMove it into the supplypod
else
for (var/atom/movable/O in launchList)
DuplicateObject(O).forceMove(toLaunch) //Duplicate each atom/movable in launchList and forceMove them into the supplypod
else
if(launchRandomItem)
var/atom/movable/O = pick_n_take(launchList)
O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location
else
for (var/atom/movable/O in launchList) //If we aren't cloning the objects, just go through the launchList
O.forceMove(toLaunch) //and forceMove any atom/moveable into the supplypod
new /obj/effect/abstract/DPtarget(A, toLaunch) //Then, create the DPTarget effect, which will eventually forceMove the temp_pod to it's location
if (launchClone)
launchCounter++ //We only need to increment launchCounter if we are cloning objects.
//If we aren't cloning objects, taking and removing the first item each time from the acceptableTurfs list will inherently iterate through the list in order
/datum/centcom_podlauncher/proc/updateSelector() //Ensures that the selector effect will showcase the next item if needed
if (launchChoice == 1 && !isemptylist(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay
if (launchChoice == 1 && length(acceptableTurfs) && !temp_pod.reversing && !temp_pod.effectMissile) //We only show the selector if we are taking items from the bay
var/index = launchCounter + 1 //launchCounter acts as an index to the ordered acceptableTurfs list, so adding one will show the next item in the list
if (index > acceptableTurfs.len) //out of bounds check
index = 1
@@ -559,8 +644,14 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
else
selector.moveToNullspace() //Otherwise, we move the selector to nullspace until it is needed again
/datum/centcom_podlauncher/proc/clearBay() //Clear all objs and mobs from the selected bay
for (var/obj/O in bay.GetAllContents())
qdel(O)
for (var/mob/M in bay.GetAllContents())
qdel(M)
/datum/centcom_podlauncher/Destroy() //The Destroy() proc. This is called by ui_close proc, or whenever the user leaves the game
updateCursor(FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode
updateCursor(FALSE, FALSE) //Make sure our moues cursor resets to default. False means we are not in launch mode
qdel(temp_pod) //Delete the temp_pod
qdel(selector) //Delete the selector effect
. = ..()
@@ -581,8 +672,8 @@ force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.adm
for (var/X in temp_pod.explosionSize)
explosionString += "[X]|"
var/msg = "launched [podString][whomString].[delayString][damageString][explosionString]]"
message_admins("[key_name_admin(usr)] [msg] in [AREACOORD(specificTarget)].")
if (!isemptylist(whoDyin))
var/msg = "launched [podString] towards [whomString] [delayString][damageString][explosionString]"
message_admins("[key_name_admin(usr)] [msg] in [ADMIN_VERBOSEJMP(specificTarget)].")
if (length(whoDyin))
for (var/mob/living/M in whoDyin)
admin_ticket_log(M, "[key_name_admin(usr)] [msg]")
+15 -19
View File
@@ -3,9 +3,6 @@
desc = "Used to order supplies, approve requests, and control the shuttle."
icon_screen = "supply"
circuit = /obj/item/circuitboard/computer/cargo
req_access = list(ACCESS_CARGO)
ui_x = 780
ui_y = 750
var/requestonly = FALSE
var/contraband = FALSE
@@ -27,7 +24,6 @@
desc = "Used to request supplies from cargo."
icon_screen = "request"
circuit = /obj/item/circuitboard/computer/cargo/request
req_access = list()
requestonly = TRUE
/obj/machinery/computer/cargo/Initialize()
@@ -66,15 +62,12 @@
var/obj/item/circuitboard/computer/cargo/board = circuit
board.contraband = TRUE
board.obj_flags |= EMAGGED
req_access = list()
update_static_data(user)
return ..()
/obj/machinery/computer/cargo/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/cargo/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Cargo", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "Cargo", name)
ui.open()
/obj/machinery/computer/cargo/ui_data()
@@ -120,7 +113,6 @@
var/list/data = list()
data["requestonly"] = requestonly
data["supplies"] = list()
data["emagged"] = obj_flags & EMAGGED
for(var/pack in SSshuttle.supply_packs)
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
if(!data["supplies"][P.group])
@@ -135,8 +127,8 @@
"cost" = P.cost,
"id" = pack,
"desc" = P.desc || P.name, // If there is a description, use it. Otherwise use the pack's name.
"goody" = P.goody,
"private_goody" = P.goody == PACK_GOODY_PRIVATE,
"goody" = P.goody == PACK_GOODY_PUBLIC,
"access" = P.access,
"can_private_buy" = P.can_private_buy
))
@@ -145,9 +137,6 @@
/obj/machinery/computer/cargo/ui_act(action, params, datum/tgui/ui)
if(..())
return
if(!allowed(usr))
to_chat(usr, "<span class='notice'>Access denied.</span>")
return
switch(action)
if("send")
if(!SSshuttle.supply.canMove())
@@ -179,6 +168,8 @@
else
SSshuttle.shuttle_loan.loan_shuttle()
say("The supply shuttle has been loaned to CentCom.")
investigate_log("[key_name(usr)] accepted a shuttle loan event.", INVESTIGATE_CARGO)
log_game("[key_name(usr)] accepted a shuttle loan event.")
. = TRUE
if("add")
var/id = text2path(params["id"])
@@ -200,13 +191,15 @@
rank = "Silicon"
var/datum/bank_account/account
if(self_paid)
if(!pack.can_private_buy && !(obj_flags & EMAGGED))
return
var/obj/item/card/id/id_card = usr.get_idcard(TRUE)
if(self_paid && ishuman(usr))
var/mob/living/carbon/human/H = usr
var/obj/item/card/id/id_card = H.get_idcard(TRUE)
if(!istype(id_card))
say("No ID card detected.")
return
if(istype(id_card, /obj/item/card/id/departmental_budget))
say("The [src] rejects [id_card].")
return
account = id_card.registered_account
if(!istype(account))
say("Invalid bank account.")
@@ -241,6 +234,9 @@
SSshuttle.shoppinglist += SO
if(self_paid)
say("Order processed. The price will be charged to [account.account_holder]'s bank account on delivery.")
if(requestonly && message_cooldown < world.time)
radio.talk_into(src, "A new order has been requested.", RADIO_CHANNEL_SUPPLY)
message_cooldown = world.time + 30 SECONDS
. = TRUE
if("remove")
var/id = text2num(params["id"])
+12 -18
View File
@@ -1,5 +1,5 @@
#define MAX_EMAG_ROCKETS 8
#define BEACON_COST 5000
#define BEACON_COST 500
#define SP_LINKED 1
#define SP_READY 2
#define SP_LAUNCH 3
@@ -13,10 +13,9 @@
All sales are near instantaneous - please choose carefully"
icon_screen = "supply_express"
circuit = /obj/item/circuitboard/computer/cargo/express
ui_x = 600
ui_y = 700
blockade_warning = "Bluespace instability detected. Delivery impossible."
req_access = list(ACCESS_QM)
var/message
var/printed_beacons = 0 //number of beacons printed. Used to determine beacon names.
var/list/meme_pack_data
@@ -42,7 +41,7 @@
to_chat(user, "<span class='notice'>You [locked ? "lock" : "unlock"] the interface.</span>")
return
else if(istype(W, /obj/item/disk/cargo/bluespace_pod))
podType = /obj/structure/closet/supplypod/bluespacepod
podType = /obj/structure/closet/supplypod/bluespacepod //doesnt effect circuit board, making reversal possible
to_chat(user, "<span class='notice'>You insert the disk into [src], allowing for advanced supply delivery vehicles.</span>")
qdel(W)
return TRUE
@@ -52,22 +51,20 @@
sb.link_console(src, user)
return TRUE
else
to_chat(user, "<span class='notice'>[src] is already linked to [sb].</span>")
to_chat(user, "<span class='alert'>[src] is already linked to [sb].</span>")
..()
/obj/machinery/computer/cargo/express/emag_act(mob/living/user)
. = SEND_SIGNAL(src, COMSIG_ATOM_EMAG_ACT)
if(obj_flags & EMAGGED)
return
user.visible_message("<span class='warning'>[user] swipes a suspicious card through [src]!</span>",
"<span class='notice'>You change the routing protocols, allowing the Supply Pod to land anywhere on the station.</span>")
if(user)
user.visible_message("<span class='warning'>[user] swipes a suspicious card through [src]!</span>",
"<span class='notice'>You change the routing protocols, allowing the Supply Pod to land anywhere on the station.</span>")
obj_flags |= EMAGGED
// This also sets this on the circuit board
var/obj/item/circuitboard/computer/cargo/board = circuit
board.obj_flags |= EMAGGED
packin_up()
req_access = list()
return TRUE
/obj/machinery/computer/cargo/express/proc/packin_up() // oh shit, I'm sorry
meme_pack_data = list() // sorry for what?
@@ -89,10 +86,10 @@
"desc" = P.desc || P.name // If there is a description, use it. Otherwise use the pack's name.
))
/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state) // Remember to use the appropriate state.
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/cargo/express/ui_interact(mob/living/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "CargoExpress", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "CargoExpress", name)
ui.open()
/obj/machinery/computer/cargo/express/ui_data(mob/user)
@@ -131,9 +128,6 @@
return data
/obj/machinery/computer/cargo/express/ui_act(action, params, datum/tgui/ui)
if(!allowed(usr))
to_chat(usr, "<span class='notice'>Access denied.</span>")
return
switch(action)
if("LZCargo")
usingBeacon = FALSE
@@ -153,6 +147,7 @@
printed_beacons++//printed_beacons starts at 0, so the first one out will be called beacon # 1
beacon.name = "Supply Pod Beacon #[printed_beacons]"
if("add")//Generate Supply Order first
var/id = text2path(params["id"])
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
@@ -195,7 +190,6 @@
LZ = pick(empty_turfs)
if (SO.pack.cost <= points_to_check && LZ)//we need to call the cost check again because of the CHECK_TICK call
D.adjust_money(-SO.pack.cost)
SSblackbox.record_feedback("nested tally", "cargo_imports", 1, list("[SO.pack.cost]", "[SO.pack.name]"))
new /obj/effect/abstract/DPtarget(LZ, podType, SO)
. = TRUE
update_icon()
@@ -209,7 +203,7 @@
CHECK_TICK
if(empty_turfs && empty_turfs.len)
D.adjust_money(-(SO.pack.cost * (0.72*MAX_EMAG_ROCKETS)))
SSblackbox.record_feedback("nested tally", "cargo_imports", MAX_EMAG_ROCKETS, list("[SO.pack.cost * 0.72]", "[SO.pack.name]"))
SO.generateRequisition(get_turf(src))
for(var/i in 1 to MAX_EMAG_ROCKETS)
var/LZ = pick(empty_turfs)
+7
View File
@@ -223,3 +223,10 @@
/obj/item/ammo_box/magazine/wt550m9/wtrubber,
/obj/item/ammo_box/magazine/wt550m9/wtrubber)
crate_name = "auto rifle ammo crate"
/datum/supply_pack/security/armory/hell_single
name = "Hellgun Single-Pack"
crate_name = "hellgun crate"
desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
cost = 1500
contains = list(/obj/item/gun/energy/laser/hellgun)
-6
View File
@@ -76,12 +76,6 @@
cost = 200
contains = list(/obj/item/toy/beach_ball)
/datum/supply_pack/goody/hell_single
name = "Hellgun Single-Pack"
desc = "Contains one hellgun, an old pattern of laser gun infamous for its ability to horribly disfigure targets with burns. Technically violates the Space Geneva Convention when used on humanoids."
cost = 1500
contains = list(/obj/item/gun/energy/laser/hellgun)
/datum/supply_pack/goody/medipen_twopak
name = "Medipen Two-Pak"
desc = "Contains one standard epinephrine medipen and one standard emergency first-aid kit medipen. For when you want to prepare for the worst."
+4 -15
View File
@@ -194,9 +194,9 @@
/datum/supply_pack/misc/dirtymags
name = "Dirty Magazines"
desc = "Get your mind out of the gutter operative, you have work to do. Three items per order. Possible Results: .357 Speedloaders, Kitchen Gun Mags, Stetchkin Mags."
desc = "Get your mind out of the gutter operative, you have work to do. Three items per order. Possible Results: .357 Speedloaders, Kitchen Gun patented magazines, or Stetchkin magazines."
hidden = TRUE
cost = 12000
cost = 4000
var/num_contained = 3
contains = list(/obj/item/ammo_box/a357,
/obj/item/ammo_box/magazine/pistolm9mm,
@@ -415,21 +415,10 @@
/obj/item/restraints/handcuffs/fake/kinky,
/obj/item/clothing/head/kitty/genuine, // Why its illegal
/obj/item/clothing/head/kitty/genuine,
/obj/item/storage/pill_bottle/penis_enlargement,
/obj/structure/reagent_dispensers/keg/aphro)
/obj/item/storage/pill_bottle/penis_enlargement)
crate_name = "lewd kit"
crate_type = /obj/structure/closet/crate
/datum/supply_pack/misc/lewdkeg
name = "Lewd Deluxe Keg"
desc = "That other stuff not getting you ready? Well I have a Chemslut making tons of the good stuff."
cost = 7500 //It can be a weapon
contraband = TRUE
contains = list(/obj/structure/reagent_dispensers/keg/aphro/strong)
crate_name = "deluxe keg"
crate_type = /obj/structure/closet/crate
///Special supply crate that generates random syndicate gear up to a determined TC value
/datum/supply_pack/misc/syndicate
@@ -466,4 +455,4 @@
if(crate_value < I.cost)
continue
crate_value -= I.cost
new I.item(C)
new I.item(C)
+5
View File
@@ -41,6 +41,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(!asset_cache_job)
return
// Rate limiting
var/mtl = CONFIG_GET(number/minute_topic_limit)
if (!holder && mtl)
var/minute = round(world.time, 600)
@@ -98,6 +99,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
keyUp(keycode)
return
// Tgui Topic middleware
if(!tgui_Topic(href_list))
return
// Admin PM
if(href_list["priv_msg"])
cmd_admin_pm(href_list["priv_msg"],null)
+84 -58
View File
@@ -40,7 +40,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
//If it's 0, that's good, if it's anything but 0, the owner of this prefs file's antag choices were,
//autocorrected this round, not that you'd need to check that.
var/UI_style = null
var/buttons_locked = FALSE
var/hotkeys = FALSE
@@ -230,7 +229,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/gear_points = 10
var/list/gear_categories
var/list/chosen_gear = list()
var/gear_tab
var/gear_category
var/gear_subcategory
var/screenshake = 100
var/damagescreenshake = 2
@@ -1059,58 +1059,83 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<br>"
if(3)
if(!gear_tab)
gear_tab = GLOB.loadout_items[1]
dat += "<table align='center' width='100%'>"
dat += "<tr><td colspan=4><center><b><font color='[gear_points == 0 ? "#E62100" : "#CCDDFF"]'>[gear_points]</font> loadout points remaining.</b> \[<a href='?_src_=prefs;preference=gear;clear_loadout=1'>Clear Loadout</a>\]</center></td></tr>"
dat += "<tr><td colspan=4><center>You can only choose one item per category, unless it's an item that spawns in your backpack or hands.</center></td></tr>"
dat += "<tr><td colspan=4><center><b>"
var/firstcat = TRUE
for(var/i in GLOB.loadout_items)
if(firstcat)
firstcat = FALSE
if(!length(GLOB.loadout_items))
dat += "<center>ERROR: No loadout categories - something is horribly wrong!"
else
if(!GLOB.loadout_categories[gear_category])
gear_category = GLOB.loadout_categories[1]
var/firstcat = TRUE
for(var/category in GLOB.loadout_categories)
if(firstcat)
firstcat = FALSE
else
dat += " |"
if(category == gear_category)
dat += " <span class='linkOn'>[category]</span> "
else
dat += " <a href='?_src_=prefs;preference=gear;select_category=[html_encode(category)]'>[category]</a> "
dat += "</b></center></td></tr>"
dat += "<tr><td colspan=4><hr></td></tr>"
dat += "<tr><td colspan=4><center><b>"
if(!length(GLOB.loadout_categories[gear_category]))
dat += "No subcategories detected. Something is horribly wrong!"
else
dat += " |"
if(i == gear_tab)
dat += " <span class='linkOn'>[i]</span> "
else
dat += " <a href='?_src_=prefs;preference=gear;select_category=[i]'>[i]</a> "
dat += "</b></center></td></tr>"
dat += "<tr><td colspan=4><hr></td></tr>"
dat += "<tr><td colspan=4><b><center>[gear_tab]</center></b></td></tr>"
dat += "<tr><td colspan=4><hr></td></tr>"
dat += "<tr width=10% style='vertical-align:top;'><td width=15%><b>Name</b></td>"
dat += "<td style='vertical-align:top'><b>Cost</b></td>"
dat += "<td width=10%><font size=2><b>Restrictions</b></font></td>"
dat += "<td width=80%><font size=2><b>Description</b></font></td></tr>"
for(var/j in GLOB.loadout_items[gear_tab])
var/datum/gear/gear = GLOB.loadout_items[gear_tab][j]
var/donoritem = gear.donoritem
if(donoritem && !gear.donator_ckey_check(user.ckey))
continue
var/class_link = ""
if(gear.type in chosen_gear)
class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(j)];toggle_gear=0'"
else if(gear_points <= 0)
class_link = "style='white-space:normal;' class='linkOff'"
else if(donoritem)
class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(j)];toggle_gear=1'"
else
class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(j)];toggle_gear=1'"
dat += "<tr style='vertical-align:top;'><td width=15%><a [class_link]>[j]</a></td>"
dat += "<td width = 5% style='vertical-align:top'>[gear.cost]</td><td>"
if(islist(gear.restricted_roles))
if(gear.restricted_roles.len)
if(gear.restricted_desc)
dat += "<font size=2>"
dat += gear.restricted_desc
dat += "</font>"
var/list/subcategories = GLOB.loadout_categories[gear_category]
if(!subcategories.Find(gear_subcategory))
gear_subcategory = subcategories[1]
var/firstsubcat = FALSE
for(var/subcategory in subcategories)
if(firstsubcat)
firstsubcat = FALSE
else
dat += "<font size=2>"
dat += gear.restricted_roles.Join(";")
dat += "</font>"
dat += "</td><td><font size=2><i>[gear.description]</i></font></td></tr>"
dat += "</table>"
dat += " |"
if(gear_subcategory == subcategory)
dat += " <span class='linkOn'>[subcategory]</span> "
else
dat += " <a href='?_src_=prefs;preference=gear;select_subcategory=[html_encode(subcategory)]'>[subcategory]</a> "
dat += "</b></center></td></tr>"
dat += "<tr width=10% style='vertical-align:top;'><td width=15%><b>Name</b></td>"
dat += "<td style='vertical-align:top'><b>Cost</b></td>"
dat += "<td width=10%><font size=2><b>Restrictions</b></font></td>"
dat += "<td width=80%><font size=2><b>Description</b></font></td></tr>"
for(var/name in GLOB.loadout_items[gear_category][gear_subcategory])
var/datum/gear/gear = GLOB.loadout_items[gear_category][gear_subcategory][name]
var/donoritem = gear.donoritem
if(donoritem && !gear.donator_ckey_check(user.ckey))
continue
var/class_link = ""
if(gear.type in chosen_gear)
class_link = "style='white-space:normal;' class='linkOn' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=0'"
else if(gear_points <= 0)
class_link = "style='white-space:normal;' class='linkOff'"
else if(donoritem)
class_link = "style='white-space:normal;background:#ebc42e;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
else
class_link = "style='white-space:normal;' href='?_src_=prefs;preference=gear;toggle_gear_path=[html_encode(name)];toggle_gear=1'"
dat += "<tr style='vertical-align:top;'><td width=15%><a [class_link]>[name]</a></td>"
dat += "<td width = 5% style='vertical-align:top'>[gear.cost]</td><td>"
if(islist(gear.restricted_roles))
if(gear.restricted_roles.len)
if(gear.restricted_desc)
dat += "<font size=2>"
dat += gear.restricted_desc
dat += "</font>"
else
dat += "<font size=2>"
dat += gear.restricted_roles.Join(";")
dat += "</font>"
dat += "</td><td><font size=2><i>[gear.description]</i></font></td></tr>"
dat += "</table>"
if(4) // Content preferences
dat += "<table><tr><td width='340px' height='300px' valign='top'>"
dat += "<h2>Fetish content prefs</h2>"
@@ -1128,7 +1153,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<b>Breast Enlargement:</b> <a href='?_src_=prefs;preference=breast_enlargement'>[(cit_toggles & BREAST_ENLARGEMENT) ? "Allowed" : "Disallowed"]</a><br>"
dat += "<b>Penis Enlargement:</b> <a href='?_src_=prefs;preference=penis_enlargement'>[(cit_toggles & PENIS_ENLARGEMENT) ? "Allowed" : "Disallowed"]</a><br>"
dat += "<b>Hypno:</b> <a href='?_src_=prefs;preference=never_hypno'>[(cit_toggles & NEVER_HYPNO) ? "Disallowed" : "Allowed"]</a><br>"
dat += "<b>Aphrodisiacs:</b> <a href='?_src_=prefs;preference=aphro'>[(cit_toggles & NO_APHRO) ? "Disallowed" : "Allowed"]</a><br>"
dat += "<b>Ass Slapping:</b> <a href='?_src_=prefs;preference=ass_slap'>[(cit_toggles & NO_ASS_SLAP) ? "Disallowed" : "Allowed"]</a><br>"
dat += "<b>Automatic Wagging:</b> <a href='?_src_=prefs;preference=auto_wag'>[(cit_toggles & NO_AUTO_WAG) ? "Disabled" : "Enabled"]</a><br>"
dat += "</tr></table>"
@@ -2691,11 +2715,13 @@ GLOBAL_LIST_EMPTY(preferences_datums)
gear_points = CONFIG_GET(number/initial_gear_points)
save_preferences()
if(href_list["select_category"])
for(var/i in GLOB.loadout_items)
if(i == href_list["select_category"])
gear_tab = i
gear_category = html_decode(href_list["select_category"])
gear_subcategory = GLOB.loadout_categories[gear_category][1]
if(href_list["select_subcategory"])
gear_subcategory = html_decode(href_list["select_subcategory"])
if(href_list["toggle_gear_path"])
var/datum/gear/G = GLOB.loadout_items[gear_tab][html_decode(href_list["toggle_gear_path"])]
var/name = html_decode(href_list["toggle_gear_path"])
var/datum/gear/G = GLOB.loadout_items[gear_category][gear_subcategory][name]
if(!G)
return
var/toggle = text2num(href_list["toggle_gear"])
@@ -2875,17 +2901,17 @@ GLOBAL_LIST_EMPTY(preferences_datums)
LAZYINITLIST(L)
for(var/i in chosen_gear)
var/datum/gear/G = i
var/occupied_slots = L[slot_to_string(initial(G.category))] ? L[slot_to_string(initial(G.category))] + 1 : 1
LAZYSET(L, slot_to_string(initial(G.category)), occupied_slots)
var/occupied_slots = L[initial(G.category)] ? L[initial(G.category)] + 1 : 1
LAZYSET(L, initial(G.category), occupied_slots)
switch(slot)
if(SLOT_IN_BACKPACK)
if(L[slot_to_string(SLOT_IN_BACKPACK)] < BACKPACK_SLOT_AMT)
if(L[LOADOUT_CATEGORY_BACKPACK] < BACKPACK_SLOT_AMT)
return TRUE
if(SLOT_HANDS)
if(L[slot_to_string(SLOT_HANDS)] < HANDS_SLOT_AMT)
if(L[LOADOUT_CATEGORY_HANDS] < HANDS_SLOT_AMT)
return TRUE
else
if(L[slot_to_string(slot)] < DEFAULT_SLOT_AMT)
if(L[slot] < DEFAULT_SLOT_AMT)
return TRUE
#undef DEFAULT_SLOT_AMT
+2 -2
View File
@@ -224,7 +224,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
var/needs_update = savefile_needs_update(S)
if(needs_update == -2) //fatal, can't load any data
return 0
. = TRUE
//general preferences
@@ -443,7 +443,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
return 0
. = TRUE
//Species
var/species_id
S["species"] >> species_id
+24
View File
@@ -149,6 +149,30 @@
icon_state = "eyepatch"
item_state = "eyepatch"
/obj/item/clothing/glasses/eyepatch/syndicate
name = "cybernetic eyepatch"
desc = "An eyepatch used to enhance one's aim with guns."
icon_state = "syndicatepatch"
item_state = "syndicatepatch"
resistance_flags = ACID_PROOF
/obj/item/clothing/glasses/eyepatch/syndicate/equipped(mob/living/carbon/human/user, slot)
. = ..()
if(slot == SLOT_GLASSES)
user.visible_message("<span class='warning'>Circuitry from the eyepatch links itself to your brain as you put on the eyepatch.")
if(HAS_TRAIT(user, TRAIT_POOR_AIM))
user.visible_message("<span class='warning'>You hear a fizzing noise from the circuit. That can't be good.")
ADD_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM")
ADD_TRAIT(src, TRAIT_NODROP, "SYNDICATE_EYEPATCH_NODROP")
/obj/item/clothing/glasses/eyepatch/syndicate/dropped(mob/living/carbon/human/user)
. = ..()
REMOVE_TRAIT(user, TRAIT_INSANE_AIM, "SYNDICATE_EYEPATCH_AIM")
var/obj/item/organ/eyes/eyes = user.getorganslot(ORGAN_SLOT_EYES)
if(eyes)
eyes.applyOrganDamage(30)
user.visible_message("<span class='warning'>Your eye stings as the circuitry is removed from your eye!")
/obj/item/clothing/glasses/monocle
name = "monocle"
desc = "Such a dapper eyepiece!"
@@ -97,14 +97,14 @@
if(get_dist(user, place) >= range*8) //Rads are easier to see than wires under the floor
continue
var/strength = round(rad_places[i] / 1000, 0.1)
var/image/pic = new(loc = place)
var/image/pic = image(loc = place)
var/mutable_appearance/MA = new()
MA.alpha = 180
MA.maptext = "[strength]k"
MA.color = "#64C864"
MA.layer = FLY_LAYER
MA.maptext = "<span class='maptext'>[strength]k</span>"
MA.color = "#04e604"
MA.layer = RAD_TEXT_LAYER
MA.plane = GAME_PLANE
pic.appearance = MA
flick_overlay(pic, list(user.client), 8)
flick_overlay(pic, list(user.client), 10)
/obj/item/clothing/glasses/meson/engine/proc/show_shuttle()
var/mob/living/carbon/human/user = loc
+10 -1
View File
@@ -21,7 +21,7 @@
desc = "An expensive ring, studded with a diamond. Cultures have used these rings in courtship for a millenia."
icon_state = "ringdiamond"
item_state = "dring"
/obj/item/clothing/gloves/ring/diamond/attack_self(mob/user)
user.visible_message("<span class='warning'>\The [user] gets down on one knee, presenting \the [src].</span>","<span class='warning'>You get down on one knee, presenting \the [src].</span>")
@@ -30,3 +30,12 @@
desc = "A tiny silver ring, sized to wrap around a finger."
icon_state = "ringsilver"
item_state = "sring"
/obj/item/clothing/gloves/ring/custom
name = "ring"
desc = "A ring."
gender = NEUTER
w_class = WEIGHT_CLASS_TINY
obj_flags = UNIQUE_RENAME
icon_state = "ringsilver"
item_state = "sring"
+13
View File
@@ -469,3 +469,16 @@
icon_state = "maid"
item_state = "maid"
dynamic_hair_suffix = ""
/obj/item/clothing/head/widered
name = "Wide red hat"
desc = "It is both wide, and red. Stylish!"
icon_state = "widehat_red"
item_state = "widehat_red"
/obj/item/clothing/head/kabuto
name = "Kabuto helmet"
desc = "A traditional kabuto helmet."
icon_state = "kabuto"
item_state = "kabuto"
flags_inv = HIDEHAIR|HIDEEARS
+1 -1
View File
@@ -214,7 +214,7 @@
name = "advanced hardsuit"
desc = "An advanced suit that protects against hazardous, low pressure environments. Shines with a high polish."
item_state = "ce_hardsuit"
armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 95, "fire" = 100, "acid" = 90, "wound" = 10)
armor = list("melee" = 40, "bullet" = 5, "laser" = 10, "energy" = 5, "bomb" = 50, "bio" = 100, "rad" = 100, "fire" = 100, "acid" = 90, "wound" = 10)
heat_protection = CHEST|GROIN|LEGS|FEET|ARMS|HANDS
max_heat_protection_temperature = FIRE_IMMUNITY_MAX_TEMP_PROTECT
helmettype = /obj/item/clothing/head/helmet/space/hardsuit/engine/elite
@@ -1057,3 +1057,9 @@
desc = "Reminds you of someone, but you just can't put your finger on it..."
icon_state = "waldo_shirt"
item_state = "waldo_shirt"
/obj/item/clothing/suit/samurai
name = "Samurai outfit"
desc = "An outfit used by traditional japanese warriors."
icon_state = "samurai"
item_state = "samurai"
@@ -369,3 +369,12 @@
icon_state = "plastics"
armor = list("melee" = 0, "bullet" = 0, "laser" = 20, "energy" = 10, "bomb" = 0, "bio" = 0, "rad" = 0, "fire" = 20, "acid" = -40)
flags_inv = HIDEACCESSORY
//necklace
/obj/item/clothing/accessory/necklace
name = "necklace"
desc = "A necklace."
icon_state = "locket"
obj_flags = UNIQUE_RENAME
custom_materials = list(/datum/material/iron=100)
resistance_flags = FIRE_PROOF
+26
View File
@@ -329,3 +329,29 @@
desc = "cloud"
icon_state = "cloud"
can_adjust = FALSE
/obj/item/clothing/under/costume/kimono
name = "Kimono"
desc = "A traditional piece of clothing from japan"
icon_state = "kimono"
item_state = "kimono"
/obj/item/clothing/under/costume/kimono/black
name = "Black Kimono"
icon_state = "kimono_a"
item_state = "kimono_a"
/obj/item/clothing/under/costume/kimono/kamishimo
name = "Kamishimo"
icon_state = "kamishimo"
item_state = "kamishimo"
/obj/item/clothing/under/costume/kimono/fancy
name = "Fancy Kimono"
icon_state = "fancy_kimono"
item_state = "fancy_kimono"
/obj/item/clothing/under/costume/kimono/sakura
name = "Sakura Kimono'"
icon_state = "sakura_kimono"
item_state = "sakura_kimono"
@@ -308,3 +308,21 @@
icon_state = "polyfemtankpantsu"
poly_states = 2
poly_colors = list("#808080", "#FF3535")
/obj/item/clothing/under/misc/black_dress
name = "little black dress"
desc = "A small black dress"
icon_state = "littleblackdress_s"
item_state = "littleblackdress_s"
/obj/item/clothing/under/misc/pinktutu
name = "pink tutu"
desc = "A pink tutu"
icon_state = "pinktutu_s"
item_state = "pinktutu_s"
/obj/item/clothing/under/misc/bathrobe
name = "bathrobe"
desc = "A blue bathrobe."
icon_state = "bathrobe"
item_state = "bathrobe"
+5
View File
@@ -16,11 +16,16 @@
search = findtext(text, ":", pos + length(text[pos]))
if(search)
emoji = lowertext(copytext(text, pos + length(text[pos]), search))
var/isthisapath = (emoji[1] == "/") && text2path(emoji)
var/datum/asset/spritesheet/sheet = get_asset_datum(/datum/asset/spritesheet/goonchat)
var/tag = sheet.icon_tag("emoji-[emoji]")
if(tag)
parsed += "<i style='width:16px !important;height:16px !important;'>[tag]</i>" //evil way of enforcing 16x16
pos = search + length(text[pos])
else if(ispath(isthisapath, /atom)) //path
var/atom/thisisanatom = isthisapath
parsed += "[icon2html(initial(thisisanatom.icon), world, initial(thisisanatom.icon_state))]"
pos = search + length(text[pos])
else
parsed += copytext(text, pos, search)
pos = search
+1
View File
@@ -42,6 +42,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
name = "immovable rod"
desc = "What the fuck is that?"
icon = 'icons/obj/objects.dmi'
movement_type = FLOATING
icon_state = "immrod"
throwforce = 100
move_force = INFINITY
+16 -23
View File
@@ -95,14 +95,11 @@
icon_state = "dominator"
density = TRUE
var/active = FALSE
var/obj/item/gps/gps
var/credits_stored = 0
var/siphon_per_tick = 5
/obj/machinery/shuttle_scrambler/Initialize(mapload)
. = ..()
gps = new/obj/item/gps/internal/pirate(src)
gps.tracking = FALSE
update_icon()
/obj/machinery/shuttle_scrambler/process()
@@ -112,6 +109,7 @@
if(D)
var/siphoned = min(D.account_balance,siphon_per_tick)
D.adjust_money(-siphoned)
credits_stored += siphoned
interrupt_research()
else
return
@@ -120,7 +118,7 @@
/obj/machinery/shuttle_scrambler/proc/toggle_on(mob/user)
SSshuttle.registerTradeBlockade(src)
gps.tracking = TRUE
AddComponent(/datum/component/gps, "Nautical Signal")
active = TRUE
to_chat(user,"<span class='notice'>You toggle [src] [active ? "on":"off"].</span>")
to_chat(user,"<span class='warning'>The scrambling signal can be now tracked by GPS.</span>")
@@ -130,7 +128,7 @@
if(!active)
if(alert(user, "Turning the scrambler on will make the shuttle trackable by GPS. Are you sure you want to do it?", "Scrambler", "Yes", "Cancel") == "Cancel")
return
if(active || !user.canUseTopic(src))
if(active || !user.canUseTopic(src, BE_CLOSE))
return
toggle_on(user)
update_icon()
@@ -147,35 +145,31 @@
new /obj/effect/temp_visual/emp(get_turf(S))
/obj/machinery/shuttle_scrambler/proc/dump_loot(mob/user)
new /obj/item/holochip(drop_location(), credits_stored)
to_chat(user,"<span class='notice'>You retrieve the siphoned credits!</span>")
credits_stored = 0
if(credits_stored) // Prevents spamming empty holochips
new /obj/item/holochip(drop_location(), credits_stored)
to_chat(user,"<span class='notice'>You retrieve the siphoned credits!</span>")
credits_stored = 0
else
to_chat(user,"<span class='notice'>There's nothing to withdraw.</span>")
/obj/machinery/shuttle_scrambler/proc/send_notification()
priority_announce("Data theft signal detected, source registered on local gps units.")
/obj/machinery/shuttle_scrambler/proc/toggle_off(mob/user)
SSshuttle.clearTradeBlockade(src)
gps.tracking = FALSE
active = FALSE
STOP_PROCESSING(SSobj,src)
/obj/machinery/shuttle_scrambler/update_overlays()
. = ..()
/obj/machinery/shuttle_scrambler/update_icon_state()
if(active)
var/mutable_appearance/M = mutable_appearance(icon, "dominator-overlay")
M.color = "#00FFFF"
. += M
icon_state = "dominator-blue"
else
icon_state = "dominator"
/obj/machinery/shuttle_scrambler/Destroy()
toggle_off()
QDEL_NULL(gps)
return ..()
/obj/item/gps/internal/pirate
gpstag = "Nautical Signal"
desc = "You can hear shanties over the static."
/obj/machinery/computer/shuttle/pirate
name = "pirate shuttle console"
shuttleId = "pirateship"
@@ -312,11 +306,10 @@
else
pad = locate() in range(4,src)
/obj/machinery/computer/piratepad_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/piratepad_control/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "CargoHoldTerminal", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "CargoHoldTerminal", name)
ui.open()
/obj/machinery/computer/piratepad_control/ui_data(mob/user)
@@ -162,6 +162,14 @@
tastes = list("brains" = 1, "meat" = 1)
foodtype = RAW | MEAT | TOXIC
/obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ethereal
icon_state = "etherealmeat"
desc = "So shiny you feel like ingesting it might make you shine too"
filling_color = "#97ee63"
list_reagents = list(/datum/reagent/consumable/liquidelectricity = 3)
tastes = list("pure electricity" = 2, "meat" = 1)
foodtype = RAW | MEAT | TOXIC
/obj/item/reagent_containers/food/snacks/carpmeat/aquatic
name = "fillet"
desc = "A fillet of one of the local water dwelling species."
@@ -139,11 +139,22 @@
tastes = list("meat" = 1, "salmon" = 1)
foodtype = MEAT | ALCOHOL
/obj/item/reagent_containers/food/snacks/rawmeatball
name = "raw meatball"
desc = "Raw mushy meat. Better cook this!"
icon_state = "rawmeatball"
cooked_type = /obj/item/reagent_containers/food/snacks/meatball
list_reagents = list(/datum/reagent/consumable/nutriment = 3)
filling_color = "#bd2020"
tastes = list("meat" = 1, "slime" = 1)
foodtype = MEAT | RAW
/obj/item/reagent_containers/food/snacks/meatball
name = "meatball"
desc = "MAMA MIA DAS A SPICY"
icon_state = "meatball"
list_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 1)
bonus_reagents = list(/datum/reagent/consumable/nutriment = 1, /datum/reagent/consumable/nutriment/vitamin = 1)
filling_color = "#800000"
tastes = list("meat" = 1)
foodtype = MEAT
@@ -113,6 +113,27 @@
tastes = list("pie" = 1, "meat" = 1)
foodtype = GRAIN | MEAT
/obj/item/reagent_containers/food/snacks/pie/burek
name = "Burek"
icon = 'icons/obj/food/piecake.dmi'
icon_state = "burek"
desc = "If you know, you know."
slice_path = /obj/item/reagent_containers/food/snacks/pie/burekslice
slices_num = 4
bonus_reagents = list(/datum/reagent/consumable/nutriment = 4, /datum/reagent/consumable/nutriment/vitamin = 6)
list_reagents = list(/datum/reagent/consumable/nutriment= 20, /datum/reagent/consumable/nutriment/vitamin = 6)
bitesize = 12
tastes = list("meat" = 1, "oil" = 1)
foodtype = GRAIN | MEAT
/obj/item/reagent_containers/food/snacks/pie/burekslice
name = "Burek Slice"
icon = 'icons/obj/food/piecake.dmi'
icon_state = "burekslice"
desc = "A slice of Burek, watch out for oil stains!"
tastes = list("meat" = 1, "oil" = 1)
foodtype = GRAIN | MEAT
/obj/item/reagent_containers/food/snacks/pie/tofupie
name = "tofu-pie"
@@ -91,3 +91,13 @@
tastes = list("sweetness" = 3, "cake" = 1)
foodtype = GRAIN | FRUIT | VEGETABLES
custom_price = PRICE_CHEAP
/obj/item/reagent_containers/food/snacks/energybar
name = "High-power energy bars"
icon_state = "energybar"
desc = "An energy bar with a lot of punch, you probably shouldn't eat this if you're not an Ethereal."
trash = /obj/item/trash/energybar
list_reagents = list(/datum/reagent/consumable/nutriment = 3, /datum/reagent/consumable/liquidelectricity = 3)
filling_color = "#97ee63"
tastes = list("pure electricity" = 3, "fitness" = 2)
foodtype = TOXIC
@@ -12,6 +12,7 @@
idle_power_usage = 5
active_power_usage = 100
circuit = /obj/item/circuitboard/machine/smartfridge
var/max_n_of_items = 1500
var/allow_ai_retrieve = FALSE
var/list/initial_contents
@@ -38,12 +39,10 @@
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: This unit can hold a maximum of <b>[max_n_of_items]</b> items.</span>"
/obj/machinery/smartfridge/power_change()
..()
update_icon()
/obj/machinery/smartfridge/update_icon_state()
SSvis_overlays.remove_vis_overlay(src, managed_vis_overlays)
if(!stat)
SSvis_overlays.add_vis_overlay(src, icon, "smartfridge-light-mask", EMISSIVE_LAYER, EMISSIVE_PLANE, dir, alpha)
if(visible_contents)
switch(contents.len)
if(0)
@@ -66,9 +65,6 @@
********************/
/obj/machinery/smartfridge/attackby(obj/item/O, mob/user, params)
if(user.a_intent == INTENT_HARM)
return ..()
if(default_deconstruction_screwdriver(user, icon_state, icon_state, O))
cut_overlays()
if(panel_open)
@@ -87,46 +83,53 @@
updateUsrDialog()
return
if(stat)
updateUsrDialog()
return FALSE
if(!stat)
if(contents.len >= max_n_of_items)
to_chat(user, "<span class='warning'>\The [src] is full!</span>")
return FALSE
if(accept_check(O))
load(O)
user.visible_message("[user] has added \the [O] to \the [src].", "<span class='notice'>You add \the [O] to \the [src].</span>")
updateUsrDialog()
if (visible_contents)
update_icon()
return TRUE
if(istype(O, /obj/item/storage/bag))
var/obj/item/storage/P = O
var/loaded = 0
for(var/obj/G in P.contents)
if(contents.len >= max_n_of_items)
break
if(accept_check(G))
load(G)
loaded++
updateUsrDialog()
if(loaded)
user.visible_message("[user] loads \the [src] with \the [O].", \
"<span class='notice'>You [contents.len >= max_n_of_items ? "fill" : "load"] \the [src] with \the [O].</span>")
if(O.contents.len > 0)
to_chat(user, "<span class='warning'>Some items are refused.</span>")
return TRUE
else
to_chat(user, "<span class='warning'>There is nothing in [O] to put in [src]!</span>")
if(contents.len >= max_n_of_items)
to_chat(user, "<span class='warning'>\The [src] is full!</span>")
return FALSE
to_chat(user, "<span class='warning'>\The [src] smartly refuses [O].</span>")
updateUsrDialog()
return FALSE
if(accept_check(O))
load(O)
user.visible_message("<span class='notice'>[user] adds \the [O] to \the [src].</span>", "<span class='notice'>You add \the [O] to \the [src].</span>")
updateUsrDialog()
if (visible_contents)
update_icon()
return TRUE
if(istype(O, /obj/item/storage/bag))
var/obj/item/storage/P = O
var/loaded = 0
for(var/obj/G in P.contents)
if(contents.len >= max_n_of_items)
break
if(accept_check(G))
load(G)
loaded++
updateUsrDialog()
if(loaded)
if(contents.len >= max_n_of_items)
user.visible_message("<span class='notice'>[user] loads \the [src] with \the [O].</span>", \
"<span class='notice'>You fill \the [src] with \the [O].</span>")
else
user.visible_message("<span class='notice'>[user] loads \the [src] with \the [O].</span>", \
"<span class='notice'>You load \the [src] with \the [O].</span>")
if(O.contents.len > 0)
to_chat(user, "<span class='warning'>Some items are refused.</span>")
if (visible_contents)
update_icon()
return TRUE
else
to_chat(user, "<span class='warning'>There is nothing in [O] to put in [src]!</span>")
return FALSE
if(user.a_intent != INTENT_HARM)
to_chat(user, "<span class='warning'>\The [src] smartly refuses [O].</span>")
updateUsrDialog()
return FALSE
else
return ..()
@@ -151,16 +154,16 @@
return TRUE
///Really simple proc, just moves the object "O" into the hands of mob "M" if able, done so I could modify the proc a little for the organ fridge
/obj/machinery/smartfridge/proc/dispense(obj/item/O, var/mob/M)
/obj/machinery/smartfridge/proc/dispense(obj/item/O, mob/M)
if(!M.put_in_hands(O))
O.forceMove(drop_location())
adjust_item_drop_location(O)
/obj/machinery/smartfridge/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/smartfridge/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "SmartVend", name, 440, 550, master_ui, state)
ui = new(user, src, "SmartVend", name)
ui.set_autoupdate(FALSE)
ui.open()
@@ -232,7 +235,7 @@
// ----------------------------
/obj/machinery/smartfridge/drying_rack
name = "drying rack"
desc = "A wooden contraption, used to dry plant products, food and leather."
desc = "A wooden contraption, used to dry plant products, food and hide."
icon = 'icons/obj/hydroponics/equipment.dmi'
icon_state = "drying_rack"
use_power = IDLE_POWER_USE
@@ -277,6 +280,11 @@
return TRUE
return FALSE
// /obj/machinery/smartfridge/drying_rack/powered() do we have this? no.
// if(!anchored)
// return FALSE
// return ..()
/obj/machinery/smartfridge/drying_rack/power_change()
if(powered() && anchored)
stat &= ~NOPOWER
@@ -285,6 +293,10 @@
toggle_drying(TRUE)
update_icon()
// . = ..()
// if(!powered())
// toggle_drying(TRUE)
/obj/machinery/smartfridge/drying_rack/load() //For updating the filled overlay
..()
update_icon()
@@ -308,7 +320,7 @@
var/obj/item/reagent_containers/food/snacks/S = O
if(S.dried_type)
return TRUE
if(istype(O, /obj/item/stack/sheet/wetleather/))
if(istype(O, /obj/item/stack/sheet/wetleather/)) //no wethide
return TRUE
return FALSE
@@ -386,19 +398,19 @@
/obj/machinery/smartfridge/extract/preloaded
initial_contents = list(/obj/item/slime_scanner = 2)
// ------------------------- You think you're better than Chem, huh?
// -------------------------
// Organ Surgery Smartfridge
// ------------------------- Just wait till Tamiorgans
// -------------------------
/obj/machinery/smartfridge/organ
name = "smart organ storage"
desc = "A refrigerated storage unit for organ storage."
max_n_of_items = 25 //vastly lower to prevent processing too long
max_n_of_items = 20 //vastly lower to prevent processing too long
var/repair_rate = 0
/obj/machinery/smartfridge/organ/accept_check(obj/item/O)
if(istype(O, /obj/item/organ))
if(isorgan(O) || isbodypart(O))
return TRUE
if(istype(O, /obj/item/reagent_containers/syringe))
if(istype(O, /obj/item/reagent_containers/syringe)) //other medical things.
return TRUE
if(istype(O, /obj/item/reagent_containers/glass/bottle))
return TRUE
@@ -410,7 +422,7 @@
. = ..()
if(!.) //if the item loads, clear can_decompose
return
if(istype(O, /obj/item/organ))
if(isorgan(O))
var/obj/item/organ/organ = O
organ.organ_flags |= ORGAN_FROZEN
@@ -426,12 +438,13 @@
return
O.applyOrganDamage(-repair_rate)
/obj/machinery/smartfridge/organ/Exited(obj/item/organ/AM, atom/newLoc)
/obj/machinery/smartfridge/organ/Exited(atom/movable/AM, atom/newLoc)
. = ..()
if(istype(AM))
AM.organ_flags &= ~ORGAN_FROZEN
if(isorgan(AM))
var/obj/item/organ/O = AM
O.organ_flags &= ~ORGAN_FROZEN
/obj/machinery/smartfridge/organ/preloaded
/obj/machinery/smartfridge/organ/preloaded //cit specific??????
initial_contents = list(
/obj/item/reagent_containers/medspray/synthtissue = 1,
/obj/item/reagent_containers/medspray/sterilizine = 1)
@@ -450,6 +463,15 @@
desc = "A refrigerated storage unit for medicine storage."
/obj/machinery/smartfridge/chemistry/accept_check(obj/item/O)
var/static/list/chemfridge_typecache = typecacheof(list(
/obj/item/reagent_containers/syringe,
/obj/item/reagent_containers/glass/bottle,
/obj/item/reagent_containers/glass/beaker,
/obj/item/reagent_containers/spray,
// /obj/item/reagent_containers/medigel,
/obj/item/reagent_containers/chem_pack
))
if(istype(O, /obj/item/storage/pill_bottle))
if(O.contents.len)
for(var/obj/item/I in O)
@@ -463,7 +485,7 @@
return TRUE
if(!O.reagents || !O.reagents.reagent_list.len) // other empty containers not accepted
return FALSE
if(istype(O, /obj/item/reagent_containers/syringe) || istype(O, /obj/item/reagent_containers/glass/bottle) || istype(O, /obj/item/reagent_containers/glass/beaker) || istype(O, /obj/item/reagent_containers/spray) || istype(O, /obj/item/reagent_containers/medspray) || istype(O, /obj/item/reagent_containers/chem_pack))
if(is_type_in_typecache(O, chemfridge_typecache))
return TRUE
return FALSE
@@ -487,6 +509,7 @@
/obj/item/reagent_containers/glass/bottle/cold = 1,
/obj/item/reagent_containers/glass/bottle/flu_virion = 1,
/obj/item/reagent_containers/glass/bottle/mutagen = 1,
/obj/item/reagent_containers/glass/bottle/sugar = 1,
/obj/item/reagent_containers/glass/bottle/plasma = 1,
/obj/item/reagent_containers/glass/bottle/synaptizine = 1,
/obj/item/reagent_containers/glass/bottle/formaldehyde = 1)
@@ -498,8 +521,8 @@
name = "disk compartmentalizer"
desc = "A machine capable of storing a variety of disks. Denoted by most as the DSU (disk storage unit)."
icon_state = "disktoaster"
visible_contents = FALSE
pass_flags = PASSTABLE
visible_contents = FALSE
/obj/machinery/smartfridge/disks/accept_check(obj/item/O)
if(istype(O, /obj/item/disk/))
@@ -6,7 +6,7 @@
/datum/food_processor_process/meat
input = /obj/item/reagent_containers/food/snacks/meat/slab
output = /obj/item/reagent_containers/food/snacks/meatball
output = /obj/item/reagent_containers/food/snacks/rawmeatball
/datum/food_processor_process/bacon
input = /obj/item/reagent_containers/food/snacks/meat/rawcutlet
@@ -130,6 +130,18 @@
result = /obj/item/reagent_containers/food/snacks/pie/dulcedebatata
subcategory = CAT_PIE
/datum/crafting_recipe/food/burek
name = "Burek"
reqs = list(
/datum/reagent/consumable/blackpepper = 3,
/datum/reagent/consumable/sodiumchloride = 3,
/obj/item/reagent_containers/food/snacks/pizzabread = 2,
/obj/item/reagent_containers/food/snacks/meat/cutlet/plain = 6,
/obj/item/reagent_containers/food/snacks/butter = 1,
)
result = /obj/item/reagent_containers/food/snacks/pie/burek
subcategory = CAT_PIE
/datum/crafting_recipe/food/meatpie
name = "Meat pie"
reqs = list(
@@ -302,4 +314,4 @@
/obj/item/reagent_containers/food/snacks/spiderling = 1
)
result = /obj/item/reagent_containers/food/snacks/spiderlollipop
subcategory = CAT_PIE
subcategory = CAT_PIE
+57 -31
View File
@@ -2,10 +2,7 @@
Holodeck Update
The on-station holodeck area is of type [holodeck_type].
All types found in GLOB.holodeck_areas_per_comp_type[src.type], generated on make_datum_references_lists(),
are loaded into the program cache or emag programs list.
Paths with their abstract_type variable equal to themselves will be skipped.
All subtypes of [program_type] are loaded into the program cache or emag programs list.
If init_program is null, a random program will be loaded on startup.
If you don't wish this, set it to the offline program or another of your choosing.
@@ -15,6 +12,7 @@
3) Create a new control console that uses those areas
Non-mapped areas should be skipped but you should probably comment them out anyway.
The base of program_type will always be ignored; only subtypes will be loaded.
*/
#define HOLODECK_CD 25
@@ -26,18 +24,20 @@
icon_screen = "holocontrol"
idle_power_usage = 10
active_power_usage = 50
var/area/holodeck/linked
var/area/holodeck/program
var/area/holodeck/last_program
var/area/offline_program = /area/holodeck/rec_center/offline
// Splitting this up allows two holodecks of the same size
// to use the same source patterns. Y'know, if you want to.
var/holodeck_type = /area/holodeck/rec_center
var/list/program_cache
var/list/emag_programs
// Splitting this up allows two holodecks of the same size
// to use the same source patterns. Y'know, if you want to.
var/holodeck_type = /area/holodeck/rec_center // locate(this) to get the target holodeck
var/program_type = /area/holodeck/rec_center // subtypes of this (but not this itself) are loadable programs
var/active = FALSE
var/damaged = FALSE
var/list/spawned = list()
@@ -49,41 +49,47 @@
return INITIALIZE_HINT_LATELOAD
/obj/machinery/computer/holodeck/LateInitialize()
linked = SSholodeck.target_holodeck_area[type]
offline_program = SSholodeck.offline_programs[type]
if(ispath(holodeck_type, /area))
linked = pop(get_areas(holodeck_type, FALSE))
if(ispath(offline_program, /area))
offline_program = pop(get_areas(offline_program), FALSE)
// the following is necessary for power reasons
if(!linked || !offline_program)
log_world("No matching holodeck area found")
qdel(src)
return
program_cache = SSholodeck.program_cache[type]
emag_programs = SSholodeck.emag_program_cache[type]
// the following is necessary for power reasons
var/area/AS = get_base_area(src)
var/area/AS = get_area(src)
if(istype(AS, /area/holodeck))
log_mapping("Holodeck computer cannot be in a holodeck, This would cause circular power dependency.")
qdel(src)
return
else
linked.linked = src
/*
var/area/my_area = get_area(src)
if(my_area)
linked.power_usage = my_area.power_usage
else
linked.power_usage = new /list(AREA_USAGE_LEN)
*/
generate_program_list()
load_program(offline_program, FALSE, FALSE)
/obj/machinery/computer/holodeck/Destroy()
emergency_shutdown()
if(linked)
linked.linked = null
//linked.power_usage = new /list(AREA_USAGE_LEN)
return ..()
/obj/machinery/computer/holodeck/power_change()
. = ..()
toggle_power(!stat)
/obj/machinery/computer/holodeck/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/holodeck/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "Holodeck", name, 400, 500, master_ui, state)
ui = new(user, src, "Holodeck", name)
ui.open()
/obj/machinery/computer/holodeck/ui_data(mob/user)
@@ -107,19 +113,27 @@
var/program_to_load = text2path(params["type"])
if(!ispath(program_to_load))
return FALSE
var/valid = FALSE
var/list/checked = program_cache.Copy()
if(obj_flags & EMAGGED)
checked |= emag_programs
for(var/prog in checked)
var/list/P = prog
if(P["type"] == program_to_load)
valid = TRUE
break
if(!valid)
return FALSE
var/area/A = locate(program_to_load) in GLOB.sortedAreas
if(A)
load_program(A)
if("safety")
if(!hasSiliconAccessInArea(usr) && !IsAdminGhost(usr))
var/msg = "[key_name(usr)] attempted to emag the holodeck using a href they shouldn't have!"
message_admins(msg)
log_admin(msg)
return
obj_flags ^= EMAGGED
if((obj_flags & EMAGGED) && program && emag_programs[program.name])
if((obj_flags & EMAGGED) && program)
emergency_shutdown()
nerf(obj_flags & EMAGGED)
obj_flags ^= EMAGGED
say("Safeties restored. Restarting...")
/obj/machinery/computer/holodeck/process()
if(damaged && prob(10))
@@ -160,13 +174,12 @@
if(!LAZYLEN(emag_programs))
to_chat(user, "[src] does not seem to have a card swipe port. It must be an inferior model.")
return
playsound(src, "sparks", 75, 1)
playsound(src, "sparks", 75, TRUE)
obj_flags |= EMAGGED
to_chat(user, "<span class='warning'>You vastly increase projector power and override the safety and security protocols.</span>")
to_chat(user, "Warning. Automatic shutoff and derezing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
say("Warning. Automatic shutoff and derezzing protocols have been corrupted. Please call Nanotrasen maintenance and do not use the simulator.")
log_game("[key_name(user)] emagged the Holodeck Control Console")
nerf(!(obj_flags & EMAGGED))
return TRUE
/obj/machinery/computer/holodeck/emp_act(severity)
. = ..()
@@ -182,6 +195,19 @@
emergency_shutdown()
return ..()
/obj/machinery/computer/holodeck/proc/generate_program_list()
for(var/typekey in subtypesof(program_type))
var/area/holodeck/A = GLOB.areas_by_type[typekey]
if(!A || !A.contents.len)
continue
var/list/info_this = list()
info_this["name"] = A.name
info_this["type"] = A.type
if(A.restricted)
LAZYADD(emag_programs, list(info_this))
else
LAZYADD(program_cache, list(info_this))
/obj/machinery/computer/holodeck/proc/toggle_power(toggleOn = FALSE)
if(active == toggleOn)
return
@@ -281,7 +307,7 @@
silent = FALSE // otherwise make sure they are dropped
if(!silent)
visible_message("[O] fades away!")
visible_message("<span class='notice'>[O] fades away!</span>")
qdel(O)
#undef HOLODECK_CD
+120 -128
View File
@@ -10,13 +10,13 @@
var/processing = FALSE
var/obj/item/reagent_containers/glass/beaker = null
var/points = 0
var/menustat = "menu"
var/efficiency = 0
var/productivity = 0
var/max_items = 40
var/datum/techweb/stored_research
var/list/show_categories = list("Food", "Botany Chemicals", "Organic Materials")
var/list/timesFiveCategories = list("Food", "Botany Chemicals")
/// Currently selected category in the UI
var/selected_cat
/obj/machinery/biogenerator/Initialize()
. = ..()
@@ -37,22 +37,20 @@
if(A == beaker)
beaker = null
update_icon()
updateUsrDialog()
/obj/machinery/biogenerator/RefreshParts()
var/E = 0.5
var/P = 0.5
var/max_storage = 20
var/E = 0
var/P = 0
var/max_storage = 40
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
P += B.rating * 0.5
max_storage = max(20 * B.rating, max_storage)
P += B.rating
max_storage = 40 * B.rating
for(var/obj/item/stock_parts/manipulator/M in component_parts)
E += M.rating * 0.5
E += M.rating
efficiency = E
productivity = P
max_items = max_storage
/obj/machinery/biogenerator/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
@@ -70,7 +68,6 @@
icon_state = "biogen-stand"
else
icon_state = "biogen-work"
return
/obj/machinery/biogenerator/attackby(obj/item/O, mob/user, params)
if(user.a_intent == INTENT_HARM)
@@ -102,7 +99,6 @@
beaker = O
to_chat(user, "<span class='notice'>You add the container to the machine.</span>")
update_icon()
updateUsrDialog()
else
to_chat(user, "<span class='warning'>Close the maintenance panel first.</span>")
return
@@ -139,9 +135,9 @@
to_chat(user, "<span class='info'>You put [O.name] in [src.name]</span>")
return TRUE //no afterattack
else if (istype(O, /obj/item/disk/design_disk))
user.visible_message("[user] begins to load \the [O] in \the [src]...",
"You begin to load a design from \the [O]...",
"You hear the chatter of a floppy drive.")
user.visible_message("<span class='notice'>[user] begins to load \the [O] in \the [src]...</span>",
"<span class='notice'>You begin to load a design from \the [O]...</span>",
"<span class='hear'>You hear the chatter of a floppy drive.</span>")
processing = TRUE
var/obj/item/disk/design_disk/D = O
if(do_after(user, 10, target = src))
@@ -153,106 +149,53 @@
else
to_chat(user, "<span class='warning'>You cannot put this in [src.name]!</span>")
/obj/machinery/biogenerator/ui_interact(mob/user)
if(stat & BROKEN || panel_open)
return
. = ..()
var/dat
if(processing)
dat += "<div class='statusDisplay'>Biogenerator is processing! Please wait...</div><BR>"
else
switch(menustat)
if("nopoints")
dat += "<div class='statusDisplay'>You do not have enough biomass to create products.<BR>Please, put growns into reactor and activate it.</div>"
menustat = "menu"
if("complete")
dat += "<div class='statusDisplay'>Operation complete.</div>"
menustat = "menu"
if("void")
dat += "<div class='statusDisplay'>Error: No growns inside.<BR>Please, put growns into reactor.</div>"
menustat = "menu"
if("nobeakerspace")
dat += "<div class='statusDisplay'>Not enough space left in container. Unable to create product.</div>"
menustat = "menu"
if(beaker)
var/categories = show_categories.Copy()
for(var/V in categories)
categories[V] = list()
for(var/V in stored_research.researched_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(V)
for(var/C in categories)
if(C in D.category)
categories[C] += D
dat += "<div class='statusDisplay'>Biomass: [points] units.</div><BR>"
dat += "<A href='?src=[REF(src)];activate=1'>Activate</A><A href='?src=[REF(src)];detach=1'>Detach Container</A>"
for(var/cat in categories)
dat += "<h3>[cat]:</h3>"
dat += "<div class='statusDisplay'>"
for(var/V in categories[cat])
var/datum/design/D = V
dat += "[D.name]: <A href='?src=[REF(src)];create=[D.id];amount=1'>Make</A>"
if(cat in timesFiveCategories)
dat += "<A href='?src=[REF(src)];create=[D.id];amount=5'>x5</A>"
if(ispath(D.build_path, /obj/item/stack))
dat += "<A href='?src=[REF(src)];create=[D.id];amount=10'>x10</A>"
dat += "([CEILING(D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency, 1)])<br>"
dat += "</div>"
else
dat += "<div class='statusDisplay'>No container inside, please insert container.</div>"
var/datum/browser/popup = new(user, "biogen", name, 350, 520)
popup.set_content(dat)
popup.open()
/obj/machinery/biogenerator/AltClick(mob/living/user)
. = ..()
if(istype(user) && user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) && can_interact(user))
detach(user)
/obj/machinery/biogenerator/proc/activate()
if (usr.stat != CONSCIOUS)
/**
* activate: Activates biomass processing and converts all inserted grown products into biomass
*
* Arguments:
* * user The mob starting the biomass processing
*/
/obj/machinery/biogenerator/proc/activate(mob/user)
if(user.stat != CONSCIOUS)
return
if (src.stat != NONE) //NOPOWER etc
if(stat != NONE)
return
if(processing)
to_chat(usr, "<span class='warning'>The biogenerator is in the process of working.</span>")
to_chat(user, "<span class='warning'>The biogenerator is in the process of working.</span>")
return
var/S = 0
var/total = 0
for(var/obj/item/reagent_containers/food/snacks/grown/I in contents)
S += 5
var/nutri_amount = I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment)
if(nutri_amount < 0.1)
total += 1*productivity
if(I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) < 0.1)
points += 1 * productivity
else
total += nutri_amount*10*productivity
points += I.reagents.get_reagent_amount(/datum/reagent/consumable/nutriment) * 10 * productivity
qdel(I)
points += round(total)
if(S)
processing = TRUE
update_icon()
updateUsrDialog()
playsound(src.loc, 'sound/machines/blender.ogg', 50, 1)
use_power(S*30)
sleep(S+15/productivity)
playsound(loc, 'sound/machines/blender.ogg', 50, TRUE)
use_power(S * 30)
sleep(S + 15 / productivity)
if(QDELETED(src)) //let's not.
return
processing = FALSE
update_icon()
else
menustat = "void"
/obj/machinery/biogenerator/proc/check_cost(list/materials, multiplier = 1, remove_points = TRUE)
if(materials.len != 1 || materials[1] != SSmaterials.GetMaterialRef(/datum/material/biomass))
return FALSE
var/cost = CEILING(materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency, 1)
if (cost > points)
menustat = "nopoints"
if (materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency > points)
return FALSE
else
if(remove_points)
points -= cost
points -= materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]*multiplier/efficiency
update_icon()
updateUsrDialog()
return TRUE
/obj/machinery/biogenerator/proc/check_container_volume(list/reagents, multiplier = 1)
@@ -262,7 +205,6 @@
sum_reagents *= multiplier
if(beaker.reagents.total_volume + sum_reagents > beaker.reagents.maximum_volume)
menustat = "nobeakerspace"
return FALSE
return TRUE
@@ -284,6 +226,7 @@
var/i = amount
while(i > 0)
if(!check_container_volume(D.make_reagents))
say("Warning: Attached container does not have enough free capacity!")
return .
if(!check_cost(D.materials))
return .
@@ -293,51 +236,100 @@
beaker.reagents.add_reagent(R, D.make_reagents[R])
. = 1
--i
menustat = "complete"
update_icon()
return .
/obj/machinery/biogenerator/proc/detach(mob/living/user)
if(beaker)
user.put_in_hands(beaker)
if(can_interact(user))
user.put_in_hands(beaker)
else
beaker.drop_location(get_turf(src))
beaker = null
update_icon()
/obj/machinery/biogenerator/Topic(href, href_list)
if(..() || panel_open)
/obj/machinery/biogenerator/ui_status(mob/user)
if(stat & BROKEN || panel_open)
return UI_CLOSE
return ..()
/obj/machinery/biogenerator/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/research_designs),
)
/obj/machinery/biogenerator/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "Biogenerator", name)
ui.open()
/obj/machinery/biogenerator/ui_data(mob/user)
var/list/data = list()
data["beaker"] = beaker ? TRUE : FALSE
data["biomass"] = points
data["processing"] = processing
if(locate(/obj/item/reagent_containers/food/snacks/grown) in contents)
data["can_process"] = TRUE
else
data["can_process"] = FALSE
return data
/obj/machinery/biogenerator/ui_static_data(mob/user)
var/list/data = list()
data["categories"] = list()
var/categories = show_categories.Copy()
for(var/V in categories)
categories[V] = list()
for(var/V in stored_research.researched_designs)
var/datum/design/D = SSresearch.techweb_design_by_id(V)
for(var/C in categories)
if(C in D.category)
categories[C] += D
for(var/category in categories)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
for(var/item in categories[category])
var/datum/design/D = item
cat["items"] += list(list(
"id" = D.id,
"name" = D.name,
"cost" = D.materials[SSmaterials.GetMaterialRef(/datum/material/biomass)]/efficiency,
))
data["categories"] += list(cat)
return data
/obj/machinery/biogenerator/ui_act(action, list/params)
if(..())
return
usr.set_machine(src)
if(href_list["activate"])
activate()
updateUsrDialog()
else if(href_list["detach"])
detach(usr)
updateUsrDialog()
else if(href_list["create"])
var/amount = (text2num(href_list["amount"]))
//Can't be outside these (if you change this keep a sane limit)
amount = clamp(amount, 1, 50)
var/id = href_list["create"]
if(!stored_research.researched_designs.Find(id))
//naughty naughty
stack_trace("ID did not map to a researched datum [id]")
return
//Get design by id (or may return error design)
var/datum/design/D = SSresearch.techweb_design_by_id(id)
//Valid design datum, amount and the datum is not the error design, lets proceed
if(D && amount && !istype(D, /datum/design/error_design))
create_product(D, amount)
//This shouldnt happen normally but href forgery is real
else
stack_trace("ID could not be turned into a valid techweb design datum [id]")
updateUsrDialog()
else if(href_list["menu"])
menustat = "menu"
updateUsrDialog()
switch(action)
if("activate")
activate(usr)
return TRUE
if("detach")
detach(usr)
return TRUE
if("create")
var/amount = text2num(params["amount"])
amount = clamp(amount, 1, 10)
if(!amount)
return
var/id = params["id"]
if(!stored_research.researched_designs.Find(id))
stack_trace("ID did not map to a researched datum [id]")
return
var/datum/design/D = SSresearch.techweb_design_by_id(id)
if(D && !istype(D, /datum/design/error_design))
create_product(D, amount)
else
stack_trace("ID could not be turned into a valid techweb design datum [id]")
return
return TRUE
if("select")
selected_cat = params["category"]
return TRUE
+82 -80
View File
@@ -1,3 +1,18 @@
/**
* Finds and extracts seeds from an object
*
* Checks if the object is such that creates a seed when extracted. Used by seed
* extractors or posably anything that would create seeds in some way. The seeds
* are dropped either at the extractor, if it exists, or where the original object
* was and it qdel's the object
*
* Arguments:
* * O - Object containing the seed, can be the loc of the dumping of seeds
* * t_max - Amount of seed copies to dump, -1 is ranomized
* * extractor - Seed Extractor, used as the dumping loc for the seeds and seed multiplier
* * user - checks if we can remove the object from the inventory
* *
*/
/proc/seedify(obj/item/O, t_max, obj/machinery/seed_extractor/extractor, mob/living/user)
var/t_amount = 0
var/list/seeds = list()
@@ -46,20 +61,22 @@
icon_state = "sextractor"
density = TRUE
circuit = /obj/item/circuitboard/machine/seed_extractor
var/piles = list()
/// Associated list of seeds, they are all weak refs. We check the len to see how many refs we have for each
// seed
var/list/piles = list()
var/max_seeds = 1000
var/seed_multiplier = 1
/obj/machinery/seed_extractor/RefreshParts()
for(var/obj/item/stock_parts/matter_bin/B in component_parts)
max_seeds = 1000 * B.rating
max_seeds = initial(max_seeds) * B.rating
for(var/obj/item/stock_parts/manipulator/M in component_parts)
seed_multiplier = M.rating
seed_multiplier = initial(seed_multiplier) * M.rating
/obj/machinery/seed_extractor/examine(mob/user)
. = ..()
if(in_range(user, src) || isobserver(user))
. += "<span class='notice'>The status display reads: Extracting <b>[seed_multiplier]</b> seed(s) per piece of produce.<br>Machine can store up to <b>[max_seeds]</b> seeds.</span>"
. += "<span class='notice'>The status display reads: Extracting <b>[seed_multiplier]</b> seed(s) per piece of produce.<br>Machine can store up to <b>[max_seeds]%</b> seeds.</span>"
/obj/machinery/seed_extractor/attackby(obj/item/O, mob/user, params)
@@ -102,78 +119,26 @@
else
return ..()
/datum/seed_pile
var/name = ""
var/lifespan = 0 //Saved stats
var/endurance = 0
var/maturation = 0
var/production = 0
var/yield = 0
var/potency = 0
var/amount = 0
/**
* Generate seed string
*
* Creates a string based of the traits of a seed. We use this string as a bucket for all
* seeds that match as well as the key the ui uses to get the seed. We also use the key
* for the data shown in the ui. Javascript parses this string to display
*
* Arguments:
* * O - seed to generate the string from
*/
/obj/machinery/seed_extractor/proc/generate_seed_string(obj/item/seeds/O)
return "name=[O.name];lifespan=[O.lifespan];endurance=[O.endurance];maturation=[O.maturation];production=[O.production];yield=[O.yield];potency=[O.potency];instability=0"
/datum/seed_pile/New(var/name, var/life, var/endur, var/matur, var/prod, var/yie, var/poten, var/am = 1)
src.name = name
src.lifespan = life
src.endurance = endur
src.maturation = matur
src.production = prod
src.yield = yie
src.potency = poten
src.amount = am
/obj/machinery/seed_extractor/ui_interact(mob/user)
. = ..()
if (stat)
return FALSE
var/dat = "<b>Stored seeds:</b><br>"
if (contents.len == 0)
dat += "<font color='red'>No seeds</font>"
else
dat += "<table cellpadding='3' style='text-align:center;'><tr><td>Name</td><td>Lifespan</td><td>Endurance</td><td>Maturation</td><td>Production</td><td>Yield</td><td>Potency</td><td>Stock</td></tr>"
for (var/datum/seed_pile/O in piles)
dat += "<tr><td>[O.name]</td><td>[O.lifespan]</td><td>[O.endurance]</td><td>[O.maturation]</td>"
dat += "<td>[O.production]</td><td>[O.yield]</td><td>[O.potency]</td><td>"
dat += "<a href='byond://?src=[REF(src)];name=[O.name];li=[O.lifespan];en=[O.endurance];ma=[O.maturation];pr=[O.production];yi=[O.yield];pot=[O.potency]'>Vend</a> ([O.amount] left)</td></tr>"
dat += "</table>"
var/datum/browser/popup = new(user, "seed_ext", name, 700, 400)
popup.set_content(dat)
popup.open()
return
/obj/machinery/seed_extractor/Topic(var/href, var/list/href_list)
if(..())
return
usr.set_machine(src)
href_list["li"] = text2num(href_list["li"])
href_list["en"] = text2num(href_list["en"])
href_list["ma"] = text2num(href_list["ma"])
href_list["pr"] = text2num(href_list["pr"])
href_list["yi"] = text2num(href_list["yi"])
href_list["pot"] = text2num(href_list["pot"])
for (var/datum/seed_pile/N in piles)//Find the pile we need to reduce...
if (href_list["name"] == N.name && href_list["li"] == N.lifespan && href_list["en"] == N.endurance && href_list["ma"] == N.maturation && href_list["pr"] == N.production && href_list["yi"] == N.yield && href_list["pot"] == N.potency)
if(N.amount <= 0)
return
N.amount = max(N.amount - 1, 0)
if (N.amount <= 0)
piles -= N
qdel(N)
break
for (var/obj/T in contents)//Now we find the seed we need to vend
var/obj/item/seeds/O = T
if (O.plantname == href_list["name"] && O.lifespan == href_list["li"] && O.endurance == href_list["en"] && O.maturation == href_list["ma"] && O.production == href_list["pr"] && O.yield == href_list["yi"] && O.potency == href_list["pot"])
O.forceMove(drop_location())
break
src.updateUsrDialog()
return
/** Add Seeds Proc.
*
* Adds the seeds to the contents and to an associated list that pregenerates the data
* needed to go to the ui handler
*
**/
/obj/machinery/seed_extractor/proc/add_seed(obj/item/seeds/O)
if(contents.len >= 999)
to_chat(usr, "<span class='notice'>\The [src] is full.</span>")
@@ -188,10 +153,47 @@
if(!M.transferItemToLoc(O, src))
return FALSE
. = TRUE
for (var/datum/seed_pile/N in piles)
if (O.plantname == N.name && O.lifespan == N.lifespan && O.endurance == N.endurance && O.maturation == N.maturation && O.production == N.production && O.yield == N.yield && O.potency == N.potency)
++N.amount
return
var/seed_string = generate_seed_string(O)
if(piles[seed_string])
piles[seed_string] += WEAKREF(O)
else
piles[seed_string] = list(WEAKREF(O))
. = TRUE
/obj/machinery/seed_extractor/ui_state(mob/user)
return GLOB.notcontained_state
/obj/machinery/seed_extractor/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, "SeedExtractor", name)
ui.open()
/obj/machinery/seed_extractor/ui_data()
var/list/V = list()
for(var/key in piles)
if(piles[key])
var/len = length(piles[key])
if(len)
V[key] = len
. = list()
.["seeds"] = V
/obj/machinery/seed_extractor/ui_act(action, params)
if(..())
return
switch(action)
if("select")
var/item = params["item"]
if(piles[item] && length(piles[item]) > 0)
var/datum/weakref/WO = piles[item][1]
var/obj/item/seeds/O = WO.resolve()
if(O)
piles[item] -= WO
O.forceMove(drop_location())
. = TRUE
//to_chat(usr, "<span class='notice'>[src] clanks to life briefly before vending [prize.equipment_name]!</span>")
piles += new /datum/seed_pile(O.plantname, O.lifespan, O.endurance, O.maturation, O.production, O.yield, O.potency)
+6
View File
@@ -324,6 +324,12 @@ Key procs
/datum/language/sylvan = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/sylvan = list(LANGUAGE_ATOM))
/datum/language_holder/ethereal
understood_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/voltaic = list(LANGUAGE_ATOM))
spoken_languages = list(/datum/language/common = list(LANGUAGE_ATOM),
/datum/language/voltaic = list(LANGUAGE_ATOM))
/datum/language_holder/empty
understood_languages = list()
spoken_languages = list()
+6 -3
View File
@@ -8,10 +8,13 @@
language_holder = null
. = ..()
/datum/language_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.language_menu_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/language_menu/ui_state(mob/user)
return GLOB.language_menu_state
/datum/language_menu/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "LanguageMenu", "Language Menu", 700, 600, master_ui, state)
ui = new(user, src, "LanguageMenu")
ui.open()
/datum/language_menu/ui_data(mob/user)
+14
View File
@@ -0,0 +1,14 @@
// One of these languages will actually work, I'm certain of it.
/datum/language/voltaic
name = "Voltaic"
desc = "A sparky language made by manipulating electrical discharge."
key = "v"
space_chance = 20
syllables = list(
"bzzt", "skrrt", "zzp", "mmm", "hzz", "tk", "shz", "k", "z",
"bzt", "zzt", "skzt", "skzz", "hmmt", "zrrt", "hzzt", "hz",
"vzt", "zt", "vz", "zip", "tzp", "lzzt", "dzzt", "zdt", "kzt",
"zzzz", "mzz"
)
icon_state = "volt"
default_priority = 90
+6 -7
View File
@@ -34,13 +34,13 @@
if(U.check_acedia())
to_chat(user, "<span class='notice'>None of this matters, why are you reading this? You put [title] down.</span>")
return
user.visible_message("[user] opens [title] and begins reading intently.")
user.visible_message("<span class='notice'>[user] opens [title] and begins reading intently.</span>")
ask_name(user)
/obj/item/book/codex_gigas/proc/perform_research(mob/user, devilName)
if(!devilName)
user.visible_message("[user] closes [title] without looking anything up.")
user.visible_message("<span class='notice'>[user] closes [title] without looking anything up.</span>")
return
inUse = TRUE
var/speed = 300
@@ -50,7 +50,7 @@
if(U.job in list("Curator")) // the curator is both faster, and more accurate than normal crew members at research
speed = 100
correctness = 100
correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) *0.5 //Brain damage makes researching hard.
correctness -= U.getOrganLoss(ORGAN_SLOT_BRAIN) * 0.5 //Brain damage makes researching hard.
speed += U.getOrganLoss(ORGAN_SLOT_BRAIN) * 3
if(do_after(user, speed, 0, user))
var/usedName = devilName
@@ -95,11 +95,10 @@
currentSection = SUFFIX
return currentSection != oldSection
/obj/item/book/codex_gigas/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/item/book/codex_gigas/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "CodexGigas", name, 450, 450, master_ui, state)
ui = new(user, src, "CodexGigas", name)
ui.open()
/obj/item/book/codex_gigas/ui_data(mob/user)
+14 -13
View File
@@ -35,13 +35,13 @@
return
if(existing_message)
user.visible_message("<span class='notice'>[user] starts erasing [existing_message].</span>", "<span class='notice'>You start erasing [existing_message].</span>", "<span class='italics'>You hear a chipping sound.</span>")
playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
user.visible_message("<span class='notice'>[user] starts erasing [existing_message].</span>", "<span class='notice'>You start erasing [existing_message].</span>", "<span class='hear'>You hear a chipping sound.</span>")
playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
if(do_after(user, tool_speed, target = existing_message))
user.visible_message("<span class='notice'>[user] erases [existing_message].</span>", "<span class='notice'>You erase [existing_message][existing_message.creator_key == user.ckey ? ", refunding a use" : ""].</span>")
existing_message.persists = FALSE
qdel(existing_message)
playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
if(existing_message.creator_key == user.ckey)
refund_use()
return
@@ -54,12 +54,12 @@
if(!target.Adjacent(user) && locate(/obj/structure/chisel_message) in T)
to_chat(user, "<span class='warning'>Someone wrote here before you chose! Find another spot.</span>")
return
playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
user.visible_message("<span class='notice'>[user] starts engraving a message into [T]...</span>", "<span class='notice'>You start engraving a message into [T]...</span>", "<span class='italics'>You hear a chipping sound.</span>")
playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
user.visible_message("<span class='notice'>[user] starts engraving a message into [T]...</span>", "<span class='notice'>You start engraving a message into [T]...</span>", "<span class='hear'>You hear a chipping sound.</span>")
if(can_use() && do_after(user, tool_speed, target = T) && can_use()) //This looks messy but it's actually really clever!
if(!locate(/obj/structure/chisel_message) in T)
user.visible_message("<span class='notice'>[user] leaves a message for future spacemen!</span>", "<span class='notice'>You engrave a message into [T]!</span>", "<span class='italics'>You hear a chipping sound.</span>")
playsound(loc, 'sound/items/gavel.ogg', 50, 1, -1)
user.visible_message("<span class='notice'>[user] leaves a message for future spacemen!</span>", "<span class='notice'>You engrave a message into [T]!</span>", "<span class='hear'>You hear a chipping sound.</span>")
playsound(loc, 'sound/items/gavel.ogg', 50, TRUE, -1)
var/obj/structure/chisel_message/M = new(T)
M.register(user, message)
remove_use()
@@ -112,12 +112,10 @@
desc = "A message from a past traveler."
icon = 'icons/obj/stationobjs.dmi'
icon_state = "soapstone_message"
layer = HIGH_OBJ_LAYER
layer = LATTICE_LAYER
density = FALSE
anchored = TRUE
max_integrity = 30
layer = LATTICE_LAYER
light_power = 0.3
var/hidden_message
var/creator_key
@@ -206,10 +204,13 @@
/obj/structure/chisel_message/interact()
return
/obj/structure/chisel_message/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.always_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/structure/chisel_message/ui_state(mob/user)
return GLOB.always_state
/obj/structure/chisel_message/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "EngravedMessage", name, 600, 300, master_ui, state)
ui = new(user, src, "EngravedMessage", name)
ui.open()
/obj/structure/chisel_message/ui_data(mob/user)
+65
View File
@@ -0,0 +1,65 @@
///how many people can play mafia without issues (running out of spawns, procs not expecting more than this amount of people, etc)
#define MAFIA_MAX_PLAYER_COUNT 12
#define MAFIA_TEAM_TOWN "town"
#define MAFIA_TEAM_MAFIA "mafia"
#define MAFIA_TEAM_SOLO "solo"
//types of town roles for random setup gen
/// assistants it's just assistants filling up the rest of the roles
#define TOWN_OVERFLOW "overflow"
/// roles that learn info about others in the game (chaplain, detective, psych)
#define TOWN_INVEST "invest"
/// roles that keep other roles safe (doctor, and weirdly enough lawyer counts)
#define TOWN_PROTECT "protect"
/// roles that don't fit into anything else (hop)
#define TOWN_MISC "misc"
//other types (mafia team, neutrals)
/// normal vote kill changelings
#define MAFIA_REGULAR "regular"
/// every other changeling role that has extra abilities
#define MAFIA_SPECIAL "special"
/// role that wins solo that nobody likes
#define NEUTRAL_KILL "kill"
/// role that upsets the game aka obsessed, usually worse for town than mafia but they can vote against mafia
#define NEUTRAL_DISRUPT "disrupt"
#define MAFIA_PHASE_SETUP 1
#define MAFIA_PHASE_DAY 2
#define MAFIA_PHASE_VOTING 3
#define MAFIA_PHASE_JUDGEMENT 4
#define MAFIA_PHASE_NIGHT 5
#define MAFIA_PHASE_VICTORY_LAP 6
#define MAFIA_ALIVE 1
#define MAFIA_DEAD 2
#define COMSIG_MAFIA_ON_KILL "mafia_onkill"
#define MAFIA_PREVENT_KILL 1
#define COMSIG_MAFIA_CAN_PERFORM_ACTION "mafia_can_perform_action"
#define MAFIA_PREVENT_ACTION 1
//in order of events + game end
/// when the shutters fall, before the 45 second wait and night event resolution
#define COMSIG_MAFIA_SUNDOWN "sundown"
/// after the 45 second wait, for actions that must go first
#define COMSIG_MAFIA_NIGHT_START "night_start"
/// most night actions now resolve
#define COMSIG_MAFIA_NIGHT_ACTION_PHASE "night_actions"
/// now killing happens from the roles that do that. the reason this is post action phase is to ensure doctors can protect and lawyers can block
#define COMSIG_MAFIA_NIGHT_KILL_PHASE "night_kill"
/// now undoing states like protection, actions that must happen last, etc. right before shutters raise and the day begins
#define COMSIG_MAFIA_NIGHT_END "night_end"
/// signal sent to roles when the game is confirmed ending
#define COMSIG_MAFIA_GAME_END "game_end"
/// list of ghosts who want to play mafia, every time someone enters the list it checks to see if enough are in
GLOBAL_LIST_EMPTY(mafia_signup)
/// list of ghosts who want to play mafia that have since disconnected. They are kept in the lobby, but not counted for starting a game.
GLOBAL_LIST_EMPTY(mafia_bad_signup)
/// the current global mafia game running.
GLOBAL_VAR(mafia_game)
+947
View File
@@ -0,0 +1,947 @@
/**
* The mafia controller handles the mafia minigame in progress.
* It is first created when the first ghost signs up to play.
*/
/datum/mafia_controller
///list of observers that should get game updates.
var/list/spectators = list()
///all roles in the game, dead or alive. check their game status if you only want living or dead.
var/list/all_roles = list()
///exists to speed up role retrieval, it's a dict. player_role_lookup[player ckey] will give you the role they play
var/list/player_role_lookup = list()
///what part of the game you're playing in. day phases, night phases, judgement phases, etc.
var/phase = MAFIA_PHASE_SETUP
///how long the game has gone on for, changes with every sunrise. day one, night one, day two, etc.
var/turn = 0
///for debugging and testing a full game, or adminbuse. If this is not null, it will use this as a setup. clears when game is over
var/list/custom_setup = list()
///first day has no voting, and thus is shorter
var/first_day_phase_period = 20 SECONDS
///talk with others about the last night
var/day_phase_period = 1 MINUTES
///vote someone to get put on trial
var/voting_phase_period = 30 SECONDS
///defend yourself! don't get lynched! sometimes skipped if nobody votes.
var/judgement_phase_period = 30 SECONDS
///guilty or innocent, we want a bit of time for players to process the outcome of the vote
var/judgement_lynch_period = 5 SECONDS
///mafia talk at night and pick someone to kill, some town roles use their actions, etc etc.
var/night_phase_period = 45 SECONDS
///like the lynch period, players need to see what the other players in the game's roles were
var/victory_lap_period = 20 SECONDS
///template picked when the game starts. used for the name and desc reading
var/datum/map_template/mafia/current_map
///map generation tool that deletes the current map after the game finishes
var/datum/mapGenerator/massdelete/map_deleter
///Readable list of roles in current game, sent to the tgui panel for roles list > list("Psychologist x1", "Clown x2")
var/list/current_setup_text
///starting outfit for all mafia players. it's just a grey jumpsuit.
var/player_outfit = /datum/outfit/mafia
///spawn points for players, each one has a house
var/list/landmarks = list()
///town center for when people get put on trial
var/town_center_landmark
///group voting on one person, like putting people to trial or choosing who to kill as mafia
var/list/votes = list()
///and these (judgement_innocent_votes, judgement_abstain_votes and judgement_guilty_votes) are the judgement phase votes, aka people sorting themselves into guilty and innocent, and "eh, i don't really care" lists. whichever has more inno or guilty wins!
var/list/judgement_abstain_votes = list()
var/list/judgement_innocent_votes = list()
var/list/judgement_guilty_votes = list()
///current role on trial for the judgement phase, will die if guilty is greater than innocent
var/datum/mafia_role/on_trial
///current timer for phase
var/next_phase_timer
///used for debugging in testing (doesn't put people out of the game, some other shit i forgot, who knows just don't set this in live) honestly kinda deprecated
var/debug = FALSE
///Max player count
var/max_player = MAFIA_MAX_PLAYER_COUNT
///Required player count
var/required_player = 5
/datum/mafia_controller/New()
. = ..()
GLOB.mafia_game = src
map_deleter = new
/datum/mafia_controller/Destroy(force, ...)
. = ..()
GLOB.mafia_game = null
end_game()
qdel(map_deleter)
/**
* Triggers at beginning of the game when there is a confirmed list of valid, ready players.
* Creates a 100% ready game that has NOT started (no players in bodies)
* Followed by start game
*
* Does the following:
* * Picks map, and loads it
* * Grabs landmarks if it is the first time it's loading
* * Sets up the role list
* * Puts players in each role randomly
* Arguments:
* * setup_list: list of all the datum setups (fancy list of roles) that would work for the game
* * ready_players: list of filtered, sane players (so not playing or disconnected) for the game to put into roles
*/
/datum/mafia_controller/proc/prepare_game(setup_list,ready_players)
var/list/possible_maps = subtypesof(/datum/map_template/mafia)
var/turf/spawn_area = get_turf(locate(/obj/effect/landmark/mafia_game_area) in GLOB.landmarks_list)
current_map = pick(possible_maps)
current_map = new current_map
if(!spawn_area)
CRASH("No spawn area detected for Mafia!")
var/list/bounds = current_map.load(spawn_area)
if(!bounds)
CRASH("Loading mafia map failed!")
map_deleter.defineRegion(spawn_area, locate(spawn_area.x + 23,spawn_area.y + 23,spawn_area.z), replace = TRUE) //so we're ready to mass delete when round ends
if(!landmarks.len)//we grab town center when we grab landmarks, if there is none (the first game signed up for let's grab them post load)
for(var/obj/effect/landmark/mafia/possible_spawn in GLOB.landmarks_list)
if(istype(possible_spawn, /obj/effect/landmark/mafia/town_center))
town_center_landmark = possible_spawn
else
landmarks += possible_spawn
current_setup_text = list()
for(var/rtype in setup_list)
for(var/i in 1 to setup_list[rtype])
all_roles += new rtype(src)
var/datum/mafia_role/rp = rtype
current_setup_text += "[initial(rp.name)] x[setup_list[rtype]]"
var/list/spawnpoints = landmarks.Copy()
for(var/datum/mafia_role/role in all_roles)
role.assigned_landmark = pick_n_take(spawnpoints)
if(!debug)
role.player_key = pick_n_take(ready_players)
else
role.player_key = pop(ready_players)
/datum/mafia_controller/proc/send_message(msg,team)
for(var/datum/mafia_role/R in all_roles)
if(team && R.team != team)
continue
to_chat(R.body,msg)
var/team_suffix = team ? "([uppertext(team)] CHAT)" : ""
for(var/M in GLOB.dead_mob_list)
var/mob/spectator = M
if(spectator.ckey in spectators) //was in current game, or spectatin' (won't send to living)
var/link = FOLLOW_LINK(M, town_center_landmark)
to_chat(M, "[link] MAFIA: [msg] [team_suffix]")
/**
* The game by this point is now all set up, and so we can put people in their bodies and start the first phase.
*
* Does the following:
* * Creates bodies for all of the roles with the first proc
* * Starts the first day manually (so no timer) with the second proc
*/
/datum/mafia_controller/proc/start_game()
create_bodies()
start_day()
/**
* How every day starts.
*
* What players do in this phase:
* * If day one, just a small starting period to see who is in the game and check role, leading to the night phase.
* * Otherwise, it's a longer period used to discuss events that happened during the night, leading to the voting phase.
*/
/datum/mafia_controller/proc/start_day()
turn += 1
phase = MAFIA_PHASE_DAY
if(!check_victory())
if(turn == 1)
send_message("<span class='notice'><b>The selected map is [current_map.name]!</b></br>[current_map.description]</span>")
send_message("<b>Day [turn] started! There is no voting on the first day. Say hello to everybody!</b>")
next_phase_timer = addtimer(CALLBACK(src,.proc/check_trial, FALSE),first_day_phase_period,TIMER_STOPPABLE) //no voting period = no votes = instant night
else
send_message("<b>Day [turn] started! Voting will start in 1 minute.</b>")
next_phase_timer = addtimer(CALLBACK(src,.proc/start_voting_phase),day_phase_period,TIMER_STOPPABLE)
SStgui.update_uis(src)
/**
* Players have finished the discussion period, and now must put up someone to the chopping block.
*
* What players do in this phase:
* * Vote on which player to put up for lynching, leading to the judgement phase.
* * If no votes are case, the judgement phase is skipped, leading to the night phase.
*/
/datum/mafia_controller/proc/start_voting_phase()
phase = MAFIA_PHASE_VOTING
next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, TRUE),voting_phase_period,TIMER_STOPPABLE) //be verbose!
send_message("<b>Voting started! Vote for who you want to see on trial today.</b>")
SStgui.update_uis(src)
/**
* Players have voted someone up, and now the person must defend themselves while the town votes innocent or guilty.
*
* What players do in this phase:
* * Vote innocent or guilty, if they are not on trial.
* * Defend themselves and wait for judgement, if they are.
* * Leads to the lynch phase.
* Arguments:
* * verbose: boolean, announces whether there were votes or not. after judgement it goes back here with no voting period to end the day.
*/
/datum/mafia_controller/proc/check_trial(verbose = TRUE)
var/datum/mafia_role/loser = get_vote_winner("Day")//, majority_of_town = TRUE)
// var/loser_votes = get_vote_count(loser,"Day")
if(loser)
// if(loser_votes > 12)
// loser.body.client?.give_award(/datum/award/achievement/mafia/universally_hated, loser.body)
send_message("<b>[loser.body.real_name] wins the day vote, Listen to their defense and vote \"INNOCENT\" or \"GUILTY\"!</b>")
//refresh the lists
judgement_abstain_votes = list()
judgement_innocent_votes = list()
judgement_guilty_votes = list()
for(var/i in all_roles)
var/datum/mafia_role/abstainee = i
if(abstainee.game_status == MAFIA_ALIVE && abstainee != loser)
judgement_abstain_votes += abstainee
on_trial = loser
on_trial.body.forceMove(get_turf(town_center_landmark))
phase = MAFIA_PHASE_JUDGEMENT
next_phase_timer = addtimer(CALLBACK(src, .proc/lynch),judgement_phase_period,TIMER_STOPPABLE)
reset_votes("Day")
else
if(verbose)
send_message("<b>Not enough people have voted to put someone on trial, nobody will be lynched today.</b>")
if(!check_victory())
lockdown()
SStgui.update_uis(src)
/**
* Players have voted innocent or guilty on the person on trial, and that person is now killed or returned home.
*
* What players do in this phase:
* * r/watchpeopledie
* * If the accused is killed, their true role is revealed to the rest of the players.
*/
/datum/mafia_controller/proc/lynch()
for(var/i in judgement_innocent_votes)
var/datum/mafia_role/role = i
send_message("<span class='green'>[role.body.real_name] voted innocent.</span>")
for(var/ii in judgement_abstain_votes)
var/datum/mafia_role/role = ii
send_message("<span class='comradio'>[role.body.real_name] abstained.</span>")
for(var/iii in judgement_guilty_votes)
var/datum/mafia_role/role = iii
send_message("<span class='red'>[role.body.real_name] voted guilty.</span>")
if(judgement_guilty_votes.len > judgement_innocent_votes.len) //strictly need majority guilty to lynch
send_message("<span class='red'><b>Guilty wins majority, [on_trial.body.real_name] has been lynched.</b></span>")
on_trial.kill(src, lynch = TRUE)
addtimer(CALLBACK(src, .proc/send_home, on_trial),judgement_lynch_period)
else
send_message("<span class='green'><b>Innocent wins majority, [on_trial.body.real_name] has been spared.</b></span>")
on_trial.body.forceMove(get_turf(on_trial.assigned_landmark))
on_trial = null
//day votes are already cleared, so this will skip the trial and check victory/lockdown/whatever else
next_phase_timer = addtimer(CALLBACK(src, .proc/check_trial, FALSE),judgement_lynch_period,TIMER_STOPPABLE)// small pause to see the guy dead, no verbosity since we already did this
/**
* Teenie helper proc to move players back to their home.
* Used in the above, but also used in the debug button "send all players home"
* Arguments:
* * role: mafia role that is getting sent back to the game.
*/
/datum/mafia_controller/proc/send_home(datum/mafia_role/role)
role.body.forceMove(get_turf(role.assigned_landmark))
/**
* Checks to see if a faction (or solo antagonist) has won.
*
* Calculates in this order:
* * counts up town, mafia, and solo
* * solos can count as town members for the purposes of mafia winning
* * sends the amount of living people to the solo antagonists, and see if they won OR block the victory of the teams
* * checks if solos won from above, then if town, then if mafia
* * starts the end of the game if a faction won
* * returns TRUE if someone won the game, halting other procs from continuing in the case of a victory
*/
/datum/mafia_controller/proc/check_victory()
//needed for achievements
var/list/total_town = list()
var/list/total_mafia = list()
var/alive_town = 0
var/alive_mafia = 0
var/list/solos_to_ask = list() //need to ask after because first round is counting team sizes
var/list/total_victors = list() //if this list gets filled with anyone, they win. list because side antags can with with people
var/blocked_victory = FALSE //if a solo antagonist is stopping the town or mafia from finishing the game.
///PHASE ONE: TALLY UP ALL NUMBERS OF PEOPLE STILL ALIVE
for(var/datum/mafia_role/R in all_roles)
switch(R.team)
if(MAFIA_TEAM_MAFIA)
total_mafia += R
if(R.game_status == MAFIA_ALIVE)
alive_mafia += R.vote_power
if(MAFIA_TEAM_TOWN)
total_town += R
if(R.game_status == MAFIA_ALIVE)
alive_town += R.vote_power
if(MAFIA_TEAM_SOLO)
if(R.game_status == MAFIA_ALIVE)
if(R.solo_counts_as_town)
alive_town += R.vote_power
solos_to_ask += R
///PHASE TWO: SEND STATS TO SOLO ANTAGS, SEE IF THEY WON OR TEAMS CANNOT WIN
for(var/datum/mafia_role/solo in solos_to_ask)
if(solo.check_total_victory(alive_town, alive_mafia))
total_victors += solo
if(solo.block_team_victory(alive_town, alive_mafia))
blocked_victory = TRUE
//solo victories!
var/solo_end = FALSE
for(var/datum/mafia_role/winner in total_victors)
send_message("<span class='big comradio'>!! [uppertext(winner.name)] VICTORY !!</span>")
// var/client/winner_client = GLOB.directory[winner.player_key]
// winner_client?.give_award(winner.winner_award, winner.body)
solo_end = TRUE
if(solo_end)
start_the_end()
return TRUE
if(blocked_victory)
return FALSE
if(alive_mafia == 0)
// for(var/datum/mafia_role/townie in total_town)
// var/client/townie_client = GLOB.directory[townie.player_key]
// townie_client?.give_award(townie.winner_award, townie.body)
start_the_end("<span class='big green'>!! TOWN VICTORY !!</span>")
return TRUE
else if(alive_mafia >= alive_town) //guess could change if town nightkill is added
start_the_end("<span class='big red'>!! MAFIA VICTORY !!</span>")
// for(var/datum/mafia_role/changeling in total_mafia)
// var/client/changeling_client = GLOB.directory[changeling.player_key]
// changeling_client?.give_award(changeling.winner_award, changeling.body)
return TRUE
/**
* The end of the game is in two procs, because we want a bit of time for players to see eachothers roles.
* Because of how check_victory works, the game is halted in other places by this point.
*
* What players do in this phase:
* * See everyone's role postgame
* * See who won the game
* Arguments:
* * message: string, if non-null it sends it to all players. used to announce team victories while solos are handled in check victory
*/
/datum/mafia_controller/proc/start_the_end(message)
SEND_SIGNAL(src,COMSIG_MAFIA_GAME_END)
if(message)
send_message(message)
for(var/datum/mafia_role/R in all_roles)
R.reveal_role(src)
phase = MAFIA_PHASE_VICTORY_LAP
next_phase_timer = addtimer(CALLBACK(src,.proc/end_game),victory_lap_period,TIMER_STOPPABLE)
/**
* Cleans up the game, resetting variables back to the beginning and removing the map with the generator.
*/
/datum/mafia_controller/proc/end_game()
map_deleter.generate() //remove the map, it will be loaded at the start of the next one
QDEL_LIST(all_roles)
current_setup_text = null
custom_setup = list()
turn = 0
votes = list()
//map gen does not deal with landmarks
QDEL_LIST(landmarks)
QDEL_NULL(town_center_landmark)
phase = MAFIA_PHASE_SETUP
/**
* After the voting and judgement phases, the game goes to night shutting the windows and beginning night with a proc.
*/
/datum/mafia_controller/proc/lockdown()
toggle_night_curtains(close=TRUE)
start_night()
/**
* Shuts poddoors attached to mafia.
* Arguments:
* * close: boolean, the state you want the curtains in.
*/
/datum/mafia_controller/proc/toggle_night_curtains(close)
for(var/obj/machinery/door/poddoor/D in GLOB.machines) //I really dislike pathing of these
if(D.id != "mafia") //so as to not trigger shutters on station, lol
continue
if(close)
INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/close)
else
INVOKE_ASYNC(D, /obj/machinery/door/poddoor.proc/open)
/**
* The actual start of night for players. Mostly info is given at the start of the night as the end of the night is when votes and actions are submitted and tried.
*
* What players do in this phase:
* * Mafia are told to begin voting on who to kill
* * Powers that are picked during the day announce themselves right now
*/
/datum/mafia_controller/proc/start_night()
phase = MAFIA_PHASE_NIGHT
send_message("<b>Night [turn] started! Lockdown will end in 45 seconds.</b>")
SEND_SIGNAL(src,COMSIG_MAFIA_SUNDOWN)
next_phase_timer = addtimer(CALLBACK(src, .proc/resolve_night),night_phase_period,TIMER_STOPPABLE)
SStgui.update_uis(src)
/**
* The end of the night, and a series of signals for the order of events on a night.
*
* Order of events, and what they mean:
* * Start of resolve (NIGHT_START) is for activating night abilities that MUST go first
* * Action phase (NIGHT_ACTION_PHASE) is for non-lethal day abilities
* * Mafia then tallies votes and kills the highest voted person (note: one random voter visits that person for the purposes of roleblocking)
* * Killing phase (NIGHT_KILL_PHASE) is for lethal night abilities
* * End of resolve (NIGHT_END) is for cleaning up abilities that went off and i guess doing some that must go last
* * Finally opens the curtains and calls the start of day phase, completing the cycle until check victory returns TRUE
*/
/datum/mafia_controller/proc/resolve_night()
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_START)
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_ACTION_PHASE)
//resolve mafia kill, todo unsnowflake this
var/datum/mafia_role/R = get_vote_winner("Mafia")
if(R)
var/datum/mafia_role/killer = get_random_voter("Mafia")
if(SEND_SIGNAL(killer,COMSIG_MAFIA_CAN_PERFORM_ACTION,src,"mafia killing",R) & MAFIA_PREVENT_ACTION)
send_message("<span class='danger'>[killer.body.real_name] was unable to attack [R.body.real_name] tonight!</span>",MAFIA_TEAM_MAFIA)
else
send_message("<span class='danger'>[killer.body.real_name] has attacked [R.body.real_name]!</span>",MAFIA_TEAM_MAFIA)
R.kill(src)
reset_votes("Mafia")
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_KILL_PHASE)
SEND_SIGNAL(src,COMSIG_MAFIA_NIGHT_END)
toggle_night_curtains(close=FALSE)
start_day()
SStgui.update_uis(src)
/**
* Proc that goes off when players vote for something with their mafia panel.
*
* If teams, it hides the tally overlay and only sends the vote messages to the team that is voting
* Arguments:
* * voter: the mafia role that is trying to vote for...
* * target: the mafia role that is getting voted for
* * vote_type: type of vote submitted (is this the day vote? is this the mafia night vote?)
* * teams: see mafia team defines for what to put in, makes the messages only send to a specific team (so mafia night votes only sending messages to mafia at night)
*/
/datum/mafia_controller/proc/vote_for(datum/mafia_role/voter,datum/mafia_role/target,vote_type, teams)
if(!votes[vote_type])
votes[vote_type] = list()
var/old_vote = votes[vote_type][voter]
if(old_vote && old_vote == target)
votes[vote_type] -= voter
else
votes[vote_type][voter] = target
if(old_vote && old_vote == target)
send_message("<span class='notice'>[voter.body.real_name] retracts their vote for [target.body.real_name]!</span>", team = teams)
else
send_message("<span class='notice'>[voter.body.real_name] voted for [target.body.real_name]!</span>",team = teams)
if(!teams)
target.body.update_icon() //Update the vote display if it's a public vote
var/datum/mafia_role/old = old_vote
if(old)
old.body.update_icon()
/**
* Clears out the votes of a certain type (day votes, mafia kill votes) while leaving others untouched
*/
/datum/mafia_controller/proc/reset_votes(vote_type)
var/list/bodies_to_update = list()
for(var/vote in votes[vote_type])
var/datum/mafia_role/R = votes[vote_type][vote]
bodies_to_update += R.body
votes[vote_type] = list()
for(var/mob/M in bodies_to_update)
M.update_icon()
/**
* Returns how many people voted for the role, in whatever vote (day vote, night kill vote)
* Arguments:
* * role: the mafia role the proc tries to get the amount of votes for
* * vote_type: the vote type (getting how many day votes were for the role, or mafia night votes for the role)
*/
/datum/mafia_controller/proc/get_vote_count(role,vote_type)
. = 0
for(var/v in votes[vote_type])
var/datum/mafia_role/votee = v
if(votes[vote_type][votee] == role)
. += votee.vote_power
/**
* Returns whichever role got the most votes, in whatever vote (day vote, night kill vote)
* returns null if no votes
* Arguments:
* * vote_type: the vote type (getting the role that got the most day votes, or the role that got the most mafia votes)
*/
/datum/mafia_controller/proc/get_vote_winner(vote_type)
var/list/tally = list()
for(var/votee in votes[vote_type])
if(!tally[votes[vote_type][votee]])
tally[votes[vote_type][votee]] = 1
else
tally[votes[vote_type][votee]] += 1
sortTim(tally,/proc/cmp_numeric_dsc,associative=TRUE)
return length(tally) ? tally[1] : null
/**
* Returns a random person who voted for whatever vote (day vote, night kill vote)
* Arguments:
* * vote_type: vote type (getting a random day voter, or mafia night voter)
*/
/datum/mafia_controller/proc/get_random_voter(vote_type)
if(length(votes[vote_type]))
return pick(votes[vote_type])
/**
* Adds mutable appearances to people who get publicly voted on (so not night votes) showing how many people are picking them
* Arguments:
* * source: the body of the role getting the overlays
* * overlay_list: signal var passing the overlay list of the mob
*/
/datum/mafia_controller/proc/display_votes(atom/source, list/overlay_list)
if(phase != MAFIA_PHASE_VOTING)
return
var/v = get_vote_count(player_role_lookup[source],"Day")
var/mutable_appearance/MA = mutable_appearance('icons/obj/mafia.dmi',"vote_[v > 12 ? "over_12" : v]")
overlay_list += MA
/**
* Called when the game is setting up, AFTER map is loaded but BEFORE the phase timers start. Creates and places each role's body and gives the correct player key
*
* Notably:
* * Toggles godmode so the mafia players cannot kill themselves
* * Adds signals for voting overlays, see display_votes proc
* * gives mafia panel
* * sends the greeting text (goals, role name, etc)
*/
/datum/mafia_controller/proc/create_bodies()
for(var/datum/mafia_role/role in all_roles)
var/mob/living/carbon/human/H = new(get_turf(role.assigned_landmark))
H.equipOutfit(player_outfit)
H.status_flags |= GODMODE
RegisterSignal(H,COMSIG_ATOM_UPDATE_OVERLAYS,.proc/display_votes)
var/datum/action/innate/mafia_panel/mafia_panel = new(null,src)
mafia_panel.Grant(H)
var/client/player_client = GLOB.directory[role.player_key]
if(player_client)
player_client.prefs.copy_to(H)
if(H.dna.species.outfit_important_for_life) //plasmamen
H.set_species(/datum/species/human)
role.body = H
player_role_lookup[H] = role
H.key = role.player_key
role.greet()
/datum/mafia_controller/ui_data(mob/user)
. = ..()
switch(phase)
if(MAFIA_PHASE_DAY,MAFIA_PHASE_VOTING,MAFIA_PHASE_JUDGEMENT)
.["phase"] = "Day [turn]"
if(MAFIA_PHASE_NIGHT)
.["phase"] = "Night [turn]"
else
.["phase"] = "No Game"
if(user.client?.holder)
.["admin_controls"] = TRUE //show admin buttons to start/setup/stop
if(phase == MAFIA_PHASE_JUDGEMENT)
.["judgement_phase"] = TRUE //show judgement section
else
.["judgement_phase"] = FALSE
var/datum/mafia_role/user_role = player_role_lookup[user]
if(user_role)
.["roleinfo"] = list("role" = user_role.name,"desc" = user_role.desc, "action_log" = user_role.role_notes, "hud_icon" = user_role.hud_icon, "revealed_icon" = user_role.revealed_icon)
var/actions = list()
for(var/action in user_role.actions)
if(user_role.validate_action_target(src,action,null))
actions += action
.["actions"] = actions
.["role_theme"] = user_role.special_theme
else
var/list/lobby_data = list()
for(var/key in GLOB.mafia_signup + GLOB.mafia_bad_signup)
var/list/lobby_member = list()
lobby_member["name"] = key
lobby_member["status"] = "Ready"
if(key in GLOB.mafia_bad_signup)
lobby_member["status"] = "Disconnected"
lobby_member["spectating"] = "Ghost"
if(key in spectators)
lobby_member["spectating"] = "Spectator"
lobby_data += list(lobby_member)
.["lobbydata"] = lobby_data
var/list/player_data = list()
for(var/datum/mafia_role/R in all_roles)
var/list/player_info = list()
var/list/actions = list()
if(user_role) //not observer
for(var/action in user_role.targeted_actions)
if(user_role.validate_action_target(src,action,R))
actions += action
//Awful snowflake, could use generalizing
if(phase == MAFIA_PHASE_VOTING)
player_info["votes"] = get_vote_count(R,"Day")
if(R.game_status == MAFIA_ALIVE && R != user_role)
actions += "Vote"
if(phase == MAFIA_PHASE_NIGHT && user_role.team == MAFIA_TEAM_MAFIA && R.game_status == MAFIA_ALIVE && R.team != MAFIA_TEAM_MAFIA)
actions += "Kill Vote"
player_info["name"] = R.body.real_name
player_info["ref"] = REF(R)
player_info["actions"] = actions
player_info["alive"] = R.game_status == MAFIA_ALIVE
player_data += list(player_info)
.["players"] = player_data
.["timeleft"] = next_phase_timer ? timeleft(next_phase_timer) : 0
//Not sure on this, should this info be visible
.["all_roles"] = current_setup_text
/datum/mafia_controller/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/mafia),
)
/datum/mafia_controller/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
. = ..()
if(.)
return
var/datum/mafia_role/user_role = player_role_lookup[usr]
//Admin actions
if(usr.client?.holder)
switch(action)
if("new_game")
end_game()
basic_setup()
if("nuke")
end_game()
qdel(src)
if("next_phase")
var/datum/timedevent/timer = SStimer.timer_id_dict[next_phase_timer]
if(!timer.spent)
var/datum/callback/tc = timer.callBack
deltimer(next_phase_timer)
tc.InvokeAsync()
return TRUE
if("players_home")
var/list/failed = list()
for(var/datum/mafia_role/player in all_roles)
if(!player.body)
failed += player
continue
player.body.forceMove(get_turf(player.assigned_landmark))
if(failed.len)
to_chat(usr, "List of players who no longer had a body (if you see this, the game is runtiming anyway so just hit \"New Game\" to end it)")
for(var/i in failed)
var/datum/mafia_role/fail = i
to_chat(usr, fail.player_key)
if("debug_setup")
var/list/debug_setup = list()
var/list/rolelist_dict = list()
var/done = FALSE
for(var/p in typesof(/datum/mafia_role))
var/datum/mafia_role/path = p
rolelist_dict[initial(path.name) + " ([uppertext(initial(path.team))])"] = path
rolelist_dict = list("CANCEL", "FINISH") + rolelist_dict
while(!done)
to_chat(usr, "You have a total player count of [assoc_value_sum(debug_setup)] in this setup.")
var/chosen_role_name = input(usr,"Select a role!","Custom Setup Creation",rolelist_dict[1]) as null|anything in rolelist_dict
if(chosen_role_name == "CANCEL")
return
if(chosen_role_name == "FINISH")
break
var/found_path = rolelist_dict[chosen_role_name]
var/role_count = input(usr,"How many? Zero to cancel.","Custom Setup Creation",0) as null|num
if(role_count > 0)
debug_setup[found_path] = role_count
custom_setup = debug_setup
if("cancel_setup")
custom_setup = list()
switch(action) //both living and dead
if("mf_lookup")
var/role_lookup = params["atype"]
var/datum/mafia_role/helper
for(var/datum/mafia_role/role in all_roles)
if(role_lookup == role.name)
helper = role
break
helper.show_help(usr)
if(!user_role)//just the dead
var/client/C = ui.user.client
switch(action)
if("mf_signup")
if(!SSticker.HasRoundStarted())
to_chat(usr, "<span class='warning'>Wait for the round to start.</span>")
return
if(GLOB.mafia_signup[C.ckey])
GLOB.mafia_signup -= C.ckey
to_chat(usr, "<span class='notice'>You unregister from Mafia.</span>")
return
else
GLOB.mafia_signup[C.ckey] = C
to_chat(usr, "<span class='notice'>You sign up for Mafia.</span>")
if(phase == MAFIA_PHASE_SETUP)
check_signups()
try_autostart()
if("mf_spectate")
if(C.ckey in spectators)
to_chat(usr, "<span class='notice'>You will no longer get messages from the game.</span>")
spectators -= C.ckey
else
to_chat(usr, "<span class='notice'>You will now get messages from the game.</span>")
spectators += C.ckey
if(user_role.game_status == MAFIA_DEAD)
return
//User actions (just living)
switch(action)
if("mf_action")
if(!user_role.actions.Find(params["atype"]))
return
user_role.handle_action(src,params["atype"],null)
return TRUE //vals for self-ui update
if("mf_targ_action")
var/datum/mafia_role/target = locate(params["target"]) in all_roles
if(!istype(target))
return
switch(params["atype"])
if("Vote")
if(phase != MAFIA_PHASE_VOTING)
return
vote_for(user_role,target,vote_type="Day")
if("Kill Vote")
if(phase != MAFIA_PHASE_NIGHT || user_role.team != MAFIA_TEAM_MAFIA)
return
vote_for(user_role,target,"Mafia", MAFIA_TEAM_MAFIA)
to_chat(user_role.body,"You will vote for [target.body.real_name] for tonights killing.")
else
if(!user_role.targeted_actions.Find(params["atype"]))
return
if(!user_role.validate_action_target(src,params["atype"],target))
return
user_role.handle_action(src,params["atype"],target)
return TRUE
if(user_role != on_trial)
switch(action)
if("vote_abstain")
if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_abstain_votes))
return
to_chat(user_role.body,"You have decided to abstain.")
judgement_innocent_votes -= user_role
judgement_guilty_votes -= user_role
judgement_abstain_votes += user_role
if("vote_innocent")
if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_innocent_votes))
return
to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as INNOCENT!")
judgement_abstain_votes -= user_role//no fakers, and...
judgement_guilty_votes -= user_role//no radical centrism
judgement_innocent_votes += user_role
if("vote_guilty")
if(phase != MAFIA_PHASE_JUDGEMENT || (user_role in judgement_guilty_votes))
return
to_chat(user_role.body,"Your vote on [on_trial.body.real_name] submitted as GUILTY!")
judgement_abstain_votes -= user_role//no fakers, and...
judgement_innocent_votes -= user_role//no radical centrism
judgement_guilty_votes += user_role
/datum/mafia_controller/ui_state(mob/user)
return GLOB.always_state
/datum/mafia_controller/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, null)
if(!ui)
ui = new(user, src, "MafiaPanel")
ui.set_autoupdate(FALSE)
ui.open()
/proc/assoc_value_sum(list/L)
. = 0
for(var/key in L)
. += L[key]
/**
* Returns a semirandom setup, with...
* Town, Two invest roles, one protect role, sometimes a misc role, and the rest assistants for town.
* Mafia, 2 normal mafia and one special.
* Neutral, two disruption roles, sometimes one is a killing.
*
* See _defines.dm in the mafia folder for a rundown on what these groups of roles include.
*/
/datum/mafia_controller/proc/generate_random_setup()
var/invests_left = 2
var/protects_left = 1
var/miscs_left = prob(35)
var/mafiareg_left = 2
var/mafiaspe_left = 1
var/killing_role = prob(50)
var/disruptors = killing_role ? 1 : 2 //still required to calculate overflow
var/overflow_left = max_player - (invests_left + protects_left + miscs_left + mafiareg_left + mafiaspe_left + killing_role + disruptors)
var/list/random_setup = list()
for(var/i in 1 to max_player) //should match the number of roles to add
if(overflow_left)
add_setup_role(random_setup, TOWN_OVERFLOW)
overflow_left--
else if(invests_left)
add_setup_role(random_setup, TOWN_INVEST)
invests_left--
else if(protects_left)
add_setup_role(random_setup, TOWN_PROTECT)
protects_left--
else if(miscs_left)
add_setup_role(random_setup, TOWN_MISC)
miscs_left--
else if(mafiareg_left)
add_setup_role(random_setup, MAFIA_REGULAR)
mafiareg_left--
else if(mafiaspe_left)
add_setup_role(random_setup, MAFIA_SPECIAL)
mafiaspe_left--
else if(killing_role)
add_setup_role(random_setup, NEUTRAL_KILL)
killing_role--
else
add_setup_role(random_setup, NEUTRAL_DISRUPT)
return random_setup
/**
* Helper proc that adds a random role of a type to a setup. if it doesn't exist in the setup, it adds the path to the list and otherwise bumps the path in the list up one
*/
/datum/mafia_controller/proc/add_setup_role(setup_list, wanted_role_type)
var/list/role_type_paths = list()
for(var/path in typesof(/datum/mafia_role))
var/datum/mafia_role/instance = path
if(initial(instance.role_type) == wanted_role_type)
role_type_paths += instance
var/mafia_path = pick(role_type_paths)
var/datum/mafia_role/mafia_path_type = mafia_path
var/found_role
for(var/searched_path in setup_list)
var/datum/mafia_role/searched_path_type = searched_path
if(initial(mafia_path_type.name) == initial(searched_path_type.name))
found_role = searched_path
break
if(found_role)
setup_list[found_role] += 1
return
setup_list[mafia_path] = 1
/**
* Called when enough players have signed up to fill a setup. DOESN'T NECESSARILY MEAN THE GAME WILL START.
*
* Checks for a custom setup, if so gets the required players from that and if not it sets the player requirement to required_player(max_player) and generates one IF basic setup starts a game.
* Checks if everyone signed up is an observer, and is still connected. If people aren't, they're removed from the list.
* If there aren't enough players post sanity, it aborts. otherwise, it selects enough people for the game and starts preparing the game for real.
*/
/datum/mafia_controller/proc/basic_setup()
var/req_players
var/list/setup = custom_setup
if(!setup.len)
req_players = required_player //max_player
else
req_players = assoc_value_sum(setup)
//final list for all the players who will be in this game
var/list/filtered_keys = list()
//cuts invalid players from signups (disconnected/not a ghost)
var/list/possible_keys = list()
for(var/key in GLOB.mafia_signup)
if(GLOB.directory[key])
var/client/C = GLOB.directory[key]
if(isobserver(C.mob))
possible_keys += key
continue
GLOB.mafia_signup -= key //not valid to play when we checked so remove them from signups
//if there were not enough players, don't start. we already trimmed the list to now hold only valid signups
if(length(possible_keys) < req_players)
return
else //hacky implementation of max players
req_players = clamp(length(possible_keys), 1, max_player)
//if there were too many players, still start but only make filtered keys as big as it needs to be (cut excess)
//also removes people who do get into final player list from the signup so they have to sign up again when game ends
for(var/i in 1 to req_players)
var/chosen_key = pick_n_take(possible_keys)
filtered_keys += chosen_key
GLOB.mafia_signup -= chosen_key
//small message about not getting into this game for clarity on why they didn't get in
for(var/unpicked in possible_keys)
var/client/unpicked_client = GLOB.directory[unpicked]
to_chat(unpicked_client, "<span class='danger'>Sorry, the starting mafia game has too many players and you were not picked.</span>")
to_chat(unpicked_client, "<span class='warning'>You're still signed up, getting messages from the current round, and have another chance to join when the one starting now finishes.</span>")
if(!setup.len) //don't actually have one yet, so generate a max player random setup. it's good to do this here instead of above so it doesn't generate one every time a game could possibly start.
setup = generate_random_setup()
prepare_game(setup,filtered_keys)
start_game()
/**
* Called when someone signs up, and sees if there are enough people in the signup list to begin.
*
* Only checks if everyone is actually valid to start (still connected and an observer) if there are enough players (basic_setup)
*/
/datum/mafia_controller/proc/try_autostart()
if(phase != MAFIA_PHASE_SETUP) // || !(GLOB.ghost_role_flags & GHOSTROLE_MINIGAME))
return
if(GLOB.mafia_signup.len >= max_player || GLOB.mafia_signup.len >= required_player|| custom_setup.len)//enough people to try and make something (or debug mode)
basic_setup()
/**
* Filters inactive player into a different list until they reconnect, and removes players who are no longer ghosts.
*
* If a disconnected player gets a non-ghost mob and reconnects, they will be first put back into mafia_signup then filtered by that.
*/
/datum/mafia_controller/proc/check_signups()
for(var/bad_key in GLOB.mafia_bad_signup)
if(GLOB.directory[bad_key])//they have reconnected if we can search their key and get a client
GLOB.mafia_bad_signup -= bad_key
GLOB.mafia_signup += bad_key
for(var/key in GLOB.mafia_signup)
var/client/C = GLOB.directory[key]
if(!C)//vice versa but in a variable we use later
GLOB.mafia_signup -= key
GLOB.mafia_bad_signup += key
if(!isobserver(C.mob))
//they are back to playing the game, remove them from the signups
GLOB.mafia_signup -= key
/datum/action/innate/mafia_panel
name = "Mafia Panel"
desc = "Use this to play."
icon_icon = 'icons/obj/mafia.dmi'
button_icon_state = "board"
var/datum/mafia_controller/parent
/datum/action/innate/mafia_panel/New(Target,mf)
. = ..()
parent = mf
/datum/action/innate/mafia_panel/Activate()
parent.ui_interact(owner)
/**
* Creates the global datum for playing mafia games, destroys the last if that's required and returns the new.
*/
/proc/create_mafia_game()
if(GLOB.mafia_game)
QDEL_NULL(GLOB.mafia_game)
var/datum/mafia_controller/MF = new()
return MF
+79
View File
@@ -0,0 +1,79 @@
/obj/effect/landmark/mafia_game_area //locations where mafia will be loaded by the datum
name = "Mafia Area Spawn"
var/game_id = "mafia"
/obj/effect/landmark/mafia
name = "Mafia Player Spawn"
var/game_id = "mafia"
/obj/effect/landmark/mafia/town_center
name = "Mafia Town Center"
//for ghosts/admins
/obj/mafia_game_board
name = "Mafia Game Board"
icon = 'icons/obj/mafia.dmi'
icon_state = "board"
anchored = TRUE
var/game_id = "mafia"
var/datum/mafia_controller/MF
/obj/mafia_game_board/attack_ghost(mob/user)
. = ..()
if(!MF)
MF = GLOB.mafia_game
if(!MF)
MF = create_mafia_game()
MF.ui_interact(user)
/area/mafia
name = "Mafia Minigame"
icon_state = "mafia"
dynamic_lighting = DYNAMIC_LIGHTING_DISABLED
requires_power = FALSE
has_gravity = STANDARD_GRAVITY
flags_1 = NONE
// block_suicide = TRUE
/datum/map_template/mafia
var/description = ""
/datum/map_template/mafia/summerball
name = "Summerball 2020"
description = "The original, the OG. The 2020 Summer ball was where mafia came from, with this map."
mappath = "_maps/map_files/Mafia/mafia_ball.dmm"
/datum/map_template/mafia/syndicate
name = "Syndicate Megastation"
description = "Yes, it's a very confusing day at the Megastation. Will the syndicate conflict resolution operatives succeed?"
mappath = "_maps/map_files/Mafia/mafia_syndie.dmm"
/datum/map_template/mafia/lavaland
name = "Lavaland Excursion"
description = "The station has no idea what's going down on lavaland right now, we got changelings... traitors, and worst of all... lawyers roleblocking you every night."
mappath = "_maps/map_files/Mafia/mafia_lavaland.dmm"
/datum/map_template/mafia/ufo
name = "Alien Mothership"
description = "The haunted ghost UFO tour has gone south and now it's up to our fine townies and scare seekers to kill the actual real alien changelings..."
mappath = "_maps/map_files/Mafia/mafia_ayylmao.dmm"
/datum/map_template/mafia/spider_clan
name = "Spider Clan Kidnapping"
description = "New and improved spider clan kidnappings are a lot less boring and have a lot more lynching. Damn westaboos!"
mappath = "_maps/map_files/Mafia/mafia_spiderclan.dmm"
/datum/map_template/mafia/snowy
name = "Snowdin"
description = "Based off of the icey moon map of the same name, the guy who reworked it pretty much did it for nothing since away missions are disabled but at least he'll get this...?"
mappath = "_maps/map_files/Mafia/mafia_snow.dmm"
/datum/map_template/mafia/gothic
name = "Vampire's Castle"
description = "Vampires and changelings clash to find out who's the superior bloodsucking monster in this creepy castle map."
mappath = "_maps/map_files/Mafia/mafia_gothic.dmm"
/datum/map_template/mafia/gothic
name = "Reebe"
description = "Trouble in Reebe station! Copypaste guranteed by ClockCo&trade;"
mappath = "_maps/map_files/Mafia/mafia_reebe.dmm"
+108
View File
@@ -0,0 +1,108 @@
//what people wear unrevealed
/datum/outfit/mafia
name = "Mafia Game Outfit"
uniform = /obj/item/clothing/under/color/grey
shoes = /obj/item/clothing/shoes/sneakers/black
//town
/datum/outfit/mafia/assistant
name = "Mafia Assistant"
uniform = /obj/item/clothing/under/color/rainbow
/datum/outfit/mafia/detective
name = "Mafia Detective"
uniform = /obj/item/clothing/under/rank/security/detective
// neck = /obj/item/clothing/neck/tie/detective
shoes = /obj/item/clothing/shoes/sneakers/brown
suit = /obj/item/clothing/suit/det_suit
gloves = /obj/item/clothing/gloves/color/black
head = /obj/item/clothing/head/fedora/det_hat
mask = /obj/item/clothing/mask/cigarette
/datum/outfit/mafia/psychologist
name = "Mafia Psychologist"
uniform = /obj/item/clothing/under/suit/black
shoes = /obj/item/clothing/shoes/laceup
/datum/outfit/mafia/md
name = "Mafia Medical Doctor"
uniform = /obj/item/clothing/under/rank/medical/doctor
shoes = /obj/item/clothing/shoes/sneakers/white
suit = /obj/item/clothing/suit/toggle/labcoat
/datum/outfit/mafia/chaplain
name = "Mafia Chaplain"
uniform = /obj/item/clothing/under/rank/civilian/chaplain
/datum/outfit/mafia/lawyer
name = "Mafia Lawyer"
uniform = /obj/item/clothing/under/rank/civilian/lawyer/bluesuit
suit = /obj/item/clothing/suit/toggle/lawyer
shoes = /obj/item/clothing/shoes/laceup
/datum/outfit/mafia/hop
name = "Mafia Head of Personnel"
uniform = /obj/item/clothing/under/rank/civilian/head_of_personnel
suit = /obj/item/clothing/suit/armor/vest/alt
shoes = /obj/item/clothing/shoes/sneakers/brown
head = /obj/item/clothing/head/hopcap
glasses = /obj/item/clothing/glasses/sunglasses
//mafia
/datum/outfit/mafia/changeling
name = "Mafia Changeling"
head = /obj/item/clothing/head/helmet/changeling
suit = /obj/item/clothing/suit/armor/changeling
//solo
/datum/outfit/mafia/fugitive
name = "Mafia Fugitive"
uniform = /obj/item/clothing/under/rank/prisoner
shoes = /obj/item/clothing/shoes/sneakers/orange
/datum/outfit/mafia/obsessed
name = "Mafia Obsessed"
uniform = /obj/item/clothing/under/misc/overalls
shoes = /obj/item/clothing/shoes/sneakers/white
gloves = /obj/item/clothing/gloves/color/latex
mask = /obj/item/clothing/mask/surgical
suit = /obj/item/clothing/suit/apron
/datum/outfit/mafia/obsessed/post_equip(mob/living/carbon/human/H)
for(var/obj/item/carried_item in H.get_equipped_items(TRUE))
carried_item.add_mob_blood(H)//Oh yes, there will be blood...
H.regenerate_icons()
/datum/outfit/mafia/clown
name = "Mafia Clown"
uniform = /obj/item/clothing/under/rank/civilian/clown
shoes = /obj/item/clothing/shoes/clown_shoes
mask = /obj/item/clothing/mask/gas/clown_hat
/datum/outfit/mafia/traitor
name = "Mafia Traitor"
mask = /obj/item/clothing/mask/gas/syndicate
uniform = /obj/item/clothing/under/syndicate/tacticool
shoes = /obj/item/clothing/shoes/jackboots
/datum/outfit/mafia/nightmare
name = "Mafia Nightmare"
uniform = null
shoes = null
+705
View File
@@ -0,0 +1,705 @@
/datum/mafia_role
var/name = "Assistant"
var/desc = "You are a crewmember without any special abilities."
var/win_condition = "kill all mafia and solo killing roles."
var/team = MAFIA_TEAM_TOWN
///how the random setup chooses which roles get put in
var/role_type = TOWN_OVERFLOW
var/player_key
var/mob/living/carbon/human/body
var/obj/effect/landmark/mafia/assigned_landmark
///how many votes submitted when you vote.
var/vote_power = 1
var/detect_immune = FALSE
var/revealed = FALSE
var/datum/outfit/revealed_outfit = /datum/outfit/mafia/assistant //the assistants need a special path to call out they were in fact assistant, everything else can just use job equipment
//action = uses
var/list/actions = list()
var/list/targeted_actions = list()
//what the role gets when it wins a game
// var/winner_award = /datum/award/achievement/mafia/assistant
//so mafia have to also kill them to have a majority
var/solo_counts_as_town = FALSE //(don't set this for town)
var/game_status = MAFIA_ALIVE
///icon state in the mafia dmi of the hud of the role, used in the mafia ui
var/hud_icon = "hudassistant"
///icon state in the mafia dmi of the hud of the role, used in the mafia ui
var/revealed_icon = "assistant"
///set this to something cool for antagonists and their window will look different
var/special_theme
var/list/role_notes = list()
/datum/mafia_role/New(datum/mafia_controller/game)
. = ..()
/datum/mafia_role/proc/kill(datum/mafia_controller/game,lynch=FALSE)
if(SEND_SIGNAL(src,COMSIG_MAFIA_ON_KILL,game,lynch) & MAFIA_PREVENT_KILL)
return FALSE
game_status = MAFIA_DEAD
body.death()
if(lynch)
reveal_role(game, verbose = TRUE)
if(!(player_key in game.spectators)) //people who played will want to see the end of the game more often than not
game.spectators += player_key
return TRUE
/datum/mafia_role/Destroy(force, ...)
QDEL_NULL(body)
. = ..()
/datum/mafia_role/proc/greet()
SEND_SOUND(body, 'sound/ambience/ambifailure.ogg')
to_chat(body,"<span class='danger'>You are the [name].</span>")
to_chat(body,"<span class='danger'>[desc]</span>")
switch(team)
if(MAFIA_TEAM_MAFIA)
to_chat(body,"<span class='danger'>You and your co-conspirators win if you outnumber crewmembers.</span>")
if(MAFIA_TEAM_TOWN)
to_chat(body,"<span class='danger'>You are a crewmember. Find out and lynch the changelings!</span>")
if(MAFIA_TEAM_SOLO)
to_chat(body,"<span class='danger'>You are not aligned to town or mafia. Accomplish your own objectives!</span>")
to_chat(body, "<b>Be sure to read <a href=\"https://tgstation13.org/wiki/Mafia\">the wiki page</a> to learn more, if you have no idea what's going on.</b>")
/datum/mafia_role/proc/reveal_role(datum/mafia_controller/game, verbose = FALSE)
if(revealed)
return
if(verbose)
game.send_message("<span class='big bold notice'>It is revealed that the true role of [body] [game_status == MAFIA_ALIVE ? "is" : "was"] [name]!</span>")
var/list/oldoutfit = body.get_equipped_items()
for(var/thing in oldoutfit)
qdel(thing)
special_reveal_equip(game)
body.equipOutfit(revealed_outfit)
revealed = TRUE
/datum/mafia_role/proc/special_reveal_equip(datum/mafia_controller/game)
return
/datum/mafia_role/proc/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
return
/datum/mafia_role/proc/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,action,target) & MAFIA_PREVENT_ACTION)
return FALSE
return TRUE
/datum/mafia_role/proc/add_note(note)
role_notes += note
/datum/mafia_role/proc/check_total_victory(alive_town, alive_mafia) //solo antags can win... solo.
return FALSE
/datum/mafia_role/proc/block_team_victory(alive_town, alive_mafia) //solo antags can also block team wins.
return FALSE
/datum/mafia_role/proc/show_help(clueless)
var/list/result = list()
var/team_desc = ""
var/team_span = ""
var/the = TRUE
switch(team)
if(MAFIA_TEAM_TOWN)
team_desc = "Town"
team_span = "nicegreen"
if(MAFIA_TEAM_MAFIA)
team_desc = "Mafia"
team_span = "red"
if(MAFIA_TEAM_SOLO)
team_desc = "Nobody"
team_span = "comradio"
the = FALSE
result += "<span class='notice'>The <span class='bold'>[name]</span> is aligned with [the ? "the " : ""]<span class='[team_span]'>[team_desc]</span></span>"
result += "<span class='bold notice'>\"[desc]\"</span>"
result += "<span class='notice'>[name] wins when they [win_condition]</span>"
to_chat(clueless, result.Join("</br>"))
/datum/mafia_role/detective
name = "Detective"
desc = "You can investigate a single person each night to learn their team."
revealed_outfit = /datum/outfit/mafia/detective
role_type = TOWN_INVEST
// winner_award = /datum/award/achievement/mafia/detective
hud_icon = "huddetective"
revealed_icon = "detective"
targeted_actions = list("Investigate")
var/datum/mafia_role/current_investigation
/datum/mafia_role/detective/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate)
/datum/mafia_role/detective/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
. = ..()
if(!.)
return
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
/datum/mafia_role/detective/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
if(!target || target.game_status != MAFIA_ALIVE)
to_chat(body,"<span class='warning'>You can only investigate alive people.</span>")
return
to_chat(body,"<span class='warning'>You will investigate [target.body.real_name] tonight.</span>")
current_investigation = target
/datum/mafia_role/detective/proc/investigate(datum/mafia_controller/game)
var/datum/mafia_role/target = current_investigation
if(target)
if(target.detect_immune)
to_chat(body,"<span class='warning'>Your investigations reveal that [target.body.real_name] is a true member of the station.</span>")
add_note("N[game.turn] - [target.body.real_name] - Town")
else
var/team_text
var/fluff
switch(target.team)
if(MAFIA_TEAM_TOWN)
team_text = "Town"
fluff = "a true member of the station."
if(MAFIA_TEAM_MAFIA)
team_text = "Mafia"
fluff = "an unfeeling, hideous changeling!"
if(MAFIA_TEAM_SOLO)
team_text = "Solo"
fluff = "a rogue, with their own objectives..."
to_chat(body,"<span class='warning'>Your investigations reveal that [target.body.real_name] is [fluff]</span>")
add_note("N[game.turn] - [target.body.real_name] - [team_text]")
current_investigation = null
/datum/mafia_role/psychologist
name = "Psychologist"
desc = "You can visit someone ONCE PER GAME to reveal their true role in the morning!"
revealed_outfit = /datum/outfit/mafia/psychologist
role_type = TOWN_INVEST
// winner_award = /datum/award/achievement/mafia/psychologist
hud_icon = "hudpsychologist"
revealed_icon = "psychologist"
targeted_actions = list("Reveal")
var/datum/mafia_role/current_target
var/can_use = TRUE
/datum/mafia_role/psychologist/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/therapy_reveal)
/datum/mafia_role/psychologist/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(!. || !can_use || game.phase == MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target.revealed || target == src)
return FALSE
/datum/mafia_role/psychologist/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
to_chat(body,"<span class='warning'>You will reveal [target.body.real_name] tonight.</span>")
current_target = target
/datum/mafia_role/psychologist/proc/therapy_reveal(datum/mafia_controller/game)
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"reveal",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by a lawyer.
current_target = null
if(current_target)
add_note("N[game.turn] - [current_target.body.real_name] - Revealed true identity")
to_chat(body,"<span class='warning'>You have revealed the true nature of the [current_target]!</span>")
current_target.reveal_role(game, verbose = TRUE)
current_target = null
can_use = FALSE
/datum/mafia_role/chaplain
name = "Chaplain"
desc = "You can communicate with spirits of the dead each night to discover dead crewmember roles."
revealed_outfit = /datum/outfit/mafia/chaplain
role_type = TOWN_INVEST
hud_icon = "hudchaplain"
revealed_icon = "chaplain"
// winner_award = /datum/award/achievement/mafia/chaplain
targeted_actions = list("Pray")
var/current_target
/datum/mafia_role/chaplain/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/commune)
/datum/mafia_role/chaplain/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(!.)
return
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_DEAD && target != src && !target.revealed
/datum/mafia_role/chaplain/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
to_chat(body,"<span class='warning'>You will commune with the spirit of [target.body.real_name] tonight.</span>")
current_target = target
/datum/mafia_role/chaplain/proc/commune(datum/mafia_controller/game)
var/datum/mafia_role/target = current_target
if(target)
to_chat(body,"<span class='warning'>You invoke spirit of [target.body.real_name] and learn their role was <b>[target.name]<b>.</span>")
add_note("N[game.turn] - [target.body.real_name] - [target.name]")
current_target = null
/datum/mafia_role/md
name = "Medical Doctor"
desc = "You can protect a single person each night from killing."
revealed_outfit = /datum/outfit/mafia/md // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
role_type = TOWN_PROTECT
hud_icon = "hudmedicaldoctor"
revealed_icon = "medicaldoctor"
// winner_award = /datum/award/achievement/mafia/md
targeted_actions = list("Protect")
var/datum/mafia_role/current_protected
/datum/mafia_role/md/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/protect)
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/end_protection)
/datum/mafia_role/md/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
. = ..()
if(!.)
return
if(target.name == "Head of Personnel" && target.revealed)
return FALSE
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
/datum/mafia_role/md/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
if(!target || target.game_status != MAFIA_ALIVE)
to_chat(body,"<span class='warning'>You can only protect alive people.</span>")
return
to_chat(body,"<span class='warning'>You will protect [target.body.real_name] tonight.</span>")
current_protected = target
/datum/mafia_role/md/proc/protect(datum/mafia_controller/game)
if(current_protected)
RegisterSignal(current_protected,COMSIG_MAFIA_ON_KILL,.proc/prevent_kill)
add_note("N[game.turn] - Protected [current_protected.body.real_name]")
/datum/mafia_role/md/proc/prevent_kill(datum/source)
to_chat(body,"<span class='warning'>The person you protected tonight was attacked!</span>")
to_chat(current_protected.body,"<span class='userdanger'>You were attacked last night, but someone nursed you back to life!</span>")
return MAFIA_PREVENT_KILL
/datum/mafia_role/md/proc/end_protection(datum/mafia_controller/game)
if(current_protected)
UnregisterSignal(current_protected,COMSIG_MAFIA_ON_KILL)
current_protected = null
/datum/mafia_role/lawyer
name = "Lawyer"
desc = "You can choose a person during the day to provide extensive legal advice to during the night, preventing night actions."
revealed_outfit = /datum/outfit/mafia/lawyer
role_type = TOWN_PROTECT
hud_icon = "hudlawyer"
revealed_icon = "lawyer"
// winner_award = /datum/award/achievement/mafia/lawyer
targeted_actions = list("Advise")
var/datum/mafia_role/current_target
/datum/mafia_role/lawyer/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/roleblock_text)
RegisterSignal(game,COMSIG_MAFIA_NIGHT_START,.proc/try_to_roleblock)
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/release)
/datum/mafia_role/lawyer/proc/roleblock_text(datum/mafia_controller/game)
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"roleblock",current_target) & MAFIA_PREVENT_ACTION || game_status != MAFIA_ALIVE) //Got lynched or roleblocked by another lawyer.
current_target = null
if(current_target)
to_chat(current_target.body,"<span class='big bold red'>YOU HAVE BEEN BLOCKED! YOU CANNOT PERFORM ANY ACTIONS TONIGHT.</span>")
add_note("N[game.turn] - [current_target.body.real_name] - Blocked")
/datum/mafia_role/lawyer/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(!.)
return FALSE
if(game.phase == MAFIA_PHASE_NIGHT)
return FALSE
if(target.game_status != MAFIA_ALIVE)
return FALSE
/datum/mafia_role/lawyer/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(target == current_target)
current_target = null
to_chat(body,"<span class='warning'>You have decided against blocking anyone tonight.</span>")
else
current_target = target
to_chat(body,"<span class='warning'>You will block [target.body.real_name] tonight.</span>")
/datum/mafia_role/lawyer/proc/try_to_roleblock(datum/mafia_controller/game)
if(current_target)
RegisterSignal(current_target,COMSIG_MAFIA_CAN_PERFORM_ACTION, .proc/prevent_action)
/datum/mafia_role/lawyer/proc/release(datum/mafia_controller/game)
. = ..()
if(current_target)
UnregisterSignal(current_target, COMSIG_MAFIA_CAN_PERFORM_ACTION)
current_target = null
/datum/mafia_role/lawyer/proc/prevent_action(datum/source)
if(game_status == MAFIA_ALIVE) //in case we got killed while imprisoning sk - bad luck edge
return MAFIA_PREVENT_ACTION
/datum/mafia_role/hop
name = "Head of Personnel"
desc = "You can reveal yourself once per game, tripling your vote power but becoming unable to be protected!"
revealed_outfit = /datum/outfit/mafia/hop
role_type = TOWN_MISC
hud_icon = "hudheadofpersonnel"
revealed_icon = "headofpersonnel"
// winner_award = /datum/award/achievement/mafia/hop
targeted_actions = list("Reveal")
/datum/mafia_role/hop/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(!. || game.phase == MAFIA_PHASE_NIGHT || game.turn == 1 || target.game_status != MAFIA_ALIVE || target != src || revealed)
return FALSE
/datum/mafia_role/hop/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
reveal_role(game, TRUE)
vote_power = 2
///MAFIA ROLES/// only one until i rework this to allow more, they're the "anti-town" working to kill off townies to win
/datum/mafia_role/mafia
name = "Changeling"
desc = "You're a member of the changeling hive. Use ':j' talk prefix to talk to your fellow lings."
team = MAFIA_TEAM_MAFIA
role_type = MAFIA_REGULAR
hud_icon = "hudchangeling"
revealed_icon = "changeling"
// winner_award = /datum/award/achievement/mafia/changeling
revealed_outfit = /datum/outfit/mafia/changeling
special_theme = "syndicate"
win_condition = "become majority over the town and no solo killing role can stop them."
/datum/mafia_role/mafia/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/mafia_text)
/datum/mafia_role/mafia/proc/mafia_text(datum/mafia_controller/source)
to_chat(body,"<b>Vote for who to kill tonight. The killer will be chosen randomly from voters.</b>")
//better detective for mafia
/datum/mafia_role/mafia/thoughtfeeder
name = "Thoughtfeeder"
desc = "You're a changeling variant that feeds on the memories of others. Use ':j' talk prefix to talk to your fellow lings, and visit people at night to learn their role."
role_type = MAFIA_SPECIAL
hud_icon = "hudthoughtfeeder"
revealed_icon = "thoughtfeeder"
// winner_award = /datum/award/achievement/mafia/thoughtfeeder
targeted_actions = list("Learn Role")
var/datum/mafia_role/current_investigation
/datum/mafia_role/mafia/thoughtfeeder/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_NIGHT_ACTION_PHASE,.proc/investigate)
/datum/mafia_role/mafia/thoughtfeeder/validate_action_target(datum/mafia_controller/game,action,datum/mafia_role/target)
. = ..()
if(!.)
return
return game.phase == MAFIA_PHASE_NIGHT && target.game_status == MAFIA_ALIVE && target != src
/datum/mafia_role/mafia/thoughtfeeder/handle_action(datum/mafia_controller/game,action,datum/mafia_role/target)
to_chat(body,"<span class='warning'>You will feast on the memories of [target.body.real_name] tonight.</span>")
current_investigation = target
/datum/mafia_role/mafia/thoughtfeeder/proc/investigate(datum/mafia_controller/game)
var/datum/mafia_role/target = current_investigation
current_investigation = null
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,game,"thoughtfeed",target) & MAFIA_PREVENT_ACTION)
to_chat(body,"<span class='warning'>You were unable to investigate [target.body.real_name].</span>")
add_note("N[game.turn] - [target.body.real_name] - Unable to investigate")
return
if(target)
if(target.detect_immune)
to_chat(body,"<span class='warning'>[target.body.real_name]'s memories reveal that they are the Assistant.</span>")
add_note("N[game.turn] - [target.body.real_name] - Assistant")
else
to_chat(body,"<span class='warning'>[target.body.real_name]'s memories reveal that they are the [target.name].</span>")
add_note("N[game.turn] - [target.body.real_name] - [target.name]")
///SOLO ROLES/// they range from anomalous factors to deranged killers that try to win alone.
/datum/mafia_role/traitor
name = "Traitor"
desc = "You're a solo traitor. You are immune to night kills, can kill every night and you win by outnumbering everyone else."
win_condition = "kill everyone."
team = MAFIA_TEAM_SOLO
role_type = NEUTRAL_KILL
// winner_award = /datum/award/achievement/mafia/traitor
targeted_actions = list("Night Kill")
revealed_outfit = /datum/outfit/mafia/traitor
hud_icon = "hudtraitor"
revealed_icon = "traitor"
special_theme = "neutral"
var/datum/mafia_role/current_victim
/datum/mafia_role/traitor/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/nightkill_immunity)
RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/try_to_kill)
/datum/mafia_role/traitor/check_total_victory(alive_town, alive_mafia) //serial killers just want teams dead
return alive_town + alive_mafia <= 1
/datum/mafia_role/traitor/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead
return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point)
/datum/mafia_role/traitor/proc/nightkill_immunity(datum/source,datum/mafia_controller/game,lynch)
if(game.phase == MAFIA_PHASE_NIGHT && !lynch)
to_chat(body,"<span class='userdanger'>You were attacked, but they'll have to try harder than that to put you down.</span>")
return MAFIA_PREVENT_KILL
/datum/mafia_role/traitor/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(!.)
return FALSE
if(game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE || target == src)
return FALSE
/datum/mafia_role/traitor/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
current_victim = target
to_chat(body,"<span class='warning'>You will attempt to kill [target.body.real_name] tonight.</span>")
/datum/mafia_role/traitor/proc/try_to_kill(datum/mafia_controller/source)
var/datum/mafia_role/target = current_victim
current_victim = null
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"traitor kill",target) & MAFIA_PREVENT_ACTION)
return
if(game_status == MAFIA_ALIVE && target && target.game_status == MAFIA_ALIVE)
if(!target.kill(source))
to_chat(body,"<span class='danger'>Your attempt at killing [target.body] was prevented!</span>")
/datum/mafia_role/nightmare
name = "Nightmare"
desc = "You're a solo monster that cannot be detected by detective roles. You can flicker lights of another room each night. You can instead decide to hunt, killing everyone in a flickering room. Kill everyone to win."
win_condition = "kill everyone."
revealed_outfit = /datum/outfit/mafia/nightmare
detect_immune = TRUE
team = MAFIA_TEAM_SOLO
role_type = NEUTRAL_KILL
special_theme = "neutral"
hud_icon = "hudnightmare"
revealed_icon = "nightmare"
// winner_award = /datum/award/achievement/mafia/nightmare
targeted_actions = list("Flicker", "Hunt")
var/list/flickering = list()
var/datum/mafia_role/flicker_target
/datum/mafia_role/nightmare/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_NIGHT_KILL_PHASE,.proc/flicker_or_hunt)
/datum/mafia_role/nightmare/check_total_victory(alive_town, alive_mafia) //nightmares just want teams dead
return alive_town + alive_mafia <= 1
/datum/mafia_role/nightmare/block_team_victory(alive_town, alive_mafia) //no team can win until they're dead
return TRUE //while alive, town AND mafia cannot win (though since mafia know who is who it's pretty easy to win from that point)
/datum/mafia_role/nightmare/special_reveal_equip()
body.underwear = "Nude"
body.undershirt = "Nude"
body.socks = "Nude"
body.set_species(/datum/species/shadow)
body.update_body()
/datum/mafia_role/nightmare/validate_action_target(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(!. || game.phase != MAFIA_PHASE_NIGHT || target.game_status != MAFIA_ALIVE)
return FALSE
if(action == "Flicker")
return target != src && !(target in flickering)
return target == src
/datum/mafia_role/nightmare/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(target == flicker_target)
to_chat(body,"<span class='warning'>You will do nothing tonight.</span>")
flicker_target = null
flicker_target = target
if(action == "Flicker")
to_chat(body,"<span class='warning'>You will attempt to flicker [target.body.real_name]'s room tonight.</span>")
else
to_chat(body,"<span class='danger'>You will hunt everyone in a flickering room down tonight.</span>")
/datum/mafia_role/nightmare/proc/flicker_or_hunt(datum/mafia_controller/source)
if(game_status != MAFIA_ALIVE || !flicker_target)
return
if(SEND_SIGNAL(src,COMSIG_MAFIA_CAN_PERFORM_ACTION,source,"nightmare actions",flicker_target) & MAFIA_PREVENT_ACTION)
to_chat(flicker_target.body, "<span class='warning'>Your actions were prevented!</span>")
return
var/datum/mafia_role/target = flicker_target
flicker_target = null
if(target != src) //flicker instead of hunt
to_chat(target.body, "<span class='userdanger'>The lights begin to flicker and dim. You're in danger.</span>")
flickering += target
return
for(var/r in flickering)
var/datum/mafia_role/role = r
if(role && role.game_status == MAFIA_ALIVE)
to_chat(role.body, "<span class='userdanger'>A shadowy monster appears out of the darkness!</span>")
role.kill(source)
flickering -= role
//just helps read better
#define FUGITIVE_NOT_PRESERVING 0//will not become night immune tonight
#define FUGITIVE_WILL_PRESERVE 1 //will become night immune tonight
/datum/mafia_role/fugitive
name = "Fugitive"
desc = "You're on the run. You can become immune to night kills exactly twice, and you win by surviving to the end of the game with anyone."
win_condition = "survive to the end of the game, with anyone"
solo_counts_as_town = TRUE //should not count towards mafia victory, they should have the option to work with town
revealed_outfit = /datum/outfit/mafia/fugitive
team = MAFIA_TEAM_SOLO
role_type = NEUTRAL_DISRUPT
special_theme = "neutral"
hud_icon = "hudfugitive"
revealed_icon = "fugitive"
// winner_award = /datum/award/achievement/mafia/fugitive
actions = list("Self Preservation")
var/charges = 2
var/protection_status = FUGITIVE_NOT_PRESERVING
/datum/mafia_role/fugitive/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/night_start)
RegisterSignal(game,COMSIG_MAFIA_NIGHT_END,.proc/night_end)
RegisterSignal(game,COMSIG_MAFIA_GAME_END,.proc/survived)
/datum/mafia_role/fugitive/handle_action(datum/mafia_controller/game, action, datum/mafia_role/target)
. = ..()
if(!charges)
to_chat(body,"<span class='danger'>You're out of supplies and cannot protect yourself anymore.</span>")
return
if(game.phase == MAFIA_PHASE_NIGHT)
to_chat(body,"<span class='danger'>You don't have time to prepare, night has already arrived.</span>")
return
if(protection_status == FUGITIVE_WILL_PRESERVE)
to_chat(body,"<span class='danger'>You decide to not prepare tonight.</span>")
else
to_chat(body,"<span class='danger'>You decide to prepare for a horrible night.</span>")
protection_status = !protection_status
/datum/mafia_role/fugitive/proc/night_start(datum/mafia_controller/game)
if(protection_status == FUGITIVE_WILL_PRESERVE)
to_chat(body,"<span class='danger'>Your preparations are complete. Nothing could kill you tonight!</span>")
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prevent_death)
/datum/mafia_role/fugitive/proc/night_end(datum/mafia_controller/game)
if(protection_status == FUGITIVE_WILL_PRESERVE)
charges--
UnregisterSignal(src,COMSIG_MAFIA_ON_KILL)
to_chat(body,"<span class='danger'>You are no longer protected. You have [charges] use[charges == 1 ? "" : "s"] left of your power.</span>")
protection_status = FUGITIVE_NOT_PRESERVING
/datum/mafia_role/fugitive/proc/prevent_death(datum/mafia_controller/game)
to_chat(body,"<span class='userdanger'>You were attacked! Luckily, you were ready for this!</span>")
return MAFIA_PREVENT_KILL
/datum/mafia_role/fugitive/proc/survived(datum/mafia_controller/game)
if(game_status == MAFIA_ALIVE)
// var/client/winner_client = GLOB.directory[player_key]
// winner_client?.give_award(winner_award, body)
game.send_message("<span class='big comradio'>!! FUGITIVE VICTORY !!</span>")
#undef FUGITIVE_NOT_PRESERVING
#undef FUGITIVE_WILL_PRESERVE
/datum/mafia_role/obsessed
name = "Obsessed"
desc = "You're completely lost in your own mind. You win by lynching your obsession before you get killed in this mess. Obsession assigned on the first night!"
win_condition = "lynch their obsession."
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
team = MAFIA_TEAM_SOLO
role_type = NEUTRAL_DISRUPT
special_theme = "neutral"
hud_icon = "hudobsessed"
revealed_icon = "obsessed"
// winner_award = /datum/award/achievement/mafia/obsessed
revealed_outfit = /datum/outfit/mafia/obsessed // /mafia <- outfit must be readded (just make a new mafia outfits file for all of these)
solo_counts_as_town = TRUE //after winning or whatever, can side with whoever. they've already done their objective!
var/datum/mafia_role/obsession
var/lynched_target = FALSE
/datum/mafia_role/obsessed/New(datum/mafia_controller/game) //note: obsession is always a townie
. = ..()
RegisterSignal(game,COMSIG_MAFIA_SUNDOWN,.proc/find_obsession)
/datum/mafia_role/obsessed/proc/find_obsession(datum/mafia_controller/game)
var/list/all_roles_shuffle = shuffle(game.all_roles)
for(var/role in all_roles_shuffle)
var/datum/mafia_role/possible = role
if(possible.team == MAFIA_TEAM_TOWN && possible.game_status != MAFIA_DEAD)
obsession = possible
break
if(!obsession)
obsession = pick(all_roles_shuffle) //okay no town just pick anyone here
//if you still don't have an obsession you're playing a single player game like i can't help your dumb ass
to_chat(body, "<span class='userdanger'>Your obsession is [obsession.body.real_name]! Get them lynched to win!</span>")
add_note("N[game.turn] - I vowed to watch my obsession, [obsession.body.real_name], hang!") //it'll always be N1 but whatever
RegisterSignal(obsession,COMSIG_MAFIA_ON_KILL,.proc/check_victory)
UnregisterSignal(game,COMSIG_MAFIA_SUNDOWN)
/datum/mafia_role/obsessed/proc/check_victory(datum/source,datum/mafia_controller/game,lynch)
UnregisterSignal(source,COMSIG_MAFIA_ON_KILL)
if(game_status == MAFIA_DEAD)
return
if(lynch)
game.send_message("<span class='big comradio'>!! OBSESSED VICTORY !!</span>")
// var/client/winner_client = GLOB.directory[player_key]
// winner_client?.give_award(winner_award, body)
reveal_role(game, FALSE)
else
to_chat(body, "<span class='userdanger'>You have failed your objective to lynch [obsession.body]!</span>")
/datum/mafia_role/clown
name = "Clown"
desc = "If you are lynched you take down one of your voters (guilty or abstain) with you and win. HONK!"
win_condition = "get themselves lynched!"
revealed_outfit = /datum/outfit/mafia/clown
solo_counts_as_town = TRUE
team = MAFIA_TEAM_SOLO
role_type = NEUTRAL_DISRUPT
special_theme = "neutral"
hud_icon = "hudclown"
revealed_icon = "clown"
// winner_award = /datum/award/achievement/mafia/clown
/datum/mafia_role/clown/New(datum/mafia_controller/game)
. = ..()
RegisterSignal(src,COMSIG_MAFIA_ON_KILL,.proc/prank)
/datum/mafia_role/clown/proc/prank(datum/source,datum/mafia_controller/game,lynch)
if(lynch)
var/datum/mafia_role/victim = pick(game.judgement_guilty_votes + game.judgement_abstain_votes)
game.send_message("<span class='big clown'>[body.real_name] WAS A CLOWN! HONK! They take down [victim.body.real_name] with their last prank.</span>")
game.send_message("<span class='big clown'>!! CLOWN VICTORY !!</span>")
// var/client/winner_client = GLOB.directory[player_key]
// winner_client?.give_award(winner_award, body)
victim.kill(game,FALSE)
+1 -1
View File
@@ -20,7 +20,7 @@
var/map_file = "BoxStation.dmm"
var/traits = null
var/space_ruin_levels = 2
var/space_ruin_levels = 4
var/space_empty_levels = 1
var/station_ruin_budget = -1 // can be set to manually override the station ruins budget on maps that don't support station ruins, stopping the error from being unable to place the ruins.
@@ -183,9 +183,40 @@
attack_verb = list("stabbed", "diced", "sliced", "cleaved", "chopped", "lacerated", "cut", "jabbed", "punctured")
icon_state = "crusher-glaive"
item_state = "crusher0-glaive"
item_flags = ITEM_CAN_PARRY
block_parry_data = /datum/block_parry_data/crusherglaive
//ideas: altclick that lets you pummel people with the handguard/handle?
//parrying functionality?
/datum/block_parry_data/crusherglaive // small perfect window, active for a fair while, time it right or use the Forbidden Technique
parry_time_windup = 0
parry_time_active = 8
parry_time_spindown = 0
parry_time_perfect = 1
parry_time_perfect_leeway = 2
parry_imperfect_falloff_percent = 20
parry_efficiency_to_counterattack = 100 // perfect parry or you're cringe
parry_failed_stagger_duration = 1.5 SECONDS // a good time to reconsider your actions...
parry_failed_clickcd_duration = 1.5 SECONDS // or your failures
/obj/item/kinetic_crusher/glaive/on_active_parry(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/block_return, parry_efficiency, parry_time) // if you're dumb enough to go for a parry...
var/turf/proj_turf = owner.loc // destabilizer bolt, ignoring cooldown
if(!isturf(proj_turf))
return
var/obj/item/projectile/destabilizer/D = new /obj/item/projectile/destabilizer(proj_turf)
for(var/t in trophies)
var/obj/item/crusher_trophy/T = t
T.on_projectile_fire(D, owner)
D.preparePixelProjectile(attacker, owner)
D.firer = owner
D.hammer_synced = src
playsound(owner, 'sound/weapons/plasma_cutter.ogg', 100, 1)
D.fire()
/obj/item/kinetic_crusher/glaive/active_parry_reflex_counter(mob/living/owner, atom/object, damage, attack_text, attack_type, armour_penetration, mob/attacker, def_zone, list/return_list, parry_efficiency, list/effect_text)
if(owner.Adjacent(attacker) && (!attacker.anchored || ismegafauna(attacker))) // free backstab, if you perfect parry
attacker.dir = get_dir(owner,attacker)
/obj/item/kinetic_crusher/glaive/update_icon_state()
item_state = "crusher[wielded]-glaive" // this is not icon_state and not supported by 2hcomponent
@@ -8,8 +8,6 @@ GLOBAL_LIST(labor_sheet_values)
icon = 'icons/obj/machines/mining_machines.dmi'
icon_state = "console"
density = FALSE
ui_x = 315
ui_y = 430
var/obj/machinery/mineral/stacking_machine/laborstacker/stacking_machine = null
var/machinedir = SOUTH
@@ -35,11 +33,10 @@ GLOBAL_LIST(labor_sheet_values)
/proc/cmp_sheet_list(list/a, list/b)
return a["value"] - b["value"]
/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/mineral/labor_claim_console/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "LaborClaimConsole", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "LaborClaimConsole", name)
ui.open()
/obj/machinery/mineral/labor_claim_console/ui_data(mob/user)
@@ -121,7 +118,7 @@ GLOBAL_LIST(labor_sheet_values)
/obj/machinery/mineral/stacking_machine/laborstacker
force_connect = TRUE
var/points = 0 //The unclaimed value of ore stacked.
//damage_deflection = 21
/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp)
points += inp.point_value * inp.amount
..()
+3 -3
View File
@@ -195,10 +195,10 @@
to_chat(user, "<span class='notice'>You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].</span>")
return TRUE
/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/mineral/ore_redemption/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "OreRedemptionMachine", "Ore Redemption Machine", 440, 550, master_ui, state)
ui = new(user, src, "OreRedemptionMachine")
ui.open()
/obj/machinery/mineral/ore_redemption/ui_data(mob/user)
+15 -17
View File
@@ -7,8 +7,6 @@
icon_state = "mining"
density = TRUE
circuit = /obj/item/circuitboard/machine/mining_equipment_vendor
ui_x = 425
ui_y = 600
var/icon_deny = "mining-deny"
var/obj/item/card/id/inserted_id
var/list/prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces.
@@ -68,13 +66,13 @@
new /datum/data/mining_equipment("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150),
new /datum/data/mining_equipment("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250),
new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300),
new /datum/data/mining_equipment("Kinetic Glaive", /obj/item/kinetic_crusher/glaive, 1500),
new /datum/data/mining_equipment("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000),
new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000),
new /datum/data/mining_equipment("Miner Full Replacement", /obj/item/storage/backpack/duffelbag/mining_cloned, 3000),
new /datum/data/mining_equipment("Premium Accelerator", /obj/item/gun/energy/kinetic_accelerator/premiumka, 8000)
new /datum/data/mining_equipment("Premium Accelerator", /obj/item/gun/energy/kinetic_accelerator/premiumka, 8000),
new /datum/data/mining_equipment("Kinetic Glaive", /obj/item/kinetic_crusher/glaive, 2250),
)
/datum/data/mining_equipment
@@ -102,17 +100,15 @@
else
icon_state = "[initial(icon_state)]-off"
/obj/machinery/mineral/equipment_vendor/ui_base_html(html)
var/datum/asset/spritesheet/assets = get_asset_datum(/datum/asset/spritesheet/vending)
. = replacetext(html, "<!--customheadhtml-->", assets.css_tag())
/obj/machinery/mineral/equipment_vendor/ui_assets(mob/user)
return list(
get_asset_datum(/datum/asset/spritesheet/vending),
)
/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/mineral/equipment_vendor/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
var/datum/asset/assets = get_asset_datum(/datum/asset/spritesheet/vending)
assets.send(user)
ui = new(user, src, ui_key, "MiningVendor", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "MiningVendor", name)
ui.open()
/obj/machinery/mineral/equipment_vendor/ui_static_data(mob/user)
@@ -137,10 +133,12 @@
if(C)
.["user"] = list()
.["user"]["points"] = C.mining_points
if(C.assignment)
.["user"]["job"] = C.assignment
else
.["user"]["job"] = "No Job"
if(C.registered_account)
.["user"]["name"] = C.registered_account.account_holder
if(C.registered_account.account_job)
.["user"]["job"] = C.registered_account.account_job.title
else
.["user"]["job"] = "No Job"
/obj/machinery/mineral/equipment_vendor/ui_act(action, params)
if(..())
+3 -7
View File
@@ -9,9 +9,6 @@
density = TRUE
pressure_resistance = 5*ONE_ATMOSPHERE
var/ui_x = 335
var/ui_y = 415
/obj/structure/ore_box/attackby(obj/item/W, mob/user, params)
if (istype(W, /obj/item/stack/ore))
user.transferItemToLoc(W, src)
@@ -58,11 +55,10 @@
stoplag()
drop = drop_location()
/obj/structure/ore_box/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, \
datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/structure/ore_box/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "OreBox", name, ui_x, ui_y, master_ui, state)
ui = new(user, src, "OreBox", name)
ui.open()
/obj/structure/ore_box/ui_data()
@@ -791,6 +791,10 @@
name = "Volaju"
icon_state = "hair_volaju"
/datum/sprite_accessory/hair/volajupompless
name = "Volaju (Alt)"
icon_state = "hair_volajupompless"
/datum/sprite_accessory/hair/wisp
name = "Wisp"
icon_state = "hair_wisp"
@@ -184,6 +184,12 @@
extra = TRUE
extra_color_src = MUTCOLORS3
/datum/sprite_accessory/mam_snouts/skulldog
name = "Skulldog"
icon_state = "skulldog"
extra = TRUE
extra_color_src = MATRIXED
/datum/sprite_accessory/mam_snouts/lcanid
name = "Mammal, Long"
icon_state = "lcanid"
@@ -3,12 +3,10 @@
set name = "Notification preferences"
set desc = "Notification preferences"
var/datum/notificationpanel/panel = new(usr)
var/datum/notificationpanel/panel = new(usr)
panel.ui_interact(usr)
/datum/notificationpanel
var/client/user
@@ -21,10 +19,13 @@
else
src.user = user
/datum/notificationpanel/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
/datum/notificationpanel/ui_state(mob/user)
return GLOB.observer_state
/datum/notificationpanel/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_key, "NotificationPreferences", "Notification Preferences", 270, 360, master_ui, state)
ui = new(user, src, "NotificationPreferences")
ui.open()
/datum/notificationpanel/ui_data(mob/user)
@@ -35,8 +36,7 @@
"key" = key,
"enabled" = (user.ckey in GLOB.poll_ignore[key]),
"desc" = GLOB.poll_ignore_desc[key]
))
))
/datum/notificationpanel/ui_act(action, params)
if(..())
@@ -905,6 +905,22 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
else
to_chat(usr, "Can't become a pAI candidate while not dead!")
/mob/dead/observer/verb/mafia_game_signup()
set category = "Ghost"
set name = "Signup for Mafia"
set desc = "Sign up for a game of Mafia to pass the time while dead."
mafia_signup()
/mob/dead/observer/proc/mafia_signup()
if(!client)
return
if(!isobserver(src))
to_chat(usr, "<span class='warning'>You must be a ghost to join mafia!</span>")
return
var/datum/mafia_controller/game = GLOB.mafia_game //this needs to change if you want multiple mafia games up at once.
if(!game)
game = create_mafia_game("mafia")
game.ui_interact(usr)
/mob/dead/observer/CtrlShiftClick(mob/user)
if(isobserver(user) && check_rights(R_SPAWN))
change_mob_type( /mob/living/carbon/human , null, null, TRUE) //always delmob, ghosts shouldn't be left lingering
+9 -5
View File
@@ -6,9 +6,12 @@
qdel(src)
owner = new_owner
/datum/orbit_menu/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.observer_state)
/datum/orbit_menu/ui_state(mob/user)
return GLOB.observer_state
/datum/orbit_menu/ui_interact(mob/user, datum/tgui/ui)
if (!ui)
ui = new(user, src, ui_key, "Orbit", "Orbit", 350, 700, master_ui, state)
ui = new(user, src, "Orbit")
ui.open()
/datum/orbit_menu/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
@@ -16,11 +19,10 @@
return
if (action == "orbit")
var/list/pois = getpois(skip_mindless = 1)
var/atom/movable/poi = pois[params["name"]]
var/ref = params["ref"]
var/atom/movable/poi = (locate(ref) in GLOB.mob_list) || (locate(ref) in GLOB.poi_list)
if (poi != null)
owner.ManualFollow(poi)
ui.close()
/datum/orbit_menu/ui_data(mob/user)
var/list/data = list()
@@ -39,6 +41,8 @@
var/poi = pois[name]
serialized["ref"] = REF(poi)
var/mob/M = poi
if (istype(M))
if (isobserver(M))
-4
View File
@@ -62,10 +62,6 @@
//Effects of bloodloss
var/word = pick("dizzy","woozy","faint")
switch(blood_volume)
if(BLOOD_VOLUME_EXCESS to BLOOD_VOLUME_MAX_LETHAL)
if(prob(15))
to_chat(src, "<span class='userdanger'>Blood starts to tear your skin apart. You're going to burst!</span>")
gib()
if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS)
if(prob(10))
to_chat(src, "<span class='warning'>You feel terribly bloated.</span>")

Some files were not shown because too many files have changed in this diff Show More