From fbf5fa078674ec960905c8fc294c82e91bf2dea6 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 15:53:12 -0800 Subject: [PATCH 01/26] the action and the item --- code/game/objects/items/robot/ai_upgrades.dm | 139 ++++++++++++++++++ .../management/ai_controlpanel.dm | 14 +- 2 files changed, 151 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 26f6b553ce4f..0c87977fd74c 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -45,3 +45,142 @@ log_game("[key_name(user)] has upgraded [key_name(AI)] with a [src].") message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") qdel(src) + +/obj/item/cameragun_upgrade + name = "camera laser upgrade" + desc = "A software package that will allow an artificial intelligence to briefly increase the amount of light an camera outputs to an outrageous amount to the point it burns skins. Must be installed using an unlocked AI control console." // In short, laser gun! + icon = 'icons/obj/module.dmi' + icon_state = "datadisk3" + +/obj/item/cameragun_upgrade/afterattack(mob/living/silicon/ai/AI, mob/user) + . = ..() + if(!istype(AI)) + return + + var/datum/action/innate/ai/ranged/cameragun/ability = new + ability.Grant(AI) + + to_chat(user, span_notice("You upgrade [AI]. [src] is consumed in the process.")) + log_game("[key_name(user)] has upgraded [key_name(AI)] with a [src].") + message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") + qdel(src) + +/// An ability that allows the user to shoot a laser beam at a target from the nearest camera. +/datum/action/innate/ai/ranged/cameragun + name = "Camera Laser Gun" + desc = "Shoots a laser from the nearest available camera toward a chosen destination. Accuracy not guaranteed." // Disclaimer is to warn people to treat this like a turret's aiming -- it might be a bit dumb at times. + button_icon = 'icons/obj/guns/energy.dmi' + button_icon_state = "laser" + enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") + disable_text = span_notice("You dissipate the overcharged energy.") + click_action = FALSE // Even though that we are an click action, we want to use Activate() and Deactivate(). + COOLDOWN_DECLARE(next_shot) + var/cooldown = 10 SECONDS + +/// Checks if it is possible for an projectile to reach a target in a straight line from a camera. +/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, turf/target, atom/A, ensureness = 0) + var/turf/turf_camera = get_turf(C.loc) + var/obj/dummy = new(turf_camera) + switch(ensureness) // How confident do we want to be about the projectile reaching their destination? Lower is more restrictive/confident. + if(0) + dummy.pass_flags |= PASSTABLE // Might hit their attached wall if the camera is on a corner -- should hit otherwise. + if(1) + dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE // Same concerns above. + if(2) + dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE|PASSMACHINES|PASSCOMPUTER|PASSMOB|PASSSTRUCTURE // May hit a dense object on the way, but intentional. + for(var/turf/turf in getline(turf_camera, target)) + if(turf.density) + qdel(dummy) + return FALSE + for(var/atom/movable/AM in turf) + if(!AM.CanPass(dummy, turf, 1)) + qdel(dummy) + return FALSE + qdel(dummy) + return TRUE + +/datum/action/innate/ai/ranged/cameragun/New() + ..() + START_PROCESSING(SSfastprocess, src) + +/datum/action/innate/ai/ranged/cameragun/Destroy() + STOP_PROCESSING(SSfastprocess, src) + return ..() + +/datum/action/innate/ai/ranged/cameragun/process() + build_all_button_icons() // To update the button to display if active/available. + +/datum/action/innate/ai/ranged/cameragun/Activate(loud = TRUE) + if(!COOLDOWN_FINISHED(src, next_shot)) + to_chat(owner, span_notice("Wait [COOLDOWN_TIMELEFT(src, next_shot)/10] seconds for the next camera overcharge.")) + return + set_ranged_ability(owner, loud ? enable_text : null) + active = TRUE + background_icon_state = "bg_default_on" + build_all_button_icons() + +/datum/action/innate/ai/ranged/cameragun/Deactivate(loud = TRUE) + unset_ranged_ability(owner, loud ? disable_text : null) + active = FALSE + background_icon_state = "bg_default" + build_all_button_icons() + +/datum/action/innate/ai/ranged/cameragun/IsAvailable(feedback = FALSE) + . = ..() + if(!. || !COOLDOWN_FINISHED(src, next_shot)) + return FALSE + +/datum/action/innate/ai/ranged/cameragun/do_ability(mob/living/caller, params, atom/target) + var/turf/loc_target = get_turf(target) + var/obj/machinery/camera/chosen_camera + for(var/obj/machinery/camera/cam in GLOB.cameranet.cameras) + if(!isturf(cam.loc)) + continue + if(cam == target) + continue + if(!cam.status || cam.emped) // Non-functional camera. + continue + var/turf/loc_camera = get_turf(cam) + if(loc_target.z != loc_camera.z) + continue + if(get_dist(cam, target) > 12) + continue + if(get_dist(cam, target) == 0) // Pointblank shot. + chosen_camera = cam + break + if(can_shoot_to(cam, loc_target, null, ensureness = 0)) // Camera with the best accuracy. + chosen_camera = cam + break + if(!can_shoot_to(cam, loc_target, null, ensureness = 2)) // Never had the possibility to hit. + continue + if(!chosen_camera) + chosen_camera = cam + continue + if(get_dist(chosen_camera, target) > get_dist(cam, target)) // Closest camera that can hit. + chosen_camera = cam + continue + if(!chosen_camera) + Deactivate(FALSE) + to_chat(caller, span_notice("Unable to find nearby available cameras for this target.")) + return FALSE + + COOLDOWN_START(src, next_shot, cooldown) + var/turf/loc_chosen = get_turf(chosen_camera) + var/obj/projectile/beam/laser/proj = null + proj = new /obj/projectile/beam/laser(loc_chosen) + proj.preparePixelProjectile(target, loc_chosen) + proj.firer = caller + + // Fire the shot. + var/pointblank = get_dist(chosen_camera, target) == 0 ? TRUE : FALSE // Same tile. + if(pointblank) + chosen_camera.visible_message(span_danger("[chosen_camera] fires a laser point blank at [target]!")) + proj.fire(direct_target = target) + else + chosen_camera.visible_message(span_danger("[chosen_camera] fires a laser!")) + proj.fire() + Deactivate(FALSE) + to_chat(caller, span_danger("Camera overcharged.")) + + chosen_camera.emp_act(EMP_LIGHT) // 90 seconds downtime -- definitely enough time to toolbox this camera (unless it is emp-proof). + return TRUE diff --git a/code/modules/mob/living/silicon/ai/decentralized/management/ai_controlpanel.dm b/code/modules/mob/living/silicon/ai/decentralized/management/ai_controlpanel.dm index eaccf21f5213..f0ac4cb859fa 100644 --- a/code/modules/mob/living/silicon/ai/decentralized/management/ai_controlpanel.dm +++ b/code/modules/mob/living/silicon/ai/decentralized/management/ai_controlpanel.dm @@ -80,7 +80,17 @@ GLOBAL_VAR_INIT(ai_control_code, random_nukecode(6)) return ..() var/obj/item/surveillance_upgrade/upgrade = W upgrade.afterattack(AI, user) - + return FALSE + if(istype(W, /obj/item/cameragun_upgrade)) + if(!authenticated) + to_chat(user, span_warning("You need to be logged in to do this!")) + return ..() + var/mob/living/silicon/ai/AI = input("Select an AI", "Select an AI", null, null) as null|anything in GLOB.ai_list + if(!AI) + return ..() + var/obj/item/cameragun_upgrade/upgrade = W + upgrade.afterattack(AI, user) + return FALSE if(istype(W, /obj/item/malf_upgrade)) if(!authenticated) to_chat(user, span_warning("You need to be logged in to do this!")) @@ -90,7 +100,7 @@ GLOBAL_VAR_INIT(ai_control_code, random_nukecode(6)) return ..() var/obj/item/malf_upgrade/upgrade = W upgrade.afterattack(AI, user) - + return FALSE return ..() /obj/machinery/computer/ai_control_console/emag_act(mob/user, obj/item/card/emag/emag_card) From ab70c85fefc43e33d5f1a0ddfc13107a47915e5f Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 16:09:19 -0800 Subject: [PATCH 02/26] uplink item and cost reason --- code/modules/uplink/uplink_items.dm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 4a0d87837d9d..b9c4661ee47f 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -2680,6 +2680,13 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 4 // Not as "destructive" as the emag. In addition, less features than the normal emag. Increase price once more impactful features are added. restricted_roles = list("Clown") +/datum/uplink_item/role_restricted/ai_cameragun + name = "AI Camera Gun Upgrade" + desc = "A disk containing experimental and illegal software that allows an AI to temporarily override the safety features on their cameras, enabling them to shoot a laser beam out of them." + item = /obj/item/cameragun_upgrade + cost = 8 // Considering that you have to: subvert an AI, trust the AI not to be a traitor/unsubverted, accept that every camera is gonna get disabled later, and pay more than an emag for this... 8 is fair. + restricted_roles = list("Roboticist", "Research Director") + // Pointless /datum/uplink_item/badass category = "(Pointless) Badassery" From 71f4574e917da3f4ebb06ad16af86e4e5ee71174 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 16:11:06 -0800 Subject: [PATCH 03/26] no need for msg due to isavailable --- code/game/objects/items/robot/ai_upgrades.dm | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 0c87977fd74c..96c4b2b5ecd2 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -111,9 +111,6 @@ build_all_button_icons() // To update the button to display if active/available. /datum/action/innate/ai/ranged/cameragun/Activate(loud = TRUE) - if(!COOLDOWN_FINISHED(src, next_shot)) - to_chat(owner, span_notice("Wait [COOLDOWN_TIMELEFT(src, next_shot)/10] seconds for the next camera overcharge.")) - return set_ranged_ability(owner, loud ? enable_text : null) active = TRUE background_icon_state = "bg_default_on" From 7eb1f47022f9f764ea5b734f29e62e66b2959515 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 16:13:58 -0800 Subject: [PATCH 04/26] better var name --- code/game/objects/items/robot/ai_upgrades.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 96c4b2b5ecd2..693d76c712b4 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -78,10 +78,10 @@ var/cooldown = 10 SECONDS /// Checks if it is possible for an projectile to reach a target in a straight line from a camera. -/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, turf/target, atom/A, ensureness = 0) +/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, turf/target, atom/A, confidence = 0) var/turf/turf_camera = get_turf(C.loc) var/obj/dummy = new(turf_camera) - switch(ensureness) // How confident do we want to be about the projectile reaching their destination? Lower is more restrictive/confident. + switch(confidence) // How confident do we want to be about the projectile reaching their destination? Lower is more restrictive/confident. if(0) dummy.pass_flags |= PASSTABLE // Might hit their attached wall if the camera is on a corner -- should hit otherwise. if(1) @@ -145,10 +145,10 @@ if(get_dist(cam, target) == 0) // Pointblank shot. chosen_camera = cam break - if(can_shoot_to(cam, loc_target, null, ensureness = 0)) // Camera with the best accuracy. + if(can_shoot_to(cam, loc_target, null, confidence = 0)) // Camera with the best accuracy. chosen_camera = cam break - if(!can_shoot_to(cam, loc_target, null, ensureness = 2)) // Never had the possibility to hit. + if(!can_shoot_to(cam, loc_target, null, confidence = 2)) // Never had the possibility to hit. continue if(!chosen_camera) chosen_camera = cam From a367e13ea0fdefb49a926fe5f8e7a4da84e65977 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 17:43:32 -0800 Subject: [PATCH 05/26] accuracy (almost) guaranteed!!! --- code/game/objects/items/robot/ai_upgrades.dm | 55 ++++++++++---------- code/modules/projectiles/projectile.dm | 6 +-- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 693d76c712b4..4fa40f3736c0 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -68,7 +68,7 @@ /// An ability that allows the user to shoot a laser beam at a target from the nearest camera. /datum/action/innate/ai/ranged/cameragun name = "Camera Laser Gun" - desc = "Shoots a laser from the nearest available camera toward a chosen destination. Accuracy not guaranteed." // Disclaimer is to warn people to treat this like a turret's aiming -- it might be a bit dumb at times. + desc = "Shoots a laser from the nearest available camera toward a chosen destination. Only fires if the laser will hit target/destination." // This means if you aim at a turf and it doesn't collide, it won't fire. button_icon = 'icons/obj/guns/energy.dmi' button_icon_state = "laser" enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") @@ -77,27 +77,31 @@ COOLDOWN_DECLARE(next_shot) var/cooldown = 10 SECONDS + /// Checks if it is possible for an projectile to reach a target in a straight line from a camera. -/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, turf/target, atom/A, confidence = 0) - var/turf/turf_camera = get_turf(C.loc) - var/obj/dummy = new(turf_camera) - switch(confidence) // How confident do we want to be about the projectile reaching their destination? Lower is more restrictive/confident. - if(0) - dummy.pass_flags |= PASSTABLE // Might hit their attached wall if the camera is on a corner -- should hit otherwise. - if(1) - dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE // Same concerns above. - if(2) - dummy.pass_flags |= PASSTABLE|PASSGLASS|PASSGRILLE|PASSMACHINES|PASSCOMPUTER|PASSMOB|PASSSTRUCTURE // May hit a dense object on the way, but intentional. - for(var/turf/turf in getline(turf_camera, target)) - if(turf.density) - qdel(dummy) - return FALSE - for(var/atom/movable/AM in turf) - if(!AM.CanPass(dummy, turf, 1)) - qdel(dummy) - return FALSE - qdel(dummy) - return TRUE +/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, turf/target, confidence = 0) + var/obj/projectile/proj = new /obj/projectile + proj.icon = null + proj.icon_state = null + proj.hitsound = "" + proj.suppressed = TRUE + proj.ricochets_max = 0 + proj.ricochet_chance = 0 + proj.damage = 0 + proj.nodamage = TRUE + proj.log_override = TRUE + proj.hitscan = TRUE + proj.pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + + var/turf/current_turf = get_turf(C) + proj.preparePixelProjectile(target, current_turf) + proj.fire() + + var/turf/target_turf = get_turf(target) + var/turf/last_turf = proj.hitscan_last + if(last_turf == target_turf) + return TRUE + return FALSE /datum/action/innate/ai/ranged/cameragun/New() ..() @@ -140,15 +144,12 @@ var/turf/loc_camera = get_turf(cam) if(loc_target.z != loc_camera.z) continue - if(get_dist(cam, target) > 12) - continue if(get_dist(cam, target) == 0) // Pointblank shot. chosen_camera = cam break - if(can_shoot_to(cam, loc_target, null, confidence = 0)) // Camera with the best accuracy. - chosen_camera = cam - break - if(!can_shoot_to(cam, loc_target, null, confidence = 2)) // Never had the possibility to hit. + if(get_dist(cam, target) > 12) + continue + if(!can_shoot_to(cam, loc_target)) // Camera can hit this spot. continue if(!chosen_camera) chosen_camera = cam diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index f8c23aa21917..6535c89380a5 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -58,7 +58,8 @@ var/hitscan = FALSE //Whether this is hitscan. If it is, speed is basically ignored. var/list/beam_segments //assoc list of datum/point or datum/point/vector, start = end. Used for hitscan effect generation. var/datum/point/beam_index - var/turf/hitscan_last //last turf touched during hitscanning. + /// The ending/last touched turf during hitscanning. + var/turf/hitscan_last var/tracer_type var/muzzle_type var/impact_type @@ -584,10 +585,9 @@ pixel_x = trajectory.return_px() pixel_y = trajectory.return_py() forcemoved = TRUE - hitscan_last = loc else if(T != loc) step_towards(src, T) - hitscan_last = loc + hitscan_last = T if(!hitscanning && !forcemoved) pixel_x = trajectory.return_px() - trajectory.mpx * trajectory_multiplier * SSprojectiles.global_iterations_per_move pixel_y = trajectory.return_py() - trajectory.mpy * trajectory_multiplier * SSprojectiles.global_iterations_per_move From 2d23c9af9021f02d9c7771e2134a6a503570b45d Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 18:13:31 -0800 Subject: [PATCH 06/26] now can target objects (but not specific turfs) --- code/game/objects/items/robot/ai_upgrades.dm | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 4fa40f3736c0..d03847083815 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -68,7 +68,7 @@ /// An ability that allows the user to shoot a laser beam at a target from the nearest camera. /datum/action/innate/ai/ranged/cameragun name = "Camera Laser Gun" - desc = "Shoots a laser from the nearest available camera toward a chosen destination. Only fires if the laser will hit target/destination." // This means if you aim at a turf and it doesn't collide, it won't fire. + desc = "Shoots a laser from the nearest available camera toward a chosen destination. Only fires if the laser could reach destination." // This means if you aim at a turf and it doesn't collide, it won't fire. Beware of moving targets. button_icon = 'icons/obj/guns/energy.dmi' button_icon_state = "laser" enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") @@ -77,9 +77,8 @@ COOLDOWN_DECLARE(next_shot) var/cooldown = 10 SECONDS - /// Checks if it is possible for an projectile to reach a target in a straight line from a camera. -/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, turf/target, confidence = 0) +/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, atom/target, confidence = 0) var/obj/projectile/proj = new /obj/projectile proj.icon = null proj.icon_state = null @@ -93,8 +92,7 @@ proj.hitscan = TRUE proj.pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE - var/turf/current_turf = get_turf(C) - proj.preparePixelProjectile(target, current_turf) + proj.preparePixelProjectile(target, C) proj.fire() var/turf/target_turf = get_turf(target) @@ -149,7 +147,7 @@ break if(get_dist(cam, target) > 12) continue - if(!can_shoot_to(cam, loc_target)) // Camera can hit this spot. + if(!can_shoot_to(cam, target)) // Camera can hit this spot. continue if(!chosen_camera) chosen_camera = cam @@ -166,7 +164,7 @@ var/turf/loc_chosen = get_turf(chosen_camera) var/obj/projectile/beam/laser/proj = null proj = new /obj/projectile/beam/laser(loc_chosen) - proj.preparePixelProjectile(target, loc_chosen) + proj.preparePixelProjectile(target, chosen_camera) proj.firer = caller // Fire the shot. From 4619e07600e2c1c2d902c53c56552886649fa12e Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 18:14:13 -0800 Subject: [PATCH 07/26] better comment --- code/game/objects/items/robot/ai_upgrades.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index d03847083815..a3cfd96b9fea 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -147,7 +147,7 @@ break if(get_dist(cam, target) > 12) continue - if(!can_shoot_to(cam, target)) // Camera can hit this spot. + if(!can_shoot_to(cam, target)) // Camera cannot hit this target (assuming they are not moving). continue if(!chosen_camera) chosen_camera = cam From 71defba9e71e0c918fa5b6515c2b42a549ad40d3 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 18:29:30 -0800 Subject: [PATCH 08/26] better uplink name --- code/modules/uplink/uplink_items.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index b9c4661ee47f..989fd308dfcf 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -2681,7 +2681,7 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) restricted_roles = list("Clown") /datum/uplink_item/role_restricted/ai_cameragun - name = "AI Camera Gun Upgrade" + name = "AI Camera Laser Gun Upgrade" desc = "A disk containing experimental and illegal software that allows an AI to temporarily override the safety features on their cameras, enabling them to shoot a laser beam out of them." item = /obj/item/cameragun_upgrade cost = 8 // Considering that you have to: subvert an AI, trust the AI not to be a traitor/unsubverted, accept that every camera is gonna get disabled later, and pay more than an emag for this... 8 is fair. From 69f32c99fe6c9019fc89eeb95a01b11694932468 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 18:30:36 -0800 Subject: [PATCH 09/26] EMP_HEAVY --- code/game/objects/items/robot/ai_upgrades.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index a3cfd96b9fea..e53f9e51541c 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -178,5 +178,5 @@ Deactivate(FALSE) to_chat(caller, span_danger("Camera overcharged.")) - chosen_camera.emp_act(EMP_LIGHT) // 90 seconds downtime -- definitely enough time to toolbox this camera (unless it is emp-proof). + chosen_camera.emp_act(EMP_HEAVY) // 90 seconds downtime -- definitely enough time to toolbox this camera (unless it is emp-proof). return TRUE From 5fb53a55b22a6d1f764e57767104b3a621b11551 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Wed, 22 Nov 2023 18:46:56 -0800 Subject: [PATCH 10/26] proper logging msg --- code/game/objects/items/robot/ai_upgrades.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index e53f9e51541c..ffa9f449aaac 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -61,8 +61,8 @@ ability.Grant(AI) to_chat(user, span_notice("You upgrade [AI]. [src] is consumed in the process.")) - log_game("[key_name(user)] has upgraded [key_name(AI)] with a [src].") - message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") + log_game("[key_name(user)] has upgraded [key_name(AI)] with [src].") + message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with [src].") qdel(src) /// An ability that allows the user to shoot a laser beam at a target from the nearest camera. From 9faae7fb0e424c99eb413378066c848be6ad05e0 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 24 Nov 2023 02:39:21 -0800 Subject: [PATCH 11/26] no dupes + fixing other upgrades logging --- code/game/objects/items/robot/ai_upgrades.dm | 24 ++++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index ffa9f449aaac..3ca94ce91a0f 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -8,7 +8,6 @@ icon = 'icons/obj/module.dmi' icon_state = "datadisk3" - /obj/item/malf_upgrade/afterattack(mob/living/silicon/ai/AI, mob/user) . = ..() if(!istype(AI)) @@ -20,12 +19,11 @@ to_chat(AI, span_userdanger("[user] has upgraded you with combat software!")) to_chat(AI, span_userdanger("Your current laws and objectives remain unchanged.")) //this unlocks malf powers, but does not give the license to plasma flood AI.add_malf_picker() - log_game("[key_name(user)] has upgraded [key_name(AI)] with a [src].") - message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") + log_game("[key_name(user)] has upgraded [key_name(AI)] with \a [src].") + message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with \a [src].") to_chat(user, span_notice("You upgrade [AI]. [src] is consumed in the process.")) qdel(src) - //Lipreading /obj/item/surveillance_upgrade name = "surveillance software upgrade" @@ -42,8 +40,8 @@ to_chat(AI, span_userdanger("[user] has upgraded you with surveillance software!")) to_chat(AI, "Via a combination of hidden microphones and lip reading software, you are able to use your cameras to listen in on conversations.") to_chat(user, span_notice("You upgrade [AI]. [src] is consumed in the process.")) - log_game("[key_name(user)] has upgraded [key_name(AI)] with a [src].") - message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with a [src].") + log_game("[key_name(user)] has upgraded [key_name(AI)] with \a [src].") + message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with \a [src].") qdel(src) /obj/item/cameragun_upgrade @@ -57,12 +55,18 @@ if(!istype(AI)) return + // No giving multiple copies. + for(var/datum/action/action in AI.actions) + if(action.type == /datum/action/innate/ai/ranged/cameragun) + to_chat(user, span_notice("[AI] has already been upgraded with \a [src].")) + return + var/datum/action/innate/ai/ranged/cameragun/ability = new ability.Grant(AI) to_chat(user, span_notice("You upgrade [AI]. [src] is consumed in the process.")) - log_game("[key_name(user)] has upgraded [key_name(AI)] with [src].") - message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with [src].") + log_game("[key_name(user)] has upgraded [key_name(AI)] with \a [src].") + message_admins("[ADMIN_LOOKUPFLW(user)] has upgraded [ADMIN_LOOKUPFLW(AI)] with \a [src].") qdel(src) /// An ability that allows the user to shoot a laser beam at a target from the nearest camera. @@ -73,7 +77,7 @@ button_icon_state = "laser" enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") disable_text = span_notice("You dissipate the overcharged energy.") - click_action = FALSE // Even though that we are an click action, we want to use Activate() and Deactivate(). + click_action = FALSE // Even though that we are a click action, we want to use Activate() and Deactivate(). COOLDOWN_DECLARE(next_shot) var/cooldown = 10 SECONDS @@ -87,7 +91,7 @@ proj.ricochets_max = 0 proj.ricochet_chance = 0 proj.damage = 0 - proj.nodamage = TRUE + proj.nodamage = TRUE // Prevents this hitscan test projectile from detonating certain objects (e.g. welding tanks). proj.log_override = TRUE proj.hitscan = TRUE proj.pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE From 3f6dd7f9eaed138c24b475a8321ce37d27045203 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 24 Nov 2023 03:18:15 -0800 Subject: [PATCH 12/26] dont need to null it i think --- code/game/objects/items/robot/ai_upgrades.dm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 3ca94ce91a0f..726b56e419ed 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -166,8 +166,7 @@ COOLDOWN_START(src, next_shot, cooldown) var/turf/loc_chosen = get_turf(chosen_camera) - var/obj/projectile/beam/laser/proj = null - proj = new /obj/projectile/beam/laser(loc_chosen) + var/obj/projectile/beam/laser/proj = new(loc_chosen) proj.preparePixelProjectile(target, chosen_camera) proj.firer = caller From b80db3f1e440719335645f93fd69011559d30fad Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 24 Nov 2023 03:29:27 -0800 Subject: [PATCH 13/26] increases pointblank from 0tile -> 1tile --- code/game/objects/items/robot/ai_upgrades.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 726b56e419ed..85d7c9eee791 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -72,7 +72,7 @@ /// An ability that allows the user to shoot a laser beam at a target from the nearest camera. /datum/action/innate/ai/ranged/cameragun name = "Camera Laser Gun" - desc = "Shoots a laser from the nearest available camera toward a chosen destination. Only fires if the laser could reach destination." // This means if you aim at a turf and it doesn't collide, it won't fire. Beware of moving targets. + desc = "Shoots a laser from the nearest available camera toward a chosen destination if it is highly probable to reach said destination." button_icon = 'icons/obj/guns/energy.dmi' button_icon_state = "laser" enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") @@ -146,12 +146,12 @@ var/turf/loc_camera = get_turf(cam) if(loc_target.z != loc_camera.z) continue - if(get_dist(cam, target) == 0) // Pointblank shot. + if(get_dist(cam, target) <= 1) // Pointblank shot. chosen_camera = cam break if(get_dist(cam, target) > 12) continue - if(!can_shoot_to(cam, target)) // Camera cannot hit this target (assuming they are not moving). + if(!can_shoot_to(cam, target)) // No chance to hit. continue if(!chosen_camera) chosen_camera = cam @@ -171,7 +171,7 @@ proj.firer = caller // Fire the shot. - var/pointblank = get_dist(chosen_camera, target) == 0 ? TRUE : FALSE // Same tile. + var/pointblank = get_dist(chosen_camera, target) <= 1 ? TRUE : FALSE // Same tile or right next. if(pointblank) chosen_camera.visible_message(span_danger("[chosen_camera] fires a laser point blank at [target]!")) proj.fire(direct_target = target) From 7cad3ca5667a2dbe6793761d1ce26586ff2a0960 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 24 Nov 2023 06:02:40 -0800 Subject: [PATCH 14/26] unused parameter --- code/game/objects/items/robot/ai_upgrades.dm | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 85d7c9eee791..f3b5b032231c 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -81,8 +81,8 @@ COOLDOWN_DECLARE(next_shot) var/cooldown = 10 SECONDS -/// Checks if it is possible for an projectile to reach a target in a straight line from a camera. -/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, atom/target, confidence = 0) +/// Checks if it is possible for a (hitscan) projectile to reach a target in a straight line from a camera. +/datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, atom/target) var/obj/projectile/proj = new /obj/projectile proj.icon = null proj.icon_state = null @@ -91,7 +91,7 @@ proj.ricochets_max = 0 proj.ricochet_chance = 0 proj.damage = 0 - proj.nodamage = TRUE // Prevents this hitscan test projectile from detonating certain objects (e.g. welding tanks). + proj.nodamage = TRUE // Prevents this projectile from detonating certain objects (e.g. welding tanks). proj.log_override = TRUE proj.hitscan = TRUE proj.pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE From deffb7145b9e76c16b6e9128a7832b263c3719a0 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Sat, 16 Dec 2023 11:29:49 -0800 Subject: [PATCH 15/26] yeet this out of uplink items --- code/modules/uplink/uplink_items.dm | 7 ------- 1 file changed, 7 deletions(-) diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 5c853ccfd07b..02868530da54 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -2689,13 +2689,6 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item)) cost = 4 // Not as "destructive" as the emag. In addition, less features than the normal emag. Increase price once more impactful features are added. restricted_roles = list("Clown") -/datum/uplink_item/role_restricted/ai_cameragun - name = "AI Camera Laser Gun Upgrade" - desc = "A disk containing experimental and illegal software that allows an AI to temporarily override the safety features on their cameras, enabling them to shoot a laser beam out of them." - item = /obj/item/cameragun_upgrade - cost = 8 // Considering that you have to: subvert an AI, trust the AI not to be a traitor/unsubverted, accept that every camera is gonna get disabled later, and pay more than an emag for this... 8 is fair. - restricted_roles = list("Roboticist", "Research Director") - // Pointless /datum/uplink_item/badass category = "(Pointless) Badassery" From 3b10ca989e1ddbc80945ca37e49958519e71471b Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Sat, 16 Dec 2023 14:04:45 -0800 Subject: [PATCH 16/26] traitor ai got it now --- code/game/objects/items/robot/ai_upgrades.dm | 15 ++++++++++----- code/modules/antagonists/traitor/datum_traitor.dm | 7 +++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index f3b5b032231c..b7b782ed1c3e 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -55,14 +55,17 @@ if(!istype(AI)) return - // No giving multiple copies. - for(var/datum/action/action in AI.actions) - if(action.type == /datum/action/innate/ai/ranged/cameragun) + var/datum/action/innate/ai/ranged/cameragun/ai_action + for(var/datum/action/innate/ai/ranged/cameragun/listed_action in AI.actions) + if(!listed_action.from_traitor) // Duplicate. to_chat(user, span_notice("[AI] has already been upgraded with \a [src].")) return + ai_action = listed_action // If they somehow have more than one action, blame adminbus first. + ai_action.from_traitor = FALSE // Let them keep the action if they lose traitor status. - var/datum/action/innate/ai/ranged/cameragun/ability = new - ability.Grant(AI) + if(!ai_action) + ai_action = new + ai_action.Grant(AI) to_chat(user, span_notice("You upgrade [AI]. [src] is consumed in the process.")) log_game("[key_name(user)] has upgraded [key_name(AI)] with \a [src].") @@ -78,6 +81,8 @@ enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") disable_text = span_notice("You dissipate the overcharged energy.") click_action = FALSE // Even though that we are a click action, we want to use Activate() and Deactivate(). + /// If this ability is sourced from being a traitor AI. + var/from_traitor = FALSE COOLDOWN_DECLARE(next_shot) var/cooldown = 10 SECONDS diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 877b97da4936..80aa81ff2f9e 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -62,6 +62,9 @@ if(traitor_kind == TRAITOR_AI && owner.current && isAI(owner.current)) var/mob/living/silicon/ai/A = owner.current A.set_zeroth_law("") + for(var/datum/action/innate/ai/ranged/cameragun/ai_action in A.actions) + if(ai_action.from_traitor) + ai_action.Remove(A) if(malf) remove_verb(A, /mob/living/silicon/ai/proc/choose_modules) A.malf_picker.remove_malf_verbs(A) @@ -261,6 +264,10 @@ add_law_zero() owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE) owner.current.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MALF) + var/datum/action/innate/ai/ranged/cameragun/ability = new + ability.from_traitor = TRUE + ability.Grant(owner.current) + if(TRAITOR_HUMAN) if(should_equip) equip(silent) From d7a5a2d750f3dc0113f5df46d31f0b558bec5b78 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Sat, 16 Dec 2023 14:12:16 -0800 Subject: [PATCH 17/26] no traitor dupes. also dunno how to get all specific types in a list and i know this works sooooooooooo --- code/modules/antagonists/traitor/datum_traitor.dm | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 80aa81ff2f9e..1c1359691403 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -264,9 +264,15 @@ add_law_zero() owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/malf.ogg', 100, FALSE, pressure_affected = FALSE) owner.current.grant_language(/datum/language/codespeak, TRUE, TRUE, LANGUAGE_MALF) - var/datum/action/innate/ai/ranged/cameragun/ability = new - ability.from_traitor = TRUE - ability.Grant(owner.current) + + var/has_action = FALSE + for(var/datum/action/innate/ai/ranged/cameragun/ai_action in owner.current.actions) + has_action = TRUE + break + if(!has_action) + var/datum/action/innate/ai/ranged/cameragun/ability = new + ability.from_traitor = TRUE + ability.Grant(owner.current) if(TRAITOR_HUMAN) if(should_equip) From 16cde7c7876661cc3cdda5e64835475e4eb7e925 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Sat, 16 Dec 2023 14:27:27 -0800 Subject: [PATCH 18/26] want this background icon state immediately --- code/game/objects/items/robot/ai_upgrades.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index b7b782ed1c3e..c5d234ddf821 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -78,6 +78,7 @@ desc = "Shoots a laser from the nearest available camera toward a chosen destination if it is highly probable to reach said destination." button_icon = 'icons/obj/guns/energy.dmi' button_icon_state = "laser" + background_icon_state = "bg_default" enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") disable_text = span_notice("You dissipate the overcharged energy.") click_action = FALSE // Even though that we are a click action, we want to use Activate() and Deactivate(). From 43ba2cc83840975223ae9f9315cceca1a10a3826 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Sat, 16 Dec 2023 14:32:17 -0800 Subject: [PATCH 19/26] i enknowledge the button sprite suck ass --- code/game/objects/items/robot/ai_upgrades.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index c5d234ddf821..c3a4cbbfced4 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -78,7 +78,7 @@ desc = "Shoots a laser from the nearest available camera toward a chosen destination if it is highly probable to reach said destination." button_icon = 'icons/obj/guns/energy.dmi' button_icon_state = "laser" - background_icon_state = "bg_default" + background_icon_state = "bg_default" // Better button sprites welcomed. :) enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") disable_text = span_notice("You dissipate the overcharged energy.") click_action = FALSE // Even though that we are a click action, we want to use Activate() and Deactivate(). From 56c62e05eaffbdabf585153dcd25ae3bbe362bdf Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 12 Jan 2024 20:23:42 -0800 Subject: [PATCH 20/26] there --- code/game/objects/items/robot/ai_upgrades.dm | 44 ++++++++++++++++---- 1 file changed, 37 insertions(+), 7 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index c3a4cbbfced4..0c138e04bb0d 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -73,9 +73,10 @@ qdel(src) /// An ability that allows the user to shoot a laser beam at a target from the nearest camera. +// TODO: If right-click functionality for buttons are added, make singleshot a left-click ability & burstmode a right-click ability. /datum/action/innate/ai/ranged/cameragun name = "Camera Laser Gun" - desc = "Shoots a laser from the nearest available camera toward a chosen destination if it is highly probable to reach said destination." + desc = "Shoots a laser from the nearest available camera toward a chosen destination if it is highly probable to reach said destination. If successful, enters burst mode which temporarily allows the ability to be reused every second for 30 seconds." button_icon = 'icons/obj/guns/energy.dmi' button_icon_state = "laser" background_icon_state = "bg_default" // Better button sprites welcomed. :) @@ -84,8 +85,17 @@ click_action = FALSE // Even though that we are a click action, we want to use Activate() and Deactivate(). /// If this ability is sourced from being a traitor AI. var/from_traitor = FALSE - COOLDOWN_DECLARE(next_shot) - var/cooldown = 10 SECONDS + /// Is burst mode activated? + var/burstmode_activated = FALSE + /// How long is burst mode? + var/burstmode_length = 30 SECONDS + COOLDOWN_DECLARE(since_burstmode) + /// How much time (after burst mode is deactivated) must pass before it can be activated again? + var/activate_cooldown = 30 SECONDS + COOLDOWN_DECLARE(next_activate) + /// How much time between shots (during burst mode)? + var/fire_cooldown = 1 SECONDS + COOLDOWN_DECLARE(next_fire) /// Checks if it is possible for a (hitscan) projectile to reach a target in a straight line from a camera. /datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, atom/target) @@ -120,7 +130,9 @@ return ..() /datum/action/innate/ai/ranged/cameragun/process() - build_all_button_icons() // To update the button to display if active/available. + if(burstmode_activated && COOLDOWN_FINISHED(src, since_burstmode)) + toggle_burstmode() + build_all_button_icons() /datum/action/innate/ai/ranged/cameragun/Activate(loud = TRUE) set_ranged_ability(owner, loud ? enable_text : null) @@ -136,7 +148,11 @@ /datum/action/innate/ai/ranged/cameragun/IsAvailable(feedback = FALSE) . = ..() - if(!. || !COOLDOWN_FINISHED(src, next_shot)) + if(!.) + return FALSE + if(burstmode_activated && !COOLDOWN_FINISHED(src, next_fire)) // Not ready to shoot (during brustmode). + return FALSE + if(!burstmode_activated && !COOLDOWN_FINISHED(src, next_activate)) // Burstmode is not ready. return FALSE /datum/action/innate/ai/ranged/cameragun/do_ability(mob/living/caller, params, atom/target) @@ -169,8 +185,10 @@ Deactivate(FALSE) to_chat(caller, span_notice("Unable to find nearby available cameras for this target.")) return FALSE - - COOLDOWN_START(src, next_shot, cooldown) + if(!burstmode_activated) + toggle_burstmode() + + COOLDOWN_START(src, next_fire, fire_cooldown) var/turf/loc_chosen = get_turf(chosen_camera) var/obj/projectile/beam/laser/proj = new(loc_chosen) proj.preparePixelProjectile(target, chosen_camera) @@ -189,3 +207,15 @@ chosen_camera.emp_act(EMP_HEAVY) // 90 seconds downtime -- definitely enough time to toolbox this camera (unless it is emp-proof). return TRUE + +/datum/action/innate/ai/ranged/cameragun/proc/toggle_burstmode() + burstmode_activated = !burstmode_activated + if(burstmode_activated) + COOLDOWN_START(src, since_burstmode, burstmode_length) + to_chat(owner, span_notice("Burstmode activated.")) + owner.playsound_local(owner, 'sound/effects/light_flicker.ogg', 50, FALSE) + else + COOLDOWN_START(src, next_activate, activate_cooldown) + to_chat(owner, span_notice("Burstmode deactivated.")) + owner.playsound_local(owner, 'sound/items/timer.ogg', 50, FALSE) + return TRUE From 82bfcaf242902165dea354ae2867fa37686ca3b4 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 12 Jan 2024 20:26:18 -0800 Subject: [PATCH 21/26] cooldown adjustments --- code/game/objects/items/robot/ai_upgrades.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 0c138e04bb0d..86c6a7a4904f 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -91,10 +91,10 @@ var/burstmode_length = 30 SECONDS COOLDOWN_DECLARE(since_burstmode) /// How much time (after burst mode is deactivated) must pass before it can be activated again? - var/activate_cooldown = 30 SECONDS + var/activate_cooldown = 60 SECONDS COOLDOWN_DECLARE(next_activate) /// How much time between shots (during burst mode)? - var/fire_cooldown = 1 SECONDS + var/fire_cooldown = 0.5 SECONDS COOLDOWN_DECLARE(next_fire) /// Checks if it is possible for a (hitscan) projectile to reach a target in a straight line from a camera. From 7b689a4f71b8ebfb24b3ead00d72b950a5edfdc3 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 12 Jan 2024 20:30:44 -0800 Subject: [PATCH 22/26] comment explictly saying that burstmode is good for emp resist cameras --- code/game/objects/items/robot/ai_upgrades.dm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 86c6a7a4904f..31225f81008f 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -205,7 +205,9 @@ Deactivate(FALSE) to_chat(caller, span_danger("Camera overcharged.")) - chosen_camera.emp_act(EMP_HEAVY) // 90 seconds downtime -- definitely enough time to toolbox this camera (unless it is emp-proof). + /* This EMP prevents burstmode from annihilating a stationary object/person. + If someone gives a camera EMP resistance, then they had it coming. */ + chosen_camera.emp_act(EMP_HEAVY) // 90 seconds downtime. return TRUE /datum/action/innate/ai/ranged/cameragun/proc/toggle_burstmode() From 233f739088d836c0b512f7a3a4cb2e31b02b2e18 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 12 Jan 2024 20:53:35 -0800 Subject: [PATCH 23/26] makes it easy to adminbus projs or add subtypes --- code/game/objects/items/robot/ai_upgrades.dm | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 31225f81008f..f71630043ee9 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -83,6 +83,10 @@ enable_text = span_notice("You prepare to overcharge a camera. Click a target for a nearby camera to shoot a laser at.") disable_text = span_notice("You dissipate the overcharged energy.") click_action = FALSE // Even though that we are a click action, we want to use Activate() and Deactivate(). + /// The beam projectile that is spawned and shot. + var/obj/projectile/beam/proj_type = /obj/projectile/beam/laser + /// Pass flags used for the `can_shoot_to` proc. + var/proj_pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE /// If this ability is sourced from being a traitor AI. var/from_traitor = FALSE /// Is burst mode activated? @@ -110,7 +114,7 @@ proj.nodamage = TRUE // Prevents this projectile from detonating certain objects (e.g. welding tanks). proj.log_override = TRUE proj.hitscan = TRUE - proj.pass_flags = PASSTABLE | PASSGLASS | PASSGRILLE + proj.pass_flags = proj_pass_flags proj.preparePixelProjectile(target, C) proj.fire() @@ -190,7 +194,10 @@ COOLDOWN_START(src, next_fire, fire_cooldown) var/turf/loc_chosen = get_turf(chosen_camera) - var/obj/projectile/beam/laser/proj = new(loc_chosen) + var/obj/projectile/beam/proj = new proj_type(loc_chosen) + if(!isprojectile(proj)) + Deactivate(FALSE) + CRASH("Camera gun's proj_type was not a projectile.") proj.preparePixelProjectile(target, chosen_camera) proj.firer = caller From 05559f3e2ae35469ffefa55e7d88b5cea87272d4 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 12 Jan 2024 20:58:41 -0800 Subject: [PATCH 24/26] custom emp drawback --- code/game/objects/items/robot/ai_upgrades.dm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index f71630043ee9..969cc2578953 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -100,6 +100,8 @@ /// How much time between shots (during burst mode)? var/fire_cooldown = 0.5 SECONDS COOLDOWN_DECLARE(next_fire) + /// What EMP strength will the camera be hit with after it is used to shoot? + var/emp_drawback = EMP_HEAVY // 7+ guarantees a 90 seconds downtime. /// Checks if it is possible for a (hitscan) projectile to reach a target in a straight line from a camera. /datum/action/innate/ai/ranged/cameragun/proc/can_shoot_to(obj/machinery/camera/C, atom/target) @@ -214,7 +216,8 @@ /* This EMP prevents burstmode from annihilating a stationary object/person. If someone gives a camera EMP resistance, then they had it coming. */ - chosen_camera.emp_act(EMP_HEAVY) // 90 seconds downtime. + if(emp_drawback > 0) + chosen_camera.emp_act(emp_drawback) return TRUE /datum/action/innate/ai/ranged/cameragun/proc/toggle_burstmode() From ff69c2e8f0d4e4768b7c71bf5d1f7510971506aa Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 12 Jan 2024 21:05:25 -0800 Subject: [PATCH 25/26] edge case --- code/game/objects/items/robot/ai_upgrades.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 969cc2578953..67036e415f8d 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -229,5 +229,6 @@ else COOLDOWN_START(src, next_activate, activate_cooldown) to_chat(owner, span_notice("Burstmode deactivated.")) + Deactivate(FALSE) // In case that they were in the middle of shooting. owner.playsound_local(owner, 'sound/items/timer.ogg', 50, FALSE) return TRUE From 4e2d546cefaa1248873695216031ecc00ac282b9 Mon Sep 17 00:00:00 2001 From: Rune Knight Date: Fri, 12 Jan 2024 21:11:56 -0800 Subject: [PATCH 26/26] the difference only matters in no lag environment --- code/game/objects/items/robot/ai_upgrades.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/items/robot/ai_upgrades.dm b/code/game/objects/items/robot/ai_upgrades.dm index 67036e415f8d..97498bd0b8a4 100644 --- a/code/game/objects/items/robot/ai_upgrades.dm +++ b/code/game/objects/items/robot/ai_upgrades.dm @@ -98,7 +98,7 @@ var/activate_cooldown = 60 SECONDS COOLDOWN_DECLARE(next_activate) /// How much time between shots (during burst mode)? - var/fire_cooldown = 0.5 SECONDS + var/fire_cooldown = 1 SECONDS COOLDOWN_DECLARE(next_fire) /// What EMP strength will the camera be hit with after it is used to shoot? var/emp_drawback = EMP_HEAVY // 7+ guarantees a 90 seconds downtime.