Skip to content

Commit

Permalink
Converting bot AI to use the mob controller system.
Browse files Browse the repository at this point in the history
  • Loading branch information
MistakeNot4892 committed Sep 24, 2024
1 parent bf45981 commit 5c4bfec
Show file tree
Hide file tree
Showing 25 changed files with 965 additions and 867 deletions.
12 changes: 9 additions & 3 deletions code/datums/ai/_ai.dm
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,10 @@
return stance

/datum/mob_controller/proc/list_targets(var/dist = 7)
return
return null

/datum/mob_controller/proc/open_fire()
return
/datum/mob_controller/proc/handle_ranged_target(atom/ranged_target)
return FALSE

/datum/mob_controller/proc/startle()
if(QDELETED(body) || body.stat != UNCONSCIOUS)
Expand Down Expand Up @@ -329,3 +329,9 @@

/datum/mob_controller/proc/is_friend(mob/friend)
. = istype(friend) && LAZYLEN(_friends) && (weakref(friend) in _friends)

/datum/mob_controller/proc/clear_friends()
LAZYCLEARLIST(_friends)

/datum/mob_controller/proc/clear_paths()
return
8 changes: 4 additions & 4 deletions code/datums/ai/aggressive.dm
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@
return
if(body.has_ranged_attack() && get_dist(body, target) <= body.get_ranged_attack_distance() && !move_only)
body.stop_automove()
open_fire()
handle_ranged_target(target)
return
set_stance(STANCE_ATTACKING)
body.start_automove(target)
Expand Down Expand Up @@ -257,10 +257,10 @@
return FALSE
return TRUE

/datum/mob_controller/aggressive/open_fire()
if(!body.can_act())
/datum/mob_controller/aggressive/handle_ranged_target(atom/ranged_target)
if(!body.can_act() || !ranged_target)
return FALSE
body.handle_ranged_attack(get_target())
body.handle_ranged_attack(ranged_target)
return TRUE

/datum/mob_controller/aggressive/lose_target()
Expand Down
62 changes: 62 additions & 0 deletions code/game/turfs/turf_navigation.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/******************************************************************/
// Navigation procs
// Used for A-star pathfinding

// Returns the surrounding cardinal turfs with open links
// Including through doors openable with the ID
/turf/proc/CardinalTurfsWithAccess(var/obj/item/card/id/ID)
var/L[] = new()

for(var/d in global.cardinal)
var/turf/T = get_step(src, d)
if(istype(T) && !T.density && T.simulated && !LinkBlockedWithAccess(src, T, ID))
L.Add(T)
return L


// Returns true if a link between A and B is blocked
// Movement through doors allowed if ID has access
/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/card/id/ID)

if(A == null || B == null) return 1
var/adir = get_dir(A,B)
var/rdir = get_dir(B,A)
if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal
var/iStep = get_step(A,adir&(NORTH|SOUTH))
if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID))
return 0

var/pStep = get_step(A,adir&(EAST|WEST))
if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID))
return 0
return 1

if(DirBlockedWithAccess(A,adir, ID))
return 1

if(DirBlockedWithAccess(B,rdir, ID))
return 1

for(var/obj/O in B)
if(O.density && !istype(O, /obj/machinery/door) && !(O.atom_flags & ATOM_FLAG_CHECKS_BORDER))
return 1

return 0

// Returns true if direction is blocked from loc
// Checks doors against access with given ID
/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/card/id/ID)
for(var/obj/structure/window/D in loc)
if(!D.density) continue
if(D.dir == SOUTHWEST) return 1
if(D.dir == dir) return 1

for(var/obj/machinery/door/D in loc)
if(!D.density) continue
if(istype(D, /obj/machinery/door/window))
if( dir & D.dir ) return !D.check_access(ID)

//if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID)
//if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID)
else return !D.check_access(ID) // it's a real, air blocking door
return 0
220 changes: 220 additions & 0 deletions code/modules/mob/living/bot/_bot.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
/mob/living/bot
name = "Bot"
max_health = 20
icon = 'icons/mob/bot/placeholder.dmi'
universal_speak = TRUE
density = FALSE
butchery_data = null
ai = /datum/mob_controller/bot
abstract_type = /mob/living/bot
layer = HIDING_MOB_LAYER

var/obj/item/card/id/botcard = null
var/on = 1
var/open = 0
var/locked = 1
var/emagged = 0
var/light_strength = 3
var/obj/access_scanner = null
var/list/req_access = list()
var/RequiresAccessToToggle = 0 // If 1, will check access to be turned on/off
var/wait_if_pulled = 0 // Only applies to moving to the target

/mob/living/bot/Initialize()
. = ..()
update_icon()

botcard = new /obj/item/card/id(src)
botcard.access = get_initial_bot_access()

access_scanner = new /obj(src)
access_scanner.req_access = req_access?.Copy()

if(on)
turn_on() // Update lights and other stuff
else
turn_off()

/mob/living/bot/proc/get_initial_bot_access()
return list()

/mob/living/bot/handle_regular_status_updates()
. = ..()
if(.)
set_status(STAT_WEAK, 0)
set_status(STAT_STUN, 0)
set_status(STAT_PARA, 0)

