diff --git a/code/__HELPERS/icon_smoothing.dm b/code/__HELPERS/icon_smoothing.dm index 5db8ea4c612..20aa9e00036 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 05a9ba6a7c7..0eae9aca64d 100644 --- a/code/_onclick/drag_drop.dm +++ b/code/_onclick/drag_drop.dm @@ -65,8 +65,18 @@ GLOBAL_VAR_INIT(use_experimental_clickdrag_thing, TRUE) if(!object.IsAutoclickable()) return var/obj/item/h = get_active_held_item() - if(h) + 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 + else 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 db1e2d19e4a..aae9b1f0105 100644 --- a/code/controllers/subsystem/jukeboxes.dm +++ b/code/controllers/subsystem/jukeboxes.dm @@ -106,9 +106,18 @@ SUBSYSTEM_DEF(jukeboxes) if(!(M.client.prefs.toggles & SOUND_INSTRUMENTS) || !M.can_hear()) M.stop_sound_channel(jukeinfo[2]) continue - - if(jukebox.z == M.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master + var/turf/juketurf = get_turf(jukebox) + var/turf/mturf = get_turf(M) + if(juketurf.z == mturf.z) //todo - expand this to work with mining planet z-levels when robust jukebox audio gets merged to master song_played.status = SOUND_UPDATE + else if(juketurf.z == mturf.z -1) + var/turf/juketurf_above = SSmapping.get_turf_above(juketurf) + if(istype(juketurf_above, /turf/open/transparent)) + song_played.status = SOUND_UPDATE + else if(juketurf.z == mturf.z +1) + var/turf/mturf_above = SSmapping.get_turf_above(mturf) + if(istype(mturf_above, /turf/open/transparent) || istype(juketurf,/turf/open/transparent)) + song_played.status = SOUND_UPDATE else song_played.status = SOUND_MUTE | SOUND_UPDATE //Setting volume = 0 doesn't let the sound properties update at all, which is lame. diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm index 9798b513ca9..87e6f68bbb0 100644 --- a/code/controllers/subsystem/throwing.dm +++ b/code/controllers/subsystem/throwing.dm @@ -147,7 +147,14 @@ SUBSYSTEM_DEF(throwing) if (obstacle == actual_target || (obstacle.density && !(obstacle.flags_1 & ON_BORDER_1))) finalize(TRUE, obstacle) 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_tools.dm b/code/datums/components/crafting/recipes/recipes_tools.dm index 99b72a1febd..110448fb210 100644 --- a/code/datums/components/crafting/recipes/recipes_tools.dm +++ b/code/datums/components/crafting/recipes/recipes_tools.dm @@ -130,3 +130,27 @@ /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 + +/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_VEHICLES + subcategory = CAT_VEHICLEPARTS + 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 cafcfd35d4c..072bf2ecd21 100644 --- a/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm +++ b/code/datums/components/crafting/recipes/recipes_weapon_and_ammo.dm @@ -681,6 +681,52 @@ 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/mech_ammo/brm8_missiles + 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/gun/HMGvehicle name = "Improvised HMG (for vehicles)" result = /obj/item/mecha_parts/mecha_equipment/weapon/ballistic/lmg/hobo diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index b7dc7d07eb9..a8b913fe805 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -479,3 +479,220 @@ //NC.mergeConnectedNetworksOnTurf() last_piece = NC return 1 + + +/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 f5844ec5e61..3128e8f912f 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 5865e2f3b7f..f7b91b53f0b 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,88 @@ 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 +439,89 @@ /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, TYPE_PROC_REF(/obj/item/grenade, 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/game/sound.dm b/code/game/sound.dm index 98466a6e6ce..095da3cc606 100644 --- a/code/game/sound.dm +++ b/code/game/sound.dm @@ -43,6 +43,9 @@ if(below_turf && istransparentturf(turf_source)) listeners += hearers(maxdistance,below_turf) + if(ismecha(source)) + var/obj/mecha/mechasound = source + listeners += mechasound?.occupant else if(above_turf && istransparentturf(above_turf)) diff --git a/code/modules/keybindings/keybind/carbon.dm b/code/modules/keybindings/keybind/carbon.dm index bd0e073826d..e7c736c4193 100644 --- a/code/modules/keybindings/keybind/carbon.dm +++ b/code/modules/keybindings/keybind/carbon.dm @@ -72,3 +72,39 @@ var/mob/living/carbon/C = user.mob C.do_wield() 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/inventory.dm b/code/modules/mob/inventory.dm index 3692d46be44..5e888b9fe61 100644 --- a/code/modules/mob/inventory.dm +++ b/code/modules/mob/inventory.dm @@ -329,6 +329,8 @@ if(!no_move && !(I.item_flags & DROPDEL)) //item may be moved/qdel'd immedietely, don't bother moving it if (isnull(newloc)) I.moveToNullspace() + if(ismecha(loc)) + I.forceMove(loc) else I.forceMove(newloc) on_item_dropped(I) diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f5cb6cff257..becd3945fbf 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -432,6 +432,9 @@ set category = "IC" if(src.incapacitated()) to_chat(src, span_warning("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)) @@ -440,16 +443,82 @@ else src.reset_perspective(T) RegisterSignal(src, COMSIG_MOB_CLIENT_CHANGE_VIEW, PROC_REF(stop_looking_up)) //no binos/scops - RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(stop_looking_up)) + RegisterSignal(src, COMSIG_MOVABLE_MOVED,PROC_REF(followcameraup)) + if(istype(loc, /obj/mecha)) + RegisterSignal(loc, COMSIG_MOVABLE_MOVED,PROC_REF(followcameraup)) RegisterSignal(src, COMSIG_LIVING_STATUS_KNOCKDOWN, PROC_REF(stop_looking_up)) RegisterSignal(src, COMSIG_LIVING_STATUS_PARALYZE, PROC_REF(stop_looking_up)) RegisterSignal(src, COMSIG_LIVING_STATUS_UNCONSCIOUS, PROC_REF(stop_looking_up)) RegisterSignal(src, COMSIG_LIVING_STATUS_SLEEP, PROC_REF(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_REF(stop_looking_down)) //no binos/scops + RegisterSignal(src, COMSIG_MOVABLE_MOVED,PROC_REF(followcameradown)) + if(istype(loc, /obj/mecha)) + RegisterSignal(loc, COMSIG_MOVABLE_MOVED,PROC_REF(followcameradown)) + RegisterSignal(src, COMSIG_LIVING_STATUS_KNOCKDOWN,PROC_REF(stop_looking_down)) + RegisterSignal(src, COMSIG_LIVING_STATUS_PARALYZE,PROC_REF(stop_looking_down)) + RegisterSignal(src, COMSIG_LIVING_STATUS_UNCONSCIOUS,PROC_REF(stop_looking_down)) + RegisterSignal(src, COMSIG_LIVING_STATUS_SLEEP,PROC_REF(stop_looking_down)) + else + var/turf/nl = SSmapping.get_turf_below(T) + src.reset_perspective(nl) + RegisterSignal(src, COMSIG_MOB_CLIENT_CHANGE_VIEW,PROC_REF(stop_looking_down)) //no binos/scops + RegisterSignal(src, COMSIG_MOVABLE_MOVED,PROC_REF(followcameradown)) + if(istype(loc, /obj/mecha)) + RegisterSignal(loc, COMSIG_MOVABLE_MOVED,PROC_REF(followcameradown)) + RegisterSignal(src, COMSIG_LIVING_STATUS_KNOCKDOWN,PROC_REF(stop_looking_down)) + RegisterSignal(src, COMSIG_LIVING_STATUS_PARALYZE,PROC_REF(stop_looking_down)) + RegisterSignal(src, COMSIG_LIVING_STATUS_UNCONSCIOUS,PROC_REF(stop_looking_down)) + RegisterSignal(src, COMSIG_LIVING_STATUS_SLEEP,PROC_REF(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 52e8c0fa40b..087cde342a0 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -898,7 +898,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 62729a2835f..41a6ce0b5ea 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -562,6 +562,11 @@ if(spread_override) setAngle(Angle + rand(-spread_override, spread_override)) 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 5e277af7cca..90929fff46c 100644 Binary files a/icons/mecha/mecha_equipment.dmi and b/icons/mecha/mecha_equipment.dmi differ