diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm index 46325a37b9f..90379fcac0a 100644 --- a/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm +++ b/code/datums/ai/basic_mobs/basic_subtrees/simple_attack_target.dm @@ -13,7 +13,7 @@ melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/average_speed /datum/ai_planning_subtree/basic_ranged_attack_subtree - operational_datums = list(/datum/element/ranged_attacks) + operational_datums = list(/datum/component/ranged_attacks) var/datum/ai_behavior/basic_ranged_attack/ranged_attack_behavior = /datum/ai_behavior/basic_ranged_attack /datum/ai_planning_subtree/basic_ranged_attack_subtree/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) diff --git a/code/datums/components/ranged_attacks.dm b/code/datums/components/ranged_attacks.dm new file mode 100644 index 00000000000..a61eff468ec --- /dev/null +++ b/code/datums/components/ranged_attacks.dm @@ -0,0 +1,78 @@ +/** + * Configurable ranged attack for basic mobs. + */ +/datum/component/ranged_attacks + /// What kind of casing do we use to fire? + var/casing_type + /// What kind of projectile to we fire? Use only one of this or casing_type + var/projectile_type + /// Sound to play when we fire our projectile + var/projectile_sound + /// Time to wait between shots + var/cooldown_time + /// Tracks time between shots + COOLDOWN_DECLARE(fire_cooldown) + +/datum/component/ranged_attacks/Initialize( + casing_type, + projectile_type, + projectile_sound = 'sound/weapons/gun/pistol/shot.ogg', + cooldown_time = 3 SECONDS, +) + . = ..() + if(!isbasicmob(parent)) + return COMPONENT_INCOMPATIBLE + + src.casing_type = casing_type + src.projectile_sound = projectile_sound + src.projectile_type = projectile_type + src.cooldown_time = cooldown_time + + if (casing_type && projectile_type) + CRASH("Set both casing type and projectile type in [parent]'s ranged attacks component! uhoh! stinky!") + if (!casing_type && !projectile_type) + CRASH("Set neither casing type nor projectile type in [parent]'s ranged attacks component! What are they supposed to be attacking with, air?") + +/datum/component/ranged_attacks/RegisterWithParent() + . = ..() + RegisterSignal(parent, COMSIG_MOB_ATTACK_RANGED, PROC_REF(fire_ranged_attack)) + ADD_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type) + +/datum/component/ranged_attacks/UnregisterFromParent() + . = ..() + UnregisterSignal(parent, COMSIG_MOB_ATTACK_RANGED) + REMOVE_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type) + +/datum/component/ranged_attacks/proc/fire_ranged_attack(mob/living/basic/firer, atom/target, modifiers) + SIGNAL_HANDLER + if (!COOLDOWN_FINISHED(src, fire_cooldown)) + return + COOLDOWN_START(src, fire_cooldown, cooldown_time) + INVOKE_ASYNC(src, PROC_REF(async_fire_ranged_attack), firer, target, modifiers) + +/// Actually fire the damn thing +/datum/component/ranged_attacks/proc/async_fire_ranged_attack(mob/living/basic/firer, atom/target, modifiers) + playsound(firer, projectile_sound, 100, TRUE) + var/turf/startloc = get_turf(firer) + + if(casing_type) + var/obj/item/ammo_casing/casing = new casing_type(startloc) + var/target_zone + if(ismob(target)) + var/mob/target_mob = target + target_zone = target_mob.get_random_valid_zone() + else + target_zone = ran_zone() + casing.fire_casing(target, firer, null, null, null, target_zone, 0, firer) + casing.AddElement(/datum/element/temporary_atom, 30 SECONDS) + return + + var/obj/projectile/bullet = new projectile_type(startloc) + bullet.starting = startloc + bullet.firer = firer + bullet.fired_from = firer + bullet.yo = target.y - startloc.y + bullet.xo = target.x - startloc.x + bullet.original = target + bullet.preparePixelProjectile(target, firer) + bullet.fire() diff --git a/code/datums/elements/ranged_attacks.dm b/code/datums/elements/ranged_attacks.dm deleted file mode 100644 index 2df0a9189ff..00000000000 --- a/code/datums/elements/ranged_attacks.dm +++ /dev/null @@ -1,59 +0,0 @@ -///This proc is used by basic mobs to give them a simple ranged attack! In theory this could be extended to -/datum/element/ranged_attacks - element_flags = ELEMENT_BESPOKE - argument_hash_start_idx = 2 - var/casingtype = /obj/item/ammo_casing/glockroach - var/projectilesound = 'sound/weapons/gun/pistol/shot.ogg' - var/projectiletype - -/datum/element/ranged_attacks/Attach(atom/movable/target, casingtype, projectilesound, projectiletype) - . = ..() - if(!isbasicmob(target)) - return COMPONENT_INCOMPATIBLE - - src.casingtype = casingtype - src.projectilesound = projectilesound - src.projectiletype = projectiletype - - RegisterSignal(target, COMSIG_MOB_ATTACK_RANGED, PROC_REF(fire_ranged_attack)) - - if(casingtype && projectiletype) - CRASH("Set both casing type and projectile type in [target]'s ranged attacks element! uhoh! stinky!") - - ADD_TRAIT(target, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type) - -/datum/element/ranged_attacks/Detach(datum/target) - UnregisterSignal(target, COMSIG_MOB_ATTACK_RANGED) - return ..() - -/datum/element/ranged_attacks/proc/fire_ranged_attack(mob/living/basic/firer, atom/target, modifiers) - SIGNAL_HANDLER - INVOKE_ASYNC(src, PROC_REF(async_fire_ranged_attack), firer, target, modifiers) - - -/datum/element/ranged_attacks/proc/async_fire_ranged_attack(mob/living/basic/firer, atom/target, modifiers) - var/turf/startloc = get_turf(firer) - - if(casingtype) - var/obj/item/ammo_casing/casing = new casingtype(startloc) - playsound(firer, projectilesound, 100, TRUE) - var/target_zone - if(ismob(target)) - var/mob/target_mob = target - target_zone = target_mob.get_random_valid_zone() - else - target_zone = ran_zone() - casing.fire_casing(target, firer, null, null, null, target_zone, 0, firer) - casing.AddElement(/datum/element/temporary_atom, 30 SECONDS) - - else if(projectiletype) - var/obj/projectile/P = new projectiletype(startloc) - playsound(firer, projectilesound, 100, TRUE) - P.starting = startloc - P.firer = firer - P.fired_from = firer - P.yo = target.y - startloc.y - P.xo = target.x - startloc.x - P.original = target - P.preparePixelProjectile(target, firer) - P.fire() diff --git a/code/modules/mob/living/basic/ruin_defender/stickman.dm b/code/modules/mob/living/basic/ruin_defender/stickman.dm index 40be390ab1c..8d0a5ab0cde 100644 --- a/code/modules/mob/living/basic/ruin_defender/stickman.dm +++ b/code/modules/mob/living/basic/ruin_defender/stickman.dm @@ -78,7 +78,7 @@ . = ..() var/static/list/stickman_drops = list(/obj/item/gun/ballistic/automatic/pistol/stickman) AddElement(/datum/element/death_drops, stickman_drops) - AddElement(/datum/element/ranged_attacks, /obj/item/ammo_casing/c9mm, 'sound/misc/bang.ogg') + AddComponent(/datum/component/ranged_attacks, casing_type = /obj/item/ammo_casing/c9mm, projectile_sound = 'sound/misc/bang.ogg', cooldown_time = 5 SECONDS) /datum/ai_controller/basic_controller/stickman/ranged planning_subtrees = list( diff --git a/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm b/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm index 83a1135ac05..df4ed505f3d 100644 --- a/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm +++ b/code/modules/mob/living/basic/space_fauna/hivebot/_hivebot.dm @@ -34,6 +34,8 @@ ai_controller = /datum/ai_controller/basic_controller/hivebot ///does this type do range attacks? var/ranged_attacker = FALSE + /// How often can we shoot? + var/ranged_attack_cooldown = 3 SECONDS /mob/living/basic/hivebot/Initialize(mapload) @@ -43,7 +45,7 @@ AddElement(/datum/element/appearance_on_aggro, overlay_icon = icon, overlay_state = "[initial(icon_state)]_attack") if(!ranged_attacker) return - AddElement(/datum/element/ranged_attacks, /obj/item/ammo_casing/hivebot) + AddComponent(/datum/component/ranged_attacks, /obj/item/ammo_casing/hivebot, cooldown_time = ranged_attack_cooldown) /mob/living/basic/hivebot/death(gibbed) do_sparks(number = 3, cardinal_only = TRUE, source = src) @@ -64,6 +66,7 @@ icon_dead = "ranged" ranged_attacker = TRUE ai_controller = /datum/ai_controller/basic_controller/hivebot/ranged/rapid + ranged_attack_cooldown = 1.5 SECONDS /mob/living/basic/hivebot/strong name = "strong hivebot" diff --git a/code/modules/mob/living/basic/syndicate/russian.dm b/code/modules/mob/living/basic/syndicate/russian.dm index de6c91a4dae..2de74b9b4d5 100644 --- a/code/modules/mob/living/basic/syndicate/russian.dm +++ b/code/modules/mob/living/basic/syndicate/russian.dm @@ -32,7 +32,7 @@ /mob/living/basic/syndicate/russian/ranged/Initialize(mapload) . = ..() - AddElement(/datum/element/ranged_attacks, casingtype, projectilesound) + AddComponent(/datum/component/ranged_attacks, casing_type = casingtype, projectile_sound = projectilesound, cooldown_time = 1 SECONDS) /mob/living/basic/syndicate/russian/ranged/lootless loot = list() diff --git a/code/modules/mob/living/basic/syndicate/syndicate.dm b/code/modules/mob/living/basic/syndicate/syndicate.dm index 25124d87da4..549dbf476d0 100644 --- a/code/modules/mob/living/basic/syndicate/syndicate.dm +++ b/code/modules/mob/living/basic/syndicate/syndicate.dm @@ -134,12 +134,16 @@ loot = list(/obj/effect/gibspawner/human) ai_controller = /datum/ai_controller/basic_controller/syndicate/ranged r_hand = /obj/item/gun/ballistic/automatic/pistol + /// Type of bullet we use var/casingtype = /obj/item/ammo_casing/c9mm + /// Sound to play when firing weapon var/projectilesound = 'sound/weapons/gun/pistol/shot.ogg' + /// Time between taking shots + var/ranged_cooldown = 1 SECONDS /mob/living/basic/syndicate/ranged/Initialize(mapload) . = ..() - AddElement(/datum/element/ranged_attacks, casingtype, projectilesound) + AddComponent(/datum/component/ranged_attacks, casing_type = casingtype, projectile_sound = projectilesound, cooldown_time = ranged_cooldown) /mob/living/basic/syndicate/ranged/infiltrator //shuttle loan event projectilesound = 'sound/weapons/gun/smg/shot_suppressed.ogg' @@ -168,6 +172,7 @@ casingtype = /obj/item/ammo_casing/c45 projectilesound = 'sound/weapons/gun/smg/shot.ogg' ai_controller = /datum/ai_controller/basic_controller/syndicate/ranged/burst + ranged_cooldown = 3 SECONDS r_hand = /obj/item/gun/ballistic/automatic/c20r /mob/living/basic/syndicate/ranged/smg/pilot //caravan ambush ruin @@ -197,6 +202,7 @@ /mob/living/basic/syndicate/ranged/shotgun casingtype = /obj/item/ammo_casing/shotgun/buckshot //buckshot (up to 72.5 brute) fired in a two-round burst ai_controller = /datum/ai_controller/basic_controller/syndicate/ranged/shotgunner + ranged_cooldown = 3 SECONDS r_hand = /obj/item/gun/ballistic/shotgun/bulldog /mob/living/basic/syndicate/ranged/shotgun/space diff --git a/code/modules/mob/living/basic/vermin/cockroach.dm b/code/modules/mob/living/basic/vermin/cockroach.dm index d7b2d8aade5..90e5bd53d2d 100644 --- a/code/modules/mob/living/basic/vermin/cockroach.dm +++ b/code/modules/mob/living/basic/vermin/cockroach.dm @@ -96,7 +96,7 @@ /mob/living/basic/cockroach/glockroach/Initialize(mapload) . = ..() - AddElement(/datum/element/ranged_attacks, /obj/item/ammo_casing/glockroach) + AddComponent(/datum/component/ranged_attacks, casing_type = /obj/item/ammo_casing/glockroach, cooldown_time = 1 SECONDS) /datum/ai_controller/basic_controller/cockroach/glockroach planning_subtrees = list( diff --git a/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/mob.dm b/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/mob.dm index 1a1b468e3f4..6efc58b3e75 100644 --- a/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/mob.dm +++ b/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/mob.dm @@ -85,7 +85,7 @@ /mob/living/basic/abductor/ranged/Initialize(mapload) . = ..() - AddElement(/datum/element/ranged_attacks, null, 'sound/weapons/laser.ogg', /obj/projectile/beam/laser) + AddComponent(/datum/component/ranged_attacks, projectile_sound = 'sound/weapons/laser.ogg', projectile_type = /obj/projectile/beam/laser) // Tankier variant diff --git a/tgstation.dme b/tgstation.dme index e429f5859af..342eb8ff344 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1103,6 +1103,7 @@ #include "code\datums\components\radiation_countdown.dm" #include "code\datums\components\radioactive_emitter.dm" #include "code\datums\components\radioactive_exposure.dm" +#include "code\datums\components\ranged_attacks.dm" #include "code\datums\components\reagent_refiller.dm" #include "code\datums\components\redirect_attack_hand_from_turf.dm" #include "code\datums\components\reflection.dm" @@ -1388,7 +1389,6 @@ #include "code\datums\elements\radiation_protected_clothing.dm" #include "code\datums\elements\radioactive.dm" #include "code\datums\elements\ranged_armour.dm" -#include "code\datums\elements\ranged_attacks.dm" #include "code\datums\elements\relay_attackers.dm" #include "code\datums\elements\ridable.dm" #include "code\datums\elements\rust.dm"