mirror of
https://github.com/CHOMPStation2/CHOMPStation2.git
synced 2025-12-25 17:42:47 +00:00
Mulebots are now mobs
obj/machinery/bot removed completely Mulebot hacking removed, you can still disable their safety. Mulebot PAD interface redone.
This commit is contained in:
@@ -1,10 +1,6 @@
|
|||||||
/atom/movable/proc/get_mob()
|
/atom/movable/proc/get_mob()
|
||||||
return
|
return
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/get_mob()
|
|
||||||
if(load && istype(load,/mob/living))
|
|
||||||
return load
|
|
||||||
|
|
||||||
/obj/mecha/get_mob()
|
/obj/mecha/get_mob()
|
||||||
return occupant
|
return occupant
|
||||||
|
|
||||||
@@ -14,6 +10,11 @@
|
|||||||
/mob/get_mob()
|
/mob/get_mob()
|
||||||
return src
|
return src
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/get_mob()
|
||||||
|
if(load && istype(load, /mob/living))
|
||||||
|
return list(src, load)
|
||||||
|
return src
|
||||||
|
|
||||||
/proc/mobs_in_view(var/range, var/source)
|
/proc/mobs_in_view(var/range, var/source)
|
||||||
var/list/mobs = list()
|
var/list/mobs = list()
|
||||||
for(var/atom/movable/AM in view(range, source))
|
for(var/atom/movable/AM in view(range, source))
|
||||||
|
|||||||
@@ -20,7 +20,7 @@
|
|||||||
return // seems legit.
|
return // seems legit.
|
||||||
|
|
||||||
// Things you might plausibly want to follow
|
// Things you might plausibly want to follow
|
||||||
if((ismob(A) && A != src) || istype(A,/obj/machinery/bot) || istype(A,/obj/singularity))
|
if((ismob(A) && A != src) || istype(A,/obj/singularity))
|
||||||
ManualFollow(A)
|
ManualFollow(A)
|
||||||
|
|
||||||
// Otherwise jump
|
// Otherwise jump
|
||||||
|
|||||||
@@ -278,11 +278,11 @@ var/list/all_supply_groups = list("Operations","Security","Hospitality","Enginee
|
|||||||
group = "Miscellaneous"
|
group = "Miscellaneous"
|
||||||
|
|
||||||
/datum/supply_packs/mule
|
/datum/supply_packs/mule
|
||||||
name = "MULEbot Crate"
|
name = "Mulebot Crate"
|
||||||
contains = list(/obj/machinery/bot/mulebot)
|
contains = list()
|
||||||
cost = 20
|
cost = 20
|
||||||
containertype = /obj/structure/largecrate/mule
|
containertype = /obj/structure/largecrate/animal/mulebot
|
||||||
containername = "MULEbot Crate"
|
containername = "Mulebot Crate"
|
||||||
group = "Operations"
|
group = "Operations"
|
||||||
|
|
||||||
/datum/supply_packs/cargotrain
|
/datum/supply_packs/cargotrain
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
/datum/wires/mulebot
|
|
||||||
random = 1
|
|
||||||
holder_type = /obj/machinery/bot/mulebot
|
|
||||||
wire_count = 10
|
|
||||||
|
|
||||||
var/const/WIRE_POWER1 = 1 // power connections
|
|
||||||
var/const/WIRE_POWER2 = 2
|
|
||||||
var/const/WIRE_AVOIDANCE = 4 // mob avoidance
|
|
||||||
var/const/WIRE_LOADCHECK = 8 // load checking (non-crate)
|
|
||||||
var/const/WIRE_MOTOR1 = 16 // motor wires
|
|
||||||
var/const/WIRE_MOTOR2 = 32 //
|
|
||||||
var/const/WIRE_REMOTE_RX = 64 // remote recv functions
|
|
||||||
var/const/WIRE_REMOTE_TX = 128 // remote trans status
|
|
||||||
var/const/WIRE_BEACON_RX = 256 // beacon ping recv
|
|
||||||
|
|
||||||
/datum/wires/mulebot/CanUse(var/mob/living/L)
|
|
||||||
var/obj/machinery/bot/mulebot/M = holder
|
|
||||||
if(M.open)
|
|
||||||
return 1
|
|
||||||
return 0
|
|
||||||
|
|
||||||
// So the wires do not open a new window, handle the interaction ourselves.
|
|
||||||
/datum/wires/mulebot/Interact(var/mob/living/user)
|
|
||||||
if(CanUse(user))
|
|
||||||
var/obj/machinery/bot/mulebot/M = holder
|
|
||||||
M.interact(user)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/UpdatePulsed(var/index)
|
|
||||||
switch(index)
|
|
||||||
if(WIRE_POWER1, WIRE_POWER2)
|
|
||||||
holder.visible_message("<span class='notice'>\icon[holder] The charge light flickers.</span>")
|
|
||||||
if(WIRE_AVOIDANCE)
|
|
||||||
holder.visible_message("<span class='notice'>\icon[holder] The external warning lights flash briefly.</span>")
|
|
||||||
if(WIRE_LOADCHECK)
|
|
||||||
holder.visible_message("<span class='notice'>\icon[holder] The load platform clunks.</span>")
|
|
||||||
if(WIRE_MOTOR1, WIRE_MOTOR2)
|
|
||||||
holder.visible_message("<span class='notice'>\icon[holder] The drive motor whines briefly.</span>")
|
|
||||||
else
|
|
||||||
holder.visible_message("<span class='notice'>\icon[holder] You hear a radio crackle.</span>")
|
|
||||||
|
|
||||||
// HELPER PROCS
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/Motor1()
|
|
||||||
return !(wires_status & WIRE_MOTOR1)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/Motor2()
|
|
||||||
return !(wires_status & WIRE_MOTOR2)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/HasPower()
|
|
||||||
return !(wires_status & WIRE_POWER1) && !(wires_status & WIRE_POWER2)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/LoadCheck()
|
|
||||||
return !(wires_status & WIRE_LOADCHECK)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/MobAvoid()
|
|
||||||
return !(wires_status & WIRE_AVOIDANCE)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/RemoteTX()
|
|
||||||
return !(wires_status & WIRE_REMOTE_TX)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/RemoteRX()
|
|
||||||
return !(wires_status & WIRE_REMOTE_RX)
|
|
||||||
|
|
||||||
/datum/wires/mulebot/proc/BeaconRX()
|
|
||||||
return !(wires_status & WIRE_BEACON_RX)
|
|
||||||
@@ -422,11 +422,6 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is
|
|||||||
M << "<br>"
|
M << "<br>"
|
||||||
M.add_ion_law("THE STATION IS [who2pref] [who2]")
|
M.add_ion_law("THE STATION IS [who2pref] [who2]")
|
||||||
|
|
||||||
if(botEmagChance)
|
|
||||||
for(var/obj/machinery/bot/bot in machines)
|
|
||||||
if(prob(botEmagChance))
|
|
||||||
bot.emag_act(1)
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
var/apcnum = 0
|
var/apcnum = 0
|
||||||
|
|||||||
@@ -1,209 +0,0 @@
|
|||||||
// AI (i.e. game AI, not the AI player) controlled bots
|
|
||||||
|
|
||||||
/obj/machinery/bot
|
|
||||||
icon = 'icons/obj/aibots.dmi'
|
|
||||||
layer = MOB_LAYER
|
|
||||||
light_range = 3
|
|
||||||
use_power = 0
|
|
||||||
var/obj/item/weapon/card/id/botcard // the ID card that the bot "holds"
|
|
||||||
var/on = 1
|
|
||||||
var/health = 0 //do not forget to set health for your bot!
|
|
||||||
var/maxhealth = 0
|
|
||||||
var/fire_dam_coeff = 1.0
|
|
||||||
var/brute_dam_coeff = 1.0
|
|
||||||
var/open = 0//Maint panel
|
|
||||||
var/locked = 1
|
|
||||||
//var/emagged = 0 //Urist: Moving that var to the general /bot tree as it's used by most bots
|
|
||||||
|
|
||||||
/obj/machinery/bot/proc/turn_on()
|
|
||||||
if(stat) return 0
|
|
||||||
on = 1
|
|
||||||
set_light(initial(light_range))
|
|
||||||
return 1
|
|
||||||
|
|
||||||
/obj/machinery/bot/proc/turn_off()
|
|
||||||
on = 0
|
|
||||||
set_light(0)
|
|
||||||
|
|
||||||
/obj/machinery/bot/proc/explode()
|
|
||||||
qdel(src)
|
|
||||||
|
|
||||||
/obj/machinery/bot/proc/healthcheck()
|
|
||||||
if (src.health <= 0)
|
|
||||||
src.explode()
|
|
||||||
|
|
||||||
/obj/machinery/bot/emag_act(var/remaining_charges, var/user)
|
|
||||||
if(locked && !emagged)
|
|
||||||
locked = 0
|
|
||||||
emagged = 1
|
|
||||||
user << "<span class='warning'>You short out [src]'s maintenance hatch lock.</span>"
|
|
||||||
log_and_message_admins("emagged [src]'s maintenance hatch lock")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if(!locked && open && emagged == 1)
|
|
||||||
emagged = 2
|
|
||||||
log_and_message_admins("emagged [src]'s inner circuits")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
/obj/machinery/bot/examine(mob/user)
|
|
||||||
..(user)
|
|
||||||
if (src.health < maxhealth)
|
|
||||||
if (src.health > maxhealth/3)
|
|
||||||
user << "<span class='warning'>[src]'s parts look loose.</span>"
|
|
||||||
else
|
|
||||||
user << "<span class='danger'>[src]'s parts look very loose!</span>"
|
|
||||||
return
|
|
||||||
|
|
||||||
/obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob)
|
|
||||||
if(istype(W, /obj/item/weapon/screwdriver))
|
|
||||||
if(!locked)
|
|
||||||
open = !open
|
|
||||||
user << "<span class='notice'>Maintenance panel is now [src.open ? "opened" : "closed"].</span>"
|
|
||||||
else if(istype(W, /obj/item/weapon/weldingtool))
|
|
||||||
if(health < maxhealth)
|
|
||||||
if(open)
|
|
||||||
health = min(maxhealth, health+10)
|
|
||||||
user.visible_message("<span class='warning'>[user] repairs [src]!</span>","<span class='notice'>You repair [src]!</span>")
|
|
||||||
else
|
|
||||||
user << "<span class='notice'>Unable to repair with the maintenance panel closed.</span>"
|
|
||||||
else
|
|
||||||
user << "<span class='notice'>[src] does not need a repair.</span>"
|
|
||||||
else
|
|
||||||
if(hasvar(W,"force") && hasvar(W,"damtype"))
|
|
||||||
switch(W.damtype)
|
|
||||||
if("fire")
|
|
||||||
src.health -= W.force * fire_dam_coeff
|
|
||||||
if("brute")
|
|
||||||
src.health -= W.force * brute_dam_coeff
|
|
||||||
..()
|
|
||||||
healthcheck()
|
|
||||||
else
|
|
||||||
..()
|
|
||||||
|
|
||||||
/obj/machinery/bot/bullet_act(var/obj/item/projectile/Proj)
|
|
||||||
if(!(Proj.damage_type == BRUTE || Proj.damage_type == BURN))
|
|
||||||
return
|
|
||||||
health -= Proj.damage
|
|
||||||
..()
|
|
||||||
healthcheck()
|
|
||||||
|
|
||||||
/obj/machinery/bot/ex_act(severity)
|
|
||||||
switch(severity)
|
|
||||||
if(1.0)
|
|
||||||
src.explode()
|
|
||||||
return
|
|
||||||
if(2.0)
|
|
||||||
src.health -= rand(5,10)*fire_dam_coeff
|
|
||||||
src.health -= rand(10,20)*brute_dam_coeff
|
|
||||||
healthcheck()
|
|
||||||
return
|
|
||||||
if(3.0)
|
|
||||||
if (prob(50))
|
|
||||||
src.health -= rand(1,5)*fire_dam_coeff
|
|
||||||
src.health -= rand(1,5)*brute_dam_coeff
|
|
||||||
healthcheck()
|
|
||||||
return
|
|
||||||
return
|
|
||||||
|
|
||||||
/obj/machinery/bot/emp_act(severity)
|
|
||||||
var/was_on = on
|
|
||||||
stat |= EMPED
|
|
||||||
var/obj/effect/overlay/pulse2 = PoolOrNew(/obj/effect/overlay, src.loc )
|
|
||||||
pulse2.icon = 'icons/effects/effects.dmi'
|
|
||||||
pulse2.icon_state = "empdisable"
|
|
||||||
pulse2.name = "emp sparks"
|
|
||||||
pulse2.anchored = 1
|
|
||||||
pulse2.set_dir(pick(cardinal))
|
|
||||||
|
|
||||||
spawn(10)
|
|
||||||
qdel(pulse2)
|
|
||||||
if (on)
|
|
||||||
turn_off()
|
|
||||||
spawn(severity*300)
|
|
||||||
stat &= ~EMPED
|
|
||||||
if (was_on)
|
|
||||||
turn_on()
|
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/bot/attack_ai(mob/user as mob)
|
|
||||||
src.attack_hand(user)
|
|
||||||
|
|
||||||
/obj/machinery/bot/attack_hand(var/mob/living/carbon/human/user)
|
|
||||||
|
|
||||||
if(!istype(user))
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
if(user.species.can_shred(user))
|
|
||||||
src.health -= rand(15,30)*brute_dam_coeff
|
|
||||||
src.visible_message("<span class='danger'>[user] has slashed [src]!</span>")
|
|
||||||
playsound(src.loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
|
||||||
if(prob(10))
|
|
||||||
new /obj/effect/decal/cleanable/blood/oil(src.loc)
|
|
||||||
healthcheck()
|
|
||||||
|
|
||||||
/******************************************************************/
|
|
||||||
// Navigation procs
|
|
||||||
// Used for A-star pathfinding
|
|
||||||
|
|
||||||
|
|
||||||
// Returns the surrounding cardinal turfs with open links
|
|
||||||
// Including through doors openable with the ID
|
|
||||||
/turf/proc/CardinalTurfsWithAccess(var/obj/item/weapon/card/id/ID)
|
|
||||||
var/L[] = new()
|
|
||||||
|
|
||||||
// for(var/turf/simulated/t in oview(src,1))
|
|
||||||
|
|
||||||
for(var/d in cardinal)
|
|
||||||
var/turf/simulated/T = get_step(src, d)
|
|
||||||
if(istype(T) && !T.density)
|
|
||||||
if(!LinkBlockedWithAccess(src, T, ID))
|
|
||||||
L.Add(T)
|
|
||||||
return L
|
|
||||||
|
|
||||||
|
|
||||||
// Returns true if a link between A and B is blocked
|
|
||||||
// Movement through doors allowed if ID has access
|
|
||||||
/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/weapon/card/id/ID)
|
|
||||||
|
|
||||||
if(A == null || B == null) return 1
|
|
||||||
var/adir = get_dir(A,B)
|
|
||||||
var/rdir = get_dir(B,A)
|
|
||||||
if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal
|
|
||||||
var/iStep = get_step(A,adir&(NORTH|SOUTH))
|
|
||||||
if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
var/pStep = get_step(A,adir&(EAST|WEST))
|
|
||||||
if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID))
|
|
||||||
return 0
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if(DirBlockedWithAccess(A,adir, ID))
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if(DirBlockedWithAccess(B,rdir, ID))
|
|
||||||
return 1
|
|
||||||
|
|
||||||
for(var/obj/O in B)
|
|
||||||
if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER))
|
|
||||||
return 1
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
// Returns true if direction is blocked from loc
|
|
||||||
// Checks doors against access with given ID
|
|
||||||
/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/weapon/card/id/ID)
|
|
||||||
for(var/obj/structure/window/D in loc)
|
|
||||||
if(!D.density) continue
|
|
||||||
if(D.dir == SOUTHWEST) return 1
|
|
||||||
if(D.dir == dir) return 1
|
|
||||||
|
|
||||||
for(var/obj/machinery/door/D in loc)
|
|
||||||
if(!D.density) continue
|
|
||||||
if(istype(D, /obj/machinery/door/window))
|
|
||||||
if( dir & D.dir ) return !D.check_access(ID)
|
|
||||||
|
|
||||||
//if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID)
|
|
||||||
//if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID)
|
|
||||||
else return !D.check_access(ID) // it's a real, air blocking door
|
|
||||||
return 0
|
|
||||||
@@ -1,887 +0,0 @@
|
|||||||
//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
|
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot
|
|
||||||
name = "Mulebot"
|
|
||||||
desc = "A Multiple Utility Load Effector bot."
|
|
||||||
icon_state = "mulebot0"
|
|
||||||
layer = MOB_LAYER
|
|
||||||
density = 1
|
|
||||||
anchored = 1
|
|
||||||
animate_movement=1
|
|
||||||
health = 150 //yeah, it's tougher than ed209 because it is a big metal box with wheels --rastaf0
|
|
||||||
maxhealth = 150
|
|
||||||
fire_dam_coeff = 0.7
|
|
||||||
brute_dam_coeff = 0.5
|
|
||||||
var/atom/movable/load = null // the loaded crate (usually)
|
|
||||||
var/beacon_freq = 1400
|
|
||||||
var/control_freq = BOT_FREQ
|
|
||||||
|
|
||||||
suffix = ""
|
|
||||||
|
|
||||||
var/turf/target // this is turf to navigate to (location of beacon)
|
|
||||||
var/loaddir = 0 // this the direction to unload onto/load from
|
|
||||||
var/new_destination = "" // pending new destination (waiting for beacon response)
|
|
||||||
var/destination = "" // destination description
|
|
||||||
var/home_destination = "" // tag of home beacon
|
|
||||||
req_access = list(access_cargo) // added robotics access so assembly line drop-off works properly -veyveyr //I don't think so, Tim. You need to add it to the MULE's hidden robot ID card. -NEO
|
|
||||||
var/path[] = new()
|
|
||||||
|
|
||||||
var/mode = 0 //0 = idle/ready
|
|
||||||
//1 = loading/unloading
|
|
||||||
//2 = moving to deliver
|
|
||||||
//3 = returning to home
|
|
||||||
//4 = blocked
|
|
||||||
//5 = computing navigation
|
|
||||||
//6 = waiting for nav computation
|
|
||||||
//7 = no destination beacon found (or no route)
|
|
||||||
|
|
||||||
var/blockcount = 0 //number of times retried a blocked path
|
|
||||||
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/obj/item/weapon/cell/cell
|
|
||||||
// the installed power cell
|
|
||||||
|
|
||||||
// constants for internal wiring bitflags
|
|
||||||
var/datum/wires/mulebot/wires = null
|
|
||||||
|
|
||||||
var/bloodiness = 0 // count of bloodiness
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/New()
|
|
||||||
..()
|
|
||||||
wires = new(src)
|
|
||||||
botcard = new(src)
|
|
||||||
botcard.access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station)
|
|
||||||
cell = new(src)
|
|
||||||
cell.charge = 2000
|
|
||||||
cell.maxcharge = 2000
|
|
||||||
|
|
||||||
spawn(5) // must wait for map loading to finish
|
|
||||||
if(radio_controller)
|
|
||||||
radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT)
|
|
||||||
radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
|
|
||||||
|
|
||||||
var/count = 0
|
|
||||||
for(var/obj/machinery/bot/mulebot/other in world)
|
|
||||||
count++
|
|
||||||
if(!suffix)
|
|
||||||
suffix = "#[count]"
|
|
||||||
name = "Mulebot ([suffix])"
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/Destroy()
|
|
||||||
unload(0)
|
|
||||||
qdel(wires)
|
|
||||||
wires = null
|
|
||||||
if(radio_controller)
|
|
||||||
radio_controller.remove_object(src,beacon_freq)
|
|
||||||
radio_controller.remove_object(src,control_freq)
|
|
||||||
return ..()
|
|
||||||
|
|
||||||
// attack by item
|
|
||||||
// emag : lock/unlock,
|
|
||||||
// screwdriver: open/close hatch
|
|
||||||
// cell: insert it
|
|
||||||
// other: chance to knock rider off bot
|
|
||||||
/obj/machinery/bot/mulebot/attackby(var/obj/item/I, var/mob/user)
|
|
||||||
if(istype(I,/obj/item/weapon/cell) && open && !cell)
|
|
||||||
var/obj/item/weapon/cell/C = I
|
|
||||||
user.drop_item()
|
|
||||||
C.loc = src
|
|
||||||
cell = C
|
|
||||||
updateDialog()
|
|
||||||
else if(istype(I,/obj/item/weapon/screwdriver))
|
|
||||||
if(locked)
|
|
||||||
user << "<span class='notice'>The maintenance hatch cannot be opened or closed while the controls are locked.</span>"
|
|
||||||
return
|
|
||||||
|
|
||||||
open = !open
|
|
||||||
if(open)
|
|
||||||
src.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
|
|
||||||
src.visible_message("[user] closes the maintenance hatch of [src]", "<span class='notice'>You close [src]'s maintenance hatch.</span>")
|
|
||||||
icon_state = "mulebot0"
|
|
||||||
|
|
||||||
updateDialog()
|
|
||||||
else if (istype(I, /obj/item/weapon/wrench))
|
|
||||||
if (src.health < maxhealth)
|
|
||||||
src.health = min(maxhealth, src.health+25)
|
|
||||||
user.visible_message(
|
|
||||||
"<span class='notice'>\The [user] repairs \the [src]!</span>",
|
|
||||||
"<span class='notice'>You repair \the [src]!</span>"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
user << "<span class='notice'>[src] does not need a repair!</span>"
|
|
||||||
else if(load && ismob(load)) // chance to knock off rider
|
|
||||||
if(prob(1+I.force * 2))
|
|
||||||
unload(0)
|
|
||||||
user.visible_message("<span class='warning'>[user] knocks [load] off [src] with \the [I]!</span>", "<span class='warning'>You knock [load] off [src] with \the [I]!</span>")
|
|
||||||
else
|
|
||||||
user << "You hit [src] with \the [I] but to no effect."
|
|
||||||
else
|
|
||||||
..()
|
|
||||||
return
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/emag_act(var/remaining_charges, var/user)
|
|
||||||
locked = !locked
|
|
||||||
user << "<span class='notice'>You [locked ? "lock" : "unlock"] the mulebot's controls!</span>"
|
|
||||||
flick("mulebot-emagged", src)
|
|
||||||
playsound(src.loc, 'sound/effects/sparks1.ogg', 100, 0)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/ex_act(var/severity)
|
|
||||||
unload(0)
|
|
||||||
switch(severity)
|
|
||||||
if(2)
|
|
||||||
BITRESET(wires, rand(0,9))
|
|
||||||
BITRESET(wires, rand(0,9))
|
|
||||||
BITRESET(wires, rand(0,9))
|
|
||||||
if(3)
|
|
||||||
BITRESET(wires, rand(0,9))
|
|
||||||
..()
|
|
||||||
return
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/bullet_act()
|
|
||||||
if(prob(50) && !isnull(load))
|
|
||||||
unload(0)
|
|
||||||
if(prob(25))
|
|
||||||
src.visible_message("<span class='warning'>Something shorts out inside [src]!</span>")
|
|
||||||
var/index = 1<< (rand(0,9))
|
|
||||||
if(wires & index)
|
|
||||||
wires &= ~index
|
|
||||||
else
|
|
||||||
wires |= index
|
|
||||||
..()
|
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/attack_ai(var/mob/user)
|
|
||||||
user.set_machine(src)
|
|
||||||
interact(user, 1)
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/attack_hand(var/mob/user)
|
|
||||||
. = ..()
|
|
||||||
if (.)
|
|
||||||
return
|
|
||||||
user.set_machine(src)
|
|
||||||
interact(user, 0)
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/interact(var/mob/user, var/ai=0)
|
|
||||||
var/dat
|
|
||||||
dat += "<TT><B>Multiple Utility Load Effector Mk. III</B></TT><BR><BR>"
|
|
||||||
dat += "ID: [suffix]<BR>"
|
|
||||||
dat += "Power: [on ? "On" : "Off"]<BR>"
|
|
||||||
|
|
||||||
if(!open)
|
|
||||||
|
|
||||||
dat += "Status: "
|
|
||||||
switch(mode)
|
|
||||||
if(0)
|
|
||||||
dat += "Ready"
|
|
||||||
if(1)
|
|
||||||
dat += "Loading/Unloading"
|
|
||||||
if(2)
|
|
||||||
dat += "Navigating to Delivery Location"
|
|
||||||
if(3)
|
|
||||||
dat += "Navigating to Home"
|
|
||||||
if(4)
|
|
||||||
dat += "Waiting for clear path"
|
|
||||||
if(5,6)
|
|
||||||
dat += "Calculating navigation path"
|
|
||||||
if(7)
|
|
||||||
dat += "Unable to locate destination"
|
|
||||||
|
|
||||||
|
|
||||||
dat += "<BR>Current Load: [load ? load.name : "<i>none</i>"]<BR>"
|
|
||||||
dat += "Destination: [!destination ? "<i>none</i>" : destination]<BR>"
|
|
||||||
dat += "Power level: [cell ? cell.percent() : 0]%<BR>"
|
|
||||||
|
|
||||||
if(locked && !ai)
|
|
||||||
dat += "<HR>Controls are locked <A href='byond://?src=\ref[src];op=unlock'><I>(unlock)</I></A>"
|
|
||||||
else
|
|
||||||
dat += "<HR>Controls are unlocked <A href='byond://?src=\ref[src];op=lock'><I>(lock)</I></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>"
|
|
||||||
|
|
||||||
if(load)
|
|
||||||
dat += "<A href='byond://?src=\ref[src];op=unload'>Unload Now</A><BR>"
|
|
||||||
dat += "<HR>The maintenance hatch is closed.<BR>"
|
|
||||||
|
|
||||||
else
|
|
||||||
if(!ai)
|
|
||||||
dat += "The maintenance hatch is open.<BR><BR>"
|
|
||||||
dat += "Power cell: "
|
|
||||||
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.GetInteractWindow()
|
|
||||||
else
|
|
||||||
dat += "The bot is in maintenance mode and cannot be controlled.<BR>"
|
|
||||||
|
|
||||||
user << browse("<HEAD><TITLE>Mulebot [suffix ? "([suffix])" : ""]</TITLE></HEAD>[dat]", "window=mulebot;size=350x500")
|
|
||||||
onclose(user, "mulebot")
|
|
||||||
return
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/Topic(href, href_list)
|
|
||||||
if(..())
|
|
||||||
return
|
|
||||||
if (usr.stat)
|
|
||||||
return
|
|
||||||
if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon)))
|
|
||||||
usr.set_machine(src)
|
|
||||||
|
|
||||||
switch(href_list["op"])
|
|
||||||
if("lock", "unlock")
|
|
||||||
if(src.allowed(usr))
|
|
||||||
locked = !locked
|
|
||||||
updateDialog()
|
|
||||||
else
|
|
||||||
usr << "<span class='warning'>Access denied.</span>"
|
|
||||||
return
|
|
||||||
if("power")
|
|
||||||
if (src.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].")
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
|
|
||||||
if("cellremove")
|
|
||||||
if(open && cell && !usr.get_active_hand())
|
|
||||||
cell.update_icon()
|
|
||||||
usr.put_in_active_hand(cell)
|
|
||||||
cell.add_fingerprint(usr)
|
|
||||||
cell = null
|
|
||||||
|
|
||||||
usr.visible_message("<span class='notice'>[usr] removes the power cell from [src].</span>", "<span class='notice'>You remove the power cell from [src].</span>")
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
if("cellinsert")
|
|
||||||
if(open && !cell)
|
|
||||||
var/obj/item/weapon/cell/C = usr.get_active_hand()
|
|
||||||
if(istype(C))
|
|
||||||
usr.drop_item()
|
|
||||||
cell = C
|
|
||||||
C.loc = src
|
|
||||||
C.add_fingerprint(usr)
|
|
||||||
|
|
||||||
usr.visible_message("<span class='notice'>[usr] inserts a power cell into [src].</span>", "<span class='notice'>You insert the power cell into [src].</span>")
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
|
|
||||||
if("stop")
|
|
||||||
if(mode >=2)
|
|
||||||
mode = 0
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
if("go")
|
|
||||||
if(mode == 0)
|
|
||||||
start()
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
if("home")
|
|
||||||
if(mode == 0 || mode == 2)
|
|
||||||
start_home()
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
if("destination")
|
|
||||||
refresh=0
|
|
||||||
var/new_dest
|
|
||||||
var/list/beaconlist = new()
|
|
||||||
for(var/obj/machinery/navbeacon/N in navbeacons)
|
|
||||||
beaconlist.Add(N.location)
|
|
||||||
if(beaconlist.len)
|
|
||||||
new_dest = input("Select new destination tag", "Mulebot [suffix ? "([suffix])" : ""]", destination) in beaconlist
|
|
||||||
else
|
|
||||||
alert("No destination beacons available.")
|
|
||||||
refresh=1
|
|
||||||
if(new_dest)
|
|
||||||
set_destination(new_dest)
|
|
||||||
|
|
||||||
|
|
||||||
if("setid")
|
|
||||||
refresh=0
|
|
||||||
var/new_id = sanitize(input("Enter new bot ID", "Mulebot [suffix ? "([suffix])" : ""]", suffix) as text|null, MAX_NAME_LEN)
|
|
||||||
refresh=1
|
|
||||||
if(new_id)
|
|
||||||
suffix = new_id
|
|
||||||
name = "Mulebot ([suffix])"
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
if("sethome")
|
|
||||||
refresh=0
|
|
||||||
var/new_home = input("Enter new home tag", "Mulebot [suffix ? "([suffix])" : ""]", home_destination) as text|null
|
|
||||||
refresh=1
|
|
||||||
if(new_home)
|
|
||||||
home_destination = new_home
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
if("unload")
|
|
||||||
if(load && mode !=1)
|
|
||||||
if(loc == target)
|
|
||||||
unload(loaddir)
|
|
||||||
else
|
|
||||||
unload(0)
|
|
||||||
|
|
||||||
if("autoret")
|
|
||||||
auto_return = !auto_return
|
|
||||||
|
|
||||||
if("autopick")
|
|
||||||
auto_pickup = !auto_pickup
|
|
||||||
|
|
||||||
if("close")
|
|
||||||
usr.unset_machine()
|
|
||||||
usr << browse(null,"window=mulebot")
|
|
||||||
|
|
||||||
updateDialog()
|
|
||||||
//src.updateUsrDialog()
|
|
||||||
else
|
|
||||||
usr << browse(null, "window=mulebot")
|
|
||||||
usr.unset_machine()
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// returns true if the bot has power
|
|
||||||
/obj/machinery/bot/mulebot/proc/has_power()
|
|
||||||
return !open && cell && cell.charge>0 && wires.HasPower()
|
|
||||||
|
|
||||||
// mousedrop a crate to load the bot
|
|
||||||
// can load anything if emagged
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/MouseDrop_T(var/atom/movable/C, mob/user)
|
|
||||||
|
|
||||||
if(user.stat)
|
|
||||||
return
|
|
||||||
|
|
||||||
if (!on || !istype(C)|| C.anchored || get_dist(user, src) > 1 || get_dist(src,C) > 1 )
|
|
||||||
return
|
|
||||||
|
|
||||||
if(load)
|
|
||||||
return
|
|
||||||
|
|
||||||
load(C)
|
|
||||||
|
|
||||||
|
|
||||||
// called to load a crate
|
|
||||||
/obj/machinery/bot/mulebot/proc/load(var/atom/movable/C)
|
|
||||||
if(wires.LoadCheck() && !istype(C,/obj/structure/closet/crate))
|
|
||||||
src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.")
|
|
||||||
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
|
||||||
return // if not emagged, only allow crates to be loaded
|
|
||||||
|
|
||||||
//I'm sure someone will come along and ask why this is here... well people were dragging screen items onto the mule, and that was not cool.
|
|
||||||
//So this is a simple fix that only allows a selection of item types to be considered. Further narrowing-down is below.
|
|
||||||
if(!istype(C,/obj/item) && !istype(C,/obj/machinery) && !istype(C,/obj/structure) && !ismob(C))
|
|
||||||
return
|
|
||||||
if(!isturf(C.loc)) //To prevent the loading from stuff from someone's inventory, which wouldn't get handled properly.
|
|
||||||
return
|
|
||||||
|
|
||||||
if(get_dist(C, src) > 1 || load || !on)
|
|
||||||
return
|
|
||||||
for(var/obj/structure/plasticflaps/P in src.loc)//Takes flaps into account
|
|
||||||
if(!CanPass(C,P))
|
|
||||||
return
|
|
||||||
mode = 1
|
|
||||||
|
|
||||||
// if a create, close before loading
|
|
||||||
var/obj/structure/closet/crate/crate = C
|
|
||||||
if(istype(crate))
|
|
||||||
crate.close()
|
|
||||||
|
|
||||||
C.loc = src.loc
|
|
||||||
sleep(2)
|
|
||||||
if(C.loc != src.loc) //To prevent you from going onto more thano ne bot.
|
|
||||||
return
|
|
||||||
C.loc = src
|
|
||||||
load = C
|
|
||||||
|
|
||||||
C.pixel_y += 9
|
|
||||||
if(C.layer < layer)
|
|
||||||
C.layer = layer + 0.1
|
|
||||||
overlays += C
|
|
||||||
|
|
||||||
if(ismob(C))
|
|
||||||
var/mob/M = C
|
|
||||||
if(M.client)
|
|
||||||
M.client.perspective = EYE_PERSPECTIVE
|
|
||||||
M.client.eye = src
|
|
||||||
|
|
||||||
mode = 0
|
|
||||||
send_status()
|
|
||||||
|
|
||||||
// called to unload the bot
|
|
||||||
// argument is optional direction to unload
|
|
||||||
// if zero, unload at bot's location
|
|
||||||
/obj/machinery/bot/mulebot/proc/unload(var/dirn = 0)
|
|
||||||
if(!load)
|
|
||||||
return
|
|
||||||
|
|
||||||
mode = 1
|
|
||||||
overlays.Cut()
|
|
||||||
|
|
||||||
load.loc = src.loc
|
|
||||||
load.pixel_y -= 9
|
|
||||||
load.layer = initial(load.layer)
|
|
||||||
if(ismob(load))
|
|
||||||
var/mob/M = load
|
|
||||||
if(M.client)
|
|
||||||
M.client.perspective = MOB_PERSPECTIVE
|
|
||||||
M.client.eye = src
|
|
||||||
|
|
||||||
|
|
||||||
if(dirn)
|
|
||||||
var/turf/T = src.loc
|
|
||||||
T = get_step(T,dirn)
|
|
||||||
if(CanPass(load,T))//Can't get off onto anything that wouldn't let you pass normally
|
|
||||||
step(load, dirn)
|
|
||||||
else
|
|
||||||
load.loc = src.loc//Drops you right there, so you shouldn't be able to get yourself stuck
|
|
||||||
|
|
||||||
load = null
|
|
||||||
|
|
||||||
// in case non-load items end up in contents, dump every else too
|
|
||||||
// this seems to happen sometimes due to race conditions
|
|
||||||
// with items dropping as mobs are loaded
|
|
||||||
|
|
||||||
for(var/atom/movable/AM in src)
|
|
||||||
if(AM == cell || AM == botcard) continue
|
|
||||||
|
|
||||||
AM.loc = src.loc
|
|
||||||
AM.layer = initial(AM.layer)
|
|
||||||
AM.pixel_y = initial(AM.pixel_y)
|
|
||||||
if(ismob(AM))
|
|
||||||
var/mob/M = AM
|
|
||||||
if(M.client)
|
|
||||||
M.client.perspective = MOB_PERSPECTIVE
|
|
||||||
M.client.eye = src
|
|
||||||
mode = 0
|
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/process()
|
|
||||||
if(!has_power())
|
|
||||||
on = 0
|
|
||||||
return
|
|
||||||
if(on)
|
|
||||||
var/speed = (wires.Motor1() ? 1:0) + (wires.Motor2() ? 2:0)
|
|
||||||
//world << "speed: [speed]"
|
|
||||||
switch(speed)
|
|
||||||
if(0)
|
|
||||||
// do nothing
|
|
||||||
if(1)
|
|
||||||
process_bot()
|
|
||||||
spawn(2)
|
|
||||||
process_bot()
|
|
||||||
sleep(2)
|
|
||||||
process_bot()
|
|
||||||
sleep(2)
|
|
||||||
process_bot()
|
|
||||||
sleep(2)
|
|
||||||
process_bot()
|
|
||||||
if(2)
|
|
||||||
process_bot()
|
|
||||||
spawn(4)
|
|
||||||
process_bot()
|
|
||||||
if(3)
|
|
||||||
process_bot()
|
|
||||||
|
|
||||||
if(refresh) updateDialog()
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/proc/process_bot()
|
|
||||||
//if(mode) world << "Mode: [mode]"
|
|
||||||
switch(mode)
|
|
||||||
if(0) // idle
|
|
||||||
icon_state = "mulebot0"
|
|
||||||
return
|
|
||||||
if(1) // loading/unloading
|
|
||||||
return
|
|
||||||
if(2,3,4) // 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)
|
|
||||||
var/newdir = get_dir(next, loc)
|
|
||||||
if(newdir == dir)
|
|
||||||
B.set_dir(newdir)
|
|
||||||
else
|
|
||||||
newdir = newdir | dir
|
|
||||||
if(newdir == 3)
|
|
||||||
newdir = 1
|
|
||||||
else if(newdir == 12)
|
|
||||||
newdir = 4
|
|
||||||
B.set_dir(newdir)
|
|
||||||
bloodiness--
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var/moved = step_towards(src, next) // attempt to move
|
|
||||||
if(cell) cell.use(1)
|
|
||||||
if(moved) // successful move
|
|
||||||
//world << "Successful move."
|
|
||||||
blockcount = 0
|
|
||||||
path -= loc
|
|
||||||
|
|
||||||
|
|
||||||
if(mode==4)
|
|
||||||
spawn(1)
|
|
||||||
send_status()
|
|
||||||
|
|
||||||
if(destination == home_destination)
|
|
||||||
mode = 3
|
|
||||||
else
|
|
||||||
mode = 2
|
|
||||||
|
|
||||||
else // failed to move
|
|
||||||
|
|
||||||
//world << "Unable to move."
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
blockcount++
|
|
||||||
mode = 4
|
|
||||||
if(blockcount == 3)
|
|
||||||
src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.")
|
|
||||||
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
|
||||||
|
|
||||||
if(blockcount > 5) // attempt 5 times before recomputing
|
|
||||||
// find new path excluding blocked turf
|
|
||||||
src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.")
|
|
||||||
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
|
||||||
|
|
||||||
spawn(2)
|
|
||||||
calc_path(next)
|
|
||||||
if(path.len > 0)
|
|
||||||
src.visible_message("[src] makes a delighted ping!", "You hear a ping.")
|
|
||||||
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
|
|
||||||
mode = 4
|
|
||||||
mode =6
|
|
||||||
return
|
|
||||||
return
|
|
||||||
else
|
|
||||||
src.visible_message("[src] makes an annoyed buzzing sound", "You hear an electronic buzzing sound.")
|
|
||||||
playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
|
||||||
//world << "Bad turf."
|
|
||||||
mode = 5
|
|
||||||
return
|
|
||||||
else
|
|
||||||
//world << "No path."
|
|
||||||
mode = 5
|
|
||||||
return
|
|
||||||
|
|
||||||
if(5) // calculate new path
|
|
||||||
//world << "Calc new path."
|
|
||||||
mode = 6
|
|
||||||
spawn(0)
|
|
||||||
|
|
||||||
calc_path()
|
|
||||||
|
|
||||||
if(path.len > 0)
|
|
||||||
blockcount = 0
|
|
||||||
mode = 4
|
|
||||||
src.visible_message("[src] makes a delighted ping!", "You hear a ping.")
|
|
||||||
playsound(src.loc, 'sound/machines/ping.ogg', 50, 0)
|
|
||||||
|
|
||||||
else
|
|
||||||
src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.")
|
|
||||||
playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
|
||||||
|
|
||||||
mode = 7
|
|
||||||
//if(6)
|
|
||||||
//world << "Pending path calc."
|
|
||||||
//if(7)
|
|
||||||
//world << "No dest / no route."
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
// calculates a path to the current destination
|
|
||||||
// given an optional turf to avoid
|
|
||||||
/obj/machinery/bot/mulebot/proc/calc_path(var/turf/avoid = null)
|
|
||||||
src.path = AStar(src.loc, src.target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id=botcard, exclude=avoid)
|
|
||||||
if(!src.path)
|
|
||||||
src.path = list()
|
|
||||||
|
|
||||||
|
|
||||||
// sets the current destination
|
|
||||||
// signals all beacons matching the delivery code
|
|
||||||
// beacons will return a signal giving their locations
|
|
||||||
/obj/machinery/bot/mulebot/proc/set_destination(var/new_dest)
|
|
||||||
spawn(0)
|
|
||||||
new_destination = new_dest
|
|
||||||
post_signal(beacon_freq, "findbeacon", "delivery")
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
// starts bot moving to current destination
|
|
||||||
/obj/machinery/bot/mulebot/proc/start()
|
|
||||||
if(destination == home_destination)
|
|
||||||
mode = 3
|
|
||||||
else
|
|
||||||
mode = 2
|
|
||||||
icon_state = "mulebot[wires.MobAvoid()]"
|
|
||||||
|
|
||||||
// starts bot moving to home
|
|
||||||
// sends a beacon query to find
|
|
||||||
/obj/machinery/bot/mulebot/proc/start_home()
|
|
||||||
spawn(0)
|
|
||||||
set_destination(home_destination)
|
|
||||||
mode = 4
|
|
||||||
icon_state = "mulebot[wires.MobAvoid()]"
|
|
||||||
|
|
||||||
// called when bot reaches current target
|
|
||||||
/obj/machinery/bot/mulebot/proc/at_target()
|
|
||||||
if(!reached_target)
|
|
||||||
src.visible_message("[src] makes a chiming sound!", "You hear a chime.")
|
|
||||||
playsound(src.loc, 'sound/machines/chime.ogg', 50, 0)
|
|
||||||
reached_target = 1
|
|
||||||
|
|
||||||
if(load) // if loaded, unload at target
|
|
||||||
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)
|
|
||||||
load(AM)
|
|
||||||
// whatever happened, check to see if we return home
|
|
||||||
|
|
||||||
if(auto_return && destination != home_destination)
|
|
||||||
// auto return set and not at home already
|
|
||||||
start_home()
|
|
||||||
mode = 4
|
|
||||||
else
|
|
||||||
mode = 0 // otherwise go idle
|
|
||||||
|
|
||||||
send_status() // report status to anyone listening
|
|
||||||
|
|
||||||
return
|
|
||||||
|
|
||||||
// called when bot bumps into anything
|
|
||||||
/obj/machinery/bot/mulebot/Bump(var/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))
|
|
||||||
src.visible_message("<span class='warning'>[src] bumps into [M]!</span>")
|
|
||||||
else
|
|
||||||
src.visible_message("<span class='warning'>[src] knocks over [M]!</span>")
|
|
||||||
M.stop_pulling()
|
|
||||||
M.Stun(8)
|
|
||||||
M.Weaken(5)
|
|
||||||
M.lying = 1
|
|
||||||
..()
|
|
||||||
|
|
||||||
// called from mob/living/carbon/human/Crossed()
|
|
||||||
// when mulebot is in the same loc
|
|
||||||
/obj/machinery/bot/mulebot/proc/RunOver(var/mob/living/carbon/human/H)
|
|
||||||
src.visible_message("<span class='warning'>[src] drives over [H]!</span>")
|
|
||||||
playsound(src.loc, 'sound/effects/splat.ogg', 50, 1)
|
|
||||||
|
|
||||||
var/damage = rand(5,15)
|
|
||||||
H.apply_damage(2*damage, BRUTE, BP_HEAD)
|
|
||||||
H.apply_damage(2*damage, BRUTE, BP_TORSO)
|
|
||||||
H.apply_damage(0.5*damage, BRUTE, BP_L_LEG)
|
|
||||||
H.apply_damage(0.5*damage, BRUTE, BP_R_LEG)
|
|
||||||
H.apply_damage(0.5*damage, BRUTE, BP_L_ARM)
|
|
||||||
H.apply_damage(0.5*damage, BRUTE, BP_R_ARM)
|
|
||||||
|
|
||||||
blood_splatter(src,H,1)
|
|
||||||
bloodiness += 4
|
|
||||||
|
|
||||||
// player on mulebot attempted to move
|
|
||||||
/obj/machinery/bot/mulebot/relaymove(var/mob/user)
|
|
||||||
if(user.stat)
|
|
||||||
return
|
|
||||||
if(load == user)
|
|
||||||
unload(0)
|
|
||||||
return
|
|
||||||
|
|
||||||
// receive a radio signal
|
|
||||||
// used for control and beacon reception
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/receive_signal(datum/signal/signal)
|
|
||||||
|
|
||||||
if(!on)
|
|
||||||
return
|
|
||||||
|
|
||||||
var/recv = signal.data["command"]
|
|
||||||
// process all-bot input
|
|
||||||
if(recv=="bot_status" && wires.RemoteRX())
|
|
||||||
send_status()
|
|
||||||
|
|
||||||
|
|
||||||
recv = signal.data["command [suffix]"]
|
|
||||||
if(wires.RemoteRX())
|
|
||||||
// process control input
|
|
||||||
switch(recv)
|
|
||||||
if("stop")
|
|
||||||
mode = 0
|
|
||||||
return
|
|
||||||
|
|
||||||
if("go")
|
|
||||||
start()
|
|
||||||
return
|
|
||||||
|
|
||||||
if("target")
|
|
||||||
set_destination(signal.data["destination"] )
|
|
||||||
return
|
|
||||||
|
|
||||||
if("unload")
|
|
||||||
if(loc == target)
|
|
||||||
unload(loaddir)
|
|
||||||
else
|
|
||||||
unload(0)
|
|
||||||
return
|
|
||||||
|
|
||||||
if("home")
|
|
||||||
start_home()
|
|
||||||
return
|
|
||||||
|
|
||||||
if("bot_status")
|
|
||||||
send_status()
|
|
||||||
return
|
|
||||||
|
|
||||||
if("autoret")
|
|
||||||
auto_return = text2num(signal.data["value"])
|
|
||||||
return
|
|
||||||
|
|
||||||
if("autopick")
|
|
||||||
auto_pickup = text2num(signal.data["value"])
|
|
||||||
return
|
|
||||||
|
|
||||||
// receive response from beacon
|
|
||||||
recv = signal.data["beacon"]
|
|
||||||
if(wires.BeaconRX())
|
|
||||||
if(recv == new_destination) // if the recvd beacon location matches the set destination
|
|
||||||
// the we will navigate there
|
|
||||||
destination = new_destination
|
|
||||||
target = signal.source.loc
|
|
||||||
var/direction = signal.data["dir"] // this will be the load/unload dir
|
|
||||||
if(direction)
|
|
||||||
loaddir = text2num(direction)
|
|
||||||
else
|
|
||||||
loaddir = 0
|
|
||||||
icon_state = "mulebot[wires.MobAvoid()]"
|
|
||||||
calc_path()
|
|
||||||
updateDialog()
|
|
||||||
|
|
||||||
// send a radio signal with a single data key/value pair
|
|
||||||
/obj/machinery/bot/mulebot/proc/post_signal(var/freq, var/key, var/value)
|
|
||||||
post_signal_multiple(freq, list("[key]" = value) )
|
|
||||||
|
|
||||||
// send a radio signal with multiple data key/values
|
|
||||||
/obj/machinery/bot/mulebot/proc/post_signal_multiple(var/freq, var/list/keyval)
|
|
||||||
|
|
||||||
if(freq == beacon_freq && !wires.BeaconRX())
|
|
||||||
return
|
|
||||||
if(freq == control_freq && !wires.RemoteTX())
|
|
||||||
return
|
|
||||||
|
|
||||||
var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq)
|
|
||||||
|
|
||||||
if(!frequency) return
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var/datum/signal/signal = new()
|
|
||||||
signal.source = src
|
|
||||||
signal.transmission_method = 1
|
|
||||||
//for(var/key in keyval)
|
|
||||||
// signal.data[key] = keyval[key]
|
|
||||||
signal.data = keyval
|
|
||||||
//world << "sent [key],[keyval[key]] on [freq]"
|
|
||||||
if (signal.data["findbeacon"])
|
|
||||||
frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS)
|
|
||||||
else if (signal.data["type"] == "mulebot")
|
|
||||||
frequency.post_signal(src, signal, filter = RADIO_MULEBOT)
|
|
||||||
else
|
|
||||||
frequency.post_signal(src, signal)
|
|
||||||
|
|
||||||
// signals bot status etc. to controller
|
|
||||||
/obj/machinery/bot/mulebot/proc/send_status()
|
|
||||||
var/list/kv = list(
|
|
||||||
"type" = "mulebot",
|
|
||||||
"name" = suffix,
|
|
||||||
"loca" = (loc ? loc.loc : "Unknown"), // somehow loc can be null and cause a runtime - Quarxink
|
|
||||||
"mode" = mode,
|
|
||||||
"powr" = (cell ? cell.percent() : 0),
|
|
||||||
"dest" = destination,
|
|
||||||
"home" = home_destination,
|
|
||||||
"load" = load,
|
|
||||||
"retn" = auto_return,
|
|
||||||
"pick" = auto_pickup,
|
|
||||||
)
|
|
||||||
post_signal_multiple(control_freq, kv)
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/emp_act(severity)
|
|
||||||
if (cell)
|
|
||||||
cell.emp_act(severity)
|
|
||||||
if(load)
|
|
||||||
load.emp_act(severity)
|
|
||||||
..()
|
|
||||||
|
|
||||||
|
|
||||||
/obj/machinery/bot/mulebot/explode()
|
|
||||||
src.visible_message("<span class='danger'>[src] blows apart!</span>", 1)
|
|
||||||
var/turf/Tsec = get_turf(src)
|
|
||||||
|
|
||||||
new /obj/item/device/assembly/prox_sensor(Tsec)
|
|
||||||
PoolOrNew(/obj/item/stack/rods, Tsec)
|
|
||||||
PoolOrNew(/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/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
|
||||||
s.set_up(3, 1, src)
|
|
||||||
s.start()
|
|
||||||
|
|
||||||
new /obj/effect/decal/cleanable/blood/oil(src.loc)
|
|
||||||
unload(0)
|
|
||||||
qdel(src)
|
|
||||||
@@ -103,13 +103,6 @@
|
|||||||
bumpopen(M)
|
bumpopen(M)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(istype(AM, /obj/machinery/bot))
|
|
||||||
var/obj/machinery/bot/bot = AM
|
|
||||||
if(src.check_access(bot.botcard))
|
|
||||||
if(density)
|
|
||||||
open()
|
|
||||||
return
|
|
||||||
|
|
||||||
if(istype(AM, /mob/living/bot))
|
if(istype(AM, /mob/living/bot))
|
||||||
var/mob/living/bot/bot = AM
|
var/mob/living/bot/bot = AM
|
||||||
if(src.check_access(bot.botcard))
|
if(src.check_access(bot.botcard))
|
||||||
|
|||||||
@@ -420,8 +420,8 @@ var/global/list/obj/item/device/pda/PDAs = list()
|
|||||||
cartdata["radio"] = 1
|
cartdata["radio"] = 1
|
||||||
if(istype(cartridge.radio, /obj/item/radio/integrated/signal))
|
if(istype(cartridge.radio, /obj/item/radio/integrated/signal))
|
||||||
cartdata["radio"] = 2
|
cartdata["radio"] = 2
|
||||||
if(istype(cartridge.radio, /obj/item/radio/integrated/mule))
|
//if(istype(cartridge.radio, /obj/item/radio/integrated/mule))
|
||||||
cartdata["radio"] = 3
|
// cartdata["radio"] = 3
|
||||||
|
|
||||||
if(mode == 2)
|
if(mode == 2)
|
||||||
cartdata["charges"] = cartridge.charges ? cartridge.charges : 0
|
cartdata["charges"] = cartridge.charges ? cartridge.charges : 0
|
||||||
|
|||||||
@@ -124,10 +124,6 @@
|
|||||||
icon_state = "cart-q"
|
icon_state = "cart-q"
|
||||||
access_quartermaster = 1
|
access_quartermaster = 1
|
||||||
|
|
||||||
/obj/item/weapon/cartridge/quartermaster/initialize()
|
|
||||||
radio = new /obj/item/radio/integrated/mule(src)
|
|
||||||
..()
|
|
||||||
|
|
||||||
/obj/item/weapon/cartridge/head
|
/obj/item/weapon/cartridge/head
|
||||||
name = "\improper Easy-Record DELUXE"
|
name = "\improper Easy-Record DELUXE"
|
||||||
icon_state = "cart-h"
|
icon_state = "cart-h"
|
||||||
@@ -141,9 +137,6 @@
|
|||||||
access_janitor = 1
|
access_janitor = 1
|
||||||
access_security = 1
|
access_security = 1
|
||||||
|
|
||||||
/obj/item/weapon/cartridge/hop/initialize()
|
|
||||||
radio = new /obj/item/radio/integrated/mule(src)
|
|
||||||
|
|
||||||
/obj/item/weapon/cartridge/hos
|
/obj/item/weapon/cartridge/hos
|
||||||
name = "\improper R.O.B.U.S.T. DELUXE"
|
name = "\improper R.O.B.U.S.T. DELUXE"
|
||||||
icon_state = "cart-hos"
|
icon_state = "cart-hos"
|
||||||
@@ -351,40 +344,26 @@
|
|||||||
/* MULEBOT Control (Mode: 48) */
|
/* MULEBOT Control (Mode: 48) */
|
||||||
|
|
||||||
if(mode==48)
|
if(mode==48)
|
||||||
var/muleData[0]
|
|
||||||
var/mulebotsData[0]
|
var/mulebotsData[0]
|
||||||
if(istype(radio,/obj/item/radio/integrated/mule))
|
var/count = 0
|
||||||
var/obj/item/radio/integrated/mule/QC = radio
|
|
||||||
muleData["active"] = QC.active
|
|
||||||
if(QC.active && !isnull(QC.botstatus))
|
|
||||||
var/area/loca = QC.botstatus["loca"]
|
|
||||||
var/loca_name = sanitize(loca.name)
|
|
||||||
muleData["botstatus"] = list("loca" = loca_name, "mode" = QC.botstatus["mode"],"home"=QC.botstatus["home"],"powr" = QC.botstatus["powr"],"retn" =QC.botstatus["retn"], "pick"=QC.botstatus["pick"], "load" = QC.botstatus["load"], "dest" = sanitize(QC.botstatus["dest"]))
|
|
||||||
|
|
||||||
else
|
for(var/mob/living/bot/mulebot/M in living_mob_list)
|
||||||
muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null)
|
if(!M.on)
|
||||||
|
continue
|
||||||
|
++count
|
||||||
|
var/muleData[0]
|
||||||
|
muleData["name"] = M.suffix
|
||||||
|
muleData["location"] = get_area(M)
|
||||||
|
muleData["mode"] = M.mode
|
||||||
|
muleData["home"] = M.homeName
|
||||||
|
muleData["target"] = M.targetName
|
||||||
|
muleData["ref"] = "\ref[M]"
|
||||||
|
muleData["load"] = M.load ? M.load.name : "Nothing"
|
||||||
|
|
||||||
|
mulebotsData[++mulebotsData.len] = muleData.Copy()
|
||||||
|
|
||||||
var/mulebotsCount=0
|
values["mulebotcount"] = count
|
||||||
for(var/obj/machinery/bot/B in QC.botlist)
|
values["mulebots"] = mulebotsData
|
||||||
mulebotsCount++
|
|
||||||
if(B.loc)
|
|
||||||
mulebotsData[++mulebotsData.len] = list("Name" = sanitize(B.name), "Location" = sanitize(B.loc.loc.name), "ref" = "\ref[B]")
|
|
||||||
|
|
||||||
if(!mulebotsData.len)
|
|
||||||
mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null)
|
|
||||||
|
|
||||||
muleData["bots"] = mulebotsData
|
|
||||||
muleData["count"] = mulebotsCount
|
|
||||||
|
|
||||||
else
|
|
||||||
muleData["botstatus"] = list("loca" = null, "mode" = -1,"home"=null,"powr" = null,"retn" =null, "pick"=null, "load" = null, "dest" = null)
|
|
||||||
muleData["active"] = 0
|
|
||||||
mulebotsData[++mulebotsData.len] = list("Name" = "No bots found", "Location" = "Invalid", "ref"= null)
|
|
||||||
muleData["bots"] = mulebotsData
|
|
||||||
muleData["count"] = 0
|
|
||||||
|
|
||||||
values["mulebot"] = muleData
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -575,5 +554,9 @@
|
|||||||
loc:mode = 43
|
loc:mode = 43
|
||||||
mode = 43
|
mode = 43
|
||||||
|
|
||||||
|
if("MULEbot")
|
||||||
|
var/mob/living/bot/mulebot/M = locate(href_list["ref"])
|
||||||
|
if(istype(M))
|
||||||
|
M.obeyCommand(href_list["command"])
|
||||||
|
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
@@ -105,107 +105,6 @@
|
|||||||
radio_controller.remove_object(src, control_freq)
|
radio_controller.remove_object(src, control_freq)
|
||||||
..()
|
..()
|
||||||
|
|
||||||
/obj/item/radio/integrated/mule
|
|
||||||
var/list/botlist = null // list of bots
|
|
||||||
var/obj/machinery/bot/mulebot/active // the active bot; if null, show bot list
|
|
||||||
var/list/botstatus // the status signal sent by the bot
|
|
||||||
var/list/beacons
|
|
||||||
|
|
||||||
var/beacon_freq = 1400
|
|
||||||
var/control_freq = BOT_FREQ
|
|
||||||
|
|
||||||
// create a new QM cartridge, and register to receive bot control & beacon message
|
|
||||||
New()
|
|
||||||
..()
|
|
||||||
spawn(5)
|
|
||||||
if(radio_controller)
|
|
||||||
radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT)
|
|
||||||
radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS)
|
|
||||||
spawn(10)
|
|
||||||
post_signal(beacon_freq, "findbeacon", "delivery", s_filter = RADIO_NAVBEACONS)
|
|
||||||
|
|
||||||
// receive radio signals
|
|
||||||
// can detect bot status signals
|
|
||||||
// and beacon locations
|
|
||||||
// create/populate lists as they are recvd
|
|
||||||
|
|
||||||
receive_signal(datum/signal/signal)
|
|
||||||
// var/obj/item/device/pda/P = src.loc
|
|
||||||
|
|
||||||
/*
|
|
||||||
world << "recvd:[P] : [signal.source]"
|
|
||||||
for(var/d in signal.data)
|
|
||||||
world << "- [d] = [signal.data[d]]"
|
|
||||||
*/
|
|
||||||
if(signal.data["type"] == "mulebot")
|
|
||||||
if(!botlist)
|
|
||||||
botlist = new()
|
|
||||||
|
|
||||||
if(!(signal.source in botlist))
|
|
||||||
botlist += signal.source
|
|
||||||
|
|
||||||
if(active == signal.source)
|
|
||||||
var/list/b = signal.data
|
|
||||||
botstatus = b.Copy()
|
|
||||||
|
|
||||||
else if(signal.data["beacon"])
|
|
||||||
if(!beacons)
|
|
||||||
beacons = new()
|
|
||||||
|
|
||||||
beacons[signal.data["beacon"] ] = signal.source
|
|
||||||
|
|
||||||
|
|
||||||
// if(istype(P)) P.updateSelfDialog()
|
|
||||||
|
|
||||||
Topic(href, href_list)
|
|
||||||
..()
|
|
||||||
var/cmd = "command"
|
|
||||||
if(active) cmd = "command [active.suffix]"
|
|
||||||
|
|
||||||
switch(href_list["op"])
|
|
||||||
|
|
||||||
if("control")
|
|
||||||
active = locate(href_list["bot"])
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
|
|
||||||
if("scanbots") // find all bots
|
|
||||||
botlist = null
|
|
||||||
post_signal(control_freq, "command", "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
|
|
||||||
if("botlist")
|
|
||||||
active = null
|
|
||||||
|
|
||||||
|
|
||||||
if("unload")
|
|
||||||
post_signal(control_freq, cmd, "unload", s_filter = RADIO_MULEBOT)
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
if("setdest")
|
|
||||||
if(beacons)
|
|
||||||
var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in beacons
|
|
||||||
if(dest)
|
|
||||||
post_signal(control_freq, cmd, "target", "destination", dest, s_filter = RADIO_MULEBOT)
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
|
|
||||||
if("retoff")
|
|
||||||
post_signal(control_freq, cmd, "autoret", "value", 0, s_filter = RADIO_MULEBOT)
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
if("reton")
|
|
||||||
post_signal(control_freq, cmd, "autoret", "value", 1, s_filter = RADIO_MULEBOT)
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
|
|
||||||
if("pickoff")
|
|
||||||
post_signal(control_freq, cmd, "autopick", "value", 0, s_filter = RADIO_MULEBOT)
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
if("pickon")
|
|
||||||
post_signal(control_freq, cmd, "autopick", "value", 1, s_filter = RADIO_MULEBOT)
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
|
|
||||||
if("stop", "go", "home")
|
|
||||||
post_signal(control_freq, cmd, href_list["op"], s_filter = RADIO_MULEBOT)
|
|
||||||
post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Radio Cartridge, essentially a signaler.
|
* Radio Cartridge, essentially a signaler.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -51,6 +51,10 @@
|
|||||||
for(var/i = 1;i<=held_count;i++)
|
for(var/i = 1;i<=held_count;i++)
|
||||||
new held_type(src)
|
new held_type(src)
|
||||||
|
|
||||||
|
/obj/structure/largecrate/animal/mulebot
|
||||||
|
name = "Mulebot crate"
|
||||||
|
held_type = /mob/living/bot/mulebot
|
||||||
|
|
||||||
/obj/structure/largecrate/animal/corgi
|
/obj/structure/largecrate/animal/corgi
|
||||||
name = "corgi carrier"
|
name = "corgi carrier"
|
||||||
held_type = /mob/living/simple_animal/corgi
|
held_type = /mob/living/simple_animal/corgi
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ var/list/mechtoys = list(
|
|||||||
layer = 4
|
layer = 4
|
||||||
explosion_resistance = 5
|
explosion_resistance = 5
|
||||||
var/list/mobs_can_pass = list(
|
var/list/mobs_can_pass = list(
|
||||||
|
/mob/living/bot,
|
||||||
/mob/living/carbon/slime,
|
/mob/living/carbon/slime,
|
||||||
/mob/living/simple_animal/mouse,
|
/mob/living/simple_animal/mouse,
|
||||||
/mob/living/silicon/robot/drone
|
/mob/living/silicon/robot/drone
|
||||||
|
|||||||
@@ -90,10 +90,6 @@
|
|||||||
if(V)
|
if(V)
|
||||||
V.ex_act(2)
|
V.ex_act(2)
|
||||||
return
|
return
|
||||||
var/obj/machinery/bot/B = locate() in T
|
|
||||||
if(B)
|
|
||||||
B.ex_act(2)
|
|
||||||
return
|
|
||||||
var/obj/mecha/M = locate() in T
|
var/obj/mecha/M = locate() in T
|
||||||
if(M)
|
if(M)
|
||||||
M.visible_message("<span class='danger'>The blob attacks \the [M]!</span>")
|
M.visible_message("<span class='danger'>The blob attacks \the [M]!</span>")
|
||||||
|
|||||||
@@ -86,12 +86,6 @@
|
|||||||
"admin","ponies","heresy","meow","Pun Pun","monkey","Ian","moron","pizza","message","spam",\
|
"admin","ponies","heresy","meow","Pun Pun","monkey","Ian","moron","pizza","message","spam",\
|
||||||
"director", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno")
|
"director", "Hello", "Hi!"," ","nuke","crate","dwarf","xeno")
|
||||||
|
|
||||||
/datum/event/ionstorm/tick()
|
|
||||||
if(botEmagChance)
|
|
||||||
for(var/obj/machinery/bot/bot in world)
|
|
||||||
if(prob(botEmagChance))
|
|
||||||
bot.emag_act(1)
|
|
||||||
|
|
||||||
/datum/event/ionstorm/end()
|
/datum/event/ionstorm/end()
|
||||||
spawn(rand(5000,8000))
|
spawn(rand(5000,8000))
|
||||||
if(prob(50))
|
if(prob(50))
|
||||||
|
|||||||
@@ -29,6 +29,8 @@
|
|||||||
access_scanner.req_access = req_access.Copy()
|
access_scanner.req_access = req_access.Copy()
|
||||||
access_scanner.req_one_access = req_one_access.Copy()
|
access_scanner.req_one_access = req_one_access.Copy()
|
||||||
|
|
||||||
|
turn_on()
|
||||||
|
|
||||||
/mob/living/bot/Life()
|
/mob/living/bot/Life()
|
||||||
..()
|
..()
|
||||||
if(health <= 0)
|
if(health <= 0)
|
||||||
@@ -125,3 +127,70 @@
|
|||||||
|
|
||||||
/mob/living/bot/attack_throat()
|
/mob/living/bot/attack_throat()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
/******************************************************************/
|
||||||
|
// Navigation procs
|
||||||
|
// Used for A-star pathfinding
|
||||||
|
|
||||||
|
|
||||||
|
// Returns the surrounding cardinal turfs with open links
|
||||||
|
// Including through doors openable with the ID
|
||||||
|
/turf/proc/CardinalTurfsWithAccess(var/obj/item/weapon/card/id/ID)
|
||||||
|
var/L[] = new()
|
||||||
|
|
||||||
|
// for(var/turf/simulated/t in oview(src,1))
|
||||||
|
|
||||||
|
for(var/d in cardinal)
|
||||||
|
var/turf/simulated/T = get_step(src, d)
|
||||||
|
if(istype(T) && !T.density)
|
||||||
|
if(!LinkBlockedWithAccess(src, T, ID))
|
||||||
|
L.Add(T)
|
||||||
|
return L
|
||||||
|
|
||||||
|
|
||||||
|
// Returns true if a link between A and B is blocked
|
||||||
|
// Movement through doors allowed if ID has access
|
||||||
|
/proc/LinkBlockedWithAccess(turf/A, turf/B, obj/item/weapon/card/id/ID)
|
||||||
|
|
||||||
|
if(A == null || B == null) return 1
|
||||||
|
var/adir = get_dir(A,B)
|
||||||
|
var/rdir = get_dir(B,A)
|
||||||
|
if((adir & (NORTH|SOUTH)) && (adir & (EAST|WEST))) // diagonal
|
||||||
|
var/iStep = get_step(A,adir&(NORTH|SOUTH))
|
||||||
|
if(!LinkBlockedWithAccess(A,iStep, ID) && !LinkBlockedWithAccess(iStep,B,ID))
|
||||||
|
return 0
|
||||||
|
|
||||||
|
var/pStep = get_step(A,adir&(EAST|WEST))
|
||||||
|
if(!LinkBlockedWithAccess(A,pStep,ID) && !LinkBlockedWithAccess(pStep,B,ID))
|
||||||
|
return 0
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if(DirBlockedWithAccess(A,adir, ID))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if(DirBlockedWithAccess(B,rdir, ID))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
for(var/obj/O in B)
|
||||||
|
if(O.density && !istype(O, /obj/machinery/door) && !(O.flags & ON_BORDER))
|
||||||
|
return 1
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
// Returns true if direction is blocked from loc
|
||||||
|
// Checks doors against access with given ID
|
||||||
|
/proc/DirBlockedWithAccess(turf/loc,var/dir,var/obj/item/weapon/card/id/ID)
|
||||||
|
for(var/obj/structure/window/D in loc)
|
||||||
|
if(!D.density) continue
|
||||||
|
if(D.dir == SOUTHWEST) return 1
|
||||||
|
if(D.dir == dir) return 1
|
||||||
|
|
||||||
|
for(var/obj/machinery/door/D in loc)
|
||||||
|
if(!D.density) continue
|
||||||
|
if(istype(D, /obj/machinery/door/window))
|
||||||
|
if( dir & D.dir ) return !D.check_access(ID)
|
||||||
|
|
||||||
|
//if((dir & SOUTH) && (D.dir & (EAST|WEST))) return !D.check_access(ID)
|
||||||
|
//if((dir & EAST ) && (D.dir & (NORTH|SOUTH))) return !D.check_access(ID)
|
||||||
|
else return !D.check_access(ID) // it's a real, air blocking door
|
||||||
|
return 0
|
||||||
|
|||||||
431
code/modules/mob/living/bot/mulebot.dm
Normal file
431
code/modules/mob/living/bot/mulebot.dm
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
#define MULE_IDLE 0
|
||||||
|
#define MULE_MOVING 1
|
||||||
|
#define MULE_UNLOAD 2
|
||||||
|
#define MULE_LOST 3
|
||||||
|
#define MULE_CALC_MIN 4
|
||||||
|
#define MULE_CALC_MAX 10
|
||||||
|
#define MULE_PATH_DONE 11
|
||||||
|
// IF YOU CHANGE THOSE, UPDATE THEM IN pda.tmpl TOO
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot
|
||||||
|
name = "Mulebot"
|
||||||
|
desc = "A Multiple Utility Load Effector bot."
|
||||||
|
icon_state = "mulebot0"
|
||||||
|
anchored = 1
|
||||||
|
density = 1
|
||||||
|
health = 150
|
||||||
|
maxHealth = 150
|
||||||
|
mob_bump_flag = HEAVY
|
||||||
|
|
||||||
|
botcard_access = list(access_maint_tunnels, access_mailsorting, access_cargo, access_cargo_bot, access_qm, access_mining, access_mining_station)
|
||||||
|
|
||||||
|
var/busy = 0
|
||||||
|
var/mode = MULE_IDLE
|
||||||
|
var/atom/movable/load
|
||||||
|
|
||||||
|
var/crates_only = 1
|
||||||
|
var/auto_return = 1
|
||||||
|
var/safety = 1
|
||||||
|
|
||||||
|
var/list/path = list()
|
||||||
|
var/frustration = 0
|
||||||
|
var/turf/target
|
||||||
|
var/targetName
|
||||||
|
var/turf/home
|
||||||
|
var/homeName
|
||||||
|
var/turf/obstacle
|
||||||
|
|
||||||
|
var/global/amount = 0
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/New()
|
||||||
|
..()
|
||||||
|
|
||||||
|
var/turf/T = get_turf(loc)
|
||||||
|
var/obj/machinery/navbeacon/N = locate() in home
|
||||||
|
if(N)
|
||||||
|
home = T
|
||||||
|
homeName = N.location
|
||||||
|
else
|
||||||
|
homeName = "Unset"
|
||||||
|
|
||||||
|
suffix = num2text(++amount) // Starts from 1
|
||||||
|
|
||||||
|
name = "Mulebot #[suffix]"
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/MouseDrop_T(var/atom/movable/C, var/mob/user)
|
||||||
|
if(user.stat)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(!istype(C) || C.anchored || get_dist(user, src) > 1 || get_dist(src, C) > 1 )
|
||||||
|
return
|
||||||
|
|
||||||
|
load(C)
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/attack_hand(var/mob/user)
|
||||||
|
interact(user)
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/proc/interact(var/mob/user)
|
||||||
|
var/dat
|
||||||
|
dat += "<TT><B>Multiple Utility Load Effector Mk. III</B></TT><BR><BR>"
|
||||||
|
dat += "ID: [suffix]<BR>"
|
||||||
|
dat += "Power: [on ? "On" : "Off"]<BR>"
|
||||||
|
|
||||||
|
if(!open)
|
||||||
|
dat += "Status: "
|
||||||
|
switch(mode)
|
||||||
|
if(MULE_IDLE)
|
||||||
|
dat += "Ready"
|
||||||
|
if(MULE_MOVING, MULE_UNLOAD, MULE_PATH_DONE)
|
||||||
|
dat += "Navigating"
|
||||||
|
if(MULE_UNLOAD)
|
||||||
|
dat += "Unloading"
|
||||||
|
if(MULE_LOST)
|
||||||
|
dat += "Processing commands"
|
||||||
|
if(MULE_CALC_MIN to MULE_CALC_MAX)
|
||||||
|
dat += "Calculating navigation path"
|
||||||
|
|
||||||
|
dat += "<BR>Current Load: [load ? load.name : "<i>none</i>"]<BR>"
|
||||||
|
|
||||||
|
if(locked)
|
||||||
|
dat += "<HR>Controls are locked"
|
||||||
|
else
|
||||||
|
dat += "<HR>Controls are unlocked<BR><BR>"
|
||||||
|
|
||||||
|
if(!locked || issilicon(user))
|
||||||
|
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=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=cargotypes'>Toggle non-standard cargo</A> ([crates_only ? "Off" : "On"])<BR>"
|
||||||
|
|
||||||
|
if(load)
|
||||||
|
dat += "<A href='byond://?src=\ref[src];op=unload'>Unload now</A><BR>"
|
||||||
|
dat += "<HR>The maintenance hatch is closed.<BR>"
|
||||||
|
|
||||||
|
else
|
||||||
|
if(!issilicon(user))
|
||||||
|
dat += "The maintenance hatch is open.<BR><BR>"
|
||||||
|
|
||||||
|
dat += "<A href='byond://?src=\ref[src];op=safety'>Toggle safety</A> ([safety ? "On" : "Off - DANGER"])<BR>"
|
||||||
|
else
|
||||||
|
dat += "The bot is in maintenance mode and cannot be controlled.<BR>"
|
||||||
|
|
||||||
|
user << browse("<HEAD><TITLE>Mulebot [suffix ? "([suffix])" : ""]</TITLE></HEAD>[dat]", "window=mulebot;size=350x500")
|
||||||
|
onclose(user, "mulebot")
|
||||||
|
return
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/Topic(href, href_list)
|
||||||
|
if(..())
|
||||||
|
return
|
||||||
|
usr.set_machine(src)
|
||||||
|
add_fingerprint(usr)
|
||||||
|
switch(href_list["op"])
|
||||||
|
if("power")
|
||||||
|
if(on)
|
||||||
|
turn_off()
|
||||||
|
else
|
||||||
|
turn_on()
|
||||||
|
visible_message("[usr] switches [on ? "on" : "off"] [src].")
|
||||||
|
|
||||||
|
if("stop")
|
||||||
|
obeyCommand("Stop")
|
||||||
|
|
||||||
|
if("go")
|
||||||
|
obeyCommand("GoTD")
|
||||||
|
|
||||||
|
if("home")
|
||||||
|
obeyCommand("Home")
|
||||||
|
|
||||||
|
if("destination")
|
||||||
|
obeyCommand("SetD")
|
||||||
|
|
||||||
|
if("sethome")
|
||||||
|
var/new_dest
|
||||||
|
var/list/beaconlist = new()
|
||||||
|
for(var/obj/machinery/navbeacon/N in navbeacons)
|
||||||
|
beaconlist.Add(N.location)
|
||||||
|
beaconlist[N.location] = N
|
||||||
|
if(beaconlist.len)
|
||||||
|
new_dest = input("Select new home tag", "Mulebot [suffix ? "([suffix])" : ""]", null) in null|beaconlist
|
||||||
|
else
|
||||||
|
alert("No destination beacons available.")
|
||||||
|
if(new_dest)
|
||||||
|
home = get_turf(beaconlist[new_dest])
|
||||||
|
homeName = new_dest
|
||||||
|
|
||||||
|
if("unload")
|
||||||
|
unload()
|
||||||
|
|
||||||
|
if("autoret")
|
||||||
|
auto_return = !auto_return
|
||||||
|
|
||||||
|
if("cargotypes")
|
||||||
|
crates_only = !crates_only
|
||||||
|
|
||||||
|
if("safety")
|
||||||
|
safety = !safety
|
||||||
|
|
||||||
|
interact(usr)
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/attackby(var/obj/item/O, var/mob/user)
|
||||||
|
..()
|
||||||
|
update_icons()
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/proc/obeyCommand(var/command)
|
||||||
|
switch(command)
|
||||||
|
if("Home")
|
||||||
|
mode = MULE_IDLE
|
||||||
|
target = home
|
||||||
|
targetName = "Home"
|
||||||
|
mode = MULE_LOST
|
||||||
|
if("SetD")
|
||||||
|
var/new_dest
|
||||||
|
var/list/beaconlist = new()
|
||||||
|
for(var/obj/machinery/navbeacon/N in navbeacons)
|
||||||
|
beaconlist.Add(N.location)
|
||||||
|
beaconlist[N.location] = N
|
||||||
|
if(beaconlist.len)
|
||||||
|
new_dest = input("Select new destination tag", "Mulebot [suffix ? "([suffix])" : ""]") in null|beaconlist
|
||||||
|
else
|
||||||
|
alert("No destination beacons available.")
|
||||||
|
if(new_dest)
|
||||||
|
target = get_turf(beaconlist[new_dest])
|
||||||
|
targetName = new_dest
|
||||||
|
if("GoTD")
|
||||||
|
if(mode == MULE_IDLE)
|
||||||
|
mode = MULE_LOST
|
||||||
|
if("Stop")
|
||||||
|
mode = MULE_IDLE
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/emag_act(var/remaining_charges, var/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)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/update_icons()
|
||||||
|
if(open)
|
||||||
|
icon_state = "mulebot-hatch"
|
||||||
|
return
|
||||||
|
if(mode == MULE_MOVING || mode == MULE_UNLOAD)
|
||||||
|
icon_state = "mulebot1"
|
||||||
|
return
|
||||||
|
icon_state = "mulebot0"
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/Life()
|
||||||
|
..()
|
||||||
|
|
||||||
|
if(busy)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(!safety && prob(1))
|
||||||
|
flick("mulebot-emagged", src)
|
||||||
|
|
||||||
|
switch(mode)
|
||||||
|
if(MULE_IDLE) // Idle
|
||||||
|
return
|
||||||
|
if(MULE_MOVING) // Moving to target
|
||||||
|
if(!target) // Return home
|
||||||
|
if(auto_return && home)
|
||||||
|
target = home
|
||||||
|
targetName = "Home"
|
||||||
|
mode = MULE_LOST
|
||||||
|
else
|
||||||
|
mode = MULE_IDLE
|
||||||
|
update_icons()
|
||||||
|
return
|
||||||
|
if(loc == target) // Unload or stop
|
||||||
|
custom_emote(2, "makes a chiming sound.")
|
||||||
|
playsound(loc, 'sound/machines/chime.ogg', 50, 0)
|
||||||
|
mode = MULE_UNLOAD
|
||||||
|
update_icons()
|
||||||
|
return
|
||||||
|
if(path.len) // Move
|
||||||
|
var/turf/next = path[1]
|
||||||
|
if(next == loc)
|
||||||
|
path -= next
|
||||||
|
return
|
||||||
|
|
||||||
|
var/moved = step_towards(src, next)
|
||||||
|
if(moved)
|
||||||
|
frustration = 0
|
||||||
|
path -= next
|
||||||
|
else if(frustration < 6)
|
||||||
|
if(frustration == 3)
|
||||||
|
custom_emote(2, "makes an annoyed buzzing sound")
|
||||||
|
playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0)
|
||||||
|
++frustration
|
||||||
|
else
|
||||||
|
custom_emote(2, "makes a sighing buzz.")
|
||||||
|
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||||
|
obstacle = next
|
||||||
|
|
||||||
|
mode = MULE_LOST
|
||||||
|
else
|
||||||
|
mode = MULE_LOST
|
||||||
|
update_icons()
|
||||||
|
return
|
||||||
|
if(MULE_UNLOAD)
|
||||||
|
unload(dir)
|
||||||
|
|
||||||
|
if(auto_return && home && (loc != home))
|
||||||
|
target = home
|
||||||
|
targetName = "Home"
|
||||||
|
mode = MULE_LOST
|
||||||
|
else
|
||||||
|
mode = MULE_IDLE
|
||||||
|
update_icons()
|
||||||
|
return
|
||||||
|
if(MULE_LOST) // Lost my way
|
||||||
|
if(target)
|
||||||
|
spawn(0)
|
||||||
|
calc_path(obstacle)
|
||||||
|
mode = MULE_CALC_MIN
|
||||||
|
else
|
||||||
|
mode = MULE_IDLE
|
||||||
|
update_icons()
|
||||||
|
return
|
||||||
|
if(MULE_CALC_MIN to MULE_CALC_MAX) // Calcing path
|
||||||
|
if(path.len)
|
||||||
|
mode = MULE_PATH_DONE
|
||||||
|
update_icons()
|
||||||
|
else
|
||||||
|
++mode
|
||||||
|
return
|
||||||
|
if(MULE_PATH_DONE) // Done with path
|
||||||
|
obstacle = null
|
||||||
|
if(path.len)
|
||||||
|
frustration = 0
|
||||||
|
mode = MULE_MOVING
|
||||||
|
else
|
||||||
|
if(home)
|
||||||
|
target = home
|
||||||
|
targetName = "Home"
|
||||||
|
mode = MULE_LOST
|
||||||
|
else
|
||||||
|
mode = MULE_IDLE
|
||||||
|
update_icons()
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/Bump(var/mob/living/M)
|
||||||
|
if(!safety && istype(M))
|
||||||
|
visible_message("<span class='warning'>[src] knocks over [M]!</span>")
|
||||||
|
M.Stun(8)
|
||||||
|
M.Weaken(5)
|
||||||
|
..()
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/proc/runOver(var/mob/living/carbon/human/H)
|
||||||
|
if(istype(H)) // No safety checks - WILL run over lying humans. Stop ERPing in the maint!
|
||||||
|
visible_message("<span class='warning'>[src] drives over [H]!</span>")
|
||||||
|
playsound(loc, 'sound/effects/splat.ogg', 50, 1)
|
||||||
|
|
||||||
|
var/damage = rand(5, 7)
|
||||||
|
H.apply_damage(2 * damage, BRUTE, BP_HEAD)
|
||||||
|
H.apply_damage(2 * damage, BRUTE, BP_TORSO)
|
||||||
|
H.apply_damage(0.5 * damage, BRUTE, BP_L_LEG)
|
||||||
|
H.apply_damage(0.5 * damage, BRUTE, BP_R_LEG)
|
||||||
|
H.apply_damage(0.5 * damage, BRUTE, BP_L_ARM)
|
||||||
|
H.apply_damage(0.5 * damage, BRUTE, BP_R_ARM)
|
||||||
|
|
||||||
|
blood_splatter(src, H, 1)
|
||||||
|
..()
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/relaymove(var/mob/user, var/direction)
|
||||||
|
if(load == user)
|
||||||
|
unload(direction)
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/explode()
|
||||||
|
unload(pick(0, 1, 2, 4, 8))
|
||||||
|
|
||||||
|
visible_message("<span class='danger'>[src] blows apart!</span>")
|
||||||
|
|
||||||
|
var/turf/Tsec = get_turf(src)
|
||||||
|
new /obj/item/device/assembly/prox_sensor(Tsec)
|
||||||
|
PoolOrNew(/obj/item/stack/rods, Tsec)
|
||||||
|
PoolOrNew(/obj/item/stack/rods, Tsec)
|
||||||
|
new /obj/item/stack/cable_coil/cut(Tsec)
|
||||||
|
|
||||||
|
var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread
|
||||||
|
s.set_up(3, 1, src)
|
||||||
|
s.start()
|
||||||
|
|
||||||
|
new /obj/effect/decal/cleanable/blood/oil(Tsec)
|
||||||
|
..()
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/proc/calc_path(var/turf/avoid = null)
|
||||||
|
path = AStar(loc, target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 250, id = botcard, exclude = avoid)
|
||||||
|
if(!path)
|
||||||
|
path = list()
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/proc/load(var/atom/movable/C)
|
||||||
|
if(busy || load || get_dist(C, src) > 1 || !isturf(C.loc))
|
||||||
|
return
|
||||||
|
|
||||||
|
for(var/obj/structure/plasticflaps/P in src.loc)//Takes flaps into account
|
||||||
|
if(!CanPass(C,P))
|
||||||
|
return
|
||||||
|
|
||||||
|
if(crates_only && !istype(C,/obj/structure/closet/crate))
|
||||||
|
custom_emote(2, "makes a sighing buzz.")
|
||||||
|
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
var/obj/structure/closet/crate/crate = C
|
||||||
|
if(istype(crate))
|
||||||
|
crate.close()
|
||||||
|
|
||||||
|
//I'm sure someone will come along and ask why this is here... well people were dragging screen items onto the mule, and that was not cool.
|
||||||
|
//So this is a simple fix that only allows a selection of item types to be considered. Further narrowing-down is below.
|
||||||
|
//if(!istype(C,/obj/item) && !istype(C,/obj/machinery) && !istype(C,/obj/structure) && !ismob(C))
|
||||||
|
// return
|
||||||
|
busy = 1
|
||||||
|
|
||||||
|
C.loc = loc
|
||||||
|
sleep(2)
|
||||||
|
if(C.loc != loc) //To prevent you from going onto more than one bot.
|
||||||
|
return
|
||||||
|
C.loc = src
|
||||||
|
load = C
|
||||||
|
|
||||||
|
C.pixel_y += 9
|
||||||
|
if(C.layer < layer)
|
||||||
|
C.layer = layer + 0.1
|
||||||
|
overlays += C
|
||||||
|
|
||||||
|
busy = 0
|
||||||
|
|
||||||
|
/mob/living/bot/mulebot/proc/unload(var/dirn = 0)
|
||||||
|
if(!load || busy)
|
||||||
|
return
|
||||||
|
|
||||||
|
busy = 1
|
||||||
|
overlays.Cut()
|
||||||
|
|
||||||
|
load.loc = loc
|
||||||
|
load.pixel_y -= 9
|
||||||
|
load.layer = initial(load.layer)
|
||||||
|
|
||||||
|
if(dirn)
|
||||||
|
step(load, dirn)
|
||||||
|
|
||||||
|
load = null
|
||||||
|
|
||||||
|
for(var/atom/movable/AM in src)
|
||||||
|
if(AM == botcard || AM == access_scanner)
|
||||||
|
continue
|
||||||
|
|
||||||
|
AM.loc = loc
|
||||||
|
AM.layer = initial(AM.layer)
|
||||||
|
AM.pixel_y = initial(AM.pixel_y)
|
||||||
|
busy = 0
|
||||||
|
|
||||||
|
#undef MULE_IDLE
|
||||||
|
#undef MULE_MOVING
|
||||||
|
#undef MULE_UNLOAD
|
||||||
|
#undef MULE_LOST
|
||||||
|
#undef MULE_CALC_MIN
|
||||||
|
#undef MULE_CALC_MAX
|
||||||
|
#undef MULE_PATH_DONE
|
||||||
@@ -235,9 +235,9 @@
|
|||||||
// called when something steps onto a human
|
// called when something steps onto a human
|
||||||
// this handles mulebots and vehicles
|
// this handles mulebots and vehicles
|
||||||
/mob/living/carbon/human/Crossed(var/atom/movable/AM)
|
/mob/living/carbon/human/Crossed(var/atom/movable/AM)
|
||||||
if(istype(AM, /obj/machinery/bot/mulebot))
|
if(istype(AM, /mob/living/bot/mulebot))
|
||||||
var/obj/machinery/bot/mulebot/MB = AM
|
var/mob/living/bot/mulebot/MB = AM
|
||||||
MB.RunOver(src)
|
MB.runOver(src)
|
||||||
|
|
||||||
if(istype(AM, /obj/vehicle))
|
if(istype(AM, /obj/vehicle))
|
||||||
var/obj/vehicle/V = AM
|
var/obj/vehicle/V = AM
|
||||||
|
|||||||
@@ -50,13 +50,6 @@
|
|||||||
stance = HOSTILE_STANCE_ATTACK
|
stance = HOSTILE_STANCE_ATTACK
|
||||||
T = M
|
T = M
|
||||||
break
|
break
|
||||||
|
|
||||||
if(istype(A, /obj/machinery/bot))
|
|
||||||
var/obj/machinery/bot/B = A
|
|
||||||
if (B.health > 0)
|
|
||||||
stance = HOSTILE_STANCE_ATTACK
|
|
||||||
T = B
|
|
||||||
break
|
|
||||||
return T
|
return T
|
||||||
|
|
||||||
|
|
||||||
@@ -101,9 +94,6 @@
|
|||||||
var/obj/mecha/M = target_mob
|
var/obj/mecha/M = target_mob
|
||||||
M.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext)
|
M.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext)
|
||||||
return M
|
return M
|
||||||
if(istype(target_mob,/obj/machinery/bot))
|
|
||||||
var/obj/machinery/bot/B = target_mob
|
|
||||||
B.attack_generic(src,rand(melee_damage_lower,melee_damage_upper),attacktext)
|
|
||||||
|
|
||||||
/mob/living/simple_animal/hostile/proc/LoseTarget()
|
/mob/living/simple_animal/hostile/proc/LoseTarget()
|
||||||
stance = HOSTILE_STANCE_IDLE
|
stance = HOSTILE_STANCE_IDLE
|
||||||
|
|||||||
@@ -359,10 +359,6 @@
|
|||||||
var/obj/mecha/M = target_mob
|
var/obj/mecha/M = target_mob
|
||||||
if (M.occupant)
|
if (M.occupant)
|
||||||
return (0)
|
return (0)
|
||||||
if (istype(target_mob,/obj/machinery/bot))
|
|
||||||
var/obj/machinery/bot/B = target_mob
|
|
||||||
if(B.health > 0)
|
|
||||||
return (0)
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
/mob/living/simple_animal/say(var/message)
|
/mob/living/simple_animal/say(var/message)
|
||||||
|
|||||||
@@ -507,17 +507,6 @@
|
|||||||
namecounts[name] = 1
|
namecounts[name] = 1
|
||||||
creatures[name] = O
|
creatures[name] = O
|
||||||
|
|
||||||
if(istype(O, /obj/machinery/bot))
|
|
||||||
var/name = "BOT: [O.name]"
|
|
||||||
if (names.Find(name))
|
|
||||||
namecounts[name]++
|
|
||||||
name = "[name] ([namecounts[name]])"
|
|
||||||
else
|
|
||||||
names.Add(name)
|
|
||||||
namecounts[name] = 1
|
|
||||||
creatures[name] = O
|
|
||||||
|
|
||||||
|
|
||||||
for(var/mob/M in sortAtom(mob_list))
|
for(var/mob/M in sortAtom(mob_list))
|
||||||
var/name = M.name
|
var/name = M.name
|
||||||
if (names.Find(name))
|
if (names.Find(name))
|
||||||
|
|||||||
@@ -6544,8 +6544,8 @@
|
|||||||
"cvS" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
"cvS" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
||||||
"cvT" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
"cvT" = (/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
||||||
"cvU" = (/obj/machinery/navbeacon{codes_txt = "delivery;dir=8"; freq = 1400; location = "QM #3"},/obj/effect/floor_decal/industrial/outline/yellow,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
"cvU" = (/obj/machinery/navbeacon{codes_txt = "delivery;dir=8"; freq = 1400; location = "QM #3"},/obj/effect/floor_decal/industrial/outline/yellow,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
||||||
"cvV" = (/obj/machinery/navbeacon{codes_txt = "delivery;dir=8"; freq = 1400; location = "QM #2"},/obj/machinery/bot/mulebot{home_destination = "QM #2"; suffix = "#2"},/obj/effect/floor_decal/industrial/outline/yellow,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
"cvV" = (/obj/machinery/navbeacon{codes_txt = "delivery;dir=8"; freq = 1400; location = "QM #2"},/mob/living/bot/mulebot,/obj/effect/floor_decal/industrial/outline/yellow,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
||||||
"cvW" = (/obj/machinery/navbeacon{codes_txt = "delivery;dir=8"; freq = 1400; location = "QM #1"},/obj/machinery/bot/mulebot{beacon_freq = 1400; home_destination = "QM #1"; suffix = "#1"},/obj/effect/floor_decal/industrial/outline/yellow,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
"cvW" = (/obj/machinery/navbeacon{codes_txt = "delivery;dir=8"; freq = 1400; location = "QM #1"},/mob/living/bot/mulebot,/obj/effect/floor_decal/industrial/outline/yellow,/turf/simulated/floor/tiled,/area/quartermaster/storage)
|
||||||
"cvX" = (/obj/machinery/status_display/supply_display,/turf/simulated/wall,/area/quartermaster/qm)
|
"cvX" = (/obj/machinery/status_display/supply_display,/turf/simulated/wall,/area/quartermaster/qm)
|
||||||
"cvY" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk,/turf/simulated/floor/tiled,/area/quartermaster/qm)
|
"cvY" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk,/turf/simulated/floor/tiled,/area/quartermaster/qm)
|
||||||
"cvZ" = (/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/tiled,/area/quartermaster/qm)
|
"cvZ" = (/obj/structure/cable/green{d1 = 1; d2 = 2; icon_state = "1-2"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/turf/simulated/floor/tiled,/area/quartermaster/qm)
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm
|
|||||||
</div>
|
</div>
|
||||||
<div class="itemContent">
|
<div class="itemContent">
|
||||||
{{:helper.link('Supply Records', 'gear', {'choice' : "47"}, null, 'fixedLeftWide')}}
|
{{:helper.link('Supply Records', 'gear', {'choice' : "47"}, null, 'fixedLeftWide')}}
|
||||||
{{if data.cartridge.radio == 3}} {{:helper.link('Delivery Bot Control', 'gear', {'choice' : "48"}, null, 'fixedLeftWide')}} {{/if}}
|
{{:helper.link('Delivery Bot Control', 'gear', {'choice' : "48"}, null, 'fixedLeftWide')}}
|
||||||
<br>
|
<br>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -762,128 +762,38 @@ Used In File(s): \code\game\objects\items\devices\PDA\PDA.dm
|
|||||||
|
|
||||||
|
|
||||||
{{else data.mode == 48}}
|
{{else data.mode == 48}}
|
||||||
<H2>Mule Control </H2>
|
<H2>Mule Control</H2>
|
||||||
{{if data.records.mulebot.active == null || data.records.mulebot.active == 0}}
|
{{if data.records.mulebotcount == 0}}
|
||||||
{{if data.records.mulebot.count == 0}}
|
|
||||||
<H1><span class="bad">No bots found.</span></H1>
|
<H1><span class="bad">No bots found.</span></H1>
|
||||||
{{else}}
|
{{else}}
|
||||||
<H2>Mule List</H2>
|
<H2>Mule List</H2>
|
||||||
|
{{for data.records.mulebots}}
|
||||||
<div class="item">
|
<div class="item">
|
||||||
Select A Mulebot
|
<div class="itemLabel">Mulebot #{{:value.name}}</div>
|
||||||
|
<div class="itemContent">Location: {{:value.location}}<br>Home: {{:value.home}}<br>Target: {{:value.target}}<br>Load: {{:value.load}}</div>
|
||||||
</div>
|
</div>
|
||||||
<br>
|
|
||||||
{{for data.records.mulebot.bots}}
|
|
||||||
<div class="item">
|
<div class="item">
|
||||||
{{:helper.link(value.Name, 'gear', {'radiomenu' : "1", 'op' : "control",'bot' : value.ref}, null, null)}} (Location: {{:value.Location}})
|
<div class="itemLabel">Status:</div>
|
||||||
|
<div class="itemContent">
|
||||||
|
{{if value.mode == 0}}
|
||||||
|
Idle
|
||||||
|
{{else value.mode == 1}}
|
||||||
|
Moving
|
||||||
|
{{else value.mode == 2}}
|
||||||
|
Unloading
|
||||||
|
{{else}}
|
||||||
|
Calculating path
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="item">
|
||||||
|
{{:helper.link('Go home', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "Home"})}}
|
||||||
|
{{:helper.link('Set destination', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "SetD"})}}
|
||||||
|
{{:helper.link('Go', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "GoTD"})}}
|
||||||
|
{{:helper.link('Stop', null, {'cartmenu' : "1", 'choice' : "MULEbot", 'ref' : value.ref, 'command' : "Stop"})}}
|
||||||
</div>
|
</div>
|
||||||
{{/for}}
|
{{/for}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<br>
|
|
||||||
{{:helper.link('Scan for Bots','gear', {'radiomenu' : "1", 'op' : "scanbots"}, null, null)}}
|
|
||||||
{{else}}
|
|
||||||
{{if data.records.mulebot.botstatus.mode == -1}}
|
|
||||||
<H1><span class="bad">Waiting for response...</span></H1>
|
|
||||||
{{else}}
|
|
||||||
<H1><span class="average">Status:</span></H1>
|
|
||||||
<br>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Location:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
<span class="average">{{:data.records.mulebot.botstatus.loca}}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Mode:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
<span class="average">
|
|
||||||
{{if data.records.mulebot.botstatus.mode ==0}}
|
|
||||||
Ready
|
|
||||||
{{else data.records.mulebot.botstatus.mode == 1}}
|
|
||||||
Loading/Unloading
|
|
||||||
{{else data.records.mulebot.botstatus.mode ==2}}
|
|
||||||
Navigating to Delivery Location
|
|
||||||
{{else data.records.mulebot.botstatus.mode == 3}}
|
|
||||||
Navigating to Home
|
|
||||||
{{else data.records.mulebot.botstatus.mode ==4}}
|
|
||||||
Waiting for Clear Path
|
|
||||||
{{else data.records.mulebot.botstatus.mode ==5 || data.records.mulebot.botstatus.mode == 6}}
|
|
||||||
Calculating navigation Path
|
|
||||||
{{else data.records.mulebot.botstatus.mode ==7}}
|
|
||||||
Unable to locate destination
|
|
||||||
{{/if}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Current Load:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
<span class="average">
|
|
||||||
{{:helper.link(data.records.mulebot.botstatus.load == null ? 'None (Unload)' : data.records.mulebot.botstatus.load + ' (Unload)', 'gear', {'radiomenu' : "1", 'op' : "unload"},data.records.mulebot.botstatus.load == null ? 'disabled' : null, null)}}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Power:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
<span class="average">
|
|
||||||
{{:data.records.mulebot.botstatus.powr}}%
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Destination:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
{{:helper.link(data.records.mulebot.botstatus.dest == null || data.records.mulebot.botstatus.dest == "" ? 'None (Set)': data.records.mulebot.botstatus.dest+ ' (Set)', 'gear', {'radiomenu' : "1", 'op' : "setdest"}, null, null)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Home:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
{{if data.records.mulebot.botstatus.home == null}} None {{else}} {{:data.records.mulebot.botstatus.home}} {{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Auto Return:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
{{:helper.link(data.records.mulebot.botstatus.retn == 1 ? 'ON' : 'OFF', 'gear', {'radiomenu' : "1", 'op' : data.records.mulebot.botstatus.retn==1 ? "retoff" : "reton"}, null, null)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Auto Pickup:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
{{:helper.link(data.records.mulebot.botstatus.pick==1? 'ON' : 'OFF', 'gear', {'radiomenu' : "1", 'op' : data.records.mulebot.botstatus.pick==1 ? "pickoff" : "pickon"}, null, null)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item">
|
|
||||||
<div class="itemLabel">
|
|
||||||
<span class="good">Functions:</span>
|
|
||||||
</div>
|
|
||||||
<div class="itemContent">
|
|
||||||
{{:helper.link('Stop', 'gear', {'radiomenu' : "1", 'op' : "stop"}, null, null)}}
|
|
||||||
{{:helper.link('Proceed', 'gear', {'radiomenu' : "1", 'op' : "go"}, null, null)}}
|
|
||||||
{{:helper.link('Return Home', 'gear', {'radiomenu' : "1", 'op' : "home"}, null, null)}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br><br>
|
|
||||||
{{:helper.link('Return to Bot list', 'gear', {'radiomenu' : "1", 'op' : "botlist"}, null, null)}}
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
|
|
||||||
{{else data.mode == 49}}
|
{{else data.mode == 49}}
|
||||||
|
|||||||
@@ -215,7 +215,6 @@
|
|||||||
#include "code\datums\wires\autolathe.dm"
|
#include "code\datums\wires\autolathe.dm"
|
||||||
#include "code\datums\wires\camera.dm"
|
#include "code\datums\wires\camera.dm"
|
||||||
#include "code\datums\wires\explosive.dm"
|
#include "code\datums\wires\explosive.dm"
|
||||||
#include "code\datums\wires\mulebot.dm"
|
|
||||||
#include "code\datums\wires\particle_accelerator.dm"
|
#include "code\datums\wires\particle_accelerator.dm"
|
||||||
#include "code\datums\wires\radio.dm"
|
#include "code\datums\wires\radio.dm"
|
||||||
#include "code\datums\wires\robot.dm"
|
#include "code\datums\wires\robot.dm"
|
||||||
@@ -456,8 +455,6 @@
|
|||||||
#include "code\game\machinery\atmoalter\portable_atmospherics.dm"
|
#include "code\game\machinery\atmoalter\portable_atmospherics.dm"
|
||||||
#include "code\game\machinery\atmoalter\pump.dm"
|
#include "code\game\machinery\atmoalter\pump.dm"
|
||||||
#include "code\game\machinery\atmoalter\scrubber.dm"
|
#include "code\game\machinery\atmoalter\scrubber.dm"
|
||||||
#include "code\game\machinery\bots\bots.dm"
|
|
||||||
#include "code\game\machinery\bots\mulebot.dm"
|
|
||||||
#include "code\game\machinery\camera\camera.dm"
|
#include "code\game\machinery\camera\camera.dm"
|
||||||
#include "code\game\machinery\camera\camera_assembly.dm"
|
#include "code\game\machinery\camera\camera_assembly.dm"
|
||||||
#include "code\game\machinery\camera\motion.dm"
|
#include "code\game\machinery\camera\motion.dm"
|
||||||
@@ -1310,6 +1307,7 @@
|
|||||||
#include "code\modules\mob\living\bot\farmbot.dm"
|
#include "code\modules\mob\living\bot\farmbot.dm"
|
||||||
#include "code\modules\mob\living\bot\floorbot.dm"
|
#include "code\modules\mob\living\bot\floorbot.dm"
|
||||||
#include "code\modules\mob\living\bot\medbot.dm"
|
#include "code\modules\mob\living\bot\medbot.dm"
|
||||||
|
#include "code\modules\mob\living\bot\mulebot.dm"
|
||||||
#include "code\modules\mob\living\bot\secbot.dm"
|
#include "code\modules\mob\living\bot\secbot.dm"
|
||||||
#include "code\modules\mob\living\carbon\breathe.dm"
|
#include "code\modules\mob\living\carbon\breathe.dm"
|
||||||
#include "code\modules\mob\living\carbon\carbon.dm"
|
#include "code\modules\mob\living\carbon\carbon.dm"
|
||||||
|
|||||||
Reference in New Issue
Block a user