/mob/living/bot/get_life_damage_types()
var/static/list/life_damage_types = list(
BURN,
BRUTE
)
return life_damage_types

/mob/living/bot/get_dusted_remains()
return /obj/effect/decal/cleanable/blood/oil

/mob/living/bot/gib(do_gibs = TRUE)
if(stat != DEAD)
death(gibbed = TRUE)
if(stat == DEAD)
turn_off()
visible_message(SPAN_DANGER("\The [src] blows apart!"))
spark_at(src, cardinal_only = TRUE)
return ..()

/mob/living/bot/death(gibbed)
. = ..()
if(. && !gibbed)
gib()

/mob/living/bot/attackby(var/obj/item/O, var/mob/user)

if(O.GetIdCard())
if(access_scanner.allowed(user) && !open)
locked = !locked
to_chat(user, SPAN_NOTICE("Controls are now [locked ? "locked" : "unlocked"]."))
Interact(usr)
else if(open)
to_chat(user, SPAN_WARNING("Please close the access panel before locking it."))
else
to_chat(user, SPAN_WARNING("Access denied."))
return TRUE

if(IS_SCREWDRIVER(O))
if(!locked)
open = !open
to_chat(user, SPAN_NOTICE("Maintenance panel is now [open ? "opened" : "closed"]."))
Interact(usr)
else
to_chat(user, SPAN_WARNING("You need to unlock the controls first."))
return TRUE

if(IS_WELDER(O))
if(current_health < get_max_health())
if(open)
heal_overall_damage(10)
user.visible_message(
SPAN_NOTICE("\The [user] repairs \the [src]."),
SPAN_NOTICE("You repair \the [src].")
)
else
to_chat(user, SPAN_WARNING("Unable to repair with the maintenance panel closed."))
else
to_chat(user, SPAN_WARNING("\The [src] does not need a repair."))
return TRUE

return ..()

/mob/living/bot/attack_ai(var/mob/living/user)
Interact(user)

/mob/living/bot/default_interaction(mob/user)
. = ..()
if(!.)
Interact(user)
return TRUE

/mob/living/bot/proc/Interact(var/mob/user)
add_fingerprint(user)
var/dat

var/curText = GetInteractTitle()
if(curText)
dat += curText
dat += "<hr>"

curText = GetInteractStatus()
if(curText)
dat += curText
dat += "<hr>"

curText = (CanAccessPanel(user)) ? GetInteractPanel() : "The access panel is locked."
if(curText)
dat += curText
dat += "<hr>"

curText = (CanAccessMaintenance(user)) ? GetInteractMaintenance() : "The maintenance panel is locked."
if(curText)
dat += curText

var/datum/browser/popup = new(user, "botpanel", "[src] controls")
popup.set_content(dat)
popup.open()

/mob/living/bot/DefaultTopicState()
return global.default_topic_state

/mob/living/bot/OnTopic(mob/user, href_list)
if(href_list["command"])
ProcessCommand(user, href_list["command"], href_list)
Interact(user)

/mob/living/bot/proc/GetInteractTitle()
return

/mob/living/bot/proc/GetInteractStatus()
. = "Status: <A href='byond://?src=\ref[src];command=toggle'>[on ? "On" : "Off"]</A>"
. += "<BR>Behaviour controls are [locked ? "locked" : "unlocked"]"
. += "<BR>Maintenance panel is [open ? "opened" : "closed"]"

/mob/living/bot/proc/GetInteractPanel()
return

/mob/living/bot/proc/GetInteractMaintenance()
return

/mob/living/bot/proc/ProcessCommand(var/mob/user, var/command, var/href_list)
if(command == "toggle" && CanToggle(user))
if(on)
turn_off()
else
turn_on()
return

/mob/living/bot/proc/CanToggle(var/mob/user)
return (!RequiresAccessToToggle || access_scanner.allowed(user) || issilicon(user))

/mob/living/bot/proc/CanAccessPanel(var/mob/user)
return (!locked || issilicon(user))

/mob/living/bot/proc/CanAccessMaintenance(var/mob/user)
return (open || issilicon(user))

// Overrides default verb to 'beeps' instead of 'says'.
/mob/living/bot/say(message, decl/language/speaking, verb = "beeps", alt_name = "", whispering)
return ..()

/mob/living/bot/Bump(var/atom/A)
if(on && botcard && istype(A, /obj/machinery/door))
var/obj/machinery/door/D = A
if(!istype(D, /obj/machinery/door/firedoor) && !istype(D, /obj/machinery/door/blast) && D.check_access(botcard))
D.open()
else
..()

/mob/living/bot/emag_act(var/remaining_charges, var/mob/user)
return 0

/mob/living/bot/proc/turn_on()
if(stat)
return FALSE
on = TRUE
set_light(light_strength)
if(ai)
ai.clear_paths()
ai.lose_target()
ai.clear_friends()
update_icon()
return TRUE

/mob/living/bot/proc/turn_off()
on = FALSE
set_light(0)
update_icon()

/mob/living/bot/GetIdCards(list/exceptions)
. = ..()
if(istype(botcard) && !is_type_in_list(botcard, exceptions))
LAZYDISTINCTADD(., botcard)
Loading

0 comments on commit 5c4bfec

Please sign in to comment.