Skip to content

Commit

Permalink
Merge pull request #7751 from RemieRichards/ProceduralGeneration
Browse files Browse the repository at this point in the history
Procedural Map Generator System
  • Loading branch information
hornygranny committed Mar 6, 2015
2 parents d61cf2b + 9402d8c commit 68c791e
Show file tree
Hide file tree
Showing 10 changed files with 442 additions and 3 deletions.
25 changes: 24 additions & 1 deletion code/game/objects/structures/flora.dm
Expand Up @@ -206,4 +206,27 @@
/obj/structure/flora/kirbyplants/dead
name = "RD's potted plant"
desc = "A gift from the botanical staff, presented after the RD's reassignment. There's a tag on it that says \"Y'all come back now, y'hear?\"\nIt doesn't look very healthy..."
icon_state = "plant-25"
icon_state = "plant-25"


//a rock is flora according to where the icon file is
//and now these defines
/obj/structure/flora/rock
name = "rock"
desc = "a rock"
icon_state = "rock1"
icon = 'icons/obj/flora/rocks.dmi'
anchored = 1

/obj/structure/flora/rock/New()
..()
icon_state = "rock[rand(1,5)]"

/obj/structure/flora/rock/pile
name = "rocks"
desc = "some rocks"
icon_state = "rockpile1"

/obj/structure/flora/rock/pile/New()
..()
icon_state = "rockpile[rand(1,5)]"
10 changes: 9 additions & 1 deletion code/game/turfs/unsimulated/floor.dm
Expand Up @@ -17,4 +17,12 @@
icon_state = "engine"

/turf/unsimulated/floor/attack_paw(user as mob)
return src.attack_hand(user)
return src.attack_hand(user)


/turf/unsimulated/floor/grass
icon_state = "grass1"

