pAI Bot Piloting (#8840)

pAI can now pilot farmbots, cleanbots, floorbots and medbots.
    Floating messages now appear even if you're inside an object or another mob.
This commit is contained in:
Geeves
2020-05-30 16:24:29 +03:00
committed by GitHub
parent b50685d08f
commit bdeab68218
14 changed files with 329 additions and 238 deletions
+3 -3
View File
@@ -43,8 +43,8 @@ var/list/floating_chat_colors = list()
else
C.images += gibberish
/proc/generate_floating_text(atom/movable/holder, message, style, size, duration, show_to)
var/image/I = image(null, holder)
/proc/generate_floating_text(atom/movable/holder, message, style, size, duration, show_to)
var/image/I = image(null, recursive_loc_turf_check(holder))
I.layer = FLY_LAYER
I.alpha = 0
I.maptext_width = 80
@@ -68,4 +68,4 @@ var/list/floating_chat_colors = list()
/proc/remove_floating_text(atom/movable/holder, image/I)
animate(I, 2, pixel_y = I.pixel_y + 10, alpha = 0)
LAZYREMOVE(holder.stored_chat_text, I)
LAZYREMOVE(holder.stored_chat_text, I)
+88 -34
View File
@@ -4,21 +4,27 @@
maxHealth = 20
icon = 'icons/obj/aibots.dmi'
layer = MOB_LAYER
universal_speak = 1
density = 0
var/obj/item/card/id/botcard = null
universal_speak = TRUE
density = FALSE
var/obj/item/card/id/botcard
var/list/botcard_access = list()
var/on = 1
var/open = 0
var/locked = 1
var/emagged = 0
var/on = TRUE
var/open = FALSE
var/locked = TRUE
var/emagged = FALSE
var/light_strength = 3
var/obj/access_scanner = null
var/obj/access_scanner
var/list/req_access = list()
var/list/req_one_access = list()
var/master_access = access_robotics
var/last_emote = 0 // timer for emotes
var/can_take_pai = TRUE
var/obj/item/device/paicard/pAI
var/old_name
/mob/living/bot/Initialize()
. = ..()
update_icons()
@@ -31,10 +37,22 @@
access_scanner.req_one_access = req_one_access.Copy()
/mob/living/bot/Destroy()
if(pAI)
if(isturf(loc))
drop_from_inventory(pAI, get_turf(src))
pAI.throw_at_random(FALSE, 3, 1)
else
drop_from_inventory(pAI, loc)
pAI = null
QDEL_NULL(botcard)
QDEL_NULL(access_scanner)
return ..()
/mob/living/bot/examine(mob/user, distance, infix, suffix)
. = ..()
if(pAI)
to_chat(user, FONT_SMALL(SPAN_NOTICE("It has a pAI piloting it.")))
/mob/living/bot/Life()
..()
if(health <= 0)
@@ -44,6 +62,9 @@
stunned = 0
paralysis = 0
/mob/living/bot/movement_delay()
return 3
/mob/living/bot/updatehealth()
if(status_flags & GODMODE)
health = maxHealth
@@ -56,54 +77,82 @@
/mob/living/bot/proc/has_master_access(var/obj/item/I)
var/list/L = I.GetAccess()
if (master_access in L)
return 1
if(master_access in L)
return TRUE
else
return 0
return FALSE
/mob/living/bot/proc/has_ui_access(mob/user)
if (access_scanner.allowed(user))
return 1
if (!locked)
return 1
if (isAI(user))
return 1
return 0
if(access_scanner.allowed(user))
return TRUE
if(!locked)
return TRUE
if(isAI(user))
return TRUE
return FALSE
/mob/living/bot/attackby(var/obj/item/O, var/mob/user)
if(O.GetID())
if((has_master_access(O) || access_scanner.allowed(user)) && !open && !emagged)
locked = !locked
to_chat(user, "<span class='notice'>Controls are now [locked ? "locked." : "unlocked."]</span>")
to_chat(user, SPAN_NOTICE("You [locked ? "lock" : "unlock"] the controls."))
else
if(emagged)
to_chat(user, "<span class='warning'>ERROR</span>")
to_chat(user, SPAN_WARNING("As you swipe your ID, it reads: \"Interface error!\""))
if(open)
to_chat(user, "<span class='warning'>Please close the access panel before locking it.</span>")
to_chat(user, SPAN_WARNING("You have to close the access panel before locking it."))
else
to_chat(user, "<span class='warning'>Access denied.</span>")
to_chat(user, SPAN_WARNING("As you swipe your ID, it reads: \"Access denied.\""))
return
else if(O.isscrewdriver())
if(!locked)
open = !open
to_chat(user, "<span class='notice'>Maintenance panel is now [open ? "opened" : "closed"].</span>")
to_chat(user, SPAN_NOTICE("You [open ? "open" : "close"] the maintenance panel."))
else
to_chat(user, "<span class='notice'>You need to unlock the controls first.</span>")
to_chat(user, SPAN_WARNING("You need to unlock the controls first."))
return
else if(O.iswelder())
if(health < maxHealth)
if(open)
health = min(maxHealth, health + 10)
user.visible_message("<span class='notice'>[user] repairs [src].</span>","<span class='notice'>You repair [src].</span>")
user.visible_message(SPAN_NOTICE("\The [user] repairs [src]."), SPAN_NOTICE("You repair [src]."))
else
to_chat(user, "<span class='notice'>Unable to repair with the maintenance panel closed.</span>")
to_chat(user, SPAN_WARNING("You are unable to repair [src] with the maintenance panel closed."))
else
to_chat(user, "<span class='notice'>[src] does not need a repair.</span>")
to_chat(user, SPAN_WARNING("[src] does not need a repair."))
return
else if(O.iscrowbar())
if(!pAI)
to_chat(user, SPAN_WARNING("\The [src] does not have a pAI installed!"))
return
if(!old_name)
old_name = initial(name)
name = old_name
user.put_in_hands(pAI)
user.visible_message(SPAN_NOTICE("\The [user] pries \the [pAI.pai] out of \the [src]."), SPAN_NOTICE("You pry \the [pAI.pai] out of \the [src]."))
pAI = null
else if(istype(O, /obj/item/device/paicard))
if(!can_take_pai)
to_chat(user, SPAN_WARNING("\The [src] cannot take a pAI!"))
return
if(pAI)
to_chat(user, SPAN_WARNING("\The [src] already has a pAI installed!"))
return
var/obj/item/device/paicard/P = O
P.pai.open_up(FALSE)
P.pai.close_up()
user.drop_from_inventory(P, src)
pAI = P
user.visible_message(SPAN_NOTICE("\The [user] places \the [pAI.pai] into \the [src]."), SPAN_NOTICE("You place \the [O] into \the [src]."))
old_name = src.name
name = pAI.pai.name
else
..()
/mob/living/bot/attack_ai(var/mob/user)
/mob/living/bot/attack_ai(mob/user)
if(pAI)
to_chat(user, SPAN_WARNING("\The [src] contains a pAI and cannot be remotely controlled."))
return
return attack_hand(user)
/mob/living/bot/say(var/message)
@@ -121,8 +170,13 @@
else
. = ..()
/mob/living/bot/emag_act(var/remaining_charges, var/mob/user)
return 0
/mob/living/bot/cleanbot/think()
if(pAI) // no AI if we have a pAI installed
return
..()
/mob/living/bot/emag_act()
return FALSE
/mob/living/bot/emp_act(severity)
switch(severity)
@@ -134,14 +188,14 @@
/mob/living/bot/proc/turn_on()
if(stat)
return 0
on = 1
return FALSE
on = TRUE
set_light(light_strength)
update_icons()
return 1
return TRUE
/mob/living/bot/proc/turn_off()
on = 0
on = FALSE
set_light(0)
update_icons()
+85 -95
View File
@@ -12,37 +12,38 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
/mob/living/bot/cleanbot
name = "Cleanbot"
desc = "A little cleaning robot, he looks so excited!"
desc = "A little cleaning robot, consisting of a bucket, a proximity sensor, and a prosthetic arm. It looks excited to clean!"
icon_state = "cleanbot0"
req_one_access = list(access_janitor, access_robotics)
botcard_access = list(access_janitor, access_maint_tunnels)
locked = 0 // Start unlocked so roboticist can set them to patrol.
locked = FALSE // Start unlocked so roboticist can set them to patrol.
var/obj/effect/decal/cleanable/target
var/list/path = list()
var/list/patrol_path = list()
var/list/ignorelist = list()
var/obj/cleanbot_listener/listener = null
var/obj/cleanbot_listener/listener
var/beacon_freq = 1445 // navigation beacon frequency
var/signal_sent = 0
var/closest_dist
var/next_dest
var/next_dest_loc
var/cleaning = 0
var/screwloose = 0
var/oddbutton = 0
var/should_patrol = 0
var/blood = 1
var/cleaning = FALSE
var/screw_loose = FALSE
var/odd_button = FALSE
var/should_patrol = FALSE
var/cleans_blood = TRUE
var/list/target_types = list()
var/maximum_search_range = 7
/mob/living/bot/cleanbot/Cross(atom/movable/crossed)
if(crossed)
if(istype(crossed,/mob/living/bot/cleanbot)) return 0
if(istype(crossed, /mob/living/bot/cleanbot))
return FALSE
return ..()
/mob/living/bot/cleanbot/Initialize()
@@ -57,13 +58,13 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
SSradio.add_object(listener, beacon_freq, filter = RADIO_NAVBEACONS)
/mob/living/bot/cleanbot/Destroy()
. = ..()
path = null
patrol_path = null
target = null
ignorelist = null
QDEL_NULL(listener)
global.janitorial_supplies -= src
return ..()
/mob/living/bot/cleanbot/proc/handle_target()
if(target.clean_marked && target.clean_marked != src)
@@ -71,24 +72,22 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
path = list()
ignorelist |= target
return
if(loc == target.loc)
if(get_turf(src) == get_turf(target))
if(!cleaning)
UnarmedAttack(target)
return 1
return TRUE
if(!path.len)
path = AStar(loc, target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
path = AStar(get_turf(src), get_turf(target), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 30, id = botcard)
if(!path)
//log_debug("[src] can't reach [target.name] ([target.x], [target.y])")
ignorelist |= target
target.clean_marked = null
target = null
path = list()
return
if(path.len)
else
step_to(src, path[1])
path -= path[1]
return 1
return
return TRUE
/mob/living/bot/cleanbot/proc/remove_from_ignore(path)
ignorelist -= path
@@ -107,23 +106,24 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
if(cleaning)
return
if(!screwloose && !oddbutton && prob(2))
if(!screw_loose && !odd_button && prob(2) && world.time > last_emote + 2 MINUTES)
custom_emote(2, "makes an excited beeping booping sound!")
last_emote = world.time
if(screwloose && prob(5)) // Make a mess
if(screw_loose && prob(5)) // Make a mess
if(istype(loc, /turf/simulated))
var/turf/simulated/T = loc
T.wet_floor()
if(oddbutton && prob(5)) // Make a big mess
visible_message("Something flies out of [src]. He seems to be acting oddly.")
var/obj/effect/decal/cleanable/blood/gibs/gib = new /obj/effect/decal/cleanable/blood/gibs(loc)
if(odd_button && prob(5)) // Make a big mess
visible_message(SPAN_WARNING("Some bloody gibs fall out of [src]..."))
var/obj/effect/decal/cleanable/blood/gibs/gib = new /obj/effect/decal/cleanable/blood/gibs(get_turf(src))
ignorelist += gib
addtimer(CALLBACK(src, .proc/remove_from_ignore, gib), 600)
/mob/living/bot/cleanbot/think()
..()
if (!on)
if(!on)
return
if(pulledby) // Don't wiggle if someone pulls you
@@ -137,11 +137,12 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
// This loop will progressively search outwards for /cleanables in view(), gradually to prevent excessively large view() calls when none are needed.
search_for: // We use the label so we can break out of this loop from within the next loop.
// Not breaking out of these loops properly is where the infinite loop was coming from before.
for(var/i=0, i <= maximum_search_range, i++)
for(var/i = 0, i <= maximum_search_range, i++)
clean_for: // This one isn't really needed in this context, but it's good to have in case we expand later.
for(var/obj/effect/decal/cleanable/D in view(i, src))
if(D.clean_marked && D.clean_marked != src) continue clean_for
var/mob/living/bot/cleanbot/other_bot = locate() in D.loc
if(D.clean_marked && D.clean_marked != src)
continue clean_for
var/mob/living/bot/cleanbot/other_bot = locate() in get_turf(D)
if(other_bot && other_bot.cleaning && other_bot != src)
continue clean_for
if((D in ignorelist))
@@ -153,9 +154,8 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
target = D
D.clean_marked = src
found_spot = handle_target()
if (found_spot)
if(found_spot)
break search_for // If the target location is found and pathed properly, break the search loop.
else
target = null // Otherwise we want to try the next cleanable in view, if any.
D.clean_marked = null
@@ -197,44 +197,47 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
patrol_path -= patrol_path[1]
/mob/living/bot/cleanbot/UnarmedAttack(var/obj/effect/decal/cleanable/D, var/proximity)
if(!..())
. = ..()
if(!.)
return
if(isturf(D))
D = locate(/obj/effect/decal/cleanable) in D
if(!istype(D))
return
if(D.loc != loc)
if(!src.Adjacent(D))
return
cleaning = 1
target.being_cleaned = 1
cleaning = TRUE
D.being_cleaned = TRUE
update_icons()
var/clean_time = istype(D, /obj/effect/decal/cleanable/dirt) ? 10 : 50
INVOKE_ASYNC(src, .proc/do_clean, D, clean_time)
/mob/living/bot/cleanbot/proc/do_clean(var/obj/effect/decal/cleanable/D, var/clean_time)
if(D && do_after(src, clean_time))
if(istype(D.loc, /turf/simulated))
var/turf/simulated/f = loc
f.dirt = 0
if(!D)
return
D.clean_marked = null
if(D == target)
target.being_cleaned = FALSE
target = null
qdel(D)
cleaning = FALSE
update_icons()
var/cleantime = istype(D, /obj/effect/decal/cleanable/dirt) ? 10 : 50
spawn(1)
if(do_after(src, cleantime))
if(istype(loc, /turf/simulated))
var/turf/simulated/f = loc
f.dirt = 0
if(!D)
return
D.clean_marked = null
if(D == target)
target.being_cleaned = 0
target = null
qdel(D)
cleaning = 0
update_icons()
/mob/living/bot/cleanbot/explode()
on = 0
visible_message("<span class='danger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
new /obj/item/reagent_containers/glass/bucket(Tsec)
new /obj/item/device/assembly/prox_sensor(Tsec)
on = FALSE // the first thing i do when i explode is turn off, tbh - geeves
visible_message(SPAN_WARNING("[src] blows apart!"))
var/turf/T = get_turf(src)
new /obj/item/reagent_containers/glass/bucket(T)
new /obj/item/device/assembly/prox_sensor(T)
if(prob(50))
new /obj/item/robot_parts/l_arm(Tsec)
new /obj/item/robot_parts/l_arm(T)
spark(src, 3, alldirs)
qdel(src)
return
@@ -252,8 +255,8 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
patrol_path = list()
/mob/living/bot/cleanbot/attack_hand(var/mob/user)
if (!has_ui_access(user) && !emagged)
to_chat(user, "<span class='warning'>The unit's interface refuses to unlock!</span>")
if(!has_ui_access(user) && !emagged)
to_chat(user, SPAN_WARNING("The unit's interface refuses to unlock!"))
return
var/dat
@@ -262,11 +265,11 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
dat += "Behaviour controls are [locked ? "locked" : "unlocked"]<BR>"
dat += "Maintenance panel is [open ? "opened" : "closed"]"
if(!locked || issilicon(user))
dat += "<BR>Cleans Blood: <A href='?src=\ref[src];operation=blood'>[blood ? "Yes" : "No"]</A><BR>"
dat += "<BR>Cleans Blood: <A href='?src=\ref[src];operation=blood'>[cleans_blood ? "Yes" : "No"]</A><BR>"
dat += "<BR>Patrol station: <A href='?src=\ref[src];operation=patrol'>[should_patrol ? "Yes" : "No"]</A><BR>"
if(open && !locked)
dat += "Odd looking screw twiddled: <A href='?src=\ref[src];operation=screw'>[screwloose ? "Yes" : "No"]</A><BR>"
dat += "Weird button pressed: <A href='?src=\ref[src];operation=oddbutton'>[oddbutton ? "Yes" : "No"]</A>"
dat += "Odd looking screw twiddled: <A href='?src=\ref[src];operation=screw'>[screw_loose ? "Yes" : "No"]</A><BR>"
dat += "Weird button pressed: <A href='?src=\ref[src];operation=odd_button'>[odd_button ? "Yes" : "No"]</A>"
user << browse("<HEAD><TITLE>Cleaner v1.1 controls</TITLE></HEAD>[dat]", "window=autocleaner")
onclose(user, "autocleaner")
@@ -278,8 +281,8 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
usr.set_machine(src)
add_fingerprint(usr)
if (!has_ui_access(usr) && !emagged)
to_chat(usr, "<span class='warning'>Insufficient permissions.</span>")
if(!has_ui_access(usr) && !emagged)
to_chat(usr, SPAN_WARNING("Insufficient permissions."))
return
switch(href_list["operation"])
@@ -289,31 +292,31 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
else
turn_on()
if("blood")
blood = !blood
cleans_blood = !cleans_blood
get_targets()
if("patrol")
should_patrol = !should_patrol
patrol_path = list()
if("freq")
var/freq = text2num(input("Select frequency for navigation beacons", "Frequnecy", num2text(beacon_freq / 10))) * 10
if (freq > 0)
var/freq = text2num(input("Select frequency for navigation beacons", "Frequnecy", num2text(beacon_freq / 10))) * 10
if(freq > 0)
beacon_freq = freq
if("screw")
screwloose = !screwloose
to_chat(usr, "<span class='notice'>You twiddle the screw.</span>")
if("oddbutton")
oddbutton = !oddbutton
to_chat(usr, "<span class='notice'>You press the weird button.</span>")
screw_loose = !screw_loose
to_chat(usr, SPAN_NOTICE("You twiddle the screw."))
if("odd_button")
odd_button = !odd_button
to_chat(usr, SPAN_NOTICE("You press the weird button."))
attack_hand(usr)
/mob/living/bot/cleanbot/emag_act(var/remaining_uses, var/mob/user)
. = ..()
if(!screwloose || !oddbutton)
if(!screw_loose || !odd_button)
if(user)
to_chat(user, "<span class='notice'>The [src] buzzes and beeps.</span>")
oddbutton = 1
screwloose = 1
return 1
to_chat(user, SPAN_NOTICE("The [src] buzzes and beeps."))
odd_button = TRUE
screw_loose = TRUE
return TRUE
/mob/living/bot/cleanbot/proc/get_targets()
// To avoid excessive loops, instead of storing a list of top-level types, we're going to store a list of all cleanables.
@@ -327,19 +330,8 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
/obj/effect/decal/cleanable/crayon,/obj/effect/decal/cleanable/liquid_fuel,/obj/effect/decal/cleanable/mucus,/obj/effect/decal/cleanable/dirt)
// I honestly forgot you could pass multiple types to typesof() until I accidentally did it here.
target_types = cleanbot_types.Copy()
if(!blood)
if(!cleans_blood)
target_types -= typesof(/obj/effect/decal/cleanable/blood)-typesof(/obj/effect/decal/cleanable/blood/oil)
/* target_types = list()
target_types += /obj/effect/decal/cleanable/blood/oil
target_types += /obj/effect/decal/cleanable/vomit
target_types += /obj/effect/decal/cleanable/crayon
target_types += /obj/effect/decal/cleanable/liquid_fuel
target_types += /obj/effect/decal/cleanable/mucus
target_types += /obj/effect/decal/cleanable/dirt
if(blood)
target_types += /obj/effect/decal/cleanable/blood*/
/* Radio object that listens to signals */
@@ -367,28 +359,26 @@ var/list/cleanbot_types // Going to use this to generate a list of types once th
/* Assembly */
/obj/item/bucket_sensor
desc = "It's a bucket. With a sensor attached."
name = "proxy bucket"
desc = "It's a bucket. With a sensor attached."
icon = 'icons/obj/aibots.dmi'
icon_state = "bucket_proxy"
force = 3.0
throwforce = 10.0
force = 3
throwforce = 10
throw_speed = 2
throw_range = 5
w_class = 3.0
var/created_name = "Cleanbot"
/obj/item/bucket_sensor/attackby(var/obj/item/O, var/mob/user)
..()
if(istype(O, /obj/item/robot_parts/l_arm) || istype(O, /obj/item/robot_parts/r_arm))
user.drop_from_inventory(O,get_turf(src))
user.drop_from_inventory(O, get_turf(src))
qdel(O)
var/turf/T = get_turf(loc)
var/turf/T = get_turf(src)
var/mob/living/bot/cleanbot/A = new /mob/living/bot/cleanbot(T)
A.name = created_name
to_chat(user, "<span class='notice'>You add the robot arm to the bucket and sensor assembly. Beep boop!</span>")
to_chat(user, SPAN_NOTICE("You add the robot arm to the bucket and sensor assembly. Beep boop!"))
qdel(src)
else if(O.ispen())
var/t = sanitizeSafe(input(user, "Enter new robot name", name, created_name), MAX_NAME_LEN)
if(!t)
+75 -89
View File
@@ -6,7 +6,7 @@
/mob/living/bot/farmbot
name = "Farmbot"
desc = "The botanist's best friend."
desc = "The botanist's best friend. Various farming equipment seems haphazardly attached to it."
icon = 'icons/obj/aibots.dmi'
icon_state = "farmbot0"
health = 50
@@ -14,17 +14,17 @@
req_one_access = list(access_hydroponics, access_robotics)
var/action = "" // Used to update icon
var/waters_trays = 1
var/refills_water = 1
var/uproots_weeds = 1
var/replaces_nutriment = 0
var/collects_produce = 0
var/removes_dead = 0
var/eliminates_pests = 0
var/waters_trays = TRUE
var/refills_water = TRUE
var/uproots_weeds = TRUE
var/replaces_nutriment = FALSE
var/collects_produce = FALSE
var/removes_dead = FALSE
var/eliminates_pests = FALSE
var/obj/structure/reagent_dispensers/watertank/tank
var/attacking = 0
var/attacking = FALSE
var/list/path = list()
var/atom/target
var/frustration = 0
@@ -45,7 +45,7 @@
return
if (!has_ui_access(user))
to_chat(user, "<span class='warning'>The unit's interface refuses to unlock!</span>")
to_chat(user, SPAN_WARNING("The unit's interface refuses to unlock!"))
return
var/dat = ""
@@ -79,11 +79,11 @@
. = ..()
if(!emagged)
if(user)
to_chat(user, "<span class='notice'>You short out [src]'s plant identifier circuits.</span>")
to_chat(user, SPAN_NOTICE("You short out [src]'s plant identifier circuits."))
spawn(rand(30, 50))
visible_message("<span class='warning'>[src] buzzes oddly.</span>")
emagged = 1
return 1
visible_message(SPAN_WARNING("[src] buzzes oddly."))
emagged = TRUE
return TRUE
/mob/living/bot/farmbot/Topic(href, href_list)
if(..())
@@ -91,8 +91,8 @@
usr.machine = src
add_fingerprint(usr)
if (!has_ui_access(usr))
to_chat(usr, "<span class='warning'>Insufficient permissions.</span>")
if(!has_ui_access(usr))
to_chat(usr, SPAN_WARNING("Insufficient permissions."))
return
if(href_list["power"])
@@ -180,7 +180,8 @@
path = list()
/mob/living/bot/farmbot/UnarmedAttack(var/atom/A, var/proximity)
if(!..())
. = ..()
if(!.)
return
if(attacking)
return
@@ -194,48 +195,48 @@
if(FARMBOT_COLLECT)
action = "collect"
update_icons()
visible_message("<span class='notice'>[src] starts [T.dead? "removing the plant from" : "harvesting"] \the [A].</span>")
attacking = 1
visible_message(SPAN_NOTICE("[src] starts [T.dead? "removing the plant from" : "harvesting"] \the [A]."))
attacking = TRUE
if(do_after(src, 30))
visible_message("<span class='notice'>[src] [T.dead? "removes the plant from" : "harvests"] \the [A].</span>")
visible_message(SPAN_NOTICE("[src] [T.dead? "removes the plant from" : "harvests"] \the [A]."))
T.attack_hand(src)
if(FARMBOT_WATER)
action = "water"
update_icons()
visible_message("<span class='notice'>[src] starts watering \the [A].</span>")
attacking = 1
visible_message(SPAN_NOTICE("[src] starts watering \the [A]."))
attacking = TRUE
if(do_after(src, 30))
playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
visible_message("<span class='notice'>[src] waters \the [A].</span>")
playsound(get_turf(src), 'sound/effects/slosh.ogg', 25, TRUE)
visible_message(SPAN_NOTICE("[src] waters \the [A]."))
tank.reagents.trans_to(T, 100 - T.waterlevel)
if(FARMBOT_UPROOT)
action = "hoe"
update_icons()
visible_message("<span class='notice'>[src] starts uprooting the weeds in \the [A].</span>")
attacking = 1
visible_message(SPAN_NOTICE("[src] starts uprooting the weeds in \the [A]."))
attacking = TRUE
if(do_after(src, 30))
visible_message("<span class='notice'>[src] uproots the weeds in \the [A].</span>")
visible_message(SPAN_NOTICE("[src] uproots the weeds in \the [A]."))
T.weedlevel = 0
T.update_icon()
if(FARMBOT_PESTKILL)
action = "hoe"
update_icons()
visible_message("<span class='notice'>[src] starts eliminating the pests in \the [A].</span>")
attacking = 1
visible_message(SPAN_NOTICE("[src] starts eliminating the pests in \the [A]."))
attacking = TRUE
if(do_after(src, 30))
visible_message("<span class='notice'>[src] decimates the pests in \the [A].</span>")
visible_message(SPAN_NOTICE("[src] eliminates the pests in \the [A]."))
T.pestlevel = 0
T.reagents.add_reagent("nutriment", 0.5)
T.update_icon()
if(FARMBOT_NUTRIMENT)
action = "fertile"
update_icons()
visible_message("<span class='notice'>[src] starts fertilizing \the [A].</span>")
attacking = 1
visible_message(SPAN_NOTICE("[src] starts fertilizing \the [A]."))
attacking = TRUE
if(do_after(src, 30))
visible_message("<span class='notice'>[src] waters \the [A].</span>")
visible_message(SPAN_NOTICE("[src] waters \the [A]."))
T.reagents.add_reagent("ammonia", 10)
attacking = 0
attacking = FALSE
action = ""
update_icons()
T.update_icon()
@@ -244,48 +245,46 @@
return
action = "water"
update_icons()
visible_message("<span class='notice'>[src] starts refilling its tank from \the [A].</span>")
attacking = 1
visible_message(SPAN_NOTICE("[src] starts refilling its tank from \the [A]."))
attacking = TRUE
while(do_after(src, 10) && tank.reagents.total_volume < tank.reagents.maximum_volume)
tank.reagents.add_reagent("water", 10)
if(prob(5))
playsound(loc, 'sound/effects/slosh.ogg', 25, 1)
attacking = 0
playsound(get_turf(src), 'sound/effects/slosh.ogg', 25, TRUE)
attacking = FALSE
action = ""
update_icons()
visible_message("<span class='notice'>[src] finishes refilling its tank.</span>")
visible_message(SPAN_NOTICE("[src] finishes refilling its tank."))
else if(emagged && ishuman(A))
var/action = pick("weed", "water")
attacking = 1
attacking = TRUE
spawn(50) // Some delay
attacking = 0
attacking = FALSE
switch(action)
if("weed")
flick("farmbot_hoe", src)
do_attack_animation(A)
if(prob(50))
visible_message("<span class='danger'>[src] swings wildly at [A] with a minihoe, missing completely!</span>")
visible_message(SPAN_DANGER("[src] swings wildly at [A] with a minihoe, missing completely!"))
return
var/t = pick("slashed", "sliced", "cut", "clawed")
A.attack_generic(src, 5, t)
if("water")
flick("farmbot_water", src)
visible_message("<span class='danger'>[src] splashes [A] with water!</span>") // That's it. RP effect.
visible_message(SPAN_DANGER("[src] splashes [A] with water!")) // That's it. RP effect.
/mob/living/bot/farmbot/explode()
visible_message("<span class='danger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
new /obj/item/material/minihoe(Tsec)
new /obj/item/reagent_containers/glass/bucket(Tsec)
new /obj/item/device/assembly/prox_sensor(Tsec)
new /obj/item/device/analyzer/plant_analyzer(Tsec)
visible_message(SPAN_DANGER("[src] blows apart!"))
var/turf/T = get_turf(src)
new /obj/item/material/minihoe(T)
new /obj/item/reagent_containers/glass/bucket(T)
new /obj/item/device/assembly/prox_sensor(T)
new /obj/item/device/analyzer/plant_analyzer(T)
if(tank)
tank.forceMove(Tsec)
tank.forceMove(T)
if(prob(50))
new /obj/item/robot_parts/l_arm(Tsec)
new /obj/item/robot_parts/l_arm(T)
spark(src, 3, alldirs)
qdel(src)
@@ -293,34 +292,27 @@
/mob/living/bot/farmbot/proc/process_tray(var/obj/machinery/portable_atmospherics/hydroponics/tray)
if(!tray || !istype(tray))
return 0
return FALSE
if(tray.closed_system || !tray.seed)
return 0
return FALSE
if(tray.dead && removes_dead || tray.harvest && collects_produce)
return FARMBOT_COLLECT
else if(waters_trays && tray.waterlevel < 10 && !tray.reagents.has_reagent("water"))
return FARMBOT_WATER
else if(uproots_weeds && tray.weedlevel >= 5)
return FARMBOT_UPROOT
else if(eliminates_pests && tray.pestlevel >= 3)
return FARMBOT_PESTKILL
else if(replaces_nutriment && tray.nutrilevel < 2 && tray.reagents.total_volume < 1)
return FARMBOT_NUTRIMENT
return 0
return FALSE
// Assembly
/mob/living/bot/farmbot/proc/check_tank()
if(!tank)
return FALSE
return ((!target && refills_water && tank.reagents.total_volume < tank.reagents.maximum_volume) || ((tank.reagents.total_volume ) / tank.reagents.maximum_volume) <= 0.3)
if(!tank)
return FALSE
return ((!target && refills_water && tank.reagents.total_volume < tank.reagents.maximum_volume) || ((tank.reagents.total_volume ) / tank.reagents.maximum_volume) <= 0.3)
/obj/item/farmbot_arm_assembly
name = "water tank/robot arm assembly"
@@ -329,46 +321,42 @@
icon_state = "water_arm"
var/build_step = 0
var/created_name = "Farmbot"
w_class = 3.0
/obj/structure/reagent_dispensers/watertank/attackby(var/obj/item/robot_parts/S, mob/user as mob)
/obj/structure/reagent_dispensers/watertank/attackby(obj/item/robot_parts/S, mob/user)
if ((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
..()
return
var/obj/item/farmbot_arm_assembly/A = new /obj/item/farmbot_arm_assembly(loc)
to_chat(user, "You add the robot arm to [src].")
to_chat(user, SPAN_NOTICE("You add \the [S] to \the [src]."))
loc = A //Place the water tank into the assembly, it will be needed for the finished bot
qdel(S)
/obj/item/farmbot_arm_assembly/attackby(obj/item/W as obj, mob/user as mob)
/obj/item/farmbot_arm_assembly/attackby(obj/item/W, mob/user)
..()
if((istype(W, /obj/item/device/analyzer/plant_analyzer)) && (build_step == 0))
if(istype(W, /obj/item/device/analyzer/plant_analyzer) && build_step == 0)
build_step++
to_chat(user, "You add the plant analyzer to [src].")
to_chat(user, SPAN_NOTICE("You add the plant analyzer to [src]."))
name = "farmbot assembly"
qdel(W)
return 1
else if((istype(W, /obj/item/reagent_containers/glass/bucket)) && (build_step == 1))
return TRUE
else if(istype(W, /obj/item/reagent_containers/glass/bucket) && build_step == 1)
build_step++
to_chat(user, "You add a bucket to [src].")
to_chat(user, SPAN_NOTICE("You add a bucket to [src]."))
name = "farmbot assembly with bucket"
qdel(W)
return 1//Prevents the object's afterattack from executing and causing runtime errors
else if((istype(W, /obj/item/material/minihoe)) && (build_step == 2))
return TRUE //Prevents the object's afterattack from executing and causing runtime errors
else if(istype(W, /obj/item/material/minihoe) && build_step == 2)
build_step++
to_chat(user, "You add a minihoe to [src].")
to_chat(user, SPAN_NOTICE("You add a minihoe to [src]."))
name = "farmbot assembly with bucket and minihoe"
user.remove_from_mob(W)
qdel(W)
return 1
else if((isprox(W)) && (build_step == 3))
return TRUE
else if(isprox(W) && build_step == 3)
build_step++
to_chat(user, "You complete the Farmbot! Beep boop.")
to_chat(user, SPAN_NOTICE("You complete the Farmbot! Beep boop."))
var/mob/living/bot/farmbot/S = new /mob/living/bot/farmbot(get_turf(src))
for(var/obj/structure/reagent_dispensers/watertank/wTank in contents)
qdel(S.tank)
@@ -378,8 +366,7 @@
user.remove_from_mob(W)
qdel(W)
qdel(src)
return 1
return TRUE
else if(W.ispen())
var/t = input(user, "Enter new robot name", name, created_name) as text
t = sanitize(t, MAX_NAME_LEN)
@@ -387,8 +374,7 @@
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
/obj/item/farmbot_arm_assembly/attack_hand(mob/user as mob)
return //it's a converted watertank, no you cannot pick it up and put it in your backpack
/obj/item/farmbot_arm_assembly/attack_hand(mob/user)
return //it's a converted watertank, no you cannot pick it up and put it in your backpack
+3 -2
View File
@@ -194,13 +194,14 @@
target = null
/mob/living/bot/floorbot/UnarmedAttack(var/atom/A, var/proximity)
if(!..())
. = ..()
if(!.)
return
if(repairing)
return
if(get_turf(A) != loc)
if(!src.Adjacent(A))
return
if(emagged && istype(A, /turf/simulated/floor))
+5 -1
View File
@@ -74,7 +74,8 @@
break
/mob/living/bot/medbot/UnarmedAttack(var/mob/living/carbon/human/H, var/proximity)
if(!..())
. = ..()
if(!.)
return
if(!on)
@@ -84,6 +85,9 @@
return
if(H.stat == DEAD)
if(pAI)
to_chat(pAI.pai, SPAN_WARNING("\The [H] is dead, you cannot help them now."))
return
var/death_message = pick("No! NO!", "Live, damnit! LIVE!", "I... I've never lost a patient before. Not today, I mean.")
say(death_message)
patient = null
+1
View File
@@ -58,6 +58,7 @@
var/datum/callback/patrol_callback // this is here so we don't constantly recreate this datum, it being identical each time.
var/move_to_delay = 4 //delay for the automated movement.
can_take_pai = FALSE
/mob/living/bot/secbot/beepsky
name = "Officer Beepsky"
@@ -35,3 +35,18 @@
stat = CONSCIOUS
else
health = 100 - getBruteLoss() - getFireLoss()
/mob/living/silicon/pai/Stat()
..()
if(istype(card.loc, /mob/living/bot))
var/mob/living/bot/B = card.loc
stat(null, "Piloting: [B.name]")
stat(null, "Bot Status: [B.on ? "Active" : "Inactive"]")
stat(null, "Maintenance Hatch: [B.open ? "Open" : "Closed"]")
stat(null, "Maintenance Lock: [B.locked ? "Locked" : "Unlocked"]")
if(B.emagged)
stat(null, "Bot M#$FUN90: MALFUNC--")
if(istype(card.loc, /mob/living/bot/floorbot))
var/mob/living/bot/floorbot/F = card.loc
stat(null, "Metal Count: [F.amount]/[F.maxAmount]")
+18 -10
View File
@@ -209,7 +209,6 @@
greet()
..()
/mob/living/silicon/pai/proc/greet()
if (!greeted)
@@ -346,12 +345,18 @@
return
last_special = world.time + 20
open_up()
/mob/living/silicon/pai/proc/open_up(var/loud = TRUE)
if(istype(card.loc, /mob/living/bot))
to_chat(src, SPAN_WARNING("You cannot unfold while inside the bot!"))
return FALSE
//I'm not sure how much of this is necessary, but I would rather avoid issues.
if(istype(card.loc,/obj/item/rig_module))
to_chat(src, "There is no room to unfold inside this rig module. You're good and stuck.")
return 0
else if(istype(card.loc,/mob))
to_chat(src, SPAN_WARNING("There is no room to unfold inside this rig module. You're good and stuck."))
return FALSE
else if(istype(card.loc, /mob))
var/mob/holder = card.loc
if(ishuman(holder))
var/mob/living/carbon/human/H = holder
@@ -359,7 +364,8 @@
if(card in affecting.implants)
affecting.take_damage(rand(30,50))
affecting.implants -= card
H.visible_message("<span class='danger'>\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!</span>")
if(loud)
H.visible_message(SPAN_DANGER("\The [src] explodes out of \the [H]'s [affecting.name] in shower of gore!"))
break
holder.drop_from_inventory(card)
else if(istype(card.loc,/obj/item/device/pda))
@@ -374,9 +380,11 @@
card.screen_loc = null
var/turf/T = get_turf(src)
if(istype(T)) T.visible_message("<b>[src]</b> folds outwards, expanding into a mobile form.")
canmove = 1
resting = 0
if(loud && istype(T))
T.visible_message(SPAN_NOTICE("<b>[src]</b> folds outwards, expanding into a mobile form."))
canmove = TRUE
resting = FALSE
/mob/living/silicon/pai/verb/fold_up()
set category = "pAI Commands"
@@ -417,8 +425,8 @@
set name = "Check location"
set desc = "Find out where on their person, someone is holding you."
if (!get_holding_mob())
to_chat(src, "Nobody is holding you!")
if(!get_holding_mob())
to_chat(src, SPAN_WARNING("Nobody is holding you!"))
return
card.report_onmob_location(0, card.get_equip_slot(), src)
+3
View File
@@ -766,6 +766,9 @@ proc/is_blind(A)
*/
/obj/proc/report_onmob_location(var/justmoved, var/slot = null, var/mob/reportto)
if(istype(reportto.loc, /mob/living/bot))
to_chat(reportto, SPAN_NOTICE("You are currently housed within \the [reportto.loc]."))
return
var/mob/living/carbon/human/H//The person who the item is on
var/newlocation
var/preposition= ""