Merge pull request #163 from MaxRobinsonTheGreat/develop

Develop
This commit is contained in:
Max Robinson
2023-10-30 22:51:38 -05:00
committed by GitHub
35 changed files with 4409 additions and 5953 deletions

3
.gitignore vendored
View File

@@ -1,2 +1,3 @@
node_modules/* node_modules/*
dist/js/bundle.js dist/js/bundle.js
./package-lock.json

View File

@@ -1,5 +1,17 @@
# Changelog # Changelog
## 1.0.5 (4/23/2023)
### UI Enhancements:
- Improved "Community Creations" list panel
- Added Mod list to Community Creations
- Added brush size slider
- Added unnatural organism warning
### Simulation Enhancements:
- Added links to community mods
- Added more worlds and organisms to community creations
## 1.0.4 (9/17/2022) ## 1.0.4 (9/17/2022)
### UI Enhancements: ### UI Enhancements:

17
dist/assets/mods/_list.json vendored Normal file
View File

@@ -0,0 +1,17 @@
[
{
"name": "Life Engine Extended",
"value": "https://lifeengineextended.github.io/",
"subname" : "SpaceEye"
},
{
"name": "Camo/Healer Cells",
"value": "https://lifeenginelocal.netlify.app/",
"subname" : "Xiko"
},
{
"name": "Neural Networks",
"value": "https://bboettcher3.github.io/LifeEngine/",
"subname" : "bradyb"
}
]

1
dist/assets/organisms/NED.json vendored Normal file
View File

@@ -0,0 +1 @@
{"c":7,"r":7,"lifetime":0,"food_collected":0,"living":true,"direction":2,"rotation":0,"can_rotate":true,"move_count":0,"move_range":2,"ignore_brain_for":0,"mutability":13,"damage":0,"anatomy":{"birth_distance":12,"is_producer":false,"is_mover":true,"has_eyes":true,"cells":[{"loc_col":5,"loc_row":5,"state":{"name":"mouth"}},{"loc_col":5,"loc_row":5,"direction":0,"state":{"name":"eye"}},{"loc_col":5,"loc_row":5,"direction":1,"state":{"name":"eye"}},{"loc_col":5,"loc_row":5,"direction":2,"state":{"name":"eye"}},{"loc_col":5,"loc_row":5,"direction":3,"state":{"name":"eye"}},{"loc_col":5,"loc_row":5,"state":{"name":"mover"}},{"loc_col":5,"loc_row":5,"state":{"name":"armor"}},{"loc_col":5,"loc_row":5,"state":{"name":"killer"}}]},"brain":{"decisions":{"empty":0,"food":2,"wall":0,"mouth":2,"producer":2,"mover":2,"killer":0,"armor":0,"eye":2}},"species_name":"7ofozvw8tx"}

View File

@@ -1,34 +1,50 @@
[ [
{
"name": "Microquasar☣",
"value": "microquasar"
},
{ {
"name": "Purple Flower", "name": "Purple Flower",
"file": "purple_flower" "value": "purple_flower"
}, },
{ {
"name": "Rosa Finalis", "name": "Rosa Finalis",
"file": "RosaFinalis" "value": "RosaFinalis"
}, },
{ {
"name": "Quadratus Anxius", "name": "Quadratus Anxius",
"file": "The_Anxious_Square" "value": "The_Anxious_Square"
}, },
{ {
"name": "Hunter", "name": "Hunter",
"file": "hunter" "value": "hunter"
}, },
{ {
"name": "Small Bush Killer", "name": "Small Bush Killer",
"file": "smallbushkiller" "value": "smallbushkiller"
}, },
{ {
"name": "Fly Catcher", "name": "Fly Catcher",
"file": "flycatcher" "value": "flycatcher"
}, },
{ {
"name": "Heart Locket", "name": "Heart Locket",
"file": "HeartLocket" "value": "HeartLocket"
}, },
{ {
"name": "Napoleon", "name": "Napoleon",
"file": "Napoleon" "value": "Napoleon"
},
{
"name": "NED☣",
"value": "NED"
},
{
"name": "Spinner",
"value": "spinner"
},
{
"name": "Sword",
"value": "sword"
} }
] ]

144
dist/assets/organisms/microquasar.json vendored Normal file
View File

@@ -0,0 +1,144 @@
{
"c": 9,
"r": 9,
"lifetime": [],
"food_collected": 12313,
"living": true,
"direction": 2,
"rotation": 0,
"can_rotate": false,
"move_count": -1,
"move_range": -1,
"ignore_brain_for": 5,
"mutability": 14,
"damage": 0,
"anatomy": {
"birth_distance": 4,
"is_producer": true,
"is_mover": true,
"has_eyes": true,
"cells": [
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "producer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"direction": 2,
"state": {
"name": "eye"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "mouth"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "killer"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "mover"
}
},
{
"loc_col": 0,
"loc_row": 0,
"state": {
"name": "armor"
}
}
]
},
"brain": {
"decisions": {
"empty": 2,
"food": 2,
"wall": 0,
"mouth": 2,
"producer": 2,
"mover": 2,
"killer": 2,
"armor": 1,
"eye": 0
}
},
"species_name": "Microquasar"
}

1
dist/assets/organisms/spinner.json vendored Normal file
View File

@@ -0,0 +1 @@
{"c":7,"r":7,"lifetime":0,"food_collected":0,"living":true,"direction":2,"rotation":0,"can_rotate":true,"move_count":0,"move_range":20,"ignore_brain_for":0,"mutability":0,"damage":0,"species_name":"1fpa7zg0ng","anatomy":{"birth_distance":8,"is_producer":false,"is_mover":true,"has_eyes":true,"cells":[{"loc_col":0,"loc_row":-1,"direction":0,"state":{"name":"eye"}},{"loc_col":-1,"loc_row":0,"direction":3,"state":{"name":"eye"}},{"loc_col":1,"loc_row":0,"direction":1,"state":{"name":"eye"}},{"loc_col":0,"loc_row":1,"direction":2,"state":{"name":"eye"}},{"loc_col":-1,"loc_row":-3,"state":{"name":"mouth"}},{"loc_col":1,"loc_row":-3,"state":{"name":"killer"}},{"loc_col":3,"loc_row":-3,"state":{"name":"mouth"}},{"loc_col":3,"loc_row":-1,"state":{"name":"killer"}},{"loc_col":3,"loc_row":1,"state":{"name":"mouth"}},{"loc_col":3,"loc_row":3,"state":{"name":"killer"}},{"loc_col":1,"loc_row":3,"state":{"name":"mouth"}},{"loc_col":-1,"loc_row":3,"state":{"name":"killer"}},{"loc_col":-3,"loc_row":3,"state":{"name":"mouth"}},{"loc_col":-3,"loc_row":-1,"state":{"name":"mouth"}},{"loc_col":-3,"loc_row":-3,"state":{"name":"killer"}},{"loc_col":-3,"loc_row":1,"state":{"name":"killer"}},{"loc_col":1,"loc_row":-2,"state":{"name":"mouth"}},{"loc_col":-1,"loc_row":2,"state":{"name":"mouth"}},{"loc_col":-2,"loc_row":1,"state":{"name":"mouth"}},{"loc_col":2,"loc_row":-1,"state":{"name":"mouth"}},{"loc_col":0,"loc_row":0,"state":{"name":"mover"}}]},"brain":{"decisions":{"empty":0,"food":2,"wall":0,"mouth":0,"producer":2,"mover":0,"killer":1,"armor":0,"eye":1}}}

1
dist/assets/organisms/sword.json vendored Normal file
View File

@@ -0,0 +1 @@
{"c":7,"r":7,"lifetime":0,"food_collected":0,"living":true,"direction":2,"rotation":0,"can_rotate":true,"move_count":0,"move_range":4,"ignore_brain_for":0,"mutability":0,"damage":0,"anatomy":{"birth_distance":16,"is_producer":true,"is_mover":true,"has_eyes":true,"cells":[{"loc_col":-1,"loc_row":-1,"state":{"name":"mouth"}},{"loc_col":0,"loc_row":-2,"state":{"name":"mouth"}},{"loc_col":1,"loc_row":-3,"state":{"name":"mouth"}},{"loc_col":2,"loc_row":-4,"state":{"name":"mouth"}},{"loc_col":3,"loc_row":-5,"state":{"name":"mouth"}},{"loc_col":4,"loc_row":-6,"state":{"name":"mouth"}},{"loc_col":1,"loc_row":1,"state":{"name":"mouth"}},{"loc_col":2,"loc_row":0,"state":{"name":"mouth"}},{"loc_col":3,"loc_row":-1,"state":{"name":"mouth"}},{"loc_col":4,"loc_row":-2,"state":{"name":"mouth"}},{"loc_col":5,"loc_row":-3,"state":{"name":"mouth"}},{"loc_col":6,"loc_row":-4,"state":{"name":"mouth"}},{"loc_col":5,"loc_row":-5,"state":{"name":"producer"}},{"loc_col":4,"loc_row":-4,"state":{"name":"producer"}},{"loc_col":3,"loc_row":-3,"state":{"name":"producer"}},{"loc_col":2,"loc_row":-2,"state":{"name":"producer"}},{"loc_col":1,"loc_row":-1,"state":{"name":"producer"}},{"loc_col":0,"loc_row":0,"state":{"name":"producer"}},{"loc_col":-1,"loc_row":1,"state":{"name":"producer"}},{"loc_col":6,"loc_row":-6,"state":{"name":"producer"}},{"loc_col":-5,"loc_row":4,"state":{"name":"armor"}},{"loc_col":-5,"loc_row":5,"state":{"name":"armor"}},{"loc_col":-4,"loc_row":5,"state":{"name":"armor"}},{"loc_col":-3,"loc_row":4,"state":{"name":"armor"}},{"loc_col":-4,"loc_row":3,"state":{"name":"armor"}},{"loc_col":-4,"loc_row":1,"state":{"name":"armor"}},{"loc_col":-5,"loc_row":0,"state":{"name":"armor"}},{"loc_col":-5,"loc_row":-1,"state":{"name":"armor"}},{"loc_col":-5,"loc_row":-2,"state":{"name":"armor"}},{"loc_col":-4,"loc_row":-2,"state":{"name":"armor"}},{"loc_col":-3,"loc_row":-1,"state":{"name":"armor"}},{"loc_col":-1,"loc_row":4,"state":{"name":"armor"}},{"loc_col":0,"loc_row":5,"state":{"name":"armor"}},{"loc_col":1,"loc_row":5,"state":{"name":"armor"}},{"loc_col":2,"loc_row":5,"state":{"name":"armor"}},{"loc_col":2,"loc_row":4,"state":{"name":"armor"}},{"loc_col":1,"loc_row":3,"state":{"name":"armor"}},{"loc_col":-2,"loc_row":3,"state":{"name":"armor"}},{"loc_col":-3,"loc_row":2,"state":{"name":"armor"}},{"loc_col":-2,"loc_row":2,"state":{"name":"mover"}},{"loc_col":-1,"loc_row":2,"direction":0,"state":{"name":"eye"}},{"loc_col":-2,"loc_row":1,"direction":1,"state":{"name":"eye"}},{"loc_col":-3,"loc_row":1,"direction":2,"state":{"name":"eye"}},{"loc_col":-3,"loc_row":0,"direction":0,"state":{"name":"eye"}},{"loc_col":-4,"loc_row":0,"direction":3,"state":{"name":"eye"}},{"loc_col":-4,"loc_row":-1,"direction":1,"state":{"name":"eye"}},{"loc_col":-1,"loc_row":3,"direction":3,"state":{"name":"eye"}},{"loc_col":0,"loc_row":3,"direction":1,"state":{"name":"eye"}},{"loc_col":0,"loc_row":4,"direction":2,"state":{"name":"eye"}},{"loc_col":1,"loc_row":4,"direction":0,"state":{"name":"eye"}},{"loc_col":-3,"loc_row":3,"direction":1,"state":{"name":"eye"}},{"loc_col":-4,"loc_row":4,"direction":3,"state":{"name":"eye"}},{"loc_col":0,"loc_row":2,"state":{"name":"armor"}},{"loc_col":-2,"loc_row":0,"state":{"name":"armor"}},{"loc_col":7,"loc_row":-7,"state":{"name":"killer"}},{"loc_col":7,"loc_row":-5,"state":{"name":"mouth"}},{"loc_col":5,"loc_row":-7,"state":{"name":"mouth"}},{"loc_col":7,"loc_row":-6,"state":{"name":"mouth"}},{"loc_col":6,"loc_row":-7,"state":{"name":"mouth"}}]},"brain":{"decisions":{"empty":0,"food":2,"wall":0,"mouth":0,"producer":0,"mover":0,"killer":1,"armor":2,"eye":0}},"species_name":"v5i5g7zqq8"}

1
dist/assets/worlds/ArthursWorld.json vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/worlds/Chains.json vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/worlds/Epic.json vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
dist/assets/worlds/Sand_Grid.json vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,38 +1,70 @@
[ [
{
"name": "☣Unnatural Battle Royale⚔",
"value": "battleground"
},
{ {
"name": "Colony", "name": "Colony",
"file": "colony" "value": "colony"
}, },
{ {
"name": "Ephemeral World", "name": "Ephemeral World",
"file": "ephemeral" "value": "ephemeral"
}, },
{ {
"name": "Trailblazer", "name": "Trailblazer",
"file": "Trailblazer3" "value": "Trailblazer3"
}, },
{ {
"name": "Scarcity", "name": "Scarcity",
"file": "scarcity" "value": "scarcity"
}, },
{ {
"name": "High Def Sweepers", "name": "High Def Sweepers",
"file": "world_HighDefSweepers_start" "value": "world_HighDefSweepers_start"
}, },
{ {
"name": "Shrub Swamp", "name": "Shrub Swamp",
"file": "shrubland" "value": "shrubland"
}, },
{ {
"name": "Computer (unprogrammed)", "name": "Computer (unprogrammed)",
"file": "computer_unprogrammed" "value": "computer_unprogrammed"
}, },
{ {
"name": "Computer (fibonacci)", "name": "Computer (fibonacci)",
"file": "compV2_fixed_labeled_fibofancy" "value": "compV2_fixed_labeled_fibofancy"
}, },
{ {
"name": "Life Engine of Nurgle", "name": "Life Engine of Nurgle",
"file": "Life_Engine_of_Nurgle" "value": "Life_Engine_of_Nurgle"
},
{
"name": "Zoo",
"value": "zoo"
},
{
"name": "Chains",
"value": "Chains"
},
{
"name": "Symbiotic Colony",
"value": "SymbioticColony"
},
{
"name": "Sand Grid",
"value": "Sand_Grid"
},
{
"name": "Epic",
"value": "Epic"
},
{
"name": "Ostracod Slide",
"value": "Ostracod_Slide"
},
{
"name": "Altruistic Arthurs",
"value": "ArthursWorld"
} }
] ]

1
dist/assets/worlds/battle_royale.json vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/worlds/battleground.json vendored Normal file

File diff suppressed because one or more lines are too long

1
dist/assets/worlds/zoo.json vendored Normal file

File diff suppressed because one or more lines are too long

63
dist/css/style.css vendored
View File

@@ -10,7 +10,7 @@
} }
body{ body{
background: var(--border); background: var(--tab);
margin: 0; margin: 0;
padding: 0; padding: 0;
height: 100%; height: 100%;
@@ -163,6 +163,10 @@ input:hover,input:active {
background-color: var(--btn-hover); background-color: var(--btn-hover);
color: black; color: black;
} }
.tabnav p:active {
background-color: var(--btn-click);
color: white;
}
.open-tab { .open-tab {
background-color: var(--tab-active); background-color: var(--tab-active);
color: black; color: black;
@@ -172,7 +176,9 @@ input:hover,input:active {
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
grid-template-rows: 1; grid-template-rows: 1;
display: none; display: none;
padding: 10px padding: 10px;
overflow-y: auto;
height: 100%;
} }
.tab#about { .tab#about {
@@ -187,11 +193,10 @@ input:hover,input:active {
grid-column: 2; grid-column: 2;
} }
.global-mutation-in { .global-mutation-container {
display: none; display: none;
} }
#editor-panel{ #editor-panel{
display: flex; display: flex;
} }
@@ -206,6 +211,10 @@ input:hover,input:active {
#clear-walls { #clear-walls {
margin-top: 5px; margin-top: 5px;
} }
#brush-size-container {
display: flex;
align-items: center;
}
#organism-options { #organism-options {
display: none; display: none;
} }
@@ -218,7 +227,21 @@ input:hover,input:active {
image-rendering: pixelated; image-rendering: pixelated;
image-rendering: crisp-edges; image-rendering: crisp-edges;
height: 195px; height: 195px;
width: 195px; width: 195px;
display: block;
}
#unnatural-org-warning {
color: red;
text-align: center;
font-size: 22px;
border-radius: 10px;
margin: 2px;
border: 2px solid red;
}
#unnatural-org-warning:hover {
color: darkred;
border: 2px solid darkred;
} }
#cell-selections { #cell-selections {
display: none; display: none;
@@ -308,10 +331,11 @@ input:hover,input:active {
} }
.load-panel { .load-panel {
width: 300px; width: 800px;
background-color: var(--panel-bg); background-color: var(--panel-bg);
position: fixed; position: fixed;
display: none; display: none;
text-align: center;
left: 50%; left: 50%;
top: 50%; top: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
@@ -324,6 +348,33 @@ input:hover,input:active {
padding: 10px; padding: 10px;
} }
.list-title-container {
flex: 1;
}
.list-container {
overflow-y: auto;
max-height: 200px;
margin-right: 5px;
}
.list-item {
cursor: pointer;
padding: 5px;
border-bottom: 1px solid #ccc;
background-color: var(--btn);
}
.list-item:hover {
background-color: var(--btn-hover);
}
.list-item:active {
background-color: var(--btn-click);
}
.all-list-container {
display: flex;
width: 100%;
}
#close-load-btn { #close-load-btn {
float: right; float: right;
} }

50
dist/index.html vendored
View File

@@ -32,6 +32,10 @@
<p id='fps-actual'></p> <p id='fps-actual'></p>
<button id='reset-env' title='Restarts simulation with default organism.'>Reset</button> <button id='reset-env' title='Restarts simulation with default organism.'>Reset</button>
<button id='clear-env' title="Removes all organisms.">Clear</button> <button id='clear-env' title="Removes all organisms.">Clear</button>
<div id="brush-size-container">
<label id="brush-slider-label" for="brush-slider" title='Size of the brush for food, walls, and killing'><i class="fa fa-brush"></i> Brush Size </label>
<input id="brush-slider" type="range" min="0" max="15" value="2">
</div>
</div> </div>
<div id='tab-container' class='control-set'> <div id='tab-container' class='control-set'>
@@ -80,6 +84,7 @@
<button class="edit-mode-btn drop-org" id="drop-org" title="Drop organism in world. Hotkey: C"><i class="fa fa-plus"></i></button> <button class="edit-mode-btn drop-org" id="drop-org" title="Drop organism in world. Hotkey: C"><i class="fa fa-plus"></i></button>
<button id="save-org" title="Save Organism"><i class="fa fa-save"></i></button> <button id="save-org" title="Save Organism"><i class="fa fa-save"></i></button>
<button id="load-org" title="Load Organism"><i class="fa fa-upload"></i></button> <button id="load-org" title="Load Organism"><i class="fa fa-upload"></i></button>
<b id="unnatural-org-warning" title="Unnatural Organism: It has overlapping cells or genetic changes that cannot evolve naturally"><i class="fa fa-biohazard"></i></i></b>
</div> </div>
<div id='editor-env'> <div id='editor-env'>
<canvas id='editor-canvas'></canvas> <canvas id='editor-canvas'></canvas>
@@ -105,6 +110,7 @@
<div class='right-half'> <div class='right-half'>
<div id='organism-details' style="display:none;"> <div id='organism-details' style="display:none;">
<h3>Organism Details</h3> <h3>Organism Details</h3>
<p class='species-name'>Species Name: </p>
<p class='cell-count'>Cell count: </p> <p class='cell-count'>Cell count: </p>
<p id='move-range'>Move Range: </p> <p id='move-range'>Move Range: </p>
<p id='mutation-rate'>Mutation Rate: </p> <p id='mutation-rate'>Mutation Rate: </p>
@@ -211,7 +217,7 @@
<label for="rot-enabled" title='Organisms rotate when born and while moving.'>Rotation Enabled</label> <label for="rot-enabled" title='Organisms rotate when born and while moving.'>Rotation Enabled</label>
<input type="checkbox" id="rot-enabled" checked> <input type="checkbox" id="rot-enabled" checked>
<br> <br>
<label for="insta-kill" title='When on, killer cells immediatly kill organisms they touch. When off, organisms have as much health as they have cells and only take 1 damage from killer cells.'>One touch kill</label> <label for="insta-kill" title='When on, killer cells immediately kill organisms they touch. When off, organisms have as much health as they have cells and only take 1 damage from killer cells.'>One touch kill</label>
<input type="checkbox" id="insta-kill"> <input type="checkbox" id="insta-kill">
<br> <br>
<label for="look-range" title='How far an eye cell can see (in number of cells)'>Look range:</label> <label for="look-range" title='How far an eye cell can see (in number of cells)'>Look range:</label>
@@ -229,12 +235,15 @@
</div> </div>
<div class='right-half'> <div class='right-half'>
<button id='reset-rules'>Reset all controls</button> <button id='reset-rules'>Reset all controls</button><br>
<h4>Mutation Rate</h4> <label for="org-limit" title='Maximum number of organisms (-1 is unlimited)'>Maximum Organisms:</label>
<input type="number" id="org-limit" min="-1" value=-1 step="1"><br>
<label for="evolved-mutation" title='When on, each organism has its own mutation rate that can increase or decrease. When off, all organisms have the same mutation rate.'>Use evolved mutation rate</label> <label for="evolved-mutation" title='When on, each organism has its own mutation rate that can increase or decrease. When off, all organisms have the same mutation rate.'>Use evolved mutation rate</label>
<input type="checkbox" id="evolved-mutation" checked> </br> <input type="checkbox" id="evolved-mutation" checked>
<label class="global-mutation-in" for="global-mutation">Global mutation rate: </label> <div class="global-mutation-container">
<input class="global-mutation-in" type="number" id="global-mutation" min="0" max="100" value=5 step="1"> <label class="global-mutation-in" for="global-mutation">Global mutation rate: </label>
<input class="global-mutation-in" type="number" id="global-mutation" min="0" max="100" value=5 step="1">
</div>
<h4 title='When an organism mutates, it can choose from one of the following mutation types.'>Mutation Type Probabilities</h4> <h4 title='When an organism mutates, it can choose from one of the following mutation types.'>Mutation Type Probabilities</h4>
<label for="add-prob" title='A new cell will stem from an existing one'>Add Cell:</label> <label for="add-prob" title='A new cell will stem from an existing one'>Add Cell:</label>
<input class="mut-prob" type="number" id="add-prob" min="0" max="100" value=33> <input class="mut-prob" type="number" id="add-prob" min="0" max="100" value=33>
@@ -260,6 +269,7 @@
<h2>Statistics</h2> <h2>Statistics</h2>
<p id='org-count'>Total Population: </p> <p id='org-count'>Total Population: </p>
<p id='species-count'>Number of Species: </p> <p id='species-count'>Number of Species: </p>
<p id='top-species'>Most Populous Species: </p>
<p id='largest-org'>Largest Organism Ever: </p> <p id='largest-org'>Largest Organism Ever: </p>
<p id='avg-mut'>Average Mutation Rate: </p> <p id='avg-mut'>Average Mutation Rate: </p>
<label for="chart-option">Chart: </label> <label for="chart-option">Chart: </label>
@@ -280,14 +290,28 @@
<div class='load-panel'> <div class='load-panel'>
<div class="load-panel-title"> <div class="load-panel-title">
<button id='close-load-btn'><i class="fa fa-times"></i></button> <button id='close-load-btn'><i class="fa fa-times"></i></button>
<h2 id="load-title">Community Creations</h2> <h1>Community Creations</h1>
</div><br> </div><br>
Worlds<br> <div class="all-list-container">
<select id="worlds-load-dropdown"></select> <div class="list-title-container">
<button id='load-env-btn'>Load</button><br> <h2>🌍Worlds</h2>
Organisms<br> <div id="worlds-list-container" class="list-container">
<select id="organisms-load-dropdown"></select> <ul id="worlds-list"></ul>
<button id='load-org-btn'>Load</button><br><br> </div>
</div>
<div class="list-title-container">
<h2>🦠Organisms</h2>
<div id="organisms-list-container" class="list-container">
<ul id="organisms-list"></ul>
</div>
</div>
<div class="list-title-container">
<h2>🔧Mods</h2>
<div id="mods-list-container" class="list-container">
<ul id="mods-list"></ul>
</div>
</div>
</div>
<input id="upload-json" style="display: none;" type="file"> <input id="upload-json" style="display: none;" type="file">
</div> </div>
<div class='community-section'> <div class='community-section'>

9737
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,10 +21,13 @@
}, },
"homepage": "https://github.com/MaxRobinsonTheGreat/EvolutionSimulatorV2#readme", "homepage": "https://github.com/MaxRobinsonTheGreat/EvolutionSimulatorV2#readme",
"devDependencies": { "devDependencies": {
"webpack": "^4.43.0", "webpack": "^5.89.0",
"webpack-cli": "^3.3.12" "webpack-cli": "^3.3.12"
}, },
"dependencies": { "dependencies": {
"jquery": "^3.6.1" "glob-parent": "^6.0.2",
"jquery": "^3.6.1",
"npm": "^10.2.1",
"watchpack": "^2.4.0"
} }
} }

View File

@@ -122,7 +122,7 @@ class ControlPanel {
this.slider = document.getElementById("slider"); this.slider = document.getElementById("slider");
this.slider.oninput = function() { this.slider.oninput = function() {
const max_fps = 300; const max_fps = 300;
this.fps = this.slider.value; this.fps = parseInt(this.slider.value);
if (this.fps>=max_fps) this.fps = 1000; if (this.fps>=max_fps) this.fps = 1000;
if (this.engine.running) { if (this.engine.running) {
this.changeEngineSpeed(this.fps); this.changeEngineSpeed(this.fps);
@@ -180,6 +180,8 @@ class ControlPanel {
}); });
$('#resize').click(function() { $('#resize').click(function() {
if (!confirm('The current environment will be lost. Proceed?'))
return;
var cell_size = $('#cell-size').val(); var cell_size = $('#cell-size').val();
var fill_window = $('#fill-window').is(":checked"); var fill_window = $('#fill-window').is(":checked");
if (fill_window) { if (fill_window) {
@@ -190,7 +192,7 @@ class ControlPanel {
var rows = $('#row-input').val(); var rows = $('#row-input').val();
this.engine.env.resizeGridColRow(cell_size, cols, rows); this.engine.env.resizeGridColRow(cell_size, cols, rows);
} }
this.engine.env.reset(); this.engine.env.reset(false);
this.stats_panel.reset(); this.stats_panel.reset();
}.bind(this)); }.bind(this));
@@ -286,14 +288,17 @@ class ControlPanel {
$('#extra-mover-cost').change(function() { $('#extra-mover-cost').change(function() {
Hyperparams.extraMoverFoodCost = parseInt($('#extra-mover-cost').val()); Hyperparams.extraMoverFoodCost = parseInt($('#extra-mover-cost').val());
}); });
$('#org-limit').change(function() {
Hyperparams.maxOrganisms = parseInt($('#org-limit').val());
});
$('#evolved-mutation').change( function() { $('#evolved-mutation').change( function() {
if (this.checked) { if (this.checked) {
$('.global-mutation-in').css('display', 'none'); $('.global-mutation-container').css('display', 'none');
$('#avg-mut').css('display', 'block'); $('#avg-mut').css('display', 'block');
} }
else { else {
$('.global-mutation-in').css('display', 'block'); $('.global-mutation-container').css('display', 'block');
$('#avg-mut').css('display', 'none'); $('#avg-mut').css('display', 'none');
} }
Hyperparams.useGlobalMutability = !this.checked; Hyperparams.useGlobalMutability = !this.checked;
@@ -369,16 +374,17 @@ class ControlPanel {
$('#food-blocks').prop('checked', Hyperparams.foodBlocksReproduction); $('#food-blocks').prop('checked', Hyperparams.foodBlocksReproduction);
$('#food-drop-rate').val(Hyperparams.foodDropProb); $('#food-drop-rate').val(Hyperparams.foodDropProb);
$('#extra-mover-cost').val(Hyperparams.extraMoverFoodCost); $('#extra-mover-cost').val(Hyperparams.extraMoverFoodCost);
$('#org-limit').val(Hyperparams.maxOrganisms);
$('#look-range').val(Hyperparams.lookRange); $('#look-range').val(Hyperparams.lookRange);
$('#see-through-self').prop('checked', Hyperparams.seeThroughSelf); $('#see-through-self').prop('checked', Hyperparams.seeThroughSelf);
$('#global-mutation').val(Hyperparams.globalMutability); $('#global-mutation').val(Hyperparams.globalMutability);
if (!Hyperparams.useGlobalMutability) { if (!Hyperparams.useGlobalMutability) {
$('.global-mutation-in').css('display', 'none'); $('.global-mutation-container').css('display', 'none');
$('#avg-mut').css('display', 'block'); $('#avg-mut').css('display', 'block');
} }
else { else {
$('.global-mutation-in').css('display', 'block'); $('.global-mutation-container').css('display', 'block');
$('#avg-mut').css('display', 'none'); $('#avg-mut').css('display', 'none');
} }
} }
@@ -427,6 +433,9 @@ class ControlPanel {
env.reset(true, false); env.reset(true, false);
this.stats_panel.reset(); this.stats_panel.reset();
}); });
$('#brush-slider').on('input change', function () {
WorldConfig.brush_size = this.value;
});
$('#random-walls').click( function() { $('#random-walls').click( function() {
this.env_controller.randomizeWalls(); this.env_controller.randomizeWalls();
}.bind(this)); }.bind(this));
@@ -434,7 +443,7 @@ class ControlPanel {
this.engine.env.clearWalls(); this.engine.env.clearWalls();
}.bind(this)); }.bind(this));
$('#clear-editor').click( function() { $('#clear-editor').click( function() {
this.engine.organism_editor.clear(); this.engine.organism_editor.setDefaultOrg();
this.editor_controller.setEditorPanel(); this.editor_controller.setEditorPanel();
}.bind(this)); }.bind(this));
$('#generate-random').click( function() { $('#generate-random').click( function() {

View File

@@ -53,7 +53,14 @@ class EditorController extends CanvasController{
} }
updateDetails() { updateDetails() {
$('.species-name').text("Species name: "+this.env.organism.species.name);
$('.cell-count').text("Cell count: "+this.env.organism.anatomy.cells.length); $('.cell-count').text("Cell count: "+this.env.organism.anatomy.cells.length);
if (this.env.organism.isNatural()){
$('#unnatural-org-warning').css('display', 'none');
}
else {
$('#unnatural-org-warning').css('display', 'block');
}
} }
defineCellTypeSelection() { defineCellTypeSelection() {
@@ -150,13 +157,14 @@ class EditorController extends CanvasController{
this.setEditorPanel(); this.setEditorPanel();
else else
this.setDetailsPanel(); this.setDetailsPanel();
} }
setDetailsPanel() { setDetailsPanel() {
this.clearDetailsPanel(); this.clearDetailsPanel();
var org = this.env.organism; var org = this.env.organism;
$('.cell-count').text("Cell count: "+org.anatomy.cells.length); this.updateDetails();
$('#move-range').text("Move Range: "+org.move_range); $('#move-range').text("Move Range: "+org.move_range);
$('#mutation-rate').text("Mutation Rate: "+org.mutability); $('#mutation-rate').text("Mutation Rate: "+org.mutability);

View File

@@ -110,19 +110,18 @@ class EnvironmentController extends CanvasController{
switch(mode) { switch(mode) {
case Modes.FoodDrop: case Modes.FoodDrop:
if (left_click){ if (left_click){
this.dropCellType(cell.col, cell.row, CellStates.food, false); this.dropCellType(cell.col, cell.row, CellStates.food, false, CellStates.wall);
} }
else if (right_click){ else if (right_click){
this.dropCellType(cell.col, cell.row, CellStates.empty, false); this.dropCellType(cell.col, cell.row, CellStates.empty, false, CellStates.wall);
} }
break; break;
case Modes.WallDrop: case Modes.WallDrop:
if (left_click){ if (left_click){
this.dropCellType(cell.col, cell.row, CellStates.wall, true); this.dropCellType(cell.col, cell.row, CellStates.wall, true);
} }
else if (right_click){ else if (right_click){
this.dropCellType(cell.col, cell.row, CellStates.empty, false); this.dropCellType(cell.col, cell.row, CellStates.empty, false, CellStates.food);
} }
break; break;
case Modes.ClickKill: case Modes.ClickKill:
@@ -144,26 +143,25 @@ class EnvironmentController extends CanvasController{
} }
break; break;
case Modes.Drag: case Modes.Drag:
var cur_top = parseInt($('#env-canvas').css('top'), 10); this.dragScreen();
var cur_left = parseInt($('#env-canvas').css('left'), 10);
var new_top = cur_top + ((this.mouse_y - this.start_y)*this.scale);
var new_left = cur_left + ((this.mouse_x - this.start_x)*this.scale);
$('#env-canvas').css('top', new_top+'px');
$('#env-canvas').css('left', new_left+'px');
break; break;
} }
} }
else if (this.middle_click) { else if (this.middle_click) {
//drag on middle click //drag on middle click
var cur_top = parseInt($('#env-canvas').css('top'), 10); this.dragScreen();
var cur_left = parseInt($('#env-canvas').css('left'), 10);
var new_top = cur_top + ((this.mouse_y - this.start_y)*this.scale);
var new_left = cur_left + ((this.mouse_x - this.start_x)*this.scale);
$('#env-canvas').css('top', new_top+'px');
$('#env-canvas').css('left', new_left+'px');
} }
} }
dragScreen() {
var cur_top = parseInt($('#env-canvas').css('top'), 10);
var cur_left = parseInt($('#env-canvas').css('left'), 10);
var new_top = cur_top + ((this.mouse_y - this.start_y)*this.scale);
var new_left = cur_left + ((this.mouse_x - this.start_x)*this.scale);
$('#env-canvas').css('top', new_top+'px');
$('#env-canvas').css('left', new_left+'px');
}
dropOrganism(organism, col, row) { dropOrganism(organism, col, row) {
// close the organism and drop it in the world // close the organism and drop it in the world
@@ -187,8 +185,8 @@ class EnvironmentController extends CanvasController{
return false; return false;
} }
dropCellType(col, row, state, killBlocking=false) { dropCellType(col, row, state, killBlocking=false, ignoreState=null) {
for (var loc of Neighbors.allSelf){ for (var loc of Neighbors.inRange(WorldConfig.brush_size)){
var c=col + loc[0]; var c=col + loc[0];
var r=row + loc[1]; var r=row + loc[1];
var cell = this.env.grid_map.cellAt(c, r); var cell = this.env.grid_map.cellAt(c, r);
@@ -200,23 +198,32 @@ class EnvironmentController extends CanvasController{
else if (cell.owner != null) { else if (cell.owner != null) {
continue; continue;
} }
if (ignoreState != null && cell.state == ignoreState)
continue;
this.env.changeCell(c, r, state, null); this.env.changeCell(c, r, state, null);
} }
} }
findNearOrganism() { findNearOrganism() {
for (var loc of Neighbors.all){ let closest = null;
var c = this.cur_cell.col + loc[0]; let closest_dist = 100;
var r = this.cur_cell.row + loc[1]; for (let loc of Neighbors.inRange(WorldConfig.brush_size)){
var cell = this.env.grid_map.cellAt(c, r); let c = this.cur_cell.col + loc[0];
if (cell != null && cell.owner != null) let r = this.cur_cell.row + loc[1];
return cell.owner; let cell = this.env.grid_map.cellAt(c, r);
let dist = Math.abs(loc[0]) + Math.abs(loc[1]);
if (cell != null && cell.owner != null) {
if (closest === null || dist < closest_dist) {
closest = cell.owner;
closest_dist = dist;
}
}
} }
return null; return closest;
} }
killNearOrganisms() { killNearOrganisms() {
for (var loc of Neighbors.allSelf){ for (var loc of Neighbors.inRange(WorldConfig.brush_size)){
var c = this.cur_cell.col + loc[0]; var c = this.cur_cell.col + loc[0];
var r = this.cur_cell.row + loc[1]; var r = this.cur_cell.row + loc[1];
var cell = this.env.grid_map.cellAt(c, r); var cell = this.env.grid_map.cellAt(c, r);

View File

@@ -9,31 +9,44 @@ const LoadController = {
$('#community-creations-btn').click(()=>{ $('#community-creations-btn').click(()=>{
this.open(); this.open();
}); });
let panel = this;
$(".load-panel").on('click', '.list-item', async function() {
let list_name = $(this).closest(".list-container").attr('id');
let value = $(this).find('.hidden-value').text();
if (list_name === 'worlds-list-container') {
const base = `./assets/worlds/`;
let resp = await fetch(base+value+'.json');
let json = await resp.json();
panel.control_panel.loadEnv(json);
panel.close();
}
else if (list_name === 'organisms-list-container') {
const base = `./assets/organisms/`;
let resp = await fetch(base+value+'.json');
let json = await resp.json();
panel.control_panel.editor_controller.loadOrg(json);
panel.close();
$('#maximize').click();
$('#editor').click();
}
else if (list_name === 'mods-list-container') {
window.open(value, '_blank');
}
});
$('#load-env-btn').click(async ()=>{ $('#load-env-btn').click(async ()=>{
let file = $('#worlds-load-dropdown').val();
const base = `./assets/worlds/`;
let resp = await fetch(base+file+'.json');
let json = await resp.json();
this.control_panel.loadEnv(json);
this.close();
}); });
$('#load-org-btn').click(async ()=>{ $('#load-org-btn').click(async ()=>{
let file = $('#organisms-load-dropdown').val();
const base = `./assets/organisms/`;
let resp = await fetch(base+file+'.json');
let json = await resp.json();
this.control_panel.editor_controller.loadOrg(json);
this.close();
$('#maximize').click();
$('#editor').click();
}); });
this.loadList('worlds');
this.loadDropdown('worlds'); this.loadList('organisms');
this.loadDropdown('organisms'); this.loadList('mods');
}, },
async loadDropdown(name) { async loadList(name) {
const base = `./assets/${name}/`; const base = `./assets/${name}/`;
let list = []; let list = [];
@@ -44,14 +57,16 @@ const LoadController = {
console.error('Failed to load list: ', e); console.error('Failed to load list: ', e);
} }
let id = `#${name}-load-dropdown` let id = `#${name}-list`
$(id).empty(); $(id).empty();
for (let opt of list) { for (let item of list) {
$(id).append( let html = `<li class="list-item">
`<option value="${opt.file}"> ${item.name}`;
${opt.name} if (item.subname)
</option>` html += `<br>(${item.subname})`;
); html +=`<div class="hidden-value" hidden>${item.value}</div>
</li>`;
$(id).append(html);
} }
}, },

View File

@@ -15,7 +15,7 @@ class OrganismEditor extends Environment{
this.renderer = new Renderer('editor-canvas', 'editor-env', cell_size); this.renderer = new Renderer('editor-canvas', 'editor-env', cell_size);
this.controller = new EditorController(this, this.renderer.canvas); this.controller = new EditorController(this, this.renderer.canvas);
this.grid_map = new GridMap(15, 15, cell_size); this.grid_map = new GridMap(15, 15, cell_size);
this.clear(); this.setDefaultOrg();
} }
update() { update() {
@@ -81,6 +81,10 @@ class OrganismEditor extends Environment{
clear() { clear() {
this.grid_map.fillGrid(CellStates.empty); this.grid_map.fillGrid(CellStates.empty);
}
setDefaultOrg() {
this.clear();
var center = this.grid_map.getCenter(); var center = this.grid_map.getCenter();
this.organism = new Organism(center[0], center[1], this, null); this.organism = new Organism(center[0], center[1], this, null);
this.organism.anatomy.addDefaultCell(CellStates.mouth, 0, 0); this.organism.anatomy.addDefaultCell(CellStates.mouth, 0, 0);

View File

@@ -93,6 +93,10 @@ class WorldEnvironment extends Environment{
this.largest_cell_count = organism.anatomy.cells.length; this.largest_cell_count = organism.anatomy.cells.length;
} }
canAddOrganism() {
return this.organisms.length < Hyperparams.maxOrganisms || Hyperparams.maxOrganisms < 0;
}
averageMutability() { averageMutability() {
if (this.organisms.length < 1) if (this.organisms.length < 1)
return 0; return 0;
@@ -193,8 +197,12 @@ class WorldEnvironment extends Environment{
loadRaw(env) { // species name->stats map, evolution controls, loadRaw(env) { // species name->stats map, evolution controls,
this.organisms = []; this.organisms = [];
FossilRecord.clear_record(); FossilRecord.clear_record();
this.resizeGridColRow(this.grid_map.cell_size, env.grid.cols, env.grid.rows) let cell_size = env.grid.cell_size ? env.grid.cell_size : this.grid_map.cell_size;
this.resizeGridColRow(cell_size, env.grid.cols, env.grid.rows)
this.grid_map.loadRaw(env.grid); this.grid_map.loadRaw(env.grid);
for (let wall of env.grid.walls) {
this.walls.push(this.grid_map.cellAt(wall.c, wall.r));
}
// create species map // create species map
let species = {}; let species = {};
@@ -218,6 +226,7 @@ class WorldEnvironment extends Environment{
s.anatomy = org.anatomy; s.anatomy = org.anatomy;
s.calcAnatomyDetails(); s.calcAnatomyDetails();
} }
s.name = orgRaw.species_name;
org.species = s; org.species = s;
} }
for (let name in species) for (let name in species)

View File

@@ -83,7 +83,7 @@ class GridMap {
// Rather than store every single cell, we will store non organism cells (food+walls) // Rather than store every single cell, we will store non organism cells (food+walls)
// and assume everything else is empty. Organism cells will be set when the organism // and assume everything else is empty. Organism cells will be set when the organism
// list is loaded. This reduces filesize and complexity. // list is loaded. This reduces filesize and complexity.
let grid = {cols:this.cols, rows:this.rows}; let grid = {cell_size:this.cell_size, cols:this.cols, rows:this.rows};
grid.food = []; grid.food = [];
grid.walls = []; grid.walls = [];
for (let col of this.grid) { for (let col of this.grid) {

View File

@@ -20,7 +20,16 @@ const Neighbors = {
all: [[0, 1],[0, -1],[1, 0],[-1, 0],[-1, -1],[1, 1],[-1, 1],[1, -1]], all: [[0, 1],[0, -1],[1, 0],[-1, 0],[-1, -1],[1, 1],[-1, 1],[1, -1]],
adjacent: [[0, 1],[0, -1],[1, 0],[-1, 0]], adjacent: [[0, 1],[0, -1],[1, 0],[-1, 0]],
corners: [[-1, -1],[1, 1],[-1, 1],[1, -1]], corners: [[-1, -1],[1, 1],[-1, 1],[1, -1]],
allSelf: [[0, 0],[0, 1],[0, -1],[1, 0],[-1, 0],[-1, -1],[1, 1],[-1, 1],[1, -1]] allSelf: [[0, 0],[0, 1],[0, -1],[1, 0],[-1, 0],[-1, -1],[1, 1],[-1, 1],[1, -1]],
inRange: function (range) {
var neighbors = [];
for (var i = -range; i <= range; i++) {
for (var j = -range; j <= range; j++) {
neighbors.push([i, j]);
}
}
return neighbors;
}
} }
module.exports = Neighbors; module.exports = Neighbors;

View File

@@ -27,6 +27,8 @@ const Hyperparams = {
this.foodDropProb = 0; this.foodDropProb = 0;
this.extraMoverFoodCost = 0; this.extraMoverFoodCost = 0;
this.maxOrganisms = -1;
}, },
loadJsonObj(obj) { loadJsonObj(obj) {

View File

@@ -102,7 +102,10 @@ class Organism {
var new_c = this.c + (direction_c*basemovement) + (direction_c*offset); var new_c = this.c + (direction_c*basemovement) + (direction_c*offset);
var new_r = this.r + (direction_r*basemovement) + (direction_r*offset); var new_r = this.r + (direction_r*basemovement) + (direction_r*offset);
if (org.isClear(new_c, new_r, org.rotation, true) && org.isStraightPath(new_c, new_r, this.c, this.r, this)){ if (org.isClear(new_c, new_r, org.rotation, true) &&
org.isStraightPath(new_c, new_r, this.c, this.r, this) &&
this.env.canAddOrganism())
{
org.c = new_c; org.c = new_c;
org.r = new_r; org.r = new_r;
this.env.addOrganism(org); this.env.addOrganism(org);
@@ -307,7 +310,6 @@ class Organism {
} }
} }
} }
return this.living; return this.living;
} }
@@ -317,6 +319,26 @@ class Organism {
return this.env.grid_map.cellAt(real_c, real_r); return this.env.grid_map.cellAt(real_c, real_r);
} }
isNatural() {
let found_center = false;
if (this.anatomy.cells.length === 0) {
return false;
}
for (let i=0; i<this.anatomy.cells.length; i++) {
let cell = this.anatomy.cells[i];
for (let j=i+1; j<this.anatomy.cells.length; j++) {
let toCompare = this.anatomy.cells[j];
if (cell.loc_col === toCompare.loc_col && cell.loc_row === toCompare.loc_row) {
return false;
}
}
if (cell.loc_col === 0 && cell.loc_row === 0) {
found_center = true;
}
}
return found_center;
}
serialize() { serialize() {
let org = SerializeHelper.copyNonObjects(this); let org = SerializeHelper.copyNonObjects(this);
org.anatomy = this.anatomy.serialize(); org.anatomy = this.anatomy.serialize();

View File

@@ -121,6 +121,18 @@ const FossilRecord = {
this.av_cell_counts.push(cell_counts); this.av_cell_counts.push(cell_counts);
}, },
getMostPopulousSpecies(){
var max_pop = 0;
var max_species = undefined;
for (let s of Object.values(this.extant_species)) {
if (s.population > max_pop) {
max_pop = s.population;
max_species = s;
}
}
return max_species;
},
clear_record() { clear_record() {
this.extant_species = []; this.extant_species = [];
this.extinct_species = []; this.extinct_species = [];

View File

@@ -51,10 +51,13 @@ class StatsPanel {
var org_count = this.env.organisms.length; var org_count = this.env.organisms.length;
$('#org-count').text("Total Population: " + org_count); $('#org-count').text("Total Population: " + org_count);
$('#species-count').text("Number of Species: " + FossilRecord.numExtantSpecies()); $('#species-count').text("Number of Species: " + FossilRecord.numExtantSpecies());
let top_species = FossilRecord.getMostPopulousSpecies();
if (top_species)
$('#top-species').text("Most Populous Species: \"" + top_species.name + "\" (" + top_species.population + " organisms)");
else
$('#top-species').text("Most Populous Species: None");
$('#largest-org').text("Largest Organism Ever: " + this.env.largest_cell_count + " cells"); $('#largest-org').text("Largest Organism Ever: " + this.env.largest_cell_count + " cells");
$('#avg-mut').text("Average Mutation Rate: " + Math.round(this.env.averageMutability() * 100) / 100); $('#avg-mut').text("Average Mutation Rate: " + Math.round(this.env.averageMutability() * 100) / 100);
} }
reset() { reset() {

View File

@@ -3,6 +3,7 @@ const WorldConfig = {
clear_walls_on_reset: false, clear_walls_on_reset: false,
auto_reset: true, auto_reset: true,
auto_pause: false, auto_pause: false,
brush_size: 2,
} }
module.exports = WorldConfig; module.exports = WorldConfig;