Convert bots to simple animals - Updated 7.0

This commit is contained in:
AnturK
2015-11-26 22:04:47 +01:00
parent f3c1ca6cde
commit e36853d7c9
38 changed files with 1389 additions and 1291 deletions
+5 -5
View File
@@ -71,7 +71,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,
@@ -89,7 +89,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/sec = 1,
/obj/item/weapon/melee/baton = 1,
@@ -101,7 +101,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)
@@ -110,7 +110,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,
@@ -120,7 +120,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,
+1 -1
View File
@@ -37,7 +37,7 @@
M << "<br>"
if(botEmagChance)
for(var/obj/machinery/bot/bot in machines)
for(var/mob/living/simple_animal/bot/bot in living_mob_list)
if(prob(botEmagChance))
bot.Emag()
@@ -272,7 +272,7 @@
// called when something steps onto a human
// this could be made more general, but for now just handle mulebot
/mob/living/carbon/human/Crossed(atom/movable/AM)
var/obj/machinery/bot/mulebot/MB = AM
var/mob/living/simple_animal/bot/mulebot/MB = AM
if(istype(MB))
MB.RunOver(src)
@@ -596,7 +596,7 @@
else
return null
/mob/living/carbon/human/assess_threat(obj/machinery/bot/secbot/judgebot, lasercolor)
/mob/living/carbon/human/assess_threat(mob/living/simple_animal/bot/secbot/judgebot, lasercolor)
if(judgebot.emagged == 2)
return 10 //Everyone is a criminal!
@@ -858,4 +858,4 @@
return
if(!isturf(M.loc) && M.loc != src)
return
return 1
return 1
-1
View File
@@ -68,7 +68,6 @@
breath_moles = environment.total_moles()*BREATH_PERCENTAGE
breath = loc.remove_air(breath_moles)
else //Breathe from loc as obj again
if(istype(loc, /obj/))
var/obj/loc_as_obj = loc
@@ -236,7 +236,7 @@
/mob/living/carbon/monkey/canBeHandcuffed()
return 1
/mob/living/carbon/monkey/assess_threat(obj/machinery/bot/secbot/judgebot, lasercolor)
/mob/living/carbon/monkey/assess_threat(mob/living/simple_animal/bot/secbot/judgebot, lasercolor)
if(judgebot.emagged == 2)
return 10 //Everyone is a criminal!
var/threatcount = 0
+4 -4
View File
@@ -35,7 +35,7 @@ var/list/ai_list = list()
radiomod = ";" //AIs will, by default, state their laws on the internal radio.
var/obj/item/device/pda/ai/aiPDA = null
var/obj/item/device/multitool/aiMulti = null
var/obj/machinery/bot/Bot
var/mob/living/simple_animal/bot/Bot
var/tracking = 0 //this is 1 if the AI is currently tracking somebody, but the track has not yet been completed.
var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N
@@ -446,14 +446,14 @@ var/list/ai_list = list()
src << "Target is not on or near any active cameras on the station."
return
if(href_list["callbot"]) //Command a bot to move to a selected location.
Bot = locate(href_list["callbot"]) in SSbot.processing
Bot = locate(href_list["callbot"]) in living_mob_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
src << "<span class='notice'>Set your waypoint by clicking on a valid location free of obstructions.</span>"
return
if(href_list["interface"]) //Remotely connect to a bot!
Bot = locate(href_list["interface"]) in SSbot.processing
Bot = locate(href_list["interface"]) in living_mob_list
if(!Bot || Bot.remote_disabled || src.control_disabled)
return
Bot.attack_ai(src)
@@ -525,7 +525,7 @@ var/list/ai_list = 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='30%'><h3>Status</h3></td><td width='30%'><h3>Location</h3></td><td width='10%'><h3>Control</h3></td></tr>"
for (Bot in SSbot.processing)
for (Bot in living_mob_list)
if(Bot.z == ai_Zlevel && !Bot.remote_disabled) //Only non-emagged bots on the same Z-level are detected!
bot_area = get_area(Bot)
d += "<tr><td width='30%'>[Bot.hacked ? "<span class='bad'>(!)</span>" : ""] [Bot.name] ([Bot.model])</td>"
@@ -0,0 +1,760 @@
#define BOT_STEP_DELAY 4 //Delay between movemements
#define DEFAULT_SCAN_RANGE 7 //default view range for finding targets.
//Mode defines
#define BOT_IDLE 0 // idle
#define BOT_HUNT 1 // found target, hunting
#define BOT_PREP_ARREST 2 // at target, preparing to arrest
#define BOT_ARREST 3 // arresting target
#define BOT_START_PATROL 4 // start patrol
#define BOT_PATROL 5 // patrolling
#define BOT_SUMMON 6 // summoned by PDA
#define BOT_CLEANING 7 // cleaning (cleanbots)
#define BOT_REPAIRING 8 // repairing hull breaches (floorbots)
#define BOT_MOVING 9 // for clean/floor/med bots, when moving.
#define BOT_HEALING 10 // healing people (medbots)
#define BOT_RESPONDING 11 // responding to a call from the AI
#define BOT_DELIVER 12 // moving to deliver
#define BOT_GO_HOME 13 // returning to home
#define BOT_BLOCKED 14 // blocked
#define BOT_NAV 15 // computing navigation
#define BOT_WAIT_FOR_NAV 16 // waiting for nav computation
#define BOT_NO_ROUTE 17 // no destination beacon found (or no route)
// AI (i.e. game AI, not the AI player) controlled bots
/mob/living/simple_animal/bot
icon = 'icons/obj/aibots.dmi'
layer = MOB_LAYER
luminosity = 3
stop_automated_movement = 1
wander = 0
healable = 0
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 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
has_unlimited_silicon_privilege = 1
sentience_type = SENTIENCE_ARTIFICIAL
var/obj/machinery/bot_core/bot_core = null
var/bot_core_type = /obj/machinery/bot_core
var/list/users = list() //for dialog updates
var/window_id = "bot_control"
var/window_name = "Protobot 1.0" //Popup title
var/window_width = 0 //0 for default size
var/window_height = 0
var/list/player_access = list() //Additonal access the bots gets when player controlled
var/emagged = 0
var/list/prev_access = list()
var/on = 1
var/open = 0//Maint panel
var/locked = 1
var/hacked = 0 //Used to differentiate between being hacked by silicons and emagged by humans.
var/text_hack = "" //Custom text returned to a silicon upon hacking a bot.
var/text_dehack = "" //Text shown when resetting a bots hacked status to normal.
var/text_dehack_fail = "" //Shown when a silicon tries to reset a bot emagged with the emag item, which cannot be reset.
var/declare_message = "" //What the bot will display to the HUD user.
var/frustration = 0 //Used by some bots for tracking failures to reach their target.
var/base_speed = 2 //The speed at which the bot moves, or the number of times it moves per process() tick.
var/turf/ai_waypoint //The end point of a bot's path, or the target location.
var/list/path = list() //List of turfs through which a bot 'steps' to reach the waypoint.
var/pathset = 0
var/list/ignore_list = list() //List of unreachable targets for an ignore-list enabled bot to ignore.
var/mode = BOT_IDLE //Standardizes the vars that indicate the bot is busy with its function.
var/tries = 0 //Number of times the bot tried and failed to move.
var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras.
var/mob/living/silicon/ai/calling_ai //Links a bot to the AI calling it.
var/obj/item/device/radio/Radio //The bot's radio, for speaking to people.
var/radio_key = null //which channels can the bot listen to
var/radio_channel = "Common" //The bot's default radio channel
var/auto_patrol = 0// set to make bot automatically patrol
var/turf/patrol_target // this is turf to navigate to (location of beacon)
var/turf/summon_target // The turf of a user summoning a bot.
var/new_destination // pending new destination (waiting for beacon response)
var/destination // destination description tag
var/next_destination // the next destination in the patrol route
var/blockcount = 0 //number of times retried a blocked path
var/awaiting_beacon = 0 // count of pticks awaiting a beacon response
var/nearest_beacon // the nearest beacon's tag
var/turf/nearest_beacon_loc // the nearest beacon's location
var/beacon_freq = 1445 // navigation beacon frequency
var/model = "" //The type of bot it is.
var/bot_type = 0 //The type of bot it is, for radio control.
var/list/mode_name = list("In Pursuit","Preparing to Arrest", "Arresting", \
"Beginning Patrol", "Patrolling", "Summoned by PDA", \
"Cleaning", "Repairing", "Proceeding to work site", "Healing", \
"Proceeding to AI waypoint", "Navigating to Delivery Location", "Navigating to Home", \
"Waiting for clear path", "Calculating navigation path", "Pinging beacon network", "Unable to reach destination")
//This holds text for what the bot is mode doing, reported on the remote bot control interface.
/mob/living/simple_animal/bot/proc/get_mode()
if(!mode)
return "Idle"
else
return mode_name[mode]
/mob/living/simple_animal/bot/proc/turn_on()
if(stat) return 0
on = 1
SetLuminosity(initial(luminosity))
update_icon()
return 1
/mob/living/simple_animal/bot/proc/turn_off()
on = 0
SetLuminosity(0)
bot_reset() //Resets an AI's call, should it exist.
update_icon()
/mob/living/simple_animal/bot/New()
..()
access_card = new /obj/item/weapon/card/id(src)
//This access is so bots can be immediately set to patrol and leave Robotics, instead of having to be let out first.
access_card.access += access_robotics
set_custom_texts()
Radio = new/obj/item/device/radio(src)
if(radio_key)
Radio.keyslot = new radio_key
Radio.canhear_range = 1 // 0 ?
Radio.recalculateChannels()
bot_core = new bot_core_type(src)
/mob/living/simple_animal/bot/Destroy()
qdel(Radio)
qdel(access_card)
qdel(bot_core)
return ..()
/mob/living/simple_animal/bot/death(gibbed)
explode()
..()
/mob/living/simple_animal/bot/proc/explode()
qdel(src)
/mob/living/simple_animal/bot/proc/Emag(mob/user) //Master Emag proc. Ensure this is called in your bot before setting unique functions.
if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again.
locked = 0
emagged = 1
user << "<span class='notice'>You bypass [src]'s controls.</span>"
if(!locked && open) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging.
emagged = 2
remote_disabled = 1 //Manually emagging the bot locks out the AI built in panel.
locked = 1 //Access denied forever!
bot_reset()
turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP.
else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet.
user << "<span class='warning'>You need to open maintenance panel first!</span>"
/mob/living/simple_animal/bot/examine(mob/user)
..()
if (health < maxHealth)
if (health > maxHealth/3)
user << "[src]'s parts look loose."
else
user << "[src]'s parts look very loose!"
else
user << "[src] is in pristine condition."
/mob/living/simple_animal/bot/adjustBruteLoss(amount)
if(amount>0 && prob(10))
new /obj/effect/decal/cleanable/oil(loc)
return ..(amount)
/mob/living/simple_animal/bot/adjustFireLoss(amount)
if(amount>0 && prob(10))
new /obj/effect/decal/cleanable/oil(loc)
return ..(amount)
/mob/living/simple_animal/bot/handle_automated_action() //Master process which handles code common across most bots.
set background = BACKGROUND_ENABLED
if(!on || ckey)
return
switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command.
if(BOT_RESPONDING) //Called by the AI.
call_mode()
return
if(BOT_SUMMON) //Called by PDA
bot_summon()
return
return 1 //Successful completion. Used to prevent child process() continuing if this one is ended early.
/mob/living/simple_animal/bot/attack_hand(mob/living/carbon/human/M)
if(M.a_intent == "help")
show_controls(M)
else
return ..()
/mob/living/simple_animal/bot/attackby(obj/item/weapon/W, mob/user, params)
if(istype(W, /obj/item/weapon/screwdriver))
if(!locked)
open = !open
user << "<span class='notice'>Maintenance panel is now [open ? "opened" : "closed"].</span>"
else
user << "<span class='warning'>Maintenance panel is locked.</span>"
else if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda))
if(bot_core.allowed(user) && !open && !emagged)
locked = !locked
user << "Controls are now [locked ? "locked." : "unlocked."]"
else
if(emagged)
user << "<span class='warning'>ERROR</span>"
if(open)
user << "<span class='danger'>Please close the access panel before locking it.</span>"
else
user << "<span class='danger'>Access denied.</span>"
else
user.changeNext_move(CLICK_CD_MELEE)
if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm")
if(health >= maxHealth)
user << "<span class='warning'>[src] does not need a repair!</span>"
return
if(!open)
user << "<span class='warning'>Unable to repair with the maintenance panel closed!</span>"
return
var/obj/item/weapon/weldingtool/WT = W
if(WT.remove_fuel(0, user))
health = min(maxHealth, health+10)
user.visible_message("[user] repairs [src]!","<span class='notice'>You repair [src].</span>")
else
user << "<span class='warning'>The welder must be on for this task!</span>"
else
if(W.force) //if force is non-zero
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(5, 1, src)
s.start()
..()
/mob/living/simple_animal/bot/emag_act(mob/user)
if(emagged < 2)
Emag(user)
/mob/living/simple_animal/bot/bullet_act(obj/item/projectile/Proj)
if(Proj && (Proj.damage_type == BRUTE || Proj.damage_type == BURN))
if(prob(75) && Proj.damage > 0)
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(5, 1, src)
s.start()
return ..()
/mob/living/simple_animal/bot/emp_act(severity)
var/was_on = on
stat |= EMPED
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)
if (on)
turn_off()
spawn(severity*300)
stat &= ~EMPED
if (was_on)
turn_on()
/mob/living/simple_animal/bot/proc/set_custom_texts() //Superclass for setting hack texts. Appears only if a set is not given to a bot locally.
text_hack = "You hack [name]."
text_dehack = "You reset [name]."
text_dehack_fail = "You fail to reset [name]."
/mob/living/simple_animal/bot/attack_ai(mob/user as mob)
attack_hand(user)
/mob/living/simple_animal/bot/proc/speak(message,channel) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio.
if((!on) || (!message))
return
if(channel)
if(!Radio.channels[channel]) //Ignore lack of keys
Radio.channels[channel] = 1
Radio.talk_into(src, message, channel)
Radio.channels[channel] = 0
else
Radio.talk_into(src, message, channel)
else
say(message)
return
/mob/living/simple_animal/bot/say(message)
return ..(message, "R")
/mob/living/simple_animal/bot/get_spans()
return ..() | SPAN_ROBOT
//Generalized behavior code, override where needed!
/*
scan() will search for a given type (such as turfs, human mobs, or objects) in the bot's view range, and return a single result.
Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the old scan result to be ignored, if one exists,
and the view range, which defaults to 7 (full screen) if an override is not passed.
If the bot maintains an ignore list, it is also checked here.
Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1)
The proc would return a human next to the bot to be set to the patient var.
Pass the desired type path itself, declaring a temporary var beforehand is not required.
*/
/mob/living/simple_animal/bot/proc/scan(scan_type, old_target, scan_range = DEFAULT_SCAN_RANGE)
var/final_result
for (var/scan in view (scan_range, src) ) //Search for something in range!
if(!istype(scan, scan_type)) //Check that the thing we found is the type we want!
continue //If not, keep searching!
if( (scan in ignore_list) || (scan == old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness
continue
var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected.
if( scan_result )
final_result = scan_result
else
continue //The current element failed assessment, move on to the next.
return final_result
//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default.
/mob/living/simple_animal/bot/proc/process_scan(scan_target)
return scan_target
/mob/living/simple_animal/bot/proc/add_to_ignore(subject)
if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot.
ignore_list |= subject
else if (ignore_list.len >= subject) //If the list is full, insert newest, delete oldest.
ignore_list -= ignore_list[1]
ignore_list |= subject
/*
Movement proc for stepping a bot through a path generated through A-star.
Pass a positive integer as an argument to override a bot's default speed.
*/
/mob/living/simple_animal/bot/proc/bot_move(dest, move_speed)
if(!dest || !path || path.len == 0) //A-star failed or a path/destination was not set.
path = list()
return 0
dest = get_turf(dest) //We must always compare turfs, so get the turf of the dest var if dest was originally something else.
var/turf/last_node = get_turf(path[path.len]) //This is the turf at the end of the path, it should be equal to dest.
if(get_turf(src) == dest) //We have arrived, no need to move again.
return 1
else if (dest != last_node) //The path should lead us to our given destination. If this is not true, we must stop.
path = list()
return 0
var/step_count = move_speed ? move_speed : base_speed //If a value is passed into move_speed, use that instead of the default speed var.
if(step_count >= 1 && tries < 4)
for(var/step_number = 0, step_number < step_count,step_number++)
spawn(BOT_STEP_DELAY*step_number)
bot_step(dest)
else
return 0
return 1
/mob/living/simple_animal/bot/proc/bot_step(dest) //Step,increase tries if failed
if(!path)
return 0
if(path.len > 1)
step_towards(src, path[1])
if(get_turf(src) == path[1]) //Successful move
path -= path[1]
else
tries++
return 0
else if(path.len == 1)
step_to(src, dest)
path = list()
return 1
/mob/living/simple_animal/bot/proc/check_bot_access()
if(mode != BOT_SUMMON && mode != BOT_RESPONDING)
access_card.access = prev_access
/mob/living/simple_animal/bot/proc/call_bot(caller, turf/waypoint, message=TRUE)
bot_reset() //Reset a bot before setting it to call mode.
var/area/end_area = get_area(waypoint)
//For giving the bot temporary all-access.
var/obj/item/weapon/card/id/all_access = new /obj/item/weapon/card/id
var/datum/job/captain/All = new/datum/job/captain
all_access.access = All.get_access()
path = get_path_to(src, waypoint, src, /turf/proc/Distance_cardinal, 0, 200, id=all_access)
calling_ai = caller //Link the AI to the bot!
ai_waypoint = waypoint
if(path && path.len) //Ensures that a valid path is calculated!
if(!on)
turn_on() //Saves the AI the hassle of having to activate a bot manually.
access_card = all_access //Give the bot all-access while under the AI's command.
if(message)
calling_ai << "<span class='notice'>\icon[src] [name] called to [end_area.name]. [path.len-1] meters to destination.</span>"
pathset = 1
mode = BOT_RESPONDING
tries = 0
else
if(message)
calling_ai << "<span class='danger'>Failed to calculate a valid route. Ensure destination is clear of obstructions and within range.</span>"
calling_ai = null
path = list()
/mob/living/simple_animal/bot/proc/call_mode() //Handles preparing a bot for a call, as well as calling the move proc.
//Handles the bot's movement during a call.
var/success = bot_move(ai_waypoint, 3)
if (!success)
if(calling_ai)
calling_ai << "\icon[src] [get_turf(src) == ai_waypoint ? "<span class='notice'>[src] successfully arrived to waypoint.</span>" : "<span class='danger'>[src] failed to reach waypoint.</span>"]"
calling_ai = null
bot_reset()
/mob/living/simple_animal/bot/proc/bot_reset()
if(calling_ai) //Simple notification to the AI if it called a bot. It will not know the cause or identity of the bot.
calling_ai << "<span class='danger'>Call command to a bot has been reset.</span>"
calling_ai = null
path = list()
summon_target = null
pathset = 0
access_card.access = prev_access
tries = 0
mode = BOT_IDLE
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//Patrol and summon code!
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/mob/living/simple_animal/bot/proc/bot_patrol()
patrol_step()
spawn(5)
if(mode == BOT_PATROL)
patrol_step()
return
/mob/living/simple_animal/bot/proc/start_patrol()
if(tries >= 4) //Bot is trapped, so stop trying to patrol.
auto_patrol = 0
tries = 0
speak("Unable to start patrol.")
return
if(!auto_patrol) //A bot not set to patrol should not be patrolling.
mode = BOT_IDLE
return
if(patrol_target) // has patrol target
spawn(0)
calc_path() // Find a route to it
if(path.len == 0)
patrol_target = null
return
mode = BOT_PATROL
else // no patrol target, so need a new one
speak("Engaging patrol mode.")
find_patrol_target()
tries++
return
// perform a single patrol step
/mob/living/simple_animal/bot/proc/patrol_step()
if(loc == patrol_target) // reached target
//Find the next beacon matching the target.
if(!get_next_patrol_target())
find_patrol_target() //If it fails, look for the nearest one instead.
return
else if(path.len > 0 && patrol_target) // valid path
var/turf/next = path[1]
if(next == loc)
path -= next
return
var/moved = bot_move(patrol_target)//step_towards(src, next) // attempt to move
if(moved) // successful move
blockcount = 0
else // failed to move
blockcount++
if(blockcount > 5) // attempt 5 times before recomputing
// find new path excluding blocked turf
spawn(2)
calc_path(next)
if(path.len == 0)
find_patrol_target() //Start looking for the next nearest beacon
tries++
else
blockcount = 0
tries = 0
return
return
else // no path, so calculate new one
mode = BOT_START_PATROL
return
// finds the nearest beacon to self
/mob/living/simple_animal/bot/proc/find_patrol_target()
nearest_beacon = null
new_destination = null
find_nearest_beacon()
if(nearest_beacon)
patrol_target = nearest_beacon_loc
destination = next_destination
else
auto_patrol = 0
mode = BOT_IDLE
speak("Disengaging patrol mode.")
/mob/living/simple_animal/bot/proc/get_next_patrol_target()
// search the beacon list for the next target in the list.
for(var/obj/machinery/navbeacon/NB in navbeacons)
if(NB.location == next_destination) //Does the Beacon location text match the destination?
destination = new_destination //We now know the name of where we want to go.
patrol_target = NB.loc //Get its location and set it as the target.
next_destination = NB.codes["next_patrol"] //Also get the name of the next beacon in line.
return 1
/mob/living/simple_animal/bot/proc/find_nearest_beacon()
for(var/obj/machinery/navbeacon/NB in navbeacons)
var/dist = get_dist(src, NB)
if(nearest_beacon) //Loop though the beacon net to find the true closest beacon.
//Ignore the beacon if were are located on it.
if(dist>1 && dist<get_dist(src,nearest_beacon_loc))
nearest_beacon = NB.location
nearest_beacon_loc = NB.loc
next_destination = NB.codes["next_patrol"]
else
continue
else if(dist > 1) //Begin the search, save this one for comparison on the next loop.
nearest_beacon = NB.location
nearest_beacon_loc = NB.loc
patrol_target = nearest_beacon_loc
destination = nearest_beacon
//PDA control. Some bots, especially MULEs, may have more parameters.
/mob/living/simple_animal/bot/proc/bot_control(command, mob/user, turf/user_turf, list/user_access = list())
if(!on || emagged == 2 || remote_disabled) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands.
return 1 //ACCESS DENIED
if(ckey)
bot_control_message(command,user,user_turf,user_access)
// process control input
switch(command)
if("patroloff")
bot_reset() //HOLD IT!!
auto_patrol = 0
return
if("patrolon")
auto_patrol = 1
return
if("summon")
bot_reset()
summon_target = user_turf
if(user_access.len != 0)
access_card.access = user_access + prev_access //Adds the user's access, if any.
mode = BOT_SUMMON
speak("Responding.", radio_channel)
calc_summon_path()
return
return
//
/mob/living/simple_animal/bot/proc/bot_control_message(command,user,user_turf,user_access)
switch(command)
if("patroloff")
src << "<span class='warning big'>STOP PATROL</span>"
if("patrolon")
src << "<span class='warning big'>START PATROL</span>"
if("summon")
var/area/a = get_area(user_turf)
src << "<span class='warning big'>PRIORITY ALERT:[user] in [a.name]!</span>"
if("stop")
src << "<span class='warning big'>STOP!</span>"
if("go")
src << "<span class='warning big'>GO!</span>"
if("home")
src << "<span class='warning big'>RETURN HOME!</span>"
else
src << "<span class='warning'>Unidentified control sequence recieved:[command]</span>"
/mob/living/simple_animal/bot/proc/bot_summon()
// summoned to PDA
summon_step()
return
// calculates a path to the current destination
// given an optional turf to avoid
/mob/living/simple_animal/bot/proc/calc_path(turf/avoid)
check_bot_access()
path = get_path_to(loc, patrol_target, src, /turf/proc/Distance_cardinal, 0, 120, id=access_card, exclude=avoid)
/mob/living/simple_animal/bot/proc/calc_summon_path(turf/avoid)
check_bot_access()
spawn()
path = get_path_to(loc, summon_target, src, /turf/proc/Distance_cardinal, 0, 150, id=access_card, exclude=avoid)
if(!path.len || tries >= 5) //Cannot reach target. Give up and announce the issue.
speak("Summon command failed, destination unreachable.",radio_channel)
bot_reset()
/mob/living/simple_animal/bot/proc/summon_step()
if(loc == summon_target) // Arrived to summon location.
bot_reset()
return
else if(path.len > 0 && summon_target) //Proper path acquired!
var/turf/next = path[1]
if(next == loc)
path -= next
return
var/moved = bot_move(summon_target, 3) // Move attempt
if(moved)
blockcount = 0
else // failed to move
blockcount++
if(blockcount > 5) // attempt 5 times before recomputing
// find new path excluding blocked turf
spawn(2)
calc_summon_path(next)
tries++
return
return
else // no path, so calculate new one
calc_summon_path()
return
/mob/living/simple_animal/bot/Bump(M as mob|obj) //Leave no door unopened!
. = ..()
if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(access_card)))
var/obj/machinery/door/D = M
if(D.check_access(access_card))
D.open()
frustration = 0
else if((istype(M, /mob/living/)) && (!anchored))
var/mob/living/Mb = M
loc = Mb.loc
frustration = 0
/mob/living/simple_animal/bot/proc/show_controls(mob/M)
users |= M
var/dat = ""
dat = get_controls(M)
var/datum/browser/popup = new(M,window_id,window_name,350,600)
popup.set_content(dat)
popup.open()
onclose(M,window_id,ref=src)
return
/mob/living/simple_animal/bot/proc/update_controls()
for(var/mob/M in users)
show_controls(M)
/mob/living/simple_animal/bot/proc/get_controls(mob/M)
return "PROTOBOT - NOT FOR USE"
/mob/living/simple_animal/bot/Topic(href, href_list)
//No ..() to prevent strip panel showing up - Todo: make that saner
if(topic_denied(usr))
usr << "<span class='warning'>[src]'s interface is not responding!</span>"
href_list = list()
return
add_fingerprint(usr)
if(href_list["close"])// HUE HUE
if(usr in users)
users.Remove(usr)
return
if((href_list["power"]) && (bot_core.allowed(usr) || !locked))
if (on)
turn_off()
else
turn_on()
switch(href_list["operation"])
if("patrol")
auto_patrol = !auto_patrol
bot_reset()
if("remote")
remote_disabled = !remote_disabled
if("hack")
if(emagged != 2)
emagged = 2
hacked = 1
locked = 1
usr << "<span class='warning'>[text_hack]</span>"
bot_reset()
else if(!hacked)
usr << "<span class='boldannounce'>[text_dehack_fail]</span>"
else
emagged = 0
hacked = 0
usr << "<span class='notice'>[text_dehack]</span>"
bot_reset()
update_controls()
/mob/living/simple_animal/bot/proc/update_icon()
icon_state = "[initial(icon_state)][on]"
// Machinery to simplify topic and access calls
/obj/machinery/bot_core
use_power = 0
interact_offline = 1 //DEBUG
var/mob/living/simple_animal/bot/owner = null
/obj/machinery/bot_core/New(loc)
..()
owner = loc
if(!istype(owner))
qdel(src)
/mob/living/simple_animal/bot/proc/topic_denied(mob/user) //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired.
// 0 for access, 1 for denied.
if(emagged == 2) //An emagged bot cannot be controlled by humans, silicons can if one hacked it.
if(hacked) //Manually emagged by a human - access denied to all.
return 1
else if(!issilicon(user)) //Bot is hacked, so only silicons are allowed access.
return 1
else
return 0
/mob/living/simple_animal/bot/proc/hack(mob/user)
var/hack
if(issilicon(user)) //Allows silicons to toggle the emag status of a bot.
hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]<BR>"
hack += "Harm Prevention Safety System: <A href='?src=\ref[src];operation=hack'>[emagged ? "<span class='bad'>DANGER</span>" : "Engaged"]</A><BR>"
else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel and PDA control.
hack += "Remote network control radio: <A href='?src=\ref[src];operation=remote'>[remote_disabled ? "Disconnected" : "Connected"]</A><BR><BR>"
return hack
/mob/living/simple_animal/bot/Login()
. = ..()
access_card.access += player_access
@@ -0,0 +1,245 @@
//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"
layer = 5
density = 0
anchored = 0
health = 25
maxHealth = 25
radio_channel = "Service" //Service
bot_type = CLEAN_BOT
model = "Cleanbot"
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]"
bot_core.updateUsrDialog()
/mob/living/simple_animal/bot/cleanbot/turn_off()
..()
icon_state = "cleanbot[on]"
bot_core.updateUsrDialog()
/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(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>")
PoolOrNew(/obj/effect/particle_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(loc, target.loc, src, /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/oil
target_types += /obj/effect/decal/cleanable/vomit
target_types += /obj/effect/decal/cleanable/robot_debris
target_types += /obj/effect/decal/cleanable/crayon
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
if(blood)
target_types += /obj/effect/decal/cleanable/xenoblood/
target_types += /obj/effect/decal/cleanable/xenoblood/xgibs
target_types += /obj/effect/decal/cleanable/blood/
target_types += /obj/effect/decal/cleanable/blood/gibs/
target_types += /obj/effect/decal/cleanable/blood/drip/
target_types += /obj/effect/decal/cleanable/trail_holder
/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='boldannounce'>[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/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
/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 += 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))
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)
..()
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,462 @@
//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/gun/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)
var/turf/T = get_turf(user)
user << "<span class='notice'>You start attaching the gun to the frame...</span>"
sleep(40)
if(get_turf(user) == T)
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
/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"
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
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(type != /obj/item/clothing/head/helmet/sec) //Eh, but we don't want people making secbots out of space helmets.
return
if(F) //Has a flashlight. Player must remove it, else it will be lost forever.
user << "<span class='warning'>The mounted flashlight is in the way, remove it first!</span>"
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.loc = 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/sec(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,563 @@
/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"
layer = 5
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
mob_size = MOB_SIZE_LARGE
radio_key = /obj/item/device/encryptionkey/headset_sec
radio_channel = "Security"
bot_type = SEC_BOT
model = "ED-209"
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)
/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 += 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))
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
..()
switch(href_list["operation"])
if ("idcheck")
idcheck = !idcheck
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/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.
threatlevel = user.assess_threat(src)
threatlevel += 6
if(threatlevel >= 4)
target = user
if(lasercolor)//To make up for the fact that lasertag bots don't hunt
shootAt(user)
mode = BOT_HUNT
/mob/living/simple_animal/bot/ed209/Emag(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)
threatlevel = Proj.firer.assess_threat(src)
threatlevel += 6
if(threatlevel >= 4)
target = Proj.firer
mode = BOT_HUNT
..()
/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='boldannounce'>[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/gun/advtaser/G = new /obj/item/weapon/gun/energy/gun/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/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/beam/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/beam/lasertag/bluetag
else if(lasercolor == "r")
projectile = /obj/item/projectile/beam/lasertag/redtag
/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
A.fire()
/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/beam/lasertag/redtag))
lasertag_check++
else if((lasercolor == "r"))
if(istype(Proj, /obj/item/projectile/beam/lasertag/bluetag))
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/New()//If desired, you spawn red and bluetag bots easily
new /mob/living/simple_animal/bot/ed209(get_turf(src),null,"b")
qdel(src)
/mob/living/simple_animal/bot/ed209/redtag/New()
new /mob/living/simple_animal/bot/ed209(get_turf(src),null,"r")
qdel(src)
/mob/living/simple_animal/bot/ed209/UnarmedAttack(atom/A)
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(ismob(A))
shootAt(A)
else
..()
/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)
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(0) //update the handcuffs overlay
back_to_idle()
@@ -0,0 +1,429 @@
//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"
layer = 5
density = 0
anchored = 0
health = 25
maxHealth = 25
radio_channel = "Engineering"
bot_type = FLOOR_BOT
model = "Floorbot"
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/turn_on()
. = ..()
update_icon()
/mob/living/simple_animal/bot/floorbot/turn_off()
..()
update_icon()
/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 += "<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))
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(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)
..()
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(loc, TL, src, /turf/proc/Distance_cardinal, 0, 30, id=access_card,simulated_only = 0)
else
path = get_path_to(loc, target, src, /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.loc = 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='boldannounce'>[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,511 @@
//MEDBOT
//MEDBOT PATHFINDING
//MEDBOT ASSEMBLY
/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"
layer = 5
density = 0
anchored = 0
health = 20
maxHealth = 20
pass_flags = PASSMOB
radio_key = /obj/item/device/encryptionkey/headset_med
radio_channel = "Medical"
bot_type = MED_BOT
model = "Medibot"
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 = "bicaridine"
var/treatment_oxy = "dexalin"
var/treatment_fire = "kelotane"
var/treatment_tox = "antitoxin"
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/mysterious
name = "\improper Mysterious Medibot"
desc = "International Medibot of mystery."
skin = "bezerk"
treatment_oxy = "tricordrazine"
treatment_brute = "tricordrazine"
treatment_fire = "tricordrazine"
treatment_tox = "tricordrazine"
/mob/living/simple_animal/bot/medbot/derelict
name = "\improper Old Medibot"
desc = "Looks like it hasn't been modified since the late 2080s."
skin = "bezerk"
heal_threshold = 0
declare_crit = 0
treatment_oxy = "pancuronium"
treatment_brute = "pancuronium"
treatment_fire = "sodium_thiopental"
treatment_tox = "sodium_thiopental"
/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)
/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/attack_paw(mob/user)
return attack_hand(user)
/mob/living/simple_animal/bot/medbot/get_controls(mob/user)
var/dat
dat += hack(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))
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(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.loc = 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.loc = 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(mob/user)
..()
if(emagged == 2)
declare_crit = 0
if(user)
user << "<span class='notice'>You short out [src]'s reagent synthesis circuits.</span>"
spawn(0)
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(loc, get_turf(patient), src, /turf/proc/Distance_cardinal, 0, 30,id=access_card)
mode = BOT_MOVING
if(!path.len) //Do not chase a patient we cannot reach.
soft_reset()
if(path.len > 0 && patient)
if(!bot_move(patient))
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(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)
for(var/datum/disease/D in C.viruses)
//the medibot can't detect viruses that are undetectable to Health Analyzers or Pandemic machines.
if(D.visibility_flags & HIDDEN_SCANNER || D.visibility_flags & HIDDEN_PANDEMIC)
return 0
if(D.severity == NONTHREAT) // medibot doesn't try to heal truly harmless viruses
return 0
if((D.stage > 1) || (D.spread_flags & AIRBORNE)) // medibot can't detect a virus in its initial stage unless it spreads airborne.
if (!C.reagents.has_reagent(treatment_virus))
return 1 //STOP DISEASE FOREVER
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/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 = "toxin"
else
if(treat_virus)
var/virus = 0
for(var/datum/disease/D in C.viruses)
//detectable virus
if((!(D.visibility_flags & HIDDEN_SCANNER)) || (!(D.visibility_flags & HIDDEN_PANDEMIC)))
if(D.severity != NONTHREAT) //virus is harmful
if((D.stage > 1) || (D.spread_flags & AIRBORNE))
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
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, INJECT, 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/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='boldannounce'>[src] blows apart!</span>")
var/turf/Tsec = get_turf(src)
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.loc = 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
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)
@@ -0,0 +1,803 @@
//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
var/global/mulebot_count = 0
#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"
layer = MOB_LAYER
density = 1
anchored = 1
animate_movement=1
health = 150
maxHealth = 150
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
mob_size = MOB_SIZE_LARGE
bot_type = MULE_BOT
model = "MULE"
bot_core_type = /obj/machinery/bot_core/mulebot
window_id = "mulebot"
window_name = "M.U.L.E. Mk. V"
window_width = 350
window_height = 600
suffix = ""
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/refresh = 1 // true to refresh dialogue
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
// the installed power cell
var/bloodiness = 0 // count of bloodiness
/mob/living/simple_animal/bot/mulebot/New()
..()
wires = new(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
spawn(5) // must wait for map loading to finish
mulebot_count += 1
if(!suffix)
suffix = "#[mulebot_count]"
name = "\improper Mulebot ([suffix])"
/mob/living/simple_animal/bot/mulebot/Destroy()
unload(0)
qdel(wires)
wires = null
return ..()
mob/living/simple_animal/bot/mulebot/bot_reset()
..()
reached_target = 0
// attack by item
// emag : lock/unlock,
// screwdriver: open/close hatch
// cell: insert it
// other: chance to knock rider off bot
/mob/living/simple_animal/bot/mulebot/attackby(obj/item/I, mob/user, params)
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.loc = src
cell = C
update_controls()
else if(istype(I,/obj/item/weapon/screwdriver))
if(locked)
user << "<span class='warning'>The maintenance hatch cannot be opened or closed while the controls are locked!</span>"
return
open = !open
if(open)
visible_message("[user] opens the maintenance hatch of [src]", "<span class='notice'>You open [src]'s maintenance hatch.</span>")
on = 0
icon_state="mulebot-hatch"
else
visible_message("[user] closes the maintenance hatch of [src]", "<span class='notice'>You close [src]'s maintenance hatch.</span>")
icon_state = "mulebot0"
update_controls()
else if(istype(I, /obj/item/device/multitool) || istype(I, /obj/item/weapon/wirecutters))
if(open)
attack_hand(usr)
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>"
..()
return
/mob/living/simple_animal/bot/mulebot/emag_act(mob/user)
locked = !locked
user << "<span class='notice'>You [locked ? "lock" : "unlock"] the mulebot's controls!</span>"
flick("mulebot-emagged", src)
playsound(loc, 'sound/effects/sparks1.ogg', 100, 0)
/mob/living/simple_animal/bot/mulebot/update_icon()
//UNFINISHED
switch(mode)
if(BOT_IDLE)
icon_state = "mulebot0"
if(open)
icon_state="mulebot-hatch"
else
icon_state = "mulebot0"
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/attack_ai(mob/user)
show_controls(user)
/mob/living/simple_animal/bot/mulebot/get_controls(mob/user)
var/ai = issilicon(user)
var/dat
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)
dat += "&nbsp;<br /><div class='notice'>Controls are locked</div><A href='byond://?src=\ref[src];op=unlock'>Unlock Controls</A>"
else
dat += "&nbsp;<br /><div class='notice'>Controls are unlocked</div><A href='byond://?src=\ref[src];op=lock'>Lock Controls</A><BR><BR>"
dat += "<A href='byond://?src=\ref[src];op=power'>Toggle Power</A><BR>"
dat += "<A href='byond://?src=\ref[src];op=stop'>Stop</A><BR>"
dat += "<A href='byond://?src=\ref[src];op=go'>Proceed</A><BR>"
dat += "<A href='byond://?src=\ref[src];op=home'>Return to Home</A><BR>"
dat += "<A href='byond://?src=\ref[src];op=destination'>Set Destination</A><BR>"
dat += "<A href='byond://?src=\ref[src];op=setid'>Set Bot ID</A><BR>"
dat += "<A href='byond://?src=\ref[src];op=sethome'>Set Home</A><BR>"
dat += "<A href='byond://?src=\ref[src];op=autoret'>Toggle Auto Return Home</A> ([auto_return ? "On":"Off"])<BR>"
dat += "<A href='byond://?src=\ref[src];op=autopick'>Toggle Auto Pickup Crate</A> ([auto_pickup ? "On":"Off"])<BR>"
dat += "<A href='byond://?src=\ref[src];op=report'>Toggle Delivery Reporting</A> ([report_delivery ? "On" : "Off"])<BR>"
dat += "<A href='byond://?src=\ref[src];op=autorefresh'>Toggle Interface Refreshing</A> ([refresh ? "On" : "Off"])<BR>"
if(load)
dat += "<A href='byond://?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='byond://?src=\ref[src];op=cellremove'>Installed</A><BR>"
else
dat += "<A href='byond://?src=\ref[src];op=cellinsert'>Removed</A><BR>"
dat += wires()
else
dat += "<div class='notice'>The bot is in maintenance mode and cannot be controlled.</div><BR>"
return dat
// returns the wire panel text
/mob/living/simple_animal/bot/mulebot/proc/wires()
return wires.GetInteractWindow()
/mob/living/simple_animal/bot/mulebot/Topic(href, href_list)
if(..())
return
if (usr.stat)
return
if ((in_range(src, usr) && istype(loc, /turf)) || (istype(usr, /mob/living/silicon)))
switch(href_list["op"])
if("lock", "unlock")
locked = !locked
update_controls()
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].")
update_controls()
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("[usr] removes the power cell from [src].", "<span class='notice'>You remove the power cell from [src].</span>")
update_controls()
if("cellinsert")
if(open && !cell)
var/obj/item/weapon/stock_parts/cell/C = usr.get_active_hand()
if(istype(C))
if(!usr.drop_item())
return
cell = C
C.loc = src
C.add_fingerprint(usr)
usr.visible_message("[usr] inserts a power cell into [src].", "<span class='notice'>You insert the power cell into [src].</span>")
update_controls()
else
bot_control(href_list["op"], usr)
update_controls()
return
/mob/living/simple_animal/bot/mulebot/bot_control(command, mob/user, pda= 0)
if(pda && !wires.RemoteRX()) //MULE wireless is controlled by wires.
return
switch(command)
if("autorefresh")
refresh = !refresh
update_controls()
if("stop")
if(mode >= BOT_DELIVER)
bot_reset()
update_controls()
if("go")
if(mode == BOT_IDLE)
start()
update_controls()
if("home")
if(mode == BOT_IDLE || mode == BOT_DELIVER)
start_home()
update_controls()
if("destination")
refresh=0
var/new_dest = input(user, "Select M.U.L.E. Destination", "Mulebot [suffix ? "([suffix])" : ""]", destination) as null|anything in deliverybeacontags
refresh=1
if(new_dest)
set_destination(new_dest)
if("setid")
refresh=0
var/new_id = stripped_input(user, "Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix, MAX_NAME_LEN)
refresh=1
if(new_id)
suffix = new_id
name = "\improper Mulebot ([suffix])"
update_controls()
if("sethome")
refresh=0
var/new_home = stripped_input(user, "Enter new home tag", "Mulebot [suffix ? "([suffix])" : ""]", home_destination)
refresh=1
if(new_home)
home_destination = new_home
update_controls()
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
if("close")
usr.unset_machine()
usr << browse(null,"window=mulebot")
// 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='italics'>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='italics'>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='italics'>You hear a ping.</span>")
playsound(loc, 'sound/machines/ping.ogg', 50, 0)
// mousedrop a crate to load the bot
// can load anything if emagged
/mob/living/simple_animal/bot/mulebot/MouseDrop_T(atom/movable/AM, mob/user)
if(user.incapacitated() || user.lying)
return
if (!istype(AM))
return
load(AM)
// called to load a crate
/mob/living/simple_animal/bot/mulebot/proc/load(atom/movable/AM)
if(load || AM.anchored)
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 emagged, 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.loc = src
load = AM
mode = BOT_IDLE
update_icon()
/mob/living/simple_animal/bot/mulebot/proc/load_mob(mob/living/M)
if(M.buckled)
return 0
passenger = M
load = M
can_buckle = 1
buckle_mob(M)
can_buckle = 0
return 1
/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()
unbuckle_mob()
if(load)
load.loc = 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
/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()
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()
if(refresh) update_controls()
/mob/living/simple_animal/bot/mulebot/proc/process_bot()
if(!on)
return
switch(mode)
if(BOT_IDLE) // idle
icon_state = "mulebot0"
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])"
if(bloodiness)
var/obj/effect/decal/cleanable/blood/tracks/B = new(loc)
B.blood_DNA |= blood_DNA.Copy()
var/newdir = get_dir(next, loc)
if(newdir == dir)
B.dir = newdir
else
newdir = newdir | dir
if(newdir == 3)
newdir = 1
else if(newdir == 12)
newdir = 4
B.dir = newdir
bloodiness--
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)
spawn(2)
calc_path(next)
if(path.len > 0)
buzz(DELIGHT)
mode = BOT_BLOCKED
mode = BOT_WAIT_FOR_NAV
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
// calculates a path to the current destination
// given an optional turf to avoid
/mob/living/simple_animal/bot/mulebot/calc_path(turf/avoid = null)
path = get_path_to(loc, target, src, /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()
update_controls()
// 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
icon_state = "mulebot[(wires.MobAvoid() != 0)]"
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
icon_state = "mulebot[(wires.MobAvoid() != 0)]"
// 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='italics'>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)
unload(loaddir)
else
// not loaded
if(auto_pickup) // find a crate
var/atom/movable/AM
if(!wires.LoadCheck()) // if emagged, 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
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)
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")
H.apply_damage(2*damage, BRUTE, "chest")
H.apply_damage(0.5*damage, BRUTE, "l_leg")
H.apply_damage(0.5*damage, BRUTE, "r_leg")
H.apply_damage(0.5*damage, BRUTE, "l_arm")
H.apply_damage(0.5*damage, BRUTE, "r_arm")
var/obj/effect/decal/cleanable/blood/B = new(loc)
B.add_blood_list(H)
add_blood_list(H)
bloodiness += 4
// 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()
//Formerly the beacon reception proc, except that it is no longer a potential lag bomb called TEN TIMES A SECOND OR MORE in some cases!
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
// the we will navigate there
destination = new_destination
target = NB.loc
var/direction = NB.dir // this will be the load/unload dir
if(direction)
loaddir = text2num(direction)
else
loaddir = 0
icon_state = "mulebot[(wires.MobAvoid() != null)]"
if(destination) // No need to calculate a path if you do not have a destination set!
calc_path()
update_controls()
/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='boldannounce'>[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.loc = 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/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
#undef SIGH
#undef ANNOYED
#undef DELIGHT
/obj/machinery/bot_core/mulebot
req_access = list(access_cargo)
@@ -0,0 +1,488 @@
/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"
layer = 5
density = 1
anchored = 0
health = 25
maxHealth = 25
damage_coeff = list(BRUTE = 0.5, BURN = 0.7, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
radio_key = /obj/item/device/encryptionkey/headset_sec
radio_channel = "Security" //Security channel
bot_type = SEC_BOT
model = "Securitron"
bot_core_type = /obj/machinery/bot_core/secbot
window_id = "autosec"
window_name = "Automatic Security Unit v1.6"
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
/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/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/New()
..()
icon_state = "secbot[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)
/mob/living/simple_animal/bot/secbot/turn_on()
..()
icon_state = "secbot[on]"
/mob/living/simple_animal/bot/secbot/turn_off()
..()
icon_state = "secbot[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 += 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))
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)
..()
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/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.
threatlevel = user.assess_threat(src)
threatlevel += 6
if(threatlevel >= 4)
target = user
mode = BOT_HUNT
/mob/living/simple_animal/bot/secbot/Emag(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 = "secbot[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)
threatlevel = Proj.firer.assess_threat(src)
threatlevel += 6
if(threatlevel >= 4)
target = Proj.firer
mode = BOT_HUNT
..()
/mob/living/simple_animal/bot/secbot/UnarmedAttack(atom/A)
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/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(0) //update the handcuffs overlay
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)
icon_state = "secbot-c"
spawn(2)
icon_state = "secbot[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)
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/secbot/handle_automated_action()
if (!..())
return
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='boldannounce'>[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/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
..()
//Secbot Construction
/obj/item/clothing/head/helmet/attackby(obj/item/device/assembly/signaler/S, mob/user, params)
..()
if(!issignaler(S))
..()
return
if(type != /obj/item/clothing/head/helmet/sec) //Eh, but we don't want people making secbots out of space helmets.
return
if(F) //Has a flashlight. Player must remove it, else it will be lost forever.
user << "<span class='warning'>The mounted flashlight is in the way, remove it first!</span>"
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(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/sec(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--
/obj/machinery/bot_core/secbot
req_access = list(access_security)
@@ -50,7 +50,7 @@
melee_damage_lower = 15
melee_damage_upper = 15
melee_damage_type = STAMINA
ignored_damage_types = list(BRUTE = 0, BURN = 0, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1)
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
languages = SWARMER
environment_smash = 0
attacktext = "shocks"
@@ -38,7 +38,7 @@
languages = DRONE
mob_size = MOB_SIZE_SMALL
has_unlimited_silicon_privilege = 1
ignored_damage_types = list(BRUTE = 0, BURN = 0, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1) // 0 to receive that damage type, 1 to ignore
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0)
staticOverlays = list()
var/staticChoice = "static"
var/list/staticChoices = list("static", "blank", "letter")
@@ -27,7 +27,7 @@
response_disarm = "swings at"
response_harm = "punches through"
unsuitable_atmos_damage = 0
ignored_damage_types = list(BRUTE = 0, BURN = 0, TOX = 1, CLONE = 1, STAMINA = 1, OXY = 1) //I don't know how you'd apply those, but revenants no-sell them anyway.
damage_coeff = list(BRUTE = 1, BURN = 1, TOX = 0, CLONE = 0, STAMINA = 0, OXY = 0) //I don't know how you'd apply those, but revenants no-sell them anyway.
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 = INFINITY
@@ -181,7 +181,7 @@
if(attempt_cast(user))
for(var/turf/T in targets)
spawn(0)
for(var/obj/machinery/bot/bot in T.contents)
for(var/mob/living/simple_animal/bot/bot in T.contents)
if(!bot.emagged)
PoolOrNew(/obj/effect/overlay/temp/revenant, bot.loc)
bot.locked = 0
@@ -194,7 +194,7 @@
PoolOrNew(/obj/effect/overlay/temp/revenant, human.loc)
human.emp_act(1)
for(var/obj/thing in T.contents)
if(istype(thing, /obj/machinery/dominator) || istype(thing, /obj/machinery/power/apc) || istype(thing, /obj/machinery/power/smes) || istype(thing, /obj/machinery/bot)) //Doesn't work on dominators, SMES and APCs, to prevent kekkery
if(istype(thing, /obj/machinery/dominator) || istype(thing, /obj/machinery/power/apc) || istype(thing, /obj/machinery/power/smes)) //Doesn't work on dominators, SMES and APCs, to prevent kekkery
continue
if(prob(20))
if(prob(50))
@@ -45,7 +45,7 @@
var/melee_damage_upper = 0
var/armour_penetration = 0 //How much armour they ignore, as a flat reduction from the targets armour value
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
@@ -67,6 +67,8 @@
var/mob/living/simple_animal/hostile/spawner/nest
var/sentience_type = SENTIENCE_ORGANIC // Sentience type, for slime potions
/mob/living/simple_animal/New()
..()
verbs -= /mob/verb/observe
@@ -271,20 +273,20 @@
return 0
/mob/living/simple_animal/adjustBruteLoss(amount)
if(!ignored_damage_types[BRUTE])
..()
if(damage_coeff[BRUTE])
..(amount*damage_coeff[BRUTE])
/mob/living/simple_animal/adjustFireLoss(amount)
if(!ignored_damage_types[BURN])
adjustBruteLoss(amount)
if(damage_coeff[BURN])
adjustBruteLoss(amount*damage_coeff[BURN])
/mob/living/simple_animal/adjustToxLoss(amount)
if(!ignored_damage_types[TOX])
..(amount)
if(damage_coeff[TOX])
..(amount*damage_coeff[TOX])
/mob/living/simple_animal/adjustCloneLoss(amount)
if(!ignored_damage_types[CLONE])
..(amount)
if(damage_coeff[CLONE])
..(amount*damage_coeff[CLONE])
/mob/living/simple_animal/adjustStaminaLoss(amount)
return
@@ -356,7 +358,7 @@
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.</span>")
else
adjustBruteLoss(damage)
@@ -506,4 +508,4 @@
if(nest)
nest.spawned_mobs -= src
nest = null
return ..()
return ..()
@@ -22,7 +22,7 @@
/obj/item/weapon/storage,
/obj/machinery/atmospherics/components/unary/cryo_cell,
/obj/item/weapon/grenade/chem_grenade,
/obj/machinery/bot/medbot,
/mob/living/simple_animal/bot/medbot,
/obj/machinery/computer/pandemic,
/obj/structure/safe,
/obj/machinery/disposal,
@@ -162,6 +162,7 @@
origin_tech = "biotech=5"
var/list/not_interested = list()
var/being_used = 0
var/sentience_type = SENTIENCE_ORGANIC
/obj/item/slimepotion/sentience/afterattack(mob/living/M, mob/user)
if(being_used || !ismob(M))
@@ -172,6 +173,12 @@
if(M.stat)
user << "<span class='warning'>[M] is dead!</span>"
return..()
var/mob/living/simple_animal/SM = M
if(SM.sentience_type != sentience_type)
user << "<span class='warning'>The potion won't work on [M].</span>"
return ..()
user << "<span class='notice'>You offer the sentience potion to [M]...</span>"
being_used = 1