Skip to content

Commit

Permalink
[MIRROR] basic honkbots [MDB IGNORE] (#2851)
Browse files Browse the repository at this point in the history
* basic honkbots (#81920)

## About The Pull Request
this refactors honkbots into basic mobs. its mostly a faithful 1:1
refactor but i couldnt keep my hands to myselves so i gave them some new
behaviors.

honkbots now love playing with clowns, they will go seek out for clowns
and celebrate around them. also, if the honkbot finds a banana peel or a
slippery item near it, it will actively drag people onto them

honkbots will now go out of theirway to mess with secbots and annoy them

## Why It's Good For The Game
refactors hinkbots into basic bots and also undoes some of the silliness
i did in the previous basic bot prs. i also added lazylist support to
remove_thing_from_list.

## Changelog
:cl:
refactor: honkbots are now basic mobs, please report any bugs
add: honkbots will try to slip people on banana peels
/:cl:

* basic honkbots

---------

Co-authored-by: Ben10Omintrix <[email protected]>
Co-authored-by: NovaBot13 <[email protected]>
  • Loading branch information
3 people authored and StealsThePRs committed Jun 6, 2024
1 parent 9ddb0e1 commit fa49874
Show file tree
Hide file tree
Showing 27 changed files with 618 additions and 191 deletions.
2 changes: 1 addition & 1 deletion _maps/shuttles/emergency_casino.dmm
Original file line number Diff line number Diff line change
Expand Up @@ -1186,7 +1186,7 @@
/obj/item/storage/crayons,
/obj/item/storage/crayons,
/obj/item/storage/crayons,
/mob/living/simple_animal/bot/secbot/honkbot,
/mob/living/basic/bot/honkbot,
/turf/open/floor/sepia,
/area/shuttle/escape)
"NN" = (
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/ai/ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

///For JPS pathing, the maximum length of a path we'll try to generate. Should be modularized depending on what we're doing later on
#define AI_MAX_PATH_LENGTH 30 // 30 is possibly overkill since by default we lose interest after 14 tiles of distance, but this gives wiggle room for weaving around obstacles
#define AI_BOT_PATH_LENGTH 150
#define AI_BOT_PATH_LENGTH 75

// How far should we, by default, be looking for interesting things to de-idle?
#define AI_DEFAULT_INTERESTING_DIST 10
Expand Down
36 changes: 36 additions & 0 deletions code/__DEFINES/ai/bot_keys.dm
Original file line number Diff line number Diff line change
@@ -1,3 +1,22 @@
//bitfield defines

///can honkbots slip people?
#define HONKBOT_MODE_SLIP (1<<0)
///can honkbots check IDs?
#define HONKBOT_CHECK_IDS (1<<1)
///can honkbots check records?
#define HONKBOT_CHECK_RECORDS (1<<2)
///can honkbots handcuff people?
#define HONKBOT_HANDCUFF_TARGET (1<<3)

DEFINE_BITFIELD(honkbot_flags, list(
"CAN_SLIP" = HONKBOT_MODE_SLIP,
"CHECK_IDS" = HONKBOT_CHECK_IDS,
"CHECK_RECORDS" = HONKBOT_CHECK_RECORDS,
"CAN_FAKE_CUFF" = HONKBOT_HANDCUFF_TARGET,
))


// bot keys
///The first beacon we find
#define BB_BEACON_TARGET "beacon_target"
Expand Down Expand Up @@ -73,3 +92,20 @@
#define BB_WASH_FRUSTRATION "wash_frustration"
///key that holds cooldown after we finish cleaning something, so we dont immediately run off to patrol
#define BB_POST_CLEAN_COOLDOWN "post_clean_cooldown"

//Honkbots
///key that holds all possible clown friends
#define BB_CLOWNS_LIST "clowns_list"
///key that holds the clown we play with
#define BB_CLOWN_FRIEND "clown_friend"
///key that holds the list of slippery items
#define BB_SLIPPERY_ITEMS "slippery_items"
///key that holds list of types we will attempt to slip
#define BB_SLIP_LIST "slip_list"
///key that holds the slippery item we will drag people too
#define BB_SLIPPERY_TARGET "slippery_target"
///key that holds the victim we will slip
#define BB_SLIP_TARGET "slip_target"
///key that holds our honk ability
#define BB_HONK_ABILITY "honk_ability"

3 changes: 3 additions & 0 deletions code/__DEFINES/dcs/signals/signals_mob/signals_mob_ai.dm
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
/// Signal sent when a blackboard key is set to a new value
#define COMSIG_AI_BLACKBOARD_KEY_SET(blackboard_key) "ai_blackboard_key_set_[blackboard_key]"

///Signal sent before a blackboard key is cleared
#define COMSIG_AI_BLACKBOARD_KEY_PRECLEAR(blackboard_key) "ai_blackboard_key_pre_clear_[blackboard_key]"

/// Signal sent when a blackboard key is cleared
#define COMSIG_AI_BLACKBOARD_KEY_CLEARED(blackboard_key) "ai_blackboard_key_clear_[blackboard_key]"

Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/traits/declarations.dm
Original file line number Diff line number Diff line change
Expand Up @@ -1144,6 +1144,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Trait applied to objects and mobs that can attack a boulder and break it down. (See /obj/item/boulder/manual_process())
#define TRAIT_BOULDER_BREAKER "boulder_breaker"

/// Trait given to mobs wearing the clown mask
#define TRAIT_PERCEIVED_AS_CLOWN "perceived_as_clown"
/// Does this item bypass ranged armor checks?
#define TRAIT_BYPASS_RANGED_ARMOR "bypass_ranged_armor"

Expand Down
1 change: 1 addition & 0 deletions code/_globalvars/traits/_traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_PARROT_PERCHED" = TRAIT_PARROT_PERCHED,
"TRAIT_PASSTABLE" = TRAIT_PASSTABLE,
"TRAIT_PASSWINDOW" = TRAIT_PASSWINDOW,
"TRAIT_PERCEIVED_AS_CLOWN" = TRAIT_PERCEIVED_AS_CLOWN,
"TRAIT_PERFECT_ATTACKER" = TRAIT_PERFECT_ATTACKER,
"TRAIT_PERMANENTLY_MORTAL" = TRAIT_PERMANENTLY_MORTAL,
"TRAIT_PHOTOGRAPHER" = TRAIT_PHOTOGRAPHER,
Expand Down
15 changes: 15 additions & 0 deletions code/datums/ai/_ai_controller.dm
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,8 @@ multiple modular subtrees with behaviors
/datum/ai_controller/proc/clear_blackboard_key(key)
if(isnull(blackboard[key]))
return
if(pawn && (SEND_SIGNAL(pawn, COMSIG_AI_BLACKBOARD_KEY_PRECLEAR(key))))
return
CLEAR_AI_DATUM_TARGET(blackboard[key], key)
blackboard[key] = null
if(isnull(pawn))
Expand Down Expand Up @@ -755,6 +757,19 @@ multiple modular subtrees with behaviors
CRASH("remove_thing_from_blackboard_key called with an invalid \"thing\" argument ([thing]). \
(The passed value is not tracked in the passed list.)")

///removes a tracked object from a lazylist
/datum/ai_controller/proc/remove_from_blackboard_lazylist_key(key, thing)
var/lazylist = blackboard[key]
if(isnull(lazylist))
return
for(var/key_index in lazylist)
if(thing == key_index || lazylist[key_index] == thing)
CLEAR_AI_DATUM_TARGET(thing, key)
lazylist -= key_index
break
if(!LAZYLEN(lazylist))
clear_blackboard_key(key)

/// Signal proc to go through every key and remove the datum from all keys it finds
/datum/ai_controller/proc/sig_remove_from_blackboard(datum/source)
SIGNAL_HANDLER
Expand Down
3 changes: 2 additions & 1 deletion code/datums/ai/movement/ai_movement_jps.dm
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
source.minimum_distance = controller.get_minimum_distance()

/datum/ai_movement/jps/bot
max_pathing_attempts = 25
max_pathing_attempts = 8
maximum_length = 25
diagonal_flags = DIAGONAL_REMOVE_ALL

Expand All @@ -51,6 +51,7 @@

/datum/ai_movement/jps/bot/travel_to_beacon
maximum_length = AI_BOT_PATH_LENGTH
max_pathing_attempts = 20

/datum/ai_movement/jps/modsuit
maximum_length = MOD_AI_RANGE
2 changes: 1 addition & 1 deletion code/datums/components/crafting/robot.dm
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@

/datum/crafting_recipe/honkbot
name = "Honkbot"
result = /mob/living/simple_animal/bot/secbot/honkbot
result = /mob/living/basic/bot/honkbot
reqs = list(
/obj/item/storage/box/clown = 1,
/obj/item/bodypart/arm/right/robot = 1,
Expand Down
101 changes: 101 additions & 0 deletions code/datums/components/cuff_n_stun.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* A component to stun and cuff targets
*/
/datum/component/stun_n_cuff
/// mobs we cannot stun nor cuff
var/list/blacklist_mobs
///sound to play when stunning
var/stun_sound
///time to stun the target for
var/stun_timer
///time it takes for us to handcuff the target
var/handcuff_timer
///callback after we have stunned someone
var/datum/callback/post_stun_callback
///callback after we have arrested someone
var/datum/callback/post_arrest_callback
///time until we can stun again
var/stun_cooldown_timer
///type of cuffs we use
var/handcuff_type
///cooldown until we can stun again
COOLDOWN_DECLARE(stun_cooldown)

/datum/component/stun_n_cuff/Initialize(list/blacklist_mobs = list(),
stun_sound = 'sound/weapons/egloves.ogg',
stun_timer = 8 SECONDS,
handcuff_timer = 4 SECONDS,
stun_cooldown_timer = 10 SECONDS,
handcuff_type = /obj/item/restraints/handcuffs/cable/zipties/used,
post_stun_callback,
post_arrest_callback,
)
if(!isliving(parent))
return COMPONENT_INCOMPATIBLE

src.blacklist_mobs = blacklist_mobs
src.stun_sound = stun_sound
src.stun_timer = stun_timer
src.handcuff_timer = handcuff_timer
src.handcuff_type = handcuff_type
src.stun_cooldown_timer = stun_cooldown_timer
src.post_stun_callback = post_stun_callback
src.post_arrest_callback = post_arrest_callback


/datum/component/stun_n_cuff/RegisterWithParent()
RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(on_unarmed_attack))

/datum/component/stun_n_cuff/UnregisterFromParent()
UnregisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET)
REMOVE_TRAIT(parent, TRAIT_MOB_BREEDER, REF(src))
post_stun_callback = null
post_arrest_callback = null

/datum/component/stun_n_cuff/proc/on_unarmed_attack(mob/living/source, atom/target)
SIGNAL_HANDLER

if(target == source || !iscarbon(target))
return NONE

if(is_type_in_typecache(target, blacklist_mobs))
return NONE

var/mob/living/carbon/living_target = target
if(living_target.IsParalyzed())
INVOKE_ASYNC(src, PROC_REF(cuff_target), target)
else
stun_target(target)

return COMPONENT_HOSTILE_NO_ATTACK

/datum/component/stun_n_cuff/proc/cuff_target(mob/living/carbon/human_target)
if(human_target.handcuffed)
var/mob/living/living_parent = parent
living_parent.balloon_alert(human_target, "already cuffed!")
return

playsound(parent, 'sound/weapons/cablecuff.ogg', 30, TRUE)
human_target.visible_message(span_danger("[parent] is trying to put zipties on [human_target]!"),\
span_danger("[parent] is trying to put zipties on you!"))

if(!do_after(parent, handcuff_timer, human_target))
return
human_target.set_handcuffed(new handcuff_type(human_target))
human_target.update_handcuffed()
post_arrest_callback?.Invoke(human_target)

/datum/component/stun_n_cuff/proc/stun_target(mob/living/carbon/human_target)
if(!COOLDOWN_FINISHED(src, stun_cooldown))
return
playsound(parent, stun_sound, 50, TRUE)
human_target.Paralyze(stun_timer)
human_target.set_stutter(40 SECONDS)
log_combat(parent, human_target, "honked")

human_target.visible_message(
span_danger("[parent] stuns [human_target]!"), \
span_userdanger("[parent] stuns you!"), \
)
COOLDOWN_START(src, stun_cooldown, stun_cooldown_timer)
post_stun_callback?.Invoke(human_target)
1 change: 1 addition & 0 deletions code/modules/clothing/masks/gasmask.dm
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ GLOBAL_LIST_INIT(clown_mask_options, list(
dye_color = DYE_CLOWN
w_class = WEIGHT_CLASS_SMALL
flags_cover = MASKCOVERSEYES
clothing_traits = list(TRAIT_PERCEIVED_AS_CLOWN)
resistance_flags = FLAMMABLE
actions_types = list(/datum/action/item_action/adjust)
dog_fashion = /datum/dog_fashion/head/clown
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mining/abandoned_crates.dm
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@
if(6 to 10)
new /obj/item/melee/skateboard/pro(src)
if(11 to 15)
new /mob/living/simple_animal/bot/secbot/honkbot(src)
new /mob/living/basic/bot/honkbot(src)
if(16 to 20)
new /obj/item/stack/ore/diamond(src, 10)
if(21 to 25)
Expand Down
16 changes: 12 additions & 4 deletions code/modules/mob/living/basic/bots/_bots.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,32 +14,40 @@ GLOBAL_LIST_INIT(command_strings, list(
gender = NEUTER
mob_biotypes = MOB_ROBOTIC
basic_mob_flags = DEL_ON_DEATH
density = FALSE

icon = 'icons/mob/silicon/aibots.dmi'
icon_state = "medibot0"
base_icon_state = "medibot"

damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, STAMINA = 0, OXY = 0)
habitable_atmos = null
hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_BATT_HUD, DIAG_PATH_HUD = HUD_LIST_LIST)

maximum_survivable_temperature = INFINITY
minimum_survivable_temperature = 0
has_unlimited_silicon_privilege = TRUE

sentience_type = SENTIENCE_ARTIFICIAL
status_flags = NONE //no default canpush
faction = list(FACTION_MINING)
ai_controller = /datum/ai_controller/basic_controller/bot
pass_flags = PASSFLAPS
pass_flags = PASSFLAPS | PASSMOB

verb_say = "states"
verb_ask = "queries"
verb_exclaim = "declares"
verb_yell = "alarms"

initial_language_holder = /datum/language_holder/synthetic
bubble_icon = "machine"

speech_span = SPAN_ROBOT
faction = list(FACTION_NEUTRAL, FACTION_SILICON, FACTION_TURRET)
faction = list(FACTION_SILICON)
light_system = OVERLAY_LIGHT
light_range = 3
light_power = 0.6
speed = 3

req_one_access = list(ACCESS_ROBOTICS)
interaction_flags_click = ALLOW_SILICON_REACH
///The Robot arm attached to this robot - has a 50% chance to drop on death.
Expand Down Expand Up @@ -101,7 +109,7 @@ GLOBAL_LIST_INIT(command_strings, list(
/mob/living/basic/bot/Initialize(mapload)
. = ..()

AddElement(/datum/element/relay_attackers)
AddElement(/datum/element/ai_retaliate)
RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(handle_loop_movement))
RegisterSignal(src, COMSIG_ATOM_WAS_ATTACKED, PROC_REF(after_attacked))
RegisterSignal(src, COMSIG_MOB_TRIED_ACCESS, PROC_REF(attempt_access))
Expand Down
52 changes: 46 additions & 6 deletions code/modules/mob/living/basic/bots/bot_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,24 @@
BB_PREVIOUS_BEACON_TARGET,
BB_BOT_SUMMON_TARGET,
)
///how many times we tried to reach the target
var/current_pathing_attempts = 0
///if we cant reach it after this many attempts, add it to our ignore list
var/max_pathing_attempts = 25
can_idle = FALSE // we want these to be running always
can_idle = FALSE

/datum/targeting_strategy/basic/bot/can_attack(mob/living/living_mob, atom/the_target, vision_range)
var/datum/ai_controller/my_controller = living_mob.ai_controller
if(isnull(my_controller))
return FALSE
if(!ishuman(the_target) || LAZYACCESS(my_controller.blackboard[BB_TEMPORARY_IGNORE_LIST], the_target))
return FALSE
var/mob/living/living_target = the_target
if(isnull(living_target.mind))
return FALSE
if(get_turf(living_mob) == get_turf(living_target))
return ..()
var/list/path = get_path_to(living_mob, living_target, max_distance = 10, access = my_controller.get_access())
if(!length(path) || QDELETED(living_mob))
my_controller?.set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, living_target, TRUE)
return FALSE
return ..()

/datum/ai_controller/basic_controller/bot/TryPossessPawn(atom/new_pawn)
. = ..()
Expand Down Expand Up @@ -66,7 +79,7 @@
set_blackboard_key(key, target)
return TRUE
if(!bypass_add_to_blacklist)
set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, REF(target), TRUE)
set_blackboard_key_assoc_lazylist(BB_TEMPORARY_IGNORE_LIST, target, TRUE)
return FALSE

/datum/ai_controller/basic_controller/bot/proc/can_reach_target(target, distance = 10)
Expand Down Expand Up @@ -236,3 +249,30 @@
/datum/ai_behavior/salute_authority/finish_action(datum/ai_controller/controller, succeeded, target_key)
. = ..()
controller.clear_blackboard_key(target_key)

/datum/ai_behavior/bot_search
action_cooldown = 2 SECONDS
behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION

/datum/ai_behavior/bot_search/perform(seconds_per_tick, datum/ai_controller/basic_controller/bot/controller, target_key, looking_for, radius = 5, pathing_distance = 10, bypass_add_blacklist = FALSE)
if(!istype(controller))
stack_trace("attempted to give [controller.pawn] the bot search behavior!")
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED

var/mob/living/living_pawn = controller.pawn
var/list/ignore_list = controller.blackboard[BB_TEMPORARY_IGNORE_LIST]
for(var/atom/potential_target as anything in oview(radius, controller.pawn))
if(QDELETED(living_pawn))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
if(!is_type_in_typecache(potential_target, looking_for))
continue
if(LAZYACCESS(ignore_list, potential_target))
continue
if(!valid_target(controller, potential_target))
continue
if(controller.set_if_can_reach(target_key, potential_target, distance = pathing_distance, bypass_add_to_blacklist = bypass_add_blacklist))
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED

/datum/ai_behavior/bot_search/proc/valid_target(datum/ai_controller/basic_controller/bot/controller, atom/my_target)
return TRUE
3 changes: 0 additions & 3 deletions code/modules/mob/living/basic/bots/cleanbot/cleanbot.dm
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
desc = "A little cleaning robot, he looks so excited!"
icon = 'icons/mob/silicon/aibots.dmi'
icon_state = "cleanbot0"
pass_flags = PASSMOB | PASSFLAPS
density = FALSE
anchored = FALSE
health = 25
maxHealth = 25
light_color = "#99ccff"
Expand Down
Loading

0 comments on commit fa49874

Please sign in to comment.