Merge branch 'master' into spike-loot-differentces
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]")
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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™"
|
||||
mappath = "_maps/map_files/Mafia/mafia_reebe.dmm"
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
..()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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(..())
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user