diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm
index d3fcecdbea..b5a80b1523 100644
--- a/code/__HELPERS/icon_smoothing.dm
+++ b/code/__HELPERS/icon_smoothing.dm
@@ -422,6 +422,7 @@
/atom
var/icon_type_smooth
var/junction
+ var/bypass_interactions = FALSE
/atom/proc/recalculate_junction()
junction = 0
diff --git a/code/_onclick/drag_drop.dm b/code/_onclick/drag_drop.dm
index a9300afbf5..8db23ce2f4 100644
--- a/code/_onclick/drag_drop.dm
+++ b/code/_onclick/drag_drop.dm
@@ -59,6 +59,12 @@
var/obj/item/h = get_active_held_item()
if(h)
. = h.CanItemAutoclick(object, location, params)
+ if(istype(loc, /obj/mecha))
+ var/obj/mecha/piloting = loc
+ if(piloting.selected && istype(piloting.selected, /obj/item/mecha_parts/mecha_equipment/weapon))
+ var/obj/item/mecha_parts/mecha_equipment/weapon/selectedweapon = piloting.selected
+ . = selectedweapon.is_automatic
+
/mob/proc/canMobMousedown(atom/object, location, params)
diff --git a/code/controllers/subsystem/jukeboxes.dm b/code/controllers/subsystem/jukeboxes.dm
index 614ffb6c89..ea50b50410 100644
--- a/code/controllers/subsystem/jukeboxes.dm
+++ b/code/controllers/subsystem/jukeboxes.dm
@@ -97,6 +97,14 @@ SUBSYSTEM_DEF(jukeboxes)
var/area/currentarea = get_area(jukebox)
var/turf/currentturf = get_turf(jukebox)
var/list/hearerscache = hearers(7, jukebox)
+ var/turf/currentturfabove = SSmapping.get_turf_above(currentturf)
+ if(istype(currentturf, /turf/open/transparent/openspace))
+ var/turf/belowturf = SSmapping.get_turf_below(currentturf)
+ hearerscache += hearers(7, belowturf)
+ if(istype(currentturfabove, /turf/open/transparent/openspace))
+ hearerscache += hearers(7, currentturfabove)
+ if(!isturf(jukebox.loc))
+ hearerscache += hearers(7, jukebox.loc)
song_played.falloff = jukeinfo[4]
@@ -108,16 +116,16 @@ SUBSYSTEM_DEF(jukeboxes)
continue
var/inrange = FALSE
- if(jukebox.z == M.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master
- song_played.status = SOUND_UPDATE
- if(get_area(M) == currentarea)
- inrange = TRUE
- else if(M in hearerscache)
- inrange = TRUE
+ //if(jukebox.z == M.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master
+ song_played.status = SOUND_UPDATE
+ if(get_area(M) == currentarea)
+ inrange = TRUE
+ else if(M in hearerscache)
+ inrange = TRUE
else
song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame.
- M.playsound_local(currentturf, null, 100, channel = jukeinfo[2], S = song_played, envwet = (inrange ? -250 : 0), envdry = (inrange ? 0 : -10000))
+ M.playsound_local(M, null, 100, channel = jukeinfo[2], S = song_played, envwet = (inrange ? -250 : 0), envdry = (inrange ? 0 : -10000))
CHECK_TICK
return
//BIG IRON EDIT start
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index ec86f6735c..e1dd0102c8 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -86,7 +86,13 @@ SUBSYSTEM_DEF(throwing)
if (dist_travelled && hitcheck()) //to catch sneaky things moving on our tile while we slept
finalize()
return
-
+ var/turf/starting_turf = get_turf(AM)
+ if(AM.z < target_turf.z)
+ var/turf/new_turf = SSmapping.get_turf_above(starting_turf)
+ AM.forceMove(new_turf)
+ if(starting_turf.z > target_turf.z)
+ var/turf/new_turf = SSmapping.get_turf_below(starting_turf)
+ AM.forceMove(new_turf)
var/atom/step
last_move = world.time
diff --git a/code/datums/components/crafting/recipes/recipes_carparts.dm b/code/datums/components/crafting/recipes/recipes_carparts.dm
index 270d25b02e..00433bc3a8 100644
--- a/code/datums/components/crafting/recipes/recipes_carparts.dm
+++ b/code/datums/components/crafting/recipes/recipes_carparts.dm
@@ -23,6 +23,18 @@
category = CAT_CARPARTS
subcategory = CAT_CAREQUIP
+/datum/crafting_recipe/carpart/stereo
+ name = "Mounted Stereo"
+ result = /obj/item/mecha_parts/mecha_equipment/stereo
+ reqs = list(/obj/item/stack/sheet/metal = 5,
+ /obj/item/stack/crafting/metalparts = 10,
+ /obj/item/circuitboard/machine/jukebox = 1,
+ /obj/item/stack/rods = 5)
+ tools = list(TOOL_WORKBENCH)
+ time = 90
+ category = CAT_CARPARTS
+ subcategory = CAT_CAREQUIP
+
/datum/crafting_recipe/carpart/Car_engine
name = "Car Engine"
result = /obj/item/mecha_parts/part/Car_engine
diff --git a/code/datums/components/crafting/recipes/recipes_tools.dm b/code/datums/components/crafting/recipes/recipes_tools.dm
index c4937363bc..2fe37ac87c 100644
--- a/code/datums/components/crafting/recipes/recipes_tools.dm
+++ b/code/datums/components/crafting/recipes/recipes_tools.dm
@@ -130,3 +130,14 @@
/obj/item/stack/rods = 2)
category = CAT_CRAFTING
subcategory = CAT_TOOL
+
+/datum/crafting_recipe/cellupgrade
+ name = "High cell to Ultra cell convertion"
+ result = /obj/item/stock_parts/cell/bluespace
+ time = 80
+ reqs = list(/obj/item/stock_parts/cell/high = 4,
+ /obj/item/stack/cable_coil = 10,
+ /obj/item/toy/crayon/spraycan = 1)
+ tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
+ category = CAT_CRAFTING
+ subcategory = CAT_TOOL
diff --git a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
index aeaff12021..24c8cb6583 100644
--- a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
+++ b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm
@@ -622,6 +622,199 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
+/datum/crafting_recipe/gun/InmolatorCannon
+ name = "CH-PS \"Immolator\" laser"
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser
+ reqs = list(/obj/item/stack/crafting/metalparts = 10,
+ /obj/item/stack/crafting/electronicparts = 10,
+ /obj/item/stack/sheet/metal = 15,
+ /obj/item/advanced_crafting_components/lenses = 1,
+ /obj/item/advanced_crafting_components/conductors = 1)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/gun/SolarisCannon
+ name = "CH-PS \"Immolator\" laser"
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/energy/laser/heavy
+ reqs = list(/obj/item/stack/crafting/metalparts = 10,
+ /obj/item/stack/crafting/electronicparts = 10,
+ /obj/item/stack/crafting/goodparts = 10,
+ /obj/item/stack/sheet/mineral/titanium = 5,
+ /obj/item/advanced_crafting_components/lenses = 1,
+ /obj/item/advanced_crafting_components/conductors = 2,
+ /obj/item/advanced_crafting_components/flux = 1)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/gun/CarbineVehicle
+ name = "FNX-99 \"Hades\" Carbine"
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/carbine
+ reqs = list(/obj/item/stack/crafting/metalparts = 5,
+ /obj/item/stack/sheet/metal = 10,
+ /obj/item/stack/rods = 2,
+ /obj/item/advanced_crafting_components/assembly = 1,
+ /datum/reagent/fuel = 50)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/gun/Scattershot
+ name = "LBX AC 10 \"Scattershot\""
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/scattershot
+ reqs = list(/obj/item/stack/crafting/metalparts = 10,
+ /obj/item/stack/crafting/goodparts = 5,
+ /obj/item/stack/sheet/metal = 15,
+ /obj/item/stack/rods = 4,
+ /obj/item/advanced_crafting_components/assembly = 1,)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/gun/minigunVehicle
+ name = "Minigun"
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/minigun
+ reqs = list(/obj/item/stack/crafting/metalparts = 10,
+ /obj/item/stack/crafting/goodparts = 5,
+ /obj/item/stack/crafting/electronicparts = 5,
+ /obj/item/stack/sheet/metal = 10,
+ /obj/item/stack/sheet/mineral/titanium = 20,
+ /obj/item/stack/rods = 6,
+ /obj/item/advanced_crafting_components/assembly = 1,
+ /obj/item/advanced_crafting_components/receiver = 1,
+ /obj/item/advanced_crafting_components/alloys = 1)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/gun/PheumonicLauncherVehicle
+ name = "Mounted Pheumonic launcher"
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind
+ reqs = list(/obj/item/stack/crafting/metalparts = 20,
+ /obj/item/stack/crafting/goodparts = 10,
+ /obj/item/stack/crafting/electronicparts = 5,
+ /obj/item/stack/sheet/metal = 30,
+ /obj/item/stack/sheet/mineral/titanium = 20,
+ /obj/item/stack/rods = 8,
+ /obj/item/advanced_crafting_components/assembly = 1,
+ /obj/item/advanced_crafting_components/receiver = 1)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/gun/Missile_rack
+ name = "SRM-8 missile rack"
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack
+ reqs = list(/obj/item/stack/crafting/metalparts = 20,
+ /obj/item/stack/crafting/goodparts = 20,
+ /obj/item/stack/crafting/electronicparts = 10,
+ /obj/item/stack/sheet/mineral/titanium = 50,
+ /obj/item/stack/rods = 8,
+ /obj/item/advanced_crafting_components/alloys = 1)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/gun/Breach_missile_rack
+ name = "BRM-6 missile rack"
+ result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack/breaching
+ reqs = list(/obj/item/stack/crafting/metalparts = 20,
+ /obj/item/stack/crafting/goodparts = 20,
+ /obj/item/stack/crafting/electronicparts = 10,
+ /obj/item/stack/sheet/mineral/titanium = 40,
+ /obj/item/stack/rods = 6,
+ /obj/item/advanced_crafting_components/alloys = 1)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_WEAPON
+
+/datum/crafting_recipe/mech_ammo/scattershot
+ name = "Scattershot ammo pack"
+ result = /obj/item/mecha_ammo/scattershot
+ reqs = list(/obj/item/ammo_box/shotgun/buck = 4,
+ /obj/item/stack/crafting/metalparts = 5,
+ /obj/item/stack/crafting/powder = 10)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/mech_ammo/carbine
+ name = "Hades ammo pack"
+ result = /obj/item/mecha_ammo/incendiary
+ reqs = list(/obj/item/ammo_box/a762box = 1,
+ /datum/reagent/fuel = 40,
+ /obj/item/stack/crafting/powder = 10)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/mech_ammo/srm8_missiles
+ name = "SRM-8 missile pack"
+ result = /obj/item/mecha_ammo/missiles_he
+ reqs = list(/obj/item/ammo_casing/caseless/rocket = 3,
+ /obj/item/stack/sheet/metal = 10,
+ /obj/item/assembly/timer = 4)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/mech_ammo/brm8_missiles
+ name = "BRM-8 missile pack"
+ result = /obj/item/mecha_ammo/missiles_br
+ reqs = list(/obj/item/ammo_casing/caseless/rocket = 4,
+ /obj/item/stack/sheet/mineral/titanium = 20,
+ /obj/item/stack/crafting/powder = 10)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/mech_ammo/minigun_ammo
+ name = "Minigun Ammo Pack"
+ result = /obj/item/mecha_ammo/minigun
+ reqs = list(/obj/item/ammo_box/magazine/ammobelt/m1919 = 3,
+ /obj/item/stack/sheet/metal = 10,
+ /obj/item/stack/sheet/mineral/titanium = 20,
+ /obj/item/stack/crafting/powder = 30)
+ tools = list(TOOL_WORKBENCH)
+ time = 180
+ category = CAT_WEAPONRY
+ subcategory = CAT_AMMO
+
+/datum/crafting_recipe/mech_ammo/antoproj_armor
+ name = "armor booster module (Ranged Weaponry)"
+ result = /obj/item/mecha_parts/mecha_equipment/antiproj_armor_booster
+ reqs = list(/obj/item/shield/riot/tower = 2,
+ /obj/item/stack/cable_coil = 5,
+ /obj/item/stack/sheet/mineral/titanium = 20)
+ tools = list(TOOL_WELDER)
+ time = 180
+ category = CAT_CARPARTS
+ subcategory = CAT_CAREQUIP
+
+/datum/crafting_recipe/mech_ammo/antomelee_armor
+ name = "armor booster module (Melee Weaponry)"
+ result = /obj/item/mecha_parts/mecha_equipment/anticcw_armor_booster
+ reqs = list(/obj/item/shield/riot/tower = 2,
+ /obj/item/stack/cable_coil = 5,
+ /obj/item/stack/sheet/mineral/titanium = 20)
+ tools = list(TOOL_WELDER)
+ time = 180
+ category = CAT_CARPARTS
+ subcategory = CAT_CAREQUIP
+
/datum/crafting_recipe/gun/HMGvehicule
name = "Improvised HMG (for vehicules)"
result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg/hobo
diff --git a/code/game/mecha/combat/phazon.dm b/code/game/mecha/combat/phazon.dm
index 20a353e264..6e1ecbc265 100644
--- a/code/game/mecha/combat/phazon.dm
+++ b/code/game/mecha/combat/phazon.dm
@@ -88,7 +88,7 @@
strafing_action.Remove(user)
zoom_action.Remove(user)
eject_action.Remove(user)
- landing_action.Grant(user)
+ landing_action.Remove(user)
//rotorup_action.Remove(user)
//rotordown_action.Remove(user)
@@ -181,7 +181,7 @@
zoom_action.Remove(user)
eject_action.Remove(user)
smoke_action.Remove(user)
- landing_action.Grant(user)
+ landing_action.Remove(user)
//rotorup_action.Remove(user)
//rotordown_action.Remove(user)
@@ -262,7 +262,7 @@
strafing_action.Remove(user)
zoom_action.Remove(user)
eject_action.Remove(user)
- landing_action.Grant(user)
+ landing_action.Remove(user)
//rotorup_action.Remove(user)
//rotordown_action.Remove(user)
@@ -337,7 +337,7 @@
strafing_action.Remove(user)
zoom_action.Remove(user)
eject_action.Remove(user)
- landing_action.Grant(user)
+ landing_action.Remove(user)
//rotorup_action.Remove(user)
//rotordown_action.Remove(user)
@@ -401,6 +401,7 @@
zoom_action.Grant(user, src)
eject_action.Grant(user, src)
smoke_action.Grant(user, src)
+ landing_action.Grant(user, src)
//rotorup_action.Grant(user, src)
//rotordown_action.Grant(user, src)
@@ -413,6 +414,7 @@
zoom_action.Remove(user)
eject_action.Remove(user)
smoke_action.Remove(user)
+ landing_action.Remove(user, src)
//rotorup_action.Remove(user)
//rotordown_action.Remove(user)
diff --git a/code/game/mecha/equipment/tools/other_tools.dm b/code/game/mecha/equipment/tools/other_tools.dm
index e74c6edce0..511a63e71d 100644
--- a/code/game/mecha/equipment/tools/other_tools.dm
+++ b/code/game/mecha/equipment/tools/other_tools.dm
@@ -586,3 +586,221 @@
/obj/item/mecha_parts/mecha_equipment/generator/nuclear/process()
if(..())
radiation_pulse(get_turf(src), rad_per_cycle)
+
+/////////////////////////////////////////// STEREO SYSTEM /////////////////////////////////////////////
+
+
+/obj/item/mecha_parts/mecha_equipment/stereo
+ name = "exosuit Stereo System"
+ desc = "a stereo system hooked up a jukebox, modified for easy transport."
+ icon_state = "mecha_stereo"
+ range = MELEE
+ var/active = FALSE
+ var/list/rangers = list()
+ var/stop = 0
+ var/volume = 70
+ var/datum/track/selection = null
+ var/open_tray = TRUE
+ var/list/obj/item/record_disk/record_disks = list()
+ var/obj/item/record_disk/selected_disk = null
+
+/obj/item/mecha_parts/mecha_equipment/stereo/attach(obj/mecha/M)
+ . = ..()
+ bypass_interactions = TRUE
+
+/obj/item/mecha_parts/mecha_equipment/stereo/detach(obj/mecha/M)
+ . = ..()
+ bypass_interactions = FALSE
+
+/obj/item/mecha_parts/mecha_equipment/stereo/attackby(obj/item/O, mob/user, params)
+ . = ..()
+ if(!active)
+ if(istype(O, /obj/item/record_disk)) //this one checks for a record disk and if the jukebox is open, it adds it to the machine
+ if(open_tray == FALSE)
+ to_chat(usr, "The Disk Tray is not open!")
+ return
+ var/obj/item/record_disk/I = O
+ if(!I.R.song_associated_id)
+ to_chat(user, span_warning("This record is empty!"))
+ return
+ for(var/datum/track/RT in SSjukeboxes.songs)
+ if(I.R.song_associated_id == RT.song_associated_id)
+ to_chat(user, span_warning("this track is already added to the jukebox!"))
+ return
+ record_disks += I
+ O.forceMove(src)
+ playsound(src, 'sound/effects/plastic_click.ogg', 100, 0)
+ if(I.R.song_path)
+ SSjukeboxes.add_song(I.R)
+ return
+
+/obj/item/mecha_parts/mecha_equipment/stereo/proc/eject_record(obj/item/record_disk/M) //BIG IRON EDIT -start- ejects a record as defined and removes it's song from the list
+ if(!M)
+ visible_message("no disk to eject")
+ return
+ playsound(src, 'sound/effects/disk_tray.ogg', 100, 0)
+ src.visible_message(" ejected the [selected_disk] from the [src]!")
+ M.forceMove(get_turf(src))
+ SSjukeboxes.remove_song(M.R)
+ record_disks -= M
+ selected_disk = null
+
+/obj/item/mecha_parts/mecha_equipment/stereo/ui_status(mob/user)
+ if(!SSjukeboxes.songs.len && !isobserver(user))
+ to_chat(user,"Error: No music tracks have been authorized for your station. Petition Central Command to resolve this issue.")
+ playsound(src, 'sound/misc/compiler-failure.ogg', 25, TRUE)
+ return UI_CLOSE
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/stereo/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "Jukebox", name)
+ ui.open()
+
+/obj/item/mecha_parts/mecha_equipment/stereo/ui_data(mob/user)
+ var/list/data = list()
+ data["active"] = active
+ data["songs"] = list()
+ for(var/datum/track/S in SSjukeboxes.songs)
+ var/list/track_data = list(
+ name = S.song_name
+ )
+ data["songs"] += list(track_data)
+ data["track_selected"] = null
+ data["track_length"] = null
+ data["track_beat"] = null
+ data["disks"] = list()
+ for(var/obj/item/record_disk/RD in record_disks)
+ var/list/tracks_data = list(
+ name = RD.name
+ )
+ data["disks"] += list(tracks_data)
+ data["disk_selected"] = null //BIG IRON EDIT- start more tracks data
+ data["disk_selected_lenght"] = null
+ data["disk_beat"] = null //BIG IRON EDIT -end
+ if(selection)
+ data["track_selected"] = selection.song_name
+ data["track_length"] = DisplayTimeText(selection.song_length)
+ data["track_beat"] = selection.song_beat
+ if(selected_disk)
+ data["disk_selected"] = selected_disk
+ data["disk_selected_length"] = DisplayTimeText(selected_disk.R.song_length)
+ data["disk_selected_beat"] = selected_disk.R.song_beat
+ data["volume"] = volume
+ return data
+
+/obj/item/mecha_parts/mecha_equipment/stereo/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("toggle")
+ if(QDELETED(src))
+ return
+ if(!active)
+ if(stop > world.time)
+ to_chat(usr, "Error: The device is still resetting from the last activation, it will be ready again in [DisplayTimeText(stop-world.time)].")
+ playsound(src, 'sound/misc/compiler-failure.ogg', 50, TRUE)
+ return
+ activate_music()
+ START_PROCESSING(SSobj, src)
+ return TRUE
+ else
+ stop = 0
+ return TRUE
+ if("select_track")
+ if(active)
+ to_chat(usr, "Error: You cannot change the song until the current one is over.")
+ return
+ var/list/available = list()
+ for(var/datum/track/S in SSjukeboxes.songs)
+ available[S.song_name] = S
+ var/selected = params["track"]
+ if(QDELETED(src) || !selected || !istype(available[selected], /datum/track))
+ return
+ selection = available[selected]
+ return TRUE
+ if("select_record")
+ if(!record_disks.len)
+ to_chat(usr, "Error: no tracks on the bin!.")
+ return
+ var/list/obj/item/record_disk/availabledisks = list()
+ for(var/obj/item/record_disk/RR in record_disks)
+ availabledisks[RR.name] = RR
+ var/selecteddisk = params["record"]
+ if(QDELETED(src) || !selecteddisk)
+ return
+ selected_disk = availabledisks[selecteddisk]
+ updateUsrDialog()
+ if("eject_disk") // sanity check for the disk ejection
+ if(!record_disks.len)
+ to_chat(usr, "Error: no disks in trays.")
+ return
+ if(!selected_disk)
+ to_chat(usr,"Error: no disk chosen." )
+ return
+ if(selection == selected_disk.R)
+ selection = null
+ eject_record(selected_disk)
+ return TRUE
+ if("set_volume")
+ var/new_volume = params["volume"]
+ if(new_volume == "reset")
+ volume = initial(volume)
+ return TRUE
+ else if(new_volume == "min")
+ volume = 0
+ return TRUE
+ else if(new_volume == "max")
+ volume = 100
+ return TRUE
+ else if(text2num(new_volume) != null)
+ volume = text2num(new_volume)
+ return TRUE
+
+/obj/item/mecha_parts/mecha_equipment/stereo/proc/activate_music()
+ if(!selection)
+ visible_message("Track is no longer avaible")
+ return
+ var/jukeboxslottotake = SSjukeboxes.addjukebox(src, selection, 2)
+ if(jukeboxslottotake)
+ active = TRUE
+ update_icon()
+ START_PROCESSING(SSobj, src)
+ stop = world.time + selection.song_length
+ return TRUE
+ else
+ return FALSE
+
+/obj/item/mecha_parts/mecha_equipment/stereo/get_equip_info()
+ var/output = ..()
+ if(output)
+ var/temp = ""
+ temp = "Dashboard"
+ return "[output] [temp]"
+ return
+
+/obj/item/mecha_parts/mecha_equipment/stereo/Topic(href,href_list)
+ ..()
+ if(href_list["dashboard"])
+ var/mob/user = chassis.occupant
+ ui_interact(user)
+ return
+
+/obj/item/mecha_parts/mecha_equipment/stereo/process()
+ if(active && world.time >= stop)
+ active = FALSE
+ dance_over()
+ playsound(src,'sound/machines/terminal_off.ogg',50,1)
+ update_icon()
+ stop = world.time + 100
+
+/obj/item/mecha_parts/mecha_equipment/stereo/proc/dance_over()
+ var/position = SSjukeboxes.findjukeboxindex(src)
+ if(!position)
+ return
+ SSjukeboxes.removejukebox(position)
+ STOP_PROCESSING(SSobj, src)
+ rangers = list()
diff --git a/code/game/mecha/equipment/weapons/mecha_ammo.dm b/code/game/mecha/equipment/weapons/mecha_ammo.dm
index 7a7ea295f5..020fdc9d06 100644
--- a/code/game/mecha/equipment/weapons/mecha_ammo.dm
+++ b/code/game/mecha/equipment/weapons/mecha_ammo.dm
@@ -99,3 +99,10 @@
round_term = "cluster"
direct_load = TRUE
ammo_type = "clusterbang"
+
+/obj/item/mecha_ammo/minigun
+ name = "Minigun ammo pack"
+ desc = "A box of high caliber ammo, ready to be consumed in nano seconds. Cannot be primed by hand."
+ icon_state = "lmg"
+ rounds = 600
+ ammo_type = "minigun"
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 172e32645b..1f50c6217e 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -268,6 +268,7 @@
projectiles_cache = 300
projectiles_cache_max = 1200
projectiles_per_shot = 3
+ is_automatic = TRUE
variance = 6
randomspread = 1
projectile_delay = 2
@@ -308,6 +309,85 @@
harmful = TRUE
ammo_type = "lmg"
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/minigun
+ name = "\improper Minigun"
+ desc = "A heavy machine gun capable of rapidly firing 7.62mm rounds. ready for vehicle mounting, with internal ammo box."
+ icon_state = "mecha_uac2"
+ fire_sound = 'sound/f13weapons/antimaterielfire.ogg'
+ equip_cooldown = 1
+ projectile = /obj/item/projectile/bullet/a762
+ projectiles = 300
+ projectiles_cache = 300
+ projectiles_cache_max = 600
+ projectiles_per_shot = 1
+ variance = 6
+ is_automatic = TRUE
+ randomspread = 112
+ harmful = TRUE
+ ammo_type = "minigun"
+ var/overheat = 0
+ var/overheat_max = 160
+ var/heat_diffusion = 2.5 //How much heat is lost per tick
+ var/damage = 25
+
+
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/minigun/Initialize()
+ . = ..()
+ START_PROCESSING(SSobj, src)
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/minigun/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/minigun/process()
+ overheat = max(0, overheat - heat_diffusion)
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/minigun/action(atom/target, params)
+ if(!action_checks(target))
+ return 0
+ var/turf/curloc = get_turf(chassis)
+ var/turf/targloc = get_turf(target)
+ if (!targloc || !istype(targloc) || !curloc)
+ return 0
+ if (targloc == curloc)
+ return 0
+ if(overheat < overheat_max)
+ overheat += projectiles_per_shot
+ else
+ chassis.occupant_message("The gun's heat sensor locked the trigger to prevent barrel damage.")
+ return
+ chassis.occupant.DelayNextAction(3)
+ set_ready_state(0)
+ for(var/i=1 to get_shot_amount())
+ var/obj/item/projectile/A = new projectile(curloc)
+ A.firer = chassis.occupant
+ A.original = target
+ A.damage = damage
+ if(!A.suppressed && firing_effect_type)
+ new firing_effect_type(get_turf(src), chassis.dir)
+
+ var/spread = 0
+ if(variance)
+ if(randomspread)
+ spread = round((rand() - 0.5) * variance)
+ else
+ spread = round((i / projectiles_per_shot - 0.5) * variance)
+ A.preparePixelProjectile(target, chassis.occupant, params, spread)
+
+ A.fire()
+ overheat++
+ projectiles--
+ playsound(chassis, fire_sound, 50, 1)
+ chassis.occupant.DelayNextAction(1)
+
+ if(kickback)
+ chassis.newtonian_move(turn(chassis.dir,180))
+
+ return 1
+
+
+
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/missile_rack
name = "\improper SRM-8 missile rack"
desc = "A weapon for combat exosuits. Launches light explosive missiles."
@@ -356,6 +436,90 @@
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/proc/proj_init(obj/O)
return
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind
+ name = "\improper Pheumonic launcher"
+ desc = "A weapon for combat exosuits. anything loaded in it."
+ icon_state = "mecha_grenadelnchr"
+ projectile = null
+ fire_sound = 'sound/weapons/grenadelaunch.ogg'
+ projectiles = 0
+ projectiles_cache = 15
+ projectiles_cache_max = 20
+ missile_speed = 1.5
+ equip_cooldown = 10
+ var/det_time = 20
+ ammo_type = "Anything"
+ var/list/obj/stuffs = new
+ var/open = FALSE
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind/action(target)
+ if(!action_checks(target))
+ return
+ if(!stuffs.len)
+ chassis.occupant_message("Nothing to shoot!")
+ return
+ var/obj/O = stuffs[1]
+ playsound(chassis, fire_sound, 50, 1)
+ mecha_log_message("Launched a [O.name] from [name], targeting [target].")
+ stuffs -= stuffs[1]
+ proj_init(O)
+ var/turf/nextt = (get_turf(src))
+ O.forceMove(nextt)
+ O.throw_at(target, missile_range, missile_speed, chassis.occupant, FALSE, diagonals_first = diags_first)
+ return 1
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind/proj_init(obj/ammo)
+ var/turf/T = get_turf(src)
+ message_admins("[ADMIN_LOOKUPFLW(chassis.occupant)] fired a [src] in [ADMIN_VERBOSEJMP(T)]")
+ log_game("[key_name(chassis.occupant)] fired a [src] in [AREACOORD(T)]")
+ if(istype(ammo, /obj/item/grenade/))
+ var/obj/item/grenade/payload = ammo
+ addtimer(CALLBACK(payload, /obj/item/grenade.proc/prime), det_time)
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind/attackby(obj/item/W, mob/user, params)
+ if(open)
+ if(stuffs.len < projectiles_cache_max)
+ W.forceMove(src)
+ stuffs += W
+ projectiles++
+ else
+ to_chat(user, "The [src] is full!")
+ return
+ . = ..()
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind/screwdriver_act(mob/living/carbon/user, obj/item/I)
+ if(user.a_intent != INTENT_DISARM)
+ if(open)
+ to_chat(user, "You close the [src]!.")
+ else
+ to_chat(user, "You open the [src]!.")
+ open = !open
+ return TRUE
+ . = ..()
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind/attack_self(mob/user)
+ if(open && stuffs.len)
+ var/obj/selectedthing = input(user, "Chosee an item to take out.", "Stuffs inside") as null|anything in stuffs
+ if(!selectedthing)
+ return
+ stuffs -= selectedthing
+ projectiles--
+ selectedthing.forceMove(get_turf(src))
+ user.put_in_hand(selectedthing)
+ return
+ . = ..()
+
+/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/anykind/AltClick(mob/user)
+ if(open && stuffs.len)
+ for(var/obj/I in stuffs)
+ I.forceMove(get_turf(src))
+ stuffs -= I
+ projectiles--
+ to_chat(user, "You empty the [src]!.")
+ return
+ . = ..()
+
+
/obj/item/mecha_parts/mecha_equipment/weapon/ballistic/launcher/flashbang
name = "\improper SGL-6 grenade launcher"
diff --git a/code/modules/keybindings/keybind/carbon.dm b/code/modules/keybindings/keybind/carbon.dm
index 46cb5cd0ac..fa01eae3b8 100644
--- a/code/modules/keybindings/keybind/carbon.dm
+++ b/code/modules/keybindings/keybind/carbon.dm
@@ -60,3 +60,39 @@
/datum/keybinding/carbon/select_harm_intent/down(client/user)
user.mob?.a_intent_change(INTENT_HARM)
return TRUE
+
+/datum/keybinding/carbon/lookup
+ hotkey_keys = list(",")
+ name = "Look_up"
+ full_name = "Look up"
+ description = "looks up"
+ category = CATEGORY_CARBON
+
+/datum/keybinding/carbon/lookup/down(client/user)
+ var/mob/living/carbon/C = user.mob
+ C.lookup()
+ return TRUE
+
+/datum/keybinding/carbon/lookdown
+ hotkey_keys = list(".")
+ name = "Look_down"
+ full_name = "looks down"
+ description = "looks down your feet"
+ category = CATEGORY_CARBON
+
+/datum/keybinding/carbon/lookdown/down(client/user)
+ var/mob/living/carbon/C = user.mob
+ C.lookdown()
+ return TRUE
+
+/datum/keybinding/carbon/lookstop
+ hotkey_keys = list("-")
+ name = "Look_stop"
+ full_name = "stop looking"
+ description = "stop looking around and see foward"
+ category = CATEGORY_CARBON
+
+/datum/keybinding/carbon/lookstop/down(client/user)
+ var/mob/living/carbon/C = user.mob
+ C.stop_looking()
+ return TRUE
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 204d342933..db465a7f8a 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -427,6 +427,9 @@
set category = "IC"
if(src.incapacitated())
to_chat(src, "You can't look up right now!")
+ if(client.eye != src && !istype(client.eye, /obj/mecha))
+ stop_looking()
+ return
var/turf/T = SSmapping.get_turf_above(get_turf(src))
if(!istype(T, /turf/open/transparent/openspace))
if(istype(T, /turf/open) || istype(T, /turf/closed))
@@ -435,16 +438,82 @@
else
src.reset_perspective(T)
RegisterSignal(src, COMSIG_MOB_CLIENT_CHANGE_VIEW, .proc/stop_looking_up) //no binos/scops
- RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/stop_looking_up)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/followcameraup)
+ if(istype(loc, /obj/mecha))
+ RegisterSignal(loc, COMSIG_MOVABLE_MOVED, .proc/followcameraup)
RegisterSignal(src, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/stop_looking_up)
RegisterSignal(src, COMSIG_LIVING_STATUS_PARALYZE, .proc/stop_looking_up)
RegisterSignal(src, COMSIG_LIVING_STATUS_UNCONSCIOUS, .proc/stop_looking_up)
RegisterSignal(src, COMSIG_LIVING_STATUS_SLEEP, .proc/stop_looking_up)
+/mob/living/verb/stop_looking()
+ set name = "Stop Looking"
+ set category = "IC"
+ src.stop_looking_up(null)
+
/mob/living/proc/stop_looking_up()
+ if(istype(loc, /obj/mecha))
+ UnregisterSignal(loc, COMSIG_MOVABLE_MOVED)
reset_perspective(null)
UnregisterSignal(src, list(COMSIG_LIVING_STATUS_PARALYZE, COMSIG_LIVING_STATUS_UNCONSCIOUS, COMSIG_LIVING_STATUS_SLEEP, COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_MOVABLE_MOVED, COMSIG_MOB_CLIENT_CHANGE_VIEW))
+/mob/living/verb/lookdown()
+ set name = "Look down"
+ set category = "IC"
+ if(src.incapacitated())
+ to_chat(src, "You can't look down right now!")
+ if(client.eye != src && !istype(client.eye, /obj/mecha))
+ stop_looking()
+ return
+ var/turf/T = get_turf(src)
+ if(!istype(T, /turf/open/transparent/openspace))
+ var/turf/nt = get_step(T, dir)
+ if(!istype(nt, /turf/open/transparent/openspace))
+ if(istype(nt, /turf/open) || istype(nt, /turf/closed))
+ to_chat(src, "You look up at the floor. You can see floor.")
+ return
+ else
+ var/turf/nl = SSmapping.get_turf_below(nt)
+ src.reset_perspective(nl)
+ RegisterSignal(src, COMSIG_MOB_CLIENT_CHANGE_VIEW, .proc/stop_looking_down) //no binos/scops
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/followcameradown)
+ if(istype(loc, /obj/mecha))
+ RegisterSignal(loc, COMSIG_MOVABLE_MOVED, .proc/followcameradown)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/stop_looking_down)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_PARALYZE, .proc/stop_looking_down)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_UNCONSCIOUS, .proc/stop_looking_down)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_SLEEP, .proc/stop_looking_down)
+ else
+ var/turf/nl = SSmapping.get_turf_below(T)
+ src.reset_perspective(nl)
+ RegisterSignal(src, COMSIG_MOB_CLIENT_CHANGE_VIEW, .proc/stop_looking_down) //no binos/scops
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, .proc/followcameradown)
+ if(istype(loc, /obj/mecha))
+ RegisterSignal(loc, COMSIG_MOVABLE_MOVED, .proc/followcameradown)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_KNOCKDOWN, .proc/stop_looking_down)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_PARALYZE, .proc/stop_looking_down)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_UNCONSCIOUS, .proc/stop_looking_down)
+ RegisterSignal(src, COMSIG_LIVING_STATUS_SLEEP, .proc/stop_looking_down)
+
+/mob/living/proc/stop_looking_down()
+ reset_perspective(null)
+ UnregisterSignal(src, list(COMSIG_LIVING_STATUS_PARALYZE, COMSIG_LIVING_STATUS_UNCONSCIOUS, COMSIG_LIVING_STATUS_SLEEP, COMSIG_LIVING_STATUS_KNOCKDOWN, COMSIG_MOVABLE_MOVED, COMSIG_MOB_CLIENT_CHANGE_VIEW))
+
+/mob/living/proc/followcameraup()
+ var/turf/T = get_turf(src)
+ var/turf/nl = SSmapping.get_turf_above(T)
+ if(istype(nl, /turf/open/transparent/openspace))
+ reset_perspective(nl)
+ else
+ reset_perspective(null)
+
+/mob/living/proc/followcameradown()
+ var/turf/T = get_turf(src)
+ var/turf/nl = SSmapping.get_turf_below(T)
+ if(istype(T, /turf/open/transparent/openspace))
+ reset_perspective(nl)
+ else
+ reset_perspective(null)
/mob/living/incapacitated(ignore_restraints = FALSE, ignore_grab = FALSE, check_immobilized = FALSE)
if(stat || IsUnconscious() || IsStun() || IsParalyzed() || (combat_flags & COMBAT_FLAG_HARD_STAMCRIT) || (check_immobilized && IsImmobilized()) || (!ignore_restraints && restrained(ignore_grab)))
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index a42ee45e01..a0ee164c82 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -857,7 +857,7 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
//Can the mob interact() with an atom?
/mob/proc/can_interact_with(atom/A)
- return IsAdminGhost(src) || Adjacent(A) || A.hasSiliconAccessInArea(src)
+ return IsAdminGhost(src) || Adjacent(A) || A.hasSiliconAccessInArea(src) || A.bypass_interactions
//Can the mob use Topic to interact with machines
/mob/proc/canUseTopic(atom/movable/M, be_close=FALSE, no_dextery=FALSE, no_tk=FALSE)
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index f26d9c18b2..af27ba211d 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -502,6 +502,11 @@
if(spread)
setAngle(Angle + ((rand() - 0.5) * spread))
var/turf/starting = get_turf(src)
+ if(original)
+ if(starting.z > original?.z)
+ starting = SSmapping.get_turf_below(starting)
+ if(starting.z < original?.z)
+ starting = SSmapping.get_turf_above(starting)
if(isnull(Angle)) //Try to resolve through offsets if there's no angle set.
if(isnull(xo) || isnull(yo))
stack_trace("WARNING: Projectile [type] deleted due to being unable to resolve a target after angle was null!")
diff --git a/icons/mecha/mecha_equipment.dmi b/icons/mecha/mecha_equipment.dmi
index 1d6bca71b9..90929fff46 100644
Binary files a/icons/mecha/mecha_equipment.dmi and b/icons/mecha/mecha_equipment.dmi differ