From e7983f65d687c6efc04774ca4dda2dad0278f731 Mon Sep 17 00:00:00 2001 From: Griefed <griefed@griefed.de> Date: Tue, 28 Sep 2021 20:01:46 +0200 Subject: [PATCH] feat: Generate random maps for a given number-seed. --- package-lock.json | 22 ++++++ package.json | 2 + src/pages/Index.vue | 69 ++++++++++++------ src/store/index.js | 170 +++++++++++++++++++++++++++++++++++--------- 4 files changed, 209 insertions(+), 54 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3901ee9..b00ff74 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,8 @@ "dependencies": { "@quasar/extras": "^1.11.1", "core-js": "^3.18.1", + "mersenne-twister": "^1.1.0", + "noise-map": "^1.1.0", "quasar": "^2.1.0" }, "devDependencies": { @@ -8480,6 +8482,11 @@ "node": ">= 8" } }, + "node_modules/mersenne-twister": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mersenne-twister/-/mersenne-twister-1.1.0.tgz", + "integrity": "sha1-+RZhjuQ9cXnvz2Qb7EUx65Zwl4o=" + }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -8763,6 +8770,11 @@ "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", "dev": true }, + "node_modules/noise-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/noise-map/-/noise-map-1.1.0.tgz", + "integrity": "sha512-eF6DGJuJF2Hj56u7Tjkeh6z/JtQQ76tBPjt8/N2uoL0gJzUMfEWavzW9v4lJTlAxNOnYsTGCkT7CoXNe/TcWHQ==" + }, "node_modules/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -19870,6 +19882,11 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "mersenne-twister": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/mersenne-twister/-/mersenne-twister-1.1.0.tgz", + "integrity": "sha1-+RZhjuQ9cXnvz2Qb7EUx65Zwl4o=" + }, "methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -20076,6 +20093,11 @@ "integrity": "sha512-Qe5OUajvqrqDSy6wrWFmMwfJ0jVgwiw4T3KqmbTcZ62qW0gQkheXYhcFM1+lOVcGUoRxcEcfyvFMAnDgaF1VWw==", "dev": true }, + "noise-map": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/noise-map/-/noise-map-1.1.0.tgz", + "integrity": "sha512-eF6DGJuJF2Hj56u7Tjkeh6z/JtQQ76tBPjt8/N2uoL0gJzUMfEWavzW9v4lJTlAxNOnYsTGCkT7CoXNe/TcWHQ==" + }, "normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", diff --git a/package.json b/package.json index f76aaf6..76513fa 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,8 @@ "dependencies": { "@quasar/extras": "^1.11.1", "core-js": "^3.18.1", + "mersenne-twister": "^1.1.0", + "noise-map": "^1.1.0", "quasar": "^2.1.0" }, "devDependencies": { diff --git a/src/pages/Index.vue b/src/pages/Index.vue index 21e72f6..48f5d67 100644 --- a/src/pages/Index.vue +++ b/src/pages/Index.vue @@ -1,4 +1,5 @@ <template> + <span v-if="store.state.lttMap"> <span v-for="(xRow, index) in store.state.lttMap" :key="index"> <div class="row no-wrap" @@ -19,11 +20,12 @@ </div> </span> </span> + <span v-else> <div class="row no-wrap q-pa-md absolute-center"> <q-card> <q-card-section> - <div class="column" style="width: 600px;"> + <div class="column" style="width: 600px;"> <div class="text-h6 q-mb-md text-black">New Map Dimensions</div> <q-item> <q-item-section avatar> @@ -42,31 +44,49 @@ <q-slider v-model="store.state.mapSizeY" :min="1" :max="101" label color="secondary" :step="1" label-always/> </q-item-section> </q-item> - <q-btn class="q-mr-xs" color="secondary" label="Generate New Map" @click='createMap()'> + + <q-btn class="q-mr-xs" color="secondary" label="Generate Empty Map" @click='createMap()'> <q-tooltip :disable="$q.platform.is.mobile"> - Generate new map with size set above + Generate empty map with size set above </q-tooltip> </q-btn> - </div> - </q-card-section> - - <q-card-section> - <div class="column"> - <div class="text-h6 q-mb-md text-black">Load Existing Map</div> - <q-input - v-model="mapString" - filled - placeholder="Paste Map Data" - type="textarea" - input-class="pastCodeArea" - ></q-input> - <q-btn class="q-mr-xs" color="secondary" label="Load Map From Filedata" @click='loadMapData()'> + + </div> + </q-card-section> + + <q-card-section> + <div class="column" style="width: 600px;"> + <q-input color="black" filled v-model="store.state.seed" label="Seed" type="number" maxlength="15"> + <template v-if="store.state.seed" v-slot:append> + <q-icon name="cancel" @click.stop="seed = null" class="cursor-pointer" /> + </template> + </q-input> + <q-btn class="q-mr-xs" color="secondary" label="Generate Random Map" @click='createRandomMap()'> <q-tooltip :disable="$q.platform.is.mobile"> - Load Map From Data + Generate random map with size set above </q-tooltip> - </q-btn> - </div> + </q-btn> + </div> + </q-card-section> + + <q-card-section> + <div class="column"> + <div class="text-h6 q-mb-md text-black">Load Existing Map</div> + <q-input + v-model="mapString" + filled + placeholder="Paste Map Data" + type="textarea" + input-class="pastCodeArea" + ></q-input> + <q-btn class="q-mr-xs" color="secondary" label="Load Map From Filedata" @click='loadMapData()'> + <q-tooltip :disable="$q.platform.is.mobile"> + Load Map From Data + </q-tooltip> + </q-btn> + </div> </q-card-section> + </q-card> </div> </span> @@ -82,20 +102,25 @@ export default defineComponent({ const store = inject('store'); - var mapString = ref('') + var mapString = ref(''); const loadMapData = function() { store.methods.loadMap(JSON.parse(mapString.value)); }; - const createMap= function(){ + const createMap = function() { store.methods.generateMap(); }; + const createRandomMap = function() { + store.methods.generateRandomMap(); + }; + return { mapString, store, createMap, + createRandomMap, loadMapData } }, diff --git a/src/store/index.js b/src/store/index.js index 28758a8..1fd3f5b 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -1,4 +1,8 @@ -import {reactive} from 'vue'; +import {reactive, ref} from 'vue'; +import NoiseMap from 'noise-map'; +import MersenneTwister from 'mersenne-twister'; + +const seedGenerator = new MersenneTwister(); const state = reactive({ maxX: 0, @@ -14,10 +18,43 @@ const state = reactive({ mapID: 'e1-m.lttmm', mapName: '', lttMap: null, -}) + seed: ref(seedGenerator.random_int31()) +}); + +const reticulating = ["Adding","Hidden","Agendas","Adjusting","Bell","Curves","Aesthesizing","Industrial","Areas","Aligning","Covariance","Matrices", + "Applying","Feng","Shui","Shaders","Applying","Theatre","Soda","Layer","Asserting","Packed","Exemplars","Attempting","to","Lock","Back-Buffer", + "Binding","Sapling","Root","System","Breeding","Fauna","Building","Data","Trees","Bureacritizing","Bureaucracies","Calculating","Inverse","Probability","Matrices", + "Calculating","Llama","Expectoration","Trajectory","Calibrating","Blue","Skies","Charging","Ozone","Layer","Coalescing","Cloud","Formations", + "Cohorting","Exemplars","Collecting","Meteor","Particles","Compounding","Inert","Tessellations","Compressing","Fish","Files", + "Computing","Optimal","Bin","Packing","Concatenating","Sub-Contractors","Containing","Existential","Buffer","Debarking","Ark","Ramp", + "Debunching","Unionized","Commercial","Services","Deciding","What","Message","to","Display","Next","Decomposing","Singular","Values", + "Decrementing","Tectonic","Plates","Deleting","Ferry","Routes","Depixelating","Inner","Mountain","Surface","Back","Faces","Depositing","Slush","Funds", + "Destabilizing","Economic","Indicators","Determining","Width","of","Blast","Fronts","Deunionizing","Bulldozers","Dicing","Models", + "Diluting","Livestock","Nutrition","Variables","Downloading","Satellite","Terrain","Data","Exposing","Flash","Variables","to","Streak","System", + "Extracting","Resources","Factoring","Pay","Scale","Fixing","Election","Outcome","Matrix","Flood-Filling","Ground","Water","Flushing","Pipe","Network", + "Gathering","Particle","Sources","Generating","Jobs","Gesticulating","Mimes","Graphing","Whale","Migration","Hiding","Willio","Webnet","Mask", + "Implementing","Impeachment","Routine","Increasing","Accuracy","of","RCI","Simulators","Increasing","Magmafacation","Initializing","My","Sim","Tracking","Mechanism", + "Initializing","Rhinoceros","Breeding","Timetable","Initializing","Robotic","Click-Path","AI","Inserting","Sublimated","Messages", + "Integrating","Curves","Integrating","Illumination","Form","Factors","Integrating","Population","Graphs","Iterating","Cellular","Automata", + "Lecturing","Errant","Subsystems","Mixing","Genetic","Pool","Modeling","Object","Components","Mopping","Occupant","Leaks","Normalizing","Power", + "Obfuscating","Quigley","Matrix","Overconstraining","Dirty","Industry","Calculations","Partitioning","City","Grid","Singularities", + "Perturbing","Matrices","Pixalating","Nude","Patch","Polishing","Water","Highlights","Populating","Lot","Templates","Preparing","Sprites","for","Random","Walks", + "Prioritizing","Landmarks","Projecting","Law","Enforcement","Pastry","Intake","Realigning","Alternate","Time","Frames","Reconfiguring","User","Mental","Processes", + "Relaxing","Splines","Removing","Road","Network","Speed","Bumps","Removing","Texture","Gradients","Removing","Vehicle","Avoidance","Behavior", + "Resolving","GUID","Conflict","Retracting","Phong","Shader","Retrieving","from","Back","Store","Reverse","Engineering","Image","Consultant", + "Routing","Neural","Network","Infanstructure","Scattering","Rhino","Food","Sources","Scrubbing","Terrain","Searching","for","Llamas", + "Seeding","Architecture","Simulation","Parameters","Sequencing","Particles","Setting","Advisor","Moods","Setting","Inner","Deity","Indicators", + "Setting","Universal","Physical","Constants","Sonically","Enhancing","Occupant-Free","Timber","Speculating","Stock","Market","Indices", + "Splatting","Transforms","Stratifying","Ground","Layers","Sub-Sampling","Water","Data","Synthesizing","Gravity","Synthesizing","Wavelets", + "Time-Compressing","Simulator","Clock","Unable","to","Reveal","Current","Activity","Weathering","Buildings","Zeroing","Crime","Network", + "Reticulating","Splines"]; const methods = { + /** + * Set values back to default for the generation of a new map. + * @author Trungel + */ reset() { state.mapSizeX = 1; state.mapSizeY = 1; @@ -28,8 +65,14 @@ const methods = { state.mapID = 'e1-m.lttmm'; state.mapName = ''; state.lttMap = null; + state.seed = ref(seedGenerator.random_int31()); }, + /** + * Get all information for the map and export it as JSON. + * @author Trungel + * @returns {{initialViewRadiusX: (number|*), tileSet, initialVieCenterX: (number|*), initialViewRadiusY: (number|*), mapID: (string|*), mapName: (string|*), initialViewCenterY: (number|*)}} The JSON containing the map. + */ exportMap() { return{ mapID: state.mapID, @@ -41,6 +84,11 @@ const methods = { tileSet: state.lttMap.flat()} }, + /** + * Load an existing map from JSON entered by a user. + * @author Trungel + * @param mapData The JSON containing the map to load. + */ loadMap(mapData) { let tileSetData = this.getMultDimArrayFromFlattendArray(mapData.tileSet); @@ -59,6 +107,12 @@ const methods = { state.centerY = mapData.initialViewCenterY; }, + /** + * Parse the tileSet of the passed map and retrieve information about said map. + * @author Trungel + * @param arr The tileSet of the map to load. + * @returns {{mapSizeY: number, minY, mapSizeX: number, minX, maxY, maxX, map: *[]}} JSON containing information about our map. + */ getMultDimArrayFromFlattendArray(arr){ let maxX,minX,maxY,minY, mapSizeX, mapSizeY; let sortArr = arr.sort((a,b)=>{ @@ -87,44 +141,31 @@ const methods = { return {mapSizeX, mapSizeY,maxX, minX,maxY, minY, map}; }, - + /** + * Generate a random name for the new map. + * @author Griefed + * @returns {string} Returns a combination of three random words from {@link reticulating}. + */ getMapName() { - const reticulating = ["Adding","Hidden","Agendas","Adjusting","Bell","Curves","Aesthesizing","Industrial","Areas","Aligning","Covariance","Matrices", - "Applying","Feng","Shui","Shaders","Applying","Theatre","Soda","Layer","Asserting","Packed","Exemplars","Attempting","to","Lock","Back-Buffer", - "Binding","Sapling","Root","System","Breeding","Fauna","Building","Data","Trees","Bureacritizing","Bureaucracies","Calculating","Inverse","Probability","Matrices", - "Calculating","Llama","Expectoration","Trajectory","Calibrating","Blue","Skies","Charging","Ozone","Layer","Coalescing","Cloud","Formations", - "Cohorting","Exemplars","Collecting","Meteor","Particles","Compounding","Inert","Tessellations","Compressing","Fish","Files", - "Computing","Optimal","Bin","Packing","Concatenating","Sub-Contractors","Containing","Existential","Buffer","Debarking","Ark","Ramp", - "Debunching","Unionized","Commercial","Services","Deciding","What","Message","to","Display","Next","Decomposing","Singular","Values", - "Decrementing","Tectonic","Plates","Deleting","Ferry","Routes","Depixelating","Inner","Mountain","Surface","Back","Faces","Depositing","Slush","Funds", - "Destabilizing","Economic","Indicators","Determining","Width","of","Blast","Fronts","Deunionizing","Bulldozers","Dicing","Models", - "Diluting","Livestock","Nutrition","Variables","Downloading","Satellite","Terrain","Data","Exposing","Flash","Variables","to","Streak","System", - "Extracting","Resources","Factoring","Pay","Scale","Fixing","Election","Outcome","Matrix","Flood-Filling","Ground","Water","Flushing","Pipe","Network", - "Gathering","Particle","Sources","Generating","Jobs","Gesticulating","Mimes","Graphing","Whale","Migration","Hiding","Willio","Webnet","Mask", - "Implementing","Impeachment","Routine","Increasing","Accuracy","of","RCI","Simulators","Increasing","Magmafacation","Initializing","My","Sim","Tracking","Mechanism", - "Initializing","Rhinoceros","Breeding","Timetable","Initializing","Robotic","Click-Path","AI","Inserting","Sublimated","Messages", - "Integrating","Curves","Integrating","Illumination","Form","Factors","Integrating","Population","Graphs","Iterating","Cellular","Automata", - "Lecturing","Errant","Subsystems","Mixing","Genetic","Pool","Modeling","Object","Components","Mopping","Occupant","Leaks","Normalizing","Power", - "Obfuscating","Quigley","Matrix","Overconstraining","Dirty","Industry","Calculations","Partitioning","City","Grid","Singularities", - "Perturbing","Matrices","Pixalating","Nude","Patch","Polishing","Water","Highlights","Populating","Lot","Templates","Preparing","Sprites","for","Random","Walks", - "Prioritizing","Landmarks","Projecting","Law","Enforcement","Pastry","Intake","Realigning","Alternate","Time","Frames","Reconfiguring","User","Mental","Processes", - "Relaxing","Splines","Removing","Road","Network","Speed","Bumps","Removing","Texture","Gradients","Removing","Vehicle","Avoidance","Behavior", - "Resolving","GUID","Conflict","Retracting","Phong","Shader","Retrieving","from","Back","Store","Reverse","Engineering","Image","Consultant", - "Routing","Neural","Network","Infanstructure","Scattering","Rhino","Food","Sources","Scrubbing","Terrain","Searching","for","Llamas", - "Seeding","Architecture","Simulation","Parameters","Sequencing","Particles","Setting","Advisor","Moods","Setting","Inner","Deity","Indicators", - "Setting","Universal","Physical","Constants","Sonically","Enhancing","Occupant-Free","Timber","Speculating","Stock","Market","Indices", - "Splatting","Transforms","Stratifying","Ground","Layers","Sub-Sampling","Water","Data","Synthesizing","Gravity","Synthesizing","Wavelets", - "Time-Compressing","Simulator","Clock","Unable","to","Reveal","Current","Activity","Weathering","Buildings","Zeroing","Crime","Network", - "Reticulating","Splines"]; return reticulating[ Math.floor( Math.random() * reticulating.length) - 1] + " " + reticulating[Math.floor(Math.random() * reticulating.length) - 1] + " " + reticulating[Math.floor(Math.random() * reticulating.length) - 1]; }, + /** + * Return the mapID for the new map. + * @author Griefed + * @returns {string} + */ getMapId() { return 'e1-m.lttmm'; }, + /** + * Generate a new map with a given size, mapID and mapName. + * @author Trungel + * @author Griefed + */ generateMap() { let defaultTile = "grass"; let map = []; @@ -152,11 +193,76 @@ const methods = { } state.lttMap = map; state.mapName = this.getMapName(); + }, + + /** + * Generate a random map with the given size, mapID and mapName based on a seed. + * @author Griefed + */ + generateRandomMap() { + let defaultTile = "grass"; + let map = []; + + const mapGenerator = new NoiseMap.MapGenerator(state.seed); + + state.minX = -Math.floor(state.mapSizeX/2); + if(state.mapSizeX%2){ + state.maxX = Math.floor(state.mapSizeX/2); + }else{ + state.maxX = Math.floor(state.mapSizeX/2)+1; + } + + state.minY = -Math.floor(state.mapSizeY/2); + if(state.mapSizeY%2){ + state.maxY = Math.floor(state.mapSizeY/2); + }else{ + state.maxY = Math.floor(state.mapSizeY/2)+1; + } + const heightmap = mapGenerator.createMap(state.mapSizeX+2, state.mapSizeY+2, {type: 'perlit'}); + heightmap.stepValues(5); + + for(let m = state.maxY; m >= state.minY; m--){ + let xArr = []; + for(let i = state.minX; i <= state.maxX; i++){ + xArr.push({ x: i, y: m, z: 0, typeId: this.getTileTypeFromHeightmap(heightmap, i+(state.mapSizeX/2), m+(state.mapSizeY/2)) }); + } + map.push(xArr); + } + state.lttMap = map; + state.mapName = this.getMapName(); + }, + + /** + * + * @author Griefed + * @param heightmap A heightmap object generated by {@link NoiseMap} + * @param x The x coordinate of which to get the value + * @param y The y coordinate of which to get the value + * @returns {string} The value at position x and y. + */ + getTileTypeFromHeightmap(heightmap, x, y) { + let value = Math.floor(heightmap.get(x, y)*100); + switch(value) { + case 100: + return "mountain"; + case 80: + return "ice"; + case 60: + return "barren"; + case 40: + return "desert"; + case 20: + return "grass"; + case 0: + return "water"; + default: + return "grass"; + } } -} +}; export default { state, methods -} +}; -- GitLab