Initial
commit
4e3fda1732
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
["env", { "modules": false }],
|
||||||
|
"stage-3"
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
@ -0,0 +1,12 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
@ -0,0 +1,18 @@
|
|||||||
|
# building-game
|
||||||
|
|
||||||
|
> A blocky, side-scrolling, building and exploration game
|
||||||
|
|
||||||
|
## Build Setup
|
||||||
|
|
||||||
|
``` bash
|
||||||
|
# install dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# serve with hot reload at localhost:8080
|
||||||
|
npm run dev
|
||||||
|
|
||||||
|
# build for production with minification
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
For detailed explanation on how things work, consult the [docs for vue-loader](http://vuejs.github.io/vue-loader).
|
@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>building-game</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script src="/dist/build.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
"name": "building-game",
|
||||||
|
"description": "A blocky, side-scrolling, building and exploration game",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "koehr <n@koehr.in>",
|
||||||
|
"license": "MIT",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
|
||||||
|
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"seedrandom": "^2.4.3",
|
||||||
|
"vue": "^2.5.11"
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not ie <= 10"
|
||||||
|
],
|
||||||
|
"devDependencies": {
|
||||||
|
"babel-core": "^6.26.0",
|
||||||
|
"babel-loader": "^7.1.2",
|
||||||
|
"babel-preset-env": "^1.6.0",
|
||||||
|
"babel-preset-stage-3": "^6.24.1",
|
||||||
|
"cross-env": "^5.0.5",
|
||||||
|
"css-loader": "^0.28.7",
|
||||||
|
"fast-simplex-noise": "^3.2.0",
|
||||||
|
"file-loader": "^1.1.4",
|
||||||
|
"vue-loader": "^13.0.5",
|
||||||
|
"vue-template-compiler": "^2.4.4",
|
||||||
|
"webpack": "^3.6.0",
|
||||||
|
"webpack-dev-server": "^2.9.1"
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
<template>
|
||||||
|
<div id="building-game">
|
||||||
|
<Field />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Field from './Field'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'building-game',
|
||||||
|
components: { Field },
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html,body,#app {
|
||||||
|
display: block;
|
||||||
|
width: 100vw;
|
||||||
|
background: black;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div class="block" :class="type"></div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'block',
|
||||||
|
props: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.block {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: #6DA956;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.block.air { background-color: #33A; }
|
||||||
|
.block.grass { background-color: #33A; height: 28px; border-bottom: 2px solid #0A0; }
|
||||||
|
.block.soil { background-color: #543; }
|
||||||
|
.block.gravel { background-color: #665; }
|
||||||
|
.block.stone { background-color: #555; }
|
||||||
|
.block.bedrock { background-color: #444; }
|
||||||
|
.block:hover {
|
||||||
|
border-color: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<div class="field">
|
||||||
|
<input v-keep-focussed type="text"
|
||||||
|
@keydown.down="goDown($event)"
|
||||||
|
@keydown.up="goUp($event)"
|
||||||
|
@keydown.right="x = x + 1"
|
||||||
|
@keydown.left="x = x > 0 ? x - 1 : 0"
|
||||||
|
/>
|
||||||
|
<template v-for="row in rows">
|
||||||
|
<div v-for="block in row" class="block" :class="block.type" />
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Level from './lib/level-generator'
|
||||||
|
|
||||||
|
const WIDTH = 32
|
||||||
|
const HEIGHT = 32
|
||||||
|
const level = new Level(WIDTH, HEIGHT)
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'field',
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
x: 0,
|
||||||
|
y: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rows () {
|
||||||
|
return level.grid(this.x, this.y)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
goDown (ev) {
|
||||||
|
if (ev.shiftKey) this.y += 32
|
||||||
|
else this.y++
|
||||||
|
},
|
||||||
|
goUp (ev) {
|
||||||
|
if (ev.shiftKey) this.y -= 32
|
||||||
|
else this.y--
|
||||||
|
this.y = Math.max(0, this.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.field {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
width: 1024px;
|
||||||
|
margin: auto;
|
||||||
|
margin-top: calc(100vh - 1056px);
|
||||||
|
border: 16px solid #222;
|
||||||
|
}
|
||||||
|
.field > input {
|
||||||
|
position: absolute;
|
||||||
|
opacity: 0;
|
||||||
|
display: block;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
}
|
||||||
|
.block {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: #6DA956;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.block.air { background-color: #56F; }
|
||||||
|
.block.grass { background-color: #56F; height: 28px; border-bottom: 2px solid #0A0; }
|
||||||
|
.block.leaves { background-color: #383; }
|
||||||
|
.block.wood { background-color: #876; }
|
||||||
|
.block.soil { background-color: #543; }
|
||||||
|
.block.gravel { background-color: #665; }
|
||||||
|
.block.stone { background-color: #555; }
|
||||||
|
.block.bedrock { background-color: #333; }
|
||||||
|
.block.cave { background-color: #000; }
|
||||||
|
.block:hover {
|
||||||
|
border-color: rgba(255,255,255,0.2);
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,57 @@
|
|||||||
|
import SeedRng from 'seedrandom'
|
||||||
|
import FastSimplexNoise from 'fast-simplex-noise'
|
||||||
|
import * as T from './block-types'
|
||||||
|
import * as P from './block-probabilities'
|
||||||
|
import * as L from './block-levels'
|
||||||
|
|
||||||
|
export default class BlockGen {
|
||||||
|
constructor (seed = 'so freakin random') {
|
||||||
|
const simplex = new FastSimplexNoise({ random: SeedRng(seed) })
|
||||||
|
this.rand = (x, y) => 0.5 + 0.5 * simplex.raw2D(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
block (level, column, above, before) {
|
||||||
|
if (level < L.PEAK) return this.air()
|
||||||
|
|
||||||
|
const r = Math.abs(this.rand(level, column))
|
||||||
|
if (level < L.GROUND) return this.tree(r, above)
|
||||||
|
if (level < L.ROCK) return this.ground(r, above)
|
||||||
|
if (level < L.UNDERGROUND) return this.rock(r)
|
||||||
|
return this.underground(r, above, before, level - L.UNDERGROUND)
|
||||||
|
}
|
||||||
|
|
||||||
|
// always returns air
|
||||||
|
air () {
|
||||||
|
return T.AIR
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns mostly air, but sometimes starts a tree
|
||||||
|
tree (r, above) {
|
||||||
|
const peak = above === T.AIR && r < P.TREE
|
||||||
|
if (peak || above === T.WOOD) return T.WOOD
|
||||||
|
return T.AIR
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns mostly soil and grass, sometimes gravel and sometimes air
|
||||||
|
ground (r, above) {
|
||||||
|
if (above === T.AIR && r < P.SOIL_HOLE) return T.AIR
|
||||||
|
if (above === T.AIR) return T.GRASS
|
||||||
|
if (above === T.WOOD) return T.SOIL
|
||||||
|
return r < P.SOIL_GRAVEL ? T.GRAVEL : T.SOIL
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns mostly stones, sometimes gravel
|
||||||
|
rock (r) {
|
||||||
|
return r < P.ROCK_GRAVEL ? T.GRAVEL : T.STONE
|
||||||
|
}
|
||||||
|
|
||||||
|
// return mostly bedrock, sometimes caves, depending on the level
|
||||||
|
underground (r, above, before, level) {
|
||||||
|
if (above === T.STONE || above === T.GRAVEL) return T.BEDROCK
|
||||||
|
const a = P.CAVE / P.CAVE_MAX**2
|
||||||
|
const p = Math.min(P.CAVE, a * level**2)
|
||||||
|
|
||||||
|
if (r < p) return T.CAVE
|
||||||
|
return T.BEDROCK
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
export const PEAK = 24
|
||||||
|
export const GROUND = 28
|
||||||
|
export const ROCK = 32
|
||||||
|
export const UNDERGROUND = 48
|
@ -0,0 +1,6 @@
|
|||||||
|
export const TREE = 0.1
|
||||||
|
export const SOIL_HOLE = 0.3
|
||||||
|
export const SOIL_GRAVEL = 0.2
|
||||||
|
export const ROCK_GRAVEL = 0.1
|
||||||
|
export const CAVE = 0.5
|
||||||
|
export const CAVE_MAX = 250
|
@ -0,0 +1,9 @@
|
|||||||
|
export const AIR = {type: 'air', hp: 0, damage: 0}
|
||||||
|
export const GRASS = {type: 'grass', hp: 1, damage: 0}
|
||||||
|
export const LEAVES = {type: 'leaves', hp: 1, damage: 0}
|
||||||
|
export const WOOD = {type: 'wood', hp: 5, damage: 0}
|
||||||
|
export const SOIL = {type: 'soil', hp: 2, damage: 0}
|
||||||
|
export const GRAVEL = {type: 'gravel', hp: 5, damage: 0}
|
||||||
|
export const STONE = {type: 'stone', hp: 10, damage: 0}
|
||||||
|
export const BEDROCK = {type: 'bedrock', hp: 25, damage: 0}
|
||||||
|
export const CAVE = {type: 'cave', hp: 0, damage: 0}
|
@ -0,0 +1,55 @@
|
|||||||
|
import * as T from './block-types'
|
||||||
|
import * as L from './block-levels'
|
||||||
|
import BlockGen from './block-generator'
|
||||||
|
|
||||||
|
export default class Level {
|
||||||
|
constructor (width, height) {
|
||||||
|
this._x = 0
|
||||||
|
this._y = 0
|
||||||
|
this._w = width
|
||||||
|
this._h = height
|
||||||
|
this._grid = new Array(height)
|
||||||
|
this.blockGen = new BlockGen('super random seed')
|
||||||
|
}
|
||||||
|
|
||||||
|
grid (x, y) {
|
||||||
|
this._x = x
|
||||||
|
this._y = y
|
||||||
|
this.generate()
|
||||||
|
return this._grid
|
||||||
|
}
|
||||||
|
|
||||||
|
generate () {
|
||||||
|
// TODO: caching
|
||||||
|
for (let i = 0; i < this._h; i++) {
|
||||||
|
this._grid[i] = this._row(i + this._y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_row (level = 0) {
|
||||||
|
const row = Array(this._w)
|
||||||
|
const previousRow = this._grid[level - 1] || Array()
|
||||||
|
|
||||||
|
// first step: generate a row for the given level
|
||||||
|
for (let i = 0; i < row.length; i++) {
|
||||||
|
const above = previousRow[i]
|
||||||
|
row[i] = this.blockGen.block(level, i, above, row[i - 1])
|
||||||
|
}
|
||||||
|
|
||||||
|
// second step: add extras like tree leaves
|
||||||
|
if (level < L.GROUND && level > L.PEAK) {
|
||||||
|
for (let i = 0; i < row.length; i++) {
|
||||||
|
const above = previousRow[i]
|
||||||
|
const block = row[i]
|
||||||
|
|
||||||
|
if (block === T.WOOD && above === T.AIR) {
|
||||||
|
if (row[i - 1] === T.AIR) row[i - 1] = T.LEAVES
|
||||||
|
if (row[i + 1] === T.AIR) row[i + 1] = T.LEAVES
|
||||||
|
previousRow[i] = T.LEAVES
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return row
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
import Vue from 'vue'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
Vue.directive('keep-focussed', {
|
||||||
|
inserted (el, binding) {
|
||||||
|
el.focus()
|
||||||
|
el.addEventListener('blur', () => el.focus())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
new Vue({
|
||||||
|
el: '#app',
|
||||||
|
render: h => h(App)
|
||||||
|
})
|
@ -0,0 +1,47 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<title>Simplex noise</title>
|
||||||
|
<style>
|
||||||
|
body, html { width: 100%; height: 100%; padding: 0; margin: 0; overflow: hidden; background: black; }
|
||||||
|
canvas { display: block; width: 1024px; height: 768px; margin: calc(50vh - 768px / 2) auto 0; border: 2px solid #333; }
|
||||||
|
</style>
|
||||||
|
<canvas></canvas>
|
||||||
|
<script src='simplex.js?8'></script>
|
||||||
|
<script>
|
||||||
|
var canvas = document.getElementsByTagName('canvas')[0];
|
||||||
|
canvas.width = 1024;
|
||||||
|
canvas.height = 768;
|
||||||
|
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
|
||||||
|
var image = ctx.createImageData(canvas.width, canvas.height);
|
||||||
|
var data = image.data;
|
||||||
|
|
||||||
|
var simplex = new SimplexNoise
|
||||||
|
|
||||||
|
function paint(data, w, h, steps = 100, threshold = 0, inverse = false) {
|
||||||
|
for (var x = 0; x < w; x++) {
|
||||||
|
for (var y = 0; y < h; y++) {
|
||||||
|
var value = simplex.noise(x / steps, y / steps);
|
||||||
|
value = 128 - 128 * value;
|
||||||
|
// value = Math.abs(256 * value);
|
||||||
|
|
||||||
|
var cell = (x + y * w) * 4;
|
||||||
|
if (threshold) value = value < threshold ? 0 : 255
|
||||||
|
if (inverse) value = 255 - value
|
||||||
|
data[cell] = value * x / y; // red
|
||||||
|
data[cell + 1] = value * y / x; // green
|
||||||
|
data[cell + 2] = 0; // blue
|
||||||
|
// data[cell] += Math.max(0, (25 - value) * 8);
|
||||||
|
data[cell + 3] = 255; // alpha.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.time('simplex canvas')
|
||||||
|
paint(data, 1024, 768, 100)
|
||||||
|
console.timeEnd('simplex canvas')
|
||||||
|
|
||||||
|
ctx.fillColor = 'black';
|
||||||
|
ctx.fillRect(0, 0, 100, 100);
|
||||||
|
ctx.putImageData(image, 0, 0);
|
||||||
|
</script>
|
@ -0,0 +1,78 @@
|
|||||||
|
var path = require('path')
|
||||||
|
var webpack = require('webpack')
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
entry: './src/main.js',
|
||||||
|
output: {
|
||||||
|
path: path.resolve(__dirname, './dist'),
|
||||||
|
publicPath: '/dist/',
|
||||||
|
filename: 'build.js'
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.css$/,
|
||||||
|
use: [
|
||||||
|
'vue-style-loader',
|
||||||
|
'css-loader'
|
||||||
|
],
|
||||||
|
}, {
|
||||||
|
test: /\.vue$/,
|
||||||
|
loader: 'vue-loader',
|
||||||
|
options: {
|
||||||
|
loaders: {
|
||||||
|
}
|
||||||
|
// other vue-loader options go here
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
loader: 'babel-loader',
|
||||||
|
exclude: /node_modules/
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.(png|jpg|gif|svg)$/,
|
||||||
|
loader: 'file-loader',
|
||||||
|
options: {
|
||||||
|
name: '[name].[ext]?[hash]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'vue$': 'vue/dist/vue.esm.js'
|
||||||
|
},
|
||||||
|
extensions: ['*', '.js', '.vue', '.json']
|
||||||
|
},
|
||||||
|
devServer: {
|
||||||
|
historyApiFallback: true,
|
||||||
|
noInfo: true,
|
||||||
|
overlay: true
|
||||||
|
},
|
||||||
|
performance: {
|
||||||
|
hints: false
|
||||||
|
},
|
||||||
|
devtool: '#eval-source-map'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === 'production') {
|
||||||
|
module.exports.devtool = '#source-map'
|
||||||
|
// http://vue-loader.vuejs.org/en/workflow/production.html
|
||||||
|
module.exports.plugins = (module.exports.plugins || []).concat([
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
'process.env': {
|
||||||
|
NODE_ENV: '"production"'
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.optimize.UglifyJsPlugin({
|
||||||
|
sourceMap: true,
|
||||||
|
compress: {
|
||||||
|
warnings: false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
new webpack.LoaderOptionsPlugin({
|
||||||
|
minimize: true
|
||||||
|
})
|
||||||
|
])
|
||||||
|
}
|
Loading…
Reference in New Issue