mirror of
https://github.com/Yawn-Wider/YWPolarisVore.git
synced 2026-08-26 14:36:50 +01:00
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.
434 lines
14 KiB
Plaintext
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
|