Files
CHOMPStation2/code/modules/rogueminer_vr/zonemaster.dm
Casey 9d16270264 Merge pull request #13620 from Cameron653/XENOARCH_HOTFIX
Buffs artifact spawns in the map. Lowers rogueminer.
2022-09-02 05:13:02 +00:00

434 lines
14 KiB
Plaintext

//////////////////////////////
// The zonemaster object, spawned to track the zone and
// clean/populate the zone with asteroids and loot
//////////////////////////////
/datum/rogue/zonemaster
//our area
var/area/asteroid/rogue/myarea
// var/area/shuttle/belter/myshuttle
var/obj/effect/shuttle_landmark/myshuttle_landmark
//world.time
var/prepared_at = 0
//accepting shuttles
var/ready = 0
//completely empty
var/clean = 1
//scored or not
var/scored = 0
//for scoring
var/list/mineral_rocks = list()
var/list/spawned_mobs = list()
var/original_mobs = 0
//in-use spawns from the area
var/list/obj/asteroid_spawner/rockspawns = list()
var/list/obj/rogue_mobspawner/mobspawns = list()
/datum/rogue/zonemaster/New(var/area/A)
ASSERT(A)
myarea = A
myshuttle_landmark = locate(/obj/effect/shuttle_landmark) in myarea
if(!istype(myshuttle_landmark))
warning("Zonemaster cannot find a shuttle landmark in its area '[A]'")
spawn(10) //This is called from controller New() and freaks out if this calls back too fast.
rm_controller.mark_clean(src)
///////////////////////////////
///// Utility Procs ///////////
///////////////////////////////
/datum/rogue/zonemaster/proc/is_occupied()
var/humans = 0
for(var/mob/living/carbon/human/H in human_mob_list)
if(H.stat >= DEAD) //Conditions for exclusion here, like if disconnected people start blocking it.
continue
var/area/A = get_area(H)
if(A == myarea) //The loc of a turf is the area it is in.
humans++
return humans
///////////////////////////////
///// Asteroid Generation /////
///////////////////////////////
/datum/rogue/zonemaster/proc/generate_asteroid(var/core_min = 2, var/core_max = 5)
//Chance for a predefined structure instead, more common later
if(prob(rm_controller.diffstep*4))
rm_controller.dbg("ZM(ga): Fell into prefab asteroid chance.")
var/prefab = pick(rm_controller.prefabs["tier[rm_controller.diffstep]"])
rm_controller.dbg("ZM(ga): Picked [prefab] as the prefab.")
var/prefabinst = new prefab(null)
return prefabinst
var/datum/rogue/asteroid/A = new(rand(core_min,core_max))
rm_controller.dbg("ZM(ga): New asteroid with C:[A.coresize], TW:[A.type_wall].")
//Add the core to the asteroid's map
rm_controller.dbg("ZM(ga): Starting core generation for [A.coresize] size core..")
for(var/x = 1; x <= A.coresize, x++)
for(var/y = 1; y <= A.coresize, y++)
rm_controller.dbg("ZM(ga): Doing core-relative [x],[y] at [A.coresize+x],[A.coresize+y], [A.type_wall].")
A.spot_add(A.coresize+x, A.coresize+y, A.type_wall)
var/max_armlen = A.coresize - 1 //Can tweak to change appearance.
//Add the arms to the asteroid's map
//Vertical arms
for(var/x = A.coresize+1, x <= A.coresize*2, x++) //Start at leftmost side of core, work towards higher X.
rm_controller.dbg("ZM(ga): Vert arms. My current column is x:[x].")
var/B_arm = rand(0,max_armlen)
var/T_arm = rand(0,max_armlen)
rm_controller.dbg("ZM(ga): B/T. Going to make B:[B_arm], T:[T_arm] for x:[x].")
//Bottom arm
for(var/y = A.coresize, y > A.coresize-B_arm, y--) //Start at bottom edge of the core, work towards lower Y.
A.spot_add(x,y,A.type_wall)
//Top arm
for(var/y = (A.coresize*2)+1, y < ((A.coresize*2)+1)+T_arm, y++) //Start at top edge of the core, work towards higher Y.
A.spot_add(x,y,A.type_wall)
//Horizontal arms
for(var/y = A.coresize+1, y <= A.coresize*2, y++) //Start at lower side of core, work towards higher Y.
rm_controller.dbg("ZM(ga): Horiz arms. My current row is y:[y].")
var/R_arm = rand(0,max_armlen)
var/L_arm = rand(0,max_armlen)
rm_controller.dbg("ZM(ga): R/L. Going to make R:[R_arm], L:[L_arm] for y:[y].")
//Right arm
for(var/x = (A.coresize*2)+1, x <= ((A.coresize*2)+1)+R_arm, x++) //Start at right edge of core, work towards higher X.
A.spot_add(x,y,A.type_wall)
//Left arm
for(var/x = A.coresize, x > A.coresize-L_arm, x--) //Start at left edge of core, work towards lower X.
A.spot_add(x,y,A.type_wall)
//Diagonals
// hao do
rm_controller.dbg("ZM(ga): Asteroid generation done.")
return A
/datum/rogue/zonemaster/proc/place_asteroid(var/datum/rogue/asteroid/A,var/obj/asteroid_spawner/SP)
ASSERT(SP && A)
rm_controller.dbg("ZM(pa): Placing at point [SP.x],[SP.y],[SP.z].")
SP.myasteroid = A
//Bottom-left corner of our bounding box
var/BLx = SP.x - (A.width/2)
var/BLy = SP.y - (A.width/2)
rm_controller.dbg("ZM(pa): BLx is [BLx], BLy is [BLy].")
rm_controller.dbg("ZM(pa): The asteroid has [A.map.len] X-lists.")
var/list/changedturfs = list()
for(var/Ix=1, Ix <= A.map.len, Ix++)
var/list/curr_x = A.map[Ix]
rm_controller.dbg("ZM(pa): Now doing X:[Ix] which has [curr_x.len] Y-lists.")
for(var/Iy=1, Iy <= curr_x.len, Iy++)
var/list/curr_y = curr_x[Iy]
rm_controller.dbg("ZM(pa): Now doing Y:[Iy] which has [curr_y.len] items.")
var/world_x = BLx+Ix
var/world_y = BLy+Iy
var/world_z = SP.z
var/spot = locate(world_x,world_y,world_z)
for(var/T in curr_y)
rm_controller.dbg("ZM(pa): Doing entry [T] in Y-list [Iy].")
if(ispath(T,/turf)) //We're spawning a turf
rm_controller.dbg("ZM(pa): Turf-generate mode.")
//Make sure we locate()'d a turf and not something else
if(!isturf(spot))
spot = get_turf(spot)
var/turf/P = spot
rm_controller.dbg("ZM(pa): Replacing [P.type] with [T].")
var/turf/newturf = P.ChangeTurf(T)
changedturfs += newturf
switch(newturf.type)
if(/turf/simulated/mineral/vacuum)
place_resources(newturf)
else //Anything not a turf
rm_controller.dbg("ZM(pa): Creating [T].")
new T(spot)
for(var/turf/T in changedturfs)
T.update_icon(1)
/datum/rogue/zonemaster/proc/place_resources(var/turf/simulated/mineral/M)
#define XENOARCH_SPAWN_CHANCE 0.3
#define DIGSITESIZE_LOWER 4
#define DIGSITESIZE_UPPER 12
#define ARTIFACTSPAWNNUM_LOWER 1
#define ARTIFACTSPAWNNUM_UPPER 1 //Replace with difficulty-based ones.
if(!M.mineral && prob(rm_controller.diffstep_chances[rm_controller.diffstep])) //Difficulty translates directly into ore chance
rm_controller.dbg("ZM(par): Adding mineral to [M.x],[M.y].")
M.make_ore(rm_controller.diffstep >= 3 ? 1 : 0)
mineral_rocks += M
//If above difficulty threshold make rare ore instead (M.make_ore(1))
//Increase with difficulty etc
if(!M.density)
return
if(isnull(M.geologic_data))
M.geologic_data = new /datum/geosample(M)
if(!prob(XENOARCH_SPAWN_CHANCE))
return
var/farEnough = 1
for(var/turf/T as anything in SSxenoarch.digsite_spawning_turfs)
if(T in range(5, M))
farEnough = 0
break
if(!farEnough)
return
SSxenoarch.digsite_spawning_turfs.Add(M)
var/digsite = get_random_digsite_type()
var/target_digsite_size = rand(DIGSITESIZE_LOWER, DIGSITESIZE_UPPER)
var/list/processed_turfs = list()
var/list/turfs_to_process = list(M)
var/list/viable_adjacent_turfs = list()
if(target_digsite_size > 1)
for(var/turf/simulated/mineral/T in orange(2, M))
if(!T.density)
continue
if(T.finds)
continue
if(T in processed_turfs)
continue
viable_adjacent_turfs.Add(T)
target_digsite_size = min(target_digsite_size, viable_adjacent_turfs.len)
for(var/i = 1 to target_digsite_size)
turfs_to_process += pick_n_take(viable_adjacent_turfs)
while(turfs_to_process.len)
var/turf/simulated/mineral/archeo_turf = pop(turfs_to_process)
rm_controller.dbg("ZM(par): Adding archeo find to [M.x],[M.y].")
processed_turfs.Add(archeo_turf)
if(isnull(archeo_turf.finds))
archeo_turf.finds = list()
if(prob(50))
archeo_turf.finds.Add(new /datum/find(digsite, rand(10, 190)))
else if(prob(75))
archeo_turf.finds.Add(new /datum/find(digsite, rand(10, 90)))
archeo_turf.finds.Add(new /datum/find(digsite, rand(110, 190)))
else
archeo_turf.finds.Add(new /datum/find(digsite, rand(10, 50)))
archeo_turf.finds.Add(new /datum/find(digsite, rand(60, 140)))
archeo_turf.finds.Add(new /datum/find(digsite, rand(150, 190)))
//sometimes a find will be close enough to the surface to show
var/datum/find/F = archeo_turf.finds[1]
if(F.excavation_required <= F.view_range)
archeo_turf.archaeo_overlay = "overlay_archaeo[rand(1,3)]"
archeo_turf.update_icon()
//have a chance for an artifact to spawn here, but not in animal or plant digsites
if(isnull(M.artifact_find) && digsite != DIGSITE_GARDEN && digsite != DIGSITE_ANIMAL)
SSxenoarch.artifact_spawning_turfs.Add(archeo_turf)
//create artifact machinery
var/num_artifacts_spawn = rand(ARTIFACTSPAWNNUM_LOWER, ARTIFACTSPAWNNUM_UPPER)
while(SSxenoarch.artifact_spawning_turfs.len > num_artifacts_spawn)
pick_n_take(SSxenoarch.artifact_spawning_turfs)
var/list/artifacts_spawnturf_temp = SSxenoarch.artifact_spawning_turfs.Copy()
while(artifacts_spawnturf_temp.len > 0)
var/turf/simulated/mineral/artifact_turf = pop(artifacts_spawnturf_temp)
artifact_turf.artifact_find = new()
#undef XENOARCH_SPAWN_CHANCE
#undef DIGSITESIZE_LOWER
#undef DIGSITESIZE_UPPER
#undef ARTIFACTSPAWNNUM_LOWER
#undef ARTIFACTSPAWNNUM_UPPER //Replace with difficulty-based ones.
///////////////////////////////
///// Zone Population /////////
///////////////////////////////
//Overall 'prepare' proc (marks as ready)
/datum/rogue/zonemaster/proc/prepare_zone(var/delay = 0)
rm_controller.unmark_clean(src)
rm_controller.dbg("ZM(p): Preparing zone with difficulty level [rm_controller.diffstep].")
rm_controller.dbg("ZM(p): Randomizing spawns.")
randomize_spawns()
rm_controller.dbg("ZM(p): [rockspawns.len] picked.")
for(var/obj/asteroid_spawner/SP in rockspawns)
rm_controller.dbg("ZM(p): Creating asteroid for [SP.x],[SP.y],[SP.z].")
var/datum/rogue/asteroid/A = generate_asteroid()
rm_controller.dbg("ZM(p): Placing asteroid.")
place_asteroid(A,SP)
if(delay)
sleep(delay)
for(var/obj/rogue_mobspawner/SP in mobspawns)
rm_controller.dbg("ZM(p): Spawning mob at [SP.x],[SP.y],[SP.z].")
//Make sure we can spawn a spacemob here
if(!istype(get_turf(SP),/turf/space))
rm_controller.dbg("ZM(p): Turf blocking mob spawn at [SP.x],[SP.y],[SP.z].")
mobspawns -= SP
for(var/obj/rogue_mobspawner/NS in myarea.mob_spawns)
if(NS in mobspawns)
continue
if(istype(get_turf(NS),/turf/space))
SP = NS
break
if(SP)
rm_controller.dbg("ZM(p): Got a mob spawnpoint, so picking a type.")
var/mobchoice = pickweight(rm_controller.mobs["tier[rm_controller.diffstep]"])
rm_controller.dbg("ZM(p): Picked [mobchoice] to spawn.")
var/mob/living/newmob = new mobchoice(get_turf(SP))
newmob.faction = "asteroid_belt"
spawned_mobs += newmob
if(delay)
sleep(delay)
rm_controller.dbg("ZM(p): Zone generation done.")
to_world_log("RM(stats): PREP [myarea] at [world.time] with [spawned_mobs.len] mobs, [mineral_rocks.len] minrocks, total of [rockspawns.len] rockspawns, [mobspawns.len] mobspawns.") //DEBUG code for playtest stats gathering.
prepared_at = world.time
rm_controller.mark_ready(src)
return myarea
//Randomize the landmarks that are enabled
/datum/rogue/zonemaster/proc/randomize_spawns(var/chance = 50)
rm_controller.dbg("ZM(rs): Previously [rockspawns.len] rockspawns.")
rockspawns.Cut()
rm_controller.dbg("ZM(rs): Now [rockspawns.len] rockspawns.")
for(var/obj/asteroid_spawner/SP in myarea.asteroid_spawns)
if(prob(chance))
rockspawns += SP
rm_controller.dbg("ZM(rs): Picked [rockspawns.len] new rockspawns with [chance]% chance.")
rm_controller.dbg("ZM(rs): Previously [mobspawns.len] mobspawns.")
mobspawns.Cut()
rm_controller.dbg("ZM(rs): Now [mobspawns.len] mobspawns.")
for(var/obj/rogue_mobspawner/SP in myarea.mob_spawns)
if(prob(rm_controller.diffstep_chances[rm_controller.diffstep]))
mobspawns += SP
original_mobs++
rm_controller.dbg("ZM(rs): Picked [mobspawns.len] new mobspawns with [chance]% chance.")
return myarea
///////////////////////////////
///// Zone Cleaning ///////////
///////////////////////////////
/datum/rogue/zonemaster/proc/score_zone(var/bonus = 10)
rm_controller.dbg("ZM(sz): Scoring zone with area [myarea].")
scored = 1
var/tally = bonus
//Ore-bearing rocks that were mined
for(var/turf/T in mineral_rocks)
var/has_minerals = 0
for(var/atom/I in T.contents)
if(istype(I,/obj/effect/mineral))
has_minerals++
break
if(has_minerals == 0)
tally += RM_DIFF_VALUE_ORE
mineral_rocks.Cut() //For good measure, to prevent rescoring.
for(var/I = 1, I <= spawned_mobs.len, I++)
if(isnull(spawned_mobs[I]))
tally += RM_DIFF_VALUE_MOB //Mobs so annihilated they were deleted
rm_controller.dbg("ZM(sz): Scoring one mob annihilated.")
if(istype(spawned_mobs[I],/mob))
var/mob/M = spawned_mobs[I]
if(M.stat > 0) //Knocked out or dead or anything other than normal
tally += RM_DIFF_VALUE_MOB
rm_controller.dbg("ZM(sz): Scoring one mob dead.")
spawned_mobs.Cut()
original_mobs = 0
rm_controller.adjust_difficulty(tally)
rm_controller.dbg("ZM(sz): Finished scoring and adjusted by [tally].")
to_world_log("RM(stats): SCORE [myarea] for [tally].") //DEBUG code for playtest stats gathering.
return tally
//Overall 'destroy' proc (marks as unready)
/datum/rogue/zonemaster/proc/clean_zone(var/delay = 1)
rm_controller.dbg("ZM(cz): Cleaning zone with area [myarea].")
to_world_log("RM(stats): CLEAN start [myarea] at [world.time] prepared at [prepared_at].") //DEBUG code for playtest stats gathering.
rm_controller.unmark_ready(src)
//Cut these lists so qdel can dereference the things properly
mineral_rocks.Cut()
spawned_mobs.Cut()
rockspawns.Cut()
mobspawns.Cut()
var/ignored = list(
/obj/asteroid_spawner,
/obj/rogue_mobspawner,
/obj/effect/shuttle_landmark,
/obj/effect/step_trigger/teleporter/roguemine_loop/north,
/obj/effect/step_trigger/teleporter/roguemine_loop/south,
/obj/effect/step_trigger/teleporter/roguemine_loop/east,
/obj/effect/step_trigger/teleporter/roguemine_loop/west)
for(var/atom/I in myarea.contents)
if(I.type == /turf/space)
I.cut_overlays()
continue
else if(!I.simulated)
continue
else if(I.type in ignored)
continue
qdel(I)
sleep(delay)
//A deletion so nice that I give it twice
for(var/atom/I in myarea.contents)
if(I.type == /turf/space)
I.cut_overlays()
continue
else if(!I.simulated)
continue
else if(I.type in ignored)
continue
qdel(I)
sleep(delay)
//Clean up vars
scored = 0
original_mobs = 0
prepared_at = 0
to_world_log("RM(stats): CLEAN done [myarea] at [world.time].") //DEBUG code for playtest stats gathering.
rm_controller.dbg("ZM(cz): Finished cleaning up zone area [myarea].")
rm_controller.mark_clean(src)
return myarea
///////////////////////////////
///// Mysterious Mystery //////
///////////////////////////////
//Throw a meteor at a player in the zone