/turf/unsimulated/floor/grass/New()
..()
icon_state = "grass[rand(1,4)]"
3 changes: 2 additions & 1 deletion code/modules/admin/admin_verbs.dm
Expand Up @@ -118,7 +118,8 @@ var/list/admin_verbs_debug = list(
/client/proc/callproc_datum,
/client/proc/SDQL2_query,
/client/proc/test_movable_UI,
/client/proc/test_snap_UI
/client/proc/test_snap_UI,
/client/proc/debugNatureMapGenerator
)
var/list/admin_verbs_possess = list(
/proc/possess,
Expand Down
123 changes: 123 additions & 0 deletions code/modules/procedural mapping/mapGenerator.dm
@@ -0,0 +1,123 @@

/datum/mapGenerator

//Map information
var/list/map = list()
var/turf/bottomLeft = null
var/turf/topRight = null

//mapGeneratorModule information
var/list/modules = list()

/datum/mapGenerator/New()
..()
initialiseModules()

//Defines the region the map represents, sets map, bottomLeft, topRight
//Returns the map
/datum/mapGenerator/proc/defineRegion(var/turf/Start, var/turf/End)
if(!checkRegion(Start, End))
return 0

if(!Start || !End)
return 0
bottomLeft = Start
topRight = End

map = block(bottomLeft,topRight)
return map


//Checks for and Rejects bad region coordinates
//Returns 1/0
/datum/mapGenerator/proc/checkRegion(var/turf/Start, var/turf/End)
. = 1

if(!Start || !End)
return 0 //Just bail

if(Start.x > world.maxx || End.x > world.maxx)
. = 0
if(Start.y > world.maxy || End.y > world.maxy)
. = 0
if(Start.z > world.maxz || End.z > world.maxz)
. = 0


//Requests the mapGeneratorModule(s) to (re)generate
/datum/mapGenerator/proc/generate()
set background = 1 //this can get beefy

syncModules()
if(!modules || !modules.len)
return
for(var/datum/mapGeneratorModule/mod in modules)
mod.generate()


//Requests the mapGeneratorModule(s) to (re)generate this one turf
/datum/mapGenerator/proc/generateOneTurf(var/turf/T)
if(!T)
return
syncModules()
if(!modules || !modules.len)
return
for(var/datum/mapGeneratorModule/mod in modules)
mod.place(T)


//Replaces all paths in the module list with actual module datums
/datum/mapGenerator/proc/initialiseModules()
for(var/path in modules)
if(ispath(path))
modules.Remove(path)
modules |= new path
syncModules()


//Sync mapGeneratorModule(s) to mapGenerator
/datum/mapGenerator/proc/syncModules()
for(var/datum/mapGeneratorModule/mod in modules)
mod.sync(src)



///////////////////////////
// HERE BE DEBUG DRAGONS //
///////////////////////////

/client/proc/debugNatureMapGenerator()
set name = "Test Nature Map Generator"
set category = "Debug"

var/datum/mapGenerator/nature/N = new()
var/startInput = input(usr,"Start turf of Map, (X;Y;Z)", "Map Gen Settings", "1;1;1") as text
var/endInput = input(usr,"End turf of Map (X;Y;Z)", "Map Gen Settings", "[world.maxx];[world.maxy];[mob ? mob.z : 1]") as text
//maxx maxy and current z so that if you fuck up, you only fuck up one entire z level instead of the entire universe
if(!startInput || !endInput)
src << "Missing Input"
return

var/list/startCoords = text2list(startInput, ";")
var/list/endCoords = text2list(endInput, ";")
if(!startCoords || !endCoords)
src << "Invalid Coords"
src << "Start Input: [startInput]"
src << "End Input: [endInput]"
return

var/turf/Start = locate(text2num(startCoords[1]),text2num(startCoords[2]),text2num(startCoords[3]))
var/turf/End = locate(text2num(endCoords[1]),text2num(endCoords[2]),text2num(endCoords[3]))
if(!Start || !End)
src << "Invalid Turfs"
src << "Start Coords: [startCoords[1]] - [startCoords[2]] - [startCoords[3]]"
src << "End Coords: [endCoords[1]] - [endCoords[2]] - [endCoords[3]]"
return

src << "Defining Region"
N.defineRegion(Start, End)
src << "Region Defined"
src << "Generating Region"
N.generate()
src << "Generated Region"

101 changes: 101 additions & 0 deletions code/modules/procedural mapping/mapGeneratorModule.dm
@@ -0,0 +1,101 @@

#define CLUSTER_CHECK_NONE 0 //No checks are done, cluster as much as possible
#define CLUSTER_CHECK_ATOMS 2 //Don't let atoms cluster, based on clusterMin and clusterMax as guides
#define CLUSTER_CHECK_TURFS 4 //Don't let turfs cluster, based on clusterMin and clusterMax as guides
#define CLUSTER_CHECK_ALL 6 //Don't let anything cluster, based on clusterMind and clusterMax as guides

/datum/mapGeneratorModule
var/datum/mapGenerator/mother = null
var/list/spawnableAtoms = list()
var/list/spawnableTurfs = list()
var/clusterMax = 5
var/clusterMin = 1
var/clusterCheckFlags = CLUSTER_CHECK_ALL


//Syncs the module up with it's mother
/datum/mapGeneratorModule/proc/sync(var/datum/mapGenerator/mum)
mother = null
if(mum)
mother = mum


//Generates it's spawnable atoms and turfs
/datum/mapGeneratorModule/proc/generate()
if(!mother)
return
var/list/map = mother.map
for(var/turf/T in map)
place(T)


//Place a spawnable atom or turf on this turf
/datum/mapGeneratorModule/proc/place(var/turf/T)
if(!T)
return 0

var/clustering = 0

//Turfs don't care whether atoms can be placed here
for(var/turfPath in spawnableTurfs)
if(clusterCheckFlags & CLUSTER_CHECK_TURFS)
if(clusterMax && clusterMin)
clustering = rand(clusterMin,clusterMax)
if(locate(/atom/movable) in range(clustering, T))
continue
if(prob(spawnableTurfs[turfPath]))
T.ChangeTurf(turfPath)

//Atoms DO care whether atoms can be placed here
if(checkPlaceAtom(T))
for(var/atomPath in spawnableAtoms)
if(clusterCheckFlags & CLUSTER_CHECK_ATOMS)
if(clusterMax && clusterMin)
clustering = rand(clusterMin,clusterMax)
if(locate(/atom/movable) in range(clustering, T))
continue
if(prob(spawnableAtoms[atomPath]))
new atomPath (T)

. = 1


//Checks and Rejects dense turfs
/datum/mapGeneratorModule/proc/checkPlaceAtom(var/turf/T)
. = 1
if(!T)
return 0
if(T.density)
. = 0
for(var/atom/A in T)
if(A.density)
. = 0
break


///////////////////////////////////////////////////////////
// PREMADE BASE TEMPLATES //
// Appropriate settings for usable types //
// Not usable types themselves, use them as parent types //
// Seriously, don't use these on their own, just parents //
///////////////////////////////////////////////////////////
//The /atom and /turf examples are just so these compile, replace those with your typepaths in your subtypes.

//Settings appropriate for a turf that covers the entire map region, eg a fill colour on a bottom layer in a graphics program.
//Should only have one of these in your mapGenerator unless you want to waste CPU
/datum/mapGeneratorModule/bottomLayer
clusterCheckFlags = CLUSTER_CHECK_NONE
spawnableAtoms = list()//Recommended: No atoms.
spawnableTurfs = list(/turf = 100)

//Settings appropriate for turfs/atoms that cover SOME of the map region, sometimes referred to as a splatter layer.
/datum/mapGeneratorModule/splatterLayer
clusterCheckFlags = CLUSTER_CHECK_ALL
spawnableAtoms = list(/atom = 30)
spawnableTurfs = list(/turf = 30)

//Settings appropriate for turfs/atoms that cover a lot of the map region, eg a dense forest.
/datum/mapGeneratorModule/denseLayer
clusterCheckFlags = CLUSTER_CHECK_NONE
spawnableAtoms = list(/atom = 75)
spawnableTurfs = list(/turf = 75)
37 changes: 37 additions & 0 deletions code/modules/procedural mapping/mapGeneratorModules/nature.dm
@@ -0,0 +1,37 @@

//Contents exist primarily for the nature generator test type.


//Pine Trees
/datum/mapGeneratorModule/pineTrees
spawnableAtoms = list(/obj/structure/flora/tree/pine = 30)

//Dead Trees
/datum/mapGeneratorModule/deadTrees
spawnableAtoms = list(/obj/structure/flora/tree/dead = 10)

//Random assortment of bushes
/datum/mapGeneratorModule/randBushes
spawnableAtoms = list()

/datum/mapGeneratorModule/randBushes/New()
..()
spawnableAtoms = typesof(/obj/structure/flora/ausbushes)
for(var/i in spawnableAtoms)
spawnableAtoms[i] = 20


//Random assortment of rocks and rockpiles
/datum/mapGeneratorModule/randRocks
spawnableAtoms = list(/obj/structure/flora/rock = 40, /obj/structure/flora/rock/pile = 20)


//Grass turfs
/datum/mapGeneratorModule/bottomLayer/grassTurfs
spawnableTurfs = list(/turf/unsimulated/floor/grass = 100)


//Grass tufts with a high spawn chance
/datum/mapGeneratorModule/denseLayer/grassTufts
spawnableTurfs = list()
spawnableAtoms = list(/obj/structure/flora/ausbushes/grassybush = 75)

0 comments on commit 68c791e

Please sign in to comment.