diff --git a/_maps/RandomRuins/SpaceRuins/medieval1.dmm b/_maps/RandomRuins/SpaceRuins/medieval1.dmm index 7216e62eb4f..e4df5166993 100644 --- a/_maps/RandomRuins/SpaceRuins/medieval1.dmm +++ b/_maps/RandomRuins/SpaceRuins/medieval1.dmm @@ -11,13 +11,13 @@ /turf/open/floor/wood/parquet, /area/ruin/space) "eo" = ( -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ dir = 1 }, /turf/open/floor/stone, /area/ruin/space) "gv" = ( -/mob/living/simple_animal/hostile/construct/artificer/hostile, +/mob/living/basic/construct/artificer/hostile, /turf/open/space/basic, /area/ruin/space) "hi" = ( @@ -37,7 +37,7 @@ /turf/open/space/basic, /area/ruin/space) "jy" = ( -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ dir = 4 }, /turf/open/misc/asteroid/basalt/wasteland, @@ -92,7 +92,7 @@ /turf/open/floor/stone, /area/ruin/space) "tj" = ( -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ dir = 4 }, /turf/open/space/basic, @@ -276,7 +276,7 @@ /turf/open/floor/wood/parquet, /area/ruin/space) "XM" = ( -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ dir = 8 }, /turf/open/space/basic, diff --git a/_maps/RandomRuins/SpaceRuins/skyrat/port_tarkon/defcon2.dmm b/_maps/RandomRuins/SpaceRuins/skyrat/port_tarkon/defcon2.dmm index 877faaad71f..95d181b18d6 100644 --- a/_maps/RandomRuins/SpaceRuins/skyrat/port_tarkon/defcon2.dmm +++ b/_maps/RandomRuins/SpaceRuins/skyrat/port_tarkon/defcon2.dmm @@ -1084,7 +1084,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/light/dim/directional/east, /obj/effect/turf_decal/delivery/blue, -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ health = 150; maxHealth = 150 }, @@ -1263,7 +1263,7 @@ /turf/open/misc/asteroid/airless, /area/solars/tarkon) "he" = ( -/mob/living/simple_animal/hostile/construct/artificer/hostile, +/mob/living/basic/construct/artificer/hostile, /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/ruin/space/has_grav/port_tarkon/porthall) @@ -3940,7 +3940,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/effect/decal/cleanable/glass, /obj/effect/decal/cleanable/blood, -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ health = 200; maxHealth = 200; name = "Third eye of the veil" @@ -4125,7 +4125,7 @@ /turf/open/floor/engine/co2, /area/ruin/space/has_grav/port_tarkon/atmos) "xo" = ( -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ health = 150; maxHealth = 150 }, @@ -4698,7 +4698,7 @@ /area/ruin/space/has_grav/port_tarkon/starboardhall) "AI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ health = 150; maxHealth = 150 }, @@ -8632,7 +8632,7 @@ "Wq" = ( /obj/structure/cable, /obj/effect/decal/cleanable/blood, -/mob/living/simple_animal/hostile/construct/artificer/hostile, +/mob/living/basic/construct/artificer/hostile, /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/ruin/space/has_grav/port_tarkon/cargo) @@ -8772,7 +8772,7 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/cable, -/mob/living/simple_animal/hostile/construct/artificer/hostile{ +/mob/living/basic/construct/artificer/hostile{ health = 150; maxHealth = 150 }, diff --git a/_maps/shuttles/emergency_narnar.dmm b/_maps/shuttles/emergency_narnar.dmm index 835a6054a87..9f8cbbcc00d 100644 --- a/_maps/shuttles/emergency_narnar.dmm +++ b/_maps/shuttles/emergency_narnar.dmm @@ -111,7 +111,7 @@ /turf/open/floor/cult, /area/shuttle/escape) "w" = ( -/mob/living/simple_animal/hostile/construct/artificer, +/mob/living/basic/construct/artificer, /turf/open/floor/cult, /area/shuttle/escape) "x" = ( diff --git a/code/__DEFINES/ai/ai_blackboard.dm b/code/__DEFINES/ai/ai_blackboard.dm index 079b052853f..9bd23d8cbf9 100644 --- a/code/__DEFINES/ai/ai_blackboard.dm +++ b/code/__DEFINES/ai/ai_blackboard.dm @@ -43,6 +43,10 @@ #define BB_BASIC_MOB_EXECUTION_TARGET "BB_basic_execution_target" ///Blackboard key for a whitelist typecache of "things we can target while trying to move" #define BB_OBSTACLE_TARGETTING_WHITELIST "BB_targetting_whitelist" +/// Key for the minimum status at which we want to target mobs (does not need to be specified if CONSCIOUS) +#define BB_TARGET_MINIMUM_STAT "BB_target_minimum_stat" +/// Flag for whether to target only wounded mobs +#define BB_TARGET_WOUNDED_ONLY "BB_target_wounded_only" /// Blackboard key storing how long your targetting datum has held a particular target #define BB_BASIC_MOB_HAS_TARGET_TIME "BB_basic_mob_has_target_time" diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 3940d070512..aa6c2d71f6f 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -924,6 +924,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_FISH_FED_LUBE "fish_fed_lube" #define TRAIT_FISH_NO_HUNGER "fish_no_hunger" +/// Trait given to angelic constructs to let them purge cult runes +#define TRAIT_ANGELIC "angelic" + // common trait sources #define TRAIT_GENERIC "generic" #define UNCONSCIOUS_TRAIT "unconscious" diff --git a/code/__HELPERS/cmp.dm b/code/__HELPERS/cmp.dm index 5627e737c2b..e1303882b1a 100644 --- a/code/__HELPERS/cmp.dm +++ b/code/__HELPERS/cmp.dm @@ -194,3 +194,7 @@ var/datum/award/award_a = SSachievements.awards[type_a] var/datum/award/award_b = SSachievements.awards[type_b] return award_b?.load_priority - award_a?.load_priority + +/// Orders mobs by health +/proc/cmp_mob_health(mob/living/mob_a, mob/living/mob_b) + return mob_b.health - mob_a.health diff --git a/code/datums/ai/basic_mobs/basic_ai_behaviors/wounded_targetting.dm b/code/datums/ai/basic_mobs/basic_ai_behaviors/wounded_targetting.dm new file mode 100644 index 00000000000..8535554e54b --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_ai_behaviors/wounded_targetting.dm @@ -0,0 +1,11 @@ +/// Picks targets based on which one has the lowest health +/datum/ai_behavior/find_potential_targets/most_wounded + +/datum/ai_behavior/find_potential_targets/most_wounded/pick_final_target(datum/ai_controller/controller, list/filtered_targets) + var/list/living_targets = list() + for(var/mob/living/living_target in filtered_targets) + living_targets += filtered_targets + if(living_targets.len) + sortTim(living_targets, GLOBAL_PROC_REF(cmp_mob_health)) + return living_targets[1] + return ..() diff --git a/code/datums/ai/basic_mobs/basic_subtrees/simple_find_wounded_target.dm b/code/datums/ai/basic_mobs/basic_subtrees/simple_find_wounded_target.dm new file mode 100644 index 00000000000..8840c1ea3a7 --- /dev/null +++ b/code/datums/ai/basic_mobs/basic_subtrees/simple_find_wounded_target.dm @@ -0,0 +1,6 @@ +/// Selects the most wounded potential target that we can see +/datum/ai_planning_subtree/simple_find_wounded_target + +/datum/ai_planning_subtree/simple_find_wounded_target/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + . = ..() + controller.queue_behavior(/datum/ai_behavior/find_potential_targets/most_wounded, BB_BASIC_MOB_CURRENT_TARGET, BB_TARGETTING_DATUM, BB_BASIC_MOB_CURRENT_TARGET_HIDING_LOCATION) diff --git a/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm b/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm index 1d6fde335cb..d8b7e49c23c 100644 --- a/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm +++ b/code/datums/ai/basic_mobs/targetting_datums/basic_targetting_datum.dm @@ -15,10 +15,12 @@ /datum/targetting_datum/basic /// When we do our basic faction check, do we look for exact faction matches? var/check_factions_exactly = FALSE - /// Minimum status to attack living beings - var/stat_attack = CONSCIOUS - ///Whether we care for seeing the target or not + /// Whether we care for seeing the target or not var/ignore_sight = FALSE + /// Blackboard key containing the minimum stat of a living mob to target + var/minimum_stat_key = BB_TARGET_MINIMUM_STAT + /// If this blackboard key is TRUE, makes us only target wounded mobs + var/target_wounded_key /datum/targetting_datum/basic/can_attack(mob/living/living_mob, atom/the_target, vision_range) var/datum/ai_controller/basic_controller/our_controller = living_mob.ai_controller @@ -54,7 +56,9 @@ var/mob/living/living_target = the_target if(faction_check(our_controller, living_mob, living_target)) return FALSE - if(living_target.stat > stat_attack) + if(living_target.stat > our_controller.blackboard[minimum_stat_key]) + return FALSE + if(target_wounded_key && our_controller.blackboard[target_wounded_key] && living_target.health == living_target.maxHealth) return FALSE return TRUE @@ -121,8 +125,8 @@ find_smaller = FALSE inclusive = FALSE -/datum/targetting_datum/basic/attack_until_dead - stat_attack = HARD_CRIT +/// Makes the mob only attack their own faction. Useful mostly if their attacks do something helpful (e.g. healing touch). +/datum/targetting_datum/basic/same_faction -/datum/targetting_datum/basic/attack_even_if_dead - stat_attack = DEAD +/datum/targetting_datum/basic/same_faction/faction_check(mob/living/living_mob, mob/living/the_target) + return !..() // inverts logic to ONLY target mobs that share a faction diff --git a/code/datums/components/healing_touch.dm b/code/datums/components/healing_touch.dm index 029b0f660ef..a81de7ab04a 100644 --- a/code/datums/components/healing_touch.dm +++ b/code/datums/components/healing_touch.dm @@ -159,7 +159,7 @@ if(show_health && !iscarbon(target)) var/formatted_string = format_string("%TARGET% now has [target.health]/[target.maxHealth] health.", healer, target) - healer.visible_message(span_danger(formatted_string)) + to_chat(healer, span_danger(formatted_string)) /// Reformats the passed string with the replacetext keys /datum/component/healing_touch/proc/format_string(string, atom/source, atom/target) diff --git a/code/datums/components/pet_commands/pet_commands_basic.dm b/code/datums/components/pet_commands/pet_commands_basic.dm index ea9612c4ada..68a20fa77f2 100644 --- a/code/datums/components/pet_commands/pet_commands_basic.dm +++ b/code/datums/components/pet_commands/pet_commands_basic.dm @@ -185,11 +185,10 @@ UnregisterSignal(unfriended, COMSIG_ATOM_WAS_ATTACKED) /datum/pet_command/protect_owner/execute_action(datum/ai_controller/controller) - var/datum/targetting_datum/basic/targetting = controller.blackboard[BB_TARGETTING_DATUM] var/mob/living/victim = controller.blackboard[BB_CURRENT_PET_TARGET] if(QDELETED(victim)) return - if(victim.stat > targetting.stat_attack) + if(victim.stat > controller.blackboard[BB_TARGET_MINIMUM_STAT]) controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) return controller.queue_behavior(protect_behavior, BB_CURRENT_PET_TARGET, BB_PET_TARGETTING_DATUM) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index c24962524e7..63b2c6613c3 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -112,7 +112,7 @@ Runes can either be invoked by one's self or with many different cultists. Each /obj/effect/rune/attack_animal(mob/living/simple_animal/user, list/modifiers) if(isshade(user) || isconstruct(user)) - if(istype(user, /mob/living/simple_animal/hostile/construct/wraith/angelic) || istype(user, /mob/living/simple_animal/hostile/construct/juggernaut/angelic) || istype(user, /mob/living/simple_animal/hostile/construct/artificer/angelic)) + if(HAS_TRAIT(user, TRAIT_ANGELIC)) to_chat(user, span_warning("You purge the rune!")) qdel(src) else if(construct_invoke || !IS_CULTIST(user)) //if you're not a cult construct we want the normal fail message diff --git a/code/modules/antagonists/wizard/equipment/soulstone.dm b/code/modules/antagonists/wizard/equipment/soulstone.dm index 610c8425d97..f7be579ad1a 100644 --- a/code/modules/antagonists/wizard/equipment/soulstone.dm +++ b/code/modules/antagonists/wizard/equipment/soulstone.dm @@ -490,15 +490,15 @@ makeNewConstruct(/mob/living/simple_animal/hostile/construct/wraith/noncult, target, creator, cultoverride, loc_override) if(CONSTRUCT_ARTIFICER) if(IS_CULTIST(creator)) - makeNewConstruct(/mob/living/simple_animal/hostile/construct/artificer, target, creator, cultoverride, loc_override) // ignore themes, the actual giving of cult info is in the makeNewConstruct proc + makeNewConstruct(/mob/living/basic/construct/artificer, target, creator, cultoverride, loc_override) // ignore themes, the actual giving of cult info is in the makeNewConstruct proc return switch(theme) if(THEME_WIZARD) - makeNewConstruct(/mob/living/simple_animal/hostile/construct/artificer/mystic, target, creator, cultoverride, loc_override) + makeNewConstruct(/mob/living/basic/construct/artificer/mystic, target, creator, cultoverride, loc_override) if(THEME_HOLY) - makeNewConstruct(/mob/living/simple_animal/hostile/construct/artificer/angelic, target, creator, cultoverride, loc_override) + makeNewConstruct(/mob/living/basic/construct/artificer/angelic, target, creator, cultoverride, loc_override) if(THEME_CULT) - makeNewConstruct(/mob/living/simple_animal/hostile/construct/artificer/noncult, target, creator, cultoverride, loc_override) + makeNewConstruct(/mob/living/basic/construct/artificer/noncult, target, creator, cultoverride, loc_override) /proc/makeNewConstruct(mob/living/simple_animal/hostile/construct/ctype, mob/target, mob/stoner = null, cultoverride = FALSE, loc_override = null) if(QDELETED(target)) diff --git a/code/modules/events/portal_storm.dm b/code/modules/events/portal_storm.dm index 9ba8ca4992e..662b2f404c3 100644 --- a/code/modules/events/portal_storm.dm +++ b/code/modules/events/portal_storm.dm @@ -23,7 +23,7 @@ max_wizard_trigger_potency = 7 /datum/round_event/portal_storm/portal_storm_narsie - boss_types = list(/mob/living/simple_animal/hostile/construct/artificer/hostile = 6) + boss_types = list(/mob/living/basic/construct/artificer/hostile = 6) hostile_types = list(/mob/living/simple_animal/hostile/construct/juggernaut/hostile = 8,\ /mob/living/simple_animal/hostile/construct/wraith/hostile = 6) diff --git a/code/modules/mob/living/basic/blob_minions/blob_ai.dm b/code/modules/mob/living/basic/blob_minions/blob_ai.dm index 6168b7ca83b..fc21d030d07 100644 --- a/code/modules/mob/living/basic/blob_minions/blob_ai.dm +++ b/code/modules/mob/living/basic/blob_minions/blob_ai.dm @@ -4,7 +4,8 @@ */ /datum/ai_controller/basic_controller/blobbernaut blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/attack_until_dead, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/jps @@ -20,7 +21,8 @@ */ /datum/ai_controller/basic_controller/blob_zombie blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/attack_until_dead, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/jps @@ -37,7 +39,8 @@ */ /datum/ai_controller/basic_controller/blob_spore blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/attack_until_dead, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/jps diff --git a/code/modules/mob/living/basic/clown/clown_ai.dm b/code/modules/mob/living/basic/clown/clown_ai.dm index b2e6418dde7..f54c432140e 100644 --- a/code/modules/mob/living/basic/clown/clown_ai.dm +++ b/code/modules/mob/living/basic/clown/clown_ai.dm @@ -14,6 +14,7 @@ /datum/ai_controller/basic_controller/clown/murder blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/attack_until_dead, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, BB_BASIC_MOB_SPEAK_LINES = null, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) diff --git a/code/modules/mob/living/basic/constructs/_construct.dm b/code/modules/mob/living/basic/constructs/_construct.dm index f2e55cceb86..fcafab62259 100644 --- a/code/modules/mob/living/basic/constructs/_construct.dm +++ b/code/modules/mob/living/basic/constructs/_construct.dm @@ -61,6 +61,7 @@ heal_burn = 0,\ heal_time = 0,\ valid_targets_typecache = typecacheof(list(/mob/living/basic/construct, /mob/living/simple_animal/hostile/construct, /mob/living/simple_animal/shade)),\ + valid_biotypes = MOB_MINERAL | MOB_SPIRIT,\ self_targetting = can_repair_self ? HEALING_TOUCH_ANYONE : HEALING_TOUCH_NOT_SELF,\ action_text = "%SOURCE% begins repairing %TARGET%'s dents.",\ complete_text = "%TARGET%'s dents are repaired.",\ diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/artificer.dm b/code/modules/mob/living/basic/constructs/artificer.dm similarity index 50% rename from code/modules/mob/living/simple_animal/hostile/constructs/artificer.dm rename to code/modules/mob/living/basic/constructs/artificer.dm index 743931c508d..d3be414791b 100644 --- a/code/modules/mob/living/simple_animal/hostile/constructs/artificer.dm +++ b/code/modules/mob/living/basic/constructs/artificer.dm @@ -1,4 +1,4 @@ -/mob/living/simple_animal/hostile/construct/artificer +/mob/living/basic/construct/artificer name = "Artificer" real_name = "Artificer" desc = "A bulbous construct dedicated to building and maintaining the Cult of Nar'Sie's armies." @@ -8,15 +8,11 @@ health = 50 response_harm_continuous = "viciously beats" response_harm_simple = "viciously beat" - harm_intent_damage = 5 obj_damage = 60 melee_damage_lower = 5 melee_damage_upper = 5 - retreat_distance = 10 - minimum_distance = 10 //AI artificers will flee like fuck attack_verb_continuous = "rams" attack_verb_simple = "ram" - environment_smash = ENVIRONMENT_SMASH_WALLS attack_sound = 'sound/weapons/punch2.ogg' construct_spells = list( /datum/action/cooldown/spell/conjure/cult_floor, @@ -33,70 +29,38 @@ can_repair = TRUE can_repair_self = TRUE + smashes_walls = TRUE ///The health HUD applied to this mob. var/health_hud = DATA_HUD_MEDICAL_ADVANCED -/mob/living/simple_animal/hostile/construct/artificer/Initialize(mapload) +/mob/living/basic/construct/artificer/Initialize(mapload) . = ..() + AddElement(/datum/element/ai_retaliate) var/datum/atom_hud/datahud = GLOB.huds[health_hud] datahud.show_to(src) -/mob/living/simple_animal/hostile/construct/artificer/Found(atom/thing) //what have we found here? - if(!isconstruct(thing)) //is it a construct? - return FALSE - var/mob/living/simple_animal/hostile/construct/cultie = thing - if(cultie.health < cultie.maxHealth) //is it hurt? let's go heal it if it is - return TRUE +/// Hostile NPC version. Heals nearby constructs and cult structures, avoids targets that aren't extremely hurt. +/mob/living/basic/construct/artificer/hostile + ai_controller = /datum/ai_controller/basic_controller/artificer + smashes_walls = FALSE -/mob/living/simple_animal/hostile/construct/artificer/CanAttack(atom/the_target) - if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it - return FALSE - if(Found(the_target) || ..()) //If we Found it or Can_Attack it normally, we Can_Attack it as long as it wasn't invisible - return TRUE //as a note this shouldn't be added to base hostile mobs because it'll mess up retaliate hostile mobs - return FALSE - -/mob/living/simple_animal/hostile/construct/artificer/MoveToTarget(list/possible_targets) - ..() - if(!isliving(target)) - return - - var/mob/living/victim = target - if(isconstruct(victim) && victim.health >= victim.maxHealth) //is this target an unhurt construct? stop trying to heal it - LoseTarget() - return - if(victim.health <= melee_damage_lower+melee_damage_upper) //ey bucko you're hurt as fuck let's go hit you - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/artificer/Aggro() - ..() - if(isconstruct(target)) //oh the target is a construct no need to flee - retreat_distance = null - minimum_distance = 1 - -/mob/living/simple_animal/hostile/construct/artificer/LoseAggro() - ..() - retreat_distance = initial(retreat_distance) - minimum_distance = initial(minimum_distance) - -/mob/living/simple_animal/hostile/construct/artificer/hostile //actually hostile, will move around, hit things, heal other constructs - AIStatus = AI_ON - environment_smash = ENVIRONMENT_SMASH_STRUCTURES //only token destruction, don't smash the cult wall NO STOP - -/////////////////////////////Artificer-alts///////////////////////// -/mob/living/simple_animal/hostile/construct/artificer/angelic +// Alternate artificer themes +/mob/living/basic/construct/artificer/angelic desc = "A bulbous construct dedicated to building and maintaining holy armies." theme = THEME_HOLY - loot = list(/obj/item/ectoplasm/angelic) construct_spells = list( /datum/action/cooldown/spell/conjure/soulstone/purified, /datum/action/cooldown/spell/conjure/construct/lesser, /datum/action/cooldown/spell/aoe/magic_missile/lesser, /datum/action/innate/cult/create_rune/revive, ) -/mob/living/simple_animal/hostile/construct/artificer/mystic + +/mob/living/basic/construct/artificer/angelic/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_ANGELIC, INNATE_TRAIT) + +/mob/living/basic/construct/artificer/mystic theme = THEME_WIZARD - loot = list(/obj/item/ectoplasm/mystic) construct_spells = list( /datum/action/cooldown/spell/conjure/cult_floor, /datum/action/cooldown/spell/conjure/cult_wall, @@ -106,7 +70,7 @@ /datum/action/innate/cult/create_rune/revive, ) -/mob/living/simple_animal/hostile/construct/artificer/noncult +/mob/living/basic/construct/artificer/noncult construct_spells = list( /datum/action/cooldown/spell/conjure/cult_floor, /datum/action/cooldown/spell/conjure/cult_wall, diff --git a/code/modules/mob/living/basic/constructs/construct_ai.dm b/code/modules/mob/living/basic/constructs/construct_ai.dm new file mode 100644 index 00000000000..fde0876955b --- /dev/null +++ b/code/modules/mob/living/basic/constructs/construct_ai.dm @@ -0,0 +1,25 @@ +/// Artificers +/datum/ai_controller/basic_controller/artificer + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/same_faction/construct, + BB_FLEE_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_WOUNDED_ONLY = TRUE, + ) + + ai_movement = /datum/ai_movement/basic_avoidance + idle_behavior = /datum/idle_behavior/idle_random_walk + planning_subtrees = list( + /datum/ai_planning_subtree/simple_find_wounded_target, + /datum/ai_planning_subtree/basic_melee_attack_subtree, + /datum/ai_planning_subtree/target_retaliate/to_flee, + /datum/ai_planning_subtree/flee_target/from_flee_key, + ) + +/// Targetting datum that will only allow mobs that constructs can heal. +/datum/targetting_datum/basic/same_faction/construct + target_wounded_key = BB_TARGET_WOUNDED_ONLY + +/datum/targetting_datum/basic/same_faction/construct/can_attack(mob/living/living_mob, atom/the_target, vision_range, check_faction = TRUE) + if(isconstruct(the_target) || istype(the_target, /mob/living/simple_animal/shade)) + return ..() + return FALSE diff --git a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm index c6230754277..daf5b7af083 100644 --- a/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm +++ b/code/modules/mob/living/basic/farm_animals/gorilla/gorilla_ai.dm @@ -1,7 +1,8 @@ /// Pretty basic, just click people to death. Also hunt and eat bananas. /datum/ai_controller/basic_controller/gorilla blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items/gorilla, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items, + BB_TARGET_MINIMUM_STAT = UNCONSCIOUS, BB_EMOTE_KEY = "ooga", BB_EMOTE_CHANCE = 40, ) @@ -18,9 +19,6 @@ /datum/ai_planning_subtree/basic_melee_attack_subtree, ) -/datum/targetting_datum/basic/allow_items/gorilla - stat_attack = UNCONSCIOUS - /datum/ai_planning_subtree/attack_obstacle_in_path/gorilla attack_behaviour = /datum/ai_behavior/attack_obstructions/gorilla diff --git a/code/modules/mob/living/basic/heretic/star_gazer.dm b/code/modules/mob/living/basic/heretic/star_gazer.dm index c55a3dafe9d..a8af89a7f71 100644 --- a/code/modules/mob/living/basic/heretic/star_gazer.dm +++ b/code/modules/mob/living/basic/heretic/star_gazer.dm @@ -78,7 +78,8 @@ /datum/ai_controller/basic_controller/star_gazer blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/star_gazer(), + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_PET_TARGETTING_DATUM = new /datum/targetting_datum/basic/not_friends/attack_closed_turfs, ) @@ -92,9 +93,6 @@ /datum/ai_planning_subtree/basic_melee_attack_subtree, ) -/datum/targetting_datum/basic/star_gazer - stat_attack = HARD_CRIT - /datum/ai_planning_subtree/attack_obstacle_in_path/star_gazer attack_behaviour = /datum/ai_behavior/attack_obstructions/star_gazer diff --git a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm index 47280af4281..2d9715caf37 100644 --- a/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm +++ b/code/modules/mob/living/basic/icemoon/ice_whelp/ice_whelp_ai.dm @@ -1,7 +1,8 @@ #define ENRAGE_ADDITION 25 /datum/ai_controller/basic_controller/ice_whelp blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items/goliath, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_WHELP_ENRAGED = 0, ) diff --git a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm index e5d793fa150..18f614359c0 100644 --- a/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm +++ b/code/modules/mob/living/basic/lavaland/brimdemon/brimdemon_ai.dm @@ -3,7 +3,8 @@ */ /datum/ai_controller/basic_controller/brimdemon blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/brimdemon, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_traits = PAUSE_DURING_DO_AFTER @@ -16,9 +17,6 @@ /datum/ai_planning_subtree/targeted_mob_ability/brimbeam, ) -/datum/targetting_datum/basic/brimdemon - stat_attack = HARD_CRIT - /datum/ai_planning_subtree/move_to_cardinal/brimdemon move_behaviour = /datum/ai_behavior/move_to_cardinal/brimdemon diff --git a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm index 7e723f5d93a..48f0e213e73 100644 --- a/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm +++ b/code/modules/mob/living/basic/lavaland/goliath/goliath_ai.dm @@ -3,7 +3,8 @@ /datum/ai_controller/basic_controller/goliath blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items/goliath, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/allow_items, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/basic_avoidance @@ -19,9 +20,6 @@ /datum/ai_planning_subtree/goliath_dig, ) -/datum/targetting_datum/basic/allow_items/goliath - stat_attack = HARD_CRIT - /datum/ai_planning_subtree/basic_melee_attack_subtree/goliath operational_datums = list(/datum/component/ai_target_timer) melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/goliath diff --git a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm index ff29ea3c240..9167846b685 100644 --- a/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm +++ b/code/modules/mob/living/basic/lavaland/legion/legion_ai.dm @@ -1,7 +1,8 @@ /// Keep away and launch skulls at every opportunity, prioritising injured allies /datum/ai_controller/basic_controller/legion blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/attack_until_dead/legion, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/legion, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_AGGRO_RANGE = 5, // Unobservant BB_BASIC_MOB_FLEE_DISTANCE = 6, ) @@ -18,7 +19,8 @@ /// Chase and attack whatever we are targetting, if it's friendly we will heal them /datum/ai_controller/basic_controller/legion_brood blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/attack_until_dead/legion, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/legion, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/basic_avoidance @@ -29,9 +31,9 @@ ) /// Target nearby friendlies if they are hurt (and are not themselves Legions) -/datum/targetting_datum/basic/attack_until_dead/legion +/datum/targetting_datum/basic/legion -/datum/targetting_datum/basic/attack_until_dead/legion/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) +/datum/targetting_datum/basic/legion/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) if (!living_mob.faction_check_atom(the_target, exact_match = check_factions_exactly)) return FALSE if (istype(the_target, living_mob.type)) diff --git a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm index 7b6926fca04..c89cc3b9c07 100644 --- a/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm +++ b/code/modules/mob/living/basic/lavaland/lobstrosity/lobstrosity_ai.dm @@ -1,6 +1,7 @@ /datum/ai_controller/basic_controller/lobstrosity blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/lobster, + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, BB_LOBSTROSITY_EXPLOIT_TRAITS = list(TRAIT_INCAPACITATED, TRAIT_FLOORED, TRAIT_IMMOBILIZED, TRAIT_KNOCKEDOUT), BB_LOBSTROSITY_FINGER_LUST = 0 ) @@ -18,9 +19,6 @@ /datum/ai_planning_subtree/find_fingers, ) -/datum/targetting_datum/basic/lobster - stat_attack = HARD_CRIT - /datum/ai_planning_subtree/basic_melee_attack_subtree/lobster melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/lobster diff --git a/code/modules/mob/living/basic/space_fauna/faithless.dm b/code/modules/mob/living/basic/space_fauna/faithless.dm index 88696b3a3c7..5cfdd4b47b6 100644 --- a/code/modules/mob/living/basic/space_fauna/faithless.dm +++ b/code/modules/mob/living/basic/space_fauna/faithless.dm @@ -55,7 +55,8 @@ /datum/ai_controller/basic_controller/faithless blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/faithless(), + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = UNCONSCIOUS, BB_LOW_PRIORITY_HUNTING_TARGET = null, // lights ) @@ -69,6 +70,3 @@ /datum/ai_planning_subtree/find_and_hunt_target/look_for_light_fixtures, /datum/ai_planning_subtree/random_speech/faithless, ) - -/datum/targetting_datum/basic/faithless - stat_attack = UNCONSCIOUS diff --git a/code/modules/mob/living/basic/space_fauna/mushroom.dm b/code/modules/mob/living/basic/space_fauna/mushroom.dm index 6f900b08abf..056a4558081 100644 --- a/code/modules/mob/living/basic/space_fauna/mushroom.dm +++ b/code/modules/mob/living/basic/space_fauna/mushroom.dm @@ -58,6 +58,7 @@ /datum/ai_controller/basic_controller/mushroom blackboard = list( BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/mushroom, + BB_TARGET_MINIMUM_STAT = DEAD, ) ai_movement = /datum/ai_movement/basic_avoidance @@ -70,7 +71,6 @@ /datum/targetting_datum/basic/mushroom - stat_attack = DEAD ///we only attacked another mushrooms /datum/targetting_datum/basic/mushroom/faction_check(datum/ai_controller/controller, mob/living/living_mob, mob/living/the_target) diff --git a/code/modules/mob/living/basic/syndicate/syndicate_ai.dm b/code/modules/mob/living/basic/syndicate/syndicate_ai.dm index be84c1a5685..b256226f161 100644 --- a/code/modules/mob/living/basic/syndicate/syndicate_ai.dm +++ b/code/modules/mob/living/basic/syndicate/syndicate_ai.dm @@ -1,6 +1,7 @@ /datum/ai_controller/basic_controller/syndicate blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/attack_until_dead + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/basic_avoidance diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 15704d88b7a..daa7d0e60ae 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -492,7 +492,7 @@ if(2) new /mob/living/simple_animal/hostile/construct/wraith/hostile(get_turf(src)) if(3) - new /mob/living/simple_animal/hostile/construct/artificer/hostile(get_turf(src)) + new /mob/living/basic/construct/artificer/hostile(get_turf(src)) if(4) new /mob/living/simple_animal/hostile/construct/proteon/hostile(get_turf(src)) spawn_dust() diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/juggernaut.dm b/code/modules/mob/living/simple_animal/hostile/constructs/juggernaut.dm index 3eb80cf7a88..893700abe8a 100644 --- a/code/modules/mob/living/simple_animal/hostile/constructs/juggernaut.dm +++ b/code/modules/mob/living/simple_animal/hostile/constructs/juggernaut.dm @@ -68,6 +68,10 @@ theme = THEME_HOLY loot = list(/obj/item/ectoplasm/angelic) +/mob/living/simple_animal/hostile/construct/juggernaut/angelic/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_ANGELIC, INNATE_TRAIT) + /mob/living/simple_animal/hostile/construct/juggernaut/mystic theme = THEME_WIZARD loot = list(/obj/item/ectoplasm/mystic) diff --git a/code/modules/mob/living/simple_animal/hostile/constructs/wraith.dm b/code/modules/mob/living/simple_animal/hostile/constructs/wraith.dm index d0727aacfa9..83bc262e8bc 100644 --- a/code/modules/mob/living/simple_animal/hostile/constructs/wraith.dm +++ b/code/modules/mob/living/simple_animal/hostile/constructs/wraith.dm @@ -67,6 +67,10 @@ ) loot = list(/obj/item/ectoplasm/angelic) +/mob/living/simple_animal/hostile/construct/wraith/angelic/Initialize(mapload) + . = ..() + ADD_TRAIT(src, TRAIT_ANGELIC, INNATE_TRAIT) + /mob/living/simple_animal/hostile/construct/wraith/mystic theme = THEME_WIZARD construct_spells = list( diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index 228330a6ece..ecfa9698362 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -67,11 +67,6 @@ /mob/living/simple_animal/hostile/asteroid/polarbear/lesser, /mob/living/simple_animal/hostile/asteroid/wolf, /mob/living/simple_animal/hostile/construct, - /mob/living/simple_animal/hostile/construct/artificer, - /mob/living/simple_animal/hostile/construct/artificer/angelic, - /mob/living/simple_animal/hostile/construct/artificer/hostile, - /mob/living/simple_animal/hostile/construct/artificer/mystic, - /mob/living/simple_animal/hostile/construct/artificer/noncult, /mob/living/simple_animal/hostile/construct/juggernaut, /mob/living/simple_animal/hostile/construct/juggernaut/angelic, /mob/living/simple_animal/hostile/construct/juggernaut/hostile, diff --git a/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/abductor_ai.dm b/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/abductor_ai.dm index 83db9694a7d..8d33efb03d5 100644 --- a/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/abductor_ai.dm +++ b/modular_skyrat/modules/awaymissions_skyrat/mothership_astrum/abductor_ai.dm @@ -3,7 +3,8 @@ */ /datum/ai_controller/basic_controller/abductor blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/abductor() + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/basic_avoidance @@ -13,16 +14,12 @@ /datum/ai_planning_subtree/basic_melee_attack_subtree/abductor ) -/datum/targetting_datum/basic/abductor - stat_attack = HARD_CRIT - /datum/ai_planning_subtree/basic_melee_attack_subtree/abductor melee_attack_behavior = /datum/ai_behavior/basic_melee_attack/abductor /datum/ai_behavior/basic_melee_attack/abductor action_cooldown = 1 SECONDS - /** * Ranged */ diff --git a/modular_skyrat/modules/clock_cult/code/mobs/clockwork_marauder.dm b/modular_skyrat/modules/clock_cult/code/mobs/clockwork_marauder.dm index 7094e11fcef..08ef285d89c 100644 --- a/modular_skyrat/modules/clock_cult/code/mobs/clockwork_marauder.dm +++ b/modular_skyrat/modules/clock_cult/code/mobs/clockwork_marauder.dm @@ -43,7 +43,6 @@ GLOBAL_LIST_EMPTY(clockwork_marauders) /// How many hits the shield can take before it breaks. var/shield_health = MARAUDER_SHIELD_MAX - /mob/living/basic/clockwork_marauder/Initialize(mapload) . = ..() if(length(loot)) @@ -80,7 +79,6 @@ GLOBAL_LIST_EMPTY(clockwork_marauders) return ..() - /mob/living/basic/clockwork_marauder/proc/block_bullets(datum/source, obj/projectile/hitting_projectile) SIGNAL_HANDLER @@ -121,7 +119,8 @@ GLOBAL_LIST_EMPTY(clockwork_marauders) /datum/ai_controller/basic_controller/clockwork_marauder blackboard = list( - BB_TARGETTING_DATUM = new /datum/targetting_datum/basic/clockwork_marauder() + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_TARGET_MINIMUM_STAT = HARD_CRIT, ) ai_movement = /datum/ai_movement/basic_avoidance @@ -138,11 +137,6 @@ GLOBAL_LIST_EMPTY(clockwork_marauders) /datum/ai_behavior/basic_melee_attack/clockwork_marauder action_cooldown = 1.2 SECONDS - -/datum/targetting_datum/basic/clockwork_marauder - stat_attack = HARD_CRIT - - /obj/item/nullrod/Initialize(mapload) . = ..() AddElement(/datum/element/bane, /mob/living/basic/clockwork_marauder, 1, 15, FALSE) diff --git a/tgstation.dme b/tgstation.dme index bcfad9b5069..a64889035cc 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -913,6 +913,7 @@ #include "code\datums\ai\basic_mobs\basic_ai_behaviors\tipped_reaction.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\travel_towards.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\ventcrawling.dm" +#include "code\datums\ai\basic_mobs\basic_ai_behaviors\wounded_targetting.dm" #include "code\datums\ai\basic_mobs\basic_ai_behaviors\write_on_paper.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\attack_adjacent_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\attack_obstacle_in_path.dm" @@ -932,6 +933,7 @@ #include "code\datums\ai\basic_mobs\basic_subtrees\simple_attack_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_nearest_target_to_flee.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_target.dm" +#include "code\datums\ai\basic_mobs\basic_subtrees\simple_find_wounded_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\sleep_with_no_target.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\speech_subtree.dm" #include "code\datums\ai\basic_mobs\basic_subtrees\stare_at_thing.dm" @@ -4460,6 +4462,8 @@ #include "code\modules\mob\living\basic\clown\clown.dm" #include "code\modules\mob\living\basic\clown\clown_ai.dm" #include "code\modules\mob\living\basic\constructs\_construct.dm" +#include "code\modules\mob\living\basic\constructs\artificer.dm" +#include "code\modules\mob\living\basic\constructs\construct_ai.dm" #include "code\modules\mob\living\basic\constructs\harvester.dm" #include "code\modules\mob\living\basic\farm_animals\deer.dm" #include "code\modules\mob\living\basic\farm_animals\pig.dm" @@ -4846,7 +4850,6 @@ #include "code\modules\mob\living\simple_animal\hostile\vatbeast.dm" #include "code\modules\mob\living\simple_animal\hostile\wizard.dm" #include "code\modules\mob\living\simple_animal\hostile\zombie.dm" -#include "code\modules\mob\living\simple_animal\hostile\constructs\artificer.dm" #include "code\modules\mob\living\simple_animal\hostile\constructs\constructs.dm" #include "code\modules\mob\living\simple_animal\hostile\constructs\juggernaut.dm" #include "code\modules\mob\living\simple_animal\hostile\constructs\wraith.dm" diff --git a/tools/UpdatePaths/Scripts/79015_simple_to_basic_construct_artificer.txt b/tools/UpdatePaths/Scripts/79015_simple_to_basic_construct_artificer.txt new file mode 100644 index 00000000000..554ed3fe1c2 --- /dev/null +++ b/tools/UpdatePaths/Scripts/79015_simple_to_basic_construct_artificer.txt @@ -0,0 +1 @@ +/mob/living/simple_animal/hostile/construct/artificer/@SUBTYPES : /mob/living/basic/construct/artificer/@SUBTYPES{@OLD}