Files
C.L 610a2a7ec5 Buffs artifact spawns in the map. Lowers rogueminer.
Artifacts used to be confined to about one Z level: The mining asteroid.

Now with there being a LOT more Z levels, this number really hasn't changed and it means that artifacts are harder to find, being spread across many more Z levels.

This could mean that if you had bad RNG, you would have 6 artifacts spread across every Z level, meaning xenoarch would very quickly exhaust the possible artifacts they had nearby and after exploring every Z level that spawns artifacts, they'd only have 6 artifacts.

This doubles that number so now there is 12 artifacts that spawn by default, up to 24.

Meanwhile, roguemining would spawn 6 to 12 artifacts per mining area visited. This meant each rogue mining area had as many artifacts as the entire universe had. This was obviously unintended, so this fixed that by lowering it to 0 or 1 artifact per roguemining Z level.
2022-08-31 21:58:33 -04: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