Ports tg simple_animal bots

This commit is contained in:
Tastyfish
2016-03-28 00:22:04 -04:00
parent a19f646e0e
commit 25195208a9
110 changed files with 5551 additions and 5360 deletions
+1 -1
View File
@@ -135,7 +135,7 @@
dat += "<a href='?src=\ref[src];screen=1'>Back</a>"
dat += "<br><b>Medical Robots:</b>"
var/bdat = null
for(var/obj/machinery/bot/medbot/M in world)
for(var/mob/living/simple_animal/bot/medbot/M in world)
if(M.z != computer.z) continue //only find medibots on the same z-level as the computer
var/turf/bl = get_turf(M)
+5 -5
View File
@@ -36,7 +36,7 @@
/datum/table_recipe/ed209
name = "ED209"
result = /obj/machinery/bot/ed209
result = /mob/living/simple_animal/bot/ed209
reqs = list(/obj/item/robot_parts/robot_suit = 1,
/obj/item/clothing/head/helmet = 1,
/obj/item/clothing/suit/armor/vest = 1,
@@ -53,7 +53,7 @@
/datum/table_recipe/secbot
name = "Secbot"
result = /obj/machinery/bot/secbot
result = /mob/living/simple_animal/bot/secbot
reqs = list(/obj/item/device/assembly/signaler = 1,
/obj/item/clothing/head/helmet = 1,
/obj/item/weapon/melee/baton = 1,
@@ -64,7 +64,7 @@
/datum/table_recipe/cleanbot
name = "Cleanbot"
result = /obj/machinery/bot/cleanbot
result = /mob/living/simple_animal/bot/cleanbot
reqs = list(/obj/item/weapon/reagent_containers/glass/bucket = 1,
/obj/item/device/assembly/prox_sensor = 1,
/obj/item/robot_parts/r_arm = 1)
@@ -72,7 +72,7 @@
/datum/table_recipe/floorbot
name = "Floorbot"
result = /obj/machinery/bot/floorbot
result = /mob/living/simple_animal/bot/floorbot
reqs = list(/obj/item/weapon/storage/toolbox/mechanical = 1,
/obj/item/stack/tile/plasteel = 1,
/obj/item/device/assembly/prox_sensor = 1,
@@ -81,7 +81,7 @@
/datum/table_recipe/medbot
name = "Medbot"
result = /obj/machinery/bot/medbot
result = /mob/living/simple_animal/bot/medbot
reqs = list(/obj/item/device/healthanalyzer = 1,
/obj/item/weapon/storage/firstaid = 1,
/obj/item/device/assembly/prox_sensor = 1,
+2 -2
View File
@@ -32,9 +32,9 @@
M << "<br>"
if(botEmagChance)
for(var/obj/machinery/bot/bot in machines)
for(var/mob/living/simple_animal/bot/bot in machines)
if(prob(botEmagChance))
bot.Emag()
bot.emag_act()
/proc/generate_ion_law(ionMessage)
if(ionMessage)
+1 -8
View File
@@ -693,14 +693,7 @@
mouse_opacity = 1
faction = list("neutral")
a_intent = I_HARM
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
wander = 0
idle_vision_range = 5
+2 -2
View File
@@ -114,7 +114,7 @@
message = stars(message)
var/speaker_name = "unknown"
if(speaker)
if(speaker)
speaker_name = speaker.name
if(vname)
@@ -160,7 +160,7 @@
else
track = "[speaker_name] ([jobname])"
else
if(istype(follow_target, /obj/machinery/bot))
if(istype(follow_target, /mob/living/simple_animal/bot))
track = "<a href='byond://?src=\ref[src];trackbot=\ref[follow_target]'>[speaker_name] ([jobname])</a>"
else
track = "<a href='byond://?src=\ref[src];track=\ref[speaker]'>[speaker_name] ([jobname])</a>"
@@ -196,7 +196,7 @@
usr.hud_used.nightvisionicon.icon_state = "nightvision0"
/mob/living/carbon/alien/assess_threat(var/obj/machinery/bot/secbot/judgebot, var/lasercolor)
/mob/living/carbon/alien/assess_threat(var/mob/living/simple_animal/bot/secbot/judgebot, var/lasercolor)
if(judgebot.emagged == 2)
return 10 //Everyone is a criminal!
var/threatcount = 0
@@ -636,8 +636,8 @@
// called when something steps onto a human
// this handles mulebots and vehicles
/mob/living/carbon/human/Crossed(var/atom/movable/AM)
if(istype(AM, /obj/machinery/bot/mulebot))
var/obj/machinery/bot/mulebot/MB = AM
if(istype(AM, /mob/living/simple_animal/bot/mulebot))
var/mob/living/simple_animal/bot/mulebot/MB = AM
MB.RunOver(src)
if(istype(AM, /obj/vehicle))
@@ -1753,7 +1753,7 @@
if(M.stat == 2)
M.gib()
/mob/living/carbon/human/assess_threat(var/obj/machinery/bot/secbot/judgebot, var/lasercolor)
/mob/living/carbon/human/assess_threat(var/mob/living/simple_animal/bot/secbot/judgebot, var/lasercolor)
if(judgebot.emagged == 2)
return 10 //Everyone is a criminal!
+7 -6
View File
@@ -41,6 +41,7 @@ var/list/ai_verbs_default = list(
anchored = 1 // -- TLE
density = 1
status_flags = CANSTUN|CANPARALYSE|CANPUSH
can_strip = 0
var/list/network = list("SS13","Telecomms","Research Outpost","Mining Outpost")
var/obj/machinery/camera/current = null
var/list/connected_robots = list()
@@ -80,7 +81,7 @@ var/list/ai_verbs_default = list(
var/can_shunt = 1
var/last_announcement = ""
var/datum/announcement/priority/announcement
var/obj/machinery/bot/Bot
var/mob/living/simple_animal/bot/Bot
var/turf/waypoint //Holds the turf of the currently selected waypoint.
var/waypoint_mode = 0 //Waypoint mode is for selecting a turf via clicking.
@@ -513,7 +514,7 @@ var/list/ai_verbs_default = list(
return
if (href_list["trackbot"])
var/obj/machinery/bot/target = locate(href_list["trackbot"]) in aibots
var/mob/living/simple_animal/bot/target = locate(href_list["trackbot"]) in simple_animal_list
if(target)
ai_actual_track(target)
else
@@ -521,7 +522,7 @@ var/list/ai_verbs_default = list(
return
if (href_list["callbot"]) //Command a bot to move to a selected location.
Bot = locate(href_list["callbot"]) in aibots
Bot = locate(href_list["callbot"]) in simple_animal_list
if(!Bot || Bot.remote_disabled || src.control_disabled)
return //True if there is no bot found, the bot is manually emagged, or the AI is carded with wireless off.
waypoint_mode = 1
@@ -529,7 +530,7 @@ var/list/ai_verbs_default = list(
return
if (href_list["interface"]) //Remotely connect to a bot!
Bot = locate(href_list["interface"]) in aibots
Bot = locate(href_list["interface"]) in simple_animal_list
if(!Bot || Bot.remote_disabled || src.control_disabled)
return
Bot.attack_ai(src)
@@ -643,10 +644,10 @@ var/list/ai_verbs_default = list(
d += "<A HREF=?src=\ref[src];botrefresh=\ref[Bot]>Query network status</A><br>"
d += "<table width='100%'><tr><td width='40%'><h3>Name</h3></td><td width='20%'><h3>Status</h3></td><td width='30%'><h3>Location</h3></td><td width='10%'><h3>Control</h3></td></tr>"
for (Bot in aibots)
for (var/mob/living/simple_animal/bot/Bot in simple_animal_list)
if((Bot.z in ai_allowed_Zlevel) && !Bot.remote_disabled) //Only non-emagged bots on the allowed Z-level are detected!
bot_area = get_area(Bot)
d += "<tr><td width='30%'>[Bot.hacked ? "<span class='bad'>(!) </span>[Bot.name]" : Bot.name] ([Bot.bot_type_name])</td>"
d += "<tr><td width='30%'>[Bot.hacked ? "<span class='bad'>(!) </span>[Bot.name]" : Bot.name] ([Bot.model])</td>"
//If the bot is on, it will display the bot's current mode status. If the bot is not mode, it will just report "Idle". "Inactive if it is not on at all.
d += "<td width='20%'>[Bot.on ? "[Bot.mode ? "<span class='average'>[ Bot.mode_name[Bot.mode] ]</span>": "<span class='good'>Idle</span>"]" : "<span class='bad'>Inactive</span>"]</td>"
d += "<td width='30%'>[bot_area.name]</td>"
+131 -139
View File
@@ -17,14 +17,6 @@
var/obj/machinery/portable_atmospherics/hydroponics/my_hydrotray
// Allow final solutions.
min_oxy = 5
max_oxy = 0
min_tox = 0
max_tox = 1
min_co2 = 0
max_co2 = 5
min_n2 = 0
max_n2 = 0
minbodytemp = 0
maxbodytemp = 360
@@ -37,145 +29,143 @@
parent.owned_bee_swarms.Remove(src)
return ..()
/mob/living/simple_animal/bee/Life()
..()
/mob/living/simple_animal/bee/process_ai()
//if we're strong enough, sting some people
var/mob/living/carbon/human/M = target_mob
var/sting_prob = 100 // Bees will always try to sting.
if(M in view(src,1)) // Can I see my target?
if(prob(max(feral * 10, 0))) // Am I mad enough to want to sting? And yes, when I initially appear, I AM mad enough
var/obj/item/clothing/worn_suit = M.wear_suit
var/obj/item/clothing/worn_helmet = M.head
if(worn_suit) // Are you wearing clothes?
sting_prob -= min(worn_suit.armor["bio"],70) // Is it sealed? I can't get to 70% of your body.
if(worn_helmet)
sting_prob -= min(worn_helmet.armor["bio"],30) // Is your helmet sealed? I can't get to 30% of your body.
if( prob(sting_prob) && (M.stat == CONSCIOUS || (M.stat == UNCONSCIOUS && prob(25))) ) // Try to sting! If you're not moving, think about stinging.
M.apply_damage(min(strength,2)+mut, BRUTE, sharp=1) // Stinging. The more mutated I am, the harder I sting.
M.apply_damage((round(feral/10,1)*(max((round(strength/20,1)),1)))+toxic, TOX) // Bee venom based on how angry I am and how many there are of me!
M << "\red You have been stung!"
if(stat == CONSCIOUS)
//if we're strong enough, sting some people
var/mob/living/carbon/human/M = target_mob
var/sting_prob = 100 // Bees will always try to sting.
if(M in view(src,1)) // Can I see my target?
if(prob(max(feral * 10, 0))) // Am I mad enough to want to sting? And yes, when I initially appear, I AM mad enough
var/obj/item/clothing/worn_suit = M.wear_suit
var/obj/item/clothing/worn_helmet = M.head
if(worn_suit) // Are you wearing clothes?
sting_prob -= min(worn_suit.armor["bio"],70) // Is it sealed? I can't get to 70% of your body.
if(worn_helmet)
sting_prob -= min(worn_helmet.armor["bio"],30) // Is your helmet sealed? I can't get to 30% of your body.
if( prob(sting_prob) && (M.stat == CONSCIOUS || (M.stat == UNCONSCIOUS && prob(25))) ) // Try to sting! If you're not moving, think about stinging.
M.apply_damage(min(strength,2)+mut, BRUTE, sharp=1) // Stinging. The more mutated I am, the harder I sting.
M.apply_damage((round(feral/10,1)*(max((round(strength/20,1)),1)))+toxic, TOX) // Bee venom based on how angry I am and how many there are of me!
M << "\red You have been stung!"
M.flash_pain()
//if we're chasing someone, get a little bit angry
if(target_mob && prob(10))
feral++
M.flash_pain()
//if we're chasing someone, get a little bit angry
if(target_mob && prob(10))
feral++
//calm down a little bit
if(feral > 0)
if(prob(feral * 10))
feral -= 1
else
//if feral is less than 0, we're becalmed by smoke or steam
if(feral < 0)
feral += 1
if(target_mob)
target_mob = null
target_turf = null
if(strength > 5)
//calm down and spread out a little
var/mob/living/simple_animal/bee/B = new(get_turf(pick(orange(src,1))))
B.strength = rand(1,5)
src.strength -= B.strength
if(src.strength <= 5)
src.icon_living = "bees[src.strength]"
B.icon_living = "bees[B.strength]"
if(src.parent)
B.parent = src.parent
src.parent.owned_bee_swarms.Add(B)
//make some noise
if(prob(0.5))
src.visible_message("\blue [pick("Buzzzz.","Hmmmmm.","Bzzz.")]")
//smoke, water and steam calms us down
var/calming = 0
var/list/calmers = list(/obj/effect/effect/chem_smoke, \
/obj/effect/effect/water, \
/obj/effect/effect/foam, \
/obj/effect/effect/steam, \
/obj/effect/mist)
for(var/obj/effect/check_effect in src.loc)
if(check_effect.type in calmers)
calming = 1
break
if(calming)
if(feral > 0)
src.visible_message("\blue The bees calm down!")
feral = -10
target_mob = null
target_turf = null
wander = 1
for(var/mob/living/simple_animal/bee/B in src.loc)
if(B == src)
continue
if(feral > 0)
src.strength += B.strength
qdel(B)
src.icon_living = "bees[src.strength]"
if(strength > 5)
icon_living = "bees_swarm"
else if(prob(10))
//make the other swarm of bees stronger, then move away
var/total_bees = B.strength + src.strength
if(total_bees < 10)
B.strength = min(5, total_bees)
src.strength = total_bees - B.strength
B.icon_living = "bees[B.strength]"
if(src.strength <= 0)
qdel(src)
return
src.icon_living = "bees[B.strength]"
var/turf/simulated/floor/T = get_turf(get_step(src, pick(1,2,4,8)))
density = 1
if(T.Enter(src, get_turf(src)))
src.loc = T
density = 0
break
//calm down a little bit
if(feral > 0)
if(prob(feral * 10))
feral -= 1
else
//if feral is less than 0, we're becalmed by smoke or steam
if(feral < 0)
feral += 1
if(target_mob)
if(target_mob in view(src,7))
target_turf = get_turf(target_mob)
wander = 0
target_mob = null
target_turf = null
if(strength > 5)
//calm down and spread out a little
var/mob/living/simple_animal/bee/B = new(get_turf(pick(orange(src,1))))
B.strength = rand(1,5)
src.strength -= B.strength
if(src.strength <= 5)
src.icon_living = "bees[src.strength]"
B.icon_living = "bees[B.strength]"
if(src.parent)
B.parent = src.parent
src.parent.owned_bee_swarms.Add(B)
else // My target's gone! But I might still be pissed! You there. You look like a good stinging target!
for(var/mob/living/carbon/G in view(src,7))
target_mob = G
break
//make some noise
if(prob(0.5))
src.visible_message("\blue [pick("Buzzzz.","Hmmmmm.","Bzzz.")]")
if(target_turf)
if (!(DirBlocked(get_step(src, get_dir(src,target_turf)),get_dir(src,target_turf)))) // Check for windows and doors!
Move(get_step(src, get_dir(src,target_turf)))
if (prob(0.1))
src.visible_message("\blue The bees swarm after [target_mob]!")
if(src.loc == target_turf)
target_turf = null
wander = 1
else
//find some flowers, harvest
//angry bee swarms don't hang around
if(feral > 0)
turns_per_move = rand(1,3)
else if(feral < 0)
turns_since_move = 0
else if(!my_hydrotray || my_hydrotray.loc != src.loc || my_hydrotray.dead || !my_hydrotray.seed)
var/obj/machinery/portable_atmospherics/hydroponics/my_hydrotray = locate() in src.loc
if(my_hydrotray)
if(!my_hydrotray.dead && my_hydrotray.seed)
turns_per_move = rand(20,50)
else
my_hydrotray = null
//smoke, water and steam calms us down
var/calming = 0
var/list/calmers = list(/obj/effect/effect/chem_smoke, \
/obj/effect/effect/water, \
/obj/effect/effect/foam, \
/obj/effect/effect/steam, \
/obj/effect/mist)
pixel_x = rand(-12,12)
pixel_y = rand(-12,12)
for(var/obj/effect/check_effect in src.loc)
if(check_effect.type in calmers)
calming = 1
break
if(calming)
if(feral > 0)
src.visible_message("\blue The bees calm down!")
feral = -10
target_mob = null
target_turf = null
wander = 1
for(var/mob/living/simple_animal/bee/B in src.loc)
if(B == src)
continue
if(feral > 0)
src.strength += B.strength
qdel(B)
src.icon_living = "bees[src.strength]"
if(strength > 5)
icon_living = "bees_swarm"
else if(prob(10))
//make the other swarm of bees stronger, then move away
var/total_bees = B.strength + src.strength
if(total_bees < 10)
B.strength = min(5, total_bees)
src.strength = total_bees - B.strength
B.icon_living = "bees[B.strength]"
if(src.strength <= 0)
qdel(src)
return
src.icon_living = "bees[B.strength]"
var/turf/simulated/floor/T = get_turf(get_step(src, pick(1,2,4,8)))
density = 1
if(T.Enter(src, get_turf(src)))
src.loc = T
density = 0
break
if(target_mob)
if(target_mob in view(src,7))
target_turf = get_turf(target_mob)
wander = 0
else // My target's gone! But I might still be pissed! You there. You look like a good stinging target!
for(var/mob/living/carbon/G in view(src,7))
target_mob = G
break
if(target_turf)
if (!(DirBlocked(get_step(src, get_dir(src,target_turf)),get_dir(src,target_turf)))) // Check for windows and doors!
Move(get_step(src, get_dir(src,target_turf)))
if (prob(0.1))
src.visible_message("\blue The bees swarm after [target_mob]!")
if(src.loc == target_turf)
target_turf = null
wander = 1
else
//find some flowers, harvest
//angry bee swarms don't hang around
if(feral > 0)
turns_per_move = rand(1,3)
else if(feral < 0)
turns_since_move = 0
else if(!my_hydrotray || my_hydrotray.loc != src.loc || my_hydrotray.dead || !my_hydrotray.seed)
var/obj/machinery/portable_atmospherics/hydroponics/my_hydrotray = locate() in src.loc
if(my_hydrotray)
if(!my_hydrotray.dead && my_hydrotray.seed)
turns_per_move = rand(20,50)
else
my_hydrotray = null
pixel_x = rand(-12,12)
pixel_y = rand(-12,12)
/mob/living/simple_animal/bee/handle_state_icons()
if(!parent && prob(10))
strength -= 1
if(strength <= 0)
@@ -187,3 +177,5 @@
/*icon_living = "[strength]"
if(strength > 5)
icon_living = "unknown"*/
..()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,227 @@
//Cleanbot
/mob/living/simple_animal/bot/cleanbot
name = "\improper Cleanbot"
desc = "A little cleaning robot, he looks so excited!"
icon = 'icons/obj/aibots.dmi'
icon_state = "cleanbot0"
density = 0
anchored = 0
health = 25
maxHealth = 25
radio_channel = "Service" //Service
bot_filter = RADIO_CLEANBOT
bot_type = CLEAN_BOT
model = "Cleanbot"
bot_purpose = "seek out messes and clean them"
bot_core_type = /obj/machinery/bot_core/cleanbot
window_id = "autoclean"
window_name = "Automatic Station Cleaner v1.1"
pass_flags = PASSMOB
var/blood = 1
var/list/target_types = list()
var/obj/effect/decal/cleanable/target
var/max_targets = 50 //Maximum number of targets a cleanbot can ignore.
var/oldloc = null
var/closest_dist
var/closest_loc
var/failed_steps
var/next_dest
var/next_dest_loc
/mob/living/simple_animal/bot/cleanbot/New()
..()
get_targets()
icon_state = "cleanbot[on]"
var/datum/job/janitor/J = new/datum/job/janitor
access_card.access += J.get_access()
prev_access = access_card.access
/mob/living/simple_animal/bot/cleanbot/turn_on()
..()
icon_state = "cleanbot[on]"
/mob/living/simple_animal/bot/cleanbot/turn_off()
..()
icon_state = "cleanbot[on]"
/mob/living/simple_animal/bot/cleanbot/bot_reset()
..()
ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable.
target = null
oldloc = null
/mob/living/simple_animal/bot/cleanbot/set_custom_texts()
text_hack = "You corrupt [name]'s cleaning software."
text_dehack = "[name]'s software has been reset!"
text_dehack_fail = "[name] does not seem to respond to your repair code!"
/mob/living/simple_animal/bot/cleanbot/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if(bot_core.allowed(user) && !open && !emagged)
locked = !locked
user << "<span class='notice'>You [ locked ? "lock" : "unlock"] \the [src] behaviour controls.</span>"
else
if(emagged)
user << "<span class='warning'>ERROR</span>"
if(open)
user << "<span class='warning'>Please close the access panel before locking it.</span>"
else
user << "<span class='notice'>\The [src] doesn't seem to respect your authority.</span>"
else
return ..()
/mob/living/simple_animal/bot/cleanbot/emag_act(mob/user)
..()
if(emagged == 2)
if(user)
user << "<span class='danger'>[src] buzzes and beeps.</span>"
/mob/living/simple_animal/bot/cleanbot/process_scan(obj/effect/decal/cleanable/D)
for(var/T in target_types)
if(istype(D, T))
return D
/mob/living/simple_animal/bot/cleanbot/handle_automated_action()
if(!..())
return
if(mode == BOT_CLEANING)
return
if(emagged == 2) //Emag functions
if(istype(loc,/turf/simulated))
if(prob(10)) //Wets floors randomly
var/turf/simulated/T = loc
T.MakeSlippery()
if(prob(5)) //Spawns foam!
visible_message("<span class='danger'>[src] whirs and bubbles violently, before releasing a plume of froth!</span>")
new /obj/effect/effect/foam(loc)
else if(prob(5))
audible_message("[src] makes an excited beeping booping sound!")
if(!target) //Search for cleanables it can see.
target = scan(/obj/effect/decal/cleanable/)
if(!target && auto_patrol) //Search for cleanables it can see.
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
start_patrol()
if(mode == BOT_PATROL)
bot_patrol()
if(target)
if(!path || path.len == 0) //No path, need a new one
//Try to produce a path to the target, and ignore airlocks to which it has access.
path = get_path_to(src, target.loc, /turf/proc/Distance_cardinal, 0, 30, id=access_card)
if(!bot_move(target))
add_to_ignore(target)
target = null
path = list()
return
mode = BOT_MOVING
else if(!bot_move(target))
target = null
mode = BOT_IDLE
return
if(target && loc == target.loc)
clean(target)
path = list()
target = null
oldloc = loc
/mob/living/simple_animal/bot/cleanbot/proc/get_targets()
target_types = new/list()
target_types += /obj/effect/decal/cleanable/blood/oil
target_types += /obj/effect/decal/cleanable/vomit
target_types += /obj/effect/decal/cleanable/poop
target_types += /obj/effect/decal/cleanable/blood/gibs/robot
target_types += /obj/effect/decal/cleanable/crayon
target_types += /obj/effect/decal/cleanable/liquid_fuel
target_types += /obj/effect/decal/cleanable/molten_item
target_types += /obj/effect/decal/cleanable/tomato_smudge
target_types += /obj/effect/decal/cleanable/egg_smudge
target_types += /obj/effect/decal/cleanable/pie_smudge
target_types += /obj/effect/decal/cleanable/flour
target_types += /obj/effect/decal/cleanable/ash
target_types += /obj/effect/decal/cleanable/greenglow
target_types += /obj/effect/decal/cleanable/dirt
target_types += /obj/effect/decal/cleanable/mucus
if(blood)
target_types += /obj/effect/decal/cleanable/blood/xeno/
target_types += /obj/effect/decal/cleanable/blood/gibs/xeno
target_types += /obj/effect/decal/cleanable/blood/
target_types += /obj/effect/decal/cleanable/blood/gibs/
target_types += /obj/effect/decal/cleanable/blood/tracks
target_types += /obj/effect/decal/cleanable/dirt
/mob/living/simple_animal/bot/cleanbot/proc/clean(obj/effect/decal/cleanable/target)
anchored = 1
icon_state = "cleanbot-c"
visible_message("<span class='notice'>[src] begins to clean up [target]</span>")
mode = BOT_CLEANING
spawn(50)
if(mode == BOT_CLEANING)
qdel(target)
anchored = 0
target = null
mode = BOT_IDLE
icon_state = "cleanbot[on]"
/mob/living/simple_animal/bot/cleanbot/explode()
on = 0
visible_message("<span class='userdanger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
new /obj/item/weapon/reagent_containers/glass/bucket(Tsec)
new /obj/item/device/assembly/prox_sensor(Tsec)
if(prob(50))
new /obj/item/robot_parts/l_arm(Tsec)
var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
..()
/obj/machinery/bot_core/cleanbot
req_one_access = list(access_janitor, access_robotics)
/mob/living/simple_animal/bot/cleanbot/get_controls(mob/user)
var/dat
dat += hack(user)
dat += showpai(user)
dat += text({"
<TT><B>Cleaner v1.1 controls</B></TT><BR><BR>
Status: []<BR>
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
Maintenance panel panel is [open ? "opened" : "closed"]"},
text("<A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A>"))
if(!locked || issilicon(user) || check_rights(R_ADMIN, 0, user))
dat += text({"<BR>Cleans Blood: []<BR>"}, text("<A href='?src=\ref[src];operation=blood'>[blood ? "Yes" : "No"]</A>"))
dat += text({"<BR>Patrol station: []<BR>"}, text("<A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A>"))
return dat
/mob/living/simple_animal/bot/cleanbot/Topic(href, href_list)
if(..())
return 1
switch(href_list["operation"])
if("blood")
blood =!blood
get_targets()
update_controls()
/mob/living/simple_animal/bot/cleanbot/UnarmedAttack(atom/A)
if(istype(A,/obj/effect/decal/cleanable))
clean(A)
else
..()
@@ -0,0 +1,480 @@
//Bot Construction
//Cleanbot assembly
/obj/item/weapon/bucket_sensor
desc = "It's a bucket. With a sensor attached."
name = "proxy bucket"
icon = 'icons/obj/aibots.dmi'
icon_state = "bucket_proxy"
force = 3
throwforce = 5
throw_speed = 2
throw_range = 5
w_class = 3
var/created_name = "Cleanbot"
/obj/item/weapon/bucket_sensor/attackby(obj/item/W, mob/user as mob, params)
..()
if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
if(!user.unEquip(W))
return
qdel(W)
var/turf/T = get_turf(loc)
var/mob/living/simple_animal/bot/cleanbot/A = new /mob/living/simple_animal/bot/cleanbot(T)
A.name = created_name
user << "<span class='notice'>You add the robot arm to the bucket and sensor assembly. Beep boop!</span>"
user.unEquip(src, 1)
qdel(src)
else if(istype(W, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
//Edbot Assembly
/obj/item/weapon/ed209_assembly
name = "\improper ED-209 assembly"
desc = "Some sort of bizarre assembly."
icon = 'icons/obj/aibots.dmi'
icon_state = "ed209_frame"
item_state = "ed209_frame"
var/build_step = 0
var/created_name = "ED-209 Security Robot" //To preserve the name if it's a unique securitron I guess
var/lasercolor = ""
/obj/item/weapon/ed209_assembly/attackby(obj/item/weapon/W, mob/user, params)
..()
if(istype(W, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
return
switch(build_step)
if(0,1)
if(istype(W, /obj/item/robot_parts/l_leg) || istype(W, /obj/item/robot_parts/r_leg))
if(!user.unEquip(W))
return
qdel(W)
build_step++
user << "<span class='notice'>You add the robot leg to [src].</span>"
name = "legs/frame assembly"
if(build_step == 1)
item_state = "ed209_leg"
icon_state = "ed209_leg"
else
item_state = "ed209_legs"
icon_state = "ed209_legs"
if(2)
var/newcolor = ""
if(istype(W, /obj/item/clothing/suit/redtag))
newcolor = "r"
else if(istype(W, /obj/item/clothing/suit/bluetag))
newcolor = "b"
if(newcolor || istype(W, /obj/item/clothing/suit/armor/vest))
if(!user.unEquip(W))
return
lasercolor = newcolor
qdel(W)
build_step++
user << "<span class='notice'>You add the armor to [src].</span>"
name = "vest/legs/frame assembly"
item_state = "[lasercolor]ed209_shell"
icon_state = "[lasercolor]ed209_shell"
if(3)
if(istype(W, /obj/item/weapon/weldingtool))
var/obj/item/weapon/weldingtool/WT = W
if(WT.remove_fuel(0,user))
build_step++
name = "shielded frame assembly"
user << "<span class='notice'>You weld the vest to [src].</span>"
if(4)
switch(lasercolor)
if("b")
if(!istype(W, /obj/item/clothing/head/helmet/bluetaghelm))
return
if("r")
if(!istype(W, /obj/item/clothing/head/helmet/redtaghelm))
return
if("")
if(!istype(W, /obj/item/clothing/head/helmet))
return
if(!user.unEquip(W))
return
qdel(W)
build_step++
user << "<span class='notice'>You add the helmet to [src].</span>"
name = "covered and shielded frame assembly"
item_state = "[lasercolor]ed209_hat"
icon_state = "[lasercolor]ed209_hat"
if(5)
if(isprox(W))
if(!user.unEquip(W))
return
qdel(W)
build_step++
user << "<span class='notice'>You add the prox sensor to [src].</span>"
name = "covered, shielded and sensored frame assembly"
item_state = "[lasercolor]ed209_prox"
icon_state = "[lasercolor]ed209_prox"
if(6)
if(istype(W, /obj/item/stack/cable_coil))
var/obj/item/stack/cable_coil/coil = W
if(coil.get_amount() < 1)
user << "<span class='warning'>You need one length of cable to wire the ED-209!</span>"
return
user << "<span class='notice'>You start to wire [src]...</span>"
if(do_after(user, 40, target = src))
if(coil.get_amount() >= 1 && build_step == 6)
coil.use(1)
build_step = 7
user << "<span class='notice'>You wire the ED-209 assembly.</span>"
name = "wired ED-209 assembly"
if(7)
var/newname = ""
switch(lasercolor)
if("b")
if(!istype(W, /obj/item/weapon/gun/energy/laser/bluetag))
return
newname = "bluetag ED-209 assembly"
if("r")
if(!istype(W, /obj/item/weapon/gun/energy/laser/redtag))
return
newname = "redtag ED-209 assembly"
if("")
if(!istype(W, /obj/item/weapon/gun/energy/advtaser))
return
newname = "taser ED-209 assembly"
else
return
if(!user.unEquip(W))
return
name = newname
build_step++
user << "<span class='notice'>You add [W] to [src].</span>"
item_state = "[lasercolor]ed209_taser"
icon_state = "[lasercolor]ed209_taser"
qdel(W)
if(8)
if(istype(W, /obj/item/weapon/screwdriver))
playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1)
user << "<span class='notice'>You start attaching the gun to the frame...</span>"
if(do_after(user, 40, target = src))
build_step++
name = "armed [name]"
user << "<span class='notice'>Taser gun attached.</span>"
if(9)
if(istype(W, /obj/item/weapon/stock_parts/cell))
if(!user.unEquip(W))
return
build_step++
user << "<span class='notice'>You complete the ED-209.</span>"
var/turf/T = get_turf(src)
new /mob/living/simple_animal/bot/ed209(T,created_name,lasercolor)
qdel(W)
user.unEquip(src, 1)
qdel(src)
//Floorbot assemblies
/obj/item/weapon/toolbox_tiles
desc = "It's a toolbox with tiles sticking out the top"
name = "tiles and toolbox"
icon = 'icons/obj/aibots.dmi'
icon_state = "toolbox_tiles"
force = 3
throwforce = 10
throw_speed = 2
throw_range = 5
w_class = 3
var/created_name = "Floorbot"
/obj/item/weapon/toolbox_tiles_sensor
desc = "It's a toolbox with tiles sticking out the top and a sensor attached"
name = "tiles, toolbox and sensor arrangement"
icon = 'icons/obj/aibots.dmi'
icon_state = "toolbox_tiles_sensor"
force = 3
throwforce = 10
throw_speed = 2
throw_range = 5
w_class = 3
var/created_name = "Floorbot"
/obj/item/weapon/storage/toolbox/mechanical/attackby(obj/item/stack/tile/plasteel/T, mob/user, params)
if(!istype(T, /obj/item/stack/tile/plasteel))
..()
return
if(contents.len >= 1)
user << "<span class='warning'>They won't fit in, as there is already stuff inside.</span>"
return
if(T.use(10))
if(user.s_active)
user.s_active.close(user)
var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles
user.put_in_hands(B)
user << "<span class='notice'>You add the tiles into the empty toolbox. They protrude from the top.</span>"
user.unEquip(src, 1)
qdel(src)
else
user << "<span class='warning'>You need 10 floor tiles to start building a floorbot.</span>"
return
/obj/item/weapon/toolbox_tiles/attackby(obj/item/W, mob/user, params)
..()
if(isprox(W))
qdel(W)
var/obj/item/weapon/toolbox_tiles_sensor/B = new /obj/item/weapon/toolbox_tiles_sensor()
B.created_name = created_name
user.put_in_hands(B)
user << "<span class='notice'>You add the sensor to the toolbox and tiles.</span>"
user.unEquip(src, 1)
qdel(src)
else if(istype(W, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
/obj/item/weapon/toolbox_tiles_sensor/attackby(obj/item/W, mob/user, params)
..()
if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm))
qdel(W)
var/turf/T = get_turf(user.loc)
var/mob/living/simple_animal/bot/floorbot/A = new /mob/living/simple_animal/bot/floorbot(T)
A.name = created_name
user << "<span class='notice'>You add the robot arm to the odd looking toolbox assembly. Boop beep!</span>"
user.unEquip(src, 1)
qdel(src)
else if(istype(W, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
//Medbot Assembly
/obj/item/weapon/firstaid_arm_assembly
name = "incomplete medibot assembly."
desc = "A first aid kit with a robot arm permanently grafted to it."
icon = 'icons/obj/aibots.dmi'
icon_state = "firstaid_arm"
var/build_step = 0
var/created_name = "Medibot" //To preserve the name if it's a unique medbot I guess
var/skin = null //Same as medbot, set to tox or ointment for the respective kits.
w_class = 3
var/treatment_brute = "salglu_solution"
var/treatment_oxy = "salbutamol"
var/treatment_fire = "salglu_solution"
var/treatment_tox = "charcoal"
var/treatment_virus = "spaceacillin"
req_one_access = list(access_medical, access_robotics)
/obj/item/weapon/firstaid_arm_assembly/New()
..()
spawn(5)
if(skin)
overlays += image('icons/obj/aibots.dmi', "kit_skin_[skin]")
/obj/item/weapon/storage/firstaid/attackby(obj/item/robot_parts/S, mob/user, params)
if((!istype(S, /obj/item/robot_parts/l_arm)) && (!istype(S, /obj/item/robot_parts/r_arm)))
..()
return
//Making a medibot!
if(contents.len >= 1)
user << "<span class='warning'>You need to empty [src] out first!</span>"
return
var/obj/item/weapon/firstaid_arm_assembly/A = new /obj/item/weapon/firstaid_arm_assembly
if(istype(src,/obj/item/weapon/storage/firstaid/fire))
A.skin = "ointment"
else if(istype(src,/obj/item/weapon/storage/firstaid/toxin))
A.skin = "tox"
else if(istype(src,/obj/item/weapon/storage/firstaid/o2))
A.skin = "o2"
else if(istype(src,/obj/item/weapon/storage/firstaid/brute))
A.skin = "brute"
else if(istype(src,/obj/item/weapon/storage/firstaid/adv))
A.skin = "adv"
else if(istype(src,/obj/item/weapon/storage/firstaid/tactical))
A.skin = "bezerk"
else if(istype(src,/obj/item/weapon/storage/firstaid/aquatic_kit))
A.skin = "fish"
A.req_one_access = req_one_access
A.treatment_oxy = treatment_oxy
A.treatment_brute = treatment_brute
A.treatment_fire = treatment_fire
A.treatment_tox = treatment_tox
A.treatment_virus = treatment_virus
qdel(S)
user.put_in_hands(A)
user << "<span class='notice'>You add the robot arm to the first aid kit.</span>"
user.unEquip(src, 1)
qdel(src)
/obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W, mob/user, params)
..()
if(istype(W, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
else
switch(build_step)
if(0)
if(istype(W, /obj/item/device/healthanalyzer))
if(!user.unEquip(W))
return
qdel(W)
build_step++
user << "<span class='notice'>You add the health sensor to [src].</span>"
name = "First aid/robot arm/health analyzer assembly"
overlays += image('icons/obj/aibots.dmi', "na_scanner")
if(1)
if(isprox(W))
if(!user.unEquip(W))
return
qdel(W)
build_step++
user << "<span class='notice'>You complete the Medibot. Beep boop!</span>"
var/turf/T = get_turf(src)
var/mob/living/simple_animal/bot/medbot/S = new /mob/living/simple_animal/bot/medbot(T)
S.skin = skin
S.name = created_name
S.bot_core.req_one_access = req_one_access
S.treatment_oxy = treatment_oxy
S.treatment_brute = treatment_brute
S.treatment_fire = treatment_fire
S.treatment_tox = treatment_tox
S.treatment_virus = treatment_virus
user.unEquip(src, 1)
qdel(src)
//Secbot Assembly
/obj/item/weapon/secbot_assembly
name = "incomplete securitron assembly"
desc = "Some sort of bizarre assembly made from a proximity sensor, helmet, and signaler."
icon = 'icons/obj/aibots.dmi'
icon_state = "helmet_signaler"
item_state = "helmet"
var/build_step = 0
var/created_name = "Securitron" //To preserve the name if it's a unique securitron I guess
/obj/item/clothing/head/helmet/attackby(obj/item/device/assembly/signaler/S, mob/user, params)
..()
if(!issignaler(S))
..()
return
if(!S.secured)
qdel(S)
var/obj/item/weapon/secbot_assembly/A = new /obj/item/weapon/secbot_assembly
user.put_in_hands(A)
user << "<span class='notice'>You add the signaler to the helmet.</span>"
user.unEquip(src, 1)
qdel(src)
else
return
/obj/item/weapon/secbot_assembly/attackby(obj/item/I, mob/user, params)
..()
if(istype(I, /obj/item/weapon/weldingtool))
if(!build_step)
var/obj/item/weapon/weldingtool/WT = I
if(WT.remove_fuel(0, user))
build_step++
overlays += "hs_hole"
user << "<span class='notice'>You weld a hole in [src]!</span>"
else if(build_step == 1)
var/obj/item/weapon/weldingtool/WT = I
if(WT.remove_fuel(0, user))
build_step--
overlays -= "hs_hole"
user << "<span class='notice'>You weld the hole in [src] shut!</span>"
else if(isprox(I) && (build_step == 1))
if(!user.unEquip(I))
return
build_step++
user << "<span class='notice'>You add the prox sensor to [src]!</span>"
overlays += "hs_eye"
name = "helmet/signaler/prox sensor assembly"
qdel(I)
else if(((istype(I, /obj/item/robot_parts/l_arm)) || (istype(I, /obj/item/robot_parts/r_arm))) && (build_step == 2))
if(!user.unEquip(I))
return
build_step++
user << "<span class='notice'>You add the robot arm to [src]!</span>"
name = "helmet/signaler/prox sensor/robot arm assembly"
overlays += "hs_arm"
qdel(I)
else if((istype(I, /obj/item/weapon/melee/baton)) && (build_step >= 3))
if(!user.unEquip(I))
return
build_step++
user << "<span class='notice'>You complete the Securitron! Beep boop.</span>"
var/mob/living/simple_animal/bot/secbot/S = new /mob/living/simple_animal/bot/secbot
S.forceMove(get_turf(src))
S.name = created_name
qdel(I)
qdel(src)
else if(istype(I, /obj/item/weapon/pen))
var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN)
if(!t)
return
if(!in_range(src, usr) && loc != usr)
return
created_name = t
else if(istype(I, /obj/item/weapon/screwdriver))
if(!build_step)
new /obj/item/device/assembly/signaler(get_turf(src))
new /obj/item/clothing/head/helmet(get_turf(src))
user << "<span class='notice'>You disconnect the signaler from the helmet.</span>"
qdel(src)
else if(build_step == 2)
overlays -= "hs_eye"
new /obj/item/device/assembly/prox_sensor(get_turf(src))
user << "<span class='notice'>You detach the proximity sensor from [src].</span>"
build_step--
else if(build_step == 3)
overlays -= "hs_arm"
new /obj/item/robot_parts/l_arm(get_turf(src))
user << "<span class='notice'>You remove the robot arm from [src].</span>"
build_step--
@@ -0,0 +1,569 @@
/mob/living/simple_animal/bot/ed209
name = "\improper ED-209 Security Robot"
desc = "A security robot. He looks less than thrilled."
icon = 'icons/obj/aibots.dmi'
icon_state = "ed2090"
density = 1
anchored = 0
health = 100
maxHealth = 100
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
environment_smash = 2 //Walls can't stop THE LAW
radio_channel = "Security"
bot_type = SEC_BOT
bot_filter = RADIO_SECBOT
model = "ED-209"
bot_purpose = "seek out criminals, handcuff them, and report their location to security"
bot_core = /obj/machinery/bot_core/secbot
window_id = "autoed209"
window_name = "Automatic Security Unit v2.6"
var/lastfired = 0
var/shot_delay = 3 //.3 seconds between shots
var/lasercolor = ""
var/disabled = 0//A holder for if it needs to be disabled, if true it will not seach for targets, shoot at targets, or move, currently only used for lasertag
var/mob/living/carbon/target
var/oldtarget_name
var/threatlevel = 0
var/target_lastloc //Loc of target when arrested.
var/last_found //There's a delay
var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds?
var/idcheck = 1 //If true, arrest people with no IDs
var/weaponscheck = 1 //If true, arrest people for weapons if they don't have access
var/check_records = 1 //Does it check security records?
var/arrest_type = 0 //If true, don't handcuff
var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type
var/shoot_sound = 'sound/weapons/Taser.ogg'
/mob/living/simple_animal/bot/ed209/New(loc,created_name,created_lasercolor)
..()
if(created_name)
name = created_name
if(created_lasercolor)
lasercolor = created_lasercolor
icon_state = "[lasercolor]ed209[on]"
set_weapon() //giving it the right projectile and firing sound.
spawn(3)
var/datum/job/detective/J = new/datum/job/detective
access_card.access += J.get_access()
prev_access = access_card.access
if(lasercolor)
shot_delay = 6//Longer shot delay because JESUS CHRIST
check_records = 0//Don't actively target people set to arrest
arrest_type = 1//Don't even try to cuff
bot_core.req_access = list(access_maint_tunnels, access_theatre)
arrest_type = 1
if((lasercolor == "b") && (name == "\improper ED-209 Security Robot"))//Picks a name if there isn't already a custome one
name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT")
if((lasercolor == "r") && (name == "\improper ED-209 Security Robot"))
name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT")
//SECHUD
var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED]
secsensor.add_hud_to(src)
permanent_huds |= secsensor
/mob/living/simple_animal/bot/ed209/turn_on()
. = ..()
icon_state = "[lasercolor]ed209[on]"
mode = BOT_IDLE
/mob/living/simple_animal/bot/ed209/turn_off()
..()
icon_state = "[lasercolor]ed209[on]"
/mob/living/simple_animal/bot/ed209/bot_reset()
..()
target = null
oldtarget_name = null
anchored = 0
walk_to(src,0)
last_found = world.time
set_weapon()
/mob/living/simple_animal/bot/ed209/set_custom_texts()
text_hack = "You disable [name]'s combat inhibitor."
text_dehack = "You restore [name]'s combat inhibitor."
text_dehack_fail = "[name] ignores your attempts to restrict him!"
/mob/living/simple_animal/bot/ed209/get_controls(mob/user)
var/dat
dat += hack(user)
dat += showpai(user)
dat += text({"
<TT><B>Security Unit v2.6 controls</B></TT><BR><BR>
Status: []<BR>
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
Maintenance panel panel is [open ? "opened" : "closed"]<BR>"},
"<A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A>" )
if(!locked || issilicon(user) || check_rights(R_ADMIN, 0, user))
if(!lasercolor)
dat += text({"<BR>
Arrest Unidentifiable Persons: []<BR>
Arrest for Unauthorized Weapons: []<BR>
Arrest for Warrant: []<BR>
<BR>
Operating Mode: []<BR>
Report Arrests[]<BR>
Auto Patrol[]"},
"<A href='?src=\ref[src];operation=idcheck'>[idcheck ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=weaponscheck'>[weaponscheck ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=ignorerec'>[check_records ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=switchmode'>[arrest_type ? "Detain" : "Arrest"]</A>",
"<A href='?src=\ref[src];operation=declarearrests'>[declare_arrests ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>" )
return dat
/mob/living/simple_animal/bot/ed209/Topic(href, href_list)
if(lasercolor && (istype(usr,/mob/living/carbon/human)))
var/mob/living/carbon/human/H = usr
if((lasercolor == "b") && (istype(H.wear_suit, /obj/item/clothing/suit/redtag)))//Opposing team cannot operate it
return
else if((lasercolor == "r") && (istype(H.wear_suit, /obj/item/clothing/suit/bluetag)))
return
if(..())
return 1
switch(href_list["operation"])
if("idcheck")
idcheck = !idcheck
update_controls()
if("weaponscheck")
weaponscheck = !weaponscheck
update_controls()
if("ignorerec")
check_records = !check_records
update_controls()
if("switchmode")
arrest_type = !arrest_type
update_controls()
if("declarearrests")
declare_arrests = !declare_arrests
update_controls()
/mob/living/simple_animal/bot/ed209/proc/retaliate(mob/living/carbon/human/H)
threatlevel = H.assess_threat(src)
threatlevel += 6
if(threatlevel >= 4)
target = H
mode = BOT_HUNT
/mob/living/simple_animal/bot/ed209/attack_hand(mob/living/carbon/human/H)
if(H.a_intent == "harm")
retaliate(H)
return ..()
/mob/living/simple_animal/bot/ed209/attackby(obj/item/weapon/W, mob/user, params)
..()
if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry.
return
if(!istype(W, /obj/item/weapon/screwdriver) && (!target)) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass.
if(W.force && W.damtype != STAMINA)//If force is non-zero and damage type isn't stamina.
retaliate(user)
if(lasercolor)//To make up for the fact that lasertag bots don't hunt
shootAt(user)
/mob/living/simple_animal/bot/ed209/emag_act(mob/user)
..()
if(emagged == 2)
if(user)
user << "<span class='warning'>You short out [src]'s target assessment circuits.</span>"
oldtarget_name = user.name
audible_message("<span class='danger'>[src] buzzes oddly!</span>")
declare_arrests = 0
icon_state = "[lasercolor]ed209[on]"
set_weapon()
/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj)
if(istype(Proj ,/obj/item/projectile/beam)||istype(Proj,/obj/item/projectile/bullet))
if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
if(!Proj.nodamage && Proj.damage < src.health)
retaliate(Proj.firer)
..()
/mob/living/simple_animal/bot/ed209/handle_automated_action()
if(!..())
return
if(disabled)
return
var/list/targets = list()
for(var/mob/living/carbon/C in view(7, src)) //Let's find us a target
var/threatlevel = 0
if((C.stat) || (C.lying))
continue
threatlevel = C.assess_threat(src, lasercolor)
//speak(C.real_name + text(": threat: []", threatlevel))
if(threatlevel < 4)
continue
var/dst = get_dist(src, C)
if(dst <= 1 || dst > 7)
continue
targets += C
if(targets.len>0)
var/mob/living/carbon/t = pick(targets)
if((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down.
shootAt(t)
switch(mode)
if(BOT_IDLE) // idle
walk_to(src,0)
if(!lasercolor) //lasertag bots don't want to arrest anyone
look_for_perp() // see if any criminals are in range
if(!mode && auto_patrol) // still idle, and set to patrol
mode = BOT_START_PATROL // switch to patrol mode
if(BOT_HUNT) // hunting for perp
// if can't reach perp for long enough, go idle
if(frustration >= 8)
walk_to(src,0)
back_to_idle()
if(target) // make sure target exists
if(Adjacent(target) && isturf(target.loc)) // if right next to perp
stun_attack(target)
mode = BOT_PREP_ARREST
anchored = 1
target_lastloc = target.loc
return
else // not next to perp
var/turf/olddist = get_dist(src, target)
walk_to(src, target,1,4)
if((get_dist(src, target)) >= (olddist))
frustration++
else
frustration = 0
else
back_to_idle()
if(BOT_PREP_ARREST) // preparing to arrest target
// see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again.
if(!Adjacent(target) || !isturf(target.loc) || target.weakened < 2)
back_to_hunt()
return
if(iscarbon(target) && target.canBeHandcuffed())
if(!arrest_type)
if(!target.handcuffed) //he's not cuffed? Try to cuff him!
cuff(target)
else
back_to_idle()
return
else
back_to_idle()
return
if(BOT_ARREST)
if(!target)
anchored = 0
mode = BOT_IDLE
last_found = world.time
frustration = 0
return
if(target.handcuffed) //no target or target cuffed? back to idle.
back_to_idle()
return
if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2)) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again.
back_to_hunt()
return
else
mode = BOT_PREP_ARREST
anchored = 0
if(BOT_START_PATROL)
look_for_perp()
start_patrol()
if(BOT_PATROL)
look_for_perp()
bot_patrol()
return
/mob/living/simple_animal/bot/ed209/proc/back_to_idle()
anchored = 0
mode = BOT_IDLE
target = null
last_found = world.time
frustration = 0
spawn(0)
handle_automated_action() //ensure bot quickly responds
/mob/living/simple_animal/bot/ed209/proc/back_to_hunt()
anchored = 0
frustration = 0
mode = BOT_HUNT
spawn(0)
handle_automated_action() //ensure bot quickly responds
// look for a criminal in view of the bot
/mob/living/simple_animal/bot/ed209/proc/look_for_perp()
if(disabled)
return
anchored = 0
threatlevel = 0
for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
if((C.stat) || (C.handcuffed))
continue
if((C.name == oldtarget_name) && (world.time < last_found + 100))
continue
threatlevel = C.assess_threat(src, lasercolor)
if(!threatlevel)
continue
else if(threatlevel >= 4)
target = C
oldtarget_name = C.name
speak("Level [threatlevel] infraction alert!")
playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/EDPlaceholder.ogg'), 50, 0)
visible_message("<b>[src]</b> points at [C.name]!")
mode = BOT_HUNT
spawn(0)
handle_automated_action() // ensure bot quickly responds to a perp
break
else
continue
/mob/living/simple_animal/bot/ed209/proc/check_for_weapons(var/obj/item/slot_item)
if(slot_item && slot_item.needs_permit)
return 1
return 0
/mob/living/simple_animal/bot/ed209/explode()
walk_to(src,0)
visible_message("<span class='userdanger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
var/obj/item/weapon/ed209_assembly/Sa = new /obj/item/weapon/ed209_assembly(Tsec)
Sa.build_step = 1
Sa.overlays += image('icons/obj/aibots.dmi', "hs_hole")
Sa.created_name = name
new /obj/item/device/assembly/prox_sensor(Tsec)
if(!lasercolor)
var/obj/item/weapon/gun/energy/advtaser/G = new /obj/item/weapon/gun/energy/advtaser(Tsec)
G.power_supply.charge = 0
G.update_icon()
else if(lasercolor == "b")
var/obj/item/weapon/gun/energy/laser/bluetag/G = new /obj/item/weapon/gun/energy/laser/bluetag(Tsec)
G.power_supply.charge = 0
G.update_icon()
else if(lasercolor == "r")
var/obj/item/weapon/gun/energy/laser/redtag/G = new /obj/item/weapon/gun/energy/laser/redtag(Tsec)
G.power_supply.charge = 0
G.update_icon()
if(prob(50))
new /obj/item/robot_parts/l_leg(Tsec)
if(prob(25))
new /obj/item/robot_parts/r_leg(Tsec)
if(prob(25))//50% chance for a helmet OR vest
if(prob(50))
new /obj/item/clothing/head/helmet(Tsec)
else
if(!lasercolor)
new /obj/item/clothing/suit/armor/vest(Tsec)
if(lasercolor == "b")
new /obj/item/clothing/suit/bluetag(Tsec)
if(lasercolor == "r")
new /obj/item/clothing/suit/redtag(Tsec)
var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
new /obj/effect/decal/cleanable/blood/oil(loc)
..()
/mob/living/simple_animal/bot/ed209/proc/set_weapon() //used to update the projectile type and firing sound
shoot_sound = 'sound/weapons/laser.ogg'
if(emagged == 2)
if(lasercolor)
projectile = /obj/item/projectile/lasertag
else
projectile = /obj/item/projectile/beam
else
if(!lasercolor)
shoot_sound = 'sound/weapons/Taser.ogg'
projectile = /obj/item/projectile/energy/electrode
else if(lasercolor == "b")
projectile = /obj/item/projectile/lasertag/blue
else if(lasercolor == "r")
projectile = /obj/item/projectile/lasertag/red
/mob/living/simple_animal/bot/ed209/proc/shootAt(mob/target)
if(lastfired && world.time - lastfired < shot_delay)
return
lastfired = world.time
var/turf/T = loc
var/atom/U = (istype(target, /atom/movable) ? target.loc : target)
if((!( U ) || !( T )))
return
while(!(istype(U, /turf)))
U = U.loc
if(!(istype(T, /turf)))
return
if(!projectile)
return
if(!(istype(U, /turf)))
return
var/obj/item/projectile/A = new projectile(loc)
playsound(loc, shoot_sound, 50, 1)
A.current = U
A.yo = U.y - T.y
A.xo = U.x - T.x
spawn(0)
A.process()
/mob/living/simple_animal/bot/ed209/attack_alien(mob/living/carbon/alien/user)
..()
if(!isalien(target))
target = user
mode = BOT_HUNT
/mob/living/simple_animal/bot/ed209/emp_act(severity)
if(severity==2 && prob(70))
..(severity-1)
else
var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( loc )
pulse2.icon = 'icons/effects/effects.dmi'
pulse2.icon_state = "empdisable"
pulse2.name = "emp sparks"
pulse2.anchored = 1
pulse2.dir = pick(cardinal)
spawn(10)
qdel(pulse2)
var/list/mob/living/carbon/targets = new
for(var/mob/living/carbon/C in view(12,src))
if(C.stat==2)
continue
targets += C
if(targets.len)
if(prob(50))
var/mob/toshoot = pick(targets)
if(toshoot)
targets-=toshoot
if(prob(50) && emagged < 2)
emagged = 2
set_weapon()
shootAt(toshoot)
emagged = 0
set_weapon()
else
shootAt(toshoot)
else if(prob(50))
if(targets.len)
var/mob/toarrest = pick(targets)
if(toarrest)
target = toarrest
mode = BOT_HUNT
/mob/living/simple_animal/bot/ed209/bullet_act(obj/item/projectile/Proj)
if(!disabled)
var/lasertag_check = 0
if((lasercolor == "b"))
if(istype(Proj, /obj/item/projectile/lasertag/red))
lasertag_check++
else if((lasercolor == "r"))
if(istype(Proj, /obj/item/projectile/lasertag/blue))
lasertag_check++
if(lasertag_check)
icon_state = "[lasercolor]ed2090"
disabled = 1
target = null
spawn(100)
disabled = 0
icon_state = "[lasercolor]ed2091"
return 1
else
..(Proj)
else
..(Proj)
/mob/living/simple_animal/bot/ed209/bluetag
lasercolor = "b"
/mob/living/simple_animal/bot/ed209/redtag
lasercolor = "r"
/mob/living/simple_animal/bot/ed209/UnarmedAttack(atom/A)
if(!on)
return
if(iscarbon(A))
var/mob/living/carbon/C = A
if(!C.stunned || arrest_type)
stun_attack(A)
else if(C.canBeHandcuffed() && !C.handcuffed)
cuff(A)
else
..()
/mob/living/simple_animal/bot/ed209/RangedAttack(atom/A)
if(!on)
return
shootAt(A)
/mob/living/simple_animal/bot/ed209/proc/stun_attack(mob/living/carbon/C)
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
icon_state = "[lasercolor]ed209-c"
spawn(2)
icon_state = "[lasercolor]ed209[on]"
var/threat = 5
if(istype(C, /mob/living/carbon/human))
C.stuttering = 5
C.Stun(5)
C.Weaken(5)
var/mob/living/carbon/human/H = C
threat = H.assess_threat(src)
else
C.Weaken(5)
C.stuttering = 5
C.Stun(5)
add_logs(src,C,"stunned")
if(declare_arrests)
var/area/location = get_area(src)
speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag <b>[C]</b> in [location].", radio_channel)
C.visible_message("<span class='danger'>[src] has stunned [C]!</span>",\
"<span class='userdanger'>[src] has stunned you!</span>")
/mob/living/simple_animal/bot/ed209/proc/cuff(mob/living/carbon/C)
mode = BOT_ARREST
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
C.visible_message("<span class='danger'>[src] is trying to put zipties on [C]!</span>",\
"<span class='userdanger'>[src] is trying to put zipties on you!</span>")
spawn(60)
if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
return
if(!C.handcuffed)
C.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(C)
C.update_inv_handcuffed(1)
back_to_idle()
@@ -0,0 +1,119 @@
/mob/living/simple_animal/bot/emote(var/act, var/m_type=1, var/message = null)
var/param = null
if (findtext(act, "-", 1, null))
var/t1 = findtext(act, "-", 1, null)
param = copytext(act, t1 + 1, length(act) + 1)
act = copytext(act, 1, t1)
if(findtext(act,"s",-1) && !findtext(act,"_",-2))//Removes ending s's unless they are prefixed with a '_'
act = copytext(act,1,length(act))
//Emote Cooldown System (it's so simple!)
// proc/handle_emote_CD() located in [code\modules\mob\emote.dm]
var/on_CD = 0
switch(act)
//Cooldown-inducing emotes
if("scream", "screams")
on_CD = handle_emote_CD(50) //longer cooldown
if("ping","buzz","beep","yes","no") //halt is exempt because it's used to stop criminal scum //WHOEVER THOUGHT THAT WAS A GOOD IDEA IS GOING TO GET SHOT.
on_CD = handle_emote_CD() //proc located in code\modules\mob\emote.dm
//Everything else, including typos of the above emotes
else
on_CD = 0 //If it doesn't induce the cooldown, we won't check for the cooldown
if(on_CD == 1) // Check if we need to suppress the emote attempt.
return // Suppress emote, you're still cooling off.
//--FalseIncarnate
switch(act)
if("ping")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<B>[src]</B> pings at [param]."
else
message = "<B>[src]</B> pings."
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
m_type = 2
if("buzz")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<B>[src]</B> buzzes at [param]."
else
message = "<B>[src]</B> buzzes."
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
m_type = 2
if("beep")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<B>[src]</B> beeps at [param]."
else
message = "<B>[src]</B> beeps."
playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0)
m_type = 2
if("yes")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<B>[src]</B> emits an affirmative blip at [param]."
else
message = "<B>[src]</B> emits an affirmative blip."
playsound(src.loc, 'sound/machines/synth_yes.ogg', 50, 0)
m_type = 2
if("no")
var/M = null
if(param)
for (var/mob/A in view(null, null))
if (param == A.name)
M = A
break
if(!M)
param = null
if (param)
message = "<B>[src]</B> emits a negative blip at [param]."
else
message = "<B>[src]</B> emits a negative blip."
playsound(src.loc, 'sound/machines/synth_no.ogg', 50, 0)
m_type = 2
if("scream", "screams")
message = "<B>[src]</B> screams!"
playsound(src.loc, 'sound/goonstation/voice/robot_scream.ogg', 80, 0)
m_type = 2
..(act, m_type, message)
@@ -0,0 +1,425 @@
//Floorbot
/mob/living/simple_animal/bot/floorbot
name = "\improper Floorbot"
desc = "A little floor repairing robot, he looks so excited!"
icon = 'icons/obj/aibots.dmi'
icon_state = "floorbot0"
density = 0
anchored = 0
health = 25
maxHealth = 25
radio_channel = "Engineering"
bot_type = FLOOR_BOT
bot_filter = RADIO_FLOORBOT
model = "Floorbot"
bot_purpose = "seek out damaged or missing floor tiles, and repair or replace them as necessary"
bot_core = /obj/machinery/bot_core/floorbot
window_id = "autofloor"
window_name = "Automatic Station Floor Repairer v1.1"
var/process_type //Determines what to do when process_scan() recieves a target. See process_scan() for details.
var/targetdirection
var/amount = 10
var/replacetiles = 0
var/eattiles = 0
var/maketiles = 0
var/fixfloors = 0
var/autotile = 0
var/nag_on_empty = 1
var/nagged = 0 //Prevents the Floorbot nagging more than once per refill.
var/max_targets = 50
var/turf/target
var/oldloc = null
#define HULL_BREACH 1
#define BRIDGE_MODE 2
#define FIX_TILE 3
#define AUTO_TILE 4
#define REPLACE_TILE 5
#define TILE_EMAG 6
/mob/living/simple_animal/bot/floorbot/New()
..()
update_icon()
var/datum/job/engineer/J = new/datum/job/engineer
access_card.access += J.get_access()
prev_access = access_card.access
/mob/living/simple_animal/bot/floorbot/bot_reset()
..()
target = null
oldloc = null
ignore_list = list()
nagged = 0
anchored = 0
update_icon()
/mob/living/simple_animal/bot/floorbot/set_custom_texts()
text_hack = "You corrupt [name]'s construction protocols."
text_dehack = "You detect errors in [name] and reset his programming."
text_dehack_fail = "[name] is not responding to reset commands!"
/mob/living/simple_animal/bot/floorbot/get_controls(mob/user)
var/dat
dat += hack(user)
dat += showpai(user)
dat += "<TT><B>Floor Repairer Controls v1.1</B></TT><BR><BR>"
dat += "Status: <A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A><BR>"
dat += "Maintenance panel panel is [open ? "opened" : "closed"]<BR>"
dat += "Tiles left: [amount]<BR>"
dat += "Behvaiour controls are [locked ? "locked" : "unlocked"]<BR>"
if(!locked || issilicon(user) || check_rights(R_ADMIN, 0, user))
dat += "Add tiles to new hull plating: <A href='?src=\ref[src];operation=autotile'>[autotile ? "Yes" : "No"]</A><BR>"
dat += "Replace floor tiles: <A href='?src=\ref[src];operation=replace'>[replacetiles ? "Yes" : "No"]</A><BR>"
dat += "Finds tiles: <A href='?src=\ref[src];operation=tiles'>[eattiles ? "Yes" : "No"]</A><BR>"
dat += "Make pieces of metal into tiles when empty: <A href='?src=\ref[src];operation=make'>[maketiles ? "Yes" : "No"]</A><BR>"
dat += "Transmit notice when empty: <A href='?src=\ref[src];operation=emptynag'>[nag_on_empty ? "Yes" : "No"]</A><BR>"
dat += "Repair damaged tiles and platings: <A href='?src=\ref[src];operation=fix'>[fixfloors ? "Yes" : "No"]</A><BR>"
dat += "Traction Magnets: <A href='?src=\ref[src];operation=anchor'>[anchored ? "Engaged" : "Disengaged"]</A><BR>"
dat += "Patrol Station: <A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "Yes" : "No"]</A><BR>"
var/bmode
if(targetdirection)
bmode = dir2text(targetdirection)
else
bmode = "disabled"
dat += "Bridge Mode : <A href='?src=\ref[src];operation=bridgemode'>[bmode]</A><BR>"
return dat
/mob/living/simple_animal/bot/floorbot/attackby(obj/item/W , mob/user, params)
if(istype(W, /obj/item/stack/tile/plasteel))
var/obj/item/stack/tile/plasteel/T = W
if(amount >= 50)
return
var/loaded = min(50-amount, T.amount)
T.use(loaded)
amount += loaded
if(loaded > 0)
user << "<span class='notice'>You load [loaded] tiles into the floorbot. He now contains [amount] tiles.</span>"
nagged = 0
update_icon()
else
user << "<span class='warning'>You need at least one floor tile to put into [src]!</span>"
else
..()
/mob/living/simple_animal/bot/floorbot/emag_act(mob/user)
..()
if(emagged == 2)
if(user)
user << "<span class='danger'>[src] buzzes and beeps.</span>"
/mob/living/simple_animal/bot/floorbot/Topic(href, href_list)
if(..())
return 1
switch(href_list["operation"])
if("replace")
replacetiles = !replacetiles
if("tiles")
eattiles = !eattiles
if("make")
maketiles = !maketiles
if("fix")
fixfloors = !fixfloors
if("autotile")
autotile = !autotile
if("emptynag")
nag_on_empty = !nag_on_empty
if("anchor")
anchored = !anchored
if("bridgemode")
var/setdir = input("Select construction direction:") as null|anything in list("north","east","south","west","disable")
switch(setdir)
if("north")
targetdirection = 1
if("south")
targetdirection = 2
if("east")
targetdirection = 4
if("west")
targetdirection = 8
if("disable")
targetdirection = null
update_controls()
/mob/living/simple_animal/bot/floorbot/handle_automated_action()
if(!..())
return
if(mode == BOT_REPAIRING)
return
if(amount <= 0 && !target) //Out of tiles! We must refill!
if(eattiles) //Configured to find and consume floortiles!
target = scan(/obj/item/stack/tile/plasteel)
process_type = null
if(!target && maketiles) //We did not manage to find any floor tiles! Scan for metal stacks and make our own!
target = scan(/obj/item/stack/sheet/metal)
process_type = null
return
else
if(nag_on_empty) //Floorbot is empty and cannot acquire more tiles, nag the engineers for more!
nag()
if(prob(5))
audible_message("[src] makes an excited booping beeping sound!")
//Normal scanning procedure. We have tiles loaded, are not emagged.
if(!target && emagged < 2 && amount > 0)
if(targetdirection != null) //The bot is in bridge mode.
//Try to find a space tile immediately in our selected direction.
var/turf/T = get_step(src, targetdirection)
if(istype(T, /turf/space))
target = T
else //Find a space tile farther way!
target = scan(/turf/space)
process_type = BRIDGE_MODE
if(!target)
process_type = HULL_BREACH //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones.
target = scan(/turf/space)
if(!target && replacetiles) //Finds a floor without a tile and gives it one.
process_type = REPLACE_TILE //The target must be the floor and not a tile. The floor must not already have a floortile.
target = scan(/turf/simulated/floor)
if(!target && fixfloors) //Repairs damaged floors and tiles.
process_type = FIX_TILE
target = scan(/turf/simulated/floor)
if(!target && emagged == 2) //We are emagged! Time to rip up the floors!
process_type = TILE_EMAG
target = scan(/turf/simulated/floor)
if(!target)
if(auto_patrol)
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
start_patrol()
if(mode == BOT_PATROL)
bot_patrol()
if(target)
if(path.len == 0)
if(!istype(target, /turf/))
var/turf/TL = get_turf(target)
path = get_path_to(src, TL, /turf/proc/Distance_cardinal, 0, 30, id=access_card,simulated_only = 0)
else
path = get_path_to(src, target, /turf/proc/Distance_cardinal, 0, 30, id=access_card,simulated_only = 0)
if(!bot_move(target))
add_to_ignore(target)
target = null
mode = BOT_IDLE
return
else if( !bot_move(target) )
target = null
mode = BOT_IDLE
return
if(loc == target || loc == target.loc)
if(istype(target, /obj/item/stack/tile/plasteel))
eattile(target)
else if(istype(target, /obj/item/stack/sheet/metal))
maketile(target)
else if(istype(target, /turf/) && emagged < 2)
repair(target)
else if(emagged == 2 && istype(target,/turf/simulated/floor))
var/turf/simulated/floor/F = target
anchored = 1
mode = BOT_REPAIRING
if(prob(90))
F.break_tile_to_plating()
else
F.ReplaceWithLattice()
audible_message("<span class='danger'>[src] makes an excited booping sound.</span>")
spawn(50)
amount ++
anchored = 0
mode = BOT_IDLE
target = null
path = list()
return
oldloc = loc
/mob/living/simple_animal/bot/floorbot/proc/nag() //Annoy everyone on the channel to refill us!
if(!nagged)
speak("Requesting refill at <b>[get_area(src)]</b>!", radio_channel)
nagged = 1
/mob/living/simple_animal/bot/floorbot/proc/is_hull_breach(turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas.
var/area/t_area = get_area(t)
if(t_area && (t_area.name == "Space" || findtext(t_area.name, "huttle")))
return 0
else
return 1
//Floorbots, having several functions, need sort out special conditions here.
/mob/living/simple_animal/bot/floorbot/process_scan(scan_target)
var/result
var/turf/simulated/floor/F
switch(process_type)
if(HULL_BREACH) //The most common job, patching breaches in the station's hull.
if(is_hull_breach(scan_target)) //Ensure that the targeted space turf is actually part of the station, and not random space.
result = scan_target
anchored = 1 //Prevent the floorbot being blown off-course while trying to reach a hull breach.
if(BRIDGE_MODE) //Only space turfs in our chosen direction are considered.
if(get_dir(src, scan_target) == targetdirection)
result = scan_target
anchored = 1
if(REPLACE_TILE)
F = scan_target
if(istype(F, /turf/simulated/floor/plating)) //The floor must not already have a tile.
result = F
if(FIX_TILE) //Selects only damaged floors.
F = scan_target
if(istype(F) && (F.broken || F.burnt))
result = F
if(TILE_EMAG) //Emag mode! Rip up the floor and cause breaches to space!
F = scan_target
if(!istype(F, /turf/simulated/floor/plating))
result = F
else //If no special processing is needed, simply return the result.
result = scan_target
return result
/mob/living/simple_animal/bot/floorbot/proc/repair(turf/target_turf)
if(istype(target_turf, /turf/space/))
//Must be a hull breach or in bridge mode to continue.
if(!is_hull_breach(target_turf) && !targetdirection)
target = null
return
else if(!istype(target_turf, /turf/simulated/floor))
return
if(amount <= 0)
mode = BOT_IDLE
target = null
return
anchored = 1
icon_state = "floorbot-c"
if(istype(target_turf, /turf/space/)) //If we are fixing an area not part of pure space, it is
visible_message("<span class='notice'>[targetdirection ? "[src] begins installing a bridge plating." : "[src] begins to repair the hole."] </span>")
mode = BOT_REPAIRING
spawn(50)
if(mode == BOT_REPAIRING)
if(autotile) //Build the floor and include a tile.
target_turf.ChangeTurf(/turf/simulated/floor/plasteel)
else //Build a hull plating without a floor tile.
target_turf.ChangeTurf(/turf/simulated/floor/plating)
mode = BOT_IDLE
amount -= 1
update_icon()
anchored = 0
target = null
else
var/turf/simulated/floor/F = target_turf
mode = BOT_REPAIRING
visible_message("<span class='notice'>[src] begins repairing the floor.</span>")
spawn(50)
if(mode == BOT_REPAIRING)
F.broken = 0
F.burnt = 0
F.ChangeTurf(/turf/simulated/floor/plasteel)
mode = BOT_IDLE
amount -= 1
update_icon()
anchored = 0
target = null
/mob/living/simple_animal/bot/floorbot/proc/eattile(obj/item/stack/tile/plasteel/T)
if(!istype(T, /obj/item/stack/tile/plasteel))
return
visible_message("<span class='notice'>[src] begins to collect tiles.</span>")
mode = BOT_REPAIRING
spawn(20)
if(isnull(T))
target = null
mode = BOT_IDLE
return
if(amount + T.amount > 50)
var/i = 50 - amount
amount += i
T.amount -= i
else
amount += T.amount
qdel(T)
update_icon()
target = null
mode = BOT_IDLE
/mob/living/simple_animal/bot/floorbot/proc/maketile(obj/item/stack/sheet/metal/M)
if(!istype(M, /obj/item/stack/sheet/metal))
return
visible_message("<span class='notice'>[src] begins to create tiles.</span>")
mode = BOT_REPAIRING
spawn(20)
if(isnull(M))
target = null
mode = BOT_IDLE
return
var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel
T.amount = 4
T.forceMove(M.loc)
if(M.amount > 1)
M.amount--
else
qdel(M)
target = null
mode = BOT_IDLE
/mob/living/simple_animal/bot/floorbot/update_icon()
if(amount > 0)
icon_state = "floorbot[on]"
else
icon_state = "floorbot[on]e"
/mob/living/simple_animal/bot/floorbot/explode()
on = 0
visible_message("<span class='userdanger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
var/obj/item/weapon/storage/toolbox/mechanical/N = new /obj/item/weapon/storage/toolbox/mechanical(Tsec)
N.contents = list()
new /obj/item/device/assembly/prox_sensor(Tsec)
if(prob(50))
new /obj/item/robot_parts/l_arm(Tsec)
while(amount)//Dumps the tiles into the appropriate sized stacks
if(amount >= 16)
var/obj/item/stack/tile/plasteel/T = new (Tsec)
T.amount = 16
amount -= 16
else
var/obj/item/stack/tile/plasteel/T = new (Tsec)
T.amount = amount
amount = 0
var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
..()
/obj/machinery/bot_core/floorbot
req_one_access = list(access_construction, access_robotics)
/mob/living/simple_animal/bot/floorbot/UnarmedAttack(atom/A)
if(isturf(A))
repair(A)
else if(istype(A,/obj/item/stack/tile/plasteel))
eattile(A)
else if(istype(A,/obj/item/stack/sheet/metal))
maketile(A)
else
..()
@@ -0,0 +1,563 @@
//Medbot
/mob/living/simple_animal/bot/medbot
name = "\improper Medibot"
desc = "A little medical robot. He looks somewhat underwhelmed."
icon = 'icons/obj/aibots.dmi'
icon_state = "medibot0"
density = 0
anchored = 0
health = 20
maxHealth = 20
pass_flags = PASSMOB
radio_channel = "Medical"
bot_type = MED_BOT
bot_filter = RADIO_MEDBOT
model = "Medibot"
bot_purpose = "seek out hurt crewmembers and ensure that they are healed"
bot_core_type = /obj/machinery/bot_core/medbot
window_id = "automed"
window_name = "Automatic Medical Unit v1.1"
var/obj/item/weapon/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents.
var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits.
var/mob/living/carbon/patient = null
var/mob/living/carbon/oldpatient = null
var/oldloc = null
var/last_found = 0
var/last_newpatient_speak = 0 //Don't spam the "HEY I'M COMING" messages
var/injection_amount = 15 //How much reagent do we inject at a time?
var/heal_threshold = 10 //Start healing when they have this much damage in a category
var/use_beaker = 0 //Use reagents in beaker instead of default treatment agents.
var/declare_crit = 1 //If active, the bot will transmit a critical patient alert to MedHUD users.
var/declare_cooldown = 0 //Prevents spam of critical patient alerts.
var/stationary_mode = 0 //If enabled, the Medibot will not move automatically.
//Setting which reagents to use to treat what by default. By id.
var/treatment_brute = "salglu_solution"
var/treatment_oxy = "salbutamol"
var/treatment_fire = "salglu_solution"
var/treatment_tox = "charcoal"
var/treatment_virus = "spaceacillin"
var/treat_virus = 1 //If on, the bot will attempt to treat viral infections, curing them if possible.
var/shut_up = 0 //self explanatory :)
/mob/living/simple_animal/bot/medbot/tox
skin = "tox"
/mob/living/simple_animal/bot/medbot/o2
skin = "o2"
/mob/living/simple_animal/bot/medbot/brute
skin = "brute"
/mob/living/simple_animal/bot/medbot/fire
skin = "ointment"
/mob/living/simple_animal/bot/medbot/adv
skin = "adv"
/mob/living/simple_animal/bot/medbot/fish
skin = "fish"
/mob/living/simple_animal/bot/medbot/mysterious
name = "\improper Mysterious Medibot"
desc = "International Medibot of mystery."
skin = "bezerk"
treatment_oxy = "perfluorodecalin"
treatment_brute = "styptic_powder"
treatment_fire = "silver_sulfadiazine"
treatment_tox = "charcoal"
/mob/living/simple_animal/bot/medbot/syndicate
name = "Suspicious Medibot"
desc = "You'd better have insurance!"
skin = "bezerk"
treatment_oxy = "perfluorodecalin"
treatment_brute = "styptic_powder"
treatment_fire = "silver_sulfadiazine"
treatment_tox = "charcoal"
bot_core_type = /obj/machinery/bot_core/medbot/syndicate
control_freq = BOT_FREQ + 1000 // make it not show up on lists
radio_channel = "Syndicate"
radio_config = list("Common" = 1, "Medical" = 1, "Syndicate" = 1)
/mob/living/simple_animal/bot/medbot/syndicate/New()
..()
Radio.syndie = 1
/mob/living/simple_animal/bot/medbot/update_icon()
if(!on)
icon_state = "medibot0"
return
if(mode == BOT_HEALING)
icon_state = "medibots[stationary_mode]"
return
else if(stationary_mode) //Bot has yellow light to indicate stationary mode.
icon_state = "medibot2"
else
icon_state = "medibot1"
/mob/living/simple_animal/bot/medbot/New()
..()
update_icon()
spawn(4)
if(skin)
overlays += image('icons/obj/aibots.dmi', "medskin_[skin]")
var/datum/job/doctor/J = new/datum/job/doctor
access_card.access += J.get_access()
prev_access = access_card.access
var/datum/atom_hud/medsensor = huds[DATA_HUD_MEDICAL_ADVANCED]
medsensor.add_hud_to(src)
permanent_huds |= medsensor
/mob/living/simple_animal/bot/medbot/bot_reset()
..()
patient = null
oldpatient = null
oldloc = null
last_found = world.time
declare_cooldown = 0
update_icon()
/mob/living/simple_animal/bot/medbot/proc/soft_reset() //Allows the medibot to still actively perform its medical duties without being completely halted as a hard reset does.
path = list()
patient = null
mode = BOT_IDLE
last_found = world.time
update_icon()
/mob/living/simple_animal/bot/medbot/set_custom_texts()
text_hack = "You corrupt [name]'s reagent processor circuits."
text_dehack = "You reset [name]'s reagent processor circuits."
text_dehack_fail = "[name] seems damaged and does not respond to reprogramming!"
/mob/living/simple_animal/bot/medbot/get_controls(mob/user)
var/dat
dat += hack(user)
dat += showpai(user)
dat += "<TT><B>Medical Unit Controls v1.1</B></TT><BR><BR>"
dat += "Status: <A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A><BR>"
dat += "Maintenance panel panel is [open ? "opened" : "closed"]<BR>"
dat += "Beaker: "
if(reagent_glass)
dat += "<A href='?src=\ref[src];eject=1'>Loaded \[[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]\]</a>"
else
dat += "None Loaded"
dat += "<br>Behaviour controls are [locked ? "locked" : "unlocked"]<hr>"
if(!locked || issilicon(user) || check_rights(R_ADMIN, 0, user))
dat += "<TT>Healing Threshold: "
dat += "<a href='?src=\ref[src];adj_threshold=-10'>--</a> "
dat += "<a href='?src=\ref[src];adj_threshold=-5'>-</a> "
dat += "[heal_threshold] "
dat += "<a href='?src=\ref[src];adj_threshold=5'>+</a> "
dat += "<a href='?src=\ref[src];adj_threshold=10'>++</a>"
dat += "</TT><br>"
dat += "<TT>Injection Level: "
dat += "<a href='?src=\ref[src];adj_inject=-5'>-</a> "
dat += "[injection_amount] "
dat += "<a href='?src=\ref[src];adj_inject=5'>+</a> "
dat += "</TT><br>"
dat += "Reagent Source: "
dat += "<a href='?src=\ref[src];use_beaker=1'>[use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]</a><br>"
dat += "Treat Viral Infections: <a href='?src=\ref[src];virus=1'>[treat_virus ? "Yes" : "No"]</a><br>"
dat += "The speaker switch is [shut_up ? "off" : "on"]. <a href='?src=\ref[src];togglevoice=[1]'>Toggle</a><br>"
dat += "Critical Patient Alerts: <a href='?src=\ref[src];critalerts=1'>[declare_crit ? "Yes" : "No"]</a><br>"
dat += "Patrol Station: <a href='?src=\ref[src];operation=patrol'>[auto_patrol ? "Yes" : "No"]</a><br>"
dat += "Stationary Mode: <a href='?src=\ref[src];stationary=1'>[stationary_mode ? "Yes" : "No"]</a><br>"
return dat
/mob/living/simple_animal/bot/medbot/Topic(href, href_list)
if(..())
return 1
if(href_list["adj_threshold"])
var/adjust_num = text2num(href_list["adj_threshold"])
heal_threshold += adjust_num
if(heal_threshold < 5)
heal_threshold = 5
if(heal_threshold > 75)
heal_threshold = 75
else if(href_list["adj_inject"])
var/adjust_num = text2num(href_list["adj_inject"])
injection_amount += adjust_num
if(injection_amount < 5)
injection_amount = 5
if(injection_amount > 15)
injection_amount = 15
else if(href_list["use_beaker"])
use_beaker = !use_beaker
else if(href_list["eject"] && (!isnull(reagent_glass)))
reagent_glass.forceMove(get_turf(src))
reagent_glass = null
else if(href_list["togglevoice"])
shut_up = !shut_up
else if(href_list["critalerts"])
declare_crit = !declare_crit
else if(href_list["stationary"])
stationary_mode = !stationary_mode
path = list()
update_icon()
else if(href_list["virus"])
treat_virus = !treat_virus
update_controls()
return
/mob/living/simple_animal/bot/medbot/attackby(obj/item/weapon/W as obj, mob/user as mob, params)
if(istype(W, /obj/item/weapon/reagent_containers/glass))
if(locked)
user << "<span class='warning'>You cannot insert a beaker because the panel is locked!</span>"
return
if(!isnull(reagent_glass))
user << "<span class='warning'>There is already a beaker loaded!</span>"
return
if(!user.drop_item())
return
W.forceMove(src)
reagent_glass = W
user << "<span class='notice'>You insert [W].</span>"
show_controls(user)
return
else
var/current_health = health
..()
if(health < current_health) //if medbot took some damage
step_to(src, (get_step_away(src,user)))
/mob/living/simple_animal/bot/medbot/emag_act(mob/user)
..()
if(emagged == 2)
declare_crit = 0
if(user)
user << "<span class='notice'>You short out [src]'s reagent synthesis circuits.</span>"
audible_message("<span class='danger'>[src] buzzes oddly!</span>")
flick("medibot_spark", src)
if(user)
oldpatient = user
/mob/living/simple_animal/bot/medbot/process_scan(mob/living/carbon/human/H)
if(H.stat == 2)
return
if((H == oldpatient) && (world.time < last_found + 200))
return
if(assess_patient(H))
last_found = world.time
if((last_newpatient_speak + 300) < world.time) //Don't spam these messages!
var/message = pick("Hey, [H.name]! Hold on, I'm coming.","Wait [H.name]! I want to help!","[H.name], you appear to be injured!")
speak(message)
last_newpatient_speak = world.time
return H
else
return
/mob/living/simple_animal/bot/medbot/handle_automated_action()
if(!..())
return
if(mode == BOT_HEALING)
return
if(stunned)
icon_state = "medibota"
stunned--
oldpatient = patient
patient = null
mode = BOT_IDLE
if(stunned <= 0)
update_icon()
stunned = 0
return
if(frustration > 8)
oldpatient = patient
soft_reset()
if(!patient)
if(!shut_up && prob(1))
var/message = pick("Radar, put a mask on!","There's always a catch, and it's the best there is.","I knew it, I should've been a plastic surgeon.","What kind of medbay is this? Everyone's dropping like dead flies.","Delicious!")
speak(message)
var/scan_range = (stationary_mode ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients.
patient = scan(/mob/living/carbon/human, oldpatient, scan_range)
oldpatient = patient
if(patient && (get_dist(src,patient) <= 1)) //Patient is next to us, begin treatment!
if(mode != BOT_HEALING)
mode = BOT_HEALING
update_icon()
frustration = 0
medicate_patient(patient)
return
//Patient has moved away from us!
else if(patient && path.len && (get_dist(patient,path[path.len]) > 2))
path = list()
mode = BOT_IDLE
last_found = world.time
else if(stationary_mode && patient) //Since we cannot move in this mode, ignore the patient and wait for another.
soft_reset()
return
if(patient && path.len == 0 && (get_dist(src,patient) > 1))
path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,id=access_card)
mode = BOT_MOVING
if(!path.len) //try to get closer if you can't reach the patient directly
path = get_path_to(src, get_turf(patient), /turf/proc/Distance_cardinal, 0, 30,1,id=access_card)
if(!path.len) //Do not chase a patient we cannot reach.
soft_reset()
if(path.len > 0 && patient)
if(!bot_move(path[path.len]))
oldpatient = patient
soft_reset()
return
if(path.len > 8 && patient)
frustration++
if(auto_patrol && !stationary_mode && !patient)
if(mode == BOT_IDLE || mode == BOT_START_PATROL)
start_patrol()
if(mode == BOT_PATROL)
bot_patrol()
return
/mob/living/simple_animal/bot/medbot/proc/assess_patient(mob/living/carbon/C)
//Time to see if they need medical help!
if(C.stat == 2)
return 0 //welp too late for them!
if(C.suiciding)
return 0 //Kevorkian school of robotic medical assistants.
if(emagged == 2) //Everyone needs our medicine. (Our medicine is toxins)
return 1
if((skin == "bezerk") && (!("syndicate" in C.faction)))
return 0
if(declare_crit && C.health <= 0) //Critical condition! Call for help!
declare(C)
//If they're injured, we're using a beaker, and don't have one of our WONDERCHEMS.
if((reagent_glass) && (use_beaker) && ((C.getBruteLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getOxyLoss() >= (heal_threshold + 15))))
for(var/datum/reagent/R in reagent_glass.reagents.reagent_list)
if(!C.reagents.has_reagent(R.id))
return 1
//They're injured enough for it!
if((C.getBruteLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_brute)))
return 1 //If they're already medicated don't bother!
if((C.getOxyLoss() >= (15 + heal_threshold)) && (!C.reagents.has_reagent(treatment_oxy)))
return 1
if((C.getFireLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_fire)))
return 1
if((C.getToxLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_tox)))
return 1
if(treat_virus && C.virus2.len && !C.reagents.has_reagent(treatment_virus))
for (var/ID in C.virus2)
if (ID in virusDB) // If the virus is known, the medbot is aware of it
return 1
return 0
/mob/living/simple_animal/bot/medbot/UnarmedAttack(atom/A)
if(iscarbon(A))
var/mob/living/carbon/C = A
patient = C
mode = BOT_HEALING
update_icon()
medicate_patient(C)
update_icon()
else
..()
/mob/living/simple_animal/bot/medbot/examinate(atom/A as mob|obj|turf in view())
..()
if(!is_blind(src))
chemscan(src, A)
/mob/living/simple_animal/bot/medbot/proc/medicate_patient(mob/living/carbon/C)
if(!on)
return
if(!istype(C))
oldpatient = patient
soft_reset()
return
if(C.stat == 2)
var/death_message = pick("No! NO!","Live, damnit! LIVE!","I...I've never lost a patient before. Not today, I mean.")
speak(death_message)
oldpatient = patient
soft_reset()
return
var/reagent_id = null
if(emagged == 2) //Emagged! Time to poison everybody.
reagent_id = "pancuronium"
else
if(treat_virus)
var/virus = 0
if(C.virus2.len)
for (var/ID in C.virus2)
if (ID in virusDB) // If the virus is known, the medbot is aware of it and will try to cure it
virus = 1
if (!reagent_id && (virus))
if(!C.reagents.has_reagent(treatment_virus))
reagent_id = treatment_virus
if(!reagent_id && (C.getBruteLoss() >= heal_threshold))
if(!C.reagents.has_reagent(treatment_brute))
reagent_id = treatment_brute
if(!reagent_id && (C.getOxyLoss() >= (15 + heal_threshold)))
if(!C.reagents.has_reagent(treatment_oxy))
reagent_id = treatment_oxy
if(!reagent_id && (C.getFireLoss() >= heal_threshold))
if(!C.reagents.has_reagent(treatment_fire))
reagent_id = treatment_fire
if(!reagent_id && (C.getToxLoss() >= heal_threshold))
if(!C.reagents.has_reagent(treatment_tox))
reagent_id = treatment_tox
//If the patient is injured but doesn't have our special reagent in them then we should give it to them first
if(reagent_id && use_beaker && reagent_glass && reagent_glass.reagents.total_volume)
for(var/datum/reagent/R in reagent_glass.reagents.reagent_list)
if(!C.reagents.has_reagent(R.id))
reagent_id = "internal_beaker"
break
if(!reagent_id) //If they don't need any of that they're probably cured!
var/message = pick("All patched up!","An apple a day keeps me away.","Feel better soon!")
speak(message)
bot_reset()
return
else
if(!emagged && check_overdose(patient,reagent_id,injection_amount))
soft_reset()
return
C.visible_message("<span class='danger'>[src] is trying to inject [patient]!</span>", \
"<span class='userdanger'>[src] is trying to inject you!</span>")
spawn(30)//replace with do mob
if((get_dist(src, patient) <= 1) && on && assess_patient(patient))
if(reagent_id == "internal_beaker")
if(use_beaker && reagent_glass && reagent_glass.reagents.total_volume)
var/fraction = min(injection_amount/reagent_glass.reagents.total_volume, 1)
reagent_glass.reagents.reaction(patient, 2, fraction)
reagent_glass.reagents.trans_to(patient, injection_amount) //Inject from beaker instead.
else
patient.reagents.add_reagent(reagent_id,injection_amount)
C.visible_message("<span class='danger'>[src] injects [patient] with its syringe!</span>", \
"<span class='userdanger'>[src] injects you with its syringe!</span>")
else
visible_message("[src] retracts its syringe.")
update_icon()
soft_reset()
return
reagent_id = null
return
/mob/living/simple_animal/bot/medbot/proc/check_overdose(mob/living/carbon/patient,reagent_id,injection_amount)
var/datum/reagent/R = chemical_reagents_list[reagent_id]
if(!R.overdose_threshold)
return 0
var/current_volume = patient.reagents.get_reagent_amount(reagent_id)
if(current_volume + injection_amount > R.overdose_threshold)
return 1
return 0
/mob/living/simple_animal/bot/medbot/bullet_act(obj/item/projectile/Proj)
if(Proj.flag == "taser")
stunned = min(stunned+10,20)
..()
/mob/living/simple_animal/bot/medbot/explode()
on = 0
visible_message("<span class='userdanger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
switch(skin)
if("ointment")
new /obj/item/weapon/storage/firstaid/fire/empty(Tsec)
if("tox")
new /obj/item/weapon/storage/firstaid/toxin/empty(Tsec)
if("o2")
new /obj/item/weapon/storage/firstaid/o2/empty(Tsec)
if("brute")
new /obj/item/weapon/storage/firstaid/brute/empty(Tsec)
if("adv")
new /obj/item/weapon/storage/firstaid/adv/empty(Tsec)
if("bezerk")
new /obj/item/weapon/storage/firstaid/tactical/empty(Tsec)
if("fish")
new /obj/item/weapon/storage/firstaid/aquatic_kit(Tsec)
else
new /obj/item/weapon/storage/firstaid(Tsec)
new /obj/item/device/assembly/prox_sensor(Tsec)
new /obj/item/device/healthanalyzer(Tsec)
if(reagent_glass)
reagent_glass.forceMove(Tsec)
reagent_glass = null
if(prob(50))
new /obj/item/robot_parts/l_arm(Tsec)
var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
..()
/mob/living/simple_animal/bot/medbot/proc/declare(crit_patient)
if(declare_cooldown)
return
if((skin == "bezerk"))
return
var/area/location = get_area(src)
speak("Medical emergency! [crit_patient ? "<b>[crit_patient]</b>" : "A patient"] is in critical condition at [location]!", radio_channel)
declare_cooldown = 1
spawn(200) //Twenty seconds
declare_cooldown = 0
/obj/machinery/bot_core/medbot
req_one_access = list(access_medical, access_robotics)
/obj/machinery/bot_core/medbot/syndicate
req_one_access = list(access_syndicate)
@@ -0,0 +1,869 @@
//This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:31
// Mulebot - carries crates around for Quartermaster
// Navigates via floor navbeacons
// Remote Controlled from QM's PDA
#define SIGH 0
#define ANNOYED 1
#define DELIGHT 2
/mob/living/simple_animal/bot/mulebot
name = "\improper MULEbot"
desc = "A Multiple Utility Load Effector bot."
icon_state = "mulebot0"
density = 1
anchored = 1
animate_movement=1
health = 50
maxHealth = 50
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
a_intent = "harm" //No swapping
buckle_lying = 0
radio_channel = "Supply"
bot_type = MULE_BOT
bot_filter = RADIO_MULEBOT
model = "MULE"
bot_purpose = "deliver crates and other packages between departments, as requested"
bot_core_type = /obj/machinery/bot_core/mulebot
suffix = ""
var/global/mulebot_count = 0
var/atom/movable/load = null
var/mob/living/passenger = null
var/turf/target // this is turf to navigate to (location of beacon)
var/loaddir = 0 // this the direction to unload onto/load from
var/home_destination = "" // tag of home beacon
var/reached_target = 1 //true if already reached the target
var/auto_return = 1 // true if auto return to home beacon after unload
var/auto_pickup = 1 // true if auto-pickup at beacon
var/report_delivery = 1 // true if bot will announce an arrival to a location.
var/obj/item/weapon/stock_parts/cell/cell
var/datum/wires/mulebot/wires = null
var/bloodiness = 0
var/currentBloodColor = "#A10808"
var/currentDNA = null
/mob/living/simple_animal/bot/mulebot/New()
..()
wires = new /datum/wires/mulebot(src)
var/datum/job/cargo_tech/J = new/datum/job/cargo_tech
access_card.access = J.get_access()
prev_access = access_card.access
cell = new(src)
cell.charge = 2000
cell.maxcharge = 2000
mulebot_count++
set_suffix(suffix ? suffix : "#[mulebot_count]")
/mob/living/simple_animal/bot/mulebot/Destroy()
unload(0)
if(wires)
qdel(wires)
wires = null
if(cell)
qdel(cell)
cell = null
return ..()
/mob/living/simple_animal/bot/mulebot/proc/set_suffix(suffix)
src.suffix = suffix
if(paicard)
bot_name = "\improper MULEbot ([suffix])"
else
name = "\improper MULEbot ([suffix])"
/mob/living/simple_animal/bot/mulebot/bot_reset()
..()
reached_target = 0
/mob/living/simple_animal/bot/mulebot/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/weapon/screwdriver))
..()
if(open)
on = FALSE
update_controls()
else if(istype(I,/obj/item/weapon/stock_parts/cell) && open && !cell)
if(!user.drop_item())
return
var/obj/item/weapon/stock_parts/cell/C = I
C.forceMove(src)
cell = C
visible_message("[user] inserts a cell into [src].",
"<span class='notice'>You insert the new cell into [src].</span>")
update_controls()
else if(istype(I, /obj/item/weapon/crowbar) && open && cell)
cell.add_fingerprint(usr)
cell.forceMove(loc)
cell = null
visible_message("[user] crowbars out the power cell from [src].",
"<span class='notice'>You pry the powercell out of [src].</span>")
update_controls()
else if (istype(I, /obj/item/weapon/wrench))
if (health < maxHealth)
adjustBruteLoss(-25)
updatehealth()
user.visible_message(
"<span class='notice'>[user] repairs [src]!</span>",
"<span class='notice'>You repair [src]!</span>"
)
else
user << "<span class='notice'>[src] does not need a repair!</span>"
else if((istype(I, /obj/item/device/multitool) || istype(I, /obj/item/weapon/wirecutters)) && open)
return attack_hand(user)
else if(load && ismob(load)) // chance to knock off rider
if(prob(1 + I.force * 2))
unload(0)
user.visible_message("<span class='danger'>[user] knocks [load] off [src] with \the [I]!</span>",
"<span class='danger'>You knock [load] off [src] with \the [I]!</span>")
else
user << "<span class='warning'>You hit [src] with \the [I] but to no effect!</span>"
..()
else
..()
update_icon()
return
/mob/living/simple_animal/bot/mulebot/emag_act(mob/user)
if(emagged < 1)
emagged = 1
if(!open)
locked = !locked
user << "<span class='notice'>You [locked ? "lock" : "unlock"] the [src]'s controls!</span>"
flick("mulebot-emagged", src)
playsound(loc, 'sound/effects/sparks1.ogg', 100, 0)
/mob/living/simple_animal/bot/mulebot/update_icon()
if(open)
icon_state="mulebot-hatch"
else
icon_state = "mulebot[!wires.MobAvoid()]"
overlays.Cut()
if(load && !ismob(load))//buckling handles the mob offsets
load.pixel_y = initial(load.pixel_y) + 9
if(load.layer < layer)
load.layer = layer + 0.1
overlays += load
return
/mob/living/simple_animal/bot/mulebot/ex_act(severity)
unload(0)
switch(severity)
if(1)
qdel(src)
if(2)
for(var/i = 1; i < 3; i++)
wires.RandomCut()
if(3)
wires.RandomCut()
return
/mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj)
if(..())
if(prob(50) && !isnull(load))
unload(0)
if(prob(25))
visible_message("<span class='danger'>Something shorts out inside [src]!</span>")
wires.RandomCut()
/mob/living/simple_animal/bot/mulebot/Topic(href, list/href_list)
if(..())
return 1
switch(href_list["op"])
if("lock")
toggle_lock(usr)
if("power")
if(on)
turn_off()
else if(cell && !open)
if(!turn_on())
usr << "<span class='warning'>You can't switch on [src]!</span>"
return
else
return
visible_message("[usr] switches [on ? "on" : "off"] [src].")
if("cellremove")
if(open && cell && !usr.get_active_hand())
cell.updateicon()
usr.put_in_active_hand(cell)
cell.add_fingerprint(usr)
cell = null
usr.visible_message("<span class='notice'>[usr] removes the power cell from [src].</span>", "<span class='notice'>You remove the power cell from [src].</span>")
if("cellinsert")
if(open && !cell)
var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand()
if(istype(C))
usr.drop_item()
cell = C
C.forceMove(src)
C.add_fingerprint(usr)
usr.visible_message("<span class='notice'>[usr] inserts a power cell into [src].</span>", "<span class='notice'>You insert the power cell into [src].</span>")
if("stop")
if(mode >= BOT_DELIVER)
bot_reset()
if("go")
if(mode == BOT_IDLE)
start()
if("home")
if(mode == BOT_IDLE || mode == BOT_DELIVER)
start_home()
if("destination")
var/new_dest = input(usr, "Enter Destination:", name, destination) as null|anything in deliverybeacontags
if(new_dest)
set_destination(new_dest)
if("setid")
var/new_id = stripped_input(usr, "Enter ID:", name, suffix, MAX_NAME_LEN)
if(new_id)
set_suffix(new_id)
if("sethome")
var/new_home = input(usr, "Enter Home:", name, home_destination) as null|anything in deliverybeacontags
if(new_home)
home_destination = new_home
if("unload")
if(load && mode != BOT_HUNT)
if(loc == target)
unload(loaddir)
else
unload(0)
if("autoret")
auto_return = !auto_return
if("autopick")
auto_pickup = !auto_pickup
if("report")
report_delivery = !report_delivery
update_controls()
/mob/living/simple_animal/bot/mulebot/proc/toggle_lock(var/mob/user)
if(bot_core.allowed(user))
locked = !locked
update_controls()
return 1
else
user << "<span class='danger'>Access denied.</span>"
return 0
// TODO: remove this; PDAs currently depend on it
/mob/living/simple_animal/bot/mulebot/get_controls(mob/user)
var/ai = issilicon(user)
var/dat
dat += hack(user)
dat += showpai(user)
dat += "<h3>Multiple Utility Load Effector Mk. V</h3>"
dat += "<b>ID:</b> [suffix]<BR>"
dat += "<b>Power:</b> [on ? "On" : "Off"]<BR>"
if(!open)
dat += "<h3>Status</h3>"
dat += "<div class='statusDisplay'>"
switch(mode)
if(BOT_IDLE)
dat += "<span class='good'>Ready</span>"
if(BOT_DELIVER)
dat += "<span class='good'>[mode_name[BOT_DELIVER]]</span>"
if(BOT_GO_HOME)
dat += "<span class='good'>[mode_name[BOT_GO_HOME]]</span>"
if(BOT_BLOCKED)
dat += "<span class='average'>[mode_name[BOT_BLOCKED]]</span>"
if(BOT_NAV,BOT_WAIT_FOR_NAV)
dat += "<span class='average'>[mode_name[BOT_NAV]]</span>"
if(BOT_NO_ROUTE)
dat += "<span class='bad'>[mode_name[BOT_NO_ROUTE]]</span>"
dat += "</div>"
dat += "<b>Current Load:</b> [load ? load.name : "<i>none</i>"]<BR>"
dat += "<b>Destination:</b> [!destination ? "<i>none</i>" : destination]<BR>"
dat += "<b>Power level:</b> [cell ? cell.percent() : 0]%"
if(locked && !ai && !check_rights(R_ADMIN, 0, user))
dat += "&nbsp;<br /><div class='notice'>Controls are locked</div><A href='?src=\ref[src];op=unlock'>Unlock Controls</A>"
else
dat += "&nbsp;<br /><div class='notice'>Controls are unlocked</div><A href='?src=\ref[src];op=lock'>Lock Controls</A><BR><BR>"
dat += "<A href='?src=\ref[src];op=power'>Toggle Power</A><BR>"
dat += "<A href='?src=\ref[src];op=stop'>Stop</A><BR>"
dat += "<A href='?src=\ref[src];op=go'>Proceed</A><BR>"
dat += "<A href='?src=\ref[src];op=home'>Return to Home</A><BR>"
dat += "<A href='?src=\ref[src];op=destination'>Set Destination</A><BR>"
dat += "<A href='?src=\ref[src];op=setid'>Set Bot ID</A><BR>"
dat += "<A href='?src=\ref[src];op=sethome'>Set Home</A><BR>"
dat += "<A href='?src=\ref[src];op=autoret'>Toggle Auto Return Home</A> ([auto_return ? "On":"Off"])<BR>"
dat += "<A href='?src=\ref[src];op=autopick'>Toggle Auto Pickup Crate</A> ([auto_pickup ? "On":"Off"])<BR>"
dat += "<A href='?src=\ref[src];op=report'>Toggle Delivery Reporting</A> ([report_delivery ? "On" : "Off"])<BR>"
if(load)
dat += "<A href='?src=\ref[src];op=unload'>Unload Now</A><BR>"
dat += "<div class='notice'>The maintenance hatch is closed.</div>"
else
if(!ai)
dat += "<div class='notice'>The maintenance hatch is open.</div><BR>"
dat += "<b>Power cell:</b> "
if(cell)
dat += "<A href='?src=\ref[src];op=cellremove'>Installed</A><BR>"
else
dat += "<A href='?src=\ref[src];op=cellinsert'>Removed</A><BR>"
dat += wires.GetInteractWindow()
else
dat += "<div class='notice'>The bot is in maintenance mode and cannot be controlled.</div><BR>"
return dat
// returns true if the bot has power
/mob/living/simple_animal/bot/mulebot/proc/has_power()
return !open && cell && cell.charge > 0 && wires.HasPower()
/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
switch(type)
if(SIGH)
audible_message("[src] makes a sighing buzz.", "<span class='emote'>You hear an electronic buzzing sound.</span>")
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
if(ANNOYED)
audible_message("[src] makes an annoyed buzzing sound.", "<span class='emote'>You hear an electronic buzzing sound.</span>")
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
if(DELIGHT)
audible_message("[src] makes a delighted ping!", "<span class='emote'>You hear a ping.</span>")
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
// mousedrop a crate to load the bot
// can load anything if hacked
/mob/living/simple_animal/bot/mulebot/MouseDrop_T(atom/movable/AM, mob/user)
if(user.incapacitated() || user.lying || get_dist(user, src) > 1)
return
if(!istype(AM))
return
load(AM)
// called to load a crate
/mob/living/simple_animal/bot/mulebot/proc/load(atom/movable/AM)
if(!on || load || AM.anchored || get_dist(src, AM) > 1)
return
//I'm sure someone will come along and ask why this is here... well people were dragging screen items onto the mule, and that was not cool.
//So this is a simple fix that only allows a selection of item types to be considered. Further narrowing-down is below.
if(!istype(AM, /obj/item) && !istype(AM, /obj/machinery) && !istype(AM, /obj/structure) && !ismob(AM))
return
if(!isturf(AM.loc)) //To prevent the loading from stuff from someone's inventory or screen icons.
return
var/obj/structure/closet/crate/CRATE
if(istype(AM,/obj/structure/closet/crate))
CRATE = AM
else
if(wires.LoadCheck())
buzz(SIGH)
return // if not hacked, only allow crates to be loaded
if(CRATE) // if it's a crate, close before loading
CRATE.close()
if(isobj(AM))
var/obj/O = AM
if(O.buckled_mob || (locate(/mob) in AM)) //can't load non crates objects with mobs buckled to it or inside it.
buzz(SIGH)
return
if(isliving(AM))
if(!load_mob(AM))
return
else
AM.forceMove(src)
load = AM
mode = BOT_IDLE
update_icon()
/mob/living/simple_animal/bot/mulebot/proc/load_mob(mob/living/M)
can_buckle = TRUE
if(buckle_mob(M))
passenger = M
load = M
can_buckle = FALSE
if(M.client)
M.client.perspective = EYE_PERSPECTIVE
M.client.eye = src
return TRUE
return FALSE
/mob/living/simple_animal/bot/mulebot/post_buckle_mob(mob/living/M)
if(M == buckled_mob) //post buckling
M.pixel_y = initial(M.pixel_y) + 9
if(M.layer < layer)
M.layer = layer + 0.1
else //post unbuckling
load = null
M.layer = initial(M.layer)
M.pixel_y = initial(M.pixel_y)
// called to unload the bot
// argument is optional direction to unload
// if zero, unload at bot's location
/mob/living/simple_animal/bot/mulebot/proc/unload(dirn)
if(!load)
return
mode = BOT_IDLE
overlays.Cut()
if(ismob(load))
var/mob/M = load
if(M.client)
M.client.perspective = MOB_PERSPECTIVE
M.client.eye = M
unbuckle_mob()
if(load)
load.forceMove(loc)
load.pixel_y = initial(load.pixel_y)
load.layer = initial(load.layer)
if(dirn)
var/turf/T = loc
var/turf/newT = get_step(T,dirn)
if(load.CanPass(load,newT)) //Can't get off onto anything that wouldn't let you pass normally
step(load, dirn)
load = null
// in case non-load items end up in contents, dump every else too
// this seems to happen sometimes due to race conditions
// with items dropping as mobs are loaded
for(var/atom/movable/AM in src)
if(AM == cell || AM == access_card || AM == Radio || AM == bot_core || AM == paicard)
continue
AM.forceMove(loc)
AM.layer = initial(AM.layer)
AM.pixel_y = initial(AM.pixel_y)
if(ismob(AM))
var/mob/M = AM
if(M.client)
M.client.perspective = MOB_PERSPECTIVE
M.client.eye = M
/mob/living/simple_animal/bot/mulebot/call_bot()
..()
var/area/dest_area
if(path && path.len)
target = ai_waypoint //Target is the end point of the path, the waypoint set by the AI.
dest_area = get_area(target)
destination = format_text(dest_area.name)
pathset = 1 //Indicates the AI's custom path is initialized.
start()
/mob/living/simple_animal/bot/mulebot/handle_automated_action()
diag_hud_set_botmode()
if(!has_power())
on = 0
return
if(on)
var/speed = (wires.Motor1() ? 1 : 0) + (wires.Motor2() ? 2 : 0)
//world << "speed: [speed]"
var/num_steps = 0
switch(speed)
if(0)
// do nothing
if(1)
num_steps = 10
if(2)
num_steps = 5
if(3)
num_steps = 3
if(num_steps)
process_bot()
num_steps--
if(mode != BOT_IDLE)
spawn(0)
for(var/i=num_steps,i>0,i--)
sleep(2)
process_bot()
/mob/living/simple_animal/bot/mulebot/proc/process_bot()
if(!on)
return
update_icon()
switch(mode)
if(BOT_IDLE) // idle
return
if(BOT_DELIVER, BOT_GO_HOME, BOT_BLOCKED) // navigating to deliver,home, or blocked
if(loc == target) // reached target
at_target()
return
else if(path.len > 0 && target) // valid path
var/turf/next = path[1]
reached_target = 0
if(next == loc)
path -= next
return
if(istype(next, /turf/simulated))
//world << "at ([x],[y]) moving to ([next.x],[next.y])"
var/oldloc = loc
var/moved = step_towards(src, next) // attempt to move
if(cell) cell.use(1)
if(moved && oldloc!=loc) // successful move
//world << "Successful move."
blockcount = 0
path -= loc
if(destination == home_destination)
mode = BOT_GO_HOME
else
mode = BOT_DELIVER
else // failed to move
//world << "Unable to move."
blockcount++
mode = BOT_BLOCKED
if(blockcount == 3)
buzz(ANNOYED)
if(blockcount > 10) // attempt 10 times before recomputing
// find new path excluding blocked turf
buzz(SIGH)
mode = BOT_WAIT_FOR_NAV
blockcount = 0
spawn(20)
calc_path(avoid=next)
if(path.len > 0)
buzz(DELIGHT)
mode = BOT_BLOCKED
return
return
else
buzz(ANNOYED)
//world << "Bad turf."
mode = BOT_NAV
return
else
//world << "No path."
mode = BOT_NAV
return
if(BOT_NAV) // calculate new path
//world << "Calc new path."
mode = BOT_WAIT_FOR_NAV
spawn(0)
calc_path()
if(path.len > 0)
blockcount = 0
mode = BOT_BLOCKED
buzz(DELIGHT)
else
buzz(SIGH)
mode = BOT_NO_ROUTE
/mob/living/simple_animal/bot/mulebot/Move(turf/simulated/next)
var/turf/simulated/last = loc
if(istype(last) && bloodiness)
last.AddTracks(/obj/effect/decal/cleanable/blood/tracks/wheels, currentDNA, 0, get_dir(last, next), currentBloodColor)
. = ..()
if(. && istype(next) && bloodiness)
next.AddTracks(/obj/effect/decal/cleanable/blood/tracks/wheels, currentDNA, get_dir(last, next), 0, currentBloodColor)
bloodiness--
// calculates a path to the current destination
// given an optional turf to avoid
/mob/living/simple_animal/bot/mulebot/calc_path(turf/avoid = null)
check_bot_access()
path = get_path_to(src, target, /turf/proc/Distance_cardinal, 0, 250, id=access_card, exclude=avoid)
// sets the current destination
// signals all beacons matching the delivery code
// beacons will return a signal giving their locations
/mob/living/simple_animal/bot/mulebot/proc/set_destination(new_dest)
new_destination = new_dest
get_nav()
// starts bot moving to current destination
/mob/living/simple_animal/bot/mulebot/proc/start()
if(!on)
return
if(destination == home_destination)
mode = BOT_GO_HOME
else
mode = BOT_DELIVER
update_icon()
get_nav()
// starts bot moving to home
// sends a beacon query to find
/mob/living/simple_animal/bot/mulebot/proc/start_home()
if(!on)
return
spawn(0)
set_destination(home_destination)
mode = BOT_BLOCKED
update_icon()
// called when bot reaches current target
/mob/living/simple_animal/bot/mulebot/proc/at_target()
if(!reached_target)
radio_channel = "Supply" //Supply channel
audible_message("[src] makes a chiming sound!", "<span class='emote'>You hear a chime.</span>")
playsound(loc, 'sound/machines/chime.ogg', 50, 0)
reached_target = 1
if(pathset) //The AI called us here, so notify it of our arrival.
loaddir = dir //The MULE will attempt to load a crate in whatever direction the MULE is "facing".
if(calling_ai)
calling_ai << "<span class='notice'>\icon[src] [src] wirelessly plays a chiming sound!</span>"
playsound(calling_ai, 'sound/machines/chime.ogg',40, 0)
calling_ai = null
radio_channel = "AI Private" //Report on AI Private instead if the AI is controlling us.
if(load) // if loaded, unload at target
if(report_delivery)
speak("Destination <b>[destination]</b> reached. Unloading [load].", radio_channel)
if(istype(load, /obj/structure/closet/crate))
var/obj/structure/closet/crate/C = load
C.notifyRecipient(destination)
unload(loaddir)
else
// not loaded
if(auto_pickup) // find a crate
var/atom/movable/AM
if(wires.LoadCheck()) // if hacked, load first unanchored thing we find
for(var/atom/movable/A in get_step(loc, loaddir))
if(!A.anchored)
AM = A
break
else // otherwise, look for crates only
AM = locate(/obj/structure/closet/crate) in get_step(loc,loaddir)
if(AM && AM.Adjacent(src))
load(AM)
if(report_delivery)
speak("Now loading [load] at <b>[get_area(src)]</b>.", radio_channel)
// whatever happened, check to see if we return home
if(auto_return && home_destination && destination != home_destination)
// auto return set and not at home already
start_home()
mode = BOT_BLOCKED
else
bot_reset() // otherwise go idle
return
// called when bot bumps into anything
/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
if(!wires.MobAvoid()) // usually just bumps, but if avoidance disabled knock over mobs
var/mob/M = obs
if(ismob(M))
if(istype(M,/mob/living/silicon/robot))
visible_message("<span class='danger'>[src] bumps into [M]!</span>")
else
add_logs(src, M, "knocked down")
visible_message("<span class='danger'>[src] knocks over [M]!</span>")
M.stop_pulling()
M.Stun(8)
M.Weaken(5)
return ..()
// called from mob/living/carbon/human/Crossed()
// when mulebot is in the same loc
/mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H)
add_logs(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])")
H.visible_message("<span class='danger'>[src] drives over [H]!</span>", \
"<span class='userdanger'>[src] drives over you!<span>")
playsound(loc, 'sound/effects/splat.ogg', 50, 1)
var/damage = rand(5,15)
H.apply_damage(2*damage, BRUTE, "head", run_armor_check("head", "melee"))
H.apply_damage(2*damage, BRUTE, "chest", run_armor_check("chest", "melee"))
H.apply_damage(0.5*damage, BRUTE, "l_leg", run_armor_check("l_leg", "melee"))
H.apply_damage(0.5*damage, BRUTE, "r_leg", run_armor_check("r_leg", "melee"))
H.apply_damage(0.5*damage, BRUTE, "l_arm", run_armor_check("l_arm", "melee"))
H.apply_damage(0.5*damage, BRUTE, "r_arm", run_armor_check("r_arm", "melee"))
var/obj/effect/decal/cleanable/blood/B = new(loc)
B.blood_DNA = list()
B.blood_DNA[H.dna.unique_enzymes] = H.dna.b_type
currentDNA = B.blood_DNA
bloodiness += 4
currentBloodColor = "#A10808"
if(istype(H))
var/datum/reagent/blood/bld = H.get_blood(H.vessel)
if(bld.data["blood_colour"])
currentBloodColor = bld.data["blood_colour"]
return
B.basecolor = currentBloodColor
B.update_icon()
/mob/living/simple_animal/bot/mulebot/bot_control_message(command, mob/user, user_turf)
switch(command)
if("start")
if(load)
src << "<span class='warning big'>DELIVER [load] TO [destination]</span>"
else
src << "<span class='warning big'>PICK UP DELIVERY AT [destination]</span>"
if("unload")
if(load)
src << "<span class='warning big'>UNLOAD</span>"
else
src << "<span class='warning big'>LOAD</span>"
if("autoret", "autopick", "target")
else
..()
/mob/living/simple_animal/bot/mulebot/receive_signal(datum/signal/signal)
if(!wires.RemoteRX() || ..())
return 1
var/recv = signal.data["command"]
// process control input
switch(recv)
if("start")
start()
if("target")
set_destination(signal.data["destination"])
if("unload")
if(client)
return 1
if(loc == target)
unload(loaddir)
else
unload(0)
if("home")
start_home()
if("autoret")
auto_return = text2num(signal.data["value"])
if("autopick")
auto_pickup = text2num(signal.data["value"])
else
return 0
return 1
// send a radio signal with multiple data key/values
/mob/living/simple_animal/bot/mulebot/post_signal_multiple(var/freq, var/list/keyval)
if(!wires.RemoteTX())
return
..()
// signals bot status etc. to controller
/mob/living/simple_animal/bot/mulebot/send_status()
var/list/kv = list(
"type" = MULE_BOT,
"name" = suffix,
"loca" = get_area(src),
"mode" = mode,
"powr" = (cell ? cell.percent() : 0),
"dest" = destination,
"home" = home_destination,
"load" = load,
"retn" = auto_return,
"pick" = auto_pickup,
)
post_signal_multiple(control_freq, kv)
// player on mulebot attempted to move
/mob/living/simple_animal/bot/mulebot/relaymove(mob/user)
if(user.incapacitated())
return
if(load == user)
unload(0)
//Update navigation data. Called when commanded to deliver, return home, or a route update is needed...
/mob/living/simple_animal/bot/mulebot/proc/get_nav()
if(!on || !wires.BeaconRX())
return
for(var/obj/machinery/navbeacon/NB in deliverybeacons)
if(NB.location == new_destination) // if the beacon location matches the set destination
destination = new_destination // the we will navigate there
target = NB.loc
var/direction = NB.dir // this will be the load/unload dir
loaddir = direction
update_icon()
calc_path()
return
/mob/living/simple_animal/bot/mulebot/emp_act(severity)
if(cell)
cell.emp_act(severity)
if(load)
load.emp_act(severity)
..()
/mob/living/simple_animal/bot/mulebot/explode()
visible_message("<span class='userdanger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
new /obj/item/device/assembly/prox_sensor(Tsec)
new /obj/item/stack/rods(Tsec)
new /obj/item/stack/rods(Tsec)
new /obj/item/stack/cable_coil/cut(Tsec)
if(cell)
cell.forceMove(Tsec)
cell.update_icon()
cell = null
var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
new /obj/effect/decal/cleanable/blood/oil(loc)
..()
/mob/living/simple_animal/bot/mulebot/remove_air(amount) //To prevent riders suffocating
if(loc)
return loc.remove_air(amount)
else
return null
/mob/living/simple_animal/bot/mulebot/resist()
..()
if(load)
unload()
/mob/living/simple_animal/bot/mulebot/UnarmedAttack(atom/A)
if(isturf(A) && isturf(loc) && loc.Adjacent(A) && load)
unload(get_dir(loc, A))
else
..()
#undef SIGH
#undef ANNOYED
#undef DELIGHT
/obj/machinery/bot_core/mulebot
req_access = list(access_cargo)
@@ -0,0 +1,462 @@
/mob/living/simple_animal/bot/secbot
name = "\improper Securitron"
desc = "A little security robot. He looks less than thrilled."
icon = 'icons/obj/aibots.dmi'
icon_state = "secbot0"
density = 0
anchored = 0
health = 25
maxHealth = 25
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
pass_flags = PASSMOB
radio_channel = "Security" //Security channel
bot_type = SEC_BOT
bot_filter = RADIO_SECBOT
model = "Securitron"
bot_purpose = "seek out criminals, handcuff them, and report their location to security"
bot_core_type = /obj/machinery/bot_core/secbot
window_id = "autosec"
window_name = "Automatic Security Unit v1.6"
var/base_icon = "secbot"
var/mob/living/carbon/target
var/oldtarget_name
var/threatlevel = 0
var/target_lastloc //Loc of target when arrested.
var/last_found //There's a delay
var/declare_arrests = 1 //When making an arrest, should it notify everyone on the security channel?
var/idcheck = 0 //If true, arrest people with no IDs
var/weaponscheck = 0 //If true, arrest people for weapons if they lack access
var/check_records = 1 //Does it check security records?
var/arrest_type = 0 //If true, don't handcuff
var/harmbaton = 0 //If true, beat instead of stun
var/flashing_lights = 0 //If true, flash lights
var/prev_flashing_lights = 0
/mob/living/simple_animal/bot/secbot/beepsky
name = "Officer Beep O'sky"
desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey."
idcheck = 0
weaponscheck = 0
auto_patrol = 1
/mob/living/simple_animal/bot/secbot/beepsky/explode()
var/turf/Tsec = get_turf(src)
new /obj/item/weapon/stock_parts/cell/potato(Tsec)
var/obj/item/weapon/reagent_containers/food/drinks/drinkingglass/S = new(Tsec)
S.reagents.add_reagent("whiskey", 15)
S.on_reagent_change()
..()
/mob/living/simple_animal/bot/secbot/pingsky
name = "Officer Pingsky"
desc = "It's Officer Pingsky! Delegated to satellite guard duty for harbouring anti-human sentiment."
radio_channel = "AI Private"
/mob/living/simple_animal/bot/secbot/ofitser
name = "Prison Ofitser"
desc = "It's Prison Ofitser! Powered by the tears and sweat of prisoners."
idcheck = 0
weaponscheck = 1
auto_patrol = 1
/mob/living/simple_animal/bot/secbot/buzzsky
name = "Officer Buzzsky"
desc = "It's Officer Buzzsky! Rusted and falling apart, he seems less than thrilled with the crew for leaving him in his current state."
base_icon = "rustbot"
icon_state = "rustbot0"
declare_arrests = 0
arrest_type = 1
harmbaton = 1
emagged = 2
/mob/living/simple_animal/bot/secbot/New()
..()
icon_state = "[base_icon][on]"
spawn(3)
var/datum/job/detective/J = new/datum/job/detective
access_card.access += J.get_access()
prev_access = access_card.access
//SECHUD
var/datum/atom_hud/secsensor = huds[DATA_HUD_SECURITY_ADVANCED]
secsensor.add_hud_to(src)
permanent_huds |= secsensor
/mob/living/simple_animal/bot/secbot/turn_on()
..()
icon_state = "[base_icon][on]"
/mob/living/simple_animal/bot/secbot/turn_off()
..()
icon_state = "[base_icon][on]"
/mob/living/simple_animal/bot/secbot/bot_reset()
..()
target = null
oldtarget_name = null
anchored = 0
walk_to(src,0)
last_found = world.time
/mob/living/simple_animal/bot/secbot/set_custom_texts()
text_hack = "You overload [name]'s target identification system."
text_dehack = "You reboot [name] and restore the target identification."
text_dehack_fail = "[name] refuses to accept your authority!"
/mob/living/simple_animal/bot/secbot/get_controls(mob/user)
var/dat
dat += hack(user)
dat += showpai(user)
dat += text({"
<TT><B>Securitron v1.6 controls</B></TT><BR><BR>
Status: []<BR>
Behaviour controls are [locked ? "locked" : "unlocked"]<BR>
Maintenance panel panel is [open ? "opened" : "closed"]"},
"<A href='?src=\ref[src];power=1'>[on ? "On" : "Off"]</A>" )
if(!locked || issilicon(user) || check_rights(R_ADMIN, 0, user))
dat += text({"<BR>
Arrest Unidentifiable Persons: []<BR>
Arrest for Unauthorized Weapons: []<BR>
Arrest for Warrant: []<BR>
Operating Mode: []<BR>
Report Arrests[]<BR>
Auto Patrol: []"},
"<A href='?src=\ref[src];operation=idcheck'>[idcheck ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=weaponscheck'>[weaponscheck ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=ignorerec'>[check_records ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=switchmode'>[arrest_type ? "Detain" : "Arrest"]</A>",
"<A href='?src=\ref[src];operation=declarearrests'>[declare_arrests ? "Yes" : "No"]</A>",
"<A href='?src=\ref[src];operation=patrol'>[auto_patrol ? "On" : "Off"]</A>" )
return dat
/mob/living/simple_animal/bot/secbot/Topic(href, href_list)
if(..())
return 1
switch(href_list["operation"])
if("idcheck")
idcheck = !idcheck
update_controls()
if("weaponscheck")
weaponscheck = !weaponscheck
update_controls()
if("ignorerec")
check_records = !check_records
update_controls()
if("switchmode")
arrest_type = !arrest_type
update_controls()
if("declarearrests")
declare_arrests = !declare_arrests
update_controls()
/mob/living/simple_animal/bot/secbot/proc/retaliate(mob/living/carbon/human/H)
threatlevel = H.assess_threat(src)
threatlevel += 6
if(threatlevel >= 4)
target = H
mode = BOT_HUNT
/mob/living/simple_animal/bot/secbot/attack_hand(mob/living/carbon/human/H)
if(H.a_intent == "harm")
retaliate(H)
return ..()
/mob/living/simple_animal/bot/secbot/attackby(obj/item/weapon/W, mob/user, params)
..()
if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry.
return
if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass.
retaliate(user)
/mob/living/simple_animal/bot/secbot/emag_act(mob/user)
..()
if(emagged == 2)
if(user)
user << "<span class='danger'>You short out [src]'s target assessment circuits.</span>"
oldtarget_name = user.name
audible_message("<span class='danger'>[src] buzzes oddly!</span>")
declare_arrests = 0
icon_state = "[base_icon][on]"
/mob/living/simple_animal/bot/secbot/bullet_act(obj/item/projectile/Proj)
if(istype(Proj ,/obj/item/projectile/beam)||istype(Proj,/obj/item/projectile/bullet))
if((Proj.damage_type == BURN) || (Proj.damage_type == BRUTE))
if(!Proj.nodamage && Proj.damage < src.health)
retaliate(Proj.firer)
..()
/mob/living/simple_animal/bot/secbot/UnarmedAttack(atom/A)
if(!on)
return
if(iscarbon(A))
var/mob/living/carbon/C = A
if(!C.stunned || arrest_type)
stun_attack(A)
else if(C.canBeHandcuffed() && !C.handcuffed)
cuff(A)
else
..()
/mob/living/simple_animal/bot/secbot/hitby(atom/movable/AM, skipcatch = 0, hitpush = 1, blocked = 0)
if(istype(AM, /obj/item))
var/obj/item/I = AM
if(I.throwforce < src.health && I.thrower && (istype(I.thrower, /mob/living/carbon/human)))
var/mob/living/carbon/human/H = I.thrower
retaliate(H)
..()
/mob/living/simple_animal/bot/secbot/proc/cuff(mob/living/carbon/C)
mode = BOT_ARREST
playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2)
C.visible_message("<span class='danger'>[src] is trying to put zipties on [C]!</span>",\
"<span class='userdanger'>[src] is trying to put zipties on you!</span>")
spawn(60)
if( !Adjacent(C) || !isturf(C.loc) ) //if he's in a closet or not adjacent, we cancel cuffing.
return
if(!C.handcuffed)
C.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(C)
C.update_inv_handcuffed(1)
playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0)
back_to_idle()
/mob/living/simple_animal/bot/secbot/proc/stun_attack(mob/living/carbon/C)
playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1)
if(harmbaton)
playsound(loc, 'sound/weapons/genhit1.ogg', 50, 1, -1)
icon_state = "[base_icon]-c"
spawn(2)
icon_state = "[base_icon][on]"
var/threat = 5
if(istype(C, /mob/living/carbon/human))
C.stuttering = 5
if(harmbaton) // Bots with harmbaton enabled become shitcurity. - Dave
C.apply_damage(10, BRUTE)
C.Stun(5)
C.Weaken(5)
var/mob/living/carbon/human/H = C
threat = H.assess_threat(src)
else
C.Weaken(5)
C.stuttering = 5
C.Stun(5)
add_logs(src,C,"stunned")
if(declare_arrests)
var/area/location = get_area(src)
speak("[arrest_type ? "Detaining" : "Arresting"] level [threat] scumbag <b>[C]</b> in [location].", radio_channel)
C.visible_message("<span class='danger'>[src] has [harmbaton ? "beaten" : "stunned"] [C]!</span>",\
"<span class='userdanger'>[src] has [harmbaton ? "beaten" : "stunned"] you!</span>")
/mob/living/simple_animal/bot/secbot/Life()
. = ..()
if(flashing_lights)
switch(light_color)
if(LIGHT_COLOR_PURE_RED)
light_color = LIGHT_COLOR_PURE_BLUE
if(LIGHT_COLOR_PURE_BLUE)
light_color = LIGHT_COLOR_PURE_RED
update_light()
else if(prev_flashing_lights)
light_color = LIGHT_COLOR_PURE_RED
update_light()
prev_flashing_lights = flashing_lights
/mob/living/simple_animal/bot/secbot/verb/toggle_flashing_lights()
set name = "Toggle Flashing Lights"
set category = "Object"
set src = usr
flashing_lights = !flashing_lights
/mob/living/simple_animal/bot/secbot/handle_automated_action()
if(!..())
return
flashing_lights = mode == BOT_HUNT
switch(mode)
if(BOT_IDLE) // idle
walk_to(src,0)
look_for_perp() // see if any criminals are in range
if(!mode && auto_patrol) // still idle, and set to patrol
mode = BOT_START_PATROL // switch to patrol mode
if(BOT_HUNT) // hunting for perp
// if can't reach perp for long enough, go idle
if(frustration >= 8)
walk_to(src,0)
back_to_idle()
return
if(target) // make sure target exists
if(Adjacent(target) && isturf(target.loc)) // if right next to perp
stun_attack(target)
mode = BOT_PREP_ARREST
anchored = 1
target_lastloc = target.loc
return
else // not next to perp
var/turf/olddist = get_dist(src, target)
walk_to(src, target,1,4)
if((get_dist(src, target)) >= (olddist))
frustration++
else
frustration = 0
else
back_to_idle()
if(BOT_PREP_ARREST) // preparing to arrest target
// see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again.
if( !Adjacent(target) || !isturf(target.loc) || target.weakened < 2 )
back_to_hunt()
return
if(iscarbon(target) && target.canBeHandcuffed())
if(!arrest_type)
if(!target.handcuffed) //he's not cuffed? Try to cuff him!
cuff(target)
else
back_to_idle()
return
else
back_to_idle()
return
if(BOT_ARREST)
if(!target)
anchored = 0
mode = BOT_IDLE
last_found = world.time
frustration = 0
return
if(target.handcuffed) //no target or target cuffed? back to idle.
back_to_idle()
return
if(!Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2)) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again.
back_to_hunt()
return
else //Try arresting again if the target escapes.
mode = BOT_PREP_ARREST
anchored = 0
if(BOT_START_PATROL)
look_for_perp()
start_patrol()
if(BOT_PATROL)
look_for_perp()
bot_patrol()
return
/mob/living/simple_animal/bot/secbot/proc/back_to_idle()
anchored = 0
mode = BOT_IDLE
target = null
last_found = world.time
frustration = 0
spawn(0)
handle_automated_action() //ensure bot quickly responds
/mob/living/simple_animal/bot/secbot/proc/back_to_hunt()
anchored = 0
frustration = 0
mode = BOT_HUNT
spawn(0)
handle_automated_action() //ensure bot quickly responds
// look for a criminal in view of the bot
/mob/living/simple_animal/bot/secbot/proc/look_for_perp()
anchored = 0
for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal
if((C.stat) || (C.handcuffed))
continue
if((C.name == oldtarget_name) && (world.time < last_found + 100))
continue
threatlevel = C.assess_threat(src)
if(!threatlevel)
continue
else if(threatlevel >= 4)
target = C
oldtarget_name = C.name
speak("Level [threatlevel] infraction alert!")
playsound(loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50, 0)
visible_message("<b>[src]</b> points at [C.name]!")
mode = BOT_HUNT
spawn(0)
handle_automated_action() // ensure bot quickly responds to a perp
break
else
continue
/mob/living/simple_animal/bot/secbot/proc/check_for_weapons(var/obj/item/slot_item)
if(slot_item && slot_item.needs_permit)
return 1
return 0
/mob/living/simple_animal/bot/secbot/explode()
walk_to(src,0)
visible_message("<span class='userdanger'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
var/obj/item/weapon/secbot_assembly/Sa = new /obj/item/weapon/secbot_assembly(Tsec)
Sa.build_step = 1
Sa.overlays += "hs_hole"
Sa.created_name = name
new /obj/item/device/assembly/prox_sensor(Tsec)
new /obj/item/weapon/melee/baton(Tsec)
if(prob(50))
new /obj/item/robot_parts/l_arm(Tsec)
var/datum/effect/system/spark_spread/s = new /datum/effect/system/spark_spread
s.set_up(3, 1, src)
s.start()
new /obj/effect/decal/cleanable/blood/oil(loc)
..()
/mob/living/simple_animal/bot/secbot/attack_alien(var/mob/living/carbon/alien/user as mob)
..()
if(!isalien(target))
target = user
mode = BOT_HUNT
/mob/living/simple_animal/bot/secbot/Crossed(atom/movable/AM)
if(ismob(AM) && target)
var/mob/living/carbon/C = AM
if(!istype(C) || !C || in_range(src, target))
return
C.visible_message("<span class='warning'>[pick( \
"[C] dives out of [src]'s way!", \
"[C] stumbles over [src]!", \
"[C] jumps out of [src]'s path!", \
"[C] trips over [src] and falls!", \
"[C] topples over [src]!", \
"[C] leaps out of [src]'s way!")]</span>")
C.Weaken(2)
return
..()
/obj/machinery/bot_core/secbot
req_access = list(access_security)
@@ -14,14 +14,7 @@
stop_automated_movement = 1
status_flags = CANPUSH
attack_sound = 'sound/weapons/punch1.ogg'
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
faction = list("cult")
flying = 1
@@ -110,7 +103,7 @@
/mob/living/simple_animal/construct/armoured/Life()
weakened = 0
..()
return ..()
/mob/living/simple_animal/construct/armoured/bullet_act(var/obj/item/projectile/P)
if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam))
@@ -399,11 +399,11 @@
response_disarm = "bops"
response_harm = "kicks"
/mob/living/simple_animal/pet/corgi/Ian/Life()
/mob/living/simple_animal/pet/corgi/Ian/process_ai()
..()
//Feeding, chasing food, FOOOOODDDD
if(!stat && !resting && !buckled && (ckey == null))
if(!resting && !buckled)
turns_since_scan++
if(turns_since_scan > 5)
turns_since_scan = 0
@@ -450,10 +450,9 @@
if(prob(1))
custom_emote(1, pick("dances around.","chases its tail!"))
spawn(0)
if (ckey == null)
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
dir = i
sleep(1)
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
dir = i
sleep(1)
/obj/item/weapon/reagent_containers/food/snacks/meat/corgi
name = "Corgi meat"
@@ -563,20 +562,18 @@
return
..()
/mob/living/simple_animal/pet/corgi/Lisa/Life()
/mob/living/simple_animal/pet/corgi/Lisa/process_ai()
..()
make_babies()
if(!stat && !resting && !buckled)
if(!resting && !buckled)
if(prob(1))
if (ckey == null)
custom_emote(1, pick("dances around.","chases her tail."))
spawn(0)
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
dir = i
sleep(1)
custom_emote(1, pick("dances around.","chases her tail."))
spawn(0)
for(var/i in list(1,2,4,8,4,2,1,2,4,8,4,2,1,2,4,8,4,2))
dir = i
sleep(1)
/mob/living/simple_animal/pet/corgi/attack_hand(mob/living/carbon/human/M)
. = ..()
@@ -25,7 +25,7 @@
pass_flags = PASSTABLE | PASSGRILLE | PASSMOB
var/mouse_color //brown, gray and white, leave blank for random
layer = MOB_LAYER
min_oxy = 16 //Require atleast 16kPA oxygen
atmos_requirements = list("min_oxy" = 16, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 223 //Below -50 Degrees Celcius
maxbodytemp = 323 //Above 50 Degrees Celcius
universal_speak = 0
@@ -41,14 +41,7 @@
/mob/living/simple_animal/mouse/Life()
. = ..()
if(!ckey && stat == CONSCIOUS && prob(0.5))
stat = UNCONSCIOUS
icon_state = "mouse_[mouse_color]_sleep"
wander = 0
speak_chance = 0
//snuffles
else if(stat == UNCONSCIOUS)
if(stat == UNCONSCIOUS)
if(ckey || prob(1))
stat = CONSCIOUS
icon_state = "mouse_[mouse_color]"
@@ -56,6 +49,15 @@
else if(prob(5))
emote("snuffles")
/mob/living/simple_animal/mouse/process_ai()
..()
if(prob(0.5))
stat = UNCONSCIOUS
icon_state = "mouse_[mouse_color]_sleep"
wander = 0
speak_chance = 0
/mob/living/simple_animal/mouse/New()
..()
if(!mouse_color)
@@ -19,10 +19,10 @@
response_harm = "kicks"
see_in_dark = 5
/mob/living/simple_animal/pet/pug/Life()
/mob/living/simple_animal/pet/pug/process_ai()
..()
if(!stat && !resting && !buckled)
if(!resting && !buckled)
if(prob(1))
custom_emote(1, pick("chases its tail."))
spawn(0)
@@ -1,8 +1,6 @@
/mob/living/simple_animal/spiderbot
min_oxy = 0
max_tox = 0
max_co2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 500
@@ -21,14 +21,7 @@
speak_emote = list("hisses")
a_intent = I_HARM
attack_sound = 'sound/weapons/bladeslice.ogg'
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
var/dead = 0
unsuitable_atmos_damage = 15
faction = list("alien")
@@ -50,14 +43,16 @@
var/plant_cooldown = 30
var/plants_off = 0
/mob/living/simple_animal/hostile/alien/drone/Life()
..()
if(!stat)
plant_cooldown--
if(stance==HOSTILE_STANCE_IDLE)
if(!plants_off && prob(10) && plant_cooldown<=0)
plant_cooldown = initial(plant_cooldown)
SpreadPlants()
/mob/living/simple_animal/hostile/alien/drone/process_ai()
. = ..()
if(!.)
return
plant_cooldown--
if(stance==HOSTILE_STANCE_IDLE)
if(!plants_off && prob(10) && plant_cooldown<=0)
plant_cooldown = initial(plant_cooldown)
SpreadPlants()
/mob/living/simple_animal/hostile/alien/sentinel
name = "alien sentinel"
@@ -95,18 +90,18 @@
var/egg_cooldown = 30
var/plant_cooldown = 30
/mob/living/simple_animal/hostile/alien/queen/Life()
/mob/living/simple_animal/hostile/alien/queen/process_ai()
..()
if(!stat)
egg_cooldown--
plant_cooldown--
if(stance==HOSTILE_STANCE_IDLE)
if(!plants_off && prob(10) && plant_cooldown<=0)
plant_cooldown = initial(plant_cooldown)
SpreadPlants()
if(!sterile && prob(10) && egg_cooldown<=0)
egg_cooldown = initial(egg_cooldown)
LayEggs()
egg_cooldown--
plant_cooldown--
if(stance==HOSTILE_STANCE_IDLE)
if(!plants_off && prob(10) && plant_cooldown<=0)
plant_cooldown = initial(plant_cooldown)
SpreadPlants()
if(!sterile && prob(10) && egg_cooldown<=0)
egg_cooldown = initial(egg_cooldown)
LayEggs()
/mob/living/simple_animal/hostile/alien/proc/SpreadPlants()
if(!isturf(loc) || istype(loc, /turf/space))
@@ -23,15 +23,7 @@
attacktext = "bites"
attack_sound = 'sound/weapons/bite.ogg'
//Space carp aren't affected by atmos.
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
// break_stuff_probability = 2
@@ -27,14 +27,7 @@
attack_sound = 'sound/weapons/genhit3.ogg'
//Space bears aren't affected by atmos.
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
var/stance_step = 0
@@ -56,12 +49,11 @@
else
icon_state = "bearfloor"
/mob/living/simple_animal/hostile/bear/Life()
/mob/living/simple_animal/hostile/bear/process_ai()
. = ..()
if(!.)
return
switch(stance)
if(HOSTILE_STANCE_TIRED)
@@ -26,14 +26,7 @@
speak_emote = list("gnashes")
//Space carp aren't affected by atmos.
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 1500
@@ -20,14 +20,7 @@
attack_sound = 'sound/hallucinations/growl1.ogg'
speak_emote = list("growls")
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
speed = 4
@@ -18,14 +18,7 @@
projectilesound = 'sound/weapons/Gunshot.ogg'
projectiletype = /obj/item/projectile/hivebotbullet
faction = list("hivebot")
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
speak_emote = list("states")
@@ -79,37 +72,36 @@
warpbots()
New()
..()
var/datum/effect/system/harmless_smoke_spread/smoke = new /datum/effect/system/harmless_smoke_spread()
smoke.set_up(5, 0, src.loc)
smoke.start()
visible_message("\red <B>The [src] warps in!</B>")
playsound(src.loc, 'sound/effects/EMPulse.ogg', 25, 1)
/mob/living/simple_animal/hostile/hivebot/tele/New()
..()
var/datum/effect/system/harmless_smoke_spread/smoke = new /datum/effect/system/harmless_smoke_spread()
smoke.set_up(5, 0, src.loc)
smoke.start()
visible_message("\red <B>The [src] warps in!</B>")
playsound(src.loc, 'sound/effects/EMPulse.ogg', 25, 1)
warpbots()
icon_state = "def_radar"
visible_message("\red The [src] turns on!")
while(bot_amt > 0)
bot_amt--
switch(bot_type)
if("norm")
var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src))
H.faction = faction
if("range")
var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src))
R.faction = faction
if("rapid")
var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src))
F.faction = faction
spawn(100)
qdel(src)
/mob/living/simple_animal/hostile/hivebot/tele/warpbots()
icon_state = "def_radar"
visible_message("\red The [src] turns on!")
while(bot_amt > 0)
bot_amt--
switch(bot_type)
if("norm")
var/mob/living/simple_animal/hostile/hivebot/H = new /mob/living/simple_animal/hostile/hivebot(get_turf(src))
H.faction = faction
if("range")
var/mob/living/simple_animal/hostile/hivebot/range/R = new /mob/living/simple_animal/hostile/hivebot/range(get_turf(src))
R.faction = faction
if("rapid")
var/mob/living/simple_animal/hostile/hivebot/rapid/F = new /mob/living/simple_animal/hostile/hivebot/rapid(get_turf(src))
F.faction = faction
spawn(100)
qdel(src)
return
/mob/living/simple_animal/hostile/hivebot/tele/process_ai()
. = ..()
if(!.)
return
Life()
..()
if(stat == 0)
if(prob(2))//Might be a bit low, will mess with it likely
warpbots()
if(prob(2))//Might be a bit low, will mess with it likely
warpbots()
@@ -32,33 +32,34 @@
var/AIStatus = AI_ON //The Status of our AI, can be set to AI_ON (On, usual processing), AI_SLEEP (Will not process, but will return to AI_ON if an enemy comes near), AI_OFF (Off, Not processing ever)
/mob/living/simple_animal/hostile/Life()
. = ..()
if(!.)
walk(src, 0)
return 0
if(ranged)
ranged_cooldown--
if(client)
return 0
/mob/living/simple_animal/hostile/process_ai()
..()
if(!AICanContinue())
return 0
if(!stat)
switch(stance)
if(HOSTILE_STANCE_IDLE)
var/new_target = FindTarget()
GiveTarget(new_target)
switch(stance)
if(HOSTILE_STANCE_IDLE)
var/new_target = FindTarget()
GiveTarget(new_target)
if(HOSTILE_STANCE_ATTACK)
MoveToTarget()
DestroySurroundings()
if(HOSTILE_STANCE_ATTACK)
MoveToTarget()
DestroySurroundings()
if(HOSTILE_STANCE_ATTACKING)
AttackTarget()
DestroySurroundings()
if(HOSTILE_STANCE_ATTACKING)
AttackTarget()
DestroySurroundings()
if(AIShouldSleep())
AIStatus = AI_SLEEP
if(AIShouldSleep())
AIStatus = AI_SLEEP
return 1
//////////////HOSTILE MOB TARGETTING AND AGGRESSION////////////
@@ -23,14 +23,7 @@
attack_sound = 'sound/weapons/bite.ogg'
speak_emote = list("creaks")
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
faction = list("mimic")
@@ -1,13 +1,6 @@
/mob/living/simple_animal/hostile/asteroid/
vision_range = 2
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 15
faction = list("mining")
environment_smash = 2
@@ -387,7 +380,7 @@
anchored = 1 //Stays anchored until death as to be unpullable
var/pre_attack = 0
/mob/living/simple_animal/hostile/asteroid/goliath/Life()
/mob/living/simple_animal/hostile/asteroid/goliath/process_ai()
..()
handle_preattack()
@@ -19,14 +19,7 @@
attacktext = "slashes"
attack_sound = 'sound/weapons/bladeslice.ogg'
min_oxy = 5
max_oxy = 0
min_tox = 0
max_tox = 1
min_co2 = 0
max_co2 = 5
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
unsuitable_atmos_damage = 15
speak_emote = list("yarrs")
var/corpse = /obj/effect/landmark/mobcorpse/pirate
@@ -22,14 +22,7 @@
flying = 1
//Spaceborn beings don't get hurt by space
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
/mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(var/check_drift = 0)
@@ -23,15 +23,6 @@
attacktext = "attacks"
attack_sound = 'sound/items/bikehorn.ogg'
environment_smash = 0
min_oxy = 5
max_oxy = 0
min_tox = 0
max_tox = 1
min_co2 = 0
max_co2 = 5
min_n2 = 0
max_n2 = 0
minbodytemp = 270
maxbodytemp = 370
heat_damage_per_tick = 15 //amount of damage applied if animal's body temperature is higher than maxbodytemp
@@ -35,14 +35,7 @@
var/exploding = 0
//Drones aren't affected by atmos.
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
var/has_loot = 1
@@ -17,7 +17,4 @@
melee_damage_lower = 15
melee_damage_upper = 20
min_oxy = 5
max_tox = 2
max_co2 = 5
atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 2, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0)
@@ -41,14 +41,7 @@
attacktext = "grips"
attack_sound = 'sound/hallucinations/growl1.ogg'
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
faction = list("undead") // did I mention ghost
@@ -93,14 +86,7 @@
attacktext = "claws"
attack_sound = 'sound/hallucinations/growl1.ogg'
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
faction = list("undead")
@@ -131,14 +117,7 @@
attacktext = "claws"
attack_sound = 'sound/hallucinations/growl1.ogg'
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
faction = list("undead")
@@ -21,14 +21,6 @@
a_intent = I_HARM
var/corpse = /obj/effect/landmark/mobcorpse/russian
var/weapon1 = /obj/item/weapon/kitchen/knife
min_oxy = 5
max_oxy = 0
min_tox = 0
max_tox = 1
min_co2 = 0
max_co2 = 5
min_n2 = 0
max_n2 = 0
unsuitable_atmos_damage = 15
faction = list("russian")
status_flags = CANPUSH
@@ -26,9 +26,7 @@
minbodytemp = 0
maxbodytemp = 350
min_oxy = 0
max_co2 = 0
max_tox = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
a_intent = I_HARM //so they don't get pushed around
@@ -23,14 +23,7 @@
attacktext = "claws"
attack_sound = 'sound/hallucinations/growl1.ogg'
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
faction = list("statue")
@@ -76,12 +69,12 @@
icon_state = "angel"
return ..()
/mob/living/simple_animal/hostile/statue/Life()
if(..())
if(!ckey)
spawn() //for saftey
checkTargets()
. = 1
/mob/living/simple_animal/hostile/statue/process_ai()
. = ..()
if(!.)
return
spawn() //for saftey
checkTargets()
/mob/living/simple_animal/hostile/statue/proc/checkTargets()
if(target)
@@ -195,6 +188,9 @@
. = ..()
return . - creator
/mob/living/simple_animal/hostile/statue/sentience_act()
faction -= "neutral"
// Statue powers
// Flicker lights
@@ -22,14 +22,6 @@
var/corpse = /obj/effect/landmark/mobcorpse/syndicatesoldier
var/weapon1
var/weapon2
min_oxy = 5
max_oxy = 0
min_tox = 0
max_tox = 1
min_co2 = 0
max_co2 = 5
min_n2 = 0
max_n2 = 0
unsuitable_atmos_damage = 15
faction = list("syndicate")
status_flags = CANPUSH
@@ -85,14 +77,7 @@
/mob/living/simple_animal/hostile/syndicate/melee/space
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
icon_state = "syndicatemeleespace"
icon_living = "syndicatemeleespace"
@@ -120,14 +105,7 @@
icon_state = "syndicaterangedpsace"
icon_living = "syndicaterangedpsace"
name = "Syndicate Commando"
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
corpse = /obj/effect/landmark/mobcorpse/syndicatecommando
speed = 1
@@ -151,14 +129,7 @@
attacktext = "cuts"
attack_sound = 'sound/weapons/bladeslice.ogg'
faction = list("syndicate")
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
flying = 1
@@ -24,15 +24,7 @@
attack_sound = 'sound/weapons/bite.ogg'
speak_emote = list("pines")
//Space carp aren't affected by atmos.
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
faction = list("hostile", "winter")
@@ -14,14 +14,7 @@
icon_living = "placeholder"
icon_dead = "placeholder"
min_oxy = 0
max_oxy = 0
min_tox = 0
max_tox = 0
min_co2 = 0
max_co2 = 0
min_n2 = 0
max_n2 = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
melee_damage_lower = 3
@@ -53,12 +53,12 @@
universal_speak = 1
var/parrot_state = PARROT_WANDER //Hunt for a perch when created
var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in life() being run every single tick.
var/parrot_sleep_max = 25 //The time the parrot sits while perched before looking around. Mosly a way to avoid the parrot's AI in process_ai() being run every single tick.
var/parrot_sleep_dur = 25 //Same as above, this is the var that physically counts down
var/parrot_dam_zone = list("chest", "head", "l_arm", "l_leg", "r_arm", "r_leg") //For humans, select a bodypart to attack
var/parrot_speed = 5 //"Delay in world ticks between movement." according to byond. Yeah, that's BS but it does directly affect movement. Higher number = slower.
var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every Life() and at 0 the parrot will return to regular speed.
var/parrot_been_shot = 0 //Parrots get a speed bonus after being shot. This will deincrement every process_ai() and at 0 the parrot will return to regular speed.
var/list/speech_buffer = list()
var/list/available_channels = list()
@@ -273,14 +273,13 @@
//Sprite and AI update for when a parrot gets pulled
if(pulledby && stat == CONSCIOUS)
icon_state = "parrot_fly"
if(!client)
parrot_state = PARROT_WANDER
icon_state = "parrot_fly"
/mob/living/simple_animal/parrot/process_ai()
if(pulledby)
parrot_state = PARROT_WANDER
return
if(client || stat)
return //Lets not force players or dead/incap parrots to move
if(!isturf(src.loc) || !canmove || buckled)
return //If it can't move, dont let it move. (The buckled check probably isn't necessary thanks to canmove)
@@ -15,9 +15,7 @@
attacktext = "sends rainbows of power to"
minbodytemp = 0
maxbodytemp = 4000
min_oxy = 0
max_co2 = 0
max_tox = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
speed = 0
stop_automated_movement = 0
status_flags = 0
@@ -18,9 +18,7 @@
attacktext = "drains the life from"
minbodytemp = 0
maxbodytemp = 4000
min_oxy = 0
max_co2 = 0
max_tox = 0
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
speed = -1
stop_automated_movement = 1
status_flags = 0
@@ -14,6 +14,7 @@
var/oxygen_alert = 0
var/toxins_alert = 0
var/fire_alert = 0
var/healable = 1
var/list/speak = list()
var/speak_chance = 0
@@ -43,14 +44,7 @@
var/cold_damage_per_tick = 2 //same as heat_damage_per_tick, only if the bodytemperature it's lower than minbodytemp
//Atmos effect - Yes, you can make creatures that require plasma or co2 to survive. N2O is a trace gas and handled separately, hence why it isn't here. It'd be hard to add it. Hard and me don't mix (Yes, yes make all the dick jokes you want with that.) - Errorage
var/min_oxy = 5
var/max_oxy = 0 //Leaving something at 0 means it's off - has no maximum
var/min_tox = 0
var/max_tox = 1
var/min_co2 = 0
var/max_co2 = 5
var/min_n2 = 0
var/max_n2 = 0
var/list/atmos_requirements = list("min_oxy" = 5, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 1, "min_co2" = 0, "max_co2" = 5, "min_n2" = 0, "max_n2" = 0) //Leaving something at 0 means it's off - has no maximum
var/unsuitable_atmos_damage = 2 //This damage is taken when atmos doesn't fit all the requirements above
@@ -58,7 +52,7 @@
var/melee_damage_lower = 0
var/melee_damage_upper = 0
var/melee_damage_type = BRUTE //Damage type of a simple mob's melee attack, should it do damage.
var/list/ignored_damage_types = list(BRUTE = 0, BURN = 0, TOX = 0, CLONE = 0, STAMINA = 1, OXY = 0) //Set 0 to receive that damage type, 1 to ignore
var/list/damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 1, CLONE = 1, STAMINA = 0, OXY = 1) // 1 for full damage , 0 for none , -1 for 1:1 heal from that source
var/attacktext = "attacks"
var/attack_sound = null
var/friendly = "nuzzles" //If the mob does no damage with it's attack
@@ -76,10 +70,12 @@
var/simplespecies //Sorry, no spider+corgi buttbabies.
var/master_commander = null //holding var for determining who own/controls a sentient simple animal (for sentience potions).
var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions
/mob/living/simple_animal/New()
..()
simple_animal_list += src
verbs -= /mob/verb/observe
if(!can_hide)
verbs -= /mob/living/simple_animal/verb/hide
@@ -92,6 +88,7 @@
if(collar)
collar.forceMove(loc)
collar = null
simple_animal_list -= src
return ..()
/mob/living/simple_animal/Login()
@@ -130,16 +127,14 @@
if(0) healths.icon_state = "health7"
/mob/living/simple_animal/Life()
if(..())
if(!ckey)
handle_automated_movement()
handle_automated_action()
handle_automated_speech()
. = 1
. = ..()
handle_state_icons()
/mob/living/simple_animal/proc/process_ai()
handle_automated_movement()
handle_automated_action()
handle_automated_speech()
/mob/living/simple_animal/proc/handle_state_icons()
if(resting && icon_resting && stat != DEAD)
icon_state = icon_resting
@@ -218,32 +213,32 @@
var/n2 = environment.nitrogen
var/co2 = environment.carbon_dioxide
if(min_oxy && oxy < min_oxy)
if(atmos_requirements["min_oxy"] && oxy < atmos_requirements["min_oxy"])
atmos_suitable = 0
oxygen_alert = 1
else if(max_oxy && oxy > max_oxy)
else if(atmos_requirements["max_oxy"] && oxy > atmos_requirements["max_oxy"])
atmos_suitable = 0
oxygen_alert = 1
else
oxygen_alert = 0
if(min_tox && tox < min_tox)
if(atmos_requirements["min_tox"] && tox < atmos_requirements["min_tox"])
atmos_suitable = 0
toxins_alert = 1
else if(max_tox && tox > max_tox)
else if(atmos_requirements["max_tox"] && tox > atmos_requirements["max_tox"])
atmos_suitable = 0
toxins_alert = 1
else
toxins_alert = 0
if(min_n2 && n2 < min_n2)
if(atmos_requirements["min_n2"] && n2 < atmos_requirements["min_n2"])
atmos_suitable = 0
else if(max_n2 && n2 > max_n2)
else if(atmos_requirements["max_n2"] && n2 > atmos_requirements["max_n2"])
atmos_suitable = 0
if(min_co2 && co2 < min_co2)
if(atmos_requirements["min_co2"] && co2 < atmos_requirements["min_co2"])
atmos_suitable = 0
else if(max_co2 && co2 > max_co2)
else if(atmos_requirements["max_co2"] && co2 > atmos_requirements["max_co2"])
atmos_suitable = 0
if(!atmos_suitable)
@@ -414,7 +409,7 @@
return
/mob/living/simple_animal/attackby(var/obj/item/O as obj, var/mob/living/user as mob) //Marker -Agouri
if(istype(O, /obj/item/stack/medical))
if(istype(O, /obj/item/stack/medical) && healable)
if(stat != DEAD)
var/obj/item/stack/medical/MED = O
if(health < maxHealth)
@@ -510,21 +505,29 @@
if(3.0)
adjustBruteLoss(30)
/mob/living/simple_animal/adjustBruteLoss(damage)
if(!ignored_damage_types[BRUTE])
..()
/mob/living/simple_animal/adjustBruteLoss(amount)
if(damage_coeff[BRUTE])
return ..(amount * damage_coeff[BRUTE])
/mob/living/simple_animal/adjustFireLoss(damage)
if(!ignored_damage_types[BURN])
..(damage)
/mob/living/simple_animal/adjustFireLoss(amount)
if(damage_coeff[BURN])
return ..(amount * damage_coeff[BURN])
/mob/living/simple_animal/adjustToxLoss(damage)
if(!ignored_damage_types[TOX])
..(damage)
/mob/living/simple_animal/adjustOxyLoss(amount)
if(damage_coeff[OXY])
return ..(amount * damage_coeff[OXY])
/mob/living/simple_animal/adjustCloneLoss(damage)
if(!ignored_damage_types[CLONE])
..(damage)
/mob/living/simple_animal/adjustToxLoss(amount)
if(damage_coeff[TOX])
return ..(amount * damage_coeff[TOX])
/mob/living/simple_animal/adjustCloneLoss(amount)
if(damage_coeff[CLONE])
return ..(amount * damage_coeff[CLONE])
/mob/living/simple_animal/adjustStaminaLoss(amount)
if(damage_coeff[STAMINA])
return ..(amount*damage_coeff[STAMINA])
/mob/living/simple_animal/proc/CanAttack(var/atom/the_target)
if(see_invisible < the_target.invisibility)
@@ -544,11 +547,10 @@
return 1
/mob/living/simple_animal/proc/attack_threshold_check(damage, damagetype = BRUTE)
if(damage <= force_threshold || ignored_damage_types[damagetype])
if(damage <= force_threshold || !damage_coeff[damagetype])
visible_message("<span class='warning'>[src] looks unharmed from the damage.</span>")
else
adjustBruteLoss(damage)
updatehealth()
apply_damage(damage, damagetype)
/mob/living/simple_animal/update_fire()
@@ -699,4 +701,7 @@
/mob/living/simple_animal/get_access()
. = ..()
if(collar)
. |= collar.GetAccess()
. |= collar.GetAccess()
/mob/living/simple_animal/proc/sentience_act() //Called when a simple animal gains sentience via gold slime potion
return
+1 -11
View File
@@ -749,16 +749,6 @@ var/list/slot_equipment_priority = list( \
namecounts[name] = 1
creatures[name] = O
if(istype(O, /obj/machinery/bot))
var/name = "BOT: [O.name]"
if (names.Find(name))
namecounts[name]++
name = "[name] ([namecounts[name]])"
else
names.Add(name)
namecounts[name] = 1
creatures[name] = O
for(var/mob/M in sortAtom(mob_list))
var/name = M.name
@@ -861,9 +851,9 @@ var/list/slot_equipment_priority = list( \
..()
if(M != usr) return
if(M.small) return // Stops pAI drones and small mobs (borers, parrots, crabs) from stripping people. --DZD
if(!M.can_strip) return
if(usr == src) return
if(!Adjacent(usr)) return
if(istype(M,/mob/living/silicon/ai)) return
show_inv(usr)
//this and stop_pulling really ought to be /mob/living procs
+1
View File
@@ -87,6 +87,7 @@
var/lastpuke = 0
var/unacidable = 0
var/small = 0
var/can_strip = 1
var/list/pinned = list() //List of things pinning this creature to walls (see living_defense.dm)
var/list/embedded = list() //Embedded items, since simple mobs don't have organs.
var/list/languages = list() // For speaking/listening.
+5
View File
@@ -94,6 +94,11 @@ proc/isembryo(A)
return 1
return 0
/proc/isbot(A)
if(istype(A, /mob/living/simple_animal/bot))
return 1
return 0
/proc/isclown(A)
if(istype(A, /mob/living/simple_animal/hostile/retaliate/clown))
return 1
+6
View File
@@ -96,4 +96,10 @@
if(. != STATUS_CLOSE)
. = min(., shared_living_nano_distance(src_object))
if(!IsAdvancedToolUser())
. = STATUS_CLOSE
/mob/living/simple_animal/bot/default_can_use_topic(var/src_object)
. = shared_nano_interaction(src_object)
if(. != STATUS_CLOSE)
if(!(src_object in contents))
. = STATUS_CLOSE
+1 -1
View File
@@ -187,7 +187,7 @@ nanoui is used to open and update nano browser uis
"showMap" = show_map,
"mapZLevel" = map_z_level,
"user" = list(
"name" = user.name,
"name" = sanitize(user.name),
"fancy" = user.client.prefs.nanoui_fancy
),
"window" = list(
+4 -4
View File
@@ -96,7 +96,7 @@
/datum/data/pda/app/power/update_ui(mob/user as mob, list/data)
update = PDA_APP_UPDATE_SLOW
if (powmonitor && !isnull(powmonitor.powernet))
data["records"] = list(
"powerconnected" = 1,
@@ -226,7 +226,7 @@
beepskyData["botstatus"] = list("loca" = null, "mode" = -1)
var/botsCount=0
if(SC.botlist && SC.botlist.len)
for(var/obj/machinery/bot/B in SC.botlist)
for(var/mob/living/simple_animal/bot/B in SC.botlist)
botsCount++
if(B.loc)
botsData[++botsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]")
@@ -276,7 +276,7 @@
var/mulebotsCount=0
for(var/obj/machinery/bot/B in QC.botlist)
for(var/mob/living/simple_animal/bot/B in QC.botlist)
mulebotsCount++
if(B.loc)
mulebotsData[++mulebotsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]")
@@ -392,7 +392,7 @@
BucketData[++BucketData.len] = list("x" = 0, "y" = 0, dir=null, status = null)
var/CbotData[0]
for(var/obj/machinery/bot/cleanbot/B in aibots)
for(var/mob/living/simple_animal/bot/cleanbot/B in simple_animal_list)
var/turf/bl = get_turf(B)
if(bl)
if(bl.z != cl.z)
+27 -92
View File
@@ -5,7 +5,7 @@
icon_state = "power_mod"
var/obj/item/device/pda/hostpda = null
var/list/botlist = null // list of bots
var/obj/machinery/bot/active // the active bot; if null, show bot list
var/mob/living/simple_animal/bot/active // the active bot; if null, show bot list
var/list/botstatus // the status signal sent by the bot
var/bot_type //The type of bot it is.
var/bot_filter //Determines which radio filter to use.
@@ -17,9 +17,9 @@
/obj/item/radio/integrated/New()
..()
if (istype(loc.loc, /obj/item/device/pda))
if(istype(loc.loc, /obj/item/device/pda))
hostpda = loc.loc
if (bot_filter)
if(bot_filter)
spawn(5)
add_to_radio(bot_filter)
@@ -51,31 +51,22 @@
frequency.post_signal(src, signal, filter = s_filter)
/obj/item/radio/integrated/receive_signal(datum/signal/signal)
/*var/obj/item/device/pda/P = src.loc
world << "recvd:[P] : [signal.source]"
for(var/d in signal.data)
world << "- [d] = [signal.data[d]]"
*/
if (signal.data["type"] == bot_type)
if(!botlist)
botlist = new()
if(!(signal.source in botlist))
botlist += signal.source
var/obj/machinery/bot_core/core = signal.source
if(active == signal.source)
if(istype(core) && !(core.owner in botlist))
botlist += core.owner
if(active == core.owner)
var/list/b = signal.data
botstatus = b.Copy()
/obj/item/radio/integrated/Topic(href, href_list)
..()
var/obj/item/device/pda/PDA = src.hostpda
switch(href_list["op"])
if("control")
active = locate(href_list["bot"])
spawn(0)
@@ -89,14 +80,14 @@
if("botlist")
active = null
if("stop", "go")
if("stop", "go", "home")
spawn(0)
post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter)
post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter)
if("summon")
spawn(0)
post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(PDA) , "useraccess", PDA.GetAccess(), s_filter = bot_filter)
post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(hostpda), "useraccess", hostpda.GetAccess(), "user", usr, s_filter = bot_filter)
post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter)
/obj/item/radio/integrated/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots.
@@ -121,101 +112,45 @@
bot_type = CLEAN_BOT
/obj/item/radio/integrated/mule
//var/list/botlist = null // list of bots
//var/obj/machinery/bot/mulebot/active // the active bot; if null, show bot list
//var/list/botstatus // the status signal sent by the bot
var/list/beacons
var/beacon_freq = 1400
control_freq = 1447
// create a new QM cartridge, and register to receive bot control & beacon message
/obj/item/radio/integrated/mule/New()
..()
spawn(5)
if(radio_controller)
radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT)
radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
spawn(10)
post_signal(beacon_freq, "findbeacon", "delivery", s_filter = RADIO_NAVBEACONS)
/obj/item/radio/integrated/mule/Destroy()
if(radio_controller)
radio_controller.remove_object(src,beacon_freq)
return ..()
// receive radio signals
// can detect bot status signals
// and beacon locations
// create/populate lists as they are recvd
/obj/item/radio/integrated/mule/receive_signal(datum/signal/signal)
if(signal.data["type"] == MULE_BOT)
if(!botlist)
botlist = new()
if(!(signal.source in botlist))
botlist += signal.source
if(active == signal.source)
var/list/b = signal.data
botstatus = b.Copy()
else if(signal.data["beacon"])
if(!beacons)
beacons = new()
beacons[signal.data["beacon"] ] = signal.source
bot_filter = RADIO_MULEBOT
bot_type = MULE_BOT
/obj/item/radio/integrated/mule/Topic(href, href_list)
//..()
//var/obj/item/device/pda/PDA = src.hostpda
var/cmd = "command"
if(active)
cmd = "command [active.suffix]"
..()
switch(href_list["op"])
if("control")
active = locate(href_list["bot"])
if("scanbots") // find all bots
botlist = null
if("botlist")
active = null
if("start")
spawn(0)
post_signal(control_freq, "command", "start", "active", active, s_filter = RADIO_MULEBOT)
if("unload")
spawn(0)
post_signal(control_freq, cmd, "unload", s_filter = RADIO_MULEBOT)
post_signal(control_freq, "command", "unload", "active", active, s_filter = RADIO_MULEBOT)
if("setdest")
if(beacons)
var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in beacons
if(deliverybeacons)
var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in deliverybeacontags
if(dest)
spawn(0)
post_signal(control_freq, cmd, "target", "destination", dest, s_filter = RADIO_MULEBOT)
post_signal(control_freq, "command", "target", "active", active, "destination", dest, s_filter = RADIO_MULEBOT)
if("retoff")
spawn(0)
post_signal(control_freq, cmd, "autoret", "value", 0, s_filter = RADIO_MULEBOT)
post_signal(control_freq, "command", "autoret", "active", active, "value", 0, s_filter = RADIO_MULEBOT)
if("reton")
spawn(0)
post_signal(control_freq, cmd, "autoret", "value", 1, s_filter = RADIO_MULEBOT)
post_signal(control_freq, "command", "autoret", "active", active, "value", 1, s_filter = RADIO_MULEBOT)
if("pickoff")
spawn(0)
post_signal(control_freq, cmd, "autopick", "value", 0, s_filter = RADIO_MULEBOT)
post_signal(control_freq, "command", "autopick", "active", active, "value", 0, s_filter = RADIO_MULEBOT)
if("pickon")
spawn(0)
post_signal(control_freq, cmd, "autopick", "value", 1, s_filter = RADIO_MULEBOT)
if("stop", "go", "home")
spawn(0)
post_signal(control_freq, cmd, href_list["op"], s_filter = RADIO_MULEBOT)
post_signal(control_freq, "command", "autopick", "active", active, "value", 1, s_filter = RADIO_MULEBOT)
spawn(10)
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_MULEBOT)
@@ -28,7 +28,7 @@
/obj/machinery/atmospherics/unary/cryo_cell,
/obj/machinery/dna_scannernew,
/obj/item/weapon/grenade/chem_grenade,
/obj/machinery/bot/medbot,
/mob/living/simple_animal/bot/medbot,
/obj/item/weapon/storage/secure/safe,
/obj/machinery/iv_drip,
/obj/machinery/disease2/incubator,
+14 -1
View File
@@ -780,7 +780,8 @@
var/list/mobs_can_pass = list(
/mob/living/carbon/slime,
/mob/living/simple_animal/mouse,
/mob/living/silicon/robot/drone
/mob/living/silicon/robot/drone,
/mob/living/simple_animal/bot/mulebot
)
/obj/structure/plasticflaps/CanPass(atom/A, turf/T)
@@ -814,6 +815,18 @@
return ..()
/obj/structure/plasticflaps/CanAStarPass(ID, to_dir, caller)
if(istype(caller, /mob/living))
for(var/mob_type in mobs_can_pass)
if(istype(caller, mob_type))
return 1
var/mob/living/M = caller
if(!M.ventcrawler && !M.small)
return 0
return 1
/obj/structure/plasticflaps/ex_act(severity)
switch(severity)
if (1)