From 2dc77b5408f52837ac570ca67ce2ff5fd6b3dc7c Mon Sep 17 00:00:00 2001 From: John Doe Date: Mon, 9 Sep 2024 17:51:04 -0700 Subject: [PATCH 1/3] disables overwatch (might re-enable it in the future but not sure yet) and makes disarm the default intent --- .../mob/living/carbon/human/ai/brain/ai_brain_targeting.dm | 2 +- code/modules/mob/living/carbon/human/ai/human_ai.dm | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm index 430c8d9281..65a612434f 100644 --- a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm +++ b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm @@ -10,7 +10,7 @@ /// Ref to the last turf that the AI shot at var/turf/open/target_floor /// If TRUE, the AI is allowed to establish overwatches - var/overwatch_allowed = TRUE + var/overwatch_allowed = FALSE /// List of overwatched turfs var/list/turf/open/overwatch_turfs = list() diff --git a/code/modules/mob/living/carbon/human/ai/human_ai.dm b/code/modules/mob/living/carbon/human/ai/human_ai.dm index 3cb016241e..bd52bb78bb 100644 --- a/code/modules/mob/living/carbon/human/ai/human_ai.dm +++ b/code/modules/mob/living/carbon/human/ai/human_ai.dm @@ -20,6 +20,7 @@ GLOBAL_LIST_EMPTY(ai_humans) GLOB.ai_humans += src ai_brain = new(src) create_hud() + a_intent = INTENT_DISARM //INVOKE_ASYNC(src) /mob/living/carbon/human/Destroy(force) From 283dccc41c9dad59ee9e76577c395c7e730af6c0 Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 11 Sep 2024 21:45:26 -0700 Subject: [PATCH 2/3] faction datums + basic communication + panel fixes --- code/controllers/subsystem/human_ai.dm | 9 +- .../items/reagent_containers/food/sandwich.dm | 1 - .../carbon/human/ai/ai_management_menu.dm | 4 +- .../living/carbon/human/ai/brain/ai_brain.dm | 56 +++++++-- .../human/ai/brain/ai_brain_communication.dm | 54 ++++++++- .../human/ai/brain/ai_brain_factions.dm | 106 ++++++++++++++++-- .../carbon/human/ai/brain/ai_brain_items.dm | 7 ++ .../carbon/human/ai/brain/ai_brain_squad.dm | 30 ++++- .../human/ai/brain/ai_brain_targeting.dm | 2 +- .../tgui/interfaces/HumanAIManager.tsx | 19 +--- 10 files changed, 250 insertions(+), 38 deletions(-) diff --git a/code/controllers/subsystem/human_ai.dm b/code/controllers/subsystem/human_ai.dm index ae1f680bd2..9450f0fe19 100644 --- a/code/controllers/subsystem/human_ai.dm +++ b/code/controllers/subsystem/human_ai.dm @@ -2,7 +2,6 @@ SUBSYSTEM_DEF(human_ai) name = "Human AI" priority = SS_PRIORITY_HUMAN_AI - flags = SS_NO_INIT wait = 0.2 SECONDS /// A list of mobs scheduled to process var/list/mob/living/carbon/human/ai/current_run = list() @@ -21,6 +20,14 @@ SUBSYSTEM_DEF(human_ai) /// List of all existing orders var/list/datum/ongoing_action/existing_orders = list() + var/list/human_ai_factions = list() + +/datum/controller/subsystem/human_ai/Initialize() + for(var/faction_path in subtypesof(/datum/human_ai_faction)) + var/datum/human_ai_faction/faction_obj = new faction_path + human_ai_factions[faction_obj.faction] = faction_obj + return SS_INIT_SUCCESS + /datum/controller/subsystem/human_ai/stat_entry(msg) msg = "P:[length(GLOB.human_ai_brains)]" return ..() diff --git a/code/game/objects/items/reagent_containers/food/sandwich.dm b/code/game/objects/items/reagent_containers/food/sandwich.dm index 1b7d61eadd..c13b1b279c 100644 --- a/code/game/objects/items/reagent_containers/food/sandwich.dm +++ b/code/game/objects/items/reagent_containers/food/sandwich.dm @@ -52,7 +52,6 @@ overlays.Cut() for(var/obj/item/reagent_container/food/snacks/O in ingredients) - i++ if(i == 1) fullname += "[O.name]" diff --git a/code/modules/mob/living/carbon/human/ai/ai_management_menu.dm b/code/modules/mob/living/carbon/human/ai/ai_management_menu.dm index b11b4f070b..c6ed91af77 100644 --- a/code/modules/mob/living/carbon/human/ai/ai_management_menu.dm +++ b/code/modules/mob/living/carbon/human/ai/ai_management_menu.dm @@ -2,7 +2,7 @@ /datum/human_ai_management_menu/New() -/datum/human_ai_management_menu/proc/ui_interact(mob/user, datum/tgui/ui) +/datum/human_ai_management_menu/tgui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) if(!ui) ui = new(user, src, "HumanAIManager") @@ -122,7 +122,7 @@ return if(human_ai_menu) - human_ai_menu.ui_interact(mob) + human_ai_menu.tgui_interact(mob) return human_ai_menu = new /datum/human_ai_management_menu(src) diff --git a/code/modules/mob/living/carbon/human/ai/brain/ai_brain.dm b/code/modules/mob/living/carbon/human/ai/brain/ai_brain.dm index 59c1cf703e..2e791e7c53 100644 --- a/code/modules/mob/living/carbon/human/ai/brain/ai_brain.dm +++ b/code/modules/mob/living/carbon/human/ai/brain/ai_brain.dm @@ -34,6 +34,10 @@ GLOBAL_LIST_EMPTY(human_ai_brains) var/combat_decay_time = 30 SECONDS var/squad_covering = FALSE + var/list/friendly_factions = list() + var/list/neutral_factions = list() + var/previous_faction + /datum/human_ai_brain/New(mob/living/carbon/human/tied_human) . = ..() src.tied_human = tied_human @@ -43,6 +47,7 @@ GLOBAL_LIST_EMPTY(human_ai_brains) RegisterSignal(tied_human, COMSIG_MOB_PICKUP_ITEM, PROC_REF(on_item_pickup)) RegisterSignal(tied_human, COMSIG_MOB_DROP_ITEM, PROC_REF(on_item_drop)) RegisterSignal(tied_human, COMSIG_MOVABLE_MOVED, PROC_REF(on_move)) + RegisterSignal(tied_human, COMSIG_HUMAN_BULLET_ACT, PROC_REF(on_shot)) if(!length(all_medical_items)) all_medical_items = brute_heal_items + burn_heal_items + tox_heal_items + oxy_heal_items + bleed_heal_items + bonebreak_heal_items + painkiller_items GLOB.human_ai_brains += src @@ -85,6 +90,13 @@ GLOBAL_LIST_EMPTY(human_ai_brains) item_search(things_around) //bullet_detect(things_around) + if(!currently_busy && primary_weapon && current_target && !currently_firing && COOLDOWN_FINISHED(src, fire_overload_cooldown) && primary_weapon.has_ammunition()) + currently_busy = TRUE + if(get_dist(tied_human, current_target) > gun_data.maximum_range) + if(!has_ongoing_action(/datum/ongoing_action/approach_target) && !in_cover) + ADD_ONGOING_ACTION(src, /datum/ongoing_action/approach_target, current_target, gun_data.maximum_range) + attack_target() + if(!currently_busy && healing_start_check()) currently_busy = TRUE start_healing() @@ -97,13 +109,6 @@ GLOBAL_LIST_EMPTY(human_ai_brains) current_target = get_target(view_distance) RegisterSignal(current_target, COMSIG_PARENT_QDELETING, PROC_REF(on_target_delete)) - if(!currently_busy && primary_weapon && current_target && !currently_firing && COOLDOWN_FINISHED(src, fire_overload_cooldown) && primary_weapon.has_ammunition()) - currently_busy = TRUE - if(get_dist(tied_human, current_target) > gun_data.maximum_range) - if(!has_ongoing_action(/datum/ongoing_action/approach_target) && !in_cover) - ADD_ONGOING_ACTION(src, /datum/ongoing_action/approach_target, current_target, gun_data.maximum_range) - attack_target() - /datum/human_ai_brain/proc/on_human_delete(datum/source, force) SIGNAL_HANDLER tied_human = null @@ -215,8 +220,21 @@ GLOBAL_LIST_EMPTY(human_ai_brains) if(!in_cover && !faction_check(firer)) // If it's our own bullets, we don't need to be alarmed locate_cover(bullet, bullet.dir) +/// Returns TRUE if the target is friendly/neutral to us /datum/human_ai_brain/proc/faction_check(mob/target) - return target?.faction == tied_human.faction + if(!target) + return FALSE + + if(target.faction == tied_human.faction) + return TRUE + + if(target.faction in friendly_factions) + return TRUE + + if(target.faction in neutral_factions) + return TRUE + + return FALSE /datum/human_ai_brain/proc/setup_detection_radius() if(length(detection_turfs)) @@ -251,10 +269,14 @@ GLOBAL_LIST_EMPTY(human_ai_brains) /datum/human_ai_brain/proc/enter_combat() SIGNAL_HANDLER + if(!in_combat) + say_in_combat_line() in_combat = TRUE addtimer(CALLBACK(src, PROC_REF(exit_combat)), combat_decay_time, TIMER_UNIQUE | TIMER_OVERRIDE) /datum/human_ai_brain/proc/exit_combat() + if(in_combat) + say_exit_combat_line() in_combat = FALSE /datum/human_ai_brain/proc/can_process_order() @@ -265,3 +287,21 @@ GLOBAL_LIST_EMPTY(human_ai_brains) return FALSE return TRUE + +/datum/human_ai_brain/proc/on_shot(datum/source, damage_result, ammo_flags, obj/projectile/bullet) + SIGNAL_HANDLER + var/mob/firer = bullet.firer + if(firer?.faction in neutral_factions) + on_neutral_faction_betray(firer.faction) + + +/datum/human_ai_brain/proc/on_neutral_faction_betray(faction) + if(!tied_human.faction) + return + + var/datum/human_ai_faction/our_faction = SShuman_ai.human_ai_factions[tied_human.faction] + if(!our_faction) + return + + our_faction.neutral_factions -= faction + our_faction.reapply_faction_data() diff --git a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_communication.dm b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_communication.dm index b8d5fd7d9c..bbd1417f2b 100644 --- a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_communication.dm +++ b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_communication.dm @@ -1,2 +1,54 @@ /datum/human_ai_brain - var/list/in_combat_lines = list() + var/list/in_combat_lines = list( + "Taking fire!", + "Getting shot at!", + "Engaging hostiles!", + "Contact!", + "Contact contact!", + "We've got hostiles!", + "Take 'em down!", + "Hostile spotted, engaging!", + "Enemy hostiles here!", + "Being fired upon!", + "Blast 'em!" + ) + + var/list/exit_combat_lines = list( + "No more contacts.", + "Don't see 'em.", + "Going back to regular duties.", + "Nothin' left.", + "Can't find 'em.", + "No hostiles, returning to duties.", + ) + + var/list/squad_member_death_lines = list( + "Fuck! Man down!", + "We lost one!", + "Man down!", + "We're taking losses here!", + "Goddamn it.", + "Fuck!", + "Shit, our squad's down a man!", + "Squad integrity's failing!" + ) + + var/in_combat_line_chance = 40 + var/exit_combat_line_chance = 40 + var/squad_member_death_line_chance = 20 + + +/datum/human_ai_brain/proc/say_in_combat_line(chance = in_combat_line_chance) + if(!prob(chance)) + return + tied_human.say(pick(in_combat_lines)) + +/datum/human_ai_brain/proc/say_exit_combat_line(chance = exit_combat_line_chance) + if(!prob(chance)) + return + tied_human.say(pick(exit_combat_lines)) + +/datum/human_ai_brain/proc/on_squad_member_death(mob/living/carbon/human/dead_member) + if(!prob(squad_member_death_line_chance)) + return + tied_human.say(pick(squad_member_death_lines)) diff --git a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_factions.dm b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_factions.dm index f31827412a..78244efcc9 100644 --- a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_factions.dm +++ b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_factions.dm @@ -1,14 +1,102 @@ -GLOBAL_LIST_INIT(human_ai_factions, assemble_human_ai_factions()) - -/proc/assemble_human_ai_factions() - . = list() - for(var/datum/human_ai_faction/faction as anything in subtypesof(/datum/human_ai_faction)) - faction = new - .[faction.faction] = faction - /datum/human_ai_faction var/faction = FACTION_NEUTRAL var/shoot_to_kill = TRUE + var/list/in_combat_lines = list() + var/list/exit_combat_lines = list() + var/list/squad_member_death_lines = list() + + var/list/friendly_factions = list() + var/list/neutral_factions = list() + /datum/human_ai_faction/proc/apply_faction_data(datum/human_ai_brain/brain) - return + if(length(in_combat_lines)) + brain.in_combat_lines = in_combat_lines + + if(length(exit_combat_lines)) + brain.exit_combat_lines = exit_combat_lines + + if(length(squad_member_death_lines)) + brain.squad_member_death_lines = squad_member_death_lines + + brain.shoot_to_kill = shoot_to_kill + brain.friendly_factions = friendly_factions + brain.neutral_factions = neutral_factions + +/datum/human_ai_faction/proc/reapply_faction_data() + for(var/datum/human_ai_brain/brain in GLOB.human_ai_brains) + if(brain.tied_human?.faction == faction) + apply_faction_data(brain) + +/datum/human_ai_faction/clf + faction = FACTION_CLF + friendly_factions = list( + FACTION_COLONIST, + ) + + +/datum/human_ai_faction/uscm + faction = FACTION_USCM + friendly_factions = list( + FACTION_COLONIST, + FACTION_TWE, + FACTION_WY, + ) + neutral_factions = list( + FACTION_FREELANCER, + FACTION_CONTRACTOR, + FACTION_UPP, + FACTION_MERCENARY, + ) + +/datum/human_ai_faction/upp + faction = FACTION_UPP + friendly_factions = list( + FACTION_COLONIST, + ) + neutral_factions = list( + FACTION_FREELANCER, + FACTION_CONTRACTOR, + FACTION_USCM, + FACTION_MERCENARY, + FACTION_TWE, + ) + +/datum/human_ai_faction/wy + faction = FACTION_WY + friendly_factions = list( + FACTION_COLONIST, + FACTION_TWE, + FACTION_MARINE, + ) + neutral_factions = list( + FACTION_FREELANCER, + FACTION_CONTRACTOR, + FACTION_USCM, + FACTION_MERCENARY, + ) + +/datum/human_ai_faction/wy_deathsquad + faction = FACTION_WY_DEATHSQUAD + friendly_factions = list( + FACTION_WY, + ) + in_combat_lines = list( + "Visual confirmed, engaging.", + "Engaging hostile.", + "Eliminating hostile.", + "Engaging.", + "Contact.", + "Viscon, proceeding." + ) + exit_combat_lines = list( + "Hostilities ceased.", + "Ceasing engagement." + ) + squad_member_death_lines = list( + "Allied unit disabled.", + "Friendly unit decomissioned.", + "Allied unit decomissioned.", + "Friendly unit disabled." + ) + diff --git a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_items.dm b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_items.dm index e7d0553282..2cddf0a4d7 100644 --- a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_items.dm +++ b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_items.dm @@ -107,6 +107,13 @@ /// Currently doesn't support recursive storage /datum/human_ai_brain/proc/appraise_inventory(belt = TRUE, back = TRUE, pocket_l = TRUE, pocket_r = TRUE) + if(previous_faction != tied_human.faction) + previous_faction = tied_human.faction + var/datum/human_ai_faction/our_faction = SShuman_ai.human_ai_factions[tied_human.faction] + if(!our_faction) + return + our_faction.apply_faction_data(src) + tried_reload = FALSE // We don't really need to do this in a smart way if(belt) if(!istype(tied_human.belt, /obj/item/storage/belt)) diff --git a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_squad.dm b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_squad.dm index 93fbb98f36..f026fdffce 100644 --- a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_squad.dm +++ b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_squad.dm @@ -25,14 +25,18 @@ ai_in_squad += adding adding.set_ongoing_order(assigned_order) + RegisterSignal(adding.tied_human, COMSIG_MOB_DEATH, PROC_REF(on_squad_member_death)) + RegisterSignal(adding, COMSIG_PARENT_QDELETING, PROC_REF(on_squad_member_delete)) /datum/human_ai_squad/proc/remove_from_squad(datum/human_ai_brain/removing) if(removing == squad_leader) - squad_leader = null + set_squad_leader(null) removing.ongoing_order = null removing.squad_id = null removing.is_squad_leader = FALSE ai_in_squad -= removing + UnregisterSignal(removing?.tied_human, COMSIG_MOB_DEATH) + UnregisterSignal(removing, COMSIG_PARENT_QDELETING) /datum/human_ai_squad/proc/set_order(datum/ongoing_action/order) assigned_order = order @@ -43,7 +47,29 @@ if(squad_leader) squad_leader.is_squad_leader = FALSE squad_leader = new_leader - new_leader.is_squad_leader = TRUE + if(squad_leader) + new_leader.is_squad_leader = TRUE + +/datum/human_ai_squad/proc/on_squad_member_death(mob/living/carbon/human/dead_mob) + SIGNAL_HANDLER + + if(istype(dead_mob, /mob/living/carbon/human/ai)) + var/mob/living/carbon/human/ai/dead_squddie = dead_mob + if(squad_leader == dead_squddie.ai_brain) + set_squad_leader(null) + + for(var/datum/human_ai_brain/squaddie as anything in ai_in_squad) + if(squaddie.tied_human.is_mob_incapacitated()) + continue + + squaddie.on_squad_member_death(dead_mob) + +/datum/human_ai_squad/proc/on_squad_member_delete(datum/human_ai_brain/deleting) + SIGNAL_HANDLER + + remove_from_squad(deleting) + + /datum/human_ai_brain /// Numeric ID of the squad this AI is in, if any diff --git a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm index 65a612434f..4b09ccb904 100644 --- a/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm +++ b/code/modules/mob/living/carbon/human/ai/brain/ai_brain_targeting.dm @@ -119,7 +119,7 @@ if(!shoot_to_kill && target.stat == UNCONSCIOUS) return FALSE - if(target.faction == tied_human.faction) + if(faction_check(target)) return FALSE if(HAS_TRAIT(target, TRAIT_CLOAKED) && get_dist(tied_human, target) > cloak_visible_range) diff --git a/tgui/packages/tgui/interfaces/HumanAIManager.tsx b/tgui/packages/tgui/interfaces/HumanAIManager.tsx index 0ca920697e..170164fe7e 100644 --- a/tgui/packages/tgui/interfaces/HumanAIManager.tsx +++ b/tgui/packages/tgui/interfaces/HumanAIManager.tsx @@ -49,13 +49,13 @@ type BackendContext = { }; const AIContext = (props, context) => { - const { data, act } = useBackend(context); + const { data, act } = useBackend(); const [squadAssignmentMode, setSquadAssignmentMode] = useLocalState( - context, + 'squad_assignment_mode', false ); const [orderAssignmentMode, setOrderAssignmentMode] = useLocalState( - context, + 'order_assignment_mode', false ); return ( @@ -137,14 +137,12 @@ const AIContext = (props, context) => { const CreatedOrder = (props) => { const order: Order = props.order; const context: BackendContext = props.context; - const { data, act } = useBackend(context); + const { data, act } = useBackend(); const [orderAssignmentMode, setOrderAssignmentMode] = useLocalState( - context, 'order_assignment_mode', false ); const [selectedSquad, setSelectedSquad] = useLocalState( - context, 'selected_squad', -1 ); @@ -202,16 +200,14 @@ const HumanAIReadout = (props) => { const human: AIHuman = props.human; const context: BackendContext = props.context; const [squadAssignmentMode, setSquadAssignmentMode] = useLocalState( - context, 'squad_assignment_mode', false ); const [selectedSquad, setSelectedSquad] = useLocalState( - context, 'selected_squad', -1 ); - const { data, act } = useBackend(context); + const { data, act } = useBackend(); const gottenSquad: Squad = data.squads[selectedSquad]; return (
{ const SquadReadout = (props) => { const squad: Squad = props.squad; const context: BackendContext = props.context; - const { data, act } = useBackend(context); + const { data, act } = useBackend(); const [squadAssignmentMode, setSquadAssignmentMode] = useLocalState( - context, 'squad_assignment_mode', false ); const [selectedSquad, setSelectedSquad] = useLocalState( - context, 'selected_squad', -1 ); const [orderAssignmentMode, setOrderAssignmentMode] = useLocalState( - context, 'order_assignment_mode', false ); From b8b68294a9c915110a223aa6566ff3229b5a1d22 Mon Sep 17 00:00:00 2001 From: John Doe Date: Wed, 11 Sep 2024 21:45:43 -0700 Subject: [PATCH 3/3] huh? --- code/game/objects/items/reagent_containers/food/sandwich.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/game/objects/items/reagent_containers/food/sandwich.dm b/code/game/objects/items/reagent_containers/food/sandwich.dm index c13b1b279c..1b7d61eadd 100644 --- a/code/game/objects/items/reagent_containers/food/sandwich.dm +++ b/code/game/objects/items/reagent_containers/food/sandwich.dm @@ -52,6 +52,7 @@ overlays.Cut() for(var/obj/item/reagent_container/food/snacks/O in ingredients) + i++ if(i == 1) fullname += "[O.name]"