Files
2020-02-16 19:40:21 -08:00

546 lines
15 KiB
Plaintext

// flockdrone stuff, ask cirr or do a search for "flockdrone"
/////////////////////////////
// FLOCK DATUM
/////////////////////////////
// used to manage and share information between members of a flock/nest
/var/list/flocks = list()
/datum/flock
var/name
var/list/all_owned_tiles = list()
var/list/busy_tiles = list()
var/list/priority_tiles = list()
var/list/traces = list()
var/list/units = list()
var/list/enemies = list()
var/list/annotation_viewers = list()
var/list/annotations = list() // key is atom ref, value is image
var/mob/living/intangible/flock/flockmind/flockmind
var/snoop_clarity = 40 // how easily we can see silicon messages, how easily silicons can see this flock's messages
var/chui/window/flockpanel/panel
/datum/flock/New()
src.name = "[pick(consonants_lower)][pick(vowels_lower)].[pick(consonants_lower)][pick(vowels_lower)]"
flocks[src.name] = src
processing_items |= src
panel = new(src)
/datum/flock/proc/describe_state()
var/list/state = list()
state["update"] = "flock"
// DESCRIBE TRACES
var/list/tracelist = list()
for(var/mob/living/intangible/flock/trace/T in src.traces)
tracelist[++tracelist.len] = T.describe_state()
state["partitions"] = tracelist
// DESCRIBE DRONES
var/list/dronelist = list()
for(var/mob/living/critter/flock/drone/F in src.units)
dronelist[++dronelist.len] = F.describe_state()
state["drones"] = dronelist
// DESCRIBE ENEMIES
var/list/enemylist = list()
for(var/name in src.enemies)
var/list/enemy_stats = src.enemies[name]
var/mob/living/M = enemy_stats["mob"]
if(istype(M)) // fix runtime: Cannot read null.name
var/list/enemy = list()
enemy["name"] = M.name
enemy["area"] = enemy_stats["last_seen"]
enemy["ref"] = "\ref[M]"
enemylist[++enemylist.len] = enemy
else
// enemy no longer exists, let's do something about that
src.enemies -= name
state["enemies"] = enemylist
// DESCRIBE VITALS (do this last so we can report list lengths)
var/list/vitals = list()
vitals["name"] = src.name
vitals["drones"] = dronelist.len
vitals["partitions"] = tracelist.len
state["vitals"] = vitals
return state
/datum/flock/disposing()
flocks[src.name] = null
processing_items -= src
..()
/datum/flock/proc/total_health_percentage()
var/hp = 0
var/max_hp = 0
for(var/mob/living/critter/flock/F in src.units)
F.count_healths()
hp += F.health
max_hp += F.max_health
if(max_hp != 0)
return hp/max_hp
else
return 0
/datum/flock/proc/total_resources()
var/res = 0
for(var/mob/living/critter/flock/F in src.units)
res += F.resources
return res
/datum/flock/proc/registerFlockmind(var/mob/living/intangible/flock/flockmind/F)
if(!F)
return
src.flockmind = F
/datum/flock/proc/addTrace(var/mob/living/intangible/flock/trace/T)
if(!T)
return
src.traces |= T
// update the flock control panel
var/list/update = T.describe_state()
update["update"] = "add"
update["key"] = "partitions"
// ref is already provided
panel.PushUpdate(update)
/datum/flock/proc/removeTrace(var/mob/living/intangible/flock/trace/T)
if(!T)
return
src.traces -= T
// update the flock control panel
var/list/update = list()
update["update"] = "remove"
update["key"] = "partitions"
update["ref"] = "\ref[T]"
panel.PushUpdate(update)
// ANNOTATIONS
// currently both flockmind and player units get the same annotations: what tiles are marked for conversion, and who is shitlisted
/datum/flock/proc/showAnnotations(var/mob/M)
if(!M)
return
src.annotation_viewers |= M
var/client/C = M.client
if(C)
var/image/I
for(var/atom/key in src.annotations)
I = src.annotations[key]
if(istype(I, /image))
C.images |= I
/datum/flock/proc/hideAnnotations(var/mob/M)
if(!M)
return
src.annotation_viewers -= M
var/client/C = M.client
if(C)
var/image/I
for(var/atom/key in src.annotations)
I = src.annotations[key]
C.images -= I
// if anyone thinks they can optimise this PLEASE DO OH GOD - cirr, 2017
/datum/flock/proc/updateAnnotations()
var/image/I
var/list/valid_keys = list()
var/list/images_to_add = list()
// highlight priority tiles
for(var/turf/T in src.priority_tiles)
if(!(T in src.annotations))
// create a new image
I = image('icons/misc/featherzone.dmi', T, "frontier")
I.blend_mode = BLEND_ADD
I.alpha = 180
I.plane = PLANE_SELFILLUM + 1.1
// add to subscribers for annotations
images_to_add |= I
src.annotations[T] = I
// add key to list
valid_keys |= T
// highlight reserved tiles
for(var/name in src.busy_tiles)
var/turf/T = src.busy_tiles[name]
if(isturf(T) && !(T in src.annotations))
// create a new image
I = image('icons/misc/featherzone.dmi', T, "frontier")
I.blend_mode = BLEND_ADD
I.alpha = 80
I.plane = PLANE_SELFILLUM + 1.1
// add to subscribers for annotations
images_to_add |= I
src.annotations[T] = I
// add key to list
valid_keys |= T
// highlight enemies
for(var/name in src.enemies)
var/mob/B = src.enemies[name]["mob"]
if(!(B in src.annotations))
// create a new image
I = image('icons/misc/featherzone.dmi', B, "hazard")
I.blend_mode = BLEND_ADD
I.pixel_y = 16
I.plane = PLANE_SELFILLUM + 1.1
I.appearance_flags = RESET_COLOR | RESET_ALPHA | RESET_TRANSFORM
// add to subscribers for annotations
images_to_add |= I
src.annotations[B] = I
// add key to list
valid_keys |= B
var/list/to_remove = list()
for(var/atom/key in src.annotations)
if(!(key in valid_keys))
to_remove |= key
// add images
for(I in images_to_add)
for(var/mob/M in src.annotation_viewers)
var/client/C = M.client
if(C)
C.images += I
// purge images & annotation entries
for(var/atom/key in to_remove)
I = src.annotations[key]
src.annotations -= key
for(var/mob/M in src.annotation_viewers)
var/client/C = M.client
if(C)
C.images -= I
// UNITS
/datum/flock/proc/registerUnit(var/atom/movable/D)
if(isflock(D))
src.units |= D
if(src.panel && istype(D, /mob/living/critter/flock/drone))
var/mob/living/critter/flock/drone/drone = D
// update the flock control panel
var/list/update = drone.describe_state()
update["update"] = "add"
update["key"] = "drones"
// ref is already provided
panel.PushUpdate(update)
if(istype(D, /obj/flock_structure/egg))
src.units |= D
/datum/flock/proc/removeDrone(var/atom/movable/D)
if(isflock(D))
src.units -= D
// update the flock control panel
var/list/update = list()
update["update"] = "remove"
update["key"] = "drones"
update["ref"] = "\ref[D]"
panel.PushUpdate(update)
if(D:real_name && busy_tiles[D:real_name])
src.busy_tiles[D:real_name] = null
if(istype(D, /obj/flock_structure/egg))
src.units -= D
/datum/flock/proc/getComplexDroneCount()
var/count = 0
for(var/mob/living/critter/flock/drone/D in src.units)
count++
return count
// ENEMIES
/datum/flock/proc/updateEnemy(var/mob/living/M)
if(!M)
return
var/enemy_name = lowertext(M.name)
var/list/enemy_deets
if(!(enemy_name in src.enemies))
// add new
var/area/enemy_area = get_area(M)
enemy_deets = list()
enemy_deets["mob"] = M
enemy_deets["last_seen"] = enemy_area
src.enemies[enemy_name] = enemy_deets
// update the flock control panel
var/list/update = list()
update["update"] = "add"
update["key"] = "enemies"
update["ref"] = "\ref[M]"
update["name"] = M.name
update["area"] = enemy_area
panel.PushUpdate(update)
else
enemy_deets = src.enemies[enemy_name]
enemy_deets["last_seen"] = get_area(M)
// update annotations indicating enemies for flockmind and co
src.updateAnnotations()
/datum/flock/proc/removeEnemy(var/mob/living/M)
// call off all drones attacking this guy
for(var/name in src.enemies)
var/list/enemy_stats = src.enemies[name]
if(enemy_stats["mob"] == M)
src.enemies -= name
// update the flock control panel
var/list/update = list()
update["update"] = "remove"
update["key"] = "enemies"
update["ref"] = "\ref[M]"
panel.PushUpdate(update)
src.updateAnnotations()
/datum/flock/proc/isEnemy(var/mob/living/M)
var/enemy_name = lowertext(M.name)
return (enemy_name in src.enemies)
// DEATH
/datum/flock/proc/perish()
//cleanup as necessary
if(src.flockmind)
hideAnnotations(src.flockmind)
for(var/mob/M in src.units)
hideAnnotations(M)
all_owned_tiles = null
busy_tiles = null
priority_tiles = null
units = null
enemies = null
annotations = null
flockmind = null
qdel(src)
// TURFS
/datum/flock/proc/reserveTurf(var/turf/simulated/T, var/name)
if(T in all_owned_tiles)
return
src.busy_tiles[name] = T
src.updateAnnotations()
/datum/flock/proc/unreserveTurf(var/name)
src.busy_tiles -= name
src.updateAnnotations()
/datum/flock/proc/claimTurf(var/turf/simulated/T)
src.all_owned_tiles |= T
src.priority_tiles -= T // we have it now, it's no longer priority
src.updateAnnotations()
/datum/flock/proc/isTurfFree(var/turf/simulated/T, var/queryName) // provide the drone's name here: if they own the turf it's free _to them_
for(var/name in src.busy_tiles)
if(name == queryName)
continue
if(src.busy_tiles[name] == T)
return 0
return 1
/datum/flock/proc/togglePriorityTurf(var/turf/simulated/T)
if(!T)
return 1 // error!!
if(T in priority_tiles)
priority_tiles -= T
else
priority_tiles |= T
src.updateAnnotations()
// get closest unclaimed tile to requester
/datum/flock/proc/getPriorityTurfs(var/mob/living/critter/flock/drone/requester)
if(!requester)
return
if(src.busy_tiles[requester.name])
return src.busy_tiles[requester.name] // work on your claimed tile first you JERK
if(priority_tiles && priority_tiles.len)
var/list/available_tiles = priority_tiles
for(var/owner in src.busy_tiles)
available_tiles -= src.busy_tiles[owner]
return available_tiles
// PROCESS
/datum/flock/proc/process()
var/list/floors_no_longer_existing = list()
// check all active floors
for(var/turf/simulated/floor/feather/T in src.all_owned_tiles)
if(!T || T.loc == null || T.broken)
// tile got killed, remove it
floors_no_longer_existing |= T
continue
// check adjacent tiles to see if we've been surrounded and can start generating, or if we're no longer surrounded and can't generate
// var/validNeighbors = 0
// var/list/neighbors = getNeighbors(T, cardinal)
// for(var/turf/simulated/floor/feather/F in neighbors)
// validNeighbors += 1
// if(validNeighbors < 4 && T.generating)
// T.off()
// else if(validNeighbors >= 4 && !T.generating)
// T.on()
if(floors_no_longer_existing.len > 0)
src.all_owned_tiles -= floors_no_longer_existing
/datum/flock/proc/convert_turf(var/turf/T, var/converterName)
src.unreserveTurf(converterName)
src.claimTurf(flock_convert_turf(T))
playsound(T, "sound/items/Deconstruct.ogg", 70, 1)
////////////////////
// GLOBAL PROCS!!
////////////////////
// made into a global proc so a reagent can use it
// simple enough: if object path matches key, replace with instance of value
// if value is null, just delete object
/var/list/flock_conversion_paths = list(
/obj/grille = /obj/grille/flock,
/obj/window = /obj/window/feather,
/obj/machinery/door/airlock = /obj/machinery/door/feather,
/obj/machinery/door = null,
/obj/stool = /obj/stool/chair/comfy/flock,
/obj/table = /obj/table/flock/auto,
/obj/lattice = /obj/lattice/flock,
/obj/machinery/light = /obj/machinery/light/flock,
/obj/storage/closet = /obj/storage/closet/flock,
/obj/storage/secure/closet = /obj/storage/closet/flock
)
/proc/flock_convert_turf(var/turf/T)
if(!T)
return
// take light values to copy over
var/RL_LumR = T.RL_LumR
var/RL_LumG = T.RL_LumG
var/RL_LumB = T.RL_LumB
var/RL_AddLumR = T.RL_AddLumR
var/RL_AddLumG = T.RL_AddLumG
var/RL_AddLumB = T.RL_AddLumB
for(var/obj/O in T)
if(istype(O, /obj/machinery/door/feather))
// repair door
var/obj/machinery/door/feather/door = O
door.heal_damage()
animate_flock_convert_complete(O)
for(var/keyPath in flock_conversion_paths)
var/obj/replacementPath = flock_conversion_paths[keyPath]
if(istype(O, keyPath))
if(isnull(replacementPath))
qdel(O)
else
var/dir = O.dir
var/obj/converted = new replacementPath(T)
// if the object is a closet, it might not have spawned its contents yet
// so force it to do that first
if(istype(O, /obj/storage))
var/obj/storage/S = O
if(!isnull(S.spawn_contents))
S.make_my_stuff()
// if the object has contents, move them over!!
for (var/obj/OO in O)
OO.set_loc(converted)
for (var/mob/M in O)
M.set_loc(converted)
qdel(O)
converted.dir = dir
animate_flock_convert_complete(converted)
// if floor, turn to floor, if wall, turn to wall
if(istype(T, /turf/simulated/floor))
if(istype(T, /turf/simulated/floor/feather))
// fix instead of replace
var/turf/simulated/floor/feather/TF = T
TF.repair()
animate_flock_convert_complete(T)
else
T.ReplaceWith("/turf/simulated/floor/feather", 0)
animate_flock_convert_complete(T)
if(istype(T, /turf/simulated/wall))
T.ReplaceWith("/turf/simulated/wall/auto/feather", 0)
animate_flock_convert_complete(T)
if(istype(T, /turf/space))
// if we have a fibrenet, make it a floor
var/obj/lattice/flock/FL = locate(/obj/lattice/flock) in T
if(istype(FL))
qdel(FL)
T.ReplaceWith("/turf/simulated/floor/feather", 0)
animate_flock_convert_complete(T)
// if we have no fibrenet, make one
else
FL = new(T)
animate_flock_convert_complete(FL)
else // don't do this stuff if the turf is space, it fucks it up more
T.RL_Cleanup()
T.RL_UpdateLight()
T.RL_LumR = RL_LumR
T.RL_LumG = RL_LumG
T.RL_LumB = RL_LumB
T.RL_AddLumR = RL_AddLumR
T.RL_AddLumG = RL_AddLumG
T.RL_AddLumB = RL_AddLumB
T.RL_UpdateLight()
return T
/proc/mass_flock_convert_turf(var/turf/T)
// a terrible idea
if(!T)
T = get_turf(usr)
if(!T)
return // not sure if this can happen, so it will
flock_spiral_conversion(T)
/proc/radial_flock_conversion(var/atom/movable/source, var/max_radius=20)
if(!source) return
var/turf/T = get_turf(source)
var/radius = 1
while(radius <= max_radius)
var/list/turfs = circular_range(T, radius)
LAGCHECK(LAG_LOW)
for(var/turf/tile in turfs)
if(istype(tile, /turf/simulated) && !isfeathertile(tile))
flock_convert_turf(tile)
sleep(0.5)
LAGCHECK(LAG_LOW)
radius++
sleep(radius * 10)
if(isnull(source))
return // our source is gone, stop the process
/proc/flock_spiral_conversion(var/turf/T)
if(!T) return
// spiral algorithm adapted from https://stackoverflow.com/questions/398299/looping-in-a-spiral
var/ox = T.x
var/oy = T.y
var/x = 0
var/y = 0
var/z = T.z
var/dx = 0
var/dy = -1
var/temp = 0
while(isturf(T))
if(istype(T, /turf/simulated) && !isfeathertile(T))
// do stuff to turf
flock_convert_turf(T)
sleep(2)
LAGCHECK(LAG_LOW)
// figure out where next turf is
if (x == y || (x < 0 && x == -y) || (x > 0 && x == 1-y))
temp = dx
dx = -dy
dy = temp
x += dx
y += dy
// get next turf
T = locate(ox + x, oy + y, z)