mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-12 07:33:34 +01:00
Merge branch 'master' into tgui-medical
This commit is contained in:
@@ -1,136 +0,0 @@
|
||||
#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone?
|
||||
|
||||
/datum/alarm_source
|
||||
var/source = null // The source trigger
|
||||
var/source_name = "" // The name of the source should it be lost (for example a destroyed camera)
|
||||
var/duration = 0 // How long this source will be alarming, 0 for indefinetely.
|
||||
var/severity = 1 // How severe the alarm from this source is.
|
||||
var/start_time = 0 // When this source began alarming.
|
||||
var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost.
|
||||
|
||||
/datum/alarm_source/New(var/atom/source)
|
||||
src.source = source
|
||||
start_time = world.time
|
||||
source_name = source.get_source_name()
|
||||
|
||||
/datum/alarm
|
||||
var/atom/origin //Used to identify the alarm area.
|
||||
var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared.
|
||||
var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source.
|
||||
var/list/cameras //List of cameras that can be switched to, if the player has that capability.
|
||||
var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera).
|
||||
var/area/last_name //The last acquired name, used should origin be lost
|
||||
var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated.
|
||||
var/end_time //Used to set when this alarm should clear, in case the origin is lost.
|
||||
|
||||
/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity)
|
||||
src.origin = origin
|
||||
|
||||
cameras() // Sets up both cameras and last alarm area.
|
||||
set_source_data(source, duration, severity)
|
||||
|
||||
/datum/alarm/process()
|
||||
// Has origin gone missing?
|
||||
if(!origin && !end_time)
|
||||
end_time = world.time + ALARM_RESET_DELAY
|
||||
for(var/datum/alarm_source/AS in sources)
|
||||
// Has the alarm passed its best before date?
|
||||
if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration)))
|
||||
sources -= AS
|
||||
// Has the source gone missing? Then reset the normal duration and set end_time
|
||||
if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely.
|
||||
AS.duration = 0
|
||||
AS.end_time = world.time + ALARM_RESET_DELAY
|
||||
|
||||
/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity)
|
||||
var/datum/alarm_source/AS = sources_assoc[source]
|
||||
if(!AS)
|
||||
AS = new/datum/alarm_source(source)
|
||||
sources += AS
|
||||
sources_assoc[source] = AS
|
||||
// Currently only non-0 durations can be altered (normal alarms VS EMP blasts)
|
||||
if(AS.duration)
|
||||
duration = duration SECONDS
|
||||
AS.duration = duration
|
||||
AS.severity = severity
|
||||
|
||||
/datum/alarm/proc/clear(var/source)
|
||||
var/datum/alarm_source/AS = sources_assoc[source]
|
||||
sources -= AS
|
||||
sources_assoc -= source
|
||||
|
||||
/datum/alarm/proc/alarm_area()
|
||||
if(!origin)
|
||||
return last_area
|
||||
|
||||
last_area = origin.get_alarm_area()
|
||||
return last_area
|
||||
|
||||
/datum/alarm/proc/alarm_name()
|
||||
if(!origin)
|
||||
return last_name
|
||||
|
||||
last_name = origin.get_alarm_name()
|
||||
return last_name
|
||||
|
||||
/datum/alarm/proc/cameras()
|
||||
// If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras
|
||||
if(cameras && (last_camera_area != alarm_area()))
|
||||
cameras = null
|
||||
|
||||
// The list of cameras is also reset by /proc/invalidateCameraCache()
|
||||
if(!cameras)
|
||||
cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras()
|
||||
|
||||
last_camera_area = last_area
|
||||
return cameras
|
||||
|
||||
/datum/alarm/proc/max_severity()
|
||||
var/max_severity = 0
|
||||
for(var/datum/alarm_source/AS in sources)
|
||||
max_severity = max(AS.severity, max_severity)
|
||||
|
||||
return max_severity
|
||||
|
||||
/******************
|
||||
* Assisting procs *
|
||||
******************/
|
||||
/atom/proc/get_alarm_area()
|
||||
var/area/A = get_area(src)
|
||||
return A
|
||||
|
||||
/area/get_alarm_area()
|
||||
return src
|
||||
|
||||
/atom/proc/get_alarm_name()
|
||||
var/area/A = get_area(src)
|
||||
return A.name
|
||||
|
||||
/area/get_alarm_name()
|
||||
return name
|
||||
|
||||
/mob/get_alarm_name()
|
||||
return name
|
||||
|
||||
/atom/proc/get_source_name()
|
||||
return name
|
||||
|
||||
/obj/machinery/camera/get_source_name()
|
||||
return c_tag
|
||||
|
||||
/atom/proc/get_alarm_cameras()
|
||||
var/area/A = get_area(src)
|
||||
return A.get_cameras()
|
||||
|
||||
/area/get_alarm_cameras()
|
||||
return get_cameras()
|
||||
|
||||
/mob/living/silicon/robot/get_alarm_cameras()
|
||||
var/list/cameras = ..()
|
||||
if(camera)
|
||||
cameras += camera
|
||||
|
||||
return cameras
|
||||
|
||||
/mob/living/silicon/robot/syndicate/get_alarm_cameras()
|
||||
return list()
|
||||
@@ -1,103 +0,0 @@
|
||||
#define ALARM_RAISED 1
|
||||
#define ALARM_CLEARED 0
|
||||
|
||||
/datum/alarm_handler
|
||||
var/category = ""
|
||||
var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms
|
||||
var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin.
|
||||
var/list/listeners = new // A list of all objects interested in alarm changes.
|
||||
|
||||
/datum/alarm_handler/process()
|
||||
for(var/datum/alarm/A in alarms)
|
||||
A.process()
|
||||
check_alarm_cleared(A)
|
||||
|
||||
/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
|
||||
var/new_alarm
|
||||
//Proper origin and source mandatory
|
||||
if(!(origin && source))
|
||||
return
|
||||
origin = origin.get_alarm_origin()
|
||||
|
||||
new_alarm = 0
|
||||
//see if there is already an alarm of this origin
|
||||
var/datum/alarm/existing = alarms_assoc[origin]
|
||||
if(existing)
|
||||
existing.set_source_data(source, duration, severity)
|
||||
else
|
||||
existing = new/datum/alarm(origin, source, duration, severity)
|
||||
new_alarm = 1
|
||||
|
||||
alarms |= existing
|
||||
alarms_assoc[origin] = existing
|
||||
if(new_alarm)
|
||||
alarms = dd_sortedObjectList(alarms)
|
||||
on_alarm_change(existing, ALARM_RAISED)
|
||||
|
||||
return new_alarm
|
||||
|
||||
/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source)
|
||||
//Proper origin and source mandatory
|
||||
if(!(origin && source))
|
||||
return
|
||||
origin = origin.get_alarm_origin()
|
||||
|
||||
var/datum/alarm/existing = alarms_assoc[origin]
|
||||
if(existing)
|
||||
existing.clear(source)
|
||||
return check_alarm_cleared(existing)
|
||||
|
||||
/datum/alarm_handler/proc/has_major_alarms()
|
||||
if(alarms && alarms.len)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/alarm_handler/proc/major_alarms()
|
||||
return alarms
|
||||
|
||||
/datum/alarm_handler/proc/minor_alarms()
|
||||
return alarms
|
||||
|
||||
/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm)
|
||||
if((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len)
|
||||
alarms -= alarm
|
||||
alarms_assoc -= alarm.origin
|
||||
on_alarm_change(alarm, ALARM_CLEARED)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
|
||||
for(var/obj/machinery/camera/C in alarm.cameras())
|
||||
if(was_raised)
|
||||
C.network.Add(category)
|
||||
else
|
||||
C.network.Remove(category)
|
||||
notify_listeners(alarm, was_raised)
|
||||
|
||||
/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin)
|
||||
if(!origin)
|
||||
return
|
||||
|
||||
origin = origin.get_alarm_origin()
|
||||
var/datum/alarm/existing = alarms_assoc[origin]
|
||||
if(!existing)
|
||||
return
|
||||
|
||||
return existing.max_severity()
|
||||
|
||||
/atom/proc/get_alarm_origin()
|
||||
return src
|
||||
|
||||
/turf/get_alarm_origin()
|
||||
var/area/area = get_area(src)
|
||||
return area // Very important to get area.master, as dynamic lightning can and will split areas.
|
||||
|
||||
/datum/alarm_handler/proc/register(var/object, var/procName)
|
||||
listeners[object] = procName
|
||||
|
||||
/datum/alarm_handler/proc/unregister(var/object)
|
||||
listeners -= object
|
||||
|
||||
/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised)
|
||||
for(var/listener in listeners)
|
||||
call(listener, listeners[listener])(src, alarm, was_raised)
|
||||
@@ -1,19 +0,0 @@
|
||||
/datum/alarm_handler/atmosphere
|
||||
category = "Atmosphere Alarms"
|
||||
|
||||
/datum/alarm_handler/atmosphere/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
|
||||
..()
|
||||
|
||||
/datum/alarm_handler/atmosphere/major_alarms()
|
||||
var/list/major_alarms = new()
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(A.max_severity() > 1)
|
||||
major_alarms.Add(A)
|
||||
return major_alarms
|
||||
|
||||
/datum/alarm_handler/atmosphere/minor_alarms()
|
||||
var/list/minor_alarms = new()
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(A.max_severity() == 1)
|
||||
minor_alarms.Add(A)
|
||||
return minor_alarms
|
||||
@@ -1,2 +0,0 @@
|
||||
/datum/alarm_handler/burglar
|
||||
category = "Burglar Alarms"
|
||||
@@ -1,2 +0,0 @@
|
||||
/datum/alarm_handler/camera
|
||||
category = "Camera Alarms"
|
||||
@@ -1,11 +0,0 @@
|
||||
/datum/alarm_handler/fire
|
||||
category = "Fire Alarms"
|
||||
|
||||
/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
|
||||
var/area/A = alarm.origin
|
||||
if(istype(A))
|
||||
if(was_raised)
|
||||
A.fire_alert()
|
||||
else
|
||||
A.fire_reset()
|
||||
..()
|
||||
@@ -1,2 +0,0 @@
|
||||
/datum/alarm_handler/motion
|
||||
category = "Motion Alarms"
|
||||
@@ -1,10 +0,0 @@
|
||||
/datum/alarm_handler/power
|
||||
category = "Power Alarms"
|
||||
|
||||
/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
|
||||
var/area/A = alarm.origin
|
||||
if(istype(A))
|
||||
A.power_alert(was_raised)
|
||||
..()
|
||||
|
||||
/area/proc/power_alert(var/alarming)
|
||||
@@ -1,3 +1,9 @@
|
||||
#define WIRE_RECEIVE (1<<0) //Allows pulse(0) to call Activate()
|
||||
#define WIRE_PULSE (1<<1) //Allows pulse(0) to act on the holder
|
||||
#define WIRE_PULSE_SPECIAL (1<<2) //Allows pulse(0) to act on the holders special assembly
|
||||
#define WIRE_RADIO_RECEIVE (1<<3) //Allows pulse(1) to call Activate()
|
||||
#define WIRE_RADIO_PULSE (1<<4) //Allows pulse(1) to send a radio message
|
||||
|
||||
/obj/item/assembly
|
||||
name = "assembly"
|
||||
desc = "A small electronic device that should never exist."
|
||||
@@ -22,21 +28,12 @@
|
||||
var/wires = WIRE_RECEIVE | WIRE_PULSE
|
||||
var/datum/wires/connected = null // currently only used by timer/signaler
|
||||
|
||||
var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate()
|
||||
var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder
|
||||
var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly
|
||||
var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate()
|
||||
var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message
|
||||
|
||||
/obj/item/assembly/proc/activate() //What the device does when turned on
|
||||
return
|
||||
|
||||
/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
|
||||
return
|
||||
|
||||
/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
|
||||
return
|
||||
|
||||
/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here
|
||||
return
|
||||
|
||||
@@ -79,7 +76,11 @@
|
||||
activate()
|
||||
return TRUE
|
||||
|
||||
/obj/item/assembly/pulse(radio = FALSE)
|
||||
//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
|
||||
/obj/item/assembly/proc/pulse(radio = FALSE)
|
||||
if(connected && wires)
|
||||
connected.pulse_assembly(src)
|
||||
return TRUE
|
||||
if(holder && (wires & WIRE_PULSE))
|
||||
holder.process_activation(src, 1, 0)
|
||||
if(holder && (wires & WIRE_PULSE_SPECIAL))
|
||||
|
||||
@@ -128,12 +128,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]")
|
||||
|
||||
/obj/item/assembly/signaler/pulse(var/radio = FALSE)
|
||||
if(connected && wires)
|
||||
connected.Pulse(src)
|
||||
else
|
||||
return ..(radio)
|
||||
|
||||
/obj/item/assembly/signaler/receive_signal(datum/signal/signal)
|
||||
if(!receiving || !signal)
|
||||
return FALSE
|
||||
|
||||
@@ -166,7 +166,3 @@
|
||||
/datum/gear/hat/flowerpin
|
||||
display_name = "hair flower"
|
||||
path = /obj/item/clothing/head/hairflower
|
||||
|
||||
/datum/gear/hat/kitty
|
||||
display_name = "kitty headband"
|
||||
path = /obj/item/clothing/head/kitty
|
||||
|
||||
@@ -8,21 +8,3 @@
|
||||
strip_delay = 15
|
||||
put_on_delay = 25
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
/obj/item/clothing/ears/headphones
|
||||
name = "headphones"
|
||||
desc = "Unce unce unce unce."
|
||||
var/on = 0
|
||||
icon_state = "headphones0"
|
||||
item_state = null
|
||||
actions_types = list(/datum/action/item_action/toggle_headphones)
|
||||
|
||||
/obj/item/clothing/ears/headphones/attack_self(mob/user)
|
||||
on = !on
|
||||
icon_state = "headphones[on]"
|
||||
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
|
||||
user.update_inv_ears()
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
resistance_flags = NONE
|
||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
|
||||
sprite_sheets = list(
|
||||
"Vox" = 'icons/mob/species/vox/suit.dmi'
|
||||
"Vox" = 'icons/mob/species/vox/suit.dmi',
|
||||
"Grey" = 'icons/mob/species/grey/suit.dmi'
|
||||
)
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
desc = "A slick, authoritative cloak designed for the Chief Engineer."
|
||||
icon_state = "cemantle"
|
||||
item_state = "cemantle"
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd)
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd)
|
||||
|
||||
//Chief Medical Officer
|
||||
/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer
|
||||
@@ -231,7 +231,7 @@
|
||||
icon_state = "hazard"
|
||||
item_state = "hazard"
|
||||
blood_overlay_type = "armor"
|
||||
allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen)
|
||||
allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen, /obj/item/rcd, /obj/item/rpd)
|
||||
resistance_flags = NONE
|
||||
|
||||
sprite_sheets = list(
|
||||
|
||||
@@ -357,7 +357,7 @@
|
||||
if(!fail_msg)
|
||||
to_chat(usr, "<span class='notice'>[TR.name] constructed.</span>")
|
||||
if(TR.alert_admins_on_craft)
|
||||
message_admins("[usr.ckey] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
|
||||
message_admins("[key_name_admin(usr)] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
|
||||
else
|
||||
to_chat(usr, "<span class ='warning'>Construction failed[fail_msg]</span>")
|
||||
busy = FALSE
|
||||
|
||||
@@ -46,10 +46,10 @@
|
||||
if(prob(APC_BREAK_PROBABILITY))
|
||||
// if it has internal wires, cut the power wires
|
||||
if(C.wires)
|
||||
if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER1))
|
||||
C.wires.CutWireIndex(APC_WIRE_MAIN_POWER1)
|
||||
if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER2))
|
||||
C.wires.CutWireIndex(APC_WIRE_MAIN_POWER2)
|
||||
if(!C.wires.is_cut(WIRE_MAIN_POWER1))
|
||||
C.wires.cut(WIRE_MAIN_POWER1)
|
||||
if(!C.wires.is_cut(WIRE_MAIN_POWER2))
|
||||
C.wires.cut(WIRE_MAIN_POWER2)
|
||||
// if it was operating, toggle off the breaker
|
||||
if(C.operating)
|
||||
C.toggle_breaker()
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
log_debug("Original brand intelligence machine: [originMachine] ([ADMIN_VV(originMachine,"VV")]) [ADMIN_JMP(originMachine)]")
|
||||
|
||||
/datum/event/brand_intelligence/tick()
|
||||
if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.IsAllCut()) //if the original vending machine is missing or has it's voice switch flipped
|
||||
if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.is_all_cut()) //if the original vending machine is missing or has it's voice switch flipped
|
||||
for(var/obj/machinery/vending/saved in infectedMachines)
|
||||
saved.shoot_inventory = 0
|
||||
if(originMachine)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
materials = list(MAT_GLASS=100)
|
||||
var/light_intensity = 2
|
||||
light_color = LIGHT_COLOR_LIGHTBLUE
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change()
|
||||
if(!isShotFlammable() && (resistance_flags & ON_FIRE))
|
||||
@@ -30,6 +31,7 @@
|
||||
overlays += filling
|
||||
name = "shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future.
|
||||
if(resistance_flags & ON_FIRE)
|
||||
cut_overlay(GLOB.fire_overlay, TRUE)
|
||||
overlays += "shotglass_fire"
|
||||
name = "flaming [name]"
|
||||
else
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
name = "-meat"
|
||||
var/subjectname = ""
|
||||
var/subjectjob = null
|
||||
tastes = list("tender meat" = 1)
|
||||
tastes = list("salty meat" = 1)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/meatproduct
|
||||
name = "meat product"
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
max_n_of_items = 1500 * B.rating
|
||||
|
||||
/obj/machinery/smartfridge/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
QDEL_NULL(wires)
|
||||
for(var/atom/movable/A in contents)
|
||||
A.forceMove(loc)
|
||||
|
||||
@@ -198,10 +198,17 @@
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/beebox/crowbar_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.use_tool(src, user, 0))
|
||||
return
|
||||
TOOL_ATTEMPT_DISMANTLE_MESSAGE
|
||||
if(I.use_tool(src, user, 50, volume = I.tool_volume))
|
||||
TOOL_DISMANTLE_SUCCESS_MESSAGE
|
||||
deconstruct(disassembled = TRUE)
|
||||
|
||||
/obj/structure/beebox/wrench_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.tool_use_check(user, 0))
|
||||
return
|
||||
default_unfasten_wrench(user, I, time = 20)
|
||||
|
||||
/obj/structure/beebox/attack_hand(mob/user)
|
||||
@@ -261,15 +268,18 @@
|
||||
visible_message("<span class='notice'>[user] removes the queen from the apiary.</span>")
|
||||
queen_bee = null
|
||||
|
||||
/obj/structure/beebox/deconstruct(disassembled = TRUE)
|
||||
new /obj/item/stack/sheet/wood(loc, 20)
|
||||
/obj/structure/beebox/deconstruct(disassembled = FALSE)
|
||||
var/mat_drop = 20
|
||||
if(disassembled)
|
||||
mat_drop = 40
|
||||
new /obj/item/stack/sheet/wood(loc, mat_drop)
|
||||
for(var/mob/living/simple_animal/hostile/poison/bees/B in bees)
|
||||
if(B.loc == src)
|
||||
B.forceMove(drop_location())
|
||||
for(var/obj/item/honey_frame/HF in honey_frames)
|
||||
HF.forceMove(drop_location())
|
||||
honey_frames -= HF
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
/obj/structure/beebox/unwrenched
|
||||
anchored = FALSE
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "barrel"
|
||||
density = TRUE
|
||||
anchored = FALSE
|
||||
anchored = TRUE
|
||||
container_type = DRAINABLE | AMOUNT_VISIBLE
|
||||
pressure_resistance = 2 * ONE_ATMOSPHERE
|
||||
max_integrity = 300
|
||||
@@ -65,6 +65,26 @@
|
||||
to_chat(user, "<span class='notice'>You close [src], letting you draw from its tap.</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/structure/fermenting_barrel/crowbar_act(mob/living/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.use_tool(src, user, 0))
|
||||
return
|
||||
TOOL_ATTEMPT_DISMANTLE_MESSAGE
|
||||
if(I.use_tool(src, user, 50, volume = I.tool_volume))
|
||||
TOOL_DISMANTLE_SUCCESS_MESSAGE
|
||||
deconstruct(disassembled = TRUE)
|
||||
|
||||
/obj/structure/fermenting_barrel/wrench_act(mob/living/user, obj/item/I)
|
||||
. = TRUE
|
||||
default_unfasten_wrench(user, I, time = 20)
|
||||
|
||||
/obj/structure/fermenting_barrel/deconstruct(disassembled = FALSE)
|
||||
var/mat_drop = 15
|
||||
if(disassembled)
|
||||
mat_drop = 30
|
||||
new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
|
||||
..()
|
||||
|
||||
/obj/structure/fermenting_barrel/update_icon()
|
||||
if(open)
|
||||
icon_state = "barrel_open"
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
if(istype(W,/obj/item/reagent_containers/syringe))
|
||||
if(!contains_sample)
|
||||
for(var/datum/reagent/blood/bloodSample in W.reagents.reagent_list)
|
||||
if(bloodSample.data["mind"] && bloodSample.data["cloneable"] == 1)
|
||||
var/datum/dna/dna = bloodSample.data["dna"]
|
||||
if(bloodSample.data["mind"] && bloodSample.data["cloneable"] && !(NO_SCAN in dna.species.species_traits))
|
||||
var/datum/mind/tempmind = bloodSample.data["mind"]
|
||||
if(tempmind.is_revivable())
|
||||
mind = bloodSample.data["mind"]
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Get all non admin_only instruments as a list of text ids.
|
||||
*/
|
||||
/proc/get_allowed_instrument_ids()
|
||||
. = list()
|
||||
for(var/id in SSinstruments.instrument_data)
|
||||
var/datum/instrument/I = SSinstruments.instrument_data[id]
|
||||
if(!I.admin_only)
|
||||
. += I.id
|
||||
|
||||
/**
|
||||
* # Instrument Datums
|
||||
*
|
||||
* Instrument datums hold the data for any given instrument, as well as data on how to play it and what bounds there are to playing it.
|
||||
*
|
||||
* The datums themselves are kept in SSinstruments in a list by their unique ID. The reason it uses ID instead of typepath is to support the runtime creation of instruments.
|
||||
* Since songs cache them while playing, there isn't realistic issues regarding performance from accessing.
|
||||
*/
|
||||
/datum/instrument
|
||||
/// Name of the instrument
|
||||
var/name = "Generic instrument"
|
||||
/// Uniquely identifies this instrument so runtime changes are possible as opposed to paths. If this is unset, things will use path instead.
|
||||
var/id
|
||||
/// Category
|
||||
var/category = "Unsorted"
|
||||
/// Used for categorization subtypes
|
||||
var/abstract_type = /datum/instrument
|
||||
/// Write here however many samples, follow this syntax: "%note num%"='%sample file%' eg. "27"='synthesizer/e2.ogg'. Key must never be lower than 0 and higher than 127
|
||||
var/list/real_samples
|
||||
/// assoc list key = /datum/instrument_key. do not fill this yourself!
|
||||
var/list/samples
|
||||
/// See __DEFINES/flags/instruments.dm
|
||||
var/instrument_flags = NONE
|
||||
/// For legacy instruments, the path to our notes
|
||||
var/legacy_instrument_path
|
||||
/// For legacy instruments, our file extension
|
||||
var/legacy_instrument_ext
|
||||
/// What songs are using us
|
||||
var/list/datum/song/songs_using = list()
|
||||
/// Don't touch this
|
||||
var/static/HIGHEST_KEY = 127
|
||||
/// Don't touch this x2
|
||||
var/static/LOWEST_KEY = 0
|
||||
/// Oh no - For truly troll instruments.
|
||||
var/admin_only = FALSE
|
||||
/// Volume multiplier. Synthesized instruments are quite loud and I don't like to cut off potential detail via editing. (someone correct me if this isn't a thing)
|
||||
var/volume_multiplier = 0.33
|
||||
|
||||
/datum/instrument/New()
|
||||
if(isnull(id))
|
||||
id = "[type]"
|
||||
|
||||
/datum/instrument/Destroy()
|
||||
SSinstruments.instrument_data -= id
|
||||
for(var/i in songs_using)
|
||||
var/datum/song/S = i
|
||||
S.set_instrument(null)
|
||||
real_samples = null
|
||||
samples = null
|
||||
songs_using = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Initializes the instrument, calculating its samples if necessary.
|
||||
*/
|
||||
/datum/instrument/proc/Initialize()
|
||||
if(instrument_flags & (INSTRUMENT_LEGACY | INSTRUMENT_DO_NOT_AUTOSAMPLE))
|
||||
return
|
||||
calculate_samples()
|
||||
|
||||
/**
|
||||
* Checks if this instrument is ready to play.
|
||||
*/
|
||||
/datum/instrument/proc/is_ready()
|
||||
if(instrument_flags & INSTRUMENT_LEGACY)
|
||||
return legacy_instrument_path && legacy_instrument_ext
|
||||
else if(instrument_flags & INSTRUMENT_DO_NOT_AUTOSAMPLE)
|
||||
return length(samples)
|
||||
return length(samples) >= 128
|
||||
|
||||
/**
|
||||
* For synthesized instruments, this is how the instrument generates the "keys" that a [/datum/song] uses to play notes.
|
||||
* Calculating them on the fly would be unperformant, so we do it during init and keep it all cached in a list.
|
||||
*/
|
||||
/datum/instrument/proc/calculate_samples()
|
||||
if(!length(real_samples))
|
||||
CRASH("No real samples defined for [id] [type] on calculate_samples() call.")
|
||||
var/list/real_keys = list()
|
||||
samples = list()
|
||||
for(var/key in real_samples)
|
||||
real_keys += text2num(key)
|
||||
sortTim(real_keys, /proc/cmp_numeric_asc, associative = FALSE)
|
||||
|
||||
for(var/i in 1 to (length(real_keys) - 1))
|
||||
var/from_key = real_keys[i]
|
||||
var/to_key = real_keys[i + 1]
|
||||
var/sample1 = real_samples[num2text(from_key)]
|
||||
var/sample2 = real_samples[num2text(to_key)]
|
||||
var/pivot = FLOOR((from_key + to_key) / 2, 1) //original code was a round but I replaced it because that's effectively a floor, thanks Baystation! who knows what was intended.
|
||||
for(var/key in from_key to pivot)
|
||||
samples[num2text(key)] = new /datum/instrument_key(sample1, key, key - from_key)
|
||||
for(var/key in (pivot + 1) to to_key)
|
||||
samples[num2text(key)] = new /datum/instrument_key(sample2, key, key - to_key)
|
||||
|
||||
// Fill in 0 to first key and last key to 127
|
||||
var/first_key = real_keys[1]
|
||||
var/last_key = real_keys[length(real_keys)]
|
||||
var/first_sample = real_samples[num2text(first_key)]
|
||||
var/last_sample = real_samples[num2text(last_key)]
|
||||
for(var/key in LOWEST_KEY to (first_key - 1))
|
||||
samples[num2text(key)] = new /datum/instrument_key(first_sample, key, key - first_key)
|
||||
for(var/key in last_key to HIGHEST_KEY)
|
||||
samples[num2text(key)] = new /datum/instrument_key(last_sample, key, key - last_key)
|
||||
@@ -0,0 +1,33 @@
|
||||
#define KEY_TWELTH (1/12)
|
||||
|
||||
/**
|
||||
* Instrument key datums contain everything needed to know how to play a specific
|
||||
* note of an instrument.*
|
||||
*/
|
||||
/datum/instrument_key
|
||||
/// The numerical key of what this is, from 1 to 127 on a standard piano keyboard.
|
||||
var/key
|
||||
/// The actual sample file that will be loaded when playing.
|
||||
var/sample
|
||||
/// The frequency to play the sample to get our desired note.
|
||||
var/frequency
|
||||
/// Deviation up/down from the pivot point that uses its sample. Used to calculate frequency.
|
||||
var/deviation
|
||||
|
||||
/datum/instrument_key/New(sample, key, deviation, frequency)
|
||||
src.sample = sample
|
||||
src.key = key
|
||||
src.deviation = deviation
|
||||
src.frequency = frequency
|
||||
if(!frequency && deviation)
|
||||
calculate()
|
||||
|
||||
/**
|
||||
* Calculates and stores our deviation.
|
||||
*/
|
||||
/datum/instrument_key/proc/calculate()
|
||||
if(!deviation)
|
||||
CRASH("Invalid calculate call: No deviation or sample in instrument_key")
|
||||
frequency = 2 ** (KEY_TWELTH * deviation)
|
||||
|
||||
#undef KEY_TWELTH
|
||||
@@ -0,0 +1,26 @@
|
||||
/datum/instrument/brass
|
||||
name = "Generic brass instrument"
|
||||
category = "Brass"
|
||||
abstract_type = /datum/instrument/brass
|
||||
|
||||
/datum/instrument/brass/crisis_section
|
||||
name = "Crisis Brass Section"
|
||||
id = "crbrass"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg')
|
||||
|
||||
/datum/instrument/brass/crisis_trombone
|
||||
name = "Crisis Trombone"
|
||||
id = "crtrombone"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_trombone/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/brass/crisis_trombone/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/brass/crisis_trombone/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_trombone/c5.ogg')
|
||||
|
||||
/datum/instrument/brass/crisis_trumpet
|
||||
name = "Crisis Trumpet"
|
||||
id = "crtrumpet"
|
||||
real_samples = list("60"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c5.ogg')
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/instrument/chromatic
|
||||
name = "Generic chromatic percussion instrument"
|
||||
category = "Chromatic percussion"
|
||||
abstract_type = /datum/instrument/chromatic
|
||||
|
||||
/datum/instrument/chromatic/vibraphone1
|
||||
name = "Crisis Vibraphone"
|
||||
id = "crvibr"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg')
|
||||
|
||||
/datum/instrument/chromatic/musicbox1
|
||||
name = "SGM Music Box"
|
||||
id = "sgmmbox"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg')
|
||||
|
||||
/datum/instrument/chromatic/fluid_celeste
|
||||
name = "FluidR3 Celeste"
|
||||
id = "r3celeste"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c8.ogg')
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/instrument/fun
|
||||
name = "Generic Fun Instrument"
|
||||
category = "Fun"
|
||||
abstract_type = /datum/instrument/fun
|
||||
|
||||
/datum/instrument/fun/honk
|
||||
name = "!!HONK!!"
|
||||
id = "honk"
|
||||
real_samples = list("74"='sound/items/bikehorn.ogg') // Cluwne Heaven
|
||||
|
||||
/datum/instrument/fun/signal
|
||||
name = "Ping"
|
||||
id = "ping"
|
||||
real_samples = list("79"='sound/machines/ping.ogg')
|
||||
|
||||
/datum/instrument/fun/chime
|
||||
name = "Chime"
|
||||
id = "chime"
|
||||
real_samples = list("79"='sound/machines/chime.ogg')
|
||||
@@ -0,0 +1,36 @@
|
||||
/datum/instrument/guitar
|
||||
name = "Generic guitar-like instrument"
|
||||
category = "Guitar"
|
||||
abstract_type = /datum/instrument/guitar
|
||||
|
||||
/datum/instrument/guitar/steel_crisis
|
||||
name = "Crisis Steel String Guitar"
|
||||
id = "csteelgt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/nylon_crisis
|
||||
name = "Crisis Nylon String Guitar"
|
||||
id = "cnylongt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/clean_crisis
|
||||
name = "Crisis Clean Guitar"
|
||||
id = "ccleangt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_clean/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_clean/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_clean/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_clean/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/muted_crisis
|
||||
name = "Crisis Muted Guitar"
|
||||
id = "cmutedgt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_muted/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_muted/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_muted/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_muted/c5.ogg')
|
||||
@@ -0,0 +1,86 @@
|
||||
//THESE ARE HARDCODED INSTRUMENT SAMPLES.
|
||||
//SONGS WILL BE AUTOMATICALLY SWITCHED TO LEGACY MODE IF THEY USE THIS KIND OF INSTRUMENT!
|
||||
//I'd prefer these stayed. They sound different from the mechanical synthesis of synthed instruments, and I quite like them that way. It's not legacy, it's hardcoded, old style. - kevinz000
|
||||
/datum/instrument/hardcoded
|
||||
abstract_type = /datum/instrument/hardcoded
|
||||
category = "Non-Synthesized"
|
||||
instrument_flags = INSTRUMENT_LEGACY
|
||||
volume_multiplier = 1 //not as loud as synth'd
|
||||
|
||||
/datum/instrument/hardcoded/accordion
|
||||
name = "Accordion"
|
||||
id = "accordion"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "accordion"
|
||||
|
||||
/datum/instrument/hardcoded/bikehorn
|
||||
name = "Bike Horn"
|
||||
id = "bikehorn"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "bikehorn"
|
||||
|
||||
/datum/instrument/hardcoded/eguitar
|
||||
name = "Electric Guitar"
|
||||
id = "eguitar"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "eguitar"
|
||||
|
||||
/datum/instrument/hardcoded/glockenspiel
|
||||
name = "Glockenspiel"
|
||||
id = "glockenspiel"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "glockenspiel"
|
||||
|
||||
/datum/instrument/hardcoded/guitar
|
||||
name = "Guitar"
|
||||
id = "guitar"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "guitar"
|
||||
|
||||
/datum/instrument/hardcoded/harmonica
|
||||
name = "Harmonica"
|
||||
id = "harmonica"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "harmonica"
|
||||
|
||||
/datum/instrument/hardcoded/piano
|
||||
name = "Piano"
|
||||
id = "piano"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "piano"
|
||||
|
||||
/datum/instrument/hardcoded/recorder
|
||||
name = "Recorder"
|
||||
id = "recorder"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "recorder"
|
||||
|
||||
/datum/instrument/hardcoded/saxophone
|
||||
name = "Saxophone"
|
||||
id = "saxophone"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "saxophone"
|
||||
|
||||
/datum/instrument/hardcoded/trombone
|
||||
name = "Trombone"
|
||||
id = "trombone"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "trombone"
|
||||
|
||||
/datum/instrument/hardcoded/violin
|
||||
name = "Violin"
|
||||
id = "violin"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "violin"
|
||||
|
||||
/datum/instrument/hardcoded/xylophone
|
||||
name = "Xylophone"
|
||||
id = "xylophone"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "xylophone"
|
||||
|
||||
/datum/instrument/hardcoded/banjo
|
||||
name = "Banjo"
|
||||
id = "banjo"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "banjo"
|
||||
@@ -0,0 +1,53 @@
|
||||
//copy pasta of the space piano, don't hurt me -Pete
|
||||
/obj/item/instrument
|
||||
name = "generic instrument"
|
||||
force = 10
|
||||
max_integrity = 100
|
||||
resistance_flags = FLAMMABLE
|
||||
icon = 'icons/obj/musician.dmi'
|
||||
lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi'
|
||||
/// Our song datum.
|
||||
var/datum/song/handheld/song
|
||||
/// Our allowed list of instrument ids. This is nulled on initialize.
|
||||
var/list/allowed_instrument_ids
|
||||
/// How far away our song datum can be heard.
|
||||
var/instrument_range = 15
|
||||
|
||||
/obj/item/instrument/Initialize(mapload)
|
||||
. = ..()
|
||||
song = new(src, allowed_instrument_ids, instrument_range)
|
||||
allowed_instrument_ids = null //We don't need this clogging memory after it's used.
|
||||
|
||||
/obj/item/instrument/Destroy()
|
||||
QDEL_NULL(song)
|
||||
return ..()
|
||||
|
||||
/obj/item/instrument/suicide_act(mob/user)
|
||||
user.visible_message("<span class='suicide'>[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
return BRUTELOSS
|
||||
|
||||
/obj/item/instrument/attack_self(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/item/instrument/tgui_data(mob/user)
|
||||
return song.tgui_data(user)
|
||||
|
||||
/obj/item/instrument/tgui_interact(mob/user)
|
||||
if(!isliving(user) || user.incapacitated())
|
||||
return
|
||||
song.tgui_interact(user)
|
||||
|
||||
/obj/item/instrument/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return song.tgui_act(action, params)
|
||||
|
||||
/**
|
||||
* Whether the instrument should stop playing
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
*/
|
||||
/obj/item/instrument/proc/should_stop_playing(mob/user)
|
||||
return !(src in user) || !isliving(user) || user.incapacitated()
|
||||
@@ -0,0 +1,80 @@
|
||||
/obj/item/clothing/ears/headphones
|
||||
name = "headphones"
|
||||
desc = "Unce unce unce unce."
|
||||
icon_state = "headphones0"
|
||||
item_state = "headphones0"
|
||||
actions_types = list(/datum/action/item_action/change_headphones_song)
|
||||
var/datum/song/headphones/song
|
||||
|
||||
/obj/item/clothing/ears/headphones/Initialize(mapload)
|
||||
. = ..()
|
||||
song = new(src, "piano") // Piano is the default instrument but all instruments are allowed
|
||||
song.instrument_range = 0
|
||||
song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids
|
||||
// To update the icon
|
||||
RegisterSignal(src, COMSIG_SONG_START, .proc/start_playing)
|
||||
RegisterSignal(src, COMSIG_SONG_END, .proc/stop_playing)
|
||||
|
||||
/obj/item/clothing/ears/headphones/Destroy()
|
||||
QDEL_NULL(song)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/ears/headphones/attack_self(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/item/clothing/ears/headphones/tgui_data(mob/user)
|
||||
return song.tgui_data(user)
|
||||
|
||||
/obj/item/clothing/ears/headphones/tgui_interact(mob/user)
|
||||
if(should_stop_playing(user) || user.incapacitated())
|
||||
return
|
||||
song.tgui_interact(user)
|
||||
|
||||
/obj/item/clothing/ears/headphones/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return song.tgui_act(action, params)
|
||||
|
||||
/obj/item/clothing/ears/headphones/update_icon()
|
||||
var/mob/living/carbon/human/user = loc
|
||||
if(istype(user))
|
||||
user.update_action_buttons_icon()
|
||||
user.update_inv_ears()
|
||||
..()
|
||||
|
||||
/obj/item/clothing/ears/headphones/item_action_slot_check(slot)
|
||||
if(slot == slot_l_ear || slot == slot_r_ear)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Called by a component signal when our song starts playing.
|
||||
*/
|
||||
/obj/item/clothing/ears/headphones/proc/start_playing()
|
||||
icon_state = item_state = "headphones1"
|
||||
update_icon()
|
||||
|
||||
/**
|
||||
* Called by a component signal when our song stops playing.
|
||||
*/
|
||||
/obj/item/clothing/ears/headphones/proc/stop_playing()
|
||||
icon_state = item_state = "headphones0"
|
||||
update_icon()
|
||||
|
||||
/**
|
||||
* Whether the headphone's song should stop playing
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
*/
|
||||
/obj/item/clothing/ears/headphones/proc/should_stop_playing(mob/living/carbon/human/user)
|
||||
return !(src in user) || !istype(user) || !((src == user.l_ear) || (src == user.r_ear))
|
||||
|
||||
// special subtype so it uses the correct item type
|
||||
/datum/song/headphones
|
||||
|
||||
/datum/song/headphones/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
var/obj/item/clothing/ears/headphones/I = parent
|
||||
return I.should_stop_playing(user)
|
||||
@@ -0,0 +1,216 @@
|
||||
/obj/item/instrument/violin
|
||||
name = "space violin"
|
||||
desc = "A wooden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\""
|
||||
icon_state = "violin"
|
||||
item_state = "violin"
|
||||
hitsound = "swing_hit"
|
||||
allowed_instrument_ids = "violin"
|
||||
|
||||
/obj/item/instrument/violin/golden
|
||||
name = "golden violin"
|
||||
desc = "A golden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\""
|
||||
icon_state = "golden_violin"
|
||||
item_state = "golden_violin"
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/instrument/piano_synth
|
||||
name = "synthesizer"
|
||||
desc = "An advanced electronic synthesizer that can be used as various instruments."
|
||||
icon_state = "synth"
|
||||
item_state = "synth"
|
||||
allowed_instrument_ids = "piano"
|
||||
|
||||
/obj/item/instrument/piano_synth/Initialize(mapload)
|
||||
. = ..()
|
||||
song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids
|
||||
|
||||
/obj/item/instrument/banjo
|
||||
name = "banjo"
|
||||
desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings."
|
||||
icon_state = "banjo"
|
||||
item_state = "banjo"
|
||||
attack_verb = list("scruggs-styled", "hum-diggitied", "shin-digged", "clawhammered")
|
||||
hitsound = 'sound/weapons/banjoslap.ogg'
|
||||
allowed_instrument_ids = "banjo"
|
||||
|
||||
/obj/item/instrument/guitar
|
||||
name = "guitar"
|
||||
desc = "It's made of wood and has bronze strings."
|
||||
icon_state = "guitar"
|
||||
item_state = "guitar"
|
||||
attack_verb = list("played metal on", "serenaded", "crashed", "smashed")
|
||||
hitsound = 'sound/weapons/guitarslam.ogg'
|
||||
allowed_instrument_ids = "guitar"
|
||||
|
||||
/obj/item/instrument/eguitar
|
||||
name = "electric guitar"
|
||||
desc = "Makes all your shredding needs possible."
|
||||
icon_state = "eguitar"
|
||||
item_state = "eguitar"
|
||||
force = 12
|
||||
attack_verb = list("played metal on", "shredded", "crashed", "smashed")
|
||||
hitsound = 'sound/weapons/stringsmash.ogg'
|
||||
allowed_instrument_ids = "eguitar"
|
||||
|
||||
/obj/item/instrument/glockenspiel
|
||||
name = "glockenspiel"
|
||||
desc = "Smooth metal bars perfect for any marching band."
|
||||
icon_state = "glockenspiel"
|
||||
item_state = "glockenspiel"
|
||||
allowed_instrument_ids = "glockenspiel"
|
||||
|
||||
/obj/item/instrument/accordion
|
||||
name = "accordion"
|
||||
desc = "Pun-Pun not included."
|
||||
icon_state = "accordion"
|
||||
item_state = "accordion"
|
||||
allowed_instrument_ids = "accordion"
|
||||
|
||||
/obj/item/instrument/trumpet
|
||||
name = "trumpet"
|
||||
desc = "To announce the arrival of the king!"
|
||||
icon_state = "trumpet"
|
||||
item_state = "trumpet"
|
||||
allowed_instrument_ids = "trombone"
|
||||
|
||||
/obj/item/instrument/trumpet/spectral
|
||||
name = "spectral trumpet"
|
||||
desc = "Things are about to get spooky!"
|
||||
icon_state = "spectral_trumpet"
|
||||
item_state = "spectral_trumpet"
|
||||
force = 0
|
||||
attack_verb = list("played", "jazzed", "trumpeted", "mourned", "dooted", "spooked")
|
||||
|
||||
/obj/item/instrument/trumpet/spectral/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/spooky)
|
||||
|
||||
/obj/item/instrument/trumpet/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound(src, 'sound/instruments/trombone/En4.mid', 100, 1, -1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/saxophone
|
||||
name = "saxophone"
|
||||
desc = "This soothing sound will be sure to leave your audience in tears."
|
||||
icon_state = "saxophone"
|
||||
item_state = "saxophone"
|
||||
allowed_instrument_ids = "saxophone"
|
||||
|
||||
/obj/item/instrument/saxophone/spectral
|
||||
name = "spectral saxophone"
|
||||
desc = "This spooky sound will be sure to leave mortals in bones."
|
||||
icon_state = "saxophone"
|
||||
item_state = "saxophone"
|
||||
force = 0
|
||||
attack_verb = list("played", "jazzed", "saxxed", "mourned", "dooted", "spooked")
|
||||
|
||||
/obj/item/instrument/saxophone/spectral/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/spooky)
|
||||
|
||||
/obj/item/instrument/saxophone/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound(src, 'sound/instruments/saxophone/En4.mid', 100,1,-1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/trombone
|
||||
name = "trombone"
|
||||
desc = "How can any pool table ever hope to compete?"
|
||||
icon_state = "trombone"
|
||||
allowed_instrument_ids = "trombone"
|
||||
item_state = "trombone"
|
||||
|
||||
/obj/item/instrument/trombone/spectral
|
||||
name = "spectral trombone"
|
||||
desc = "A skeleton's favorite instrument. Apply directly on the mortals."
|
||||
icon_state = "trombone"
|
||||
item_state = "trombone"
|
||||
force = 0
|
||||
attack_verb = list("played", "jazzed", "tromboned", "mourned", "dooted", "spooked")
|
||||
|
||||
/obj/item/instrument/trombone/spectral/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/spooky)
|
||||
|
||||
/obj/item/instrument/trombone/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound (src, 'sound/instruments/trombone/Cn4.mid', 100,1,-1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/recorder
|
||||
name = "recorder"
|
||||
desc = "Just like in school, playing ability and all."
|
||||
force = 5
|
||||
icon_state = "recorder"
|
||||
item_state = "recorder"
|
||||
allowed_instrument_ids = "recorder"
|
||||
|
||||
/obj/item/instrument/harmonica
|
||||
name = "harmonica"
|
||||
desc = "For when you get a bad case of the space blues."
|
||||
icon_state = "harmonica"
|
||||
item_state = "harmonica"
|
||||
force = 5
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
allowed_instrument_ids = "harmonica"
|
||||
|
||||
/obj/item/instrument/xylophone
|
||||
name = "xylophone"
|
||||
desc = "A percussion instrument with a bright tone."
|
||||
icon_state = "xylophone"
|
||||
item_state = "xylophone"
|
||||
allowed_instrument_ids = "bikehorn"
|
||||
|
||||
/obj/item/instrument/bikehorn
|
||||
name = "gilded bike horn"
|
||||
desc = "An exquisitely decorated bike horn, capable of honking in a variety of notes."
|
||||
icon_state = "bike_horn"
|
||||
item_state = "bike_horn"
|
||||
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
|
||||
attack_verb = list("beautifully honks")
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
force = 0
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
hitsound = 'sound/items/bikehorn.ogg'
|
||||
allowed_instrument_ids = "bikehorn"
|
||||
|
||||
// Crafting recipes
|
||||
/datum/crafting_recipe/violin
|
||||
name = "Violin"
|
||||
result = /obj/item/instrument/violin
|
||||
reqs = list(/obj/item/stack/sheet/wood = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/guitar
|
||||
name = "Guitar"
|
||||
result = /obj/item/instrument/guitar
|
||||
reqs = list(/obj/item/stack/sheet/wood = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/eguitar
|
||||
name = "Electric Guitar"
|
||||
result = /obj/item/instrument/eguitar
|
||||
reqs = list(/obj/item/stack/sheet/metal = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/banjo
|
||||
name = "Banjo"
|
||||
result = /obj/item/instrument/banjo
|
||||
reqs = list(/obj/item/stack/sheet/wood = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
@@ -0,0 +1,47 @@
|
||||
/obj/structure/musician
|
||||
name = "Not A Piano"
|
||||
desc = "Something broke!"
|
||||
var/can_play_unanchored = FALSE
|
||||
var/list/allowed_instrument_ids
|
||||
var/datum/song/song
|
||||
|
||||
/obj/structure/musician/Initialize(mapload)
|
||||
. = ..()
|
||||
song = new(src, allowed_instrument_ids)
|
||||
allowed_instrument_ids = null
|
||||
|
||||
/obj/structure/musician/Destroy()
|
||||
QDEL_NULL(song)
|
||||
return ..()
|
||||
|
||||
/obj/structure/musician/attack_hand(mob/user)
|
||||
add_fingerprint(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/structure/musician/tgui_data(mob/user)
|
||||
return song.tgui_data(user)
|
||||
|
||||
/obj/structure/musician/tgui_interact(mob/user)
|
||||
song.tgui_interact(user)
|
||||
|
||||
/obj/structure/musician/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return song.tgui_act(action, params)
|
||||
|
||||
/obj/structure/musician/wrench_act(mob/living/user, obj/item/I)
|
||||
default_unfasten_wrench(user, I, 40)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Whether the instrument should stop playing
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
*/
|
||||
/obj/structure/musician/proc/should_stop_playing(mob/user)
|
||||
if(!(anchored || can_play_unanchored))
|
||||
return TRUE
|
||||
if(!user)
|
||||
return FALSE
|
||||
return !CanUseTopic(user, GLOB.physical_state)
|
||||
@@ -0,0 +1,22 @@
|
||||
/obj/structure/piano
|
||||
parent_type = /obj/structure/musician // TODO: Can't edit maps right now due to a freeze, remove and update path when it's done
|
||||
name = "space minimoog"
|
||||
icon = 'icons/obj/musician.dmi'
|
||||
icon_state = "minimoog"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
allowed_instrument_ids = "piano"
|
||||
|
||||
/obj/structure/piano/unanchored
|
||||
anchored = FALSE
|
||||
|
||||
/obj/structure/piano/Initialize(mapload)
|
||||
. = ..()
|
||||
if(prob(50) && icon_state == initial(icon_state))
|
||||
name = "space minimoog"
|
||||
desc = "This is a minimoog, like a space piano, but more spacey!"
|
||||
icon_state = "minimoog"
|
||||
else
|
||||
name = "space piano"
|
||||
desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
|
||||
icon_state = "piano"
|
||||
@@ -0,0 +1,43 @@
|
||||
/datum/instrument/organ
|
||||
name = "Generic organ"
|
||||
category = "Organ"
|
||||
abstract_type = /datum/instrument/organ
|
||||
|
||||
/datum/instrument/organ/crisis_church
|
||||
name = "Crisis Church Organ"
|
||||
id = "crichugan"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_hammond
|
||||
name = "Crisis Hammond Organ"
|
||||
id = "crihamgan"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_accordian
|
||||
name = "Crisis Accordion"
|
||||
id = "crack"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_harmonica
|
||||
name = "Crisis Harmonica"
|
||||
id = "crharmony"
|
||||
real_samples = list("48"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_tango_accordian
|
||||
name = "Crisis Tango Accordion"
|
||||
id = "crtango"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg')
|
||||
@@ -0,0 +1,56 @@
|
||||
/datum/instrument/piano
|
||||
name = "Generic piano"
|
||||
category = "Piano"
|
||||
abstract_type = /datum/instrument/piano
|
||||
|
||||
/datum/instrument/piano/fluid_piano
|
||||
name = "FluidR3 Grand Piano"
|
||||
id = "r3grand"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/fluid_harpsichord
|
||||
name = "FluidR3 Harpsichord"
|
||||
id = "r3harpsi"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_harpsichord
|
||||
name = "Crisis Harpsichord"
|
||||
id = "crharpsi"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_grandpiano_uni
|
||||
name = "Crisis Grand Piano One"
|
||||
id = "crgrand1"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_brightpiano_uni
|
||||
name = "Crisis Bright Piano One"
|
||||
id = "crbright1"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg')
|
||||
@@ -0,0 +1,410 @@
|
||||
#define MUSICIAN_HEARCHECK_MINDELAY 4
|
||||
#define MUSIC_MAXLINES 1000
|
||||
#define MUSIC_MAXLINECHARS 300
|
||||
|
||||
/**
|
||||
* # Song datum
|
||||
*
|
||||
* These are the actual backend behind instruments.
|
||||
* They attach to an atom and provide the editor + playback functionality.
|
||||
*/
|
||||
/datum/song
|
||||
/// Name of the song
|
||||
var/name = "Untitled"
|
||||
|
||||
/// The atom we're attached to/playing from
|
||||
var/atom/parent
|
||||
|
||||
/// Our song lines
|
||||
var/list/lines
|
||||
|
||||
/// delay between notes in deciseconds
|
||||
var/tempo = 5
|
||||
|
||||
/// How far we can be heard
|
||||
var/instrument_range = 15
|
||||
|
||||
/// Are we currently playing?
|
||||
var/playing = FALSE
|
||||
|
||||
/// Are we currently editing?
|
||||
var/editing = TRUE
|
||||
/// Is the help screen open?
|
||||
var/help = FALSE
|
||||
|
||||
/// Repeats left
|
||||
var/repeat = 0
|
||||
/// Maximum times we can repeat
|
||||
var/max_repeats = 10
|
||||
|
||||
/// Our volume
|
||||
var/volume = 75
|
||||
/// Max volume
|
||||
var/max_volume = 75
|
||||
/// Min volume - This is so someone doesn't decide it's funny to set it to 0 and play invisible songs.
|
||||
var/min_volume = 1
|
||||
|
||||
/// What instruments our built in picker can use. The picker won't show unless this is longer than one.
|
||||
var/list/allowed_instrument_ids
|
||||
|
||||
//////////// Cached instrument variables /////////////
|
||||
/// Instrument we are currently using
|
||||
var/datum/instrument/using_instrument
|
||||
/// Cached legacy ext for legacy instruments
|
||||
var/cached_legacy_ext
|
||||
/// Cached legacy dir for legacy instruments
|
||||
var/cached_legacy_dir
|
||||
/// Cached list of samples, referenced directly from the instrument for synthesized instruments
|
||||
var/list/cached_samples
|
||||
/// Are we operating in legacy mode (so if the instrument is a legacy instrument)
|
||||
var/legacy = FALSE
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
/////////////////// Playing variables ////////////////
|
||||
/**
|
||||
* Build by compile_chords()
|
||||
* Must be rebuilt on instrument switch.
|
||||
* Compilation happens when we start playing and is cleared after we finish playing.
|
||||
* Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv)
|
||||
*/
|
||||
var/list/compiled_chords
|
||||
/// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag.
|
||||
var/elapsed_delay
|
||||
/// Amount of delay to wait before playing the next chord
|
||||
var/delay_by
|
||||
/// Current chord we're on.
|
||||
var/current_chord
|
||||
/// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1.
|
||||
var/list/channels_playing
|
||||
/// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments.
|
||||
var/list/channels_idle
|
||||
/// Person playing us
|
||||
var/mob/user_playing
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
/// Last world.time we checked for who can hear us
|
||||
var/last_hearcheck = 0
|
||||
/// The list of mobs that can hear us
|
||||
var/list/hearing_mobs
|
||||
/// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop)
|
||||
var/debug_mode = FALSE
|
||||
/// Max sound channels to occupy
|
||||
var/max_sound_channels = CHANNELS_PER_INSTRUMENT
|
||||
/// Current channels, so we can save a length() call.
|
||||
var/using_sound_channels = 0
|
||||
/// Last channel to play. text.
|
||||
var/last_channel_played
|
||||
/// Should we not decay our last played note?
|
||||
var/full_sustain_held_note = TRUE
|
||||
|
||||
/////////////////////// DO NOT TOUCH THESE ///////////////////
|
||||
var/octave_min = INSTRUMENT_MIN_OCTAVE
|
||||
var/octave_max = INSTRUMENT_MAX_OCTAVE
|
||||
var/key_min = INSTRUMENT_MIN_KEY
|
||||
var/key_max = INSTRUMENT_MAX_KEY
|
||||
var/static/list/note_offset_lookup
|
||||
var/static/list/accent_lookup
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
///////////// !!FUN!! - Only works in synthesized mode! /////////////////
|
||||
/// Note numbers to shift.
|
||||
var/note_shift = 0
|
||||
var/note_shift_min = -100
|
||||
var/note_shift_max = 100
|
||||
var/can_noteshift = TRUE
|
||||
/// The kind of sustain we're using
|
||||
var/sustain_mode = SUSTAIN_LINEAR
|
||||
/// When a note is considered dead if it is below this in volume
|
||||
var/sustain_dropoff_volume = INSTRUMENT_MIN_SUSTAIN_DROPOFF
|
||||
/// Total duration of linear sustain for 100 volume note to get to SUSTAIN_DROPOFF
|
||||
var/sustain_linear_duration = 5
|
||||
/// Exponential sustain dropoff rate per decisecond
|
||||
var/sustain_exponential_dropoff = 1.4
|
||||
////////// DO NOT DIRECTLY SET THESE!
|
||||
/// Do not directly set, use update_sustain()
|
||||
var/cached_linear_dropoff = 10
|
||||
/// Do not directly set, use update_sustain()
|
||||
var/cached_exponential_dropoff = 1.045
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var/static/list/valid_files[0] // Cache to avoid running fexists() every time
|
||||
|
||||
/datum/song/New(atom/parent, list/instrument_ids, new_range)
|
||||
SSinstruments.on_song_new(src)
|
||||
lines = list()
|
||||
tempo = sanitize_tempo(tempo)
|
||||
src.parent = parent
|
||||
if(instrument_ids)
|
||||
allowed_instrument_ids = islist(instrument_ids) ? instrument_ids : list(instrument_ids)
|
||||
if(length(allowed_instrument_ids))
|
||||
set_instrument(allowed_instrument_ids[1])
|
||||
hearing_mobs = list()
|
||||
volume = clamp(volume, min_volume, max_volume)
|
||||
channels_playing = list()
|
||||
channels_idle = list()
|
||||
if(!note_offset_lookup)
|
||||
note_offset_lookup = list(9, 11, 0, 2, 4, 5, 7)
|
||||
accent_lookup = list("b" = -1, "s" = 1, "#" = 1, "n" = 0)
|
||||
update_sustain()
|
||||
if(new_range)
|
||||
instrument_range = new_range
|
||||
|
||||
/datum/song/Destroy()
|
||||
stop_playing()
|
||||
SSinstruments.on_song_del(src)
|
||||
lines = null
|
||||
using_instrument = null
|
||||
allowed_instrument_ids = null
|
||||
parent = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range.
|
||||
*/
|
||||
/datum/song/proc/do_hearcheck()
|
||||
last_hearcheck = world.time
|
||||
var/list/old = hearing_mobs.Copy()
|
||||
hearing_mobs.len = 0
|
||||
var/turf/source = get_turf(parent)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
var/dist = get_dist(M, source)
|
||||
if(dist > instrument_range) // Distance check
|
||||
continue
|
||||
if(!isInSight(M, source)) // Visibility check (direct line of sight)
|
||||
continue
|
||||
hearing_mobs[M] = dist
|
||||
var/list/exited = old - hearing_mobs
|
||||
for(var/i in exited)
|
||||
terminate_sound_mob(i)
|
||||
|
||||
/**
|
||||
* Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum.
|
||||
*/
|
||||
/datum/song/proc/set_instrument(datum/instrument/I)
|
||||
terminate_all_sounds()
|
||||
var/old_legacy
|
||||
if(using_instrument)
|
||||
using_instrument.songs_using -= src
|
||||
old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY)
|
||||
using_instrument = null
|
||||
cached_samples = null
|
||||
cached_legacy_ext = null
|
||||
cached_legacy_dir = null
|
||||
legacy = null
|
||||
if(istext(I) || ispath(I))
|
||||
I = SSinstruments.instrument_data[I]
|
||||
if(istype(I))
|
||||
using_instrument = I
|
||||
I.songs_using += src
|
||||
var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY)
|
||||
if(instrument_legacy)
|
||||
cached_legacy_ext = I.legacy_instrument_ext
|
||||
cached_legacy_dir = I.legacy_instrument_path
|
||||
legacy = TRUE
|
||||
else
|
||||
cached_samples = I.samples
|
||||
legacy = FALSE
|
||||
if(isnull(old_legacy) || (old_legacy != instrument_legacy))
|
||||
if(playing)
|
||||
compile_chords()
|
||||
|
||||
/**
|
||||
* Attempts to start playing our song.
|
||||
*/
|
||||
/datum/song/proc/start_playing(mob/user)
|
||||
if(playing)
|
||||
return
|
||||
if(!using_instrument?.is_ready())
|
||||
to_chat(user, "<span class='warning'>An error has occured with [src]. Please reset the instrument.</span>")
|
||||
return
|
||||
compile_chords()
|
||||
if(!length(compiled_chords))
|
||||
to_chat(user, "<span class='warning'>Song is empty.</span>")
|
||||
return
|
||||
playing = TRUE
|
||||
SStgui.update_uis(parent)
|
||||
//we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak.
|
||||
//wrap the rest of the stuff to ensure stop_playing() is called.
|
||||
do_hearcheck()
|
||||
SEND_SIGNAL(parent, COMSIG_SONG_START)
|
||||
elapsed_delay = 0
|
||||
delay_by = 0
|
||||
current_chord = 1
|
||||
user_playing = user
|
||||
START_PROCESSING(SSinstruments, src)
|
||||
|
||||
/**
|
||||
* Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs.
|
||||
*/
|
||||
/datum/song/proc/stop_playing()
|
||||
if(!playing)
|
||||
return
|
||||
playing = FALSE
|
||||
if(!debug_mode)
|
||||
compiled_chords = null
|
||||
STOP_PROCESSING(SSinstruments, src)
|
||||
SEND_SIGNAL(parent, COMSIG_SONG_END)
|
||||
terminate_all_sounds(TRUE)
|
||||
hearing_mobs.len = 0
|
||||
SStgui.update_uis(parent)
|
||||
user_playing = null
|
||||
|
||||
/**
|
||||
* Processes our song.
|
||||
*/
|
||||
/datum/song/proc/process_song(wait)
|
||||
if(!length(compiled_chords) || current_chord > length(compiled_chords) || should_stop_playing(user_playing))
|
||||
stop_playing()
|
||||
return
|
||||
var/list/chord = compiled_chords[current_chord]
|
||||
if(++elapsed_delay >= delay_by)
|
||||
play_chord(chord)
|
||||
elapsed_delay = 0
|
||||
delay_by = tempodiv_to_delay(chord[length(chord)])
|
||||
current_chord++
|
||||
if(current_chord > length(compiled_chords))
|
||||
if(repeat)
|
||||
repeat--
|
||||
current_chord = 1
|
||||
SStgui.update_uis(parent)
|
||||
return
|
||||
else
|
||||
stop_playing()
|
||||
return
|
||||
|
||||
/**
|
||||
* Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo.
|
||||
*/
|
||||
/datum/song/proc/tempodiv_to_delay(tempodiv)
|
||||
tempodiv = tempodiv || world.tick_lag // Default to world.tick_lag in case someone's trying to be smart
|
||||
return max(1, round((tempo / tempodiv) / world.tick_lag, 1))
|
||||
|
||||
/**
|
||||
* Compiles chords.
|
||||
*/
|
||||
/datum/song/proc/compile_chords()
|
||||
legacy ? compile_legacy() : compile_synthesized()
|
||||
|
||||
/**
|
||||
* Plays a chord.
|
||||
*/
|
||||
/datum/song/proc/play_chord(list/chord)
|
||||
// last value is timing information
|
||||
for(var/i in 1 to (length(chord) - 1))
|
||||
legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing)
|
||||
|
||||
/**
|
||||
* Checks if we should halt playback.
|
||||
*/
|
||||
/datum/song/proc/should_stop_playing(mob/user)
|
||||
return QDELETED(parent) || !using_instrument || !playing
|
||||
|
||||
/**
|
||||
* Sanitizes tempo to a value that makes sense and fits the current world.tick_lag.
|
||||
*/
|
||||
/datum/song/proc/sanitize_tempo(new_tempo)
|
||||
new_tempo = abs(new_tempo)
|
||||
return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS)
|
||||
|
||||
/**
|
||||
* Gets our beats per minute based on our tempo.
|
||||
*/
|
||||
/datum/song/proc/get_bpm()
|
||||
return 600 / tempo
|
||||
|
||||
/**
|
||||
* Sets our tempo from a beats-per-minute, sanitizing it to a valid number first.
|
||||
*/
|
||||
/datum/song/proc/set_bpm(bpm)
|
||||
tempo = sanitize_tempo(600 / bpm)
|
||||
|
||||
/datum/song/process(wait)
|
||||
if(!playing)
|
||||
return PROCESS_KILL
|
||||
// it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up.
|
||||
process_song(world.tick_lag)
|
||||
process_decay(world.tick_lag)
|
||||
|
||||
/**
|
||||
* Updates our cached linear/exponential falloff stuff, saving calculations down the line.
|
||||
*/
|
||||
/datum/song/proc/update_sustain()
|
||||
// Exponential is easy
|
||||
cached_exponential_dropoff = sustain_exponential_dropoff
|
||||
// Linear, not so much, since it's a target duration from 100 volume rather than an exponential rate.
|
||||
var/target_duration = sustain_linear_duration
|
||||
var/volume_diff = max(0, 100 - sustain_dropoff_volume)
|
||||
var/volume_decrease_per_decisecond = volume_diff / target_duration
|
||||
cached_linear_dropoff = volume_decrease_per_decisecond
|
||||
|
||||
/**
|
||||
* Setter for setting output volume.
|
||||
*/
|
||||
/datum/song/proc/set_volume(volume)
|
||||
src.volume = clamp(volume, max(0, min_volume), min(100, max_volume))
|
||||
update_sustain()
|
||||
// We don't want to send the whole payload (song included) just for volume
|
||||
var/datum/tgui/ui = SStgui.get_open_ui(usr, parent, "main")
|
||||
if(ui)
|
||||
ui.push_data(list("volume" = volume), force = TRUE)
|
||||
|
||||
/**
|
||||
* Setter for setting how low the volume has to get before a note is considered "dead" and dropped
|
||||
*/
|
||||
/datum/song/proc/set_dropoff_volume(volume, no_refresh = FALSE)
|
||||
sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100)
|
||||
update_sustain()
|
||||
if(!no_refresh)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/**
|
||||
* Setter for setting exponential falloff factor.
|
||||
*/
|
||||
/datum/song/proc/set_exponential_drop_rate(drop, no_refresh = FALSE)
|
||||
sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX)
|
||||
update_sustain()
|
||||
if(!no_refresh)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/**
|
||||
* Setter for setting linear falloff duration.
|
||||
*/
|
||||
/datum/song/proc/set_linear_falloff_duration(duration, no_refresh = FALSE)
|
||||
sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN)
|
||||
update_sustain()
|
||||
if(!no_refresh)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/datum/song/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(.)
|
||||
switch(var_name)
|
||||
if(NAMEOF(src, volume))
|
||||
set_volume(var_value)
|
||||
if(NAMEOF(src, sustain_dropoff_volume))
|
||||
set_dropoff_volume(var_value)
|
||||
if(NAMEOF(src, sustain_exponential_dropoff))
|
||||
set_exponential_drop_rate(var_value)
|
||||
if(NAMEOF(src, sustain_linear_duration))
|
||||
set_linear_falloff_duration(var_value)
|
||||
|
||||
// subtype for handheld instruments, like violin
|
||||
/datum/song/handheld
|
||||
|
||||
/datum/song/handheld/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
var/obj/item/instrument/I = parent
|
||||
return I.should_stop_playing(user)
|
||||
|
||||
// subtype for stationary structures, like pianos
|
||||
/datum/song/stationary
|
||||
|
||||
/datum/song/stationary/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
var/obj/structure/musician/M = parent
|
||||
return M.should_stop_playing(user)
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/datum/song/tgui_data(mob/user)
|
||||
var/data[0]
|
||||
|
||||
// General
|
||||
data["playing"] = playing
|
||||
data["repeat"] = repeat
|
||||
data["maxRepeats"] = max_repeats
|
||||
data["editing"] = editing
|
||||
data["lines"] = lines
|
||||
data["tempo"] = tempo
|
||||
data["minTempo"] = world.tick_lag
|
||||
data["maxTempo"] = 5 SECONDS
|
||||
data["tickLag"] = world.tick_lag
|
||||
data["help"] = help
|
||||
|
||||
// Status
|
||||
var/list/allowed_instrument_names = list()
|
||||
for(var/i in allowed_instrument_ids)
|
||||
var/datum/instrument/I = SSinstruments.get_instrument(i)
|
||||
if(I)
|
||||
allowed_instrument_names += I.name
|
||||
data["allowedInstrumentNames"] = allowed_instrument_names
|
||||
data["instrumentLoaded"] = !isnull(using_instrument)
|
||||
if(using_instrument)
|
||||
data["instrument"] = using_instrument.name
|
||||
data["canNoteShift"] = can_noteshift
|
||||
if(can_noteshift)
|
||||
data["noteShift"] = note_shift
|
||||
data["noteShiftMin"] = note_shift_min
|
||||
data["noteShiftMax"] = note_shift_max
|
||||
data["sustainMode"] = sustain_mode
|
||||
switch(sustain_mode)
|
||||
if(SUSTAIN_LINEAR)
|
||||
data["sustainLinearDuration"] = sustain_linear_duration
|
||||
if(SUSTAIN_EXPONENTIAL)
|
||||
data["sustainExponentialDropoff"] = sustain_exponential_dropoff
|
||||
data["ready"] = using_instrument?.is_ready()
|
||||
data["legacy"] = legacy
|
||||
data["volume"] = volume
|
||||
data["minVolume"] = min_volume
|
||||
data["maxVolume"] = max_volume
|
||||
data["sustainDropoffVolume"] = sustain_dropoff_volume
|
||||
data["sustainHeldNote"] = full_sustain_held_note
|
||||
|
||||
return data
|
||||
|
||||
/datum/song/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, parent, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, parent, ui_key, "Instrument", parent?.name || "Instrument", 700, 500)
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE) // NO!!! Don't auto-update this!!
|
||||
|
||||
/datum/song/tgui_act(action, params)
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("newsong")
|
||||
lines = new()
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
name = ""
|
||||
if("import")
|
||||
var/t = ""
|
||||
do
|
||||
t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
|
||||
if(!in_range(parent, usr))
|
||||
return
|
||||
|
||||
if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
|
||||
if(cont == "no")
|
||||
break
|
||||
while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
parse_song(t)
|
||||
return FALSE
|
||||
if("help")
|
||||
help = !help
|
||||
if("edit")
|
||||
editing = !editing
|
||||
if("repeat") //Changing this from a toggle to a number of repeats to avoid infinite loops.
|
||||
if(playing)
|
||||
return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
|
||||
repeat = clamp(round(text2num(params["new"])), 0, max_repeats)
|
||||
if("tempo")
|
||||
tempo = sanitize_tempo(text2num(params["new"]))
|
||||
if("play")
|
||||
INVOKE_ASYNC(src, .proc/start_playing, usr)
|
||||
if("newline")
|
||||
var/newline = html_encode(input("Enter your line: ", parent.name) as text|null)
|
||||
if(!newline || !in_range(parent, usr))
|
||||
return
|
||||
if(length(lines) > MUSIC_MAXLINES)
|
||||
return
|
||||
if(length(newline) > MUSIC_MAXLINECHARS)
|
||||
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
|
||||
lines.Add(newline)
|
||||
if("deleteline")
|
||||
var/num = round(text2num(params["line"]))
|
||||
if(num > length(lines) || num < 1)
|
||||
return
|
||||
lines.Cut(num, num + 1)
|
||||
if("modifyline")
|
||||
var/num = round(text2num(params["line"]))
|
||||
var/content = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS)
|
||||
if(!content || !in_range(parent, usr))
|
||||
return
|
||||
if(num > length(lines) || num < 1)
|
||||
return
|
||||
lines[num] = content
|
||||
if("stop")
|
||||
stop_playing()
|
||||
if("setlinearfalloff")
|
||||
set_linear_falloff_duration(round(text2num(params["new"]) * 10, world.tick_lag), TRUE)
|
||||
if("setexpfalloff")
|
||||
set_exponential_drop_rate(round(text2num(params["new"]), 0.00001), TRUE)
|
||||
if("setvolume")
|
||||
set_volume(round(text2num(params["new"]), 1))
|
||||
if("setdropoffvolume")
|
||||
set_dropoff_volume(round(text2num(params["new"]), 0.01), TRUE)
|
||||
if("switchinstrument")
|
||||
if(!length(allowed_instrument_ids))
|
||||
return
|
||||
else if(length(allowed_instrument_ids) == 1)
|
||||
set_instrument(allowed_instrument_ids[1])
|
||||
return
|
||||
var/choice = params["name"]
|
||||
for(var/i in allowed_instrument_ids)
|
||||
var/datum/instrument/I = SSinstruments.get_instrument(i)
|
||||
if(I && I.name == choice)
|
||||
set_instrument(I)
|
||||
if("setnoteshift")
|
||||
note_shift = clamp(round(text2num(params["new"])), note_shift_min, note_shift_max)
|
||||
if("setsustainmode")
|
||||
var/static/list/sustain_modes
|
||||
if(!length(sustain_modes))
|
||||
sustain_modes = list("Linear" = SUSTAIN_LINEAR, "Exponential" = SUSTAIN_EXPONENTIAL)
|
||||
var/choice = params["new"]
|
||||
sustain_mode = sustain_modes[choice] || sustain_mode
|
||||
if("togglesustainhold")
|
||||
full_sustain_held_note = !full_sustain_held_note
|
||||
if("reset")
|
||||
var/default_instrument = allowed_instrument_ids[1]
|
||||
if(using_instrument != SSinstruments.instrument_data[default_instrument])
|
||||
set_instrument(default_instrument)
|
||||
note_shift = initial(note_shift)
|
||||
sustain_mode = initial(sustain_mode)
|
||||
set_linear_falloff_duration(initial(sustain_linear_duration), TRUE)
|
||||
set_exponential_drop_rate(initial(sustain_exponential_dropoff), TRUE)
|
||||
set_dropoff_volume(initial(sustain_dropoff_volume), TRUE)
|
||||
else
|
||||
return FALSE
|
||||
parent.add_fingerprint(usr)
|
||||
|
||||
/**
|
||||
* Parses a song the user has input into lines and stores them.
|
||||
*/
|
||||
/datum/song/proc/parse_song(text)
|
||||
set waitfor = FALSE
|
||||
//split into lines
|
||||
stop_playing()
|
||||
lines = splittext(text, "\n")
|
||||
if(length(lines))
|
||||
var/bpm_string = "BPM: "
|
||||
if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1))
|
||||
var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default
|
||||
tempo = sanitize_tempo(600 / round(divisor, 1))
|
||||
lines.Cut(1, 2)
|
||||
else
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
if(length(lines) > MUSIC_MAXLINES)
|
||||
to_chat(usr, "Too many lines!")
|
||||
lines.Cut(MUSIC_MAXLINES + 1)
|
||||
var/linenum = 1
|
||||
for(var/l in lines)
|
||||
if(length_char(l) > MUSIC_MAXLINECHARS)
|
||||
to_chat(usr, "Line [linenum] too long!")
|
||||
lines.Remove(l)
|
||||
else
|
||||
linenum++
|
||||
SStgui.update_uis(parent)
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
|
||||
*/
|
||||
/datum/song/proc/compile_legacy()
|
||||
if(!length(src.lines))
|
||||
return
|
||||
var/list/lines = src.lines //cache for hyepr speed!
|
||||
compiled_chords = list()
|
||||
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
|
||||
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
|
||||
for(var/line in lines)
|
||||
var/list/chords = splittext(lowertext(line), ",")
|
||||
for(var/chord in chords)
|
||||
var/list/compiled_chord = list()
|
||||
var/tempodiv = 1
|
||||
var/list/notes_tempodiv = splittext(chord, "/")
|
||||
var/len = length(notes_tempodiv)
|
||||
if(len >= 2)
|
||||
tempodiv = text2num(notes_tempodiv[2])
|
||||
if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
|
||||
var/list/notes = splittext(notes_tempodiv[1], "-")
|
||||
for(var/note in notes)
|
||||
if(length(note) == 0)
|
||||
continue
|
||||
// 1-7, A-G
|
||||
var/key = text2ascii(note) - 96
|
||||
if((key < 1) || (key > 7))
|
||||
continue
|
||||
for(var/i in 2 to length(note))
|
||||
var/oct_acc = copytext(note, i, i + 1)
|
||||
var/num = text2num(oct_acc)
|
||||
if(!num) //it's an accidental
|
||||
accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
|
||||
else //octave
|
||||
octaves[key] = clamp(num, octave_min, octave_max)
|
||||
compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key])
|
||||
compiled_chord += tempodiv //this goes last
|
||||
if(length(compiled_chord))
|
||||
compiled_chords[++compiled_chords.len] = compiled_chord
|
||||
|
||||
/**
|
||||
* Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management.
|
||||
*
|
||||
* Arguments:
|
||||
* * note - number from 1-7 for A-G
|
||||
* * acc - either "b", "n", or "#"
|
||||
* * oct - 1-8 (or 9 for C)
|
||||
*/
|
||||
/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user)
|
||||
// handle accidental -> B<>C of E<>F
|
||||
if(acc == "b" && (note == 3 || note == 6)) // C or F
|
||||
if(note == 3)
|
||||
oct--
|
||||
note--
|
||||
acc = "n"
|
||||
else if(acc == "#" && (note == 2 || note == 5)) // B or E
|
||||
if(note == 2)
|
||||
oct++
|
||||
note++
|
||||
acc = "n"
|
||||
else if(acc == "#" && (note == 7)) //G#
|
||||
note = 1
|
||||
acc = "b"
|
||||
else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
|
||||
acc = "b"
|
||||
note++
|
||||
|
||||
// check octave, C is allowed to go to 9
|
||||
if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
|
||||
return
|
||||
|
||||
// now generate name
|
||||
var/filename = "sound/instruments/[cached_legacy_dir]/[ascii2text(note + 64)][acc][oct].[cached_legacy_ext]"
|
||||
var/soundfile = file(filename)
|
||||
// make sure the note exists
|
||||
var/cached_fexists = valid_files[filename]
|
||||
if(!isnull(cached_fexists))
|
||||
if(!cached_fexists)
|
||||
return
|
||||
else if(!fexists(soundfile))
|
||||
valid_files[filename] = FALSE
|
||||
return
|
||||
else
|
||||
valid_files[filename] = TRUE
|
||||
// and play
|
||||
var/turf/source = get_turf(parent)
|
||||
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
|
||||
do_hearcheck()
|
||||
var/sound/music_played = sound(soundfile)
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS))
|
||||
continue
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier, falloff = 5, S = music_played)
|
||||
// Could do environment and echo later but not for now
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
|
||||
*/
|
||||
/datum/song/proc/compile_synthesized()
|
||||
if(!length(src.lines))
|
||||
return
|
||||
var/list/lines = src.lines //cache for hyepr speed!
|
||||
compiled_chords = list()
|
||||
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
|
||||
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
|
||||
for(var/line in lines)
|
||||
var/list/chords = splittext(lowertext(line), ",")
|
||||
for(var/chord in chords)
|
||||
var/list/compiled_chord = list()
|
||||
var/tempodiv = 1
|
||||
var/list/notes_tempodiv = splittext(chord, "/")
|
||||
var/len = length(notes_tempodiv)
|
||||
if(len >= 2)
|
||||
tempodiv = text2num(notes_tempodiv[2])
|
||||
if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
|
||||
var/list/notes = splittext(notes_tempodiv[1], "-")
|
||||
for(var/note in notes)
|
||||
if(length(note) == 0)
|
||||
continue
|
||||
// 1-7, A-G
|
||||
var/key = text2ascii(note) - 96
|
||||
if((key < 1) || (key > 7))
|
||||
continue
|
||||
for(var/i in 2 to length(note))
|
||||
var/oct_acc = copytext(note, i, i + 1)
|
||||
var/num = text2num(oct_acc)
|
||||
if(!num) //it's an accidental
|
||||
accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
|
||||
else //octave
|
||||
octaves[key] = clamp(num, octave_min, octave_max)
|
||||
compiled_chord += clamp((note_offset_lookup[key] + octaves[key] * 12 + accent_lookup[accents[key]]), key_min, key_max)
|
||||
compiled_chord += tempodiv //this goes last
|
||||
if(length(compiled_chord))
|
||||
compiled_chords[++compiled_chords.len] = compiled_chord
|
||||
|
||||
/**
|
||||
* Plays a specific numerical key from our instrument to anyone who can hear us.
|
||||
* Does a hearing check if enough time has passed.
|
||||
*/
|
||||
/datum/song/proc/playkey_synth(key, mob/user)
|
||||
if(can_noteshift)
|
||||
key = clamp(key + note_shift, key_min, key_max)
|
||||
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
|
||||
do_hearcheck()
|
||||
var/datum/instrument_key/K = using_instrument.samples[num2text(key)] //See how fucking easy it is to make a number text? You don't need a complicated 9 line proc!
|
||||
//Should probably add channel limiters here at some point but I don't care right now.
|
||||
var/channel = pop_channel()
|
||||
if(isnull(channel))
|
||||
return FALSE
|
||||
. = TRUE
|
||||
var/sound/copy = sound(K.sample)
|
||||
var/volume = src.volume * using_instrument.volume_multiplier
|
||||
copy.frequency = K.frequency
|
||||
copy.volume = volume
|
||||
var/channel_text = num2text(channel)
|
||||
channels_playing[channel_text] = 100
|
||||
last_channel_played = channel_text
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS))
|
||||
continue
|
||||
M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF)
|
||||
// Could do environment and echo later but not for now
|
||||
|
||||
/**
|
||||
* Stops all sounds we are "responsible" for. Only works in synthesized mode.
|
||||
*/
|
||||
/datum/song/proc/terminate_all_sounds(clear_channels = TRUE)
|
||||
for(var/i in hearing_mobs)
|
||||
terminate_sound_mob(i)
|
||||
if(clear_channels && channels_playing)
|
||||
channels_playing.len = 0
|
||||
channels_idle.len = 0
|
||||
SSinstruments.current_instrument_channels -= using_sound_channels
|
||||
using_sound_channels = 0
|
||||
SSsounds.free_datum_channels(src)
|
||||
|
||||
/**
|
||||
* Stops all sounds we are responsible for in a given person. Only works in synthesized mode.
|
||||
*/
|
||||
/datum/song/proc/terminate_sound_mob(mob/M)
|
||||
for(var/channel in channels_playing)
|
||||
M.stop_sound_channel(text2num(channel))
|
||||
|
||||
/**
|
||||
* Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster.
|
||||
*/
|
||||
/datum/song/proc/pop_channel()
|
||||
if(length(channels_idle)) //just pop one off of here if we have one available
|
||||
. = text2num(channels_idle[1])
|
||||
channels_idle.Cut(1, 2)
|
||||
return
|
||||
if(using_sound_channels >= max_sound_channels)
|
||||
return
|
||||
. = SSinstruments.reserve_instrument_channel(src)
|
||||
if(!isnull(.))
|
||||
using_sound_channels++
|
||||
|
||||
/**
|
||||
* Decays our channels and updates their volumes to mobs who can hear us.
|
||||
*
|
||||
* Arguments:
|
||||
* * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation.
|
||||
*/
|
||||
/datum/song/proc/process_decay(wait_ds)
|
||||
var/linear_dropoff = cached_linear_dropoff * wait_ds
|
||||
var/exponential_dropoff = cached_exponential_dropoff ** wait_ds
|
||||
for(var/channel in channels_playing)
|
||||
if(full_sustain_held_note && (channel == last_channel_played))
|
||||
continue
|
||||
var/current_volume = channels_playing[channel]
|
||||
switch(sustain_mode)
|
||||
if(SUSTAIN_LINEAR)
|
||||
current_volume -= linear_dropoff
|
||||
if(SUSTAIN_EXPONENTIAL)
|
||||
current_volume /= exponential_dropoff
|
||||
channels_playing[channel] = current_volume
|
||||
var/dead = current_volume <= sustain_dropoff_volume
|
||||
var/channelnumber = text2num(channel)
|
||||
if(dead)
|
||||
channels_playing -= channel
|
||||
channels_idle += channel
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.stop_sound_channel(channelnumber)
|
||||
else
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.set_sound_channel_volume(channelnumber, (current_volume * 0.01) * volume * using_instrument.volume_multiplier)
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/instrument/tones
|
||||
name = "Ideal tone"
|
||||
category = "Tones"
|
||||
abstract_type = /datum/instrument/tones
|
||||
|
||||
/datum/instrument/tones/square_wave
|
||||
name = "Ideal square wave"
|
||||
id = "square"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Square.ogg')
|
||||
|
||||
/datum/instrument/tones/sine_wave
|
||||
name = "Ideal sine wave"
|
||||
id = "sine"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sine.ogg')
|
||||
|
||||
/datum/instrument/tones/saw_wave
|
||||
name = "Ideal sawtooth wave"
|
||||
id = "saw"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sawtooth.ogg')
|
||||
@@ -1,104 +1,21 @@
|
||||
/**********************Mining Equipment Locker**************************/
|
||||
// Use this define to register something as a purchasable!
|
||||
// * n — The proper name of the purchasable
|
||||
// * o — The object type path of the purchasable to spawn
|
||||
// * p — The price of the purchasable in mining points
|
||||
#define EQUIPMENT(n, o, p) n = new /datum/data/mining_equipment(n, o, p)
|
||||
|
||||
/**********************Mining Equipment Vendor**************************/
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor
|
||||
name = "mining equipment vendor"
|
||||
desc = "An equipment vendor for miners, points collected at an ore redemption machine can be spent here."
|
||||
icon = 'icons/obj/machines/mining_machines.dmi'
|
||||
icon_state = "mining"
|
||||
density = 1
|
||||
anchored = 1.0
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
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.
|
||||
new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 10),
|
||||
new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100),
|
||||
new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 300),
|
||||
new /datum/data/mining_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100),
|
||||
new /datum/data/mining_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100),
|
||||
new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150),
|
||||
new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200),
|
||||
new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300),
|
||||
new /datum/data/mining_equipment("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300),
|
||||
new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
|
||||
new /datum/data/mining_equipment("Space Cash", /obj/item/stack/spacecash/c1000, 2000),
|
||||
new /datum/data/mining_equipment("Point Transfer Card", /obj/item/card/mining_point_card, 500),
|
||||
new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 400),
|
||||
new /datum/data/mining_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400),
|
||||
new /datum/data/mining_equipment("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500),
|
||||
new /datum/data/mining_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
|
||||
new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500),
|
||||
new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600),
|
||||
new /datum/data/mining_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600),
|
||||
new /datum/data/mining_equipment("Jaunter", /obj/item/wormhole_jaunter, 750),
|
||||
new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750),
|
||||
new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
|
||||
new /datum/data/mining_equipment("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800),
|
||||
new /datum/data/mining_equipment("Resonator", /obj/item/resonator, 800),
|
||||
new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1000),
|
||||
new /datum/data/mining_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000),
|
||||
new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
|
||||
new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript/full, 1500),
|
||||
new /datum/data/mining_equipment("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000),
|
||||
new /datum/data/mining_equipment("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
|
||||
new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
|
||||
new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500),
|
||||
new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
|
||||
new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
|
||||
new /datum/data/mining_equipment("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800),
|
||||
new /datum/data/mining_equipment("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400),
|
||||
new /datum/data/mining_equipment("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400),
|
||||
new /datum/data/mining_equipment("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600),
|
||||
new /datum/data/mining_equipment("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000),
|
||||
new /datum/data/mining_equipment("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100),
|
||||
new /datum/data/mining_equipment("Lazarus Capsule", /obj/item/mobcapsule, 800),
|
||||
new /datum/data/mining_equipment("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200),
|
||||
new /datum/data/mining_equipment("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100),
|
||||
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("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)
|
||||
)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem
|
||||
name = "golem ship equipment vendor"
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/New()
|
||||
..()
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/Initialize()
|
||||
. = ..()
|
||||
desc += "\nIt seems a few selections have been added."
|
||||
prize_list += list(
|
||||
new /datum/data/mining_equipment("Extra Id", /obj/item/card/id/golem, 250),
|
||||
new /datum/data/mining_equipment("Science Backpack", /obj/item/storage/backpack/science, 250),
|
||||
new /datum/data/mining_equipment("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250),
|
||||
new /datum/data/mining_equipment("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250),
|
||||
new /datum/data/mining_equipment("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500),
|
||||
new /datum/data/mining_equipment("Grey Slime Extract", /obj/item/slime_extract/grey, 1000),
|
||||
new /datum/data/mining_equipment("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000),
|
||||
new /datum/data/mining_equipment("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000),
|
||||
new /datum/data/mining_equipment("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000)
|
||||
|
||||
)
|
||||
|
||||
/datum/data/mining_equipment
|
||||
var/equipment_name = "generic"
|
||||
var/equipment_path = null
|
||||
var/cost = 0
|
||||
|
||||
/datum/data/mining_equipment/New(name, path, equipment_cost)
|
||||
equipment_name = name
|
||||
equipment_path = path
|
||||
cost = equipment_cost
|
||||
var/list/prize_list // Initialized just below! (if you're wondering why - check CONTRIBUTING.md, look for: "hidden" init proc)
|
||||
var/dirty_items = FALSE // Used to refresh the static/redundant data in case the machine gets VV'd
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/New()
|
||||
..()
|
||||
@@ -110,6 +27,72 @@
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/Initialize(mapload)
|
||||
. = ..()
|
||||
prize_list = list()
|
||||
prize_list["Gear"] = list(
|
||||
EQUIPMENT("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800),
|
||||
EQUIPMENT("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
|
||||
EQUIPMENT("Fulton Beacon", /obj/item/fulton_core, 400),
|
||||
EQUIPMENT("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript, 1500),
|
||||
EQUIPMENT("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000),
|
||||
EQUIPMENT("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
|
||||
EQUIPMENT("Lazarus Capsule", /obj/item/mobcapsule, 800),
|
||||
EQUIPMENT("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200),
|
||||
EQUIPMENT("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
|
||||
EQUIPMENT("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600),
|
||||
)
|
||||
prize_list["Consumables"] = list(
|
||||
EQUIPMENT("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100),
|
||||
EQUIPMENT("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600),
|
||||
EQUIPMENT("Fulton Pack", /obj/item/extraction_pack, 1000),
|
||||
EQUIPMENT("Jaunter", /obj/item/wormhole_jaunter, 750),
|
||||
EQUIPMENT("Lazarus Injector", /obj/item/lazarus_injector, 1000),
|
||||
EQUIPMENT("Point Transfer Card", /obj/item/card/mining_point_card, 500),
|
||||
EQUIPMENT("Shelter Capsule", /obj/item/survivalcapsule, 400),
|
||||
EQUIPMENT("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
|
||||
EQUIPMENT("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500),
|
||||
)
|
||||
prize_list["Kinetic Accelerator"] = list(
|
||||
EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
|
||||
EQUIPMENT("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150),
|
||||
EQUIPMENT("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000),
|
||||
EQUIPMENT("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
|
||||
EQUIPMENT("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
|
||||
EQUIPMENT("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300),
|
||||
EQUIPMENT("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100),
|
||||
EQUIPMENT("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000),
|
||||
EQUIPMENT("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250),
|
||||
EQUIPMENT("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100),
|
||||
)
|
||||
prize_list["Digging Tools"] = list(
|
||||
EQUIPMENT("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
|
||||
EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
|
||||
EQUIPMENT("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750),
|
||||
EQUIPMENT("Resonator", /obj/item/resonator, 800),
|
||||
EQUIPMENT("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
|
||||
EQUIPMENT("Super Resonator", /obj/item/resonator/upgraded, 2500),
|
||||
)
|
||||
prize_list["Minebot"] = list(
|
||||
EQUIPMENT("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800),
|
||||
EQUIPMENT("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000),
|
||||
EQUIPMENT("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400),
|
||||
EQUIPMENT("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600),
|
||||
EQUIPMENT("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400),
|
||||
)
|
||||
prize_list["Miscellaneous"] = list(
|
||||
EQUIPMENT("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100),
|
||||
EQUIPMENT("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300),
|
||||
EQUIPMENT("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150),
|
||||
EQUIPMENT("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500),
|
||||
EQUIPMENT("Laser Pointer", /obj/item/laser_pointer, 300),
|
||||
EQUIPMENT("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
|
||||
EQUIPMENT("Soap", /obj/item/soap/nanotrasen, 200),
|
||||
EQUIPMENT("Space Cash", /obj/item/stack/spacecash/c1000, 2000),
|
||||
EQUIPMENT("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100),
|
||||
)
|
||||
prize_list["Extra"] = list() // Used in child vendors
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/power_change()
|
||||
..()
|
||||
update_icon()
|
||||
@@ -126,90 +109,126 @@
|
||||
/obj/machinery/mineral/equipment_vendor/attack_hand(mob/user)
|
||||
if(..())
|
||||
return
|
||||
interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/attack_ghost(mob/user)
|
||||
interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/interact(mob/user)
|
||||
user.set_machine(src)
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_data(mob/user)
|
||||
var/list/data[0]
|
||||
|
||||
var/dat
|
||||
dat +="<div class='statusDisplay'>"
|
||||
if(istype(inserted_id))
|
||||
dat += "You have [inserted_id.mining_points] mining points collected. <A href='?src=[UID()];choice=eject'>Eject ID.</A><br>"
|
||||
// ID
|
||||
if(inserted_id)
|
||||
data["has_id"] = TRUE
|
||||
data["id"] = list(
|
||||
"name" = inserted_id.registered_name,
|
||||
"points" = inserted_id.mining_points,
|
||||
)
|
||||
else
|
||||
dat += "No ID inserted. <A href='?src=[UID()];choice=insert'>Insert ID.</A><br>"
|
||||
dat += "</div>"
|
||||
dat += "<br><b>Equipment point cost list:</b><BR><table border='0' width='200'>"
|
||||
for(var/datum/data/mining_equipment/prize in prize_list)
|
||||
dat += "<tr><td>[prize.equipment_name]</td><td>[prize.cost]</td><td><A href='?src=[UID()];purchase=\ref[prize]'>Purchase</A></td></tr>"
|
||||
dat += "</table>"
|
||||
var/datum/browser/popup = new(user, "miningvendor", "Mining Equipment Vendor", 400, 350)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
data["has_id"] = FALSE
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/Topic(href, href_list)
|
||||
return data
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_static_data(mob/user)
|
||||
var/list/static_data[0]
|
||||
|
||||
// Available items - in static data because we don't wanna compute this list every time! It hardly changes.
|
||||
static_data["items"] = list()
|
||||
for(var/cat in prize_list)
|
||||
var/list/cat_items = list()
|
||||
for(var/prize_name in prize_list[cat])
|
||||
var/datum/data/mining_equipment/prize = prize_list[cat][prize_name]
|
||||
cat_items[prize_name] = list("name" = prize_name, "price" = prize.cost)
|
||||
static_data["items"][cat] = cat_items
|
||||
|
||||
return static_data
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/vv_edit_var(var_name, var_value)
|
||||
// Gotta update the static data in case an admin VV's the items for some reason..!
|
||||
if(var_name == "prize_list")
|
||||
dirty_items = TRUE
|
||||
return ..()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
// Update static data if need be
|
||||
if(dirty_items)
|
||||
if(!ui)
|
||||
ui = SStgui.get_open_ui(user, src, ui_key)
|
||||
if(ui) // OK so ui?. somehow breaks the implied src so this is needed
|
||||
ui.initial_static_data = tgui_static_data(user)
|
||||
dirty_items = FALSE
|
||||
|
||||
// Open the window
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "MiningVendor", name, 400, 450)
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_act(action, params)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
if(href_list["choice"])
|
||||
if(istype(inserted_id))
|
||||
if(href_list["choice"] == "eject")
|
||||
inserted_id.loc = loc
|
||||
inserted_id.verb_pickup()
|
||||
inserted_id = null
|
||||
else if(href_list["choice"] == "insert")
|
||||
var/obj/item/card/id/I = usr.get_active_hand()
|
||||
if(istype(I))
|
||||
if(!usr.drop_item())
|
||||
return
|
||||
I.loc = src
|
||||
inserted_id = I
|
||||
else
|
||||
to_chat(usr, "<span class='danger'>No valid ID.</span>")
|
||||
|
||||
if(href_list["purchase"])
|
||||
if(istype(inserted_id))
|
||||
var/datum/data/mining_equipment/prize = locate(href_list["purchase"])
|
||||
if(!prize || !(prize in prize_list) || prize.cost > inserted_id.mining_points)
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("logoff")
|
||||
if(!inserted_id)
|
||||
return
|
||||
usr.put_in_hands(inserted_id)
|
||||
inserted_id = null
|
||||
if("purchase")
|
||||
if(!inserted_id)
|
||||
return
|
||||
var/category = params["cat"] // meow
|
||||
var/name = params["name"]
|
||||
if(!(category in prize_list) || !(name in prize_list[category])) // Not trying something that's not in the list, are you?
|
||||
return
|
||||
var/datum/data/mining_equipment/prize = prize_list[category][name]
|
||||
if(prize.cost > inserted_id.mining_points) // shouldn't be able to access this since the button is greyed out, but..
|
||||
to_chat(usr, "<span class='danger'>You have insufficient points.</span>")
|
||||
return
|
||||
|
||||
inserted_id.mining_points -= prize.cost
|
||||
new prize.equipment_path(src.loc)
|
||||
updateUsrDialog()
|
||||
new prize.equipment_path(loc)
|
||||
else
|
||||
return FALSE
|
||||
add_fingerprint()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/attackby(obj/item/I, mob/user, params)
|
||||
if(default_deconstruction_screwdriver(user, "mining-open", "mining", I))
|
||||
updateUsrDialog()
|
||||
return
|
||||
if(panel_open)
|
||||
if(istype(I, /obj/item/crowbar))
|
||||
if(inserted_id)
|
||||
inserted_id.forceMove(loc) //Prevents deconstructing the ORM from deleting whatever ID was inside it.
|
||||
default_deconstruction_crowbar(user, I)
|
||||
return 1
|
||||
return TRUE
|
||||
if(istype(I, /obj/item/mining_voucher))
|
||||
if(!powered())
|
||||
return
|
||||
else
|
||||
RedeemVoucher(I, user)
|
||||
redeem_voucher(I, user)
|
||||
return
|
||||
if(istype(I,/obj/item/card/id))
|
||||
if(istype(I, /obj/item/card/id))
|
||||
if(!powered())
|
||||
return
|
||||
else
|
||||
var/obj/item/card/id/C = usr.get_active_hand()
|
||||
if(istype(C) && !istype(inserted_id))
|
||||
if(!usr.drop_item())
|
||||
return
|
||||
C.forceMove(src)
|
||||
inserted_id = C
|
||||
interact(user)
|
||||
var/obj/item/card/id/C = user.get_active_hand()
|
||||
if(istype(C) && !istype(inserted_id))
|
||||
if(!user.drop_item())
|
||||
return
|
||||
C.forceMove(src)
|
||||
inserted_id = C
|
||||
tgui_interact(user)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/proc/RedeemVoucher(obj/item/mining_voucher/voucher, mob/redeemer)
|
||||
/**
|
||||
* Called when someone slaps the machine with a mining voucher
|
||||
*
|
||||
* Arguments:
|
||||
* * voucher - The voucher card item
|
||||
* * redeemer - The person holding it
|
||||
*/
|
||||
/obj/machinery/mineral/equipment_vendor/proc/redeem_voucher(obj/item/mining_voucher/voucher, mob/redeemer)
|
||||
var/items = list("Survival Capsule and Explorer's Webbing", "Resonator Kit", "Minebot Kit", "Extraction and Rescue Kit", "Crusher Kit", "Mining Conscription Kit")
|
||||
|
||||
var/selection = input(redeemer, "Pick your equipment", "Mining Voucher Redemption") as null|anything in items
|
||||
@@ -240,11 +259,51 @@
|
||||
qdel(voucher)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/ex_act(severity, target)
|
||||
do_sparks(5, 1, src)
|
||||
do_sparks(5, TRUE, src)
|
||||
if(prob(50 / severity) && severity < 3)
|
||||
qdel(src)
|
||||
|
||||
/**********************Mining Equipment Locker Items**************************/
|
||||
/**********************Mining Equiment Vendor (Golem)**************************/
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem
|
||||
name = "golem ship equipment vendor"
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/New()
|
||||
..()
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/Initialize()
|
||||
. = ..()
|
||||
desc += "\nIt seems a few selections have been added."
|
||||
prize_list["Extra"] += list(
|
||||
EQUIPMENT("Extra ID", /obj/item/card/id/golem, 250),
|
||||
EQUIPMENT("Science Backpack", /obj/item/storage/backpack/science, 250),
|
||||
EQUIPMENT("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250),
|
||||
EQUIPMENT("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250),
|
||||
EQUIPMENT("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500),
|
||||
EQUIPMENT("Grey Slime Extract", /obj/item/slime_extract/grey, 1000),
|
||||
EQUIPMENT("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000),
|
||||
EQUIPMENT("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000),
|
||||
EQUIPMENT("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000),
|
||||
)
|
||||
|
||||
/**********************Mining Equipment Datum**************************/
|
||||
|
||||
/datum/data/mining_equipment
|
||||
var/equipment_name = "generic"
|
||||
var/equipment_path = null
|
||||
var/cost = 0
|
||||
|
||||
/datum/data/mining_equipment/New(name, path, equipment_cost)
|
||||
equipment_name = name
|
||||
equipment_path = path
|
||||
cost = equipment_cost
|
||||
|
||||
/**********************Mining Equipment Voucher**********************/
|
||||
|
||||
@@ -300,3 +359,5 @@
|
||||
/obj/item/storage/backpack/duffel/mining_conscript/full/New()
|
||||
..()
|
||||
new /obj/item/card/id/mining_access_card(src)
|
||||
|
||||
#undef EQUIPMENT
|
||||
|
||||
@@ -220,6 +220,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
|
||||
var/datum/wires/explosive/gibtonite/wires
|
||||
|
||||
/obj/item/twohanded/required/gibtonite/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
QDEL_NULL(wires)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
/mob/dead/observer/say(var/message)
|
||||
/mob/dead/observer/say(message)
|
||||
message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN))
|
||||
|
||||
if(!message)
|
||||
return
|
||||
|
||||
log_ghostsay(message, src)
|
||||
|
||||
if(src.client)
|
||||
if(src.client.prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='warning'>You cannot talk in deadchat (muted).</span>")
|
||||
return
|
||||
|
||||
if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
. = src.say_dead(message)
|
||||
return say_dead(message)
|
||||
|
||||
|
||||
/mob/dead/observer/emote(act, type, message, force)
|
||||
|
||||
@@ -171,7 +171,14 @@
|
||||
|
||||
to_chat(src, heard)
|
||||
|
||||
/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null)
|
||||
/mob/proc/hear_holopad_talk(list/message_pieces, verb = "says", mob/speaker = null)
|
||||
if(sleeping || stat == UNCONSCIOUS)
|
||||
hear_sleep(multilingual_to_message(message_pieces))
|
||||
return
|
||||
|
||||
if(!can_hear())
|
||||
return
|
||||
|
||||
var/message = combine_message(message_pieces, verb, speaker)
|
||||
|
||||
var/name = speaker.name
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
if(prob(80))
|
||||
new_name += " [pick(list("Hadii","Kaytam","Zhan-Khazan","Hharar","Njarir'Akhan"))]"
|
||||
else
|
||||
new_name += ..(gender,1)
|
||||
new_name += " [..(gender,1)]"
|
||||
return new_name
|
||||
|
||||
/datum/language/vulpkanin
|
||||
|
||||
@@ -301,14 +301,13 @@
|
||||
else if(nutrition >= NUTRITION_LEVEL_FAT)
|
||||
msg += "[p_they(TRUE)] [p_are()] quite chubby.\n"
|
||||
|
||||
if(!ismachineperson(src) && blood_volume < BLOOD_VOLUME_SAFE)
|
||||
if(blood_volume < BLOOD_VOLUME_SAFE)
|
||||
msg += "[p_they(TRUE)] [p_have()] pale skin.\n"
|
||||
|
||||
if(bleedsuppress)
|
||||
msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n"
|
||||
else if(bleed_rate)
|
||||
var/bleed_message = !ismachineperson(src) ? "bleeding" : "leaking"
|
||||
msg += "<B>[p_they(TRUE)] [p_are()] [bleed_message]!</B>\n"
|
||||
msg += "<B>[p_they(TRUE)] [p_are()] bleeding!</B>\n"
|
||||
|
||||
if(reagents.has_reagent("teslium"))
|
||||
msg += "[p_they(TRUE)] [p_are()] emitting a gentle blue glow!\n"
|
||||
|
||||
@@ -572,41 +572,34 @@ emp_act
|
||||
if(M.a_intent == INTENT_HARM)
|
||||
if(w_uniform)
|
||||
w_uniform.add_fingerprint(M)
|
||||
var/damage = rand(15, 30)
|
||||
var/damage = prob(90) ? 20 : 0
|
||||
if(!damage)
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] has lunged at [src]!</span>")
|
||||
return 0
|
||||
var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected))
|
||||
var/armor_block = run_armor_check(affecting, "melee")
|
||||
var/armor_block = run_armor_check(affecting, "melee", armour_penetration = 10)
|
||||
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
if(damage >= 25)
|
||||
visible_message("<span class='danger'>[M] has wounded [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has wounded [src]!</span>")
|
||||
apply_effect(4, WEAKEN, armor_block)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
updatehealth("alien attack")
|
||||
|
||||
if(M.a_intent == INTENT_DISARM)
|
||||
if(prob(80))
|
||||
if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead.
|
||||
var/obj/item/I = get_active_hand()
|
||||
if(I && unEquip(I))
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] disarms [src]!</span>", "<span class='userdanger'>[M] disarms you!</span>", "<span class='hear'>You hear aggressive shuffling!</span>")
|
||||
to_chat(M, "<span class='danger'>You disarm [src]!</span>")
|
||||
else
|
||||
var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected))
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
|
||||
apply_effect(5, WEAKEN, run_armor_check(affecting, "melee"))
|
||||
add_attack_logs(M, src, "Alien tackled")
|
||||
visible_message("<span class='danger'>[M] has tackled down [src]!</span>")
|
||||
else
|
||||
if(prob(99)) //this looks fucking stupid but it was previously 'var/randn = rand(1, 100); if(randn <= 99)'
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
|
||||
drop_item()
|
||||
visible_message("<span class='danger'>[M] disarmed [src]!</span>")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has tried to disarm [src]!</span>")
|
||||
|
||||
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M)
|
||||
. = ..()
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
..()
|
||||
H.gender = NEUTER
|
||||
|
||||
/datum/species/diona/on_species_loss(mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
H.clear_alert("nolight")
|
||||
|
||||
/datum/species/diona/handle_reagents(mob/living/carbon/human/H, datum/reagent/R)
|
||||
if(R.id == "glyphosate" || R.id == "atrazine")
|
||||
H.adjustToxLoss(3) //Deal aditional damage
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
death_message = "gives a short series of shrill beeps, their chassis shuddering before falling limp, nonfunctional."
|
||||
death_sounds = list('sound/voice/borg_deathsound.ogg') //I've made this a list in the event we add more sounds for dead robots.
|
||||
|
||||
species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie!
|
||||
species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_BLOOD, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie!
|
||||
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
|
||||
bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS
|
||||
dietflags = 0 //IPCs can't eat, so no diet
|
||||
@@ -30,10 +30,6 @@
|
||||
blood_color = "#1F181F"
|
||||
flesh_color = "#AAAAAA"
|
||||
|
||||
blood_color = "#3C3C3C"
|
||||
exotic_blood = "oil"
|
||||
blood_damage_type = STAMINA
|
||||
|
||||
//Default styles for created mobs.
|
||||
default_hair = "Blue IPC Screen"
|
||||
dies_at_threshold = TRUE
|
||||
|
||||
@@ -351,8 +351,6 @@ proc/get_radio_key_from_channel(var/channel)
|
||||
return
|
||||
|
||||
if(stat)
|
||||
if(stat == DEAD)
|
||||
return say_dead(message_pieces)
|
||||
return
|
||||
|
||||
if(is_muzzled())
|
||||
|
||||
@@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
var/list/connected_robots = list()
|
||||
var/aiRestorePowerRoutine = 0
|
||||
//var/list/laws = list()
|
||||
var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list())
|
||||
alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera", "Burglar")
|
||||
var/viewalerts = 0
|
||||
var/icon/holo_icon//Default is assigned when AI is created.
|
||||
var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
|
||||
@@ -173,19 +173,19 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
add_language("Galactic Common", 1)
|
||||
add_language("Sol Common", 1)
|
||||
add_language("Tradeband", 1)
|
||||
add_language("Neo-Russkiya", 0)
|
||||
add_language("Gutter", 0)
|
||||
add_language("Sinta'unathi", 0)
|
||||
add_language("Siik'tajr", 0)
|
||||
add_language("Canilunzt", 0)
|
||||
add_language("Skrellian", 0)
|
||||
add_language("Vox-pidgin", 0)
|
||||
add_language("Orluum", 0)
|
||||
add_language("Rootspeak", 0)
|
||||
add_language("Neo-Russkiya", 1)
|
||||
add_language("Gutter", 1)
|
||||
add_language("Sinta'unathi", 1)
|
||||
add_language("Siik'tajr", 1)
|
||||
add_language("Canilunzt", 1)
|
||||
add_language("Skrellian", 1)
|
||||
add_language("Vox-pidgin", 1)
|
||||
add_language("Orluum", 1)
|
||||
add_language("Rootspeak", 1)
|
||||
add_language("Trinary", 1)
|
||||
add_language("Chittin", 0)
|
||||
add_language("Bubblish", 0)
|
||||
add_language("Clownish", 0)
|
||||
add_language("Chittin", 1)
|
||||
add_language("Bubblish", 1)
|
||||
add_language("Clownish", 1)
|
||||
|
||||
if(!safety)//Only used by AIize() to successfully spawn an AI.
|
||||
if(!B)//If there is no player/brain inside.
|
||||
@@ -242,6 +242,46 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
return
|
||||
show_borg_info()
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_alerts()
|
||||
var/list/dat = list("<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n")
|
||||
dat += "<A HREF='?src=[UID()];mach_close=aialerts'>Close</A><BR><BR>"
|
||||
var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
|
||||
for(var/cat in temp_alarm_list)
|
||||
if(!(cat in alarms_listend_for))
|
||||
continue
|
||||
dat += text("<B>[]</B><BR>\n", cat)
|
||||
var/list/list/L = temp_alarm_list[cat].Copy()
|
||||
for(var/alarm in L)
|
||||
var/list/list/alm = L[alarm].Copy()
|
||||
var/area_name = alm[1]
|
||||
var/C = alm[2]
|
||||
var/list/list/sources = alm[3].Copy()
|
||||
for(var/thing in sources)
|
||||
var/atom/A = locateUID(thing)
|
||||
if(A && A.z != z)
|
||||
L -= alarm
|
||||
continue
|
||||
dat += "<NOBR>"
|
||||
if(C && islist(C))
|
||||
var/dat2 = ""
|
||||
for(var/cam in C)
|
||||
var/obj/machinery/camera/I = locateUID(cam)
|
||||
if(!QDELETED(I))
|
||||
dat2 += text("[]<A HREF=?src=[UID()];switchcamera=[cam]>[]</A>", (dat2 == "") ? "" : " | ", I.c_tag)
|
||||
dat += text("-- [] ([])", area_name, (dat2 != "") ? dat2 : "No Camera")
|
||||
else
|
||||
dat += text("-- [] (No Camera)", area_name)
|
||||
if(sources.len > 1)
|
||||
dat += text("- [] sources", sources.len)
|
||||
dat += "</NOBR><BR>\n"
|
||||
if(!L.len)
|
||||
dat += "-- All Systems Nominal<BR>\n"
|
||||
dat += "<BR>\n"
|
||||
|
||||
viewalerts = TRUE
|
||||
var/dat_text = dat.Join("")
|
||||
src << browse(dat_text, "window=aialerts&can_close=0")
|
||||
|
||||
/mob/living/silicon/ai/proc/show_borg_info()
|
||||
stat(null, text("Connected cyborgs: [connected_robots.len]"))
|
||||
for(var/thing in connected_robots)
|
||||
@@ -612,7 +652,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
if(href_list["switchcamera"])
|
||||
switchCamera(locate(href_list["switchcamera"])) in GLOB.cameranet.cameras
|
||||
if(href_list["showalerts"])
|
||||
subsystem_alarm_monitor()
|
||||
ai_alerts()
|
||||
if(href_list["show_paper"])
|
||||
if(last_paper_seen)
|
||||
src << browse(last_paper_seen, "window=show_paper")
|
||||
@@ -787,12 +827,49 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
|
||||
Bot.call_bot(src, waypoint)
|
||||
|
||||
/mob/living/silicon/ai/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(alarmsource.z != z)
|
||||
return
|
||||
if(stat == DEAD)
|
||||
return TRUE
|
||||
if(O)
|
||||
var/obj/machinery/camera/C = locateUID(O[1])
|
||||
if(O.len == 1 && !QDELETED(C) && C.can_use())
|
||||
queueAlarm("--- [class] alarm detected in [A.name]! (<A HREF=?src=[UID()];switchcamera=[O[1]]>[C.c_tag]</A>)", class)
|
||||
else if(O && O.len)
|
||||
var/foo = 0
|
||||
var/dat2 = ""
|
||||
for(var/thing in O)
|
||||
var/obj/machinery/camera/I = locateUID(thing)
|
||||
if(!QDELETED(I))
|
||||
dat2 += text("[]<A HREF=?src=[UID()];switchcamera=[thing]>[]</A>", (!foo) ? "" : " | ", I.c_tag) //I'm not fixing this shit...
|
||||
foo = 1
|
||||
queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class)
|
||||
else
|
||||
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
|
||||
else
|
||||
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
|
||||
if(viewalerts)
|
||||
ai_alerts()
|
||||
|
||||
/mob/living/silicon/ai/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
if(cleared)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(origin.z != z)
|
||||
return
|
||||
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
|
||||
if(viewalerts)
|
||||
ai_alerts()
|
||||
|
||||
/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C)
|
||||
|
||||
if(!tracking)
|
||||
cameraFollow = null
|
||||
|
||||
if(!C || stat == DEAD) //C.can_use())
|
||||
if(QDELETED(C) || stat == DEAD) //C.can_use())
|
||||
return FALSE
|
||||
|
||||
if(!eyeobj)
|
||||
|
||||
@@ -132,8 +132,6 @@
|
||||
sleep(50)
|
||||
theAPC = null
|
||||
|
||||
process_queued_alarms()
|
||||
|
||||
/mob/living/silicon/ai/updatehealth(reason = "none given")
|
||||
if(status_flags & GODMODE)
|
||||
health = 100
|
||||
|
||||
@@ -563,6 +563,10 @@
|
||||
var/obj/item/holder/H = ..()
|
||||
if(!istype(H))
|
||||
return
|
||||
if(stat == DEAD)
|
||||
H.icon = 'icons/mob/pai.dmi'
|
||||
H.icon_state = "[chassis]_dead"
|
||||
return
|
||||
if(resting)
|
||||
icon_state = "[chassis]"
|
||||
resting = 0
|
||||
|
||||
@@ -312,8 +312,3 @@ proc/robot_healthscan(mob/user, mob/living/M)
|
||||
to_chat(user, "[capitalize(O.name)]: <font color='red'>[O.damage]</font>")
|
||||
if(!organ_found)
|
||||
to_chat(user, "<span class='warning'>No prosthetics located.</span>")
|
||||
|
||||
if(ismachineperson(H))
|
||||
to_chat(user, "<span class='notice'>Internal Fluid Level:[H.blood_volume]/[H.max_blood]</span>")
|
||||
if(H.bleed_rate)
|
||||
to_chat(user, "<span class='warning'>Warning:External component leak detected!</span>")
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
emote("deathgasp", force = TRUE)
|
||||
|
||||
if(module)
|
||||
module.handle_death(gibbed)
|
||||
module.handle_death(src, gibbed)
|
||||
|
||||
// Only execute the below if we successfully died
|
||||
. = ..(gibbed)
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
// Disable the microphone wire on Drones
|
||||
if(radio)
|
||||
radio.wires.CutWireIndex(RADIO_WIRE_TRANSMIT)
|
||||
radio.wires.cut(WIRE_RADIO_TRANSMIT)
|
||||
|
||||
if(camera && ("Robots" in camera.network))
|
||||
camera.network.Add("Engineering")
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
handle_robot_cell()
|
||||
process_locks()
|
||||
update_items()
|
||||
process_queued_alarms()
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_robot_cell()
|
||||
@@ -46,7 +45,7 @@
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_equipment()
|
||||
if(camera && !scrambledcodes)
|
||||
if(stat == DEAD || wires.IsCameraCut())
|
||||
if(stat == DEAD || wires.is_cut(WIRE_BORG_CAMERA))
|
||||
camera.status = 0
|
||||
else
|
||||
camera.status = 1
|
||||
|
||||
@@ -59,7 +59,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
var/ear_protection = 0
|
||||
var/damage_protection = 0
|
||||
var/emp_protection = FALSE
|
||||
var/xeno_disarm_chance = 85
|
||||
|
||||
var/list/force_modules = list()
|
||||
var/allow_rename = TRUE
|
||||
@@ -72,11 +71,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
var/list/req_access
|
||||
var/ident = 0
|
||||
//var/list/laws = list()
|
||||
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list())
|
||||
var/viewalerts = 0
|
||||
var/modtype = "Default"
|
||||
var/lower_mod = 0
|
||||
var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N
|
||||
var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever/N
|
||||
var/jeton = 0
|
||||
var/low_power_mode = 0 //whether the robot has no charge left.
|
||||
var/weapon_lock = 0
|
||||
@@ -112,7 +110,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
/mob/living/silicon/robot/get_cell()
|
||||
return cell
|
||||
|
||||
/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
spark_system = new /datum/effect_system/spark_spread()
|
||||
spark_system.set_up(5, 0, src)
|
||||
spark_system.attach(src)
|
||||
@@ -134,13 +132,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
radio = new /obj/item/radio/borg(src)
|
||||
common_radio = radio
|
||||
|
||||
init(ai_to_sync_to = ai_to_sync_to)
|
||||
init(alien, connect_to_AI, ai_to_sync_to)
|
||||
|
||||
if(has_camera && !camera)
|
||||
camera = new /obj/machinery/camera(src)
|
||||
camera.c_tag = real_name
|
||||
camera.network = list("SS13","Robots")
|
||||
if(wires.IsCameraCut()) // 5 = BORG CAMERA
|
||||
if(wires.is_cut(WIRE_BORG_CAMERA)) // 5 = BORG CAMERA
|
||||
camera.status = 0
|
||||
|
||||
if(mmi == null)
|
||||
@@ -172,18 +170,20 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
scanner = new(src)
|
||||
scanner.Grant(src)
|
||||
|
||||
/mob/living/silicon/robot/proc/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
||||
make_laws()
|
||||
additional_law_channels["Binary"] = ":b "
|
||||
if(!connect_to_AI)
|
||||
return
|
||||
var/found_ai = ai_to_sync_to
|
||||
if(!found_ai)
|
||||
found_ai = select_active_ai_with_fewest_borgs()
|
||||
if(found_ai)
|
||||
lawupdate = 1
|
||||
lawupdate = TRUE
|
||||
connect_to_ai(found_ai)
|
||||
else
|
||||
lawupdate = 0
|
||||
lawupdate = FALSE
|
||||
|
||||
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
|
||||
|
||||
@@ -270,6 +270,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
|
||||
//Improved /N
|
||||
/mob/living/silicon/robot/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
|
||||
var/turf/T = get_turf(loc)//To hopefully prevent run time errors.
|
||||
if(T) mmi.loc = T
|
||||
@@ -543,6 +544,43 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
src.verbs -= GLOB.robot_verbs_default
|
||||
src.verbs -= silicon_subsystems
|
||||
|
||||
/mob/living/silicon/robot/verb/cmd_robot_alerts()
|
||||
set category = "Robot Commands"
|
||||
set name = "Show Alerts"
|
||||
if(usr.stat == DEAD)
|
||||
to_chat(src, "<span class='userdanger'>Alert: You are dead.</span>")
|
||||
return //won't work if dead
|
||||
robot_alerts()
|
||||
|
||||
/mob/living/silicon/robot/proc/robot_alerts()
|
||||
var/list/dat = list()
|
||||
var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
|
||||
for(var/cat in temp_alarm_list)
|
||||
if(!(cat in alarms_listend_for))
|
||||
continue
|
||||
dat += text("<B>[cat]</B><BR>\n")
|
||||
var/list/list/L = temp_alarm_list[cat].Copy()
|
||||
for(var/alarm in L)
|
||||
var/list/list/alm = L[alarm].Copy()
|
||||
var/list/list/sources = alm[3].Copy()
|
||||
var/area_name = alm[1]
|
||||
for(var/thing in sources)
|
||||
var/atom/A = locateUID(thing)
|
||||
if(A && A.z != z)
|
||||
L -= alarm
|
||||
continue
|
||||
dat += "<NOBR>"
|
||||
dat += text("-- [area_name]")
|
||||
dat += "</NOBR><BR>\n"
|
||||
if(!L.len)
|
||||
dat += "-- All Systems Nominal<BR>\n"
|
||||
dat += "<BR>\n"
|
||||
|
||||
var/datum/browser/alerts = new(usr, "robotalerts", "Current Station Alerts", 400, 410)
|
||||
var/dat_text = dat.Join("")
|
||||
alerts.set_content(dat_text)
|
||||
alerts.open()
|
||||
|
||||
/mob/living/silicon/robot/proc/ionpulse()
|
||||
if(!ionpulse_on)
|
||||
return
|
||||
@@ -604,6 +642,23 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
/mob/living/silicon/robot/InCritical()
|
||||
return low_power_mode
|
||||
|
||||
/mob/living/silicon/robot/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(alarmsource.z != z)
|
||||
return
|
||||
if(stat == DEAD)
|
||||
return
|
||||
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
|
||||
|
||||
/mob/living/silicon/robot/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
if(cleared)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(origin.z != z)
|
||||
return
|
||||
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
|
||||
|
||||
/mob/living/silicon/robot/ex_act(severity)
|
||||
switch(severity)
|
||||
if(1.0)
|
||||
@@ -795,7 +850,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
opened = FALSE
|
||||
update_icons()
|
||||
return
|
||||
else if(wiresexposed && wires.IsAllCut())
|
||||
else if(wiresexposed && wires.is_all_cut())
|
||||
//Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob.
|
||||
if(!mmi)
|
||||
to_chat(user, "[src] has no brain to remove.")
|
||||
@@ -1023,10 +1078,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
src << browse(null, t1)
|
||||
return 1
|
||||
|
||||
if(href_list["showalerts"])
|
||||
subsystem_alarm_monitor()
|
||||
return 1
|
||||
|
||||
if(href_list["mod"])
|
||||
var/obj/item/O = locate(href_list["mod"])
|
||||
if(istype(O) && (O.loc == src))
|
||||
@@ -1041,6 +1092,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
activate_module(O)
|
||||
installed_modules()
|
||||
|
||||
//Show alerts window if user clicked on "Show alerts" in chat
|
||||
if(href_list["showalerts"])
|
||||
robot_alerts()
|
||||
return TRUE
|
||||
|
||||
if(href_list["deact"])
|
||||
var/obj/item/O = locate(href_list["deact"])
|
||||
if(activated(O))
|
||||
@@ -1233,7 +1289,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
|
||||
/mob/living/silicon/robot/proc/SetLockdown(var/state = 1)
|
||||
// They stay locked down if their wire is cut.
|
||||
if(wires.LockedCut())
|
||||
if(wires.is_cut(WIRE_BORG_LOCKED))
|
||||
state = 1
|
||||
if(state)
|
||||
throw_alert("locked", /obj/screen/alert/locked)
|
||||
@@ -1343,14 +1399,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
eye_protection = 2 // Immunity to flashes and the visual part of flashbangs
|
||||
ear_protection = 1 // Immunity to the audio part of flashbangs
|
||||
damage_protection = 10 // Reduce all incoming damage by this number
|
||||
xeno_disarm_chance = 20
|
||||
allow_rename = FALSE
|
||||
modtype = "Commando"
|
||||
faction = list("nanotrasen")
|
||||
is_emaggable = FALSE
|
||||
default_cell_type = /obj/item/stock_parts/cell/bluespace
|
||||
|
||||
/mob/living/silicon/robot/deathsquad/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/deathsquad/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
laws = new /datum/ai_laws/deathsquad
|
||||
module = new /obj/item/robot_module/deathsquad(src)
|
||||
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
||||
@@ -1380,7 +1435,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
var/eprefix = "Amber"
|
||||
|
||||
|
||||
/mob/living/silicon/robot/ert/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/ert/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
laws = new /datum/ai_laws/ert_override
|
||||
radio = new /obj/item/radio/borg/ert(src)
|
||||
radio.recalculateChannels()
|
||||
@@ -1413,7 +1468,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
damage_protection = 5 // Reduce all incoming damage by this number
|
||||
eprefix = "Gamma"
|
||||
magpulse = 1
|
||||
xeno_disarm_chance = 40
|
||||
|
||||
|
||||
/mob/living/silicon/robot/destroyer
|
||||
@@ -1433,10 +1487,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
ear_protection = 1 // Immunity to the audio part of flashbangs
|
||||
emp_protection = TRUE // Immunity to EMP, due to heavy shielding
|
||||
damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg.
|
||||
xeno_disarm_chance = 10
|
||||
default_cell_type = /obj/item/stock_parts/cell/bluespace
|
||||
|
||||
/mob/living/silicon/robot/destroyer/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/destroyer/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
||||
additional_law_channels["Binary"] = ":b "
|
||||
laws = new /datum/ai_laws/deathsquad
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
if(M.a_intent == INTENT_DISARM)
|
||||
if(!lying)
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
if(prob(xeno_disarm_chance))
|
||||
Stun(7)
|
||||
step(src, get_dir(M,src))
|
||||
spawn(5)
|
||||
step(src, get_dir(M,src))
|
||||
add_attack_logs(M, src, "Alien pushed over")
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has forced back [src]!</span>",\
|
||||
"<span class='userdanger'>[M] has forced back [src]!</span>")
|
||||
var/obj/item/I = get_active_hand()
|
||||
if(I)
|
||||
uneq_active()
|
||||
visible_message("<span class='danger'>[M] disarmed [src]!</span>", "<span class='userdanger'>[M] has disabled [src]'s active module!</span>")
|
||||
add_attack_logs(M, src, "alien disarmed")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] took a swipe at [src]!</span>",\
|
||||
"<span class='userdanger'>[M] took a swipe at [src]!</span>")
|
||||
Stun(2)
|
||||
step(src, get_dir(M,src))
|
||||
add_attack_logs(M, src, "Alien pushed over")
|
||||
visible_message("<span class='danger'>[M] forces back [src]!</span>", "<span class='userdanger'>[M] forces back [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 50, TRUE, -1)
|
||||
else
|
||||
..()
|
||||
return
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W)
|
||||
return FALSE
|
||||
|
||||
/obj/item/robot_module/proc/handle_death(gibbed)
|
||||
/obj/item/robot_module/proc/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
return
|
||||
|
||||
/obj/item/robot_module/standard
|
||||
@@ -255,7 +255,7 @@
|
||||
|
||||
fix_modules()
|
||||
|
||||
/obj/item/robot_module/engineering/handle_death()
|
||||
/obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
var/obj/item/gripper/G = locate(/obj/item/gripper) in modules
|
||||
if(G)
|
||||
G.drop_gripped_item(silent = TRUE)
|
||||
@@ -368,6 +368,11 @@
|
||||
R.add_language("Clownish",1)
|
||||
R.add_language("Neo-Russkiya", 1)
|
||||
|
||||
/obj/item/robot_module/butler/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
var/obj/item/storage/bag/tray/cyborg/T = locate(/obj/item/storage/bag/tray/cyborg) in modules
|
||||
if(istype(T))
|
||||
T.drop_inventory(R)
|
||||
|
||||
|
||||
/obj/item/robot_module/miner
|
||||
name = "miner robot module"
|
||||
@@ -623,7 +628,7 @@
|
||||
..()
|
||||
|
||||
|
||||
/obj/item/robot_module/drone/handle_death()
|
||||
/obj/item/robot_module/drone/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
var/obj/item/gripper/G = locate(/obj/item/gripper) in modules
|
||||
if(G)
|
||||
G.drop_gripped_item(silent = TRUE)
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
var/list/stating_laws = list()// Channels laws are currently being stated on
|
||||
var/list/alarms_to_show = list()
|
||||
var/list/alarms_to_clear = list()
|
||||
var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
var/list/alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera")
|
||||
//var/list/hud_list[10]
|
||||
var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer
|
||||
var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to
|
||||
var/designation = ""
|
||||
var/obj/item/camera/siliconcam/aiCamera = null //photography
|
||||
//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented.
|
||||
@@ -25,9 +27,6 @@
|
||||
|
||||
//var/sensor_mode = 0 //Determines the current HUD.
|
||||
|
||||
var/next_alarm_notice
|
||||
var/list/datum/alarm/queued_alarms = new()
|
||||
|
||||
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD)
|
||||
|
||||
|
||||
@@ -46,6 +45,8 @@
|
||||
diag_hud_set_health()
|
||||
add_language("Galactic Common")
|
||||
init_subsystems()
|
||||
RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
|
||||
RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
|
||||
|
||||
/mob/living/silicon/med_hud_set_health()
|
||||
return //we use a different hud
|
||||
@@ -55,10 +56,93 @@
|
||||
|
||||
/mob/living/silicon/Destroy()
|
||||
GLOB.silicon_mob_list -= src
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.unregister(src)
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/queueAlarm(message, type, incoming = TRUE)
|
||||
var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
|
||||
if(incoming)
|
||||
alarms_to_show += message
|
||||
alarm_types_show[type] += 1
|
||||
else
|
||||
alarms_to_clear += message
|
||||
alarm_types_clear[type] += 1
|
||||
|
||||
if(in_cooldown)
|
||||
return
|
||||
|
||||
addtimer(CALLBACK(src, .proc/show_alarms), 3 SECONDS)
|
||||
|
||||
/mob/living/silicon/proc/show_alarms()
|
||||
if(alarms_to_show.len < 5)
|
||||
for(var/msg in alarms_to_show)
|
||||
to_chat(src, msg)
|
||||
else if(length(alarms_to_show))
|
||||
|
||||
var/list/msg = list("--- ")
|
||||
|
||||
if(alarm_types_show["Burglar"])
|
||||
msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Motion"])
|
||||
msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Fire"])
|
||||
msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Power"])
|
||||
msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - "
|
||||
|
||||
msg += "<A href=?src=[UID()];showalerts=1'>\[Show Alerts\]</a>"
|
||||
var/msg_text = msg.Join("")
|
||||
to_chat(src, msg_text)
|
||||
|
||||
if(alarms_to_clear.len < 3)
|
||||
for(var/msg in alarms_to_clear)
|
||||
to_chat(src, msg)
|
||||
|
||||
else if(alarms_to_clear.len)
|
||||
var/list/msg = list("--- ")
|
||||
|
||||
if(alarm_types_clear["Motion"])
|
||||
msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Fire"])
|
||||
msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Power"])
|
||||
msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - "
|
||||
|
||||
msg += "<A href=?src=[UID()];showalerts=1'>\[Show Alerts\]</a>"
|
||||
|
||||
var/msg_text = msg.Join("")
|
||||
to_chat(src, msg_text)
|
||||
|
||||
|
||||
alarms_to_show.Cut()
|
||||
alarms_to_clear.Cut()
|
||||
for(var/key in alarm_types_show)
|
||||
alarm_types_show[key] = 0
|
||||
for(var/key in alarm_types_clear)
|
||||
alarm_types_clear[key] = 0
|
||||
|
||||
/mob/living/silicon/rename_character(oldname, newname)
|
||||
// we actually don't want it changing minds and stuff
|
||||
if(!newname)
|
||||
@@ -283,63 +367,6 @@
|
||||
if("Disable")
|
||||
to_chat(src, "Sensor augmentations disabled.")
|
||||
|
||||
/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised)
|
||||
if(!next_alarm_notice)
|
||||
next_alarm_notice = world.time + 10 SECONDS
|
||||
|
||||
var/list/alarms = queued_alarms[alarm_handler]
|
||||
if(was_raised)
|
||||
// Raised alarms are always set
|
||||
alarms[alarm] = 1
|
||||
else
|
||||
// Alarms that were raised but then cleared before the next notice are instead removed
|
||||
if(alarm in alarms)
|
||||
alarms -= alarm
|
||||
// And alarms that have only been cleared thus far are set as such
|
||||
else
|
||||
alarms[alarm] = -1
|
||||
|
||||
/mob/living/silicon/proc/process_queued_alarms()
|
||||
if(next_alarm_notice && (world.time > next_alarm_notice))
|
||||
next_alarm_notice = 0
|
||||
|
||||
var/alarm_raised = 0
|
||||
for(var/datum/alarm_handler/AH in queued_alarms)
|
||||
var/list/alarms = queued_alarms[AH]
|
||||
var/reported = 0
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(alarms[A] == 1)
|
||||
if(!reported)
|
||||
reported = 1
|
||||
to_chat(src, "<span class='warning'>--- [AH.category] Detected ---</span>")
|
||||
raised_alarm(A)
|
||||
|
||||
for(var/datum/alarm_handler/AH in queued_alarms)
|
||||
var/list/alarms = queued_alarms[AH]
|
||||
var/reported = 0
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(alarms[A] == -1)
|
||||
if(!reported)
|
||||
reported = 1
|
||||
to_chat(src, "<span class='notice'>--- [AH.category] Cleared ---</span>")
|
||||
to_chat(src, "\The [A.alarm_name()].")
|
||||
|
||||
if(alarm_raised)
|
||||
to_chat(src, "<A HREF=?src=[UID()];showalerts=1>\[Show Alerts\]</A>")
|
||||
|
||||
for(var/datum/alarm_handler/AH in queued_alarms)
|
||||
var/list/alarms = queued_alarms[AH]
|
||||
alarms.Cut()
|
||||
|
||||
/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A)
|
||||
to_chat(src, "[A.alarm_name()]!")
|
||||
|
||||
/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A)
|
||||
var/cameratext = ""
|
||||
for(var/obj/machinery/camera/C in A.cameras())
|
||||
cameratext += "[(cameratext == "")? "" : "|"]<A HREF=?src=[UID()];switchcamera=\ref[C]>[C.c_tag]</A>"
|
||||
to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])")
|
||||
|
||||
/mob/living/silicon/adjustToxLoss(var/amount)
|
||||
return STATUS_UPDATE_NONE
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
/mob/living/silicon/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(..()) //if harm or disarm intent
|
||||
var/damage = rand(10, 20)
|
||||
var/damage = 20
|
||||
if(prob(90))
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", "<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
if(prob(8))
|
||||
flash_eyes(affect_silicon = 1)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
@@ -64,6 +63,6 @@
|
||||
else
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
|
||||
playsound(loc, 'sound/effects/bang.ogg', 10, 1)
|
||||
visible_message("<span class='danger'>[M] punches [src], but doesn't leave a dent.</span>", \
|
||||
"<span class='userdanger'>[M] punches [src], but doesn't leave a dent.!</span>")
|
||||
visible_message("<span class='notice'>[M] punches [src], but doesn't leave a dent.</span>", \
|
||||
"<span class='notice'>[M] punches [src], but doesn't leave a dent.</span>")
|
||||
return FALSE
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
|
||||
/mob/living/silicon
|
||||
var/list/silicon_subsystems = list(
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor,
|
||||
/mob/living/silicon/proc/subsystem_law_manager
|
||||
)
|
||||
|
||||
/mob/living/silicon/ai
|
||||
silicon_subsystems = list(
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor,
|
||||
/mob/living/silicon/proc/subsystem_atmos_control,
|
||||
/mob/living/silicon/proc/subsystem_crew_monitor,
|
||||
/mob/living/silicon/proc/subsystem_law_manager,
|
||||
@@ -23,7 +21,6 @@
|
||||
|
||||
/mob/living/silicon/robot/drone
|
||||
silicon_subsystems = list(
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor,
|
||||
/mob/living/silicon/proc/subsystem_law_manager,
|
||||
/mob/living/silicon/proc/subsystem_power_monitor
|
||||
)
|
||||
@@ -32,30 +29,11 @@
|
||||
register_alarms = 0
|
||||
|
||||
/mob/living/silicon/proc/init_subsystems()
|
||||
alarm_monitor = new(src)
|
||||
atmos_control = new(src)
|
||||
crew_monitor = new(src)
|
||||
law_manager = new(src)
|
||||
power_monitor = new(src)
|
||||
|
||||
if(!register_alarms)
|
||||
return
|
||||
|
||||
var/list/register_to = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
|
||||
for(var/datum/alarm_handler/AH in register_to)
|
||||
AH.register(src, /mob/living/silicon/proc/receive_alarm)
|
||||
queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
|
||||
alarm_handlers |= AH
|
||||
|
||||
/********************
|
||||
* Alarm Monitor *
|
||||
********************/
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor()
|
||||
set name = "Alarm Monitor"
|
||||
set category = "Subsystems"
|
||||
|
||||
alarm_monitor.ui_interact(usr, state = GLOB.self_state)
|
||||
|
||||
/********************
|
||||
* Atmos Control *
|
||||
********************/
|
||||
|
||||
@@ -42,13 +42,18 @@
|
||||
|
||||
/mob/living/simple_animal/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(..()) //if harm or disarm intent.
|
||||
var/damage = rand(15, 30)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
attack_threshold_check(damage)
|
||||
return
|
||||
if(M.a_intent == INTENT_DISARM)
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] [response_disarm] [name]!</span>", "<span class='userdanger'>[M] [response_disarm] you!</span>")
|
||||
add_attack_logs(M, src, "Alien disarmed")
|
||||
else
|
||||
var/damage = rand(15, 30)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
attack_threshold_check(damage)
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
if(..()) //successful larva bite
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/human_squish_check)
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
unload(0)
|
||||
QDEL_NULL(wires)
|
||||
QDEL_NULL(cell)
|
||||
@@ -142,7 +143,7 @@
|
||||
if(open)
|
||||
icon_state="mulebot-hatch"
|
||||
else
|
||||
icon_state = "mulebot[!wires.MobAvoid()]"
|
||||
icon_state = "mulebot[wires.is_cut(WIRE_MOB_AVOIDANCE)]"
|
||||
overlays.Cut()
|
||||
if(load && !ismob(load))//buckling handles the mob offsets
|
||||
load.pixel_y = initial(load.pixel_y) + 9
|
||||
@@ -158,9 +159,9 @@
|
||||
qdel(src)
|
||||
if(2)
|
||||
for(var/i = 1; i < 3; i++)
|
||||
wires.RandomCut()
|
||||
wires.cut_random()
|
||||
if(3)
|
||||
wires.RandomCut()
|
||||
wires.cut_random()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj)
|
||||
@@ -169,7 +170,7 @@
|
||||
unload(0)
|
||||
if(prob(25))
|
||||
visible_message("<span class='danger'>Something shorts out inside [src]!</span>")
|
||||
wires.RandomCut()
|
||||
wires.cut_random()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/Topic(href, list/href_list)
|
||||
if(..())
|
||||
@@ -318,7 +319,7 @@
|
||||
|
||||
// returns true if the bot has power
|
||||
/mob/living/simple_animal/bot/mulebot/proc/has_power()
|
||||
return !open && cell && cell.charge > 0 && wires.HasPower()
|
||||
return !open && cell && cell.charge > 0 && !wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2)
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
|
||||
switch(type)
|
||||
@@ -362,7 +363,7 @@
|
||||
if(istype(AM,/obj/structure/closet/crate))
|
||||
CRATE = AM
|
||||
else
|
||||
if(wires.LoadCheck())
|
||||
if(!wires.is_cut(WIRE_LOADCHECK))
|
||||
buzz(SIGH)
|
||||
return // if not hacked, only allow crates to be loaded
|
||||
|
||||
@@ -459,8 +460,7 @@
|
||||
on = 0
|
||||
return
|
||||
if(on)
|
||||
var/speed = (wires.Motor1() ? 1 : 0) + (wires.Motor2() ? 2 : 0)
|
||||
// to_chat(world, "speed: [speed]")
|
||||
var/speed = (wires.is_cut(WIRE_MOTOR1) ? 1 : 0) + (wires.is_cut(WIRE_MOTOR2) ? 2 : 0)
|
||||
var/num_steps = 0
|
||||
switch(speed)
|
||||
if(0)
|
||||
@@ -624,7 +624,7 @@
|
||||
// not loaded
|
||||
if(auto_pickup) // find a crate
|
||||
var/atom/movable/AM
|
||||
if(wires.LoadCheck()) // if hacked, load first unanchored thing we find
|
||||
if(wires.is_cut(WIRE_LOADCHECK)) // if hacked, load first unanchored thing we find
|
||||
for(var/atom/movable/A in get_step(loc, loaddir))
|
||||
if(!A.anchored)
|
||||
AM = A
|
||||
@@ -672,7 +672,7 @@
|
||||
|
||||
// called when bot bumps into anything
|
||||
/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
|
||||
if(!wires.MobAvoid()) // usually just bumps, but if avoidance disabled knock over mobs
|
||||
if(wires.is_cut(WIRE_MOB_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs
|
||||
var/mob/M = obs
|
||||
if(ismob(M))
|
||||
if(istype(M,/mob/living/silicon/robot))
|
||||
@@ -736,8 +736,8 @@
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/receive_signal(datum/signal/signal)
|
||||
if(!wires.RemoteRX() || ..())
|
||||
return 1
|
||||
if(wires.is_cut(WIRE_REMOTE_RX) || ..())
|
||||
return TRUE
|
||||
|
||||
var/recv = signal.data["command"]
|
||||
|
||||
@@ -772,7 +772,7 @@
|
||||
|
||||
// send a radio signal with multiple data key/values
|
||||
/mob/living/simple_animal/bot/mulebot/post_signal_multiple(var/freq, var/list/keyval)
|
||||
if(!wires.RemoteTX())
|
||||
if(wires.is_cut(WIRE_REMOTE_TX))
|
||||
return
|
||||
|
||||
..()
|
||||
@@ -803,7 +803,7 @@
|
||||
|
||||
//Update navigation data. Called when commanded to deliver, return home, or a route update is needed...
|
||||
/mob/living/simple_animal/bot/mulebot/proc/get_nav()
|
||||
if(!on || !wires.BeaconRX())
|
||||
if(!on || wires.is_cut(WIRE_BEACON_RX))
|
||||
return
|
||||
|
||||
for(var/obj/machinery/navbeacon/NB in GLOB.deliverybeacons)
|
||||
|
||||
+16
-7
@@ -44,18 +44,27 @@
|
||||
usr.emote(message)
|
||||
|
||||
|
||||
/mob/proc/say_dead(var/message)
|
||||
if(!(client && client.holder))
|
||||
if(!config.dsay_allowed)
|
||||
to_chat(src, "<span class='danger'>Deadchat is globally muted.</span>")
|
||||
/mob/proc/say_dead(message)
|
||||
if(client)
|
||||
if(!client.holder)
|
||||
if(!config.dsay_allowed)
|
||||
to_chat(src, "<span class='danger'>Deadchat is globally muted.</span>")
|
||||
return
|
||||
|
||||
if(client.prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='warning'>You cannot talk in deadchat (muted).</span>")
|
||||
return
|
||||
|
||||
if(client && !(client.prefs.toggles & CHAT_DEAD))
|
||||
to_chat(usr, "<span class='danger'>You have deadchat muted.</span>")
|
||||
return
|
||||
if(!(client.prefs.toggles & CHAT_DEAD))
|
||||
to_chat(src, "<span class='danger'>You have deadchat muted.</span>")
|
||||
return
|
||||
|
||||
if(client.handle_spam_prevention(message, MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], <span class='message'>\"[message]\"</span>", src)
|
||||
create_log(DEADCHAT_LOG, message)
|
||||
log_ghostsay(message, src)
|
||||
|
||||
/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null)
|
||||
if(stat == DEAD)
|
||||
|
||||
@@ -44,26 +44,37 @@
|
||||
|
||||
|
||||
|
||||
//human -> robot
|
||||
/mob/living/carbon/human/proc/Robotize()
|
||||
/**
|
||||
For transforming humans into robots (cyborgs).
|
||||
|
||||
Arguments:
|
||||
* cell_type: A type path of the cell the new borg should receive.
|
||||
* connect_to_default_AI: TRUE if you want /robot/New() to handle connecting the borg to the AI with the least borgs.
|
||||
* AI: A reference to the AI we want to connect to.
|
||||
*/
|
||||
/mob/living/carbon/human/proc/Robotize(cell_type = null, connect_to_default_AI = TRUE, mob/living/silicon/ai/AI = null)
|
||||
if(notransform)
|
||||
return
|
||||
for(var/obj/item/W in src)
|
||||
unEquip(W)
|
||||
regenerate_icons()
|
||||
|
||||
notransform = 1
|
||||
canmove = 0
|
||||
icon = null
|
||||
invisibility = 101
|
||||
for(var/t in bodyparts)
|
||||
qdel(t)
|
||||
for(var/i in internal_organs)
|
||||
qdel(i)
|
||||
|
||||
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc )
|
||||
// Creating a new borg here will connect them to a default AI and notify that AI, if `connect_to_default_AI` is TRUE.
|
||||
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(loc, connect_to_AI = connect_to_default_AI)
|
||||
|
||||
// cyborgs produced by Robotize get an automatic power cell
|
||||
O.cell = new /obj/item/stock_parts/cell/high(O)
|
||||
// If `AI` is passed in, we want to connect to that AI specifically.
|
||||
if(AI)
|
||||
O.lawupdate = TRUE
|
||||
O.connect_to_ai(AI)
|
||||
|
||||
if(!cell_type)
|
||||
O.cell = new /obj/item/stock_parts/cell/high(O)
|
||||
else
|
||||
O.cell = new cell_type(O)
|
||||
|
||||
O.gender = gender
|
||||
O.invisibility = 0
|
||||
@@ -77,9 +88,8 @@
|
||||
else
|
||||
O.key = key
|
||||
|
||||
O.loc = loc
|
||||
O.forceMove(loc)
|
||||
O.job = "Cyborg"
|
||||
O.notify_ai(1)
|
||||
|
||||
if(O.mind && O.mind.assigned_role == "Cyborg")
|
||||
if(O.mind.role_alt_title == "Robot")
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
/obj/machinery/modular_computer/console/preset/engineering/install_programs()
|
||||
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
|
||||
hard_drive.store_file(new/datum/computer_file/program/power_monitor())
|
||||
hard_drive.store_file(new/datum/computer_file/program/alarm_monitor())
|
||||
// hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) //TO-DO:TGUI--Uncomment Modular computers
|
||||
hard_drive.store_file(new/datum/computer_file/program/supermatter_monitor())
|
||||
|
||||
// ===== RESEARCH CONSOLE =====
|
||||
|
||||
@@ -7,65 +7,71 @@
|
||||
requires_ntnet = 1
|
||||
network_destination = "alarm monitoring network"
|
||||
size = 5
|
||||
var/list/datum/alarm_handler/alarm_handlers
|
||||
var/tgui_id = "NtosStationAlertConsole"
|
||||
var/ui_x = 315
|
||||
var/ui_y = 500
|
||||
var/has_alert = 0
|
||||
var/list/alarms_listend_for = list("Fire", "Atmosphere", "Power")
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/New()
|
||||
/datum/computer_file/program/alarm_monitor/process_tick()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.register(src, /datum/computer_file/program/alarm_monitor/proc/update_icon)
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/Destroy()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.unregister(src)
|
||||
QDEL_NULL(alarm_handlers)
|
||||
return ..()
|
||||
if(has_alert)
|
||||
program_icon_state = "alert-red"
|
||||
ui_header = "alarm_red.gif"
|
||||
update_computer_icon()
|
||||
else
|
||||
program_icon_state = "alert-green"
|
||||
ui_header = "alarm_green.gif"
|
||||
update_computer_icon()
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/update_icon()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
if(AH.has_major_alarms())
|
||||
program_icon_state = "alert-red"
|
||||
ui_header = "alarm_red.gif"
|
||||
update_computer_icon()
|
||||
return 1
|
||||
program_icon_state = "alert-green"
|
||||
ui_header = "alarm_green.gif"
|
||||
update_computer_icon()
|
||||
return 0
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
|
||||
assets.send(user)
|
||||
ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700)
|
||||
ui.set_auto_update(1)
|
||||
ui.set_layout_key("program")
|
||||
ui.open()
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/ui_data(mob/user)
|
||||
/datum/computer_file/program/alarm_monitor/tgui_data(mob/user)
|
||||
var/list/data = get_header_data()
|
||||
|
||||
var/categories[0]
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
|
||||
for(var/datum/alarm/A in AH.major_alarms())
|
||||
var/cameras[0]
|
||||
var/lost_sources[0]
|
||||
|
||||
if(isAI(user))
|
||||
for(var/obj/machinery/camera/C in A.cameras())
|
||||
cameras[++cameras.len] = C.nano_structure()
|
||||
for(var/datum/alarm_source/AS in A.sources)
|
||||
if(!AS.source)
|
||||
lost_sources[++lost_sources.len] = AS.source_name
|
||||
|
||||
categories[categories.len]["alarms"] += list(list(
|
||||
"name" = sanitize(A.alarm_name()),
|
||||
"origin_lost" = A.origin == null,
|
||||
"has_cameras" = cameras.len,
|
||||
"cameras" = cameras,
|
||||
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
|
||||
data["categories"] = categories
|
||||
data["alarms"] = list()
|
||||
for(var/class in SSalarm.alarms)
|
||||
if(!(class in alarms_listend_for))
|
||||
continue
|
||||
data["alarms"][class] = list()
|
||||
for(var/area in alarms[class])
|
||||
data["alarms"][class] += area
|
||||
|
||||
return data
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
if(is_station_level(alarmsource.z))
|
||||
if(!(A.type in GLOB.the_station_areas))
|
||||
return
|
||||
else if(!is_mining_level(alarmsource.z) || istype(A, /area/ruin))
|
||||
return
|
||||
update_alarm_display()
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
if(is_station_level(origin.z))
|
||||
if(!(A.type in GLOB.the_station_areas))
|
||||
return
|
||||
else if(!is_mining_level(origin.z) || istype(A, /area/ruin))
|
||||
return
|
||||
update_alarm_display()
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/update_alarm_display()
|
||||
has_alert = FALSE
|
||||
for(var/cat in alarms)
|
||||
if(!(cat in alarms_listend_for))
|
||||
continue
|
||||
var/list/L = alarms[cat]
|
||||
if(length(L))
|
||||
has_alert = TRUE
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/run_program(mob/user)
|
||||
. = ..(user)
|
||||
GLOB.alarmdisplay += src
|
||||
RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
|
||||
RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE)
|
||||
GLOB.alarmdisplay -= src
|
||||
UnregisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM)
|
||||
UnregisterSignal(SSalarm, COMSIG_CANCELLED_ALARM)
|
||||
..()
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/datum/nano_module/alarm_monitor
|
||||
name = "Alarm monitor"
|
||||
var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use.
|
||||
var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user.
|
||||
|
||||
/datum/nano_module/alarm_monitor/all/New()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/engineering/New()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/security/New()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.motion_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/register(var/object, var/procName)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.register(object, procName)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/unregister(var/object)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.unregister(object)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/all_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.alarms
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/major_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.major_alarms()
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/minor_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.minor_alarms()
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/Topic(ref, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["switchTo"])
|
||||
var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras
|
||||
if(!C || !isAI(usr))
|
||||
return
|
||||
|
||||
usr.switch_to_camera(C)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
|
||||
var/categories[0]
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
|
||||
for(var/datum/alarm/A in AH.major_alarms())
|
||||
var/cameras[0]
|
||||
var/lost_sources[0]
|
||||
|
||||
if(isAI(user))
|
||||
for(var/obj/machinery/camera/C in A.cameras())
|
||||
cameras[++cameras.len] = C.nano_structure()
|
||||
for(var/datum/alarm_source/AS in A.sources)
|
||||
if(!AS.source)
|
||||
lost_sources[++lost_sources.len] = AS.source_name
|
||||
|
||||
categories[categories.len]["alarms"] += list(list(
|
||||
"name" = sanitize(A.alarm_name()),
|
||||
"origin_lost" = A.origin == null,
|
||||
"has_cameras" = cameras.len,
|
||||
"cameras" = cameras,
|
||||
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
|
||||
data["categories"] = categories
|
||||
|
||||
return data
|
||||
+140
-179
@@ -25,6 +25,11 @@
|
||||
|
||||
#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds
|
||||
|
||||
// main_status var
|
||||
#define APC_EXTERNAL_POWER_NOTCONNECTED 0
|
||||
#define APC_EXTERNAL_POWER_NOENERGY 1
|
||||
#define APC_EXTERNAL_POWER_GOOD 2
|
||||
|
||||
// APC malf status
|
||||
#define APC_MALF_NOT_HACKED 1
|
||||
#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core.
|
||||
@@ -75,7 +80,7 @@
|
||||
var/lastused_equip = 0
|
||||
var/lastused_environ = 0
|
||||
var/lastused_total = 0
|
||||
var/main_status = 0
|
||||
var/main_status = APC_EXTERNAL_POWER_NOTCONNECTED
|
||||
powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :(
|
||||
var/malfhack = 0 //New var for my changes to AI malf. --NeoFite
|
||||
var/mob/living/silicon/ai/malfai = null //See above --NeoFite
|
||||
@@ -170,6 +175,7 @@
|
||||
addtimer(CALLBACK(src, .proc/update), 5)
|
||||
|
||||
/obj/machinery/power/apc/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
GLOB.apcs -= src
|
||||
if(malfai && operating)
|
||||
malfai.malf_picker.processing_time = clamp(malfai.malf_picker.processing_time - 10,0,1000)
|
||||
@@ -212,12 +218,11 @@
|
||||
if(isarea(A))
|
||||
area = A
|
||||
// no-op, keep the name
|
||||
else if(isarea(A) && src.areastring == null)
|
||||
else if(isarea(A) && !areastring)
|
||||
area = A
|
||||
name = "\improper [area.name] APC"
|
||||
else
|
||||
area = get_area_name(areastring)
|
||||
name = "\improper [area.name] APC"
|
||||
name = "\improper [get_area_name(area, TRUE)] APC"
|
||||
area.apc |= src
|
||||
|
||||
update_icon()
|
||||
@@ -429,8 +434,8 @@
|
||||
//attack with an item - open/close cover, insert cell, or (un)lock interface
|
||||
/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params)
|
||||
|
||||
if(issilicon(user) && get_dist(src,user)>1)
|
||||
return src.attack_hand(user)
|
||||
if(issilicon(user) && get_dist(src, user) > 1)
|
||||
return attack_hand(user)
|
||||
|
||||
else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
|
||||
if(cell)
|
||||
@@ -445,7 +450,7 @@
|
||||
W.forceMove(src)
|
||||
cell = W
|
||||
user.visible_message(\
|
||||
"[user.name] has inserted the power cell to [src.name]!",\
|
||||
"[user.name] has inserted the power cell to [name]!",\
|
||||
"<span class='notice'>You insert the power cell.</span>")
|
||||
chargecount = 0
|
||||
update_icon()
|
||||
@@ -474,7 +479,7 @@
|
||||
return
|
||||
user.visible_message("[user.name] adds cables to the APC frame.", \
|
||||
"<span class='notice'>You start adding cables to the APC frame...</span>")
|
||||
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
|
||||
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
||||
if(do_after(user, 20, target = src))
|
||||
if(C.get_amount() < 10 || !C)
|
||||
return
|
||||
@@ -499,10 +504,10 @@
|
||||
|
||||
user.visible_message("[user.name] inserts the power control board into [src].", \
|
||||
"<span class='notice'>You start to insert the power control board into the frame...</span>")
|
||||
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
|
||||
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
||||
if(do_after(user, 10, target = src))
|
||||
if(has_electronics==0)
|
||||
has_electronics = 1
|
||||
if(!has_electronics)
|
||||
has_electronics = TRUE
|
||||
locked = FALSE
|
||||
to_chat(user, "<span class='notice'>You place the power control board inside the frame.</span>")
|
||||
qdel(W)
|
||||
@@ -549,11 +554,11 @@
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You are trying to remove the power control board...</span>" )
|
||||
if(I.use_tool(src, user, 50, volume = I.tool_volume))
|
||||
if(has_electronics==1)
|
||||
has_electronics = 0
|
||||
if(has_electronics)
|
||||
has_electronics = FALSE
|
||||
if(stat & BROKEN)
|
||||
user.visible_message(\
|
||||
"[user.name] has broken the power control board inside [src.name]!",
|
||||
"[user.name] has broken the power control board inside [name]!",
|
||||
"<span class='notice'>You break the charred power control board and remove the remains.</span>",
|
||||
"<span class='italics'>You hear a crack.</span>")
|
||||
return
|
||||
@@ -561,19 +566,19 @@
|
||||
else if(emagged) // We emag board, not APC's frame
|
||||
emagged = FALSE
|
||||
user.visible_message(
|
||||
"[user.name] has discarded emaged power control board from [src.name]!",
|
||||
"<span class='notice'>You discarded shorten board.</span>")
|
||||
"[user.name] has discarded the shorted power control board from [name]!",
|
||||
"<span class='notice'>You discarded the shorted board.</span>")
|
||||
return
|
||||
else if(malfhack) // AI hacks board, not APC's frame
|
||||
user.visible_message(\
|
||||
"[user.name] has discarded strangely programmed power control board from [src.name]!",
|
||||
"<span class='notice'>You discarded strangely programmed board.</span>")
|
||||
"[user.name] has discarded strangely the programmed power control board from [name]!",
|
||||
"<span class='notice'>You discarded the strangely programmed board.</span>")
|
||||
malfai = null
|
||||
malfhack = 0
|
||||
return
|
||||
else
|
||||
user.visible_message(\
|
||||
"[user.name] has removed the power control board from [src.name]!",
|
||||
"[user.name] has removed the power control board from [name]!",
|
||||
"<span class='notice'>You remove the power control board.</span>")
|
||||
new /obj/item/apc_electronics(loc)
|
||||
return
|
||||
@@ -648,7 +653,7 @@
|
||||
else if(stat & (BROKEN|MAINT))
|
||||
to_chat(user, "<span class='warning'>Nothing happens!</span>")
|
||||
else
|
||||
if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack)
|
||||
if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack)
|
||||
locked = !locked
|
||||
to_chat(user, "<span class='notice'>You [ locked ? "lock" : "unlock"] the APC interface.</span>")
|
||||
update_icon()
|
||||
@@ -711,36 +716,29 @@
|
||||
|
||||
// attack with hand - remove cell (if cover open) or interact with the APC
|
||||
/obj/machinery/power/apc/attack_hand(mob/user)
|
||||
// if(!can_use(user)) This already gets called in interact() and in topic()
|
||||
// return
|
||||
if(!user)
|
||||
return
|
||||
src.add_fingerprint(user)
|
||||
add_fingerprint(user)
|
||||
|
||||
if(usr == user && opened && (!issilicon(user)))
|
||||
if(usr == user && opened && !issilicon(user))
|
||||
if(cell)
|
||||
if(issilicon(user))
|
||||
cell.loc=src.loc // Drop it, whoops.
|
||||
else
|
||||
user.put_in_hands(cell)
|
||||
user.put_in_hands(cell)
|
||||
cell.add_fingerprint(user)
|
||||
cell.update_icon()
|
||||
|
||||
src.cell = null
|
||||
user.visible_message("<span class='warning'>[user.name] removes the power cell from [src.name]!", "You remove the power cell.</span>")
|
||||
// to_chat(user, "You remove the power cell.")
|
||||
charging = 0
|
||||
src.update_icon()
|
||||
cell = null
|
||||
user.visible_message("<span class='warning'>[user.name] removes [cell] from [src]!", "You remove the [cell].</span>")
|
||||
charging = FALSE
|
||||
update_icon()
|
||||
return
|
||||
if(stat & (BROKEN|MAINT))
|
||||
return
|
||||
|
||||
src.interact(user)
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/power/apc/attack_ghost(mob/user)
|
||||
if(panel_open)
|
||||
wires.Interact(user)
|
||||
return ui_interact(user)
|
||||
return tgui_interact(user)
|
||||
|
||||
/obj/machinery/power/apc/interact(mob/user)
|
||||
if(!user)
|
||||
@@ -749,7 +747,7 @@
|
||||
if(panel_open)
|
||||
wires.Interact(user)
|
||||
|
||||
return ui_interact(user)
|
||||
return tgui_interact(user)
|
||||
|
||||
|
||||
/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf)
|
||||
@@ -770,22 +768,16 @@
|
||||
else
|
||||
return APC_MALF_NOT_HACKED
|
||||
|
||||
/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
if(!user)
|
||||
return
|
||||
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/machinery/power/apc/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
// the ui does not exist, so we'll create a new one
|
||||
ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460)
|
||||
ui = new(user, src, ui_key, "APC", name, 510, 460, master_ui, state)
|
||||
ui.open()
|
||||
// Auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
/obj/machinery/power/apc/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["locked"] = is_locked(user)
|
||||
data["normallyLocked"] = locked
|
||||
data["isOperating"] = operating
|
||||
data["externalPower"] = main_status
|
||||
data["powerCellStatus"] = cell ? cell.percent() : null
|
||||
@@ -853,10 +845,6 @@
|
||||
// to_chat(world, "[area.power_equip]")
|
||||
area.power_change()
|
||||
|
||||
/obj/machinery/power/apc/proc/isWireCut(var/wireIndex)
|
||||
return wires.IsIndexCut(wireIndex)
|
||||
|
||||
|
||||
/obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic()
|
||||
if(user.can_admin_interact())
|
||||
return 1
|
||||
@@ -866,7 +854,7 @@
|
||||
var/mob/living/silicon/ai/AI = user
|
||||
var/mob/living/silicon/robot/robot = user
|
||||
if( \
|
||||
src.aidisabled || \
|
||||
aidisabled || \
|
||||
malfhack && istype(malfai) && \
|
||||
( \
|
||||
(istype(AI) && (malfai!=AI && malfai != AI.parent)) || \
|
||||
@@ -877,21 +865,21 @@
|
||||
to_chat(user, "<span class='danger'>\The [src] has AI control disabled!</span>")
|
||||
user << browse(null, "window=apc")
|
||||
user.unset_machine()
|
||||
return 0
|
||||
return FALSE
|
||||
else
|
||||
if((!in_range(src, user) || !istype(src.loc, /turf)))
|
||||
return 0
|
||||
if((!in_range(src, user) || !istype(loc, /turf)))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(istype(H))
|
||||
if(H.getBrainLoss() >= 60)
|
||||
for(var/mob/M in viewers(src, null))
|
||||
to_chat(M, "<span class='danger'>[H] stares cluelessly at [src] and drools.</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
else if(prob(H.getBrainLoss()))
|
||||
to_chat(user, "<span class='danger'>You momentarily forget how to use [src].</span>")
|
||||
return 0
|
||||
return 1
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob)
|
||||
if(user.can_admin_interact())
|
||||
@@ -909,104 +897,59 @@
|
||||
else
|
||||
return locked
|
||||
|
||||
/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(!can_use(usr, 1))
|
||||
return 1
|
||||
|
||||
if(href_list["lock"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
coverlocked = !coverlocked
|
||||
|
||||
else if(href_list["breaker"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
toggle_breaker()
|
||||
|
||||
else if(href_list["toggle_nightshift"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
if(last_nightshift_switch > world.time + 100) // don't spam...
|
||||
to_chat(usr, "<span class='warning'>[src]'s night lighting circuit breaker is still cycling!</span>")
|
||||
return
|
||||
last_nightshift_switch = world.time
|
||||
set_nightshift(!nightshift_lights)
|
||||
|
||||
else if(href_list["cmode"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
chargemode = !chargemode
|
||||
if(!chargemode)
|
||||
charging = 0
|
||||
update_icon()
|
||||
|
||||
else if(href_list["eqp"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
var/val = text2num(href_list["eqp"])
|
||||
equipment = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
|
||||
else if(href_list["lgt"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
var/val = text2num(href_list["lgt"])
|
||||
lighting = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
|
||||
else if(href_list["env"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
var/val = text2num(href_list["env"])
|
||||
environ = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
else if( href_list["close"] )
|
||||
SSnanoui.close_user_uis(usr, src)
|
||||
|
||||
return 0
|
||||
else if(href_list["close2"])
|
||||
usr << browse(null, "window=apcwires")
|
||||
|
||||
return 0
|
||||
|
||||
else if(href_list["overload"])
|
||||
if(issilicon(usr) && !aidisabled)
|
||||
overload_lighting()
|
||||
|
||||
else if(href_list["malfhack"])
|
||||
if(get_malf_status(usr))
|
||||
malfhack(usr)
|
||||
|
||||
else if(href_list["occupyapc"])
|
||||
if(get_malf_status(usr))
|
||||
malfoccupy(usr)
|
||||
|
||||
else if(href_list["deoccupyapc"])
|
||||
if(get_malf_status(usr))
|
||||
malfvacate()
|
||||
|
||||
else if(href_list["toggleaccess"])
|
||||
if(istype(usr, /mob/living/silicon))
|
||||
if(emagged || aidisabled || (stat & (BROKEN|MAINT)))
|
||||
to_chat(usr, "The APC does not respond to the command.")
|
||||
/obj/machinery/power/apc/tgui_act(action, params)
|
||||
if(..() || !can_use(usr, TRUE) || (locked && !usr.has_unlimited_silicon_privilege && (action != "toggle_nightshift") && !usr.can_admin_interact()))
|
||||
return
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("lock")
|
||||
if(usr.has_unlimited_silicon_privilege)
|
||||
if(emagged || stat & BROKEN)
|
||||
to_chat(usr, "<span class='warning'>The APC does not respond to the command!</span>")
|
||||
return FALSE
|
||||
else
|
||||
locked = !locked
|
||||
update_icon()
|
||||
else
|
||||
locked = !locked
|
||||
to_chat(usr, "<span class='warning'>Access Denied!</span>")
|
||||
return FALSE
|
||||
if("cover")
|
||||
coverlocked = !coverlocked
|
||||
if("breaker")
|
||||
toggle_breaker(usr)
|
||||
if("toggle_nightshift")
|
||||
if(last_nightshift_switch > world.time + 100) // don't spam...
|
||||
to_chat(usr, "<span class='warning'>[src]'s night lighting circuit breaker is still cycling!</span>")
|
||||
return FALSE
|
||||
last_nightshift_switch = world.time
|
||||
set_nightshift(!nightshift_lights)
|
||||
if("charge")
|
||||
chargemode = !chargemode
|
||||
if("channel")
|
||||
if(params["eqp"])
|
||||
equipment = setsubsystem(text2num(params["eqp"]))
|
||||
update_icon()
|
||||
|
||||
return 0
|
||||
update()
|
||||
else if(params["lgt"])
|
||||
lighting = setsubsystem(text2num(params["lgt"]))
|
||||
update_icon()
|
||||
update()
|
||||
else if(params["env"])
|
||||
environ = setsubsystem(text2num(params["env"]))
|
||||
update_icon()
|
||||
update()
|
||||
if("overload")
|
||||
if(usr.has_unlimited_silicon_privilege)
|
||||
overload_lighting()
|
||||
if("hack")
|
||||
if(get_malf_status(usr))
|
||||
malfhack(usr)
|
||||
if("occupy")
|
||||
if(get_malf_status(usr))
|
||||
malfoccupy(usr)
|
||||
if("deoccupy")
|
||||
if(get_malf_status(usr))
|
||||
malfvacate()
|
||||
|
||||
/obj/machinery/power/apc/proc/toggle_breaker()
|
||||
operating = !operating
|
||||
@@ -1037,7 +980,7 @@
|
||||
if(!malf.can_shunt)
|
||||
to_chat(malf, "<span class='warning'>You cannot shunt!</span>")
|
||||
return
|
||||
if(!is_station_level(src.z))
|
||||
if(!is_station_level(z))
|
||||
return
|
||||
occupier = new /mob/living/silicon/ai(src,malf.laws,null,1)
|
||||
occupier.adjustOxyLoss(malf.getOxyLoss())
|
||||
@@ -1084,21 +1027,21 @@
|
||||
|
||||
/obj/machinery/power/apc/proc/ion_act()
|
||||
//intended to be exactly the same as an AI malf attack
|
||||
if(!src.malfhack && is_station_level(src.z))
|
||||
if(!malfhack && is_station_level(z))
|
||||
if(prob(3))
|
||||
src.locked = 1
|
||||
if(src.cell.charge > 0)
|
||||
src.cell.charge = 0
|
||||
locked = TRUE
|
||||
if(cell.charge > 0)
|
||||
cell.charge = 0
|
||||
cell.corrupt()
|
||||
src.malfhack = 1
|
||||
malfhack = TRUE
|
||||
update_icon()
|
||||
var/datum/effect_system/smoke_spread/smoke = new
|
||||
smoke.set_up(3, 0, src.loc)
|
||||
smoke.set_up(3, 0, loc)
|
||||
smoke.attach(src)
|
||||
smoke.start()
|
||||
do_sparks(3, 1, src)
|
||||
for(var/mob/M in viewers(src))
|
||||
M.show_message("<span class='danger'>The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "<span class='danger'>You hear sizzling electronics.</span>", 2)
|
||||
M.show_message("<span class='danger'>The [name] suddenly lets out a blast of smoke and some sparks!", 3, "<span class='danger'>You hear sizzling electronics.</span>", 2)
|
||||
|
||||
|
||||
/obj/machinery/power/apc/surplus()
|
||||
@@ -1141,12 +1084,12 @@
|
||||
|
||||
var/excess = surplus()
|
||||
|
||||
if(!src.avail())
|
||||
main_status = 0
|
||||
if(!avail())
|
||||
main_status = APC_EXTERNAL_POWER_NOTCONNECTED
|
||||
else if(excess < 0)
|
||||
main_status = 1
|
||||
main_status = APC_EXTERNAL_POWER_NOENERGY
|
||||
else
|
||||
main_status = 2
|
||||
main_status = APC_EXTERNAL_POWER_GOOD
|
||||
|
||||
if(debug)
|
||||
log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]")
|
||||
@@ -1192,31 +1135,31 @@
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
autoflag = 3
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.clearAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(TRUE, src)
|
||||
else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment
|
||||
if(autoflag != 2)
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 2
|
||||
else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment
|
||||
if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0))
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 2)
|
||||
environ = autoset(environ, 1)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 1
|
||||
else if(cell.charge <= 0) // zero charge, turn all off
|
||||
if(autoflag != 0)
|
||||
equipment = autoset(equipment, 0)
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 0
|
||||
|
||||
// now trickle-charge the cell
|
||||
@@ -1261,7 +1204,7 @@
|
||||
if(shock_mobs.len)
|
||||
var/mob/living/L = pick(shock_mobs)
|
||||
L.electrocute_act(rand(5, 25), "electrical arc")
|
||||
playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1)
|
||||
playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, TRUE)
|
||||
Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5)
|
||||
|
||||
else // no cell, switch everything off
|
||||
@@ -1271,8 +1214,8 @@
|
||||
equipment = autoset(equipment, 0)
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 0
|
||||
|
||||
// update icon & area power if anything changed
|
||||
@@ -1372,4 +1315,22 @@
|
||||
L.update(FALSE)
|
||||
CHECK_TICK
|
||||
|
||||
/obj/machinery/power/apc/proc/relock_callback()
|
||||
locked = TRUE
|
||||
updateDialog()
|
||||
|
||||
/obj/machinery/power/apc/proc/check_main_power_callback()
|
||||
if(!wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2))
|
||||
shorted = FALSE
|
||||
updateDialog()
|
||||
|
||||
/obj/machinery/power/apc/proc/check_ai_control_callback()
|
||||
if(!wires.is_cut(WIRE_AI_CONTROL))
|
||||
aidisabled = FALSE
|
||||
updateDialog()
|
||||
|
||||
#undef APC_UPDATE_ICON_COOLDOWN
|
||||
|
||||
#undef APC_EXTERNAL_POWER_NOTCONNECTED
|
||||
#undef APC_EXTERNAL_POWER_NOENERGY
|
||||
#undef APC_EXTERNAL_POWER_GOOD
|
||||
|
||||
+16
-13
@@ -229,14 +229,15 @@ By design, d1 is the smallest direction and d2 is the highest
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct()
|
||||
|
||||
obj/structure/cable/proc/cable_color(var/colorC)
|
||||
if(colorC)
|
||||
if(colorC == "rainbow")
|
||||
color = color_rainbow()
|
||||
else
|
||||
color = colorC
|
||||
obj/structure/cable/proc/cable_color(colorC)
|
||||
if(!colorC)
|
||||
color = COLOR_RED
|
||||
else if(colorC == "rainbow")
|
||||
color = color_rainbow()
|
||||
else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
|
||||
color = COLOR_ORANGE
|
||||
else
|
||||
color = "#DD0000"
|
||||
color = colorC
|
||||
|
||||
/obj/structure/cable/proc/color_rainbow()
|
||||
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
|
||||
@@ -845,13 +846,15 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
||||
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
|
||||
..()
|
||||
|
||||
/obj/item/stack/cable_coil/proc/cable_color(var/colorC)
|
||||
if(colorC)
|
||||
if(colorC == "rainbow")
|
||||
colorC = color_rainbow()
|
||||
color = colorC
|
||||
else
|
||||
/obj/item/stack/cable_coil/proc/cable_color(colorC)
|
||||
if(!colorC)
|
||||
color = COLOR_RED
|
||||
else if(colorC == "rainbow")
|
||||
color = color_rainbow()
|
||||
else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
|
||||
color = COLOR_ORANGE
|
||||
else
|
||||
color = colorC
|
||||
|
||||
/obj/item/stack/cable_coil/proc/color_rainbow()
|
||||
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
|
||||
|
||||
@@ -177,6 +177,8 @@
|
||||
var/nightshift_light_power = 0.45
|
||||
var/nightshift_light_color = "#FFDDCC"
|
||||
|
||||
var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode
|
||||
|
||||
// the smaller bulb light fixture
|
||||
|
||||
/obj/machinery/light/small
|
||||
@@ -238,7 +240,11 @@
|
||||
|
||||
switch(status) // set icon_states
|
||||
if(LIGHT_OK)
|
||||
icon_state = "[base_state][on]"
|
||||
var/area/A = get_area(src)
|
||||
if(A && A.fire)
|
||||
icon_state = "[base_state]_emergency"
|
||||
else
|
||||
icon_state = "[base_state][on]"
|
||||
if(LIGHT_EMPTY)
|
||||
icon_state = "[base_state]-empty"
|
||||
on = FALSE
|
||||
@@ -260,10 +266,20 @@
|
||||
on = FALSE
|
||||
update_icon()
|
||||
if(on)
|
||||
var/BR = nightshift_enabled ? nightshift_light_range : brightness_range
|
||||
var/PO = nightshift_enabled ? nightshift_light_power : brightness_power
|
||||
var/CO = nightshift_enabled ? nightshift_light_color : brightness_color
|
||||
var/matching = light_range == BR && light_power == PO && light_color == CO
|
||||
var/BR = brightness_range
|
||||
var/PO = brightness_power
|
||||
var/CO = brightness_color
|
||||
if(color)
|
||||
CO = color
|
||||
var/area/A = get_area(src)
|
||||
if(A && A.fire)
|
||||
CO = bulb_emergency_colour
|
||||
else if(nightshift_enabled)
|
||||
BR = nightshift_light_range
|
||||
PO = nightshift_light_power
|
||||
if(!color)
|
||||
CO = nightshift_light_color
|
||||
var/matching = light && BR == light.light_range && PO == light.light_power && CO == light.light_color
|
||||
if(!matching)
|
||||
switchcount++
|
||||
if(rigged)
|
||||
|
||||
@@ -310,16 +310,25 @@ field_generator power level display
|
||||
//This is here to help fight the "hurr durr, release singulo cos nobody will notice before the
|
||||
//singulo eats the evidence". It's not fool-proof but better than nothing.
|
||||
//I want to avoid using global variables.
|
||||
spawn(1)
|
||||
var/temp = 1 //stops spam
|
||||
for(var/thing in GLOB.singularities)
|
||||
var/obj/singularity/O = thing
|
||||
if(O.last_warning && temp)
|
||||
if((world.time - O.last_warning) > 50) //to stop message-spam
|
||||
temp = 0
|
||||
message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</A>)",1)
|
||||
investigate_log("has <font color='red'>failed</font> whilst a singulo exists.","singulo")
|
||||
O.last_warning = world.time
|
||||
INVOKE_ASYNC(src, .proc/admin_alert)
|
||||
|
||||
/obj/machinery/field/generator/proc/admin_alert()
|
||||
var/temp = TRUE //stops spam
|
||||
for(var/thing in GLOB.singularities)
|
||||
var/obj/singularity/O = thing
|
||||
if(O.last_warning && temp && atoms_share_level(O, src))
|
||||
if((world.time - O.last_warning) > 50) //to stop message-spam
|
||||
temp = FALSE
|
||||
// To the person who asks "Hey affected, why are you using this massive operator when you can use AREACOORD?" Well, ill tell you
|
||||
// get_area_name is fucking broken and uses a for(x in world) search
|
||||
// It doesnt even work, is expensive, and returns 0
|
||||
// Im not refactoring one thing which could risk breaking all admin location logs
|
||||
// Fight me
|
||||
// [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)] works much better and actually works at all
|
||||
// Oh and yes, this exact comment was pasted from the exact same thing I did to tcomms code. Dont at me.
|
||||
message_admins("A singularity exists and a containment field has failed on the same Z-Level. Singulo location: [O ? "[get_location_name(O, TRUE)] [COORD(O)]" : "nonexistent location"] [ADMIN_JMP(O)] | Field generator location: [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]")
|
||||
investigate_log("has <font color='red'>failed</font> whilst a singulo exists.","singulo")
|
||||
O.last_warning = world.time
|
||||
|
||||
/obj/machinery/field/generator/shock_field(mob/living/user)
|
||||
if(fields.len)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/area/engine/engineering/power_alert(var/alarming)
|
||||
if(alarming)
|
||||
investigate_log("has a power alarm!","singulo")
|
||||
/area/engine/engineering/poweralert(state, source)
|
||||
if(state != poweralm)
|
||||
investigate_log("has a power alarm!", "singulo")
|
||||
..()
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
use_log = list()
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
if(active)
|
||||
toggle_power()
|
||||
QDEL_NULL(wires)
|
||||
@@ -41,6 +42,11 @@
|
||||
else if(construction_state == 2) // Wires exposed
|
||||
wires.Interact(user)
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/multitool_act(mob/living/user, obj/item/I)
|
||||
if(construction_state == 2) // Wires exposed
|
||||
wires.Interact(user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/update_state()
|
||||
if(construction_state < 3)
|
||||
use_power = NO_POWER_USE
|
||||
@@ -93,18 +99,18 @@
|
||||
usr.unset_machine()
|
||||
return
|
||||
if(href_list["togglep"])
|
||||
if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE))
|
||||
if(!wires.is_cut(WIRE_PARTICLE_POWER))
|
||||
toggle_power()
|
||||
|
||||
else if(href_list["scan"])
|
||||
part_scan()
|
||||
|
||||
else if(href_list["strengthup"])
|
||||
if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
|
||||
if(!wires.is_cut(WIRE_PARTICLE_STRENGTH))
|
||||
add_strength()
|
||||
|
||||
else if(href_list["strengthdown"])
|
||||
if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
|
||||
if(!wires.is_cut(WIRE_PARTICLE_STRENGTH))
|
||||
remove_strength()
|
||||
|
||||
updateDialog()
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/power/tesla_coil/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
QDEL_NULL(wires)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
if(keep)
|
||||
stored_ammo.Insert(1,b)
|
||||
update_mat_value()
|
||||
update_icon()
|
||||
return b
|
||||
|
||||
/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0)
|
||||
|
||||
@@ -133,6 +133,9 @@
|
||||
/obj/item/ammo_box/magazine/internal/shot/riot/short
|
||||
max_ammo = 3
|
||||
|
||||
/obj/item/ammo_box/magazine/internal/shot/riot/buckshot
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/buckshot
|
||||
|
||||
/obj/item/ammo_box/magazine/internal/grenadelauncher
|
||||
name = "grenade launcher internal magazine"
|
||||
ammo_type = /obj/item/ammo_casing/a40mm
|
||||
@@ -380,10 +383,7 @@
|
||||
origin_tech = "combat=3;syndicate=1"
|
||||
caliber = "shotgun"
|
||||
max_ammo = 8
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/update_icon()
|
||||
..()
|
||||
icon_state = "[initial(icon_state)]-[CEILING(ammo_count(0)/8, 1)*8]"
|
||||
multiple_sprites = 2
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/buckshot
|
||||
name = "shotgun magazine (12g buckshot slugs)"
|
||||
@@ -411,6 +411,24 @@
|
||||
icon_state = "m12gbc"
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/breaching
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/XtrLrg
|
||||
name = "\improper XL shotgun magazine (12g slugs)"
|
||||
desc = "An extra large drum magazine."
|
||||
icon_state = "m12gXlSl"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
ammo_type = /obj/item/ammo_casing/shotgun
|
||||
max_ammo = 16
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/XtrLrg/buckshot
|
||||
name = "\improper XL shotgun magazine (12g buckshot)"
|
||||
icon_state = "m12gXlBs"
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/buckshot
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/XtrLrg/dragon
|
||||
name = "\improper XL shotgun magazine (12g dragon's breath)"
|
||||
icon_state = "m12gXlDb"
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath
|
||||
|
||||
/obj/item/ammo_box/magazine/toy
|
||||
name = "foam force META magazine"
|
||||
ammo_type = /obj/item/ammo_casing/caseless/foam_dart
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
if(!user.unEquip(A))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You screw [S] onto [src].</span>")
|
||||
playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
|
||||
suppressed = A
|
||||
S.oldsound = fire_sound
|
||||
S.initial_w_class = w_class
|
||||
@@ -120,6 +121,7 @@
|
||||
..()
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You unscrew [suppressed] from [src].</span>")
|
||||
playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
|
||||
user.put_in_hands(suppressed)
|
||||
fire_sound = S.oldsound
|
||||
w_class = S.initial_w_class
|
||||
|
||||
@@ -275,13 +275,27 @@
|
||||
if(magazine)
|
||||
overlays.Cut()
|
||||
overlays += "[magazine.icon_state]"
|
||||
return
|
||||
if(istype(magazine, /obj/item/ammo_box/magazine/m12g/XtrLrg))
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
else
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
else
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
|
||||
/obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon()
|
||||
overlays.Cut()
|
||||
update_magazine()
|
||||
icon_state = "bulldog[chambered ? "" : "-e"]"
|
||||
|
||||
/obj/item/gun/projectile/automatic/shotgun/bulldog/attackby(var/obj/item/A as obj, mob/user as mob, params)
|
||||
if(istype(A, /obj/item/ammo_box/magazine/m12g/XtrLrg))
|
||||
if(istype(loc, /obj/item/storage)) // To prevent inventory exploits
|
||||
var/obj/item/storage/Strg = loc
|
||||
if(Strg.max_w_class < WEIGHT_CLASS_BULKY)
|
||||
to_chat(user, "<span class='warning'>You can't reload [src], with a XL mag, while it's in a normal bag.</span>")
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag)
|
||||
..()
|
||||
empty_alarm()
|
||||
|
||||
@@ -197,6 +197,8 @@
|
||||
..()
|
||||
post_sawoff()
|
||||
|
||||
/obj/item/gun/projectile/shotgun/riot/buckshot //comes pre-loaded with buckshot rather than rubber
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/buckshot
|
||||
|
||||
|
||||
///////////////////////
|
||||
|
||||
@@ -543,6 +543,14 @@
|
||||
build_path = /obj/item/assembly/health
|
||||
category = list("initial", "Medical")
|
||||
|
||||
/datum/design/stethoscope
|
||||
name = "Stethoscope"
|
||||
id = "stethoscope"
|
||||
build_type = AUTOLATHE
|
||||
materials = list(MAT_METAL = 500)
|
||||
build_path = /obj/item/clothing/accessory/stethoscope
|
||||
category = list("initial", "Medical")
|
||||
|
||||
/datum/design/timer
|
||||
name = "Timer"
|
||||
id = "timer"
|
||||
|
||||
@@ -272,6 +272,16 @@
|
||||
build_path = /obj/item/circuitboard/solar_control
|
||||
category = list("Computer Boards")
|
||||
|
||||
/datum/design/sm_monitor
|
||||
name = "Console Board (Supermatter Monitoring)"
|
||||
desc = "Allows for the construction of circuit boards used to build a supermatter monitoring console"
|
||||
id = "sm_monitor"
|
||||
req_tech = list("programming" = 2, "powerstorage" = 2)
|
||||
build_type = IMPRINTER
|
||||
materials = list(MAT_GLASS = 1000)
|
||||
build_path = /obj/item/circuitboard/sm_monitor
|
||||
category = list("Computer Boards")
|
||||
|
||||
/datum/design/spacepodlocator
|
||||
name = "Console Board (Spacepod Locator)"
|
||||
desc = "Allows for the construction of circuit boards used to build a space-pod locating console"
|
||||
|
||||
@@ -751,7 +751,7 @@
|
||||
category = list("Exosuit Equipment")
|
||||
|
||||
/datum/design/mech_mining_scanner
|
||||
name = "Exosuit Engineering Equipement (Mining Scanner)"
|
||||
name = "Exosuit Engineering Equipment (Mining Scanner)"
|
||||
id = "mech_mscanner"
|
||||
build_type = MECHFAB
|
||||
build_path = /obj/item/mecha_parts/mecha_equipment/mining_scanner
|
||||
|
||||
@@ -291,8 +291,7 @@
|
||||
for(var/turf/T in oview(1, src))
|
||||
if(!T.density)
|
||||
if(prob(EFFECT_PROB_VERYHIGH))
|
||||
var/obj/effect/decal/cleanable/reagentdecal = new/obj/effect/decal/cleanable/greenglow(T)
|
||||
reagentdecal.reagents.add_reagent("radium", 7)
|
||||
new /obj/effect/decal/cleanable/greenglow(T)
|
||||
if(prob(EFFECT_PROB_MEDIUM-badThingCoeff))
|
||||
var/savedName = "[exp_on]"
|
||||
ejectItem(TRUE)
|
||||
|
||||
@@ -296,7 +296,7 @@
|
||||
return
|
||||
var/sound/S = sound(mysound)
|
||||
S.wait = 0 //No queue
|
||||
S.channel = open_sound_channel()
|
||||
S.channel = SSsounds.random_available_channel()
|
||||
S.volume = 50
|
||||
for(var/mob/M in passengers | pilot)
|
||||
M << S
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
|
||||
/obj/machinery/bsa/middle/proc/check_completion()
|
||||
if(!front || !back)
|
||||
return "No linked parts detected!"
|
||||
return "No multitool-linked parts detected!"
|
||||
if(!front.anchored || !back.anchored || !anchored)
|
||||
return "Linked parts unwrenched!"
|
||||
if(front.y != y || back.y != y || !(front.x > x && back.x < x || front.x < x && back.x > x) || front.z != z || back.z != z)
|
||||
@@ -305,51 +305,45 @@
|
||||
/obj/machinery/computer/bsa_control/attack_hand(mob/user)
|
||||
if(..())
|
||||
return 1
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/computer/bsa_control/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/machinery/computer/bsa_control/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "bsa.tmpl", name, 400, 305)
|
||||
ui = new(user, src, ui_key, "BlueSpaceArtilleryControl", name, 400, 155, master_ui, state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/computer/bsa_control/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
/obj/machinery/computer/bsa_control/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["connected"] = cannon
|
||||
data["notice"] = notice
|
||||
if(target)
|
||||
data["target"] = get_target_name()
|
||||
|
||||
if(cannon)
|
||||
var/reload_cooldown = cannon.reload_cooldown
|
||||
var/last_fire_time = cannon.last_fire_time
|
||||
var/time_to_wait = max(0, round(reload_cooldown - ((world.time / 10) - last_fire_time)))
|
||||
var/minutes = max(0, round(time_to_wait / 60))
|
||||
var/seconds = max(0, time_to_wait - (60 * minutes))
|
||||
|
||||
data["reloadtime_mins"] = minutes
|
||||
data["reloadtime_secs"] = (seconds < 10) ? "0[seconds]" : seconds
|
||||
var/seconds2 = (seconds < 10) ? "0[seconds]" : seconds
|
||||
data["reloadtime_text"] = "[minutes]:[seconds2]"
|
||||
data["ready"] = minutes == 0 && seconds == 0
|
||||
else
|
||||
data["ready"] = FALSE
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/computer/bsa_control/Topic(href, href_list)
|
||||
/obj/machinery/computer/bsa_control/tgui_act(action, params)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(href_list["build"])
|
||||
cannon = deploy()
|
||||
. = TRUE
|
||||
else if(href_list["fire"])
|
||||
fire(usr)
|
||||
. = TRUE
|
||||
else if(href_list["recalibrate"])
|
||||
calibrate(usr)
|
||||
. = TRUE
|
||||
return
|
||||
switch(action)
|
||||
if("build")
|
||||
cannon = deploy()
|
||||
if("fire")
|
||||
fire(usr)
|
||||
if("recalibrate")
|
||||
calibrate(usr)
|
||||
update_icon()
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/computer/bsa_control/proc/calibrate(mob/user)
|
||||
var/list/gps_locators = list()
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
#define VAULT_NOBREATH "Lung Enhancement"
|
||||
#define VAULT_FIREPROOF "Thermal Regulation"
|
||||
#define VAULT_STUNTIME "Neural Repathing"
|
||||
#define VAULT_ARMOUR "Bone Reinforcement"
|
||||
#define VAULT_ARMOUR "Hardened Skin"
|
||||
#define VAULT_SPEED "Leg Muscle Stimulus"
|
||||
#define VAULT_QUICK "Arm Muscle Stimulus"
|
||||
|
||||
|
||||
@@ -111,6 +111,12 @@
|
||||
typepath = /obj/item/instrument/eguitar
|
||||
cost = 500
|
||||
|
||||
/datum/storeitem/banjo
|
||||
name = "Banjo"
|
||||
desc = "It's pretty much just a drum with a neck and strings."
|
||||
typepath = /obj/item/instrument/banjo
|
||||
cost = 500
|
||||
|
||||
/datum/storeitem/piano_synth
|
||||
name = "Piano Synthesizer"
|
||||
desc = "An advanced electronic synthesizer that can emulate various instruments."
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
var/obj/item/organ/external/BP = X
|
||||
var/brutedamage = BP.brute_dam
|
||||
|
||||
if(BP.is_robotic() && !ismachineperson(src))
|
||||
if(BP.is_robotic())
|
||||
continue
|
||||
|
||||
//We want an accurate reading of .len
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
var/eye_colour = "#000000" // Should never be null
|
||||
var/list/colourmatrix = null
|
||||
var/list/colourblind_matrix = MATRIX_GREYSCALE //Special colourblindness parameters. By default, it's black-and-white.
|
||||
var/list/replace_colours = LIST_GREYSCALE_REPLACE
|
||||
var/list/replace_colours = GREYSCALE_COLOR_REPLACE
|
||||
var/dependent_disabilities = list() //Gets set by eye-dependent disabilities such as colourblindness so the eyes can transfer the disability during transplantation.
|
||||
var/weld_proof = null //If set, the eyes will not take damage during welding. eg. IPC optical sensors do not take damage when they weld things while all other eyes will.
|
||||
|
||||
|
||||
@@ -236,35 +236,6 @@
|
||||
status &= ~ORGAN_SPLINTED
|
||||
status |= ORGAN_ROBOT
|
||||
|
||||
/obj/item/organ/external/emp_act(severity)
|
||||
if(!is_robotic() || emp_proof)
|
||||
return
|
||||
if(tough)
|
||||
switch(severity)
|
||||
if(1)
|
||||
receive_damage(0, 5.5)
|
||||
if(owner)
|
||||
owner.Stun(10)
|
||||
if(2)
|
||||
receive_damage(0, 2.8)
|
||||
if(owner)
|
||||
owner.Stun(5)
|
||||
else
|
||||
switch(severity)
|
||||
if(1)
|
||||
receive_damage(0, 20)
|
||||
if(2)
|
||||
receive_damage(0, 7)
|
||||
|
||||
/obj/item/organ/internal/emp_act(severity)
|
||||
if(!is_robotic() || emp_proof)
|
||||
return
|
||||
switch(severity)
|
||||
if(1)
|
||||
receive_damage(20, 1)
|
||||
if(2)
|
||||
receive_damage(7, 1)
|
||||
|
||||
/obj/item/organ/proc/shock_organ(intensity)
|
||||
return
|
||||
|
||||
|
||||
@@ -41,6 +41,9 @@
|
||||
var/list/obj/item/organ/external/children
|
||||
var/list/convertable_children = list()
|
||||
|
||||
// Does the organ take reduce damage from EMPs? IPC limbs get this by default
|
||||
var/emp_resistant = FALSE
|
||||
|
||||
// Internal organs of this body part
|
||||
var/list/internal_organs = list()
|
||||
|
||||
@@ -256,6 +259,34 @@
|
||||
|
||||
return update_icon()
|
||||
|
||||
/obj/item/organ/external/emp_act(severity)
|
||||
if(!is_robotic() || emp_proof)
|
||||
return
|
||||
if(tough) // Augmented limbs
|
||||
switch(severity)
|
||||
if(1)
|
||||
receive_damage(0, 5.5)
|
||||
if(owner)
|
||||
owner.Stun(10)
|
||||
if(2)
|
||||
receive_damage(0, 2.8)
|
||||
if(owner)
|
||||
owner.Stun(5)
|
||||
else if(emp_resistant) // IPC limbs
|
||||
switch(severity)
|
||||
if(1)
|
||||
// 5.28 (9 * 0.66 burn_mod) burn damage, 65.34 damage with 11 limbs.
|
||||
receive_damage(0, 9)
|
||||
if(2)
|
||||
// 3.63 (5 * 0.66 burn_mod) burn damage, 39.93 damage with 11 limbs.
|
||||
receive_damage(0, 5.5)
|
||||
else // Basic prosthetic limbs
|
||||
switch(severity)
|
||||
if(1)
|
||||
receive_damage(0, 20)
|
||||
if(2)
|
||||
receive_damage(0, 7)
|
||||
|
||||
/*
|
||||
This function completely restores a damaged organ to perfect condition.
|
||||
*/
|
||||
|
||||
@@ -73,6 +73,15 @@
|
||||
A.Remove(M)
|
||||
return src
|
||||
|
||||
/obj/item/organ/internal/emp_act(severity)
|
||||
if(!is_robotic() || emp_proof)
|
||||
return
|
||||
switch(severity)
|
||||
if(1)
|
||||
receive_damage(20, 1)
|
||||
if(2)
|
||||
receive_damage(7, 1)
|
||||
|
||||
/obj/item/organ/internal/replaced(var/mob/living/carbon/human/target)
|
||||
insert(target)
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
min_broken_damage = 30
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/head/ipc/New(mob/living/carbon/holder, datum/species/species_override = null)
|
||||
..(holder, /datum/species/machine) // IPC heads need to be explicitly set to this since you can print them
|
||||
@@ -13,6 +14,7 @@
|
||||
/obj/item/organ/external/chest/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/chest/ipc/New()
|
||||
..()
|
||||
@@ -21,6 +23,7 @@
|
||||
/obj/item/organ/external/groin/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/groin/ipc/New()
|
||||
..()
|
||||
@@ -29,6 +32,7 @@
|
||||
/obj/item/organ/external/arm/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/arm/ipc/New()
|
||||
..()
|
||||
@@ -37,6 +41,7 @@
|
||||
/obj/item/organ/external/arm/right/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/arm/right/ipc/New()
|
||||
..()
|
||||
@@ -45,6 +50,7 @@
|
||||
/obj/item/organ/external/leg/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/leg/ipc/New()
|
||||
..()
|
||||
@@ -53,6 +59,7 @@
|
||||
/obj/item/organ/external/leg/right/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/leg/right/ipc/New()
|
||||
..()
|
||||
@@ -61,6 +68,7 @@
|
||||
/obj/item/organ/external/foot/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/foot/ipc/New()
|
||||
..()
|
||||
@@ -69,6 +77,7 @@
|
||||
/obj/item/organ/external/foot/right/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/foot/right/ipc/New()
|
||||
..()
|
||||
@@ -77,6 +86,7 @@
|
||||
/obj/item/organ/external/hand/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/hand/ipc/New()
|
||||
..()
|
||||
@@ -85,6 +95,7 @@
|
||||
/obj/item/organ/external/hand/right/ipc
|
||||
encased = null
|
||||
status = ORGAN_ROBOT
|
||||
emp_resistant = TRUE
|
||||
|
||||
/obj/item/organ/external/hand/right/ipc/New()
|
||||
..()
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
icon = 'icons/obj/species_organs/tajaran.dmi'
|
||||
name = "tajaran eyeballs"
|
||||
colourblind_matrix = MATRIX_TAJ_CBLIND //The colour matrix and darksight parameters that the mob will recieve when they get the disability.
|
||||
replace_colours = LIST_TAJ_REPLACE
|
||||
replace_colours = TRITANOPIA_COLOR_REPLACE
|
||||
see_in_dark = 8
|
||||
|
||||
/obj/item/organ/internal/eyes/tajaran/farwa //Being the lesser form of Tajara, Farwas have an utterly incurable version of their colourblindness.
|
||||
name = "farwa eyeballs"
|
||||
colourmatrix = MATRIX_TAJ_CBLIND
|
||||
see_in_dark = 8
|
||||
replace_colours = LIST_TAJ_REPLACE
|
||||
replace_colours = TRITANOPIA_COLOR_REPLACE
|
||||
|
||||
/obj/item/organ/internal/heart/tajaran
|
||||
name = "tajaran heart"
|
||||
|
||||
@@ -7,14 +7,14 @@
|
||||
name = "vulpkanin eyeballs"
|
||||
icon = 'icons/obj/species_organs/vulpkanin.dmi'
|
||||
colourblind_matrix = MATRIX_VULP_CBLIND //The colour matrix and darksight parameters that the mob will recieve when they get the disability.
|
||||
replace_colours = LIST_VULP_REPLACE
|
||||
replace_colours = PROTANOPIA_COLOR_REPLACE
|
||||
see_in_dark = 8
|
||||
|
||||
/obj/item/organ/internal/eyes/vulpkanin/wolpin //Being the lesser form of Vulpkanin, Wolpins have an utterly incurable version of their colourblindness.
|
||||
name = "wolpin eyeballs"
|
||||
colourmatrix = MATRIX_VULP_CBLIND
|
||||
see_in_dark = 8
|
||||
replace_colours = LIST_VULP_REPLACE
|
||||
replace_colours = PROTANOPIA_COLOR_REPLACE
|
||||
|
||||
/obj/item/organ/internal/heart/vulpkanin
|
||||
name = "vulpkanin heart"
|
||||
|
||||
Reference in New Issue
Block